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