Home | History | Annotate | Download | only in web
      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