| Index: third_party/WebKit/Source/core/dom/Fullscreen.cpp
|
| diff --git a/third_party/WebKit/Source/core/dom/Fullscreen.cpp b/third_party/WebKit/Source/core/dom/Fullscreen.cpp
|
| index f5d0797c9511f0ad501ad0659358d828cd1efae8..2b7200658fd9f9944efb788e3f256d6fea6e9e5a 100644
|
| --- a/third_party/WebKit/Source/core/dom/Fullscreen.cpp
|
| +++ b/third_party/WebKit/Source/core/dom/Fullscreen.cpp
|
| @@ -162,17 +162,26 @@ bool FullscreenElementReady(const Element& element) {
|
| return true;
|
| }
|
|
|
| -bool IsPrefixed(const AtomicString& type) {
|
| - return type == EventTypeNames::webkitfullscreenchange ||
|
| - type == EventTypeNames::webkitfullscreenerror;
|
| -}
|
| +// https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen 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;
|
|
|
| -Event* CreateEvent(const AtomicString& type, EventTarget& target) {
|
| - EventInit initializer;
|
| - initializer.setBubbles(IsPrefixed(type));
|
| - Event* event = Event::Create(type, initializer);
|
| - event->SetTarget(&target);
|
| - return event;
|
| + // 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;
|
| }
|
|
|
| // Walks the frame tree and returns the first local ancestor frame, if any.
|
| @@ -191,7 +200,7 @@ Document* NextLocalAncestor(Document& document) {
|
| LocalFrame* frame = document.GetFrame();
|
| if (!frame)
|
| return nullptr;
|
| - LocalFrame* next = NextLocalAncestor(*document.GetFrame());
|
| + LocalFrame* next = NextLocalAncestor(*frame);
|
| if (!next)
|
| return nullptr;
|
| DCHECK(next->GetDocument());
|
| @@ -209,34 +218,94 @@ Document& TopmostLocalAncestor(Document& document) {
|
| return document;
|
| }
|
|
|
| -// Helper to find the browsing context container in |doc| that embeds the
|
| -// |descendant| Document, possibly through multiple levels of nesting. This
|
| -// works even in OOPIF scenarios like A-B-A, where there may be remote frames
|
| -// in between |doc| and |descendant|.
|
| -HTMLFrameOwnerElement* FindContainerForDescendant(const Document& doc,
|
| - const Document& descendant) {
|
| - Frame* frame = descendant.GetFrame();
|
| - while (frame->Tree().Parent() != doc.GetFrame())
|
| - frame = frame->Tree().Parent();
|
| - return ToHTMLFrameOwnerElement(frame->Owner());
|
| +// https://fullscreen.spec.whatwg.org/#collect-documents-to-unfullscreen
|
| +HeapVector<Member<Document>> CollectDocumentsToUnfullscreen(
|
| + Document& doc,
|
| + Fullscreen::ExitType exit_type) {
|
| + DCHECK(Fullscreen::FullscreenElementFrom(doc));
|
| +
|
| + // 1. If |doc|'s top layer consists of more than a single element that has
|
| + // its fullscreen flag set, return the empty set.
|
| + // TODO(foolip): See TODO in |fullyExitFullscreen()|.
|
| + if (exit_type != Fullscreen::ExitType::kFully &&
|
| + Fullscreen::FullscreenElementStackSizeFrom(doc) > 1)
|
| + return HeapVector<Member<Document>>();
|
| +
|
| + // 2. Let |docs| be an ordered set consisting of |doc|.
|
| + HeapVector<Member<Document>> docs;
|
| + docs.push_back(&doc);
|
| +
|
| + // 3. While |docs|'s last document ...
|
| + //
|
| + // 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
|
| + // https://crbug.com/617369.
|
| + for (Document* document = NextLocalAncestor(doc); document;
|
| + document = NextLocalAncestor(*document)) {
|
| + // ... has a browsing context container whose node document's top layer
|
| + // consists of a single element that has its fullscreen flag set and does
|
| + // not have its iframe fullscreen flag set (if any), append that node
|
| + // document to |docs|.
|
| + // TODO(foolip): Support the iframe fullscreen flag.
|
| + // https://crbug.com/644695
|
| + if (Fullscreen::FullscreenElementStackSizeFrom(*document) == 1)
|
| + docs.push_back(document);
|
| + else
|
| + break;
|
| + }
|
| +
|
| + // 4. Return |docs|.
|
| + return docs;
|
| }
|
|
|
| -// Fullscreen status affects scroll paint properties through
|
| -// LocalFrameView::userInputScrollable().
|
| -void SetNeedsPaintPropertyUpdate(Document* document) {
|
| - if (!RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled() ||
|
| - RuntimeEnabledFeatures::rootLayerScrollingEnabled())
|
| - return;
|
| +// Creates a non-bubbling event with |document| as its target.
|
| +Event* CreateEvent(const AtomicString& type, Document& document) {
|
| + DCHECK(type == EventTypeNames::fullscreenchange ||
|
| + type == EventTypeNames::fullscreenerror);
|
|
|
| - if (!document)
|
| - return;
|
| + Event* event = Event::Create(type);
|
| + event->SetTarget(&document);
|
| + return event;
|
| +}
|
|
|
| - LocalFrame* frame = document->GetFrame();
|
| - if (!frame)
|
| - return;
|
| +// Creates a bubbling event with |element| as its target. If |element| is not
|
| +// connected to |document|, then |document| is used as the target instead.
|
| +Event* CreatePrefixedEvent(const AtomicString& type,
|
| + Element& element,
|
| + Document& document) {
|
| + DCHECK(type == EventTypeNames::webkitfullscreenchange ||
|
| + type == EventTypeNames::webkitfullscreenerror);
|
| +
|
| + Event* event = Event::CreateBubble(type);
|
| + if (element.isConnected() && element.GetDocument() == document)
|
| + event->SetTarget(&element);
|
| + else
|
| + event->SetTarget(&document);
|
| + return event;
|
| +}
|
|
|
| - if (LocalFrameView* frame_view = frame->View())
|
| - frame_view->SetNeedsPaintPropertyUpdate();
|
| +Event* CreateChangeEvent(Document& document,
|
| + Element& element,
|
| + Fullscreen::RequestType request_type) {
|
| + if (request_type == Fullscreen::RequestType::kUnprefixed)
|
| + return CreateEvent(EventTypeNames::fullscreenchange, document);
|
| + return CreatePrefixedEvent(EventTypeNames::webkitfullscreenchange, element,
|
| + document);
|
| +}
|
| +
|
| +Event* CreateErrorEvent(Document& document,
|
| + Element& element,
|
| + Fullscreen::RequestType request_type) {
|
| + if (request_type == Fullscreen::RequestType::kUnprefixed)
|
| + return CreateEvent(EventTypeNames::fullscreenerror, document);
|
| + return CreatePrefixedEvent(EventTypeNames::webkitfullscreenerror, element,
|
| + document);
|
| +}
|
| +
|
| +void DispatchEvents(const HeapVector<Member<Event>>& events) {
|
| + for (Event* event : events)
|
| + event->target()->DispatchEvent(event);
|
| }
|
|
|
| } // anonymous namespace
|
| @@ -266,6 +335,12 @@ Element* Fullscreen::FullscreenElementFrom(Document& document) {
|
| 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())
|
| @@ -287,28 +362,6 @@ Element* Fullscreen::FullscreenElementForBindingFrom(TreeScope& scope) {
|
| return scope.AdjustedElement(*element);
|
| }
|
|
|
| -Element* Fullscreen::CurrentFullScreenElementFrom(Document& document) {
|
| - if (Fullscreen* found = FromIfExists(document))
|
| - return found->CurrentFullScreenElement();
|
| - return nullptr;
|
| -}
|
| -
|
| -Element* Fullscreen::CurrentFullScreenElementForBindingFrom(
|
| - Document& document) {
|
| - Element* element = CurrentFullScreenElementFrom(document);
|
| - if (!element || !RuntimeEnabledFeatures::fullscreenUnprefixedEnabled())
|
| - return element;
|
| -
|
| - // For Shadow DOM V0 compatibility: We allow returning an element in V0 shadow
|
| - // tree, even though it leaks the Shadow DOM.
|
| - if (element->IsInV0ShadowTree()) {
|
| - UseCounter::Count(document,
|
| - UseCounter::kDocumentFullscreenElementInV0Shadow);
|
| - return element;
|
| - }
|
| - return document.AdjustedElement(*element);
|
| -}
|
| -
|
| bool Fullscreen::IsInFullscreenElementStack(const Element& element) {
|
| const Fullscreen* found = FromIfExists(element.GetDocument());
|
| if (!found)
|
| @@ -323,42 +376,40 @@ bool Fullscreen::IsInFullscreenElementStack(const Element& element) {
|
| Fullscreen::Fullscreen(Document& document)
|
| : Supplement<Document>(document),
|
| ContextLifecycleObserver(&document),
|
| - full_screen_layout_object_(nullptr),
|
| - event_queue_timer_(
|
| - TaskRunnerHelper::Get(TaskType::kUnthrottled, &document),
|
| - this,
|
| - &Fullscreen::EventQueueTimerFired),
|
| - for_cross_process_descendant_(false) {
|
| + full_screen_layout_object_(nullptr) {
|
| document.SetHasFullscreenSupplement();
|
| }
|
|
|
| Fullscreen::~Fullscreen() {}
|
|
|
| -inline Document* Fullscreen::GetDocument() {
|
| +Document* Fullscreen::GetDocument() {
|
| return ToDocument(LifecycleContext());
|
| }
|
|
|
| void Fullscreen::ContextDestroyed(ExecutionContext*) {
|
| - event_queue_.clear();
|
| -
|
| if (full_screen_layout_object_)
|
| full_screen_layout_object_->Destroy();
|
|
|
| - current_full_screen_element_ = nullptr;
|
| + pending_requests_.clear();
|
| fullscreen_element_stack_.clear();
|
| }
|
|
|
| // https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen
|
| -void Fullscreen::RequestFullscreen(Element& element) {
|
| +void Fullscreen::RequestFullscreen(Element& pending) {
|
| // TODO(foolip): Make RequestType::Unprefixed the default when the unprefixed
|
| // API is enabled. https://crbug.com/383813
|
| - RequestFullscreen(element, RequestType::kPrefixed, false);
|
| + RequestFullscreen(pending, RequestType::kPrefixed);
|
| }
|
|
|
| -void Fullscreen::RequestFullscreen(Element& element,
|
| - RequestType request_type,
|
| - bool for_cross_process_descendant) {
|
| - Document& document = element.GetDocument();
|
| +void Fullscreen::RequestFullscreen(Element& pending, RequestType request_type) {
|
| + Document& document = pending.GetDocument();
|
| +
|
| + // Ignore this call if the document is not in a live frame.
|
| + if (!document.IsActive() || !document.GetFrame())
|
| + return;
|
| +
|
| + bool for_cross_process_descendant =
|
| + request_type == RequestType::kPrefixedForCrossProcessDescendant;
|
|
|
| // Use counters only need to be incremented in the process of the actual
|
| // fullscreen element.
|
| @@ -372,379 +423,356 @@ void Fullscreen::RequestFullscreen(Element& element,
|
| }
|
| }
|
|
|
| - // Ignore this call if the document is not in a live frame.
|
| - if (!document.IsActive() || !document.GetFrame())
|
| - return;
|
| + // 1. Let |pending| be the context object.
|
|
|
| - // If |element| is on top of |doc|'s fullscreen element stack, terminate these
|
| - // substeps.
|
| - if (&element == FullscreenElementFrom(document))
|
| - return;
|
| + // 2. Let |error| be false.
|
| + bool error = false;
|
|
|
| - do {
|
| - // 1. If any of the following conditions are false, then terminate these
|
| - // steps and queue a task to fire an event named fullscreenerror with its
|
| - // bubbles attribute set to true on the context object's node document:
|
| + // 3. Let |promise| be a new promise.
|
| + // TODO(foolip): Promises. https://crbug.com/644637
|
|
|
| - // |element|'s namespace is the HTML namespace or |element| is an SVG
|
| - // svg or MathML math element.
|
| - // Note: MathML is not supported.
|
| - if (!element.IsHTMLElement() && !isSVGSVGElement(element))
|
| - break;
|
| + // 4. 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;
|
| +
|
| + // 5. Return |promise|, and run the remaining steps in parallel.
|
| + // TODO(foolip): Promises. https://crbug.com/644637
|
| +
|
| + // 6. If |error| is false: Resize pending'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()) {
|
| + UseCounter::Count(document,
|
| + UseCounter::kFullscreenRequestWithPendingElement);
|
| + }
|
|
|
| // TODO(foolip): In order to reinstate the hierarchy restrictions in the
|
| // spec, something has to prevent dialog elements from moving within top
|
| // layer. Either disallowing fullscreen for dialog elements entirely or just
|
| // preventing dialog elements from simultaneously being fullscreen and modal
|
| // are good candidates. See https://github.com/whatwg/fullscreen/pull/91
|
| - if (isHTMLDialogElement(element)) {
|
| + if (isHTMLDialogElement(pending)) {
|
| UseCounter::Count(document,
|
| UseCounter::kRequestFullscreenForDialogElement);
|
| - if (element.IsInTopLayer()) {
|
| + if (pending.IsInTopLayer()) {
|
| UseCounter::Count(
|
| document, UseCounter::kRequestFullscreenForDialogElementInTopLayer);
|
| }
|
| }
|
|
|
| - // The fullscreen element ready check for |element| returns true.
|
| - if (!FullscreenElementReady(element))
|
| - break;
|
| -
|
| - // Fullscreen is supported.
|
| - if (!FullscreenIsSupported(document))
|
| - break;
|
| -
|
| - // This algorithm is allowed to request fullscreen.
|
| - // OOPIF: If |forCrossProcessDescendant| is true, requestFullscreen was
|
| - // already called on a descendant element in another process, and
|
| - // getting here means that it was already allowed to request fullscreen.
|
| - if (!for_cross_process_descendant && !AllowedToRequestFullscreen(document))
|
| - break;
|
| -
|
| - // 2. Let doc be element's node document. (i.e. "this")
|
| -
|
| - // 3. Let docs be all doc's ancestor browsing context's documents (if any)
|
| - // and doc.
|
| - //
|
| - // For OOPIF scenarios, |docs| will only contain documents 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.
|
| - HeapDeque<Member<Document>> docs;
|
| - for (Document* doc = &document; doc; doc = NextLocalAncestor(*doc))
|
| - docs.push_front(doc);
|
| -
|
| - // 4. For each document in docs, run these substeps:
|
| - HeapDeque<Member<Document>>::iterator current = docs.begin(),
|
| - following = docs.begin();
|
| -
|
| - do {
|
| - ++following;
|
| -
|
| - // 1. Let following document be the document after document in docs, or
|
| - // null if there is no such document.
|
| - Document* current_doc = *current;
|
| - Document* following_doc = following != docs.end() ? *following : nullptr;
|
| -
|
| - // 2. If following document is null, push context object on document's
|
| - // fullscreen element stack, and queue a task to fire an event named
|
| - // fullscreenchange with its bubbles attribute set to true on the
|
| - // document.
|
| - if (!following_doc) {
|
| - From(*current_doc).PushFullscreenElementStack(element, request_type);
|
| - From(document).EnqueueChangeEvent(*current_doc, request_type);
|
| - continue;
|
| - }
|
| -
|
| - // 3. Otherwise, if document's fullscreen element stack is either empty or
|
| - // its top element is not following document's browsing context container,
|
| - Element* top_element = FullscreenElementFrom(*current_doc);
|
| - HTMLFrameOwnerElement* following_owner =
|
| - FindContainerForDescendant(*current_doc, *following_doc);
|
| - if (!top_element || top_element != following_owner) {
|
| - // ...push following document's browsing context container on document's
|
| - // fullscreen element stack, and queue a task to fire an event named
|
| - // fullscreenchange with its bubbles attribute set to true on document.
|
| - From(*current_doc)
|
| - .PushFullscreenElementStack(*following_owner, request_type);
|
| - From(document).EnqueueChangeEvent(*current_doc, request_type);
|
| - continue;
|
| - }
|
| -
|
| - // 4. Otherwise, do nothing for this document. It stays the same.
|
| - } while (++current != docs.end());
|
| -
|
| - From(document).for_cross_process_descendant_ = for_cross_process_descendant;
|
| -
|
| - // 5. Return, and run the remaining steps asynchronously.
|
| - // 6. Optionally, perform some animation.
|
| - if (From(document).pending_fullscreen_element_) {
|
| - UseCounter::Count(document,
|
| - UseCounter::kFullscreenRequestWithPendingElement);
|
| - }
|
| - From(document).pending_fullscreen_element_ = &element;
|
| - document.GetFrame()->GetChromeClient().EnterFullscreen(
|
| - *document.GetFrame());
|
| -
|
| - // 7. Optionally, display a message indicating how the user can exit
|
| - // displaying the context object fullscreen.
|
| - return;
|
| - } while (false);
|
| -
|
| - From(document).EnqueueErrorEvent(element, request_type);
|
| + From(document).pending_requests_.push_back(
|
| + std::make_pair(&pending, request_type));
|
| + LocalFrame& frame = *document.GetFrame();
|
| + frame.GetChromeClient().EnterFullscreen(frame);
|
| + } else {
|
| + EnqueueTaskForRequest(document, pending, request_type, true);
|
| + }
|
| }
|
|
|
| -// https://fullscreen.spec.whatwg.org/#fully-exit-fullscreen
|
| -void Fullscreen::FullyExitFullscreen(Document& document) {
|
| - // To fully exit fullscreen, run these steps:
|
| -
|
| - // 1. Let |doc| be the top-level browsing context's document.
|
| - //
|
| - // Since the top-level browsing context's document might be unavailable in
|
| - // OOPIF scenarios (i.e., when the top frame is remote), this actually uses
|
| - // the Document of the topmost local ancestor frame. Without OOPIF, this
|
| - // will be the top frame's document. With OOPIF, each renderer process for
|
| - // the current page will separately call fullyExitFullscreen to cover all
|
| - // local frames in each process.
|
| - Document& doc = TopmostLocalAncestor(document);
|
| -
|
| - // 2. If |doc|'s fullscreen element stack is empty, terminate these steps.
|
| - if (!FullscreenElementFrom(doc))
|
| +void Fullscreen::DidEnterFullscreen() {
|
| + if (!GetDocument())
|
| return;
|
|
|
| - // 3. Remove elements from |doc|'s fullscreen element stack until only the top
|
| - // element is left.
|
| - size_t stack_size = From(doc).fullscreen_element_stack_.size();
|
| - From(doc).fullscreen_element_stack_.erase(0, stack_size - 1);
|
| - DCHECK_EQ(From(doc).fullscreen_element_stack_.size(), 1u);
|
| -
|
| - // 4. Act as if the exitFullscreen() method was invoked on |doc|.
|
| - ExitFullscreen(doc);
|
| + ElementStack requests;
|
| + requests.swap(pending_requests_);
|
| + for (const ElementStackEntry& request : requests) {
|
| + EnqueueTaskForRequest(*GetDocument(), *request.first, request.second,
|
| + false);
|
| + }
|
| }
|
|
|
| -// https://fullscreen.spec.whatwg.org/#exit-fullscreen
|
| -void Fullscreen::ExitFullscreen(Document& document) {
|
| - // The exitFullscreen() method must run these steps:
|
| +void Fullscreen::EnqueueTaskForRequest(Document& document,
|
| + Element& pending,
|
| + RequestType request_type,
|
| + bool error) {
|
| + // 7. As part of the next animation frame task, run these substeps:
|
| + document.EnqueueAnimationFrameTask(
|
| + WTF::Bind(&RunTaskForRequest, WrapPersistent(&document),
|
| + WrapPersistent(&pending), request_type, error));
|
| +}
|
|
|
| - // Ignore this call if the document is not in a live frame.
|
| - if (!document.IsActive() || !document.GetFrame())
|
| +void Fullscreen::RunTaskForRequest(Document* document,
|
| + Element* element,
|
| + RequestType request_type,
|
| + bool error) {
|
| + DCHECK(document);
|
| + DCHECK(document->IsActive());
|
| + DCHECK(document->GetFrame());
|
| + DCHECK(element);
|
| +
|
| + Document& pending_doc = *document;
|
| + Element& pending = *element;
|
| +
|
| + // TODO(foolip): Spec something like: If |pending|'s node document is not
|
| + // |pendingDoc|, then set |error| to true.
|
| + // https://github.com/whatwg/fullscreen/issues/33
|
| + if (pending.GetDocument() != pending_doc)
|
| + error = true;
|
| +
|
| + // 7.1. If either |error| is true or the fullscreen element ready check for
|
| + // |pending| returns false, fire an event named fullscreenerror on
|
| + // |pending|'s node document, reject |promise| with a TypeError exception,
|
| + // and terminate these steps.
|
| + // TODO(foolip): Promises. https://crbug.com/644637
|
| + if (error || !FullscreenElementReady(pending)) {
|
| + Event* event = CreateErrorEvent(pending_doc, pending, request_type);
|
| + event->target()->DispatchEvent(event);
|
| return;
|
| + }
|
|
|
| - // 1. Let doc be the context object. (i.e. "this")
|
| - // 2. If doc's fullscreen element stack is empty, terminate these steps.
|
| - if (!FullscreenElementFrom(document))
|
| - return;
|
| + // 7.2. Let |fullscreenElements| be an ordered set initially consisting of
|
| + // |pending|.
|
| + HeapDeque<Member<Element>> fullscreen_elements;
|
| + fullscreen_elements.push_back(pending);
|
|
|
| - // 3. Let descendants be all the doc's descendant browsing context's documents
|
| - // with a non-empty fullscreen element stack (if any), ordered so that the
|
| - // child of the doc is last and the document furthest away from the doc is
|
| - // first.
|
| - HeapDeque<Member<Document>> descendants;
|
| - for (Frame* descendant = document.GetFrame()->Tree().TraverseNext();
|
| - descendant; descendant = descendant->Tree().TraverseNext()) {
|
| - if (!descendant->IsLocalFrame())
|
| + // 7.3. While the first element in |fullscreenElements| is in a nested
|
| + // browsing context, prepend 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())
|
| continue;
|
| - DCHECK(ToLocalFrame(descendant)->GetDocument());
|
| - if (FullscreenElementFrom(*ToLocalFrame(descendant)->GetDocument()))
|
| - descendants.push_front(ToLocalFrame(descendant)->GetDocument());
|
| + Element* element = ToHTMLFrameOwnerElement(frame->Owner());
|
| + fullscreen_elements.push_front(element);
|
| }
|
|
|
| - // 4. For each descendant in descendants, empty descendant's fullscreen
|
| - // element stack, and queue a task to fire an event named fullscreenchange
|
| - // with its bubbles attribute set to true on descendant.
|
| - for (auto& descendant : descendants) {
|
| - DCHECK(descendant);
|
| - RequestType request_type =
|
| - From(*descendant).fullscreen_element_stack_.back().second;
|
| - From(*descendant).ClearFullscreenElementStack();
|
| - From(document).EnqueueChangeEvent(*descendant, request_type);
|
| - }
|
| + // 7.4. Let |eventDocs| be an empty list.
|
| + // Note: For prefixed requests, the event target is an element, so instead
|
| + // let |events| be a list of events to dispatch.
|
| + HeapVector<Member<Event>> events;
|
|
|
| - // 5. While doc is not null, run these substeps:
|
| - Element* new_top = nullptr;
|
| - for (Document* current_doc = &document; current_doc;) {
|
| - RequestType request_type =
|
| - From(*current_doc).fullscreen_element_stack_.back().second;
|
| -
|
| - // 1. Pop the top element of doc's fullscreen element stack.
|
| - From(*current_doc).PopFullscreenElementStack();
|
| -
|
| - // If doc's fullscreen element stack is non-empty and the element now at
|
| - // the top is either not in a document or its node document is not doc,
|
| - // repeat this substep.
|
| - new_top = FullscreenElementFrom(*current_doc);
|
| - if (new_top &&
|
| - (!new_top->isConnected() || new_top->GetDocument() != current_doc))
|
| - continue;
|
| + // 7.5. For each |element| in |fullscreenElements|, in order, run these
|
| + // subsubsteps:
|
| + for (Element* element : fullscreen_elements) {
|
| + // 7.5.1. Let |doc| be |element|'s node document.
|
| + Document& doc = element->GetDocument();
|
|
|
| - // 2. Queue a task to fire an event named fullscreenchange with its bubbles
|
| - // attribute set to true on doc.
|
| - From(document).EnqueueChangeEvent(*current_doc, request_type);
|
| -
|
| - // 3. If doc's fullscreen element stack is empty and doc's browsing context
|
| - // has a browsing context container, set doc to that browsing context
|
| - // container's node document.
|
| - //
|
| - // OOPIF: If browsing context container's document is in another
|
| - // process, keep moving up the ancestor chain and looking for a
|
| - // browsing context container with a local document.
|
| - // TODO(alexmos): Deal with nested fullscreen cases, see
|
| - // https://crbug.com/617369.
|
| - if (!new_top) {
|
| - current_doc = NextLocalAncestor(*current_doc);
|
| + // 7.5.2. If |element| is |doc|'s fullscreen element, terminate these
|
| + // subsubsteps.
|
| + if (element == FullscreenElementFrom(doc))
|
| continue;
|
| - }
|
|
|
| - // 4. Otherwise, set doc to null.
|
| - current_doc = nullptr;
|
| - }
|
| + // 7.5.3. Otherwise, append |doc| to |eventDocs|.
|
| + events.push_back(CreateChangeEvent(doc, *element, request_type));
|
|
|
| - // 6. Return, and run the remaining steps asynchronously.
|
| - // 7. Optionally, perform some animation.
|
| + // 7.5.4. If |element| is |pending| and |pending| is an iframe element,
|
| + // set |element|'s iframe fullscreen flag.
|
| + // TODO(foolip): Support the iframe fullscreen flag.
|
| + // https://crbug.com/644695
|
|
|
| - // Only exit fullscreen mode if the fullscreen element stack is empty.
|
| - if (!new_top) {
|
| - document.GetFrame()->GetChromeClient().ExitFullscreen(*document.GetFrame());
|
| - return;
|
| + // 7.5.5. Fullscreen |element| within |doc|.
|
| + // TODO(foolip): Merge fullscreen element stack into top layer.
|
| + // https://crbug.com/627790
|
| + From(doc).PushFullscreenElementStack(*element, request_type);
|
| }
|
|
|
| - // Otherwise, enter fullscreen for the fullscreen element stack's top element.
|
| - From(document).pending_fullscreen_element_ = new_top;
|
| - From(document).DidEnterFullscreen();
|
| -}
|
| + // 7.6. For each |doc| in |eventDocs|, in order, fire an event named
|
| + // fullscreenchange on |doc|.
|
| + DispatchEvents(events);
|
|
|
| -// https://fullscreen.spec.whatwg.org/#dom-document-fullscreenenabled
|
| -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()) &&
|
| - FullscreenIsSupported(document);
|
| + // 7.7. Fulfill |promise| with undefined.
|
| + // TODO(foolip): Promises. https://crbug.com/644637
|
| }
|
|
|
| -void Fullscreen::DidEnterFullscreen() {
|
| - if (!GetDocument()->IsActive() || !GetDocument()->GetFrame())
|
| - return;
|
| +// https://fullscreen.spec.whatwg.org/#fully-exit-fullscreen
|
| +void Fullscreen::FullyExitFullscreen(Document& document) {
|
| + // 1. If |document|'s fullscreen element is null, terminate these steps.
|
|
|
| - // Start the timer for events enqueued by |requestFullscreen()|. The hover
|
| - // state update is scheduled first so that it's done when the events fire.
|
| - GetDocument()->GetFrame()->GetEventHandler().ScheduleHoverStateUpdate();
|
| - event_queue_timer_.StartOneShot(0, BLINK_FROM_HERE);
|
| + // 2. Unfullscreen elements whose fullscreen flag is set, within
|
| + // |document|'s top layer, except for |document|'s fullscreen element.
|
|
|
| - Element* element = pending_fullscreen_element_.Release();
|
| - if (!element)
|
| - return;
|
| + // 3. Exit fullscreen |document|.
|
|
|
| - if (current_full_screen_element_ == element)
|
| - return;
|
| + // TODO(foolip): Change the spec. To remove elements from |document|'s top
|
| + // layer as in step 2 could leave descendant frames in fullscreen. It may work
|
| + // to give the "exit fullscreen" algorithm a |fully| flag that's used in the
|
| + // animation frame task after exit. Here, retain the old behavior of fully
|
| + // exiting fullscreen for the topmost local ancestor:
|
| + ExitFullscreen(TopmostLocalAncestor(document), ExitType::kFully);
|
| +}
|
|
|
| - if (!element->isConnected() || &element->GetDocument() != GetDocument()) {
|
| - // The element was removed or has moved to another document since the
|
| - // |requestFullscreen()| call. Exit fullscreen again to recover.
|
| - // TODO(foolip): Fire a fullscreenerror event. This is currently difficult
|
| - // because the fullscreenchange event has already been enqueued and possibly
|
| - // even fired. https://crbug.com/402376
|
| - LocalFrame& frame = *GetDocument()->GetFrame();
|
| - frame.GetChromeClient().ExitFullscreen(frame);
|
| +// https://fullscreen.spec.whatwg.org/#exit-fullscreen
|
| +void Fullscreen::ExitFullscreen(Document& doc, ExitType exit_type) {
|
| + if (!doc.IsActive() || !doc.GetFrame())
|
| return;
|
| - }
|
|
|
| - if (full_screen_layout_object_)
|
| - full_screen_layout_object_->UnwrapLayoutObject();
|
| + // 1. Let |promise| be a new promise.
|
| + // 2. If |doc|'s fullscreen element is null, reject |promise| with a
|
| + // TypeError exception, and return |promise|.
|
| + // TODO(foolip): Promises. https://crbug.com/644637
|
| + if (!FullscreenElementFrom(doc))
|
| + return;
|
|
|
| - Element* previous_element = current_full_screen_element_;
|
| - current_full_screen_element_ = element;
|
| -
|
| - // Create a placeholder block for a the full-screen 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 = current_full_screen_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_ =
|
| - ComputedStyle::Clone(layout_object->StyleRef());
|
| - }
|
| + // 3. Let |resize| be false.
|
| + bool resize = false;
|
|
|
| - // TODO(alexmos): When |m_forCrossProcessDescendant| is true, some of
|
| - // this layout work has already been done in another process, so it should
|
| - // not be necessary to repeat it here.
|
| - if (current_full_screen_element_ != GetDocument()->documentElement()) {
|
| - LayoutFullScreen::WrapLayoutObject(
|
| - layout_object, layout_object ? layout_object->Parent() : 0,
|
| - GetDocument());
|
| - }
|
| + // 4. Let |docs| be the result of collecting documents to unfullscreen given
|
| + // |doc|.
|
| + HeapVector<Member<Document>> docs =
|
| + CollectDocumentsToUnfullscreen(doc, exit_type);
|
|
|
| - // When |m_forCrossProcessDescendant| is true, m_currentFullScreenElement
|
| - // corresponds to the HTMLFrameOwnerElement for the out-of-process iframe
|
| - // that contains the actual fullscreen element. Hence, it must also set
|
| - // the ContainsFullScreenElement flag (so that it gains the
|
| - // -webkit-full-screen-ancestor style).
|
| - if (for_cross_process_descendant_) {
|
| - DCHECK(current_full_screen_element_->IsFrameOwnerElement());
|
| - DCHECK(ToHTMLFrameOwnerElement(current_full_screen_element_)
|
| - ->ContentFrame()
|
| - ->IsRemoteFrame());
|
| - current_full_screen_element_->SetContainsFullScreenElement(true);
|
| + // 5. Let |topLevelDoc| be |doc|'s top-level browsing context's 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
|
| + // https://crbug.com/617369.
|
| + Document& top_level_doc = TopmostLocalAncestor(doc);
|
| +
|
| + // 6. If |topLevelDoc| is in |docs|, set |resize| to true.
|
| + if (!docs.IsEmpty() && docs.back() == &top_level_doc)
|
| + resize = true;
|
| +
|
| + // 7. Return |promise|, and run the remaining steps in parallel.
|
| + // TODO(foolip): Promises. https://crbug.com/644637
|
| +
|
| + // Note: |ExitType::Fully| is only used together with the topmost local
|
| + // ancestor in |fullyExitFullscreen()|, and so implies that |resize| is true.
|
| + // This would change if matching the spec for "fully exit fullscreen".
|
| + if (exit_type == ExitType::kFully)
|
| + DCHECK(resize);
|
| +
|
| + // 8. If |resize| is true, resize |topLevelDoc|'s viewport to its "normal"
|
| + // dimensions.
|
| + if (resize) {
|
| + LocalFrame& frame = *doc.GetFrame();
|
| + frame.GetChromeClient().ExitFullscreen(frame);
|
| + } else {
|
| + EnqueueTaskForExit(doc, exit_type);
|
| }
|
| +}
|
|
|
| - current_full_screen_element_
|
| - ->SetContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(true);
|
| +void Fullscreen::DidExitFullscreen() {
|
| + if (!GetDocument())
|
| + return;
|
|
|
| - GetDocument()->GetStyleEngine().EnsureUAStyleForFullscreen();
|
| - current_full_screen_element_->PseudoStateChanged(
|
| - CSSSelector::kPseudoFullScreen);
|
| + DCHECK_EQ(GetDocument(), &TopmostLocalAncestor(*GetDocument()));
|
|
|
| - // FIXME: This should not call updateStyleAndLayoutTree.
|
| - GetDocument()->UpdateStyleAndLayoutTree();
|
| + EnqueueTaskForExit(*GetDocument(), ExitType::kFully);
|
| +}
|
|
|
| - GetDocument()->GetFrame()->GetChromeClient().FullscreenElementChanged(
|
| - previous_element, element);
|
| +void Fullscreen::EnqueueTaskForExit(Document& document, ExitType exit_type) {
|
| + // 9. As part of the next animation frame task, run these substeps:
|
| + document.EnqueueAnimationFrameTask(
|
| + WTF::Bind(&RunTaskForExit, WrapPersistent(&document), exit_type));
|
| }
|
|
|
| -void Fullscreen::DidExitFullscreen() {
|
| - if (!GetDocument()->IsActive() || !GetDocument()->GetFrame())
|
| +void Fullscreen::RunTaskForExit(Document* document, ExitType exit_type) {
|
| + DCHECK(document);
|
| + DCHECK(document->IsActive());
|
| + DCHECK(document->GetFrame());
|
| +
|
| + Document& doc = *document;
|
| +
|
| + if (!FullscreenElementFrom(doc))
|
| return;
|
|
|
| - // Start the timer for events enqueued by |exitFullscreen()|. The hover state
|
| - // update is scheduled first so that it's done when the events fire.
|
| - GetDocument()->GetFrame()->GetEventHandler().ScheduleHoverStateUpdate();
|
| - event_queue_timer_.StartOneShot(0, BLINK_FROM_HERE);
|
| + // 9.1. Let |exitDocs| be the result of collecting documents to unfullscreen
|
| + // given |doc|.
|
|
|
| - // If fullscreen was canceled by the browser, e.g. if the user pressed Esc,
|
| - // then |exitFullscreen()| was never called. Let |fullyExitFullscreen()| clear
|
| - // the fullscreen element stack and fire any events as necessary.
|
| - // TODO(foolip): Remove this when state changes and events are synchronized
|
| - // with animation frames. https://crbug.com/402376
|
| - FullyExitFullscreen(*GetDocument());
|
| + // 9.2. If |resize| is true and |topLevelDoc| is not in |exitDocs|, fully
|
| + // exit fullscreen |topLevelDoc|, reject promise with a TypeError exception,
|
| + // and terminate these steps.
|
|
|
| - if (!current_full_screen_element_)
|
| - return;
|
| + // TODO(foolip): See TODO in |fullyExitFullscreen()|. Instead of using "fully
|
| + // exit fullscreen" in step 9.2 (which is async), give "exit fullscreen" a
|
| + // |fully| flag which is always true if |resize| was true.
|
|
|
| - if (for_cross_process_descendant_)
|
| - current_full_screen_element_->SetContainsFullScreenElement(false);
|
| + HeapVector<Member<Document>> exit_docs =
|
| + CollectDocumentsToUnfullscreen(doc, exit_type);
|
|
|
| - current_full_screen_element_
|
| - ->SetContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
|
| + // 9.3. If |exitDocs| is the empty set, append |doc| to |exitDocs|.
|
| + if (exit_docs.IsEmpty())
|
| + exit_docs.push_back(&doc);
|
|
|
| - if (full_screen_layout_object_)
|
| - LayoutFullScreenItem(full_screen_layout_object_).UnwrapLayoutObject();
|
| + // 9.4. If |exitDocs|'s last document has a browsing context container,
|
| + // append that browsing context container's node document to |exitDocs|.
|
| + //
|
| + // 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
|
| + // https://crbug.com/617369.
|
| + if (Document* document = NextLocalAncestor(*exit_docs.back()))
|
| + exit_docs.push_back(document);
|
| +
|
| + // 9.5. Let |descendantDocs| be an ordered set consisting of |doc|'s
|
| + // descendant browsing contexts' documents whose fullscreen element is
|
| + // non-null, if any, in *reverse* tree order.
|
| + HeapDeque<Member<Document>> descendant_docs;
|
| + for (Frame* descendant = doc.GetFrame()->Tree().FirstChild(); descendant;
|
| + descendant = descendant->Tree().TraverseNext(doc.GetFrame())) {
|
| + if (!descendant->IsLocalFrame())
|
| + continue;
|
| + DCHECK(ToLocalFrame(descendant)->GetDocument());
|
| + if (FullscreenElementFrom(*ToLocalFrame(descendant)->GetDocument()))
|
| + descendant_docs.push_front(ToLocalFrame(descendant)->GetDocument());
|
| + }
|
|
|
| - GetDocument()->GetStyleEngine().EnsureUAStyleForFullscreen();
|
| - current_full_screen_element_->PseudoStateChanged(
|
| - CSSSelector::kPseudoFullScreen);
|
| - Element* previous_element = current_full_screen_element_;
|
| - current_full_screen_element_ = nullptr;
|
| + // Note: For prefixed requests, the event target is an element, so let
|
| + // |events| be a list of events to dispatch.
|
| + HeapVector<Member<Event>> events;
|
| +
|
| + // 9.6. For each |descendantDoc| in |descendantDocs|, in order, unfullscreen
|
| + // |descendantDoc|.
|
| + for (Document* descendant_doc : descendant_docs) {
|
| + Fullscreen& fullscreen = From(*descendant_doc);
|
| + ElementStack& stack = fullscreen.fullscreen_element_stack_;
|
| + DCHECK(!stack.IsEmpty());
|
| + events.push_back(CreateChangeEvent(*descendant_doc, *stack.back().first,
|
| + stack.back().second));
|
| + while (!stack.IsEmpty())
|
| + fullscreen.PopFullscreenElementStack();
|
| + }
|
|
|
| - for_cross_process_descendant_ = false;
|
| + // 9.7. For each |exitDoc| in |exitDocs|, in order, unfullscreen |exitDoc|'s
|
| + // fullscreen element.
|
| + for (Document* exit_doc : exit_docs) {
|
| + Fullscreen& fullscreen = From(*exit_doc);
|
| + ElementStack& stack = fullscreen.fullscreen_element_stack_;
|
| + DCHECK(!stack.IsEmpty());
|
| + events.push_back(
|
| + CreateChangeEvent(*exit_doc, *stack.back().first, stack.back().second));
|
| + fullscreen.PopFullscreenElementStack();
|
| +
|
| + // TODO(foolip): See TODO in |fullyExitFullscreen()|.
|
| + if (exit_doc == &doc && exit_type == ExitType::kFully) {
|
| + while (!stack.IsEmpty())
|
| + fullscreen.PopFullscreenElementStack();
|
| + }
|
| + }
|
|
|
| - GetDocument()->GetFrame()->GetChromeClient().FullscreenElementChanged(
|
| - previous_element, nullptr);
|
| + // 9.8. For each |descendantDoc| in |descendantDocs|, in order, fire an
|
| + // event named fullscreenchange on |descendantDoc|.
|
| + // 9.9. For each |exitDoc| in |exitDocs|, in order, fire an event named
|
| + // fullscreenchange on |exitDoc|.
|
| + DispatchEvents(events);
|
| +
|
| + // 9.10. Fulfill |promise| with undefined.
|
| + // TODO(foolip): Promises. https://crbug.com/644637
|
| +}
|
| +
|
| +// https://fullscreen.spec.whatwg.org/#dom-document-fullscreenenabled
|
| +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()) &&
|
| + FullscreenIsSupported(document);
|
| }
|
|
|
| void Fullscreen::SetFullScreenLayoutObject(LayoutFullScreen* layout_object) {
|
| @@ -774,56 +802,9 @@ void Fullscreen::FullScreenLayoutObjectDestroyed() {
|
| full_screen_layout_object_ = nullptr;
|
| }
|
|
|
| -void Fullscreen::EnqueueChangeEvent(Document& document,
|
| - RequestType request_type) {
|
| - Event* event;
|
| - if (request_type == RequestType::kUnprefixed) {
|
| - event = CreateEvent(EventTypeNames::fullscreenchange, document);
|
| - } else {
|
| - DCHECK(document.HasFullscreenSupplement());
|
| - Fullscreen& fullscreen = From(document);
|
| - EventTarget* target = fullscreen.FullscreenElement();
|
| - if (!target)
|
| - target = fullscreen.CurrentFullScreenElement();
|
| - if (!target)
|
| - target = &document;
|
| - event = CreateEvent(EventTypeNames::webkitfullscreenchange, *target);
|
| - }
|
| - event_queue_.push_back(event);
|
| - // NOTE: The timer is started in didEnterFullscreen/didExitFullscreen.
|
| -}
|
| -
|
| -void Fullscreen::EnqueueErrorEvent(Element& element, RequestType request_type) {
|
| - Event* event;
|
| - if (request_type == RequestType::kUnprefixed)
|
| - event = CreateEvent(EventTypeNames::fullscreenerror, element.GetDocument());
|
| - else
|
| - event = CreateEvent(EventTypeNames::webkitfullscreenerror, element);
|
| - event_queue_.push_back(event);
|
| - event_queue_timer_.StartOneShot(0, BLINK_FROM_HERE);
|
| -}
|
| -
|
| -void Fullscreen::EventQueueTimerFired(TimerBase*) {
|
| - HeapDeque<Member<Event>> event_queue;
|
| - event_queue_.Swap(event_queue);
|
| -
|
| - while (!event_queue.IsEmpty()) {
|
| - Event* event = event_queue.TakeFirst();
|
| - Node* target = event->target()->ToNode();
|
| -
|
| - // If the element was removed from our tree, also message the
|
| - // documentElement.
|
| - if (!target->isConnected() && GetDocument()->documentElement()) {
|
| - DCHECK(IsPrefixed(event->type()));
|
| - event_queue.push_back(
|
| - CreateEvent(event->type(), *GetDocument()->documentElement()));
|
| - }
|
| -
|
| - target->DispatchEvent(event);
|
| - }
|
| -}
|
| -
|
| 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:
|
|
|
| @@ -845,36 +826,107 @@ void Fullscreen::ElementRemoved(Element& node) {
|
| // Note: |node| was not in the fullscreen element stack.
|
| }
|
|
|
| -void Fullscreen::ClearFullscreenElementStack() {
|
| - if (fullscreen_element_stack_.IsEmpty())
|
| - return;
|
| -
|
| - fullscreen_element_stack_.clear();
|
| -
|
| - SetNeedsPaintPropertyUpdate(GetDocument());
|
| -}
|
| -
|
| void Fullscreen::PopFullscreenElementStack() {
|
| - if (fullscreen_element_stack_.IsEmpty())
|
| - return;
|
| + DCHECK(!fullscreen_element_stack_.IsEmpty());
|
|
|
| + Element* previous_element = FullscreenElement();
|
| fullscreen_element_stack_.pop_back();
|
|
|
| - SetNeedsPaintPropertyUpdate(GetDocument());
|
| + // 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(previous_element, FullscreenElement(), request_type);
|
| }
|
|
|
| void Fullscreen::PushFullscreenElementStack(Element& element,
|
| RequestType request_type) {
|
| + Element* previous_element = FullscreenElement();
|
| fullscreen_element_stack_.push_back(std::make_pair(&element, request_type));
|
|
|
| - SetNeedsPaintPropertyUpdate(GetDocument());
|
| + FullscreenElementChanged(previous_element, &element, request_type);
|
| +}
|
| +
|
| +void Fullscreen::FullscreenElementChanged(Element* from_element,
|
| + Element* to_element,
|
| + RequestType to_request_type) {
|
| + DCHECK_NE(from_element, to_element);
|
| +
|
| + if (!GetDocument())
|
| + return;
|
| +
|
| + GetDocument()->GetStyleEngine().EnsureUAStyleForFullscreen();
|
| +
|
| + if (full_screen_layout_object_)
|
| + full_screen_layout_object_->UnwrapLayoutObject();
|
| + DCHECK(!full_screen_layout_object_);
|
| +
|
| + if (from_element) {
|
| + DCHECK_NE(from_element, FullscreenElement());
|
| +
|
| + from_element->PseudoStateChanged(CSSSelector::kPseudoFullScreen);
|
| +
|
| + from_element->SetContainsFullScreenElement(false);
|
| + from_element
|
| + ->SetContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
|
| + }
|
| +
|
| + if (to_element) {
|
| + DCHECK_EQ(to_element, FullscreenElement());
|
| +
|
| + to_element->PseudoStateChanged(CSSSelector::kPseudoFullScreen);
|
| +
|
| + // 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 (to_request_type == RequestType::kPrefixedForCrossProcessDescendant) {
|
| + DCHECK(isHTMLIFrameElement(to_element));
|
| + to_element->SetContainsFullScreenElement(true);
|
| + }
|
| + to_element->SetContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(
|
| + true);
|
| +
|
| + // 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 = to_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_ =
|
| + ComputedStyle::Clone(layout_object->StyleRef());
|
| + }
|
| +
|
| + if (to_element != GetDocument()->documentElement()) {
|
| + LayoutFullScreen::WrapLayoutObject(
|
| + layout_object, layout_object ? layout_object->Parent() : 0,
|
| + GetDocument());
|
| + }
|
| + }
|
| +
|
| + if (LocalFrame* frame = GetDocument()->GetFrame()) {
|
| + // TODO(foolip): Synchronize hover state changes with animation frames.
|
| + // https://crbug.com/668758
|
| + frame->GetEventHandler().ScheduleHoverStateUpdate();
|
| + frame->GetChromeClient().FullscreenElementChanged(from_element, to_element);
|
| +
|
| + if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled() &&
|
| + !RuntimeEnabledFeatures::rootLayerScrollingEnabled()) {
|
| + // Fullscreen status affects scroll paint properties through
|
| + // LocalFrameView::UserInputScrollable().
|
| + if (LocalFrameView* frame_view = frame->View())
|
| + frame_view->SetNeedsPaintPropertyUpdate();
|
| + }
|
| + }
|
| +
|
| + // TODO(foolip): This should not call updateStyleAndLayoutTree.
|
| + GetDocument()->UpdateStyleAndLayoutTree();
|
| }
|
|
|
| DEFINE_TRACE(Fullscreen) {
|
| - visitor->Trace(pending_fullscreen_element_);
|
| + visitor->Trace(pending_requests_);
|
| visitor->Trace(fullscreen_element_stack_);
|
| - visitor->Trace(current_full_screen_element_);
|
| - visitor->Trace(event_queue_);
|
| Supplement<Document>::Trace(visitor);
|
| ContextLifecycleObserver::Trace(visitor);
|
| }
|
|
|