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