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