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