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 59namespace blink { 60 61class PagePopupChromeClient : public EmptyChromeClient { 62 WTF_MAKE_NONCOPYABLE(PagePopupChromeClient); 63 WTF_MAKE_FAST_ALLOCATED; 64 65public: 66 explicit PagePopupChromeClient(WebPagePopupImpl* popup) 67 : m_popup(popup) 68 { 69 ASSERT(m_popup->widgetClient()); 70 } 71 72private: 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 175class PagePopupFeaturesClient : public ContextFeaturesClient { 176 virtual bool isEnabled(Document*, ContextFeatures::FeatureType, bool) OVERRIDE; 177}; 178 179bool 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 188WebPagePopupImpl::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 199WebPagePopupImpl::~WebPagePopupImpl() 200{ 201 ASSERT(!m_page); 202} 203 204bool 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 221bool 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 257void WebPagePopupImpl::destroyPage() 258{ 259 if (!m_page) 260 return; 261 262 m_page->willBeDestroyed(); 263 m_page.clear(); 264} 265 266AXObject* 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 278void 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 293void 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 317WebSize WebPagePopupImpl::size() 318{ 319 return m_popupClient->contentSize(); 320} 321 322void 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 329void WebPagePopupImpl::willCloseLayerTreeView() 330{ 331 setIsAcceleratedCompositingActive(false); 332 m_layerTreeView = 0; 333} 334 335void WebPagePopupImpl::layout() 336{ 337 PageWidgetDelegate::layout(m_page.get()); 338} 339 340void 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 346void 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 356bool 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 363bool 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 370bool 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 378bool WebPagePopupImpl::handleInputEvent(const WebInputEvent& event) 379{ 380 if (m_closing) 381 return false; 382 return PageWidgetDelegate::handleInputEvent(m_page.get(), *this, event); 383} 384 385bool 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 392void 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 401void WebPagePopupImpl::close() 402{ 403 m_closing = true; 404 destroyPage(); // In case closePopup() was not called. 405 m_widgetClient = 0; 406 deref(); 407} 408 409void 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 429void WebPagePopupImpl::compositeAndReadbackAsync(WebCompositeAndReadbackAsyncCallback* callback) 430{ 431 ASSERT(isAcceleratedCompositingActive()); 432 m_layerTreeView->compositeAndReadbackAsync(callback); 433} 434 435WebPoint 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 443WebPagePopup* 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