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 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 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: 85 explicit HelperPluginChromeClient(WebHelperPluginImpl* widget) 86 : m_widget(widget) 87 { 88 ASSERT(m_widget->m_widgetClient); 89 } 90 91 private: 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 100 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: 114 HelperPluginFrameClient(WebFrameClient* hostWebFrameClient) 115 : m_hostWebFrameClient(hostWebFrameClient) 116 { 117 } 118 119 virtual ~HelperPluginFrameClient() 120 { 121 } 122 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 135 WebHelperPluginImpl::WebHelperPluginImpl(WebWidgetClient* client) 136 : m_widgetClient(client) 137 , m_webView(0) 138 , m_mainFrame(0) 139 { 140 ASSERT(client); 141 } 142 143 WebHelperPluginImpl::~WebHelperPluginImpl() 144 { 145 ASSERT(!m_page); 146 } 147 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 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 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. 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 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 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 246 void WebHelperPluginImpl::layout() 247 { 248 PageWidgetDelegate::layout(m_page.get()); 249 } 250 251 void WebHelperPluginImpl::setFocus(bool) 252 { 253 ASSERT_NOT_REACHED(); 254 } 255 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 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