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 "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