1/*
2 * Copyright (C) 2006-2017 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
20 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "config.h"
26#include "PluginDocument.h"
27
28#include "DocumentLoader.h"
29#include "Frame.h"
30#include "FrameLoader.h"
31#include "FrameLoaderClient.h"
32#include "FrameView.h"
33#include "HTMLBodyElement.h"
34#include "HTMLEmbedElement.h"
35#include "HTMLHtmlElement.h"
36#include "HTMLNames.h"
37#include "RawDataDocumentParser.h"
38#include "RenderEmbeddedObject.h"
39#include <wtf/IsoMallocInlines.h>
40
41namespace WebCore {
42
43WTF_MAKE_ISO_ALLOCATED_IMPL(PluginDocument);
44
45using namespace HTMLNames;
46
47// FIXME: Share more code with MediaDocumentParser.
48class PluginDocumentParser final : public RawDataDocumentParser {
49public:
50 static Ref<PluginDocumentParser> create(PluginDocument& document)
51 {
52 return adoptRef(*new PluginDocumentParser(document));
53 }
54
55private:
56 PluginDocumentParser(Document& document)
57 : RawDataDocumentParser(document)
58 {
59 }
60
61 void appendBytes(DocumentWriter&, const char*, size_t) final;
62 void createDocumentStructure();
63
64 HTMLEmbedElement* m_embedElement { nullptr };
65};
66
67void PluginDocumentParser::createDocumentStructure()
68{
69 auto& document = downcast<PluginDocument>(*this->document());
70
71 auto rootElement = HTMLHtmlElement::create(document);
72 document.appendChild(rootElement);
73 rootElement->insertedByParser();
74
75 if (document.frame())
76 document.frame()->injectUserScripts(InjectAtDocumentStart);
77
78#if PLATFORM(IOS_FAMILY)
79 // Should not be able to zoom into standalone plug-in documents.
80 document.processViewport("user-scalable=no"_s, ViewportArguments::PluginDocument);
81#endif
82
83 auto body = HTMLBodyElement::create(document);
84 body->setAttributeWithoutSynchronization(marginwidthAttr, AtomicString("0", AtomicString::ConstructFromLiteral));
85 body->setAttributeWithoutSynchronization(marginheightAttr, AtomicString("0", AtomicString::ConstructFromLiteral));
86#if PLATFORM(IOS_FAMILY)
87 body->setAttribute(styleAttr, AtomicString("background-color: rgb(217,224,233)", AtomicString::ConstructFromLiteral));
88#else
89 body->setAttribute(styleAttr, AtomicString("background-color: rgb(38,38,38)", AtomicString::ConstructFromLiteral));
90#endif
91
92 rootElement->appendChild(body);
93
94 auto embedElement = HTMLEmbedElement::create(document);
95
96 m_embedElement = embedElement.ptr();
97 embedElement->setAttributeWithoutSynchronization(widthAttr, AtomicString("100%", AtomicString::ConstructFromLiteral));
98 embedElement->setAttributeWithoutSynchronization(heightAttr, AtomicString("100%", AtomicString::ConstructFromLiteral));
99
100 embedElement->setAttributeWithoutSynchronization(nameAttr, AtomicString("plugin", AtomicString::ConstructFromLiteral));
101 embedElement->setAttributeWithoutSynchronization(srcAttr, document.url().string());
102
103 ASSERT(document.loader());
104 if (auto loader = makeRefPtr(document.loader()))
105 m_embedElement->setAttributeWithoutSynchronization(typeAttr, loader->writer().mimeType());
106
107 document.setPluginElement(*m_embedElement);
108
109 body->appendChild(embedElement);
110}
111
112void PluginDocumentParser::appendBytes(DocumentWriter&, const char*, size_t)
113{
114 if (m_embedElement)
115 return;
116
117 createDocumentStructure();
118
119 auto frame = makeRefPtr(document()->frame());
120 if (!frame)
121 return;
122
123 document()->updateLayout();
124
125 // Below we assume that renderer->widget() to have been created by
126 // document()->updateLayout(). However, in some cases, updateLayout() will
127 // recurse too many times and delay its post-layout tasks (such as creating
128 // the widget). Here we kick off the pending post-layout tasks so that we
129 // can synchronously redirect data to the plugin.
130 frame->view()->flushAnyPendingPostLayoutTasks();
131
132 if (RenderWidget* renderer = m_embedElement->renderWidget()) {
133 if (RefPtr<Widget> widget = renderer->widget()) {
134 frame->loader().client().redirectDataToPlugin(*widget);
135 // In a plugin document, the main resource is the plugin. If we have a null widget, that means
136 // the loading of the plugin was cancelled, which gives us a null mainResourceLoader(), so we
137 // need to have this call in a null check of the widget or of mainResourceLoader().
138 frame->loader().activeDocumentLoader()->setMainResourceDataBufferingPolicy(DataBufferingPolicy::DoNotBufferData);
139 }
140 }
141}
142
143PluginDocument::PluginDocument(Frame* frame, const URL& url)
144 : HTMLDocument(frame, url, PluginDocumentClass)
145{
146 setCompatibilityMode(DocumentCompatibilityMode::QuirksMode);
147 lockCompatibilityMode();
148}
149
150Ref<DocumentParser> PluginDocument::createParser()
151{
152 return PluginDocumentParser::create(*this);
153}
154
155Widget* PluginDocument::pluginWidget()
156{
157 if (!m_pluginElement)
158 return nullptr;
159 auto* renderer = m_pluginElement->renderer();
160 if (!renderer)
161 return nullptr;
162 return downcast<RenderEmbeddedObject>(*m_pluginElement->renderer()).widget();
163}
164
165void PluginDocument::setPluginElement(HTMLPlugInElement& element)
166{
167 m_pluginElement = &element;
168}
169
170void PluginDocument::detachFromPluginElement()
171{
172 // Release the plugin Element so that we don't have a circular reference.
173 m_pluginElement = nullptr;
174}
175
176void PluginDocument::cancelManualPluginLoad()
177{
178 // PluginDocument::cancelManualPluginLoad should only be called once, but there are issues
179 // with how many times we call beforeload on object elements. <rdar://problem/8441094>.
180 if (!shouldLoadPluginManually())
181 return;
182
183 auto& frameLoader = frame()->loader();
184 auto& documentLoader = *frameLoader.activeDocumentLoader();
185 documentLoader.cancelMainResourceLoad(frameLoader.cancelledError(documentLoader.request()));
186 m_shouldLoadPluginManually = false;
187}
188
189}
190