blob: c85d8b98c742e7ee8b4b95a6d37d11eb433613a2 [file] [log] [blame]
* Copyright (C) 1999 Lars Knoll (
* (C) 1999 Antti Koivisto (
* (C) 2001 Dirk Mueller (
* (C) 2006 Alexey Proskuryakov (
* Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All
* rights reserved.
* Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved.
* (
* Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
* Copyright (C) 2013 Google Inc. All rights reserved.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* Library General Public License for more details.
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
#include "core/fullscreen/Fullscreen.h"
#include "base/macros.h"
#include "core/css/StyleEngine.h"
#include "core/dom/Document.h"
#include "core/dom/ElementTraversal.h"
#include "core/dom/events/Event.h"
#include "core/frame/HostsUsingFeatures.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/LocalFrameView.h"
#include "core/frame/Settings.h"
#include "core/frame/UseCounter.h"
#include "core/html/HTMLIFrameElement.h"
#include "core/html_element_type_helpers.h"
#include "core/input/EventHandler.h"
#include "core/inspector/ConsoleMessage.h"
#include "core/layout/LayoutBlockFlow.h"
#include "core/layout/LayoutFullScreen.h"
#include "core/page/ChromeClient.h"
#include "core/svg/SVGSVGElement.h"
#include "platform/ScopedOrientationChangeIndicator.h"
#include "platform/bindings/Microtask.h"
#include "platform/feature_policy/FeaturePolicy.h"
#include "public/platform/TaskType.h"
namespace blink {
namespace {
bool AllowedToUseFullscreen(const Frame* frame) {
// To determine whether a Document object |document| is allowed to use the
// feature indicated by attribute name |allowattribute|, run these steps:
// 1. If |document| has no browsing context, then return false.
if (!frame)
return false;
if (!IsSupportedInFeaturePolicy(FeaturePolicyFeature::kFullscreen)) {
// 2. If |document|'s browsing context is a top-level browsing context, then
// return true.
if (frame->IsMainFrame())
return true;
// 3. If |document|'s browsing context has a browsing context container that
// is an iframe element with an |allowattribute| attribute specified, and
// whose node document is allowed to use the feature indicated by
// |allowattribute|, then return true.
if (frame->Owner() && frame->Owner()->AllowFullscreen())
return AllowedToUseFullscreen(frame->Tree().Parent());
// 4. Return false.
return false;
// 2. If Feature Policy is enabled, return the policy for "fullscreen"
// feature.
return frame->IsFeatureEnabled(FeaturePolicyFeature::kFullscreen);
bool AllowedToRequestFullscreen(Document& document) {
// An algorithm is allowed to request fullscreen if one of the following is
// true:
// The algorithm is triggered by a user activation.
if (Frame::HasTransientUserActivation(document.GetFrame()))
return true;
// The algorithm is triggered by a user generated orientation change.
if (ScopedOrientationChangeIndicator::ProcessingOrientationChange()) {
return true;
String message = ExceptionMessages::FailedToExecute(
"requestFullscreen", "Element",
"API can only be initiated by a user gesture.");
ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel, message));
return false;
bool FullscreenIsSupported(const Document& document) {
LocalFrame* frame = document.GetFrame();
if (!frame)
return false;
// Fullscreen is supported if there is no previously-established user
// preference, security risk, or platform limitation.
return !document.GetSettings() ||
bool FullscreenElementReady(const Element& element) {
// A fullscreen element ready check for an element |element| returns true if
// all of the following are true, and false otherwise:
// |element| is in a document.
if (!element.isConnected())
return false;
// |element|'s node document is allowed to use the feature indicated by
// attribute name allowfullscreen.
if (!AllowedToUseFullscreen(element.GetDocument().GetFrame()))
return false;
// |element|'s node document's fullscreen element stack is either empty or its
// top element is an inclusive ancestor of |element|.
if (const Element* top_element =
Fullscreen::FullscreenElementFrom(element.GetDocument())) {
if (!top_element->contains(&element))
return false;
// |element| has no ancestor element whose local name is iframe and namespace
// is the HTML namespace.
if (Traversal<HTMLIFrameElement>::FirstAncestor(element))
return false;
// |element|'s node document's browsing context either has a browsing context
// container and the fullscreen element ready check returns true for
// |element|'s node document's browsing context's browsing context container,
// or it has no browsing context container.
if (const Element* owner = element.GetDocument().LocalOwner()) {
if (!FullscreenElementReady(*owner))
return false;
return true;
// step 4:
bool RequestFullscreenConditionsMet(Element& pending, Document& document) {
// |pending|'s namespace is the HTML namespace or |pending| is an SVG svg or
// MathML math element. Note: MathML is not supported.
if (!pending.IsHTMLElement() && !IsSVGSVGElement(pending))
return false;
// |pending| is not a dialog element.
if (IsHTMLDialogElement(pending))
return false;
// The fullscreen element ready check for |pending| returns false.
if (!FullscreenElementReady(pending))
return false;
// Fullscreen is supported.
if (!FullscreenIsSupported(document))
return false;
// This algorithm is allowed to request fullscreen.
if (!AllowedToRequestFullscreen(document))
return false;
return true;
// RequestFullscreenScope is allocated at the top of |RequestFullscreen()| and
// used to avoid synchronously changing any state within that method, by
// deferring changes in |DidEnterFullscreen()|.
class RequestFullscreenScope {
RequestFullscreenScope() {
running_request_fullscreen_ = true;
~RequestFullscreenScope() {
running_request_fullscreen_ = false;
static bool RunningRequestFullscreen() { return running_request_fullscreen_; }
static bool running_request_fullscreen_;
bool RequestFullscreenScope::running_request_fullscreen_ = false;
// Walks the frame tree and returns the first local ancestor frame, if any.
LocalFrame* NextLocalAncestor(Frame& frame) {
Frame* parent = frame.Tree().Parent();
if (!parent)
return nullptr;
if (parent->IsLocalFrame())
return ToLocalFrame(parent);
return NextLocalAncestor(*parent);
// Walks the document's frame tree and returns the document of the first local
// ancestor frame, if any.
Document* NextLocalAncestor(Document& document) {
LocalFrame* frame = document.GetFrame();
if (!frame)
return nullptr;
LocalFrame* next = NextLocalAncestor(*frame);
if (!next)
return nullptr;
return next->GetDocument();
// Helper to walk the ancestor chain and return the Document of the topmost
// local ancestor frame. Note that this is not the same as the topmost frame's
// Document, which might be unavailable in OOPIF scenarios. For example, with
// OOPIFs, when called on the bottom frame's Document in a A-B-C-B hierarchy in
// process B, this will skip remote frame C and return this frame: A-[B]-C-B.
Document& TopmostLocalAncestor(Document& document) {
if (Document* next = NextLocalAncestor(document))
return TopmostLocalAncestor(*next);
return document;
bool IsSimpleFullscreenDocument(Document& document) {
return Fullscreen::FullscreenElementStackSizeFrom(document) == 1;
HeapVector<Member<Document>> CollectDocumentsToUnfullscreen(Document& doc) {
// 1. Let |docs| be an ordered set consisting of |doc|.
HeapVector<Member<Document>> docs;
// 2. While true:
for (Document* lastDoc = &doc;;) {
// 2.1. Let |lastDoc| be |docs|'s last document.
// 2.2. Assert: |lastDoc|'s fullscreen element is not null.
// 2.3. If |lastDoc| is not a simple fullscreen document, break.
if (!IsSimpleFullscreenDocument(*lastDoc))
// 2.4. Let |container| be |lastDoc|'s browsing context container, if any,
// and otherwise break.
// OOPIF: Skip over remote frames, assuming that they have exactly one
// element in their fullscreen element stacks, thereby erring on the side of
// exiting fullscreen. TODO(alexmos): Deal with nested fullscreen cases, see
lastDoc = NextLocalAncestor(*lastDoc);
if (!lastDoc)
// 2.5. If |container|'s iframe fullscreen flag is set, break.
// TODO(foolip): Support the iframe fullscreen flag.
// 2.6. Append |container|'s node document to |docs|.
// 3. Return |docs|.
return docs;
void FireEvent(const AtomicString& type, Element* element, Document* document) {
// |Document::EnqueueAnimationFrameTask()| is used instead of a "list of
// pending fullscreen events", so only the body of the "run the fullscreen
// rendering steps" loop appears here:
// 3.1. Let |target| be |element| if |element| is connected and its node
// document is |document|, and otherwise let |target| be |document|.
EventTarget* target =
element->isConnected() && &element->GetDocument() == document
? static_cast<EventTarget*>(element)
: static_cast<EventTarget*>(document);
// 3.2. Fire an event named |type|, with its bubbles and composed attributes
// set to true, at |target|.
Event* event = Event::CreateBubble(type);
const AtomicString& AdjustEventType(const AtomicString& type,
Fullscreen::RequestType request_type) {
DCHECK(type == EventTypeNames::fullscreenchange ||
type == EventTypeNames::fullscreenerror);
if (request_type == Fullscreen::RequestType::kUnprefixed)
return type;
return type == EventTypeNames::fullscreenchange
? EventTypeNames::webkitfullscreenchange
: EventTypeNames::webkitfullscreenerror;
void EnqueueEvent(const AtomicString& type,
Element& element,
Document& document,
Fullscreen::RequestType request_type) {
const AtomicString& adjusted_type = AdjustEventType(type, request_type);
document.EnqueueAnimationFrameTask(WTF::Bind(FireEvent, adjusted_type,
} // anonymous namespace
const char* Fullscreen::SupplementName() {
return "Fullscreen";
Fullscreen& Fullscreen::From(Document& document) {
Fullscreen* fullscreen = FromIfExists(document);
if (!fullscreen) {
fullscreen = new Fullscreen(document);
Supplement<Document>::ProvideTo(document, SupplementName(), fullscreen);
return *fullscreen;
Fullscreen* Fullscreen::FromIfExistsSlow(Document& document) {
return static_cast<Fullscreen*>(
Supplement<Document>::From(document, SupplementName()));
Element* Fullscreen::FullscreenElementFrom(Document& document) {
if (Fullscreen* found = FromIfExists(document))
return found->FullscreenElement();
return nullptr;
size_t Fullscreen::FullscreenElementStackSizeFrom(Document& document) {
if (Fullscreen* found = FromIfExists(document))
return found->fullscreen_element_stack_.size();
return 0;
Element* Fullscreen::FullscreenElementForBindingFrom(TreeScope& scope) {
Element* element = FullscreenElementFrom(scope.GetDocument());
if (!element || !RuntimeEnabledFeatures::FullscreenUnprefixedEnabled())
return element;
// TODO(kochi): Once V0 code is removed, we can use the same logic for
// Document and ShadowRoot.
if (!scope.RootNode().IsShadowRoot()) {
// For Shadow DOM V0 compatibility: We allow returning an element in V0
// shadow tree, even though it leaks the Shadow DOM.
if (element->IsInV0ShadowTree()) {
return element;
} else if (!ToShadowRoot(scope.RootNode()).IsV1()) {
return nullptr;
return scope.AdjustedElement(*element);
bool Fullscreen::IsInFullscreenElementStack(const Element& element) {
const Fullscreen* found = FromIfExists(element.GetDocument());
if (!found)
return false;
for (size_t i = 0; i < found->fullscreen_element_stack_.size(); ++i) {
if (found->fullscreen_element_stack_[i].first.Get() == &element)
return true;
return false;
Fullscreen::Fullscreen(Document& document)
: Supplement<Document>(document),
full_screen_layout_object_(nullptr) {
Fullscreen::~Fullscreen() = default;
Document* Fullscreen::GetDocument() {
return ToDocument(LifecycleContext());
void Fullscreen::ContextDestroyed(ExecutionContext*) {
if (full_screen_layout_object_)
void Fullscreen::RequestFullscreen(Element& pending) {
// TODO(foolip): Make RequestType::Unprefixed the default when the unprefixed
// API is enabled.
RequestFullscreen(pending, RequestType::kPrefixed);
void Fullscreen::RequestFullscreen(Element& pending, RequestType request_type) {
RequestFullscreenScope scope;
// 1. Let |pending| be the context object.
// 2. Let |pendingDoc| be |pending|'s node document.
Document& document = pending.GetDocument();
// 3. Let |promise| be a new promise.
// TODO(foolip): Promises.
// 4. If |pendingDoc| is not fully active, then reject |promise| with a
// TypeError exception and return |promise|.
if (!document.IsActive() || !document.GetFrame())
bool for_cross_process_descendant =
request_type == RequestType::kPrefixedForCrossProcessDescendant;
// Use counters only need to be incremented in the process of the actual
// fullscreen element.
if (!for_cross_process_descendant) {
if (document.IsSecureContext()) {
UseCounter::Count(document, WebFeature::kFullscreenSecureOrigin);
} else {
UseCounter::Count(document, WebFeature::kFullscreenInsecureOrigin);
document, HostsUsingFeatures::Feature::kFullscreenInsecureHost);
// 5. Let |error| be false.
bool error = false;
// 6. If any of the following conditions are false, then set |error| to true:
// OOPIF: If |RequestFullscreen()| was already called in a descendant frame
// and passed the checks, do not check again here.
if (!for_cross_process_descendant &&
!RequestFullscreenConditionsMet(pending, document))
error = true;
// 7. Return |promise|, and run the remaining steps in parallel.
// 8. If |error| is false: Resize |pendingDoc|'s top-level browsing context's
// document's viewport's dimensions to match the dimensions of the screen of
// the output device. Optionally display a message how the end user can
// revert this.
if (!error) {
if (From(document).pending_requests_.size()) {
std::make_pair(&pending, request_type));
LocalFrame& frame = *document.GetFrame();
} else {
// Note: Although we are past the "in parallel" point, it's OK to continue
// synchronously because when |error| is true, |ContinueRequestFullscreen()|
// will only queue a task and return. This is indistinguishable from, e.g.,
// enqueueing a microtask to continue at step 9.
ContinueRequestFullscreen(document, pending, request_type,
true /* error */);
void Fullscreen::DidEnterFullscreen() {
if (!GetDocument())
// We may be called synchronously from within
// |FullscreenController::EnterFullscreen()| if we were already fullscreen,
// but must still not synchronously change the fullscreen element. Instead
// enqueue a microtask to continue.
if (RequestFullscreenScope::RunningRequestFullscreen()) {
WTF::Bind(&Fullscreen::DidEnterFullscreen, WrapPersistent(this)));
ElementStack requests;
for (const ElementStackEntry& request : requests) {
ContinueRequestFullscreen(*GetDocument(), *request.first, request.second,
false /* error */);
void Fullscreen::ContinueRequestFullscreen(Document& document,
Element& pending,
RequestType request_type,
bool error) {
// 9. If any of the following conditions are false, then set |error| to true:
// * |pending|'s node document is |pendingDoc|.
// * The fullscreen element ready check for |pending| returns true.
if (pending.GetDocument() != document || !FullscreenElementReady(pending))
error = true;
// 10. If |error| is true:
if (error) {
// 10.1. Append (fullscreenerror, |pending|) to |pendingDoc|'s list of
// pending fullscreen events.
EnqueueEvent(EventTypeNames::fullscreenerror, pending, document,
// 10.2. Reject |promise| with a TypeError exception and terminate these
// steps.
// 11. Let |fullscreenElements| be an ordered set initially consisting of
// |pending|.
HeapVector<Member<Element>> fullscreen_elements;
// 12. While the first element in |fullscreenElements| is in a nested browsing
// context: append its browsing context container to |fullscreenElements|.
// OOPIF: |fullscreenElements| will only contain elements for local ancestors,
// and remote ancestors will be processed in their respective processes. This
// preserves the spec's event firing order for local ancestors, but not for
// remote ancestors. However, that difference shouldn't be observable in
// practice: a fullscreenchange event handler would need to postMessage a
// frame in another renderer process, where the message should be queued up
// and processed after the IPC that dispatches fullscreenchange.
for (Frame* frame = pending.GetDocument().GetFrame(); frame;
frame = frame->Tree().Parent()) {
if (!frame->Owner() || !frame->Owner()->IsLocal())
Element* element = ToHTMLFrameOwnerElement(frame->Owner());
// 13. For each |element| in |fullscreenElements|:
for (Element* element : fullscreen_elements) {
// 13.1. Let |doc| be |element|'s node document.
Document& doc = element->GetDocument();
// 13.2. If |element| is |doc|'s fullscreen element, continue.
if (element == FullscreenElementFrom(doc))
// 13.3. If |element| is |pending| and |pending| is an iframe element, set
// |element|'s iframe fullscreen flag.
// TODO(foolip): Support the iframe fullscreen flag.
// 13.4. Fullscreen |element| within |doc|.
// TODO(foolip): Merge fullscreen element stack into top layer.
From(doc).PushFullscreenElementStack(*element, request_type);
// 13.5. Append (fullscreenchange, |element|) to |doc|'s list of pending
// fullscreen events.
EnqueueEvent(EventTypeNames::fullscreenchange, *element, doc, request_type);
// 14. Resolve |promise| with undefined.
void Fullscreen::FullyExitFullscreen(Document& document) {
// TODO(foolip): The spec used to have a first step saying "Let |doc| be the
// top-level browsing context's document" which was removed in
// Remove it here as well.
Document& doc = TopmostLocalAncestor(document);
// 1. If |document|'s fullscreen element is null, terminate these steps.
if (!FullscreenElementFrom(doc))
// 2. Unfullscreen elements whose fullscreen flag is set, within
// |document|'s top layer, except for |document|'s fullscreen element.
size_t stack_size = From(doc).fullscreen_element_stack_.size();
From(doc).fullscreen_element_stack_.EraseAt(0, stack_size - 1);
DCHECK_EQ(From(doc).fullscreen_element_stack_.size(), 1u);
// 3. Exit fullscreen |document|.
void Fullscreen::ExitFullscreen(Document& doc) {
// 1. Let |promise| be a new promise.
// TODO(foolip): Promises.
// 2. If |doc| is not fully active or |doc|'s fullscreen element is null, then
// reject |promise| with a TypeError exception and return |promise|.
if (!doc.IsActive() || !doc.GetFrame() || !FullscreenElementFrom(doc))
// 3. Let |resize| be false.
bool resize = false;
// 4. Let |docs| be the result of collecting documents to unfullscreen given
// |doc|.
HeapVector<Member<Document>> docs = CollectDocumentsToUnfullscreen(doc);
// 5. Let |topLevelDoc| be |doc|'s top-level browsing context's active
// document.
// OOPIF: Let |topLevelDoc| be the topmost local ancestor instead. If the main
// frame is in another process, we will still fully exit fullscreen even
// though that's wrong if the main frame was in nested fullscreen.
// TODO(alexmos): Deal with nested fullscreen cases, see
Document& top_level_doc = TopmostLocalAncestor(doc);
// 6. If |topLevelDoc| is in |docs|, and it is a simple fullscreen document,
// then set |doc| to |topLevelDoc| and |resize| to true.
// Note: |doc| is not set here, but |doc| will be the topmost local ancestor
// in |Fullscreen::ContinueExitFullscreen| if |resize| is true.
if (!docs.IsEmpty() && docs.back() == &top_level_doc &&
IsSimpleFullscreenDocument(top_level_doc)) {
resize = true;
// 7. Return |promise|, and run the remaining steps in parallel.
// 8. If |resize| is true, resize |doc|'s viewport to its "normal" dimensions.
if (resize) {
LocalFrame& frame = *doc.GetFrame();
} else {
// Note: We are past the "in parallel" point, and |ContinueExitFullscreen()|
// will change script-observable state (document.fullscreenElement)
// synchronously, so we have to continue asynchronously.
ContinueExitFullscreen, WrapWeakPersistent(&doc), false /* resize */));
void Fullscreen::DidExitFullscreen() {
ContinueExitFullscreen(GetDocument(), true /* resize */);
void Fullscreen::ContinueExitFullscreen(Document* doc, bool resize) {
if (!doc || !doc->IsActive() || !doc->GetFrame())
if (resize) {
// See comment for step 6.
DCHECK_EQ(nullptr, NextLocalAncestor(*doc));
// 9. If |doc|'s fullscreen element is null, then resolve |promise| with
// undefined and terminate these steps.
if (!FullscreenElementFrom(*doc))
// 10. Let |exitDocs| be the result of collecting documents to unfullscreen
// given |doc|.
HeapVector<Member<Document>> exit_docs = CollectDocumentsToUnfullscreen(*doc);
// 11. Let |descendantDocs| be an ordered set consisting of |doc|'s
// descendant browsing contexts' documents whose fullscreen element is
// non-null, if any, in tree order.
HeapVector<Member<Document>> descendant_docs;
for (Frame* descendant = doc->GetFrame()->Tree().FirstChild(); descendant;
descendant = descendant->Tree().TraverseNext(doc->GetFrame())) {
if (!descendant->IsLocalFrame())
if (FullscreenElementFrom(*ToLocalFrame(descendant)->GetDocument()))
// 12. For each |exitDoc| in |exitDocs|:
for (Document* exit_doc : exit_docs) {
Fullscreen& fullscreen = From(*exit_doc);
ElementStack& stack = fullscreen.fullscreen_element_stack_;
// 12.1. Append (fullscreenchange, |exitDoc|'s fullscreen element) to
// |exitDoc|'s list of pending fullscreen events.
EnqueueEvent(EventTypeNames::fullscreenchange, *stack.back().first,
*exit_doc, stack.back().second);
// 12.2. If |resize| is true, unfullscreen |exitDoc|.
// 12.3. Otherwise, unfullscreen |exitDoc|'s fullscreen element.
if (resize) {
while (!stack.IsEmpty())
} else {
// 13. For each |descendantDoc| in |descendantDocs|:
for (Document* descendant_doc : descendant_docs) {
Fullscreen& fullscreen = From(*descendant_doc);
ElementStack& stack = fullscreen.fullscreen_element_stack_;
// 13.1. Append (fullscreenchange, |descendantDoc|'s fullscreen element) to
// |descendantDoc|'s list of pending fullscreen events.
EnqueueEvent(EventTypeNames::fullscreenchange, *stack.back().first,
*descendant_doc, stack.back().second);
// 13.2. Unfullscreen |descendantDoc|.
while (!stack.IsEmpty())
// 14. Resolve |promise| with undefined.
bool Fullscreen::FullscreenEnabled(Document& document) {
// The fullscreenEnabled attribute's getter must return true if the context
// object is allowed to use the feature indicated by attribute name
// allowfullscreen and fullscreen is supported, and false otherwise.
return AllowedToUseFullscreen(document.GetFrame()) &&
void Fullscreen::SetFullScreenLayoutObject(LayoutFullScreen* layout_object) {
if (layout_object == full_screen_layout_object_)
if (layout_object && saved_placeholder_computed_style_) {
} else if (layout_object && full_screen_layout_object_ &&
full_screen_layout_object_->Placeholder()) {
LayoutBlockFlow* placeholder = full_screen_layout_object_->Placeholder();
if (full_screen_layout_object_)
full_screen_layout_object_ = layout_object;
void Fullscreen::FullScreenLayoutObjectDestroyed() {
full_screen_layout_object_ = nullptr;
void Fullscreen::ElementRemoved(Element& node) {
DCHECK_EQ(GetDocument(), &node.GetDocument());
// |Fullscreen::ElementRemoved()| is called for each removed element, so only
// the body of the spec "removing steps" loop appears here:
// 2.1. If |node| is its node document's fullscreen element, exit fullscreen
// that document.
if (FullscreenElement() == &node) {
// 2.2. Otherwise, unfullscreen |node| within its node document.
for (size_t i = 0; i < fullscreen_element_stack_.size(); ++i) {
if (fullscreen_element_stack_[i].first.Get() == &node) {
// Note: |node| was not in the fullscreen element stack.
void Fullscreen::PopFullscreenElementStack() {
Element* old_element = FullscreenElement();
// Note: |requestType| is only used if |FullscreenElement()| is non-null.
RequestType request_type = fullscreen_element_stack_.IsEmpty()
? RequestType::kUnprefixed
: fullscreen_element_stack_.back().second;
FullscreenElementChanged(old_element, FullscreenElement(), request_type);
void Fullscreen::PushFullscreenElementStack(Element& element,
RequestType request_type) {
Element* old_element = FullscreenElement();
fullscreen_element_stack_.push_back(std::make_pair(&element, request_type));
FullscreenElementChanged(old_element, &element, request_type);
void Fullscreen::FullscreenElementChanged(Element* old_element,
Element* new_element,
RequestType new_request_type) {
DCHECK_NE(old_element, new_element);
if (!GetDocument())
if (full_screen_layout_object_)
if (old_element) {
DCHECK_NE(old_element, FullscreenElement());
if (new_element) {
DCHECK_EQ(new_element, FullscreenElement());
// OOPIF: For RequestType::PrefixedForCrossProcessDescendant, |toElement| is
// the iframe element for the out-of-process frame that contains the
// fullscreen element. Hence, it must match :-webkit-full-screen-ancestor.
if (new_request_type == RequestType::kPrefixedForCrossProcessDescendant) {
// Create a placeholder block for the fullscreen element, to keep the page
// from reflowing when the element is removed from the normal flow. Only do
// this for a LayoutBox, as only a box will have a frameRect. The
// placeholder will be created in setFullScreenLayoutObject() during layout.
LayoutObject* layout_object = new_element->GetLayoutObject();
bool should_create_placeholder = layout_object && layout_object->IsBox();
if (should_create_placeholder) {
saved_placeholder_frame_rect_ = ToLayoutBox(layout_object)->FrameRect();
saved_placeholder_computed_style_ =
if (new_element != GetDocument()->documentElement()) {
layout_object, layout_object ? layout_object->Parent() : nullptr,
if (LocalFrame* frame = GetDocument()->GetFrame()) {
// TODO(foolip): Synchronize hover state changes with animation frames.
frame->GetChromeClient().FullscreenElementChanged(old_element, new_element);
if (!RuntimeEnabledFeatures::RootLayerScrollingEnabled()) {
// Fullscreen status affects scroll paint properties through
// LocalFrameView::UserInputScrollable().
if (LocalFrameView* frame_view = frame->View())
// Descendant frames may have been inert because their owner iframes were
// outside of fullscreen element. SetIsInert recurses through subframes to
// propagate the inert bit as needed.
frame->SetIsInert(GetDocument()->LocalOwner() &&
// TODO(foolip): This should not call |UpdateStyleAndLayoutTree()|.
// Any element not contained by the fullscreen element is inert (see
// |Node::IsInert()|), so changing the fullscreen element will typically
// change the inertness of most elements. Clear the entire cache.
void Fullscreen::Trace(blink::Visitor* visitor) {
} // namespace blink