1/*
2 * Copyright (C) 2006, 2007, 2008, 2009, 2011 Apple Inc. All rights reserved.
3 * Copyright (C) Research In Motion Limited 2009. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28
29#if ENABLE(FULLSCREEN_API)
30
31#include "FullScreenController.h"
32
33#include "Element.h"
34#include "FullScreenControllerClient.h"
35#include "IntRect.h"
36#include "MediaPlayerPrivateFullscreenWindow.h"
37#include "Timer.h"
38#include "WebCoreInstanceHandle.h"
39#include <wtf/RefPtr.h>
40
41using namespace WebCore;
42
43static const int kFullScreenAnimationDuration = 500; // milliseconds
44
45class FullScreenController::Private : public MediaPlayerPrivateFullscreenClient  {
46public:
47    Private(FullScreenController* controller, FullScreenControllerClient* client)
48        : m_controller(controller)
49        , m_client(client)
50        , m_originalHost(0)
51        , m_isFullScreen(false)
52        , m_isEnteringFullScreen(false)
53        , m_isExitingFullScreen(false)
54    {
55    }
56    virtual ~Private() { }
57
58    virtual LRESULT fullscreenClientWndProc(HWND, UINT, WPARAM, LPARAM);
59
60    FullScreenController* m_controller;
61    FullScreenControllerClient* m_client;
62    OwnPtr<MediaPlayerPrivateFullscreenWindow> m_fullScreenWindow;
63    OwnPtr<MediaPlayerPrivateFullscreenWindow> m_backgroundWindow;
64    IntRect m_fullScreenFrame;
65    IntRect m_originalFrame;
66    HWND m_originalHost;
67    bool m_isFullScreen;
68    bool m_isEnteringFullScreen;
69    bool m_isExitingFullScreen;
70};
71
72LRESULT FullScreenController::Private::fullscreenClientWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
73{
74    LRESULT lResult = 0;
75
76    switch (msg) {
77    case WM_MOVE:
78        m_fullScreenFrame.setX(LOWORD(lParam));
79        m_fullScreenFrame.setY(HIWORD(lParam));
80        break;
81    case WM_SIZE:
82        m_fullScreenFrame.setWidth(LOWORD(lParam));
83        m_fullScreenFrame.setHeight(HIWORD(lParam));
84        if (m_client->fullScreenClientWindow())
85            ::SetWindowPos(m_client->fullScreenClientWindow(), 0, 0, 0, m_fullScreenFrame.width(), m_fullScreenFrame.height(), SWP_NOREPOSITION  | SWP_NOMOVE);
86        break;
87    case WM_ACTIVATE:
88        // Because m_fullScreenWindow is a topmost window, we need to exit full screen explicitly when it's deactivated.
89        if (!wParam && m_fullScreenWindow && (hwnd == m_fullScreenWindow->hwnd()))
90            m_controller->exitFullScreen();
91        break;
92    case WM_KEYDOWN:
93        if (wParam == VK_ESCAPE) {
94            m_controller->exitFullScreen();
95            break;
96        }
97        FALLTHROUGH;
98    default:
99        lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
100    }
101
102    return lResult;
103}
104
105FullScreenController::FullScreenController(FullScreenControllerClient* client)
106    : m_private(adoptPtr(new FullScreenController::Private(this, client)))
107{
108    ASSERT_ARG(client, client);
109}
110
111FullScreenController::~FullScreenController()
112{
113}
114
115bool FullScreenController::isFullScreen() const
116{
117    return m_private->m_isFullScreen;
118}
119
120void FullScreenController::enterFullScreen()
121{
122    if (m_private->m_isFullScreen || m_private->m_isEnteringFullScreen)
123        return;
124    m_private->m_isFullScreen = true;
125    m_private->m_isEnteringFullScreen = true;
126
127    m_private->m_client->fullScreenClientSaveScrollPosition();
128
129    m_private->m_originalHost = m_private->m_client->fullScreenClientParentWindow();
130    RECT originalFrame = {0, 0, 0, 0};
131    ::GetClientRect(m_private->m_client->fullScreenClientWindow(), &originalFrame);
132    ::MapWindowPoints(m_private->m_client->fullScreenClientWindow(), m_private->m_originalHost, reinterpret_cast<LPPOINT>(&originalFrame), 2);
133    m_private->m_originalFrame = originalFrame;
134
135    ASSERT(!m_private->m_backgroundWindow);
136    m_private->m_backgroundWindow = adoptPtr(new MediaPlayerPrivateFullscreenWindow(m_private.get()));
137    m_private->m_backgroundWindow->createWindow(0);
138    ::AnimateWindow(m_private->m_backgroundWindow->hwnd(), kFullScreenAnimationDuration, AW_BLEND | AW_ACTIVATE);
139
140    m_private->m_client->fullScreenClientWillEnterFullScreen();
141    ASSERT(!m_private->m_fullScreenWindow);
142    m_private->m_fullScreenWindow = adoptPtr(new MediaPlayerPrivateFullscreenWindow(m_private.get()));
143    ASSERT(m_private->m_fullScreenWindow);
144    m_private->m_fullScreenWindow->createWindow(0);
145
146    m_private->m_client->fullScreenClientSetParentWindow(m_private->m_fullScreenWindow->hwnd());
147
148    IntRect viewFrame(IntPoint(), m_private->m_fullScreenFrame.size());
149    ::SetWindowPos(m_private->m_fullScreenWindow->hwnd(), HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
150    ::SetWindowPos(m_private->m_client->fullScreenClientWindow(), HWND_TOP, 0, 0, viewFrame.width(), viewFrame.height(), SWP_NOACTIVATE);
151
152    m_private->m_client->fullScreenClientDidEnterFullScreen();
153    m_private->m_client->fullScreenClientForceRepaint();
154}
155
156void FullScreenController::enterFullScreenRepaintCompleted()
157{
158    if (!m_private->m_isEnteringFullScreen)
159        return;
160    m_private->m_isEnteringFullScreen = false;
161
162    // Normally, when the background fullscreen window is animated in, the Windows taskbar will be hidden, but this doesn't always work for some reason.
163    // Setting the real fullscreen window to be a topmost window will force the taskbar to be hidden when we call AnimateWindow() below if it wasn't before.
164    ::SetWindowPos(m_private->m_fullScreenWindow->hwnd(), HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
165    ::AnimateWindow(m_private->m_fullScreenWindow->hwnd(), kFullScreenAnimationDuration, AW_BLEND | AW_ACTIVATE);
166}
167
168void FullScreenController::exitFullScreen()
169{
170    if (!m_private->m_isFullScreen || m_private->m_isExitingFullScreen)
171        return;
172    m_private->m_isFullScreen = false;
173    m_private->m_isExitingFullScreen = true;
174
175    ::AnimateWindow(m_private->m_fullScreenWindow->hwnd(), kFullScreenAnimationDuration, AW_HIDE | AW_BLEND);
176
177    m_private->m_client->fullScreenClientWillExitFullScreen();
178    m_private->m_client->fullScreenClientSetParentWindow(m_private->m_originalHost);
179    m_private->m_fullScreenWindow = nullptr;
180
181    ::SetWindowPos(m_private->m_client->fullScreenClientWindow(), 0, m_private->m_originalFrame.x(), m_private->m_originalFrame.y(), m_private->m_originalFrame.width(), m_private->m_originalFrame.height(), SWP_NOACTIVATE | SWP_NOZORDER);
182
183    m_private->m_client->fullScreenClientRestoreScrollPosition();
184    m_private->m_client->fullScreenClientDidExitFullScreen();
185    m_private->m_client->fullScreenClientForceRepaint();
186}
187
188void FullScreenController::exitFullScreenRepaintCompleted()
189{
190    if (!m_private->m_isExitingFullScreen)
191        return;
192    m_private->m_isExitingFullScreen = false;
193
194    ASSERT(m_private->m_backgroundWindow);
195    ::AnimateWindow(m_private->m_backgroundWindow->hwnd(), kFullScreenAnimationDuration, AW_HIDE | AW_BLEND);
196    m_private->m_backgroundWindow = nullptr;
197}
198
199void FullScreenController::repaintCompleted()
200{
201    if (m_private->m_isEnteringFullScreen)
202        enterFullScreenRepaintCompleted();
203    else if (m_private->m_isExitingFullScreen)
204        exitFullScreenRepaintCompleted();
205}
206
207void FullScreenController::close()
208{
209    if (!m_private->m_isFullScreen)
210        return;
211    m_private->m_isFullScreen = false;
212
213    m_private->m_client->fullScreenClientWillExitFullScreen();
214    m_private->m_client->fullScreenClientSetParentWindow(m_private->m_originalHost);
215    m_private->m_fullScreenWindow = nullptr;
216
217    m_private->m_client->fullScreenClientDidExitFullScreen();
218    ::SetWindowPos(m_private->m_client->fullScreenClientWindow(), 0, m_private->m_originalFrame.x(), m_private->m_originalFrame.y(), m_private->m_originalFrame.width(), m_private->m_originalFrame.height(), SWP_NOACTIVATE | SWP_NOZORDER);
219    ::RedrawWindow(m_private->m_client->fullScreenClientWindow(), 0, 0, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE | RDW_ALLCHILDREN);
220    m_private->m_backgroundWindow = nullptr;
221}
222#endif
223