1 /*
2 * Copyright (C) 2012 Google 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 are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "config.h"
32 #include "WebHelperPluginImpl.h"
33
34 #include "PageWidgetDelegate.h"
35 #include "WebDocument.h"
36 #include "WebFrameClient.h"
37 #include "WebFrameImpl.h"
38 #include "WebPlugin.h"
39 #include "WebPluginContainerImpl.h"
40 #include "WebViewClient.h"
41 #include "WebViewImpl.h"
42 #include "WebWidgetClient.h"
43 #include "core/dom/NodeList.h"
44 #include "core/html/HTMLPlugInElement.h"
45 #include "core/loader/DocumentLoader.h"
46 #include "core/loader/EmptyClients.h"
47 #include "core/page/FocusController.h"
48 #include "core/frame/FrameView.h"
49 #include "core/page/Page.h"
50 #include "core/frame/Settings.h"
51
52 using namespace WebCore;
53
54 namespace blink {
55
56 #define addLiteral(literal, writer) writer->addData(literal, sizeof(literal) - 1)
57
addString(const String & str,DocumentWriter * writer)58 static inline void addString(const String& str, DocumentWriter* writer)
59 {
60 CString str8 = str.utf8();
61 writer->addData(str8.data(), str8.length());
62 }
63
writeDocument(const String & pluginType,const WebDocument & hostDocument,WebCore::DocumentLoader * loader)64 static void writeDocument(const String& pluginType, const WebDocument& hostDocument, WebCore::DocumentLoader* loader)
65 {
66 // Give the new document the same URL as the hose document so that content
67 // settings and other decisions can be made based on the correct origin.
68 const WebURL& url = hostDocument.url();
69
70 DocumentWriter* writer = loader->beginWriting("text/html", "UTF-8", url);
71
72 addLiteral("<!DOCTYPE html><head><meta charset='UTF-8'></head><body>\n", writer);
73 String objectTag = "<object type=\"" + pluginType + "\"></object>";
74 addString(objectTag, writer);
75 addLiteral("</body>\n", writer);
76
77 loader->endWriting(writer);
78 }
79
80 class HelperPluginChromeClient : public EmptyChromeClient {
81 WTF_MAKE_NONCOPYABLE(HelperPluginChromeClient);
82 WTF_MAKE_FAST_ALLOCATED;
83
84 public:
HelperPluginChromeClient(WebHelperPluginImpl * widget)85 explicit HelperPluginChromeClient(WebHelperPluginImpl* widget)
86 : m_widget(widget)
87 {
88 ASSERT(m_widget->m_widgetClient);
89 }
90
91 private:
closeWindowSoon()92 virtual void closeWindowSoon() OVERRIDE
93 {
94 // This should never be called since the only way to close the
95 // invisible page is via closeHelperPlugin().
96 ASSERT_NOT_REACHED();
97 m_widget->closeHelperPlugin();
98 }
99
webView() const100 virtual void* webView() const OVERRIDE
101 {
102 return m_widget->m_webView;
103 }
104
105 WebHelperPluginImpl* m_widget;
106 };
107
108 // HelperPluginFrameClient acts as a filter to only forward messages onto the
109 // main render frame that WebHelperPlugin actually needs. This prevents
110 // having the WebHelperPlugin's frame accidentally signaling events on the
111 // client that are meant only for WebFrames which are part of the main DOM.
112 class HelperPluginFrameClient : public WebFrameClient {
113 public:
HelperPluginFrameClient(WebFrameClient * hostWebFrameClient)114 HelperPluginFrameClient(WebFrameClient* hostWebFrameClient)
115 : m_hostWebFrameClient(hostWebFrameClient)
116 {
117 }
118
~HelperPluginFrameClient()119 virtual ~HelperPluginFrameClient()
120 {
121 }
122
createPlugin(blink::WebFrame * frame,const WebPluginParams & params)123 virtual WebPlugin* createPlugin(blink::WebFrame* frame, const WebPluginParams& params)
124 {
125 return m_hostWebFrameClient->createPlugin(frame, params);
126 }
127
128 private:
129 WebFrameClient* m_hostWebFrameClient;
130 };
131
132
133 // WebHelperPluginImpl ----------------------------------------------------------------
134
WebHelperPluginImpl(WebWidgetClient * client)135 WebHelperPluginImpl::WebHelperPluginImpl(WebWidgetClient* client)
136 : m_widgetClient(client)
137 , m_webView(0)
138 , m_mainFrame(0)
139 {
140 ASSERT(client);
141 }
142
~WebHelperPluginImpl()143 WebHelperPluginImpl::~WebHelperPluginImpl()
144 {
145 ASSERT(!m_page);
146 }
147
initialize(const String & pluginType,const WebDocument & hostDocument,WebViewImpl * webView)148 bool WebHelperPluginImpl::initialize(const String& pluginType, const WebDocument& hostDocument, WebViewImpl* webView)
149 {
150 ASSERT(webView);
151 m_webView = webView;
152
153 return initializePage(pluginType, hostDocument);
154 }
155
closeHelperPlugin()156 void WebHelperPluginImpl::closeHelperPlugin()
157 {
158 if (m_page) {
159 m_page->clearPageGroup();
160 m_page->mainFrame()->loader().stopAllLoaders();
161 }
162
163 // We must destroy the page now in case the host page is being destroyed, in
164 // which case some of the objects the page depends on may have been
165 // destroyed by the time this->close() is called asynchronously.
166 destroyPage();
167
168 // m_widgetClient might be 0 because this widget might be already closed.
169 if (m_widgetClient) {
170 // closeWidgetSoon() will call this->close() later.
171 m_widgetClient->closeWidgetSoon();
172 }
173 m_mainFrame->close();
174 }
175
initializeFrame(WebFrameClient * client)176 void WebHelperPluginImpl::initializeFrame(WebFrameClient* client)
177 {
178 ASSERT(m_page);
179 ASSERT(!m_frameClient);
180 m_frameClient = adoptPtr(new HelperPluginFrameClient(client));
181 m_mainFrame = WebFrameImpl::create(m_frameClient.get());
182 m_mainFrame->initializeAsMainFrame(m_page.get());
183 }
184
185 // Returns a pointer to the WebPlugin by finding the single <object> tag in the page.
getPlugin()186 WebPlugin* WebHelperPluginImpl::getPlugin()
187 {
188 ASSERT(m_page);
189
190 RefPtr<NodeList> objectElements = m_page->mainFrame()->document()->getElementsByTagName(WebCore::HTMLNames::objectTag.localName());
191 ASSERT(objectElements && objectElements->length() == 1);
192 if (!objectElements || objectElements->length() < 1)
193 return 0;
194 Node* node = objectElements->item(0);
195 ASSERT(node->hasTagName(WebCore::HTMLNames::objectTag));
196 WebCore::Widget* widget = toHTMLPlugInElement(node)->pluginWidget();
197 if (!widget)
198 return 0;
199 WebPlugin* plugin = toPluginContainerImpl(widget)->plugin();
200 ASSERT(plugin);
201 // If the plugin is a placeholder, it is not useful to the caller, and it
202 // could be replaced at any time. Therefore, do not return it.
203 if (plugin->isPlaceholder())
204 return 0;
205
206 // The plugin was instantiated and will outlive this object.
207 return plugin;
208 }
209
initializePage(const String & pluginType,const WebDocument & hostDocument)210 bool WebHelperPluginImpl::initializePage(const String& pluginType, const WebDocument& hostDocument)
211 {
212 Page::PageClients pageClients;
213 fillWithEmptyClients(pageClients);
214 m_chromeClient = adoptPtr(new HelperPluginChromeClient(this));
215 pageClients.chromeClient = m_chromeClient.get();
216
217 m_page = adoptPtr(new Page(pageClients));
218 ASSERT(!m_page->settings().isScriptEnabled());
219 m_page->settings().setPluginsEnabled(true);
220
221 m_webView->client()->initializeHelperPluginWebFrame(this);
222
223 // The page's main frame was set in initializeFrame() as a result of the above call.
224 Frame* frame = m_page->mainFrame();
225 ASSERT(frame);
226 frame->loader().forceSandboxFlags(SandboxAll & ~SandboxPlugins);
227 frame->setView(FrameView::create(frame));
228 // No need to set a size or make it not transparent.
229
230 writeDocument(pluginType, hostDocument, frame->loader().activeDocumentLoader());
231
232 return true;
233 }
234
destroyPage()235 void WebHelperPluginImpl::destroyPage()
236 {
237 if (!m_page)
238 return;
239
240 if (m_page->mainFrame())
241 m_page->mainFrame()->loader().frameDetached();
242
243 m_page.clear();
244 }
245
layout()246 void WebHelperPluginImpl::layout()
247 {
248 PageWidgetDelegate::layout(m_page.get());
249 }
250
setFocus(bool)251 void WebHelperPluginImpl::setFocus(bool)
252 {
253 ASSERT_NOT_REACHED();
254 }
255
close()256 void WebHelperPluginImpl::close()
257 {
258 ASSERT(!m_page); // Should only be called via closePopup().
259 m_widgetClient = 0;
260 deref();
261 }
262
263 // WebHelperPlugin ----------------------------------------------------------------
264
create(WebWidgetClient * client)265 WebHelperPlugin* WebHelperPlugin::create(WebWidgetClient* client)
266 {
267 RELEASE_ASSERT(client);
268 // A WebHelperPluginImpl instance usually has two references.
269 // - One owned by the instance itself. It represents the visible widget.
270 // - One owned by the hosting element. It's released when the hosting
271 // element asks the WebHelperPluginImpl to close.
272 // We need them because the closing operation is asynchronous and the widget
273 // can be closed while the hosting element is unaware of it.
274 return adoptRef(new WebHelperPluginImpl(client)).leakRef();
275 }
276
277 } // namespace blink
278