Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(214)

Unified Diff: third_party/WebKit/Source/core/dom/Fullscreen.cpp

Issue 2654083006: Revert of Sync requestFullscreen() and exitFullscreen() algorithms with the spec (Closed)
Patch Set: add failing test expectations Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 5ead9962ce68a06a27485a7d5403b828ff7ffc93..39f7e4c3d48fec8d8eba51582fbcd54c61d63df5 100644
--- a/third_party/WebKit/Source/core/dom/Fullscreen.cpp
+++ b/third_party/WebKit/Source/core/dom/Fullscreen.cpp
@@ -33,6 +33,7 @@
#include "core/dom/Document.h"
#include "core/dom/ElementTraversal.h"
#include "core/dom/StyleEngine.h"
+#include "core/dom/TaskRunnerHelper.h"
#include "core/events/Event.h"
#include "core/frame/FrameView.h"
#include "core/frame/HostsUsingFeatures.h"
@@ -182,26 +183,17 @@ bool fullscreenElementReady(const Element& element) {
return true;
}
-// 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;
-
- // 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;
+bool isPrefixed(const AtomicString& type) {
+ return type == EventTypeNames::webkitfullscreenchange ||
+ type == EventTypeNames::webkitfullscreenerror;
+}
- return true;
+Event* createEvent(const AtomicString& type, EventTarget& target) {
+ EventInit initializer;
+ initializer.setBubbles(isPrefixed(type));
+ Event* event = Event::create(type, initializer);
+ event->setTarget(&target);
+ return event;
}
// Walks the frame tree and returns the first local ancestor frame, if any.
@@ -220,7 +212,7 @@ Document* nextLocalAncestor(Document& document) {
LocalFrame* frame = document.frame();
if (!frame)
return nullptr;
- LocalFrame* next = nextLocalAncestor(*frame);
+ LocalFrame* next = nextLocalAncestor(*document.frame());
if (!next)
return nullptr;
DCHECK(next->document());
@@ -238,94 +230,34 @@ Document& topmostLocalAncestor(Document& document) {
return document;
}
-// https://fullscreen.spec.whatwg.org/#collect-documents-to-unfullscreen
-HeapVector<Member<Document>> collectDocumentsToUnfullscreen(
- Document& doc,
- Fullscreen::ExitType exitType) {
- 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 (exitType != Fullscreen::ExitType::Fully &&
- 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;
-}
-
-// Creates a non-bubbling event with |document| as its target.
-Event* createEvent(const AtomicString& type, Document& document) {
- DCHECK(type == EventTypeNames::fullscreenchange ||
- type == EventTypeNames::fullscreenerror);
-
- Event* event = Event::create(type);
- event->setTarget(&document);
- return event;
+// 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.frame();
+ while (frame->tree().parent() != doc.frame())
+ frame = frame->tree().parent();
+ return toHTMLFrameOwnerElement(frame->owner());
}
-// 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.document() == document)
- event->setTarget(&element);
- else
- event->setTarget(&document);
- return event;
-}
+// Fullscreen status affects scroll paint properties through
+// FrameView::userInputScrollable().
+void setNeedsPaintPropertyUpdate(Document* document) {
+ if (!RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled() ||
+ RuntimeEnabledFeatures::rootLayerScrollingEnabled())
+ return;
-Event* createChangeEvent(Document& document,
- Element& element,
- Fullscreen::RequestType requestType) {
- if (requestType == Fullscreen::RequestType::Unprefixed)
- return createEvent(EventTypeNames::fullscreenchange, document);
- return createPrefixedEvent(EventTypeNames::webkitfullscreenchange, element,
- document);
-}
+ if (!document)
+ return;
-Event* createErrorEvent(Document& document,
- Element& element,
- Fullscreen::RequestType requestType) {
- if (requestType == Fullscreen::RequestType::Unprefixed)
- return createEvent(EventTypeNames::fullscreenerror, document);
- return createPrefixedEvent(EventTypeNames::webkitfullscreenerror, element,
- document);
-}
+ LocalFrame* frame = document->frame();
+ if (!frame)
+ return;
-void dispatchEvents(const HeapVector<Member<Event>>& events) {
- for (Event* event : events)
- event->target()->dispatchEvent(event);
+ if (FrameView* frameView = frame->view())
+ frameView->setNeedsPaintPropertyUpdate();
}
} // anonymous namespace
@@ -355,12 +287,6 @@ Element* Fullscreen::fullscreenElementFrom(Document& document) {
return nullptr;
}
-size_t Fullscreen::fullscreenElementStackSizeFrom(Document& document) {
- if (Fullscreen* found = fromIfExists(document))
- return found->m_fullscreenElementStack.size();
- return 0;
-}
-
Element* Fullscreen::fullscreenElementForBindingFrom(TreeScope& scope) {
Element* element = fullscreenElementFrom(scope.document());
if (!element || !RuntimeEnabledFeatures::fullscreenUnprefixedEnabled())
@@ -382,43 +308,66 @@ 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::DocumentFullscreenElementInV0Shadow);
+ return element;
+ }
+ return document.adjustedElement(*element);
+}
+
Fullscreen::Fullscreen(Document& document)
: Supplement<Document>(document),
ContextLifecycleObserver(&document),
- m_fullScreenLayoutObject(nullptr) {
+ m_fullScreenLayoutObject(nullptr),
+ m_eventQueueTimer(TaskRunnerHelper::get(TaskType::Unthrottled, &document),
+ this,
+ &Fullscreen::eventQueueTimerFired),
+ m_forCrossProcessDescendant(false) {
document.setHasFullscreenSupplement();
}
Fullscreen::~Fullscreen() {}
-Document* Fullscreen::document() {
+inline Document* Fullscreen::document() {
return toDocument(lifecycleContext());
}
void Fullscreen::contextDestroyed(ExecutionContext*) {
+ m_eventQueue.clear();
+
if (m_fullScreenLayoutObject)
m_fullScreenLayoutObject->destroy();
- m_pendingRequests.clear();
+ m_currentFullScreenElement = nullptr;
m_fullscreenElementStack.clear();
}
// https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen
-void Fullscreen::requestFullscreen(Element& pending) {
+void Fullscreen::requestFullscreen(Element& element) {
// TODO(foolip): Make RequestType::Unprefixed the default when the unprefixed
// API is enabled. https://crbug.com/383813
- requestFullscreen(pending, RequestType::Prefixed);
+ requestFullscreen(element, RequestType::Prefixed, false);
}
-void Fullscreen::requestFullscreen(Element& pending, RequestType requestType) {
- Document& document = pending.document();
-
- // Ignore this call if the document is not in a live frame.
- if (!document.isActive() || !document.frame())
- return;
-
- bool forCrossProcessDescendant =
- requestType == RequestType::PrefixedForCrossProcessDescendant;
+void Fullscreen::requestFullscreen(Element& element,
+ RequestType requestType,
+ bool forCrossProcessDescendant) {
+ Document& document = element.document();
// Use counters only need to be incremented in the process of the actual
// fullscreen element.
@@ -432,335 +381,356 @@ void Fullscreen::requestFullscreen(Element& pending, RequestType requestType) {
}
}
- // 1. Let |pending| be the context object.
+ // Ignore this call if the document is not in a live frame.
+ if (!document.isActive() || !document.frame())
+ return;
- // 2. Let |error| be false.
- bool error = false;
+ // If |element| is on top of |doc|'s fullscreen element stack, terminate these
+ // substeps.
+ if (&element == fullscreenElementFrom(document))
+ return;
- // 3. Let |promise| be a new promise.
- // TODO(foolip): Promises. https://crbug.com/644637
+ 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:
- // 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 (!forCrossProcessDescendant &&
- !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) {
- from(document).m_pendingRequests.push_back(
- std::make_pair(&pending, requestType));
- LocalFrame& frame = *document.frame();
- frame.chromeClient().enterFullscreen(frame);
- } else {
- enqueueTaskForRequest(document, pending, requestType, true);
- }
-}
+ // |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;
-void Fullscreen::didEnterFullscreen() {
- if (!document())
+ // 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 (!forCrossProcessDescendant && !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.prepend(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* currentDoc = *current;
+ Document* followingDoc = 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 (!followingDoc) {
+ from(*currentDoc).pushFullscreenElementStack(element, requestType);
+ from(document).enqueueChangeEvent(*currentDoc, requestType);
+ 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* topElement = fullscreenElementFrom(*currentDoc);
+ HTMLFrameOwnerElement* followingOwner =
+ findContainerForDescendant(*currentDoc, *followingDoc);
+ if (!topElement || topElement != followingOwner) {
+ // ...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(*currentDoc)
+ .pushFullscreenElementStack(*followingOwner, requestType);
+ from(document).enqueueChangeEvent(*currentDoc, requestType);
+ continue;
+ }
+
+ // 4. Otherwise, do nothing for this document. It stays the same.
+ } while (++current != docs.end());
+
+ from(document).m_forCrossProcessDescendant = forCrossProcessDescendant;
+
+ // 5. Return, and run the remaining steps asynchronously.
+ // 6. Optionally, perform some animation.
+ from(document).m_pendingFullscreenElement = &element;
+ document.frame()->chromeClient().enterFullscreen(*document.frame());
+
+ // 7. Optionally, display a message indicating how the user can exit
+ // displaying the context object fullscreen.
return;
+ } while (false);
- ElementStack requests;
- requests.swap(m_pendingRequests);
- for (const ElementStackEntry& request : requests)
- enqueueTaskForRequest(*document(), *request.first, request.second, false);
+ from(document).enqueueErrorEvent(element, requestType);
}
-void Fullscreen::enqueueTaskForRequest(Document& document,
- Element& pending,
- RequestType requestType,
- bool error) {
- // 7. As part of the next animation frame task, run these substeps:
- document.enqueueAnimationFrameTask(
- WTF::bind(&runTaskForRequest, wrapPersistent(&document),
- wrapPersistent(&pending), requestType, error));
+// 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))
+ return;
+
+ // 3. Remove elements from |doc|'s fullscreen element stack until only the top
+ // element is left.
+ size_t stackSize = from(doc).m_fullscreenElementStack.size();
+ from(doc).m_fullscreenElementStack.remove(0, stackSize - 1);
+ DCHECK_EQ(from(doc).m_fullscreenElementStack.size(), 1u);
+
+ // 4. Act as if the exitFullscreen() method was invoked on |doc|.
+ exitFullscreen(doc);
}
-void Fullscreen::runTaskForRequest(Document* document,
- Element* element,
- RequestType requestType,
- bool error) {
- DCHECK(document);
- DCHECK(document->isActive());
- DCHECK(document->frame());
- DCHECK(element);
-
- Document& pendingDoc = *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.document() != pendingDoc)
- 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(pendingDoc, pending, requestType);
- event->target()->dispatchEvent(event);
+// https://fullscreen.spec.whatwg.org/#exit-fullscreen
+void Fullscreen::exitFullscreen(Document& document) {
+ // The exitFullscreen() method must run these steps:
+
+ // Ignore this call if the document is not in a live frame.
+ if (!document.isActive() || !document.frame())
return;
- }
- // 7.2. Let |fullscreenElements| be an ordered set initially consisting of
- // |pending|.
- HeapDeque<Member<Element>> fullscreenElements;
- fullscreenElements.append(pending);
+ // 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.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.document().frame(); frame;
- frame = frame->tree().parent()) {
- if (!frame->owner() || !frame->owner()->isLocal())
+ // 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.frame()->tree().traverseNext(); descendant;
+ descendant = descendant->tree().traverseNext()) {
+ if (!descendant->isLocalFrame())
continue;
- Element* element = toHTMLFrameOwnerElement(frame->owner());
- fullscreenElements.prepend(element);
+ DCHECK(toLocalFrame(descendant)->document());
+ if (fullscreenElementFrom(*toLocalFrame(descendant)->document()))
+ descendants.prepend(toLocalFrame(descendant)->document());
}
- // 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;
+ // 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 requestType =
+ from(*descendant).m_fullscreenElementStack.back().second;
+ from(*descendant).clearFullscreenElementStack();
+ from(document).enqueueChangeEvent(*descendant, requestType);
+ }
- // 7.5. For each |element| in |fullscreenElements|, in order, run these
- // subsubsteps:
- for (Element* element : fullscreenElements) {
- // 7.5.1. Let |doc| be |element|'s node document.
- Document& doc = element->document();
+ // 5. While doc is not null, run these substeps:
+ Element* newTop = nullptr;
+ for (Document* currentDoc = &document; currentDoc;) {
+ RequestType requestType =
+ from(*currentDoc).m_fullscreenElementStack.back().second;
- // 7.5.2. If |element| is |doc|'s fullscreen element, terminate these
- // subsubsteps.
- if (element == fullscreenElementFrom(doc))
- continue;
+ // 1. Pop the top element of doc's fullscreen element stack.
+ from(*currentDoc).popFullscreenElementStack();
- // 7.5.3. Otherwise, append |doc| to |eventDocs|.
- events.push_back(createChangeEvent(doc, *element, requestType));
+ // 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.
+ newTop = fullscreenElementFrom(*currentDoc);
+ if (newTop && (!newTop->isConnected() || newTop->document() != currentDoc))
+ continue;
- // 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
+ // 2. Queue a task to fire an event named fullscreenchange with its bubbles
+ // attribute set to true on doc.
+ from(document).enqueueChangeEvent(*currentDoc, requestType);
+
+ // 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 (!newTop) {
+ currentDoc = nextLocalAncestor(*currentDoc);
+ continue;
+ }
- // 7.5.5. Fullscreen |element| within |doc|.
- // TODO(foolip): Merge fullscreen element stack into top layer.
- // https://crbug.com/627790
- from(doc).pushFullscreenElementStack(*element, requestType);
+ // 4. Otherwise, set doc to null.
+ currentDoc = nullptr;
}
- // 7.6. For each |doc| in |eventDocs|, in order, fire an event named
- // fullscreenchange on |doc|.
- dispatchEvents(events);
-
- // 7.7. Fulfill |promise| with undefined.
- // TODO(foolip): Promises. https://crbug.com/644637
-}
-
-// https://fullscreen.spec.whatwg.org/#fully-exit-fullscreen
-void Fullscreen::fullyExitFullscreen(Document& document) {
- // 1. If |document|'s fullscreen element is null, terminate these steps.
+ // 6. Return, and run the remaining steps asynchronously.
+ // 7. Optionally, perform some animation.
- // 2. Unfullscreen elements whose fullscreen flag is set, within
- // |document|'s top layer, except for |document|'s fullscreen element.
+ // Only exit fullscreen mode if the fullscreen element stack is empty.
+ if (!newTop) {
+ document.frame()->chromeClient().exitFullscreen(*document.frame());
+ return;
+ }
- // 3. Exit fullscreen |document|.
+ // Otherwise, enter fullscreen for the fullscreen element stack's top element.
+ from(document).m_pendingFullscreenElement = newTop;
+ from(document).didEnterFullscreen();
+}
- // 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::Fully);
+// 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.frame()) &&
+ fullscreenIsSupported(document);
}
-// https://fullscreen.spec.whatwg.org/#exit-fullscreen
-void Fullscreen::exitFullscreen(Document& doc, ExitType exitType) {
- if (!doc.isActive() || !doc.frame())
+void Fullscreen::didEnterFullscreen() {
+ if (!document()->isActive() || !document()->frame())
return;
- // 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;
+ // Start the timer for events enqueued by |requestFullscreen()|. The hover
+ // state update is scheduled first so that it's done when the events fire.
+ document()->frame()->eventHandler().scheduleHoverStateUpdate();
+ m_eventQueueTimer.startOneShot(0, BLINK_FROM_HERE);
- // 3. Let |resize| be false.
- bool resize = false;
+ Element* element = m_pendingFullscreenElement.release();
+ if (!element)
+ return;
- // 4. Let |docs| be the result of collecting documents to unfullscreen given
- // |doc|.
- HeapVector<Member<Document>> docs =
- collectDocumentsToUnfullscreen(doc, exitType);
+ if (m_currentFullScreenElement == element)
+ return;
- // 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& topLevelDoc = topmostLocalAncestor(doc);
-
- // 6. If |topLevelDoc| is in |docs|, set |resize| to true.
- if (!docs.isEmpty() && docs.back() == &topLevelDoc)
- 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 (exitType == ExitType::Fully)
- DCHECK(resize);
-
- // 8. If |resize| is true, resize |topLevelDoc|'s viewport to its "normal"
- // dimensions.
- if (resize) {
- LocalFrame& frame = *doc.frame();
+ if (!element->isConnected() || &element->document() != document()) {
+ // 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 = *document()->frame();
frame.chromeClient().exitFullscreen(frame);
- } else {
- enqueueTaskForExit(doc, exitType);
+ return;
}
-}
-void Fullscreen::didExitFullscreen() {
- if (!document())
- return;
+ if (m_fullScreenLayoutObject)
+ m_fullScreenLayoutObject->unwrapLayoutObject();
- DCHECK_EQ(document(), &topmostLocalAncestor(*document()));
+ Element* previousElement = m_currentFullScreenElement;
+ m_currentFullScreenElement = 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* layoutObject = m_currentFullScreenElement->layoutObject();
+ bool shouldCreatePlaceholder = layoutObject && layoutObject->isBox();
+ if (shouldCreatePlaceholder) {
+ m_savedPlaceholderFrameRect = toLayoutBox(layoutObject)->frameRect();
+ m_savedPlaceholderComputedStyle =
+ ComputedStyle::clone(layoutObject->styleRef());
+ }
- enqueueTaskForExit(*document(), ExitType::Fully);
-}
+ // 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 (m_currentFullScreenElement != document()->documentElement()) {
+ LayoutFullScreen::wrapLayoutObject(
+ layoutObject, layoutObject ? layoutObject->parent() : 0, document());
+ }
-void Fullscreen::enqueueTaskForExit(Document& document, ExitType exitType) {
- // 9. As part of the next animation frame task, run these substeps:
- document.enqueueAnimationFrameTask(
- WTF::bind(&runTaskForExit, wrapPersistent(&document), exitType));
-}
+ // 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 (m_forCrossProcessDescendant) {
+ DCHECK(m_currentFullScreenElement->isFrameOwnerElement());
+ DCHECK(toHTMLFrameOwnerElement(m_currentFullScreenElement)
+ ->contentFrame()
+ ->isRemoteFrame());
+ m_currentFullScreenElement->setContainsFullScreenElement(true);
+ }
-void Fullscreen::runTaskForExit(Document* document, ExitType exitType) {
- DCHECK(document);
- DCHECK(document->isActive());
- DCHECK(document->frame());
+ m_currentFullScreenElement
+ ->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(true);
- Document& doc = *document;
+ document()->styleEngine().ensureUAStyleForFullscreen();
+ m_currentFullScreenElement->pseudoStateChanged(CSSSelector::PseudoFullScreen);
- if (!fullscreenElementFrom(doc))
- return;
+ // FIXME: This should not call updateStyleAndLayoutTree.
+ document()->updateStyleAndLayoutTree();
- // 9.1. Let |exitDocs| be the result of collecting documents to unfullscreen
- // given |doc|.
+ document()->frame()->chromeClient().fullscreenElementChanged(previousElement,
+ element);
+}
- // 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.
+void Fullscreen::didExitFullscreen() {
+ if (!document()->isActive() || !document()->frame())
+ 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.
+ // Start the timer for events enqueued by |exitFullscreen()|. The hover state
+ // update is scheduled first so that it's done when the events fire.
+ document()->frame()->eventHandler().scheduleHoverStateUpdate();
+ m_eventQueueTimer.startOneShot(0, BLINK_FROM_HERE);
- HeapVector<Member<Document>> exitDocs =
- collectDocumentsToUnfullscreen(doc, exitType);
+ // 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(*document());
- // 9.3. If |exitDocs| is the empty set, append |doc| to |exitDocs|.
- if (exitDocs.isEmpty())
- exitDocs.push_back(&doc);
+ if (!m_currentFullScreenElement)
+ return;
- // 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(*exitDocs.back()))
- exitDocs.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>> descendantDocs;
- for (Frame* descendant = doc.frame()->tree().firstChild(); descendant;
- descendant = descendant->tree().traverseNext(doc.frame())) {
- if (!descendant->isLocalFrame())
- continue;
- DCHECK(toLocalFrame(descendant)->document());
- if (fullscreenElementFrom(*toLocalFrame(descendant)->document()))
- descendantDocs.prepend(toLocalFrame(descendant)->document());
- }
+ if (m_forCrossProcessDescendant)
+ m_currentFullScreenElement->setContainsFullScreenElement(false);
- // 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* descendantDoc : descendantDocs) {
- Fullscreen& fullscreen = from(*descendantDoc);
- ElementStack& stack = fullscreen.m_fullscreenElementStack;
- DCHECK(!stack.isEmpty());
- events.push_back(createChangeEvent(*descendantDoc, *stack.back().first,
- stack.back().second));
- while (!stack.isEmpty())
- fullscreen.popFullscreenElementStack();
- }
+ m_currentFullScreenElement
+ ->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
- // 9.7. For each |exitDoc| in |exitDocs|, in order, unfullscreen |exitDoc|'s
- // fullscreen element.
- for (Document* exitDoc : exitDocs) {
- Fullscreen& fullscreen = from(*exitDoc);
- ElementStack& stack = fullscreen.m_fullscreenElementStack;
- DCHECK(!stack.isEmpty());
- events.push_back(
- createChangeEvent(*exitDoc, *stack.back().first, stack.back().second));
- fullscreen.popFullscreenElementStack();
-
- // TODO(foolip): See TODO in |fullyExitFullscreen()|.
- if (exitDoc == &doc && exitType == ExitType::Fully) {
- while (!stack.isEmpty())
- fullscreen.popFullscreenElementStack();
- }
- }
+ if (m_fullScreenLayoutObject)
+ LayoutFullScreenItem(m_fullScreenLayoutObject).unwrapLayoutObject();
- // 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);
+ document()->styleEngine().ensureUAStyleForFullscreen();
+ m_currentFullScreenElement->pseudoStateChanged(CSSSelector::PseudoFullScreen);
+ Element* previousElement = m_currentFullScreenElement;
+ m_currentFullScreenElement = nullptr;
- // 9.10. Fulfill |promise| with undefined.
- // TODO(foolip): Promises. https://crbug.com/644637
-}
+ m_forCrossProcessDescendant = false;
-// 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.frame()) &&
- fullscreenIsSupported(document);
+ document()->frame()->chromeClient().fullscreenElementChanged(previousElement,
+ nullptr);
}
void Fullscreen::setFullScreenLayoutObject(LayoutFullScreen* layoutObject) {
@@ -789,9 +759,56 @@ void Fullscreen::fullScreenLayoutObjectDestroyed() {
m_fullScreenLayoutObject = nullptr;
}
-void Fullscreen::elementRemoved(Element& oldNode) {
- DCHECK_EQ(document(), &oldNode.document());
+void Fullscreen::enqueueChangeEvent(Document& document,
+ RequestType requestType) {
+ Event* event;
+ if (requestType == RequestType::Unprefixed) {
+ 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);
+ }
+ m_eventQueue.append(event);
+ // NOTE: The timer is started in didEnterFullscreen/didExitFullscreen.
+}
+
+void Fullscreen::enqueueErrorEvent(Element& element, RequestType requestType) {
+ Event* event;
+ if (requestType == RequestType::Unprefixed)
+ event = createEvent(EventTypeNames::fullscreenerror, element.document());
+ else
+ event = createEvent(EventTypeNames::webkitfullscreenerror, element);
+ m_eventQueue.append(event);
+ m_eventQueueTimer.startOneShot(0, BLINK_FROM_HERE);
+}
+
+void Fullscreen::eventQueueTimerFired(TimerBase*) {
+ HeapDeque<Member<Event>> eventQueue;
+ m_eventQueue.swap(eventQueue);
+
+ while (!eventQueue.isEmpty()) {
+ Event* event = eventQueue.takeFirst();
+ Node* target = event->target()->toNode();
+
+ // If the element was removed from our tree, also message the
+ // documentElement.
+ if (!target->isConnected() && document()->documentElement()) {
+ DCHECK(isPrefixed(event->type()));
+ eventQueue.append(
+ createEvent(event->type(), *document()->documentElement()));
+ }
+ target->dispatchEvent(event);
+ }
+}
+
+void Fullscreen::elementRemoved(Element& oldNode) {
// Whenever the removing steps run with an |oldNode| and |oldNode| is in its
// node document's fullscreen element stack, run these steps:
@@ -814,106 +831,36 @@ void Fullscreen::elementRemoved(Element& oldNode) {
// NOTE: |oldNode| was not in the fullscreen element stack.
}
+void Fullscreen::clearFullscreenElementStack() {
+ if (m_fullscreenElementStack.isEmpty())
+ return;
+
+ m_fullscreenElementStack.clear();
+
+ setNeedsPaintPropertyUpdate(document());
+}
+
void Fullscreen::popFullscreenElementStack() {
- DCHECK(!m_fullscreenElementStack.isEmpty());
+ if (m_fullscreenElementStack.isEmpty())
+ return;
- Element* previousElement = fullscreenElement();
m_fullscreenElementStack.pop_back();
- // Note: |requestType| is only used if |fullscreenElement()| is non-null.
- RequestType requestType = m_fullscreenElementStack.isEmpty()
- ? RequestType::Unprefixed
- : m_fullscreenElementStack.back().second;
- fullscreenElementChanged(previousElement, fullscreenElement(), requestType);
+ setNeedsPaintPropertyUpdate(document());
}
void Fullscreen::pushFullscreenElementStack(Element& element,
RequestType requestType) {
- Element* previousElement = fullscreenElement();
m_fullscreenElementStack.push_back(std::make_pair(&element, requestType));
- fullscreenElementChanged(previousElement, &element, requestType);
-}
-
-void Fullscreen::fullscreenElementChanged(Element* fromElement,
- Element* toElement,
- RequestType toRequestType) {
- DCHECK_NE(fromElement, toElement);
-
- if (!document())
- return;
-
- document()->styleEngine().ensureUAStyleForFullscreen();
-
- if (m_fullScreenLayoutObject)
- m_fullScreenLayoutObject->unwrapLayoutObject();
- DCHECK(!m_fullScreenLayoutObject);
-
- if (fromElement) {
- DCHECK_NE(fromElement, fullscreenElement());
-
- fromElement->pseudoStateChanged(CSSSelector::PseudoFullScreen);
-
- fromElement->setContainsFullScreenElement(false);
- fromElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(
- false);
- }
-
- if (toElement) {
- DCHECK_EQ(toElement, fullscreenElement());
-
- toElement->pseudoStateChanged(CSSSelector::PseudoFullScreen);
-
- // 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 (toRequestType == RequestType::PrefixedForCrossProcessDescendant) {
- DCHECK(isHTMLIFrameElement(toElement));
- toElement->setContainsFullScreenElement(true);
- }
- toElement->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* layoutObject = toElement->layoutObject();
- bool shouldCreatePlaceholder = layoutObject && layoutObject->isBox();
- if (shouldCreatePlaceholder) {
- m_savedPlaceholderFrameRect = toLayoutBox(layoutObject)->frameRect();
- m_savedPlaceholderComputedStyle =
- ComputedStyle::clone(layoutObject->styleRef());
- }
-
- if (toElement != document()->documentElement()) {
- LayoutFullScreen::wrapLayoutObject(
- layoutObject, layoutObject ? layoutObject->parent() : 0, document());
- }
- }
-
- if (LocalFrame* frame = document()->frame()) {
- // TODO(foolip): Synchronize hover state changes with animation frames.
- // https://crbug.com/668758
- frame->eventHandler().scheduleHoverStateUpdate();
- frame->chromeClient().fullscreenElementChanged(fromElement, toElement);
-
- if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled() &&
- !RuntimeEnabledFeatures::rootLayerScrollingEnabled()) {
- // Fullscreen status affects scroll paint properties through
- // FrameView::userInputScrollable().
- if (FrameView* frameView = frame->view())
- frameView->setNeedsPaintPropertyUpdate();
- }
- }
-
- // TODO(foolip): This should not call updateStyleAndLayoutTree.
- document()->updateStyleAndLayoutTree();
+ setNeedsPaintPropertyUpdate(document());
}
DEFINE_TRACE(Fullscreen) {
- visitor->trace(m_pendingRequests);
+ visitor->trace(m_pendingFullscreenElement);
visitor->trace(m_fullscreenElementStack);
+ visitor->trace(m_currentFullScreenElement);
+ visitor->trace(m_eventQueue);
Supplement<Document>::trace(visitor);
ContextLifecycleObserver::trace(visitor);
}
« no previous file with comments | « third_party/WebKit/Source/core/dom/Fullscreen.h ('k') | third_party/WebKit/Source/core/dom/LayoutTreeBuilder.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698