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/FullscreenElementStack.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/HTMLFrameOwnerElement.h"
     39 #include "core/html/HTMLMediaElement.h"
     40 #include "core/page/Chrome.h"
     41 #include "core/page/ChromeClient.h"
     42 #include "core/rendering/RenderFullScreen.h"
     43 #include "platform/UserGestureIndicator.h"
     44 
     45 namespace WebCore {
     46 
     47 using namespace HTMLNames;
     48 
     49 static bool fullscreenIsAllowedForAllOwners(const Document& document)
     50 {
     51     const HTMLFrameOwnerElement* owner = document.ownerElement();
     52     if (!owner)
     53         return true;
     54     do {
     55         if (!owner->hasAttribute(allowfullscreenAttr)) {
     56             if (owner->hasAttribute(webkitallowfullscreenAttr))
     57                 UseCounter::count(document, UseCounter::PrefixedAllowFullscreenAttribute);
     58             else
     59                 return false;
     60         }
     61     } while ((owner = owner->document().ownerElement()));
     62     return true;
     63 }
     64 
     65 static bool fullscreenIsSupported(const Document& document)
     66 {
     67     // Fullscreen is supported if there is no previously-established user preference,
     68     // security risk, or platform limitation.
     69     return !document.settings() || document.settings()->fullscreenSupported();
     70 }
     71 
     72 static bool fullscreenIsSupported(const Document& document, const Element& element)
     73 {
     74     if (!document.settings() || (document.settings()->disallowFullscreenForNonMediaElements() && !isHTMLMediaElement(element)))
     75         return false;
     76     return fullscreenIsSupported(document);
     77 }
     78 
     79 const char* FullscreenElementStack::supplementName()
     80 {
     81     return "FullscreenElementStack";
     82 }
     83 
     84 FullscreenElementStack& FullscreenElementStack::from(Document& document)
     85 {
     86     FullscreenElementStack* fullscreen = fromIfExists(document);
     87     if (!fullscreen) {
     88         fullscreen = new FullscreenElementStack(document);
     89         DocumentSupplement::provideTo(document, supplementName(), adoptPtrWillBeNoop(fullscreen));
     90     }
     91 
     92     return *fullscreen;
     93 }
     94 
     95 FullscreenElementStack* FullscreenElementStack::fromIfExistsSlow(Document& document)
     96 {
     97     return static_cast<FullscreenElementStack*>(DocumentSupplement::from(document, supplementName()));
     98 }
     99 
    100 Element* FullscreenElementStack::fullscreenElementFrom(Document& document)
    101 {
    102     if (FullscreenElementStack* found = fromIfExists(document))
    103         return found->webkitFullscreenElement();
    104     return 0;
    105 }
    106 
    107 Element* FullscreenElementStack::currentFullScreenElementFrom(Document& document)
    108 {
    109     if (FullscreenElementStack* found = fromIfExists(document))
    110         return found->webkitCurrentFullScreenElement();
    111     return 0;
    112 }
    113 
    114 bool FullscreenElementStack::isFullScreen(Document& document)
    115 {
    116     if (FullscreenElementStack* found = fromIfExists(document))
    117         return found->webkitIsFullScreen();
    118     return false;
    119 }
    120 
    121 FullscreenElementStack::FullscreenElementStack(Document& document)
    122     : DocumentLifecycleObserver(&document)
    123     , m_areKeysEnabledInFullScreen(false)
    124     , m_fullScreenRenderer(0)
    125     , m_fullScreenChangeDelayTimer(this, &FullscreenElementStack::fullScreenChangeDelayTimerFired)
    126 {
    127     document.setHasFullscreenElementStack();
    128 }
    129 
    130 FullscreenElementStack::~FullscreenElementStack()
    131 {
    132 }
    133 
    134 inline Document* FullscreenElementStack::document()
    135 {
    136     return lifecycleContext();
    137 }
    138 
    139 void FullscreenElementStack::documentWasDetached()
    140 {
    141     m_fullScreenChangeEventTargetQueue.clear();
    142     m_fullScreenErrorEventTargetQueue.clear();
    143 
    144     if (m_fullScreenRenderer)
    145         m_fullScreenRenderer->destroy();
    146 
    147 #if ENABLE(OILPAN)
    148     m_fullScreenElement = nullptr;
    149     m_fullScreenElementStack.clear();
    150 #endif
    151 
    152 }
    153 
    154 #if !ENABLE(OILPAN)
    155 void FullscreenElementStack::documentWasDisposed()
    156 {
    157     // NOTE: the context dispose phase is not supported in oilpan. Please
    158     // consider using the detach phase instead.
    159     m_fullScreenElement = nullptr;
    160     m_fullScreenElementStack.clear();
    161 }
    162 #endif
    163 
    164 bool FullscreenElementStack::fullScreenIsAllowedForElement(Element* element) const
    165 {
    166     ASSERT(element);
    167     return fullscreenIsAllowedForAllOwners(element->document());
    168 }
    169 
    170 void FullscreenElementStack::requestFullScreenForElement(Element* element, unsigned short flags, FullScreenCheckType checkType)
    171 {
    172     // Ignore this request if the document is not in a live frame.
    173     if (!document()->isActive())
    174         return;
    175 
    176     // The Mozilla Full Screen API <https://wiki.mozilla.org/Gecko:FullScreenAPI> has different requirements
    177     // for full screen mode, and do not have the concept of a full screen element stack.
    178     bool inLegacyMozillaMode = (flags & Element::LEGACY_MOZILLA_REQUEST);
    179 
    180     do {
    181         if (!element)
    182             element = document()->documentElement();
    183 
    184         // 1. If any of the following conditions are true, terminate these steps and queue a task to fire
    185         // an event named fullscreenerror with its bubbles attribute set to true on the context object's
    186         // node document:
    187 
    188         // The context object is not in a document.
    189         if (!element->inDocument())
    190             break;
    191 
    192         // The context object's node document, or an ancestor browsing context's document does not have
    193         // the fullscreen enabled flag set.
    194         if (checkType == EnforceIFrameAllowFullScreenRequirement && !fullScreenIsAllowedForElement(element))
    195             break;
    196 
    197         // The context object's node document fullscreen element stack is not empty and its top element
    198         // is not an ancestor of the context object. (NOTE: Ignore this requirement if the request was
    199         // made via the legacy Mozilla-style API.)
    200         if (!m_fullScreenElementStack.isEmpty() && !inLegacyMozillaMode) {
    201             Element* lastElementOnStack = m_fullScreenElementStack.last().get();
    202             if (lastElementOnStack == element || !lastElementOnStack->contains(element))
    203                 break;
    204         }
    205 
    206         // A descendant browsing context's document has a non-empty fullscreen element stack.
    207         bool descendentHasNonEmptyStack = false;
    208         for (Frame* descendant = document()->frame() ? document()->frame()->tree().traverseNext() : 0; descendant; descendant = descendant->tree().traverseNext()) {
    209             if (!descendant->isLocalFrame())
    210                 continue;
    211             ASSERT(toLocalFrame(descendant)->document());
    212             if (fullscreenElementFrom(*toLocalFrame(descendant)->document())) {
    213                 descendentHasNonEmptyStack = true;
    214                 break;
    215             }
    216         }
    217         if (descendentHasNonEmptyStack && !inLegacyMozillaMode)
    218             break;
    219 
    220         // This algorithm is not allowed to show a pop-up:
    221         //   An algorithm is allowed to show a pop-up if, in the task in which the algorithm is running, either:
    222         //   - an activation behavior is currently being processed whose click event was trusted, or
    223         //   - the event listener for a trusted click event is being handled.
    224         if (!UserGestureIndicator::processingUserGesture())
    225             break;
    226 
    227         // Fullscreen is not supported.
    228         if (document() && !fullscreenIsSupported(*document(), *element))
    229             break;
    230 
    231         // 2. Let doc be element's node document. (i.e. "this")
    232         Document* currentDoc = document();
    233 
    234         // 3. Let docs be all doc's ancestor browsing context's documents (if any) and doc.
    235         Deque<Document*> docs;
    236 
    237         do {
    238             docs.prepend(currentDoc);
    239             currentDoc = currentDoc->ownerElement() ? &currentDoc->ownerElement()->document() : 0;
    240         } while (currentDoc);
    241 
    242         // 4. For each document in docs, run these substeps:
    243         Deque<Document*>::iterator current = docs.begin(), following = docs.begin();
    244 
    245         do {
    246             ++following;
    247 
    248             // 1. Let following document be the document after document in docs, or null if there is no
    249             // such document.
    250             Document* currentDoc = *current;
    251             Document* followingDoc = following != docs.end() ? *following : 0;
    252 
    253             // 2. If following document is null, push context object on document's fullscreen element
    254             // stack, and queue a task to fire an event named fullscreenchange with its bubbles attribute
    255             // set to true on the document.
    256             if (!followingDoc) {
    257                 from(*currentDoc).pushFullscreenElementStack(element);
    258                 addDocumentToFullScreenChangeEventQueue(currentDoc);
    259                 continue;
    260             }
    261 
    262             // 3. Otherwise, if document's fullscreen element stack is either empty or its top element
    263             // is not following document's browsing context container,
    264             Element* topElement = fullscreenElementFrom(*currentDoc);
    265             if (!topElement || topElement != followingDoc->ownerElement()) {
    266                 // ...push following document's browsing context container on document's fullscreen element
    267                 // stack, and queue a task to fire an event named fullscreenchange with its bubbles attribute
    268                 // set to true on document.
    269                 from(*currentDoc).pushFullscreenElementStack(followingDoc->ownerElement());
    270                 addDocumentToFullScreenChangeEventQueue(currentDoc);
    271                 continue;
    272             }
    273 
    274             // 4. Otherwise, do nothing for this document. It stays the same.
    275         } while (++current != docs.end());
    276 
    277         // 5. Return, and run the remaining steps asynchronously.
    278         // 6. Optionally, perform some animation.
    279         m_areKeysEnabledInFullScreen = flags & Element::ALLOW_KEYBOARD_INPUT;
    280         document()->frameHost()->chrome().client().enterFullScreenForElement(element);
    281 
    282         // 7. Optionally, display a message indicating how the user can exit displaying the context object fullscreen.
    283         return;
    284     } while (0);
    285 
    286     m_fullScreenErrorEventTargetQueue.append(element ? element : document()->documentElement());
    287     m_fullScreenChangeDelayTimer.startOneShot(0, FROM_HERE);
    288 }
    289 
    290 void FullscreenElementStack::webkitCancelFullScreen()
    291 {
    292     // The Mozilla "cancelFullScreen()" API behaves like the W3C "fully exit fullscreen" behavior, which
    293     // is defined as:
    294     // "To fully exit fullscreen act as if the exitFullscreen() method was invoked on the top-level browsing
    295     // context's document and subsequently empty that document's fullscreen element stack."
    296     if (!fullscreenElementFrom(document()->topDocument()))
    297         return;
    298 
    299     // To achieve that aim, remove all the elements from the top document's stack except for the first before
    300     // calling webkitExitFullscreen():
    301     WillBeHeapVector<RefPtrWillBeMember<Element> > replacementFullscreenElementStack;
    302     replacementFullscreenElementStack.append(fullscreenElementFrom(document()->topDocument()));
    303     FullscreenElementStack& topFullscreenElementStack = from(document()->topDocument());
    304     topFullscreenElementStack.m_fullScreenElementStack.swap(replacementFullscreenElementStack);
    305     topFullscreenElementStack.webkitExitFullscreen();
    306 }
    307 
    308 void FullscreenElementStack::webkitExitFullscreen()
    309 {
    310     // The exitFullscreen() method must run these steps:
    311 
    312     // 1. Let doc be the context object. (i.e. "this")
    313     Document* currentDoc = document();
    314     if (!currentDoc->isActive())
    315         return;
    316 
    317     // 2. If doc's fullscreen element stack is empty, terminate these steps.
    318     if (m_fullScreenElementStack.isEmpty())
    319         return;
    320 
    321     // 3. Let descendants be all the doc's descendant browsing context's documents with a non-empty fullscreen
    322     // element stack (if any), ordered so that the child of the doc is last and the document furthest
    323     // away from the doc is first.
    324     WillBeHeapDeque<RefPtrWillBeMember<Document> > descendants;
    325     for (Frame* descendant = document()->frame() ? document()->frame()->tree().traverseNext() : 0; descendant; descendant = descendant->tree().traverseNext()) {
    326         if (!descendant->isLocalFrame())
    327             continue;
    328         ASSERT(toLocalFrame(descendant)->document());
    329         if (fullscreenElementFrom(*toLocalFrame(descendant)->document()))
    330             descendants.prepend(toLocalFrame(descendant)->document());
    331     }
    332 
    333     // 4. For each descendant in descendants, empty descendant's fullscreen element stack, and queue a
    334     // task to fire an event named fullscreenchange with its bubbles attribute set to true on descendant.
    335     for (WillBeHeapDeque<RefPtrWillBeMember<Document> >::iterator i = descendants.begin(); i != descendants.end(); ++i) {
    336         ASSERT(*i);
    337         from(**i).clearFullscreenElementStack();
    338         addDocumentToFullScreenChangeEventQueue(i->get());
    339     }
    340 
    341     // 5. While doc is not null, run these substeps:
    342     Element* newTop = 0;
    343     while (currentDoc) {
    344         // 1. Pop the top element of doc's fullscreen element stack.
    345         from(*currentDoc).popFullscreenElementStack();
    346 
    347         //    If doc's fullscreen element stack is non-empty and the element now at the top is either
    348         //    not in a document or its node document is not doc, repeat this substep.
    349         newTop = fullscreenElementFrom(*currentDoc);
    350         if (newTop && (!newTop->inDocument() || newTop->document() != currentDoc))
    351             continue;
    352 
    353         // 2. Queue a task to fire an event named fullscreenchange with its bubbles attribute set to true
    354         // on doc.
    355         addDocumentToFullScreenChangeEventQueue(currentDoc);
    356 
    357         // 3. If doc's fullscreen element stack is empty and doc's browsing context has a browsing context
    358         // container, set doc to that browsing context container's node document.
    359         if (!newTop && currentDoc->ownerElement()) {
    360             currentDoc = &currentDoc->ownerElement()->document();
    361             continue;
    362         }
    363 
    364         // 4. Otherwise, set doc to null.
    365         currentDoc = 0;
    366     }
    367 
    368     // 6. Return, and run the remaining steps asynchronously.
    369     // 7. Optionally, perform some animation.
    370 
    371     FrameHost* host = document()->frameHost();
    372 
    373     // Speculative fix for engaget.com/videos per crbug.com/336239.
    374     // FIXME: This check is wrong. We ASSERT(document->isActive()) above
    375     // so this should be redundant and should be removed!
    376     if (!host)
    377         return;
    378 
    379     // Only exit out of full screen window mode if there are no remaining elements in the
    380     // full screen stack.
    381     if (!newTop) {
    382         host->chrome().client().exitFullScreenForElement(m_fullScreenElement.get());
    383         return;
    384     }
    385 
    386     // Otherwise, notify the chrome of the new full screen element.
    387     host->chrome().client().enterFullScreenForElement(newTop);
    388 }
    389 
    390 bool FullscreenElementStack::webkitFullscreenEnabled(Document& document)
    391 {
    392     // 4. The fullscreenEnabled attribute must return true if the context object has its
    393     //    fullscreen enabled flag set and fullscreen is supported, and false otherwise.
    394 
    395     // Top-level browsing contexts are implied to have their allowFullScreen attribute set.
    396     return fullscreenIsAllowedForAllOwners(document) && fullscreenIsSupported(document);
    397 }
    398 
    399 void FullscreenElementStack::webkitWillEnterFullScreenForElement(Element* element)
    400 {
    401     ASSERT(element);
    402     if (!document()->isActive())
    403         return;
    404 
    405     if (m_fullScreenRenderer)
    406         m_fullScreenRenderer->unwrapRenderer();
    407 
    408     m_fullScreenElement = element;
    409 
    410     // Create a placeholder block for a the full-screen element, to keep the page from reflowing
    411     // when the element is removed from the normal flow. Only do this for a RenderBox, as only
    412     // a box will have a frameRect. The placeholder will be created in setFullScreenRenderer()
    413     // during layout.
    414     RenderObject* renderer = m_fullScreenElement->renderer();
    415     bool shouldCreatePlaceholder = renderer && renderer->isBox();
    416     if (shouldCreatePlaceholder) {
    417         m_savedPlaceholderFrameRect = toRenderBox(renderer)->frameRect();
    418         m_savedPlaceholderRenderStyle = RenderStyle::clone(renderer->style());
    419     }
    420 
    421     if (m_fullScreenElement != document()->documentElement())
    422         RenderFullScreen::wrapRenderer(renderer, renderer ? renderer->parent() : 0, document());
    423 
    424     m_fullScreenElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(true);
    425 
    426     // FIXME: This should not call updateStyleIfNeeded.
    427     document()->setNeedsStyleRecalc(SubtreeStyleChange);
    428     document()->updateRenderTreeIfNeeded();
    429 }
    430 
    431 void FullscreenElementStack::webkitDidEnterFullScreenForElement(Element*)
    432 {
    433     if (!m_fullScreenElement)
    434         return;
    435 
    436     if (!document()->isActive())
    437         return;
    438 
    439     m_fullScreenElement->didBecomeFullscreenElement();
    440 
    441     m_fullScreenChangeDelayTimer.startOneShot(0, FROM_HERE);
    442 }
    443 
    444 void FullscreenElementStack::webkitWillExitFullScreenForElement(Element*)
    445 {
    446     if (!m_fullScreenElement)
    447         return;
    448 
    449     if (!document()->isActive())
    450         return;
    451 
    452     m_fullScreenElement->willStopBeingFullscreenElement();
    453 }
    454 
    455 void FullscreenElementStack::webkitDidExitFullScreenForElement(Element*)
    456 {
    457     if (!m_fullScreenElement)
    458         return;
    459 
    460     if (!document()->isActive())
    461         return;
    462 
    463     m_fullScreenElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
    464 
    465     m_areKeysEnabledInFullScreen = false;
    466 
    467     if (m_fullScreenRenderer)
    468         m_fullScreenRenderer->unwrapRenderer();
    469 
    470     m_fullScreenElement = nullptr;
    471     document()->setNeedsStyleRecalc(SubtreeStyleChange);
    472 
    473     // When webkitCancelFullScreen is called, we call webkitExitFullScreen on the topDocument(). That
    474     // means that the events will be queued there. So if we have no events here, start the timer on
    475     // the exiting document.
    476     Document* exitingDocument = document();
    477     if (m_fullScreenChangeEventTargetQueue.isEmpty() && m_fullScreenErrorEventTargetQueue.isEmpty())
    478         exitingDocument = &document()->topDocument();
    479     ASSERT(exitingDocument);
    480     from(*exitingDocument).m_fullScreenChangeDelayTimer.startOneShot(0, FROM_HERE);
    481 }
    482 
    483 void FullscreenElementStack::setFullScreenRenderer(RenderFullScreen* renderer)
    484 {
    485     if (renderer == m_fullScreenRenderer)
    486         return;
    487 
    488     if (renderer && m_savedPlaceholderRenderStyle) {
    489         renderer->createPlaceholder(m_savedPlaceholderRenderStyle.release(), m_savedPlaceholderFrameRect);
    490     } else if (renderer && m_fullScreenRenderer && m_fullScreenRenderer->placeholder()) {
    491         RenderBlock* placeholder = m_fullScreenRenderer->placeholder();
    492         renderer->createPlaceholder(RenderStyle::clone(placeholder->style()), placeholder->frameRect());
    493     }
    494 
    495     if (m_fullScreenRenderer)
    496         m_fullScreenRenderer->unwrapRenderer();
    497     ASSERT(!m_fullScreenRenderer);
    498 
    499     m_fullScreenRenderer = renderer;
    500 }
    501 
    502 void FullscreenElementStack::fullScreenRendererDestroyed()
    503 {
    504     m_fullScreenRenderer = 0;
    505 }
    506 
    507 void FullscreenElementStack::fullScreenChangeDelayTimerFired(Timer<FullscreenElementStack>*)
    508 {
    509     // Since we dispatch events in this function, it's possible that the
    510     // document will be detached and GC'd. We protect it here to make sure we
    511     // can finish the function successfully.
    512     RefPtrWillBeRawPtr<Document> protectDocument(document());
    513     WillBeHeapDeque<RefPtrWillBeMember<Node> > changeQueue;
    514     m_fullScreenChangeEventTargetQueue.swap(changeQueue);
    515     WillBeHeapDeque<RefPtrWillBeMember<Node> > errorQueue;
    516     m_fullScreenErrorEventTargetQueue.swap(errorQueue);
    517 
    518     while (!changeQueue.isEmpty()) {
    519         RefPtrWillBeRawPtr<Node> node = changeQueue.takeFirst();
    520         if (!node)
    521             node = document()->documentElement();
    522         // The dispatchEvent below may have blown away our documentElement.
    523         if (!node)
    524             continue;
    525 
    526         // If the element was removed from our tree, also message the documentElement. Since we may
    527         // have a document hierarchy, check that node isn't in another document.
    528         if (!document()->contains(node.get()) && !node->inDocument())
    529             changeQueue.append(document()->documentElement());
    530 
    531         node->dispatchEvent(Event::createBubble(EventTypeNames::webkitfullscreenchange));
    532     }
    533 
    534     while (!errorQueue.isEmpty()) {
    535         RefPtrWillBeRawPtr<Node> node = errorQueue.takeFirst();
    536         if (!node)
    537             node = document()->documentElement();
    538         // The dispatchEvent below may have blown away our documentElement.
    539         if (!node)
    540             continue;
    541 
    542         // If the element was removed from our tree, also message the documentElement. Since we may
    543         // have a document hierarchy, check that node isn't in another document.
    544         if (!document()->contains(node.get()) && !node->inDocument())
    545             errorQueue.append(document()->documentElement());
    546 
    547         node->dispatchEvent(Event::createBubble(EventTypeNames::webkitfullscreenerror));
    548     }
    549 }
    550 
    551 void FullscreenElementStack::fullScreenElementRemoved()
    552 {
    553     m_fullScreenElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
    554     webkitCancelFullScreen();
    555 }
    556 
    557 void FullscreenElementStack::removeFullScreenElementOfSubtree(Node* node, bool amongChildrenOnly)
    558 {
    559     if (!m_fullScreenElement)
    560         return;
    561 
    562     // If the node isn't in a document it can't have a fullscreen'd child.
    563     if (!node->inDocument())
    564         return;
    565 
    566     bool elementInSubtree = false;
    567     if (amongChildrenOnly)
    568         elementInSubtree = m_fullScreenElement->isDescendantOf(node);
    569     else
    570         elementInSubtree = (m_fullScreenElement == node) || m_fullScreenElement->isDescendantOf(node);
    571 
    572     if (elementInSubtree)
    573         fullScreenElementRemoved();
    574 }
    575 
    576 void FullscreenElementStack::clearFullscreenElementStack()
    577 {
    578     m_fullScreenElementStack.clear();
    579 }
    580 
    581 void FullscreenElementStack::popFullscreenElementStack()
    582 {
    583     if (m_fullScreenElementStack.isEmpty())
    584         return;
    585 
    586     m_fullScreenElementStack.removeLast();
    587 }
    588 
    589 void FullscreenElementStack::pushFullscreenElementStack(Element* element)
    590 {
    591     m_fullScreenElementStack.append(element);
    592 }
    593 
    594 void FullscreenElementStack::addDocumentToFullScreenChangeEventQueue(Document* doc)
    595 {
    596     ASSERT(doc);
    597 
    598     Node* target = 0;
    599     if (FullscreenElementStack* fullscreen = fromIfExists(*doc)) {
    600         target = fullscreen->webkitFullscreenElement();
    601         if (!target)
    602             target = fullscreen->webkitCurrentFullScreenElement();
    603     }
    604 
    605     if (!target)
    606         target = doc;
    607     m_fullScreenChangeEventTargetQueue.append(target);
    608 }
    609 
    610 void FullscreenElementStack::trace(Visitor* visitor)
    611 {
    612     visitor->trace(m_fullScreenElement);
    613     visitor->trace(m_fullScreenElementStack);
    614     visitor->trace(m_fullScreenChangeEventTargetQueue);
    615     visitor->trace(m_fullScreenErrorEventTargetQueue);
    616     DocumentSupplement::trace(visitor);
    617 }
    618 
    619 } // namespace WebCore
    620