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 "web/WebPagePopupImpl.h"
     33 
     34 #include "core/dom/ContextFeatures.h"
     35 #include "core/frame/FrameView.h"
     36 #include "core/frame/LocalFrame.h"
     37 #include "core/frame/Settings.h"
     38 #include "core/loader/EmptyClients.h"
     39 #include "core/loader/FrameLoadRequest.h"
     40 #include "core/page/Chrome.h"
     41 #include "core/page/DOMWindowPagePopup.h"
     42 #include "core/page/EventHandler.h"
     43 #include "core/page/FocusController.h"
     44 #include "core/page/Page.h"
     45 #include "core/page/PagePopupClient.h"
     46 #include "platform/TraceEvent.h"
     47 #include "platform/heap/Handle.h"
     48 #include "public/platform/WebCompositeAndReadbackAsyncCallback.h"
     49 #include "public/platform/WebCursorInfo.h"
     50 #include "public/web/WebAXObject.h"
     51 #include "public/web/WebFrameClient.h"
     52 #include "public/web/WebViewClient.h"
     53 #include "public/web/WebWidgetClient.h"
     54 #include "web/WebInputEventConversion.h"
     55 #include "web/WebLocalFrameImpl.h"
     56 #include "web/WebSettingsImpl.h"
     57 #include "web/WebViewImpl.h"
     58 
     59 namespace blink {
     60 
     61 class PagePopupChromeClient : public EmptyChromeClient {
     62     WTF_MAKE_NONCOPYABLE(PagePopupChromeClient);
     63     WTF_MAKE_FAST_ALLOCATED;
     64 
     65 public:
     66     explicit PagePopupChromeClient(WebPagePopupImpl* popup)
     67         : m_popup(popup)
     68     {
     69         ASSERT(m_popup->widgetClient());
     70     }
     71 
     72 private:
     73     virtual void closeWindowSoon() OVERRIDE
     74     {
     75         m_popup->closePopup();
     76     }
     77 
     78     virtual FloatRect windowRect() OVERRIDE
     79     {
     80         return FloatRect(m_popup->m_windowRectInScreen.x, m_popup->m_windowRectInScreen.y, m_popup->m_windowRectInScreen.width, m_popup->m_windowRectInScreen.height);
     81     }
     82 
     83     virtual void setWindowRect(const FloatRect& rect) OVERRIDE
     84     {
     85         m_popup->m_windowRectInScreen = IntRect(rect);
     86         m_popup->widgetClient()->setWindowRect(m_popup->m_windowRectInScreen);
     87     }
     88 
     89     virtual IntRect rootViewToScreen(const IntRect& rect) const OVERRIDE
     90     {
     91         IntRect rectInScreen(rect);
     92         rectInScreen.move(m_popup->m_windowRectInScreen.x, m_popup->m_windowRectInScreen.y);
     93         return rectInScreen;
     94     }
     95 
     96     virtual void addMessageToConsole(LocalFrame*, MessageSource, MessageLevel, const String& message, unsigned lineNumber, const String&, const String&) OVERRIDE
     97     {
     98 #ifndef NDEBUG
     99         fprintf(stderr, "CONSOLE MESSSAGE:%u: %s\n", lineNumber, message.utf8().data());
    100 #endif
    101     }
    102 
    103     virtual void invalidateContentsAndRootView(const IntRect& paintRect) OVERRIDE
    104     {
    105         if (paintRect.isEmpty())
    106             return;
    107         m_popup->widgetClient()->didInvalidateRect(paintRect);
    108     }
    109 
    110     virtual void invalidateContentsForSlowScroll(const IntRect& updateRect) OVERRIDE
    111     {
    112         invalidateContentsAndRootView(updateRect);
    113     }
    114 
    115     virtual void scheduleAnimation() OVERRIDE
    116     {
    117         if (m_popup->isAcceleratedCompositingActive()) {
    118             ASSERT(m_popup->m_layerTreeView);
    119             m_popup->m_layerTreeView->setNeedsAnimate();
    120             return;
    121         }
    122         m_popup->widgetClient()->scheduleAnimation();
    123     }
    124 
    125     virtual WebScreenInfo screenInfo() const OVERRIDE
    126     {
    127         return m_popup->m_webView->client() ? m_popup->m_webView->client()->screenInfo() : WebScreenInfo();
    128     }
    129 
    130     virtual void* webView() const OVERRIDE
    131     {
    132         return m_popup->m_webView;
    133     }
    134 
    135     virtual FloatSize minimumWindowSize() const OVERRIDE
    136     {
    137         return FloatSize(0, 0);
    138     }
    139 
    140     virtual void setCursor(const Cursor& cursor) OVERRIDE
    141     {
    142         if (m_popup->m_webView->client())
    143             m_popup->m_webView->client()->didChangeCursor(WebCursorInfo(cursor));
    144     }
    145 
    146     virtual void needTouchEvents(bool needsTouchEvents) OVERRIDE
    147     {
    148         m_popup->widgetClient()->hasTouchEventHandlers(needsTouchEvents);
    149     }
    150 
    151     virtual GraphicsLayerFactory* graphicsLayerFactory() const OVERRIDE
    152     {
    153         return m_popup->m_webView->graphicsLayerFactory();
    154     }
    155 
    156     virtual void attachRootGraphicsLayer(GraphicsLayer* graphicsLayer) OVERRIDE
    157     {
    158         m_popup->setRootGraphicsLayer(graphicsLayer);
    159     }
    160 
    161     virtual void postAccessibilityNotification(AXObject* obj, AXObjectCache::AXNotification notification) OVERRIDE
    162     {
    163         WebLocalFrameImpl* frame = WebLocalFrameImpl::fromFrame(m_popup->m_popupClient->ownerElement().document().frame());
    164         if (obj && frame && frame->client())
    165             frame->client()->postAccessibilityEvent(WebAXObject(obj), static_cast<WebAXEvent>(notification));
    166 
    167         // FIXME: Delete these lines once Chromium only uses the frame client interface, above.
    168         if (obj && m_popup->m_webView->client())
    169             m_popup->m_webView->client()->postAccessibilityEvent(WebAXObject(obj), static_cast<WebAXEvent>(notification));
    170     }
    171 
    172     WebPagePopupImpl* m_popup;
    173 };
    174 
    175 class PagePopupFeaturesClient : public ContextFeaturesClient {
    176     virtual bool isEnabled(Document*, ContextFeatures::FeatureType, bool) OVERRIDE;
    177 };
    178 
    179 bool PagePopupFeaturesClient::isEnabled(Document*, ContextFeatures::FeatureType type, bool defaultValue)
    180 {
    181     if (type == ContextFeatures::PagePopup)
    182         return true;
    183     return defaultValue;
    184 }
    185 
    186 // WebPagePopupImpl ----------------------------------------------------------------
    187 
    188 WebPagePopupImpl::WebPagePopupImpl(WebWidgetClient* client)
    189     : m_widgetClient(client)
    190     , m_closing(false)
    191     , m_layerTreeView(0)
    192     , m_rootLayer(0)
    193     , m_rootGraphicsLayer(0)
    194     , m_isAcceleratedCompositingActive(false)
    195 {
    196     ASSERT(client);
    197 }
    198 
    199 WebPagePopupImpl::~WebPagePopupImpl()
    200 {
    201     ASSERT(!m_page);
    202 }
    203 
    204 bool WebPagePopupImpl::initialize(WebViewImpl* webView, PagePopupClient* popupClient, const IntRect&)
    205 {
    206     ASSERT(webView);
    207     ASSERT(popupClient);
    208     m_webView = webView;
    209     m_popupClient = popupClient;
    210 
    211     resize(m_popupClient->contentSize());
    212 
    213     if (!m_widgetClient || !initializePage())
    214         return false;
    215     m_widgetClient->show(WebNavigationPolicy());
    216     setFocus(true);
    217 
    218     return true;
    219 }
    220 
    221 bool WebPagePopupImpl::initializePage()
    222 {
    223     Page::PageClients pageClients;
    224     fillWithEmptyClients(pageClients);
    225     m_chromeClient = adoptPtr(new PagePopupChromeClient(this));
    226     pageClients.chromeClient = m_chromeClient.get();
    227 
    228     m_page = adoptPtrWillBeNoop(new Page(pageClients));
    229     m_page->settings().setScriptEnabled(true);
    230     m_page->settings().setAllowScriptsToCloseWindows(true);
    231     m_page->setDeviceScaleFactor(m_webView->deviceScaleFactor());
    232     m_page->settings().setDeviceSupportsTouch(m_webView->page()->settings().deviceSupportsTouch());
    233     // FIXME: Should we support enabling a11y while a popup is shown?
    234     m_page->settings().setAccessibilityEnabled(m_webView->page()->settings().accessibilityEnabled());
    235 
    236     provideContextFeaturesTo(*m_page, adoptPtr(new PagePopupFeaturesClient()));
    237     static FrameLoaderClient* emptyFrameLoaderClient = new EmptyFrameLoaderClient();
    238     RefPtrWillBeRawPtr<LocalFrame> frame = LocalFrame::create(emptyFrameLoaderClient, &m_page->frameHost(), 0);
    239     frame->setPagePopupOwner(m_popupClient->ownerElement());
    240     frame->setView(FrameView::create(frame.get()));
    241     frame->init();
    242     frame->view()->resize(m_popupClient->contentSize());
    243     frame->view()->setTransparent(false);
    244     if (AXObjectCache* cache = m_popupClient->ownerElement().document().existingAXObjectCache())
    245         cache->childrenChanged(&m_popupClient->ownerElement());
    246 
    247     ASSERT(frame->domWindow());
    248     DOMWindowPagePopup::install(*frame->domWindow(), m_popupClient);
    249     ASSERT(m_popupClient->ownerElement().document().existingAXObjectCache() == frame->document()->existingAXObjectCache());
    250 
    251     RefPtr<SharedBuffer> data = SharedBuffer::create();
    252     m_popupClient->writeDocument(data.get());
    253     frame->loader().load(FrameLoadRequest(0, blankURL(), SubstituteData(data, "text/html", "UTF-8", KURL(), ForceSynchronousLoad)));
    254     return true;
    255 }
    256 
    257 void WebPagePopupImpl::destroyPage()
    258 {
    259     if (!m_page)
    260         return;
    261 
    262     m_page->willBeDestroyed();
    263     m_page.clear();
    264 }
    265 
    266 AXObject* WebPagePopupImpl::rootAXObject()
    267 {
    268     if (!m_page || !m_page->mainFrame())
    269         return 0;
    270     Document* document = toLocalFrame(m_page->mainFrame())->document();
    271     if (!document)
    272         return 0;
    273     AXObjectCache* cache = document->axObjectCache();
    274     ASSERT(cache);
    275     return cache->getOrCreate(document->view());
    276 }
    277 
    278 void WebPagePopupImpl::setRootGraphicsLayer(GraphicsLayer* layer)
    279 {
    280     m_rootGraphicsLayer = layer;
    281     m_rootLayer = layer ? layer->platformLayer() : 0;
    282 
    283     setIsAcceleratedCompositingActive(layer);
    284     if (m_layerTreeView) {
    285         if (m_rootLayer) {
    286             m_layerTreeView->setRootLayer(*m_rootLayer);
    287         } else {
    288             m_layerTreeView->clearRootLayer();
    289         }
    290     }
    291 }
    292 
    293 void WebPagePopupImpl::setIsAcceleratedCompositingActive(bool enter)
    294 {
    295     if (m_isAcceleratedCompositingActive == enter)
    296         return;
    297 
    298     if (!enter) {
    299         m_isAcceleratedCompositingActive = false;
    300     } else if (m_layerTreeView) {
    301         m_isAcceleratedCompositingActive = true;
    302     } else {
    303         TRACE_EVENT0("blink", "WebPagePopupImpl::setIsAcceleratedCompositingActive(true)");
    304 
    305         m_widgetClient->initializeLayerTreeView();
    306         m_layerTreeView = m_widgetClient->layerTreeView();
    307         if (m_layerTreeView) {
    308             m_layerTreeView->setVisible(true);
    309             m_isAcceleratedCompositingActive = true;
    310             m_layerTreeView->setDeviceScaleFactor(m_widgetClient->deviceScaleFactor());
    311         } else {
    312             m_isAcceleratedCompositingActive = false;
    313         }
    314     }
    315 }
    316 
    317 WebSize WebPagePopupImpl::size()
    318 {
    319     return m_popupClient->contentSize();
    320 }
    321 
    322 void WebPagePopupImpl::beginFrame(const WebBeginFrameArgs& frameTime)
    323 {
    324     // FIXME: This should use frameTime.lastFrameTimeMonotonic but doing so
    325     // breaks tests.
    326     PageWidgetDelegate::animate(m_page.get(), monotonicallyIncreasingTime());
    327 }
    328 
    329 void WebPagePopupImpl::willCloseLayerTreeView()
    330 {
    331     setIsAcceleratedCompositingActive(false);
    332     m_layerTreeView = 0;
    333 }
    334 
    335 void WebPagePopupImpl::layout()
    336 {
    337     PageWidgetDelegate::layout(m_page.get());
    338 }
    339 
    340 void WebPagePopupImpl::paint(WebCanvas* canvas, const WebRect& rect)
    341 {
    342     if (!m_closing)
    343         PageWidgetDelegate::paint(m_page.get(), 0, canvas, rect, PageWidgetDelegate::Opaque);
    344 }
    345 
    346 void WebPagePopupImpl::resize(const WebSize& newSize)
    347 {
    348     m_windowRectInScreen = WebRect(m_windowRectInScreen.x, m_windowRectInScreen.y, newSize.width, newSize.height);
    349     m_widgetClient->setWindowRect(m_windowRectInScreen);
    350 
    351     if (m_page)
    352         toLocalFrame(m_page->mainFrame())->view()->resize(newSize);
    353     m_widgetClient->didInvalidateRect(WebRect(0, 0, newSize.width, newSize.height));
    354 }
    355 
    356 bool WebPagePopupImpl::handleKeyEvent(const WebKeyboardEvent&)
    357 {
    358     // The main WebView receives key events and forward them to this via handleKeyEvent().
    359     ASSERT_NOT_REACHED();
    360     return false;
    361 }
    362 
    363 bool WebPagePopupImpl::handleCharEvent(const WebKeyboardEvent&)
    364 {
    365     // The main WebView receives key events and forward them to this via handleKeyEvent().
    366     ASSERT_NOT_REACHED();
    367     return false;
    368 }
    369 
    370 bool WebPagePopupImpl::handleGestureEvent(const WebGestureEvent& event)
    371 {
    372     if (m_closing || !m_page || !m_page->mainFrame() || !toLocalFrame(m_page->mainFrame())->view())
    373         return false;
    374     LocalFrame& frame = *toLocalFrame(m_page->mainFrame());
    375     return frame.eventHandler().handleGestureEvent(PlatformGestureEventBuilder(frame.view(), event));
    376 }
    377 
    378 bool WebPagePopupImpl::handleInputEvent(const WebInputEvent& event)
    379 {
    380     if (m_closing)
    381         return false;
    382     return PageWidgetDelegate::handleInputEvent(m_page.get(), *this, event);
    383 }
    384 
    385 bool WebPagePopupImpl::handleKeyEvent(const PlatformKeyboardEvent& event)
    386 {
    387     if (m_closing || !m_page->mainFrame() || !toLocalFrame(m_page->mainFrame())->view())
    388         return false;
    389     return toLocalFrame(m_page->mainFrame())->eventHandler().keyEvent(event);
    390 }
    391 
    392 void WebPagePopupImpl::setFocus(bool enable)
    393 {
    394     if (!m_page)
    395         return;
    396     m_page->focusController().setFocused(enable);
    397     if (enable)
    398         m_page->focusController().setActive(true);
    399 }
    400 
    401 void WebPagePopupImpl::close()
    402 {
    403     m_closing = true;
    404     destroyPage(); // In case closePopup() was not called.
    405     m_widgetClient = 0;
    406     deref();
    407 }
    408 
    409 void WebPagePopupImpl::closePopup()
    410 {
    411     if (m_page) {
    412         toLocalFrame(m_page->mainFrame())->loader().stopAllLoaders();
    413         ASSERT(m_page->mainFrame()->domWindow());
    414         DOMWindowPagePopup::uninstall(*m_page->mainFrame()->domWindow());
    415     }
    416     m_closing = true;
    417 
    418     destroyPage();
    419 
    420     // m_widgetClient might be 0 because this widget might be already closed.
    421     if (m_widgetClient) {
    422         // closeWidgetSoon() will call this->close() later.
    423         m_widgetClient->closeWidgetSoon();
    424     }
    425 
    426     m_popupClient->didClosePopup();
    427 }
    428 
    429 void WebPagePopupImpl::compositeAndReadbackAsync(WebCompositeAndReadbackAsyncCallback* callback)
    430 {
    431     ASSERT(isAcceleratedCompositingActive());
    432     m_layerTreeView->compositeAndReadbackAsync(callback);
    433 }
    434 
    435 WebPoint WebPagePopupImpl::positionRelativeToOwner()
    436 {
    437     WebRect windowRect = m_webView->client()->rootWindowRect();
    438     return WebPoint(m_windowRectInScreen.x - windowRect.x, m_windowRectInScreen.y - windowRect.y);
    439 }
    440 
    441 // WebPagePopup ----------------------------------------------------------------
    442 
    443 WebPagePopup* WebPagePopup::create(WebWidgetClient* client)
    444 {
    445     if (!client)
    446         CRASH();
    447     // A WebPagePopupImpl instance usually has two references.
    448     //  - One owned by the instance itself. It represents the visible widget.
    449     //  - One owned by a WebViewImpl. It's released when the WebViewImpl ask the
    450     //    WebPagePopupImpl to close.
    451     // We need them because the closing operation is asynchronous and the widget
    452     // can be closed while the WebViewImpl is unaware of it.
    453     return adoptRef(new WebPagePopupImpl(client)).leakRef();
    454 }
    455 
    456 } // namespace blink
    457