blob: bc592d27f0e00ab04bc54a074dbd36fb46d3addd [file]
/*
* Copyright (C) 2013 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "web/FullscreenController.h"
#include "core/dom/Document.h"
#include "core/dom/Fullscreen.h"
#include "core/frame/FrameView.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/PageScaleConstraintsSet.h"
#include "core/html/HTMLVideoElement.h"
#include "core/layout/LayoutFullScreen.h"
#include "public/platform/WebLayerTreeView.h"
#include "public/web/WebFrameClient.h"
#include "web/WebLocalFrameImpl.h"
#include "web/WebViewImpl.h"
namespace blink {
namespace {
WebFrameClient& webFrameClient(LocalFrame& frame) {
WebLocalFrameImpl* webFrame = WebLocalFrameImpl::fromFrame(frame);
DCHECK(webFrame);
DCHECK(webFrame->client());
return *webFrame->client();
}
} // anonymous namespace
std::unique_ptr<FullscreenController> FullscreenController::create(
WebViewImpl* webViewImpl) {
return WTF::wrapUnique(new FullscreenController(webViewImpl));
}
FullscreenController::FullscreenController(WebViewImpl* webViewImpl)
: m_webViewImpl(webViewImpl) {}
void FullscreenController::didEnterFullscreen() {
// |Browser::EnterFullscreenModeForTab()| can enter fullscreen without going
// through |Fullscreen::requestFullscreen()|, in which case there will be no
// fullscreen element. Do nothing.
if (m_state != State::EnteringFullscreen)
return;
updatePageScaleConstraints(false);
m_webViewImpl->setPageScaleFactor(1.0f);
if (m_webViewImpl->mainFrame()->isWebLocalFrame())
m_webViewImpl->mainFrame()->setScrollOffset(WebSize());
m_webViewImpl->setVisualViewportOffset(FloatPoint());
m_state = State::Fullscreen;
// Notify all local frames that we have entered fullscreen.
for (Frame* frame = m_webViewImpl->page()->mainFrame(); frame;
frame = frame->tree().traverseNext()) {
if (!frame->isLocalFrame())
continue;
if (Document* document = toLocalFrame(frame)->document()) {
if (Fullscreen* fullscreen = Fullscreen::fromIfExists(*document))
fullscreen->didEnterFullscreen();
}
}
// TODO(foolip): If the top level browsing context (main frame) ends up with
// no fullscreen element, exit fullscreen again to recover.
}
void FullscreenController::didExitFullscreen() {
// The browser process can exit fullscreen at any time, e.g. if the user
// presses Esc. After |Browser::EnterFullscreenModeForTab()|,
// |Browser::ExitFullscreenModeForTab()| will make it seem like we exit when
// not even in fullscreen. Do nothing.
if (m_state == State::Initial)
return;
updatePageScaleConstraints(true);
// We need to wait until style and layout are updated in order to properly
// restore scroll offsets since content may not be overflowing in the same way
// until they are.
m_state = State::NeedsScrollAndScaleRestore;
// Notify the topmost local frames that we have exited fullscreen.
// |Fullscreen::didExitFullscreen()| will take care of descendant frames.
for (Frame* frame = m_webViewImpl->page()->mainFrame(); frame;) {
Frame* nextFrame = frame->tree().traverseNext();
if (frame->isRemoteFrame()) {
frame = nextFrame;
continue;
}
DCHECK(frame->isLocalRoot());
if (Document* document = toLocalFrame(frame)->document()) {
if (Fullscreen* fullscreen = Fullscreen::fromIfExists(*document))
fullscreen->didExitFullscreen();
}
// Skip over all descendant frames.
while (nextFrame && nextFrame->tree().isDescendantOf(frame))
nextFrame = nextFrame->tree().traverseNext();
frame = nextFrame;
}
}
void FullscreenController::enterFullscreen(LocalFrame& frame) {
// If already fullscreen or exiting fullscreen, synchronously call
// |didEnterFullscreen()|. When exiting, the coming |didExitFullscren()| call
// will again notify all frames.
if (m_state == State::Fullscreen || m_state == State::ExitingFullscreen) {
State oldState = m_state;
m_state = State::EnteringFullscreen;
didEnterFullscreen();
m_state = oldState;
return;
}
// We need to store these values here rather than in |didEnterFullscreen()|
// since by the time the latter is called, a Resize has already occured,
// clamping the scroll offset. Don't save values if we're still waiting to
// restore a previous set. This can happen if we exit and quickly reenter
// fullscreen without performing a layout.
if (m_state == State::Initial) {
m_initialPageScaleFactor = m_webViewImpl->pageScaleFactor();
m_initialScrollOffset = m_webViewImpl->mainFrame()->isWebLocalFrame()
? m_webViewImpl->mainFrame()->getScrollOffset()
: WebSize();
m_initialVisualViewportOffset = m_webViewImpl->visualViewportOffset();
}
// If already entering fullscreen, just wait.
if (m_state == State::EnteringFullscreen)
return;
DCHECK(m_state == State::Initial ||
m_state == State::NeedsScrollAndScaleRestore);
webFrameClient(frame).enterFullscreen();
m_state = State::EnteringFullscreen;
}
void FullscreenController::exitFullscreen(LocalFrame& frame) {
// If not in fullscreen, ignore any attempt to exit. In particular, when
// entering fullscreen, allow the transition into fullscreen to complete. Note
// that the browser process is ultimately in control and can still exit
// fullscreen at any time.
if (m_state != State::Fullscreen)
return;
webFrameClient(frame).exitFullscreen();
m_state = State::ExitingFullscreen;
}
void FullscreenController::fullscreenElementChanged(Element* fromElement,
Element* toElement) {
DCHECK_NE(fromElement, toElement);
if (toElement) {
DCHECK(Fullscreen::isFullscreenElement(*toElement));
if (isHTMLVideoElement(*toElement)) {
HTMLVideoElement& videoElement = toHTMLVideoElement(*toElement);
videoElement.didEnterFullscreen();
// If the video uses overlay fullscreen mode, make the background
// transparent.
if (videoElement.usesOverlayFullscreenVideo() &&
m_webViewImpl->layerTreeView()) {
m_webViewImpl->layerTreeView()->setHasTransparentBackground(true);
}
}
}
if (fromElement) {
DCHECK(!Fullscreen::isFullscreenElement(*fromElement));
if (isHTMLVideoElement(*fromElement)) {
// If the video used overlay fullscreen mode, restore the transparency.
if (m_webViewImpl->layerTreeView()) {
m_webViewImpl->layerTreeView()->setHasTransparentBackground(
m_webViewImpl->isTransparent());
}
HTMLVideoElement& videoElement = toHTMLVideoElement(*fromElement);
videoElement.didExitFullscreen();
}
}
}
void FullscreenController::updateSize() {
DCHECK(m_webViewImpl->page());
if (m_state != State::Fullscreen)
return;
updatePageScaleConstraints(false);
// Traverse all local frames and notify the LayoutFullScreen object, if any.
for (Frame* frame = m_webViewImpl->page()->mainFrame(); frame;
frame = frame->tree().traverseNext()) {
if (!frame->isLocalFrame())
continue;
if (Document* document = toLocalFrame(frame)->document()) {
if (Fullscreen* fullscreen = Fullscreen::fromIfExists(*document)) {
if (LayoutFullScreen* layoutObject =
fullscreen->fullScreenLayoutObject())
layoutObject->updateStyle();
}
}
}
}
void FullscreenController::didUpdateLayout() {
if (m_state != State::NeedsScrollAndScaleRestore)
return;
m_webViewImpl->setPageScaleFactor(m_initialPageScaleFactor);
if (m_webViewImpl->mainFrame()->isWebLocalFrame())
m_webViewImpl->mainFrame()->setScrollOffset(WebSize(m_initialScrollOffset));
m_webViewImpl->setVisualViewportOffset(m_initialVisualViewportOffset);
m_state = State::Initial;
}
void FullscreenController::updatePageScaleConstraints(bool removeConstraints) {
PageScaleConstraints fullscreenConstraints;
if (!removeConstraints) {
fullscreenConstraints = PageScaleConstraints(1.0, 1.0, 1.0);
fullscreenConstraints.layoutSize = FloatSize(m_webViewImpl->size());
}
m_webViewImpl->pageScaleConstraintsSet().setFullscreenConstraints(
fullscreenConstraints);
m_webViewImpl->pageScaleConstraintsSet().computeFinalConstraints();
// Although we called |computedFinalConstraints()| above, the "final"
// constraints are not actually final. They are still subject to scale factor
// clamping by contents size. Normally they should be dirtied due to contents
// size mutation after layout, however the contents size is not guaranteed to
// mutate, and the scale factor may remain unclamped. Just fire the event
// again to ensure the final constraints pick up the latest contents size.
m_webViewImpl->didChangeContentsSize();
if (m_webViewImpl->mainFrameImpl() &&
m_webViewImpl->mainFrameImpl()->frameView())
m_webViewImpl->mainFrameImpl()->frameView()->setNeedsLayout();
m_webViewImpl->updateMainFrameLayoutSize();
}
} // namespace blink