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 "WebFrameImpl.h" 37 #include "WebPlugin.h" 38 #include "WebPluginContainerImpl.h" 39 #include "WebViewClient.h" 40 #include "WebViewImpl.h" 41 #include "WebWidgetClient.h" 42 #include "core/dom/NodeList.h" 43 #include "core/html/HTMLPlugInElement.h" 44 #include "core/loader/DocumentLoader.h" 45 #include "core/loader/EmptyClients.h" 46 #include "core/page/FocusController.h" 47 #include "core/page/FrameView.h" 48 #include "core/page/Page.h" 49 #include "core/page/Settings.h" 50 51 using namespace WebCore; 52 53 namespace WebKit { 54 55 #define addLiteral(literal, writer) writer->addData(literal, sizeof(literal) - 1) 56 57 static inline void addString(const String& str, DocumentWriter* writer) 58 { 59 CString str8 = str.utf8(); 60 writer->addData(str8.data(), str8.length()); 61 } 62 63 static void writeDocument(const String& pluginType, const WebDocument& hostDocument, WebCore::DocumentLoader* loader) 64 { 65 // Give the new document the same URL as the hose document so that content 66 // settings and other decisions can be made based on the correct origin. 67 const WebURL& url = hostDocument.url(); 68 69 DocumentWriter* writer = loader->beginWriting("text/html", "UTF-8", url); 70 71 addLiteral("<!DOCTYPE html><head><meta charset='UTF-8'></head><body>\n", writer); 72 String objectTag = "<object type=\"" + pluginType + "\"></object>"; 73 addString(objectTag, writer); 74 addLiteral("</body>\n", writer); 75 76 loader->endWriting(writer); 77 } 78 79 class HelperPluginChromeClient : public EmptyChromeClient { 80 WTF_MAKE_NONCOPYABLE(HelperPluginChromeClient); 81 WTF_MAKE_FAST_ALLOCATED; 82 83 public: 84 explicit HelperPluginChromeClient(WebHelperPluginImpl* widget) 85 : m_widget(widget) 86 { 87 ASSERT(m_widget->m_widgetClient); 88 } 89 90 private: 91 virtual void closeWindowSoon() OVERRIDE 92 { 93 // This should never be called since the only way to close the 94 // invisible page is via closeHelperPlugin(). 95 ASSERT_NOT_REACHED(); 96 m_widget->closeHelperPlugin(); 97 } 98 99 virtual void* webView() const OVERRIDE 100 { 101 return m_widget->m_webView; 102 } 103 104 WebHelperPluginImpl* m_widget; 105 }; 106 107 // WebHelperPluginImpl ---------------------------------------------------------------- 108 109 WebHelperPluginImpl::WebHelperPluginImpl(WebWidgetClient* client) 110 : m_widgetClient(client) 111 , m_webView(0) 112 { 113 ASSERT(client); 114 } 115 116 WebHelperPluginImpl::~WebHelperPluginImpl() 117 { 118 ASSERT(!m_page); 119 } 120 121 bool WebHelperPluginImpl::initialize(const String& pluginType, const WebDocument& hostDocument, WebViewImpl* webView) 122 { 123 ASSERT(webView); 124 m_webView = webView; 125 126 return initializePage(pluginType, hostDocument); 127 } 128 129 void WebHelperPluginImpl::closeHelperPlugin() 130 { 131 if (m_page) { 132 m_page->clearPageGroup(); 133 m_page->mainFrame()->loader()->stopAllLoaders(); 134 m_page->mainFrame()->loader()->stopLoading(UnloadEventPolicyNone); 135 } 136 137 // We must destroy the page now in case the host page is being destroyed, in 138 // which case some of the objects the page depends on may have been 139 // destroyed by the time this->close() is called asynchronously. 140 destroyPage(); 141 142 // m_widgetClient might be 0 because this widget might be already closed. 143 if (m_widgetClient) { 144 // closeWidgetSoon() will call this->close() later. 145 m_widgetClient->closeWidgetSoon(); 146 } 147 } 148 149 void WebHelperPluginImpl::initializeFrame(WebFrameClient* client) 150 { 151 ASSERT(m_page); 152 RefPtr<WebFrameImpl> frame = WebFrameImpl::create(client); 153 frame->initializeAsMainFrame(m_page.get()); 154 } 155 156 // Returns a pointer to the WebPlugin by finding the single <object> tag in the page. 157 WebPlugin* WebHelperPluginImpl::getPlugin() 158 { 159 ASSERT(m_page); 160 161 RefPtr<NodeList> objectElements = m_page->mainFrame()->document()->getElementsByTagName(WebCore::HTMLNames::objectTag.localName()); 162 ASSERT(objectElements && objectElements->length() == 1); 163 if (!objectElements || objectElements->length() < 1) 164 return 0; 165 Node* node = objectElements->item(0); 166 ASSERT(node->hasTagName(WebCore::HTMLNames::objectTag)); 167 WebCore::Widget* widget = toHTMLPlugInElement(node)->pluginWidget(); 168 if (!widget) 169 return 0; 170 WebPlugin* plugin = static_cast<WebPluginContainerImpl*>(widget)->plugin(); 171 ASSERT(plugin); 172 // If the plugin is a placeholder, it is not useful to the caller, and it 173 // could be replaced at any time. Therefore, do not return it. 174 if (plugin->isPlaceholder()) 175 return 0; 176 177 // The plugin was instantiated and will outlive this object. 178 return plugin; 179 } 180 181 bool WebHelperPluginImpl::initializePage(const String& pluginType, const WebDocument& hostDocument) 182 { 183 Page::PageClients pageClients; 184 fillWithEmptyClients(pageClients); 185 m_chromeClient = adoptPtr(new HelperPluginChromeClient(this)); 186 pageClients.chromeClient = m_chromeClient.get(); 187 188 m_page = adoptPtr(new Page(pageClients)); 189 ASSERT(!m_page->settings()->isScriptEnabled()); 190 m_page->settings()->setPluginsEnabled(true); 191 192 unsigned layoutMilestones = DidFirstLayout | DidFirstVisuallyNonEmptyLayout; 193 m_page->addLayoutMilestones(static_cast<LayoutMilestones>(layoutMilestones)); 194 195 m_webView->client()->initializeHelperPluginWebFrame(this); 196 197 // The page's main frame was set in initializeFrame() as a result of the above call. 198 Frame* frame = m_page->mainFrame(); 199 ASSERT(frame); 200 frame->loader()->forceSandboxFlags(SandboxAll & ~SandboxPlugins); 201 frame->setView(FrameView::create(frame)); 202 // No need to set a size or make it not transparent. 203 204 writeDocument(pluginType, hostDocument, frame->loader()->activeDocumentLoader()); 205 206 return true; 207 } 208 209 void WebHelperPluginImpl::destroyPage() 210 { 211 if (!m_page) 212 return; 213 214 if (m_page->mainFrame()) 215 m_page->mainFrame()->loader()->frameDetached(); 216 217 m_page.clear(); 218 } 219 220 void WebHelperPluginImpl::layout() 221 { 222 PageWidgetDelegate::layout(m_page.get()); 223 } 224 225 void WebHelperPluginImpl::setFocus(bool) 226 { 227 ASSERT_NOT_REACHED(); 228 } 229 230 void WebHelperPluginImpl::close() 231 { 232 ASSERT(!m_page); // Should only be called via closePopup(). 233 m_widgetClient = 0; 234 deref(); 235 } 236 237 // WebHelperPlugin ---------------------------------------------------------------- 238 239 WebHelperPlugin* WebHelperPlugin::create(WebWidgetClient* client) 240 { 241 RELEASE_ASSERT(client); 242 // A WebHelperPluginImpl instance usually has two references. 243 // - One owned by the instance itself. It represents the visible widget. 244 // - One owned by the hosting element. It's released when the hosting 245 // element asks the WebHelperPluginImpl to close. 246 // We need them because the closing operation is asynchronous and the widget 247 // can be closed while the hosting element is unaware of it. 248 return adoptRef(new WebHelperPluginImpl(client)).leakRef(); 249 } 250 251 } // namespace WebKit 252