Home | History | Annotate | Download | only in dom
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 1999 Antti Koivisto (koivisto (at) kde.org)
      4  *           (C) 2001 Dirk Mueller (mueller (at) kde.org)
      5  *           (C) 2006 Alexey Proskuryakov (ap (at) webkit.org)
      6  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All rights reserved.
      7  * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
      8  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
      9  * Copyright (C) 2013 Google Inc. All rights reserved.
     10  *
     11  * This library is free software; you can redistribute it and/or
     12  * modify it under the terms of the GNU Library General Public
     13  * License as published by the Free Software Foundation; either
     14  * version 2 of the License, or (at your option) any later version.
     15  *
     16  * This library is distributed in the hope that it will be useful,
     17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     19  * Library General Public License for more details.
     20  *
     21  * You should have received a copy of the GNU Library General Public License
     22  * along with this library; see the file COPYING.LIB.  If not, write to
     23  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     24  * Boston, MA 02110-1301, USA.
     25  *
     26  */
     27 
     28 #include "config.h"
     29 #include "core/dom/Fullscreen.h"
     30 
     31 #include "core/HTMLNames.h"
     32 #include "core/dom/Document.h"
     33 #include "core/events/Event.h"
     34 #include "core/frame/FrameHost.h"
     35 #include "core/frame/LocalFrame.h"
     36 #include "core/frame/Settings.h"
     37 #include "core/frame/UseCounter.h"
     38 #include "core/html/HTMLIFrameElement.h"
     39 #include "core/html/HTMLMediaElement.h"
     40 #include "core/page/Chrome.h"
     41 #include "core/page/ChromeClient.h"
     42 #include "core/page/EventHandler.h"
     43 #include "core/rendering/RenderFullScreen.h"
     44 #include "platform/UserGestureIndicator.h"
     45 
     46 namespace blink {
     47 
     48 using namespace HTMLNames;
     49 
     50 static bool fullscreenIsAllowedForAllOwners(const Document& document)
     51 {
     52     for (const Element* owner = document.ownerElement(); owner; owner = owner->document().ownerElement()) {
     53         if (!isHTMLIFrameElement(owner))
     54             return false;
     55         if (!owner->hasAttribute(allowfullscreenAttr))
     56             return false;
     57     }
     58     return true;
     59 }
     60 
     61 static bool fullscreenIsSupported(const Document& document)
     62 {
     63     // Fullscreen is supported if there is no previously-established user preference,
     64     // security risk, or platform limitation.
     65     return !document.settings() || document.settings()->fullscreenSupported();
     66 }
     67 
     68 static bool fullscreenIsSupported(const Document& document, const Element& element)
     69 {
     70     if (!document.settings() || (document.settings()->disallowFullscreenForNonMediaElements() && !isHTMLMediaElement(element)))
     71         return false;
     72     return fullscreenIsSupported(document);
     73 }
     74 
     75 static bool fullscreenElementReady(const Element& element, Fullscreen::RequestType requestType)
     76 {
     77     // A fullscreen element ready check for an element |element| returns true if all of the
     78     // following are true, and false otherwise:
     79 
     80     // |element| is in a document.
     81     if (!element.inDocument())
     82         return false;
     83 
     84     // |element|'s node document's fullscreen enabled flag is set.
     85     if (!fullscreenIsAllowedForAllOwners(element.document())) {
     86         if (requestType == Fullscreen::PrefixedVideoRequest)
     87             UseCounter::count(element.document(), UseCounter::VideoFullscreenAllowedExemption);
     88         else
     89             return false;
     90     }
     91 
     92     // |element|'s node document's fullscreen element stack is either empty or its top element is an
     93     // inclusive ancestor of |element|.
     94     if (const Element* topElement = Fullscreen::fullscreenElementFrom(element.document())) {
     95         if (!topElement->contains(&element))
     96             return false;
     97     }
     98 
     99     // |element| has no ancestor element whose local name is iframe and namespace is the HTML
    100     // namespace.
    101     if (Traversal<HTMLIFrameElement>::firstAncestor(element))
    102         return false;
    103 
    104     // |element|'s node document's browsing context either has a browsing context container and the
    105     // fullscreen element ready check returns true for |element|'s node document's browsing
    106     // context's browsing context container, or it has no browsing context container.
    107     if (const Element* owner = element.document().ownerElement()) {
    108         if (!fullscreenElementReady(*owner, requestType))
    109             return false;
    110     }
    111 
    112     return true;
    113 }
    114 
    115 static bool isPrefixed(const AtomicString& type)
    116 {
    117     return type == EventTypeNames::webkitfullscreenchange || type == EventTypeNames::webkitfullscreenerror;
    118 }
    119 
    120 static PassRefPtrWillBeRawPtr<Event> createEvent(const AtomicString& type, EventTarget& target)
    121 {
    122     EventInit initializer;
    123     initializer.bubbles = isPrefixed(type);
    124     RefPtrWillBeRawPtr<Event> event = Event::create(type, initializer);
    125     event->setTarget(&target);
    126     return event;
    127 }
    128 
    129 const char* Fullscreen::supplementName()
    130 {
    131     return "Fullscreen";
    132 }
    133 
    134 Fullscreen& Fullscreen::from(Document& document)
    135 {
    136     Fullscreen* fullscreen = fromIfExists(document);
    137     if (!fullscreen) {
    138         fullscreen = new Fullscreen(document);
    139         DocumentSupplement::provideTo(document, supplementName(), adoptPtrWillBeNoop(fullscreen));
    140     }
    141 
    142     return *fullscreen;
    143 }
    144 
    145 Fullscreen* Fullscreen::fromIfExistsSlow(Document& document)
    146 {
    147     return static_cast<Fullscreen*>(DocumentSupplement::from(document, supplementName()));
    148 }
    149 
    150 Element* Fullscreen::fullscreenElementFrom(Document& document)
    151 {
    152     if (Fullscreen* found = fromIfExists(document))
    153         return found->fullscreenElement();
    154     return 0;
    155 }
    156 
    157 Element* Fullscreen::currentFullScreenElementFrom(Document& document)
    158 {
    159     if (Fullscreen* found = fromIfExists(document))
    160         return found->webkitCurrentFullScreenElement();
    161     return 0;
    162 }
    163 
    164 bool Fullscreen::isFullScreen(Document& document)
    165 {
    166     return currentFullScreenElementFrom(document);
    167 }
    168 
    169 Fullscreen::Fullscreen(Document& document)
    170     : DocumentLifecycleObserver(&document)
    171     , m_areKeysEnabledInFullScreen(false)
    172     , m_fullScreenRenderer(nullptr)
    173     , m_eventQueueTimer(this, &Fullscreen::eventQueueTimerFired)
    174 {
    175     document.setHasFullscreenSupplement();
    176 }
    177 
    178 Fullscreen::~Fullscreen()
    179 {
    180 }
    181 
    182 inline Document* Fullscreen::document()
    183 {
    184     return lifecycleContext();
    185 }
    186 
    187 void Fullscreen::documentWasDetached()
    188 {
    189     m_eventQueue.clear();
    190 
    191     if (m_fullScreenRenderer)
    192         m_fullScreenRenderer->destroy();
    193 
    194 #if ENABLE(OILPAN)
    195     m_fullScreenElement = nullptr;
    196     m_fullScreenElementStack.clear();
    197 #endif
    198 
    199 }
    200 
    201 #if !ENABLE(OILPAN)
    202 void Fullscreen::documentWasDisposed()
    203 {
    204     // NOTE: the context dispose phase is not supported in oilpan. Please
    205     // consider using the detach phase instead.
    206     m_fullScreenElement = nullptr;
    207     m_fullScreenElementStack.clear();
    208 }
    209 #endif
    210 
    211 void Fullscreen::requestFullscreen(Element& element, RequestType requestType)
    212 {
    213     // Ignore this request if the document is not in a live frame.
    214     if (!document()->isActive())
    215         return;
    216 
    217     // If |element| is on top of |doc|'s fullscreen element stack, terminate these substeps.
    218     if (&element == fullscreenElement())
    219         return;
    220 
    221     do {
    222         // 1. If any of the following conditions are true, terminate these steps and queue a task to fire
    223         // an event named fullscreenerror with its bubbles attribute set to true on the context object's
    224         // node document:
    225 
    226         // The fullscreen element ready check returns false.
    227         if (!fullscreenElementReady(element, requestType))
    228             break;
    229 
    230         // This algorithm is not allowed to show a pop-up:
    231         //   An algorithm is allowed to show a pop-up if, in the task in which the algorithm is running, either:
    232         //   - an activation behavior is currently being processed whose click event was trusted, or
    233         //   - the event listener for a trusted click event is being handled.
    234         if (!UserGestureIndicator::processingUserGesture())
    235             break;
    236 
    237         // Fullscreen is not supported.
    238         if (!fullscreenIsSupported(element.document(), element))
    239             break;
    240 
    241         // 2. Let doc be element's node document. (i.e. "this")
    242         Document* currentDoc = document();
    243 
    244         // 3. Let docs be all doc's ancestor browsing context's documents (if any) and doc.
    245         Deque<Document*> docs;
    246 
    247         do {
    248             docs.prepend(currentDoc);
    249             currentDoc = currentDoc->ownerElement() ? &currentDoc->ownerElement()->document() : 0;
    250         } while (currentDoc);
    251 
    252         // 4. For each document in docs, run these substeps:
    253         Deque<Document*>::iterator current = docs.begin(), following = docs.begin();
    254 
    255         do {
    256             ++following;
    257 
    258             // 1. Let following document be the document after document in docs, or null if there is no
    259             // such document.
    260             Document* currentDoc = *current;
    261             Document* followingDoc = following != docs.end() ? *following : 0;
    262 
    263             // 2. If following document is null, push context object on document's fullscreen element
    264             // stack, and queue a task to fire an event named fullscreenchange with its bubbles attribute
    265             // set to true on the document.
    266             if (!followingDoc) {
    267                 from(*currentDoc).pushFullscreenElementStack(element, requestType);
    268                 enqueueChangeEvent(*currentDoc, requestType);
    269                 continue;
    270             }
    271 
    272             // 3. Otherwise, if document's fullscreen element stack is either empty or its top element
    273             // is not following document's browsing context container,
    274             Element* topElement = fullscreenElementFrom(*currentDoc);
    275             if (!topElement || topElement != followingDoc->ownerElement()) {
    276                 // ...push following document's browsing context container on document's fullscreen element
    277                 // stack, and queue a task to fire an event named fullscreenchange with its bubbles attribute
    278                 // set to true on document.
    279                 from(*currentDoc).pushFullscreenElementStack(*followingDoc->ownerElement(), requestType);
    280                 enqueueChangeEvent(*currentDoc, requestType);
    281                 continue;
    282             }
    283 
    284             // 4. Otherwise, do nothing for this document. It stays the same.
    285         } while (++current != docs.end());
    286 
    287         // 5. Return, and run the remaining steps asynchronously.
    288         // 6. Optionally, perform some animation.
    289         m_areKeysEnabledInFullScreen = requestType != PrefixedMozillaRequest && requestType != PrefixedVideoRequest;
    290         document()->frameHost()->chrome().client().enterFullScreenForElement(&element);
    291 
    292         // 7. Optionally, display a message indicating how the user can exit displaying the context object fullscreen.
    293         return;
    294     } while (0);
    295 
    296     enqueueErrorEvent(element, requestType);
    297 }
    298 
    299 void Fullscreen::fullyExitFullscreen(Document& document)
    300 {
    301     // To fully exit fullscreen, run these steps:
    302 
    303     // 1. Let |doc| be the top-level browsing context's document.
    304     Document& doc = document.topDocument();
    305 
    306     // 2. If |doc|'s fullscreen element stack is empty, terminate these steps.
    307     if (!fullscreenElementFrom(doc))
    308         return;
    309 
    310     // 3. Remove elements from |doc|'s fullscreen element stack until only the top element is left.
    311     size_t stackSize = from(doc).m_fullScreenElementStack.size();
    312     from(doc).m_fullScreenElementStack.remove(0, stackSize - 1);
    313     ASSERT(from(doc).m_fullScreenElementStack.size() == 1);
    314 
    315     // 4. Act as if the exitFullscreen() method was invoked on |doc|.
    316     from(doc).exitFullscreen();
    317 }
    318 
    319 void Fullscreen::exitFullscreen()
    320 {
    321     // The exitFullscreen() method must run these steps:
    322 
    323     // 1. Let doc be the context object. (i.e. "this")
    324     Document* currentDoc = document();
    325     if (!currentDoc->isActive())
    326         return;
    327 
    328     // 2. If doc's fullscreen element stack is empty, terminate these steps.
    329     if (m_fullScreenElementStack.isEmpty())
    330         return;
    331 
    332     // 3. Let descendants be all the doc's descendant browsing context's documents with a non-empty fullscreen
    333     // element stack (if any), ordered so that the child of the doc is last and the document furthest
    334     // away from the doc is first.
    335     WillBeHeapDeque<RefPtrWillBeMember<Document> > descendants;
    336     for (Frame* descendant = document()->frame() ? document()->frame()->tree().traverseNext() : 0; descendant; descendant = descendant->tree().traverseNext()) {
    337         if (!descendant->isLocalFrame())
    338             continue;
    339         ASSERT(toLocalFrame(descendant)->document());
    340         if (fullscreenElementFrom(*toLocalFrame(descendant)->document()))
    341             descendants.prepend(toLocalFrame(descendant)->document());
    342     }
    343 
    344     // 4. For each descendant in descendants, empty descendant's fullscreen element stack, and queue a
    345     // task to fire an event named fullscreenchange with its bubbles attribute set to true on descendant.
    346     for (WillBeHeapDeque<RefPtrWillBeMember<Document> >::iterator i = descendants.begin(); i != descendants.end(); ++i) {
    347         ASSERT(*i);
    348         RequestType requestType = from(**i).m_fullScreenElementStack.last().second;
    349         from(**i).clearFullscreenElementStack();
    350         enqueueChangeEvent(**i, requestType);
    351     }
    352 
    353     // 5. While doc is not null, run these substeps:
    354     Element* newTop = 0;
    355     while (currentDoc) {
    356         RequestType requestType = from(*currentDoc).m_fullScreenElementStack.last().second;
    357 
    358         // 1. Pop the top element of doc's fullscreen element stack.
    359         from(*currentDoc).popFullscreenElementStack();
    360 
    361         //    If doc's fullscreen element stack is non-empty and the element now at the top is either
    362         //    not in a document or its node document is not doc, repeat this substep.
    363         newTop = fullscreenElementFrom(*currentDoc);
    364         if (newTop && (!newTop->inDocument() || newTop->document() != currentDoc))
    365             continue;
    366 
    367         // 2. Queue a task to fire an event named fullscreenchange with its bubbles attribute set to true
    368         // on doc.
    369         enqueueChangeEvent(*currentDoc, requestType);
    370 
    371         // 3. If doc's fullscreen element stack is empty and doc's browsing context has a browsing context
    372         // container, set doc to that browsing context container's node document.
    373         if (!newTop && currentDoc->ownerElement()) {
    374             currentDoc = &currentDoc->ownerElement()->document();
    375             continue;
    376         }
    377 
    378         // 4. Otherwise, set doc to null.
    379         currentDoc = 0;
    380     }
    381 
    382     // 6. Return, and run the remaining steps asynchronously.
    383     // 7. Optionally, perform some animation.
    384 
    385     FrameHost* host = document()->frameHost();
    386 
    387     // Speculative fix for engaget.com/videos per crbug.com/336239.
    388     // FIXME: This check is wrong. We ASSERT(document->isActive()) above
    389     // so this should be redundant and should be removed!
    390     if (!host)
    391         return;
    392 
    393     // Only exit out of full screen window mode if there are no remaining elements in the
    394     // full screen stack.
    395     if (!newTop) {
    396         host->chrome().client().exitFullScreenForElement(m_fullScreenElement.get());
    397         return;
    398     }
    399 
    400     // Otherwise, notify the chrome of the new full screen element.
    401     host->chrome().client().enterFullScreenForElement(newTop);
    402 }
    403 
    404 bool Fullscreen::fullscreenEnabled(Document& document)
    405 {
    406     // 4. The fullscreenEnabled attribute must return true if the context object has its
    407     //    fullscreen enabled flag set and fullscreen is supported, and false otherwise.
    408 
    409     // Top-level browsing contexts are implied to have their allowFullScreen attribute set.
    410     return fullscreenIsAllowedForAllOwners(document) && fullscreenIsSupported(document);
    411 }
    412 
    413 void Fullscreen::didEnterFullScreenForElement(Element* element)
    414 {
    415     ASSERT(element);
    416     if (!document()->isActive())
    417         return;
    418 
    419     if (m_fullScreenRenderer)
    420         m_fullScreenRenderer->unwrapRenderer();
    421 
    422     m_fullScreenElement = element;
    423 
    424     // Create a placeholder block for a the full-screen element, to keep the page from reflowing
    425     // when the element is removed from the normal flow. Only do this for a RenderBox, as only
    426     // a box will have a frameRect. The placeholder will be created in setFullScreenRenderer()
    427     // during layout.
    428     RenderObject* renderer = m_fullScreenElement->renderer();
    429     bool shouldCreatePlaceholder = renderer && renderer->isBox();
    430     if (shouldCreatePlaceholder) {
    431         m_savedPlaceholderFrameRect = toRenderBox(renderer)->frameRect();
    432         m_savedPlaceholderRenderStyle = RenderStyle::clone(renderer->style());
    433     }
    434 
    435     if (m_fullScreenElement != document()->documentElement())
    436         RenderFullScreen::wrapRenderer(renderer, renderer ? renderer->parent() : 0, document());
    437 
    438     m_fullScreenElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(true);
    439 
    440     // FIXME: This should not call updateStyleIfNeeded.
    441     document()->setNeedsStyleRecalc(SubtreeStyleChange);
    442     document()->updateRenderTreeIfNeeded();
    443 
    444     m_fullScreenElement->didBecomeFullscreenElement();
    445 
    446     if (document()->frame())
    447         document()->frame()->eventHandler().scheduleHoverStateUpdate();
    448 
    449     m_eventQueueTimer.startOneShot(0, FROM_HERE);
    450 }
    451 
    452 void Fullscreen::didExitFullScreenForElement(Element*)
    453 {
    454     if (!m_fullScreenElement)
    455         return;
    456 
    457     if (!document()->isActive())
    458         return;
    459 
    460     m_fullScreenElement->willStopBeingFullscreenElement();
    461 
    462     m_fullScreenElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
    463 
    464     m_areKeysEnabledInFullScreen = false;
    465 
    466     if (m_fullScreenRenderer)
    467         m_fullScreenRenderer->unwrapRenderer();
    468 
    469     m_fullScreenElement = nullptr;
    470     document()->setNeedsStyleRecalc(SubtreeStyleChange);
    471 
    472     if (document()->frame())
    473         document()->frame()->eventHandler().scheduleHoverStateUpdate();
    474 
    475     // When fullyExitFullscreen is called, we call exitFullscreen on the topDocument(). That means
    476     // that the events will be queued there. So if we have no events here, start the timer on the
    477     // exiting document.
    478     Document* exitingDocument = document();
    479     if (m_eventQueue.isEmpty())
    480         exitingDocument = &document()->topDocument();
    481     ASSERT(exitingDocument);
    482     from(*exitingDocument).m_eventQueueTimer.startOneShot(0, FROM_HERE);
    483 }
    484 
    485 void Fullscreen::setFullScreenRenderer(RenderFullScreen* renderer)
    486 {
    487     if (renderer == m_fullScreenRenderer)
    488         return;
    489 
    490     if (renderer && m_savedPlaceholderRenderStyle) {
    491         renderer->createPlaceholder(m_savedPlaceholderRenderStyle.release(), m_savedPlaceholderFrameRect);
    492     } else if (renderer && m_fullScreenRenderer && m_fullScreenRenderer->placeholder()) {
    493         RenderBlock* placeholder = m_fullScreenRenderer->placeholder();
    494         renderer->createPlaceholder(RenderStyle::clone(placeholder->style()), placeholder->frameRect());
    495     }
    496 
    497     if (m_fullScreenRenderer)
    498         m_fullScreenRenderer->unwrapRenderer();
    499     ASSERT(!m_fullScreenRenderer);
    500 
    501     m_fullScreenRenderer = renderer;
    502 }
    503 
    504 void Fullscreen::fullScreenRendererDestroyed()
    505 {
    506     m_fullScreenRenderer = nullptr;
    507 }
    508 
    509 void Fullscreen::enqueueChangeEvent(Document& document, RequestType requestType)
    510 {
    511     RefPtrWillBeRawPtr<Event> event;
    512     if (requestType == UnprefixedRequest) {
    513         event = createEvent(EventTypeNames::fullscreenchange, document);
    514     } else {
    515         ASSERT(document.hasFullscreenSupplement());
    516         Fullscreen& fullscreen = from(document);
    517         EventTarget* target = fullscreen.fullscreenElement();
    518         if (!target)
    519             target = fullscreen.webkitCurrentFullScreenElement();
    520         if (!target)
    521             target = &document;
    522         event = createEvent(EventTypeNames::webkitfullscreenchange, *target);
    523     }
    524     m_eventQueue.append(event);
    525     // NOTE: The timer is started in didEnterFullScreenForElement/didExitFullScreenForElement.
    526 }
    527 
    528 void Fullscreen::enqueueErrorEvent(Element& element, RequestType requestType)
    529 {
    530     RefPtrWillBeRawPtr<Event> event;
    531     if (requestType == UnprefixedRequest)
    532         event = createEvent(EventTypeNames::fullscreenerror, element.document());
    533     else
    534         event = createEvent(EventTypeNames::webkitfullscreenerror, element);
    535     m_eventQueue.append(event);
    536     m_eventQueueTimer.startOneShot(0, FROM_HERE);
    537 }
    538 
    539 void Fullscreen::eventQueueTimerFired(Timer<Fullscreen>*)
    540 {
    541     // Since we dispatch events in this function, it's possible that the
    542     // document will be detached and GC'd. We protect it here to make sure we
    543     // can finish the function successfully.
    544     RefPtrWillBeRawPtr<Document> protectDocument(document());
    545     WillBeHeapDeque<RefPtrWillBeMember<Event> > eventQueue;
    546     m_eventQueue.swap(eventQueue);
    547 
    548     while (!eventQueue.isEmpty()) {
    549         RefPtrWillBeRawPtr<Event> event = eventQueue.takeFirst();
    550         Node* target = event->target()->toNode();
    551 
    552         // If the element was removed from our tree, also message the documentElement.
    553         if (!target->inDocument() && document()->documentElement()) {
    554             ASSERT(isPrefixed(event->type()));
    555             eventQueue.append(createEvent(event->type(), *document()->documentElement()));
    556         }
    557 
    558         target->dispatchEvent(event);
    559     }
    560 }
    561 
    562 void Fullscreen::elementRemoved(Element& oldNode)
    563 {
    564     // Whenever the removing steps run with an |oldNode| and |oldNode| is in its node document's
    565     // fullscreen element stack, run these steps:
    566 
    567     // 1. If |oldNode| is at the top of its node document's fullscreen element stack, act as if the
    568     //    exitFullscreen() method was invoked on that document.
    569     if (fullscreenElement() == &oldNode) {
    570         exitFullscreen();
    571         return;
    572     }
    573 
    574     // 2. Otherwise, remove |oldNode| from its node document's fullscreen element stack.
    575     for (size_t i = 0; i < m_fullScreenElementStack.size(); ++i) {
    576         if (m_fullScreenElementStack[i].first.get() == &oldNode) {
    577             m_fullScreenElementStack.remove(i);
    578             return;
    579         }
    580     }
    581 
    582     // NOTE: |oldNode| was not in the fullscreen element stack.
    583 }
    584 
    585 void Fullscreen::clearFullscreenElementStack()
    586 {
    587     m_fullScreenElementStack.clear();
    588 }
    589 
    590 void Fullscreen::popFullscreenElementStack()
    591 {
    592     if (m_fullScreenElementStack.isEmpty())
    593         return;
    594 
    595     m_fullScreenElementStack.removeLast();
    596 }
    597 
    598 void Fullscreen::pushFullscreenElementStack(Element& element, RequestType requestType)
    599 {
    600     m_fullScreenElementStack.append(std::make_pair(&element, requestType));
    601 }
    602 
    603 void Fullscreen::trace(Visitor* visitor)
    604 {
    605     visitor->trace(m_fullScreenElement);
    606     visitor->trace(m_fullScreenElementStack);
    607     visitor->trace(m_fullScreenRenderer);
    608     visitor->trace(m_eventQueue);
    609     DocumentSupplement::trace(visitor);
    610 }
    611 
    612 } // namespace blink
    613