| 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); | 
| } | 
|  |