• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2001 Dirk Mueller (mueller@kde.org)
5  *           (C) 2006 Alexey Proskuryakov (ap@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 
fullscreenIsAllowedForAllOwners(const Document & document)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 
fullscreenIsSupported(const Document & document)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 
fullscreenIsSupported(const Document & document,const Element & element)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 
supplementName()79 const char* FullscreenElementStack::supplementName()
80 {
81     return "FullscreenElementStack";
82 }
83 
from(Document & document)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 
fromIfExistsSlow(Document & document)95 FullscreenElementStack* FullscreenElementStack::fromIfExistsSlow(Document& document)
96 {
97     return static_cast<FullscreenElementStack*>(DocumentSupplement::from(document, supplementName()));
98 }
99 
fullscreenElementFrom(Document & document)100 Element* FullscreenElementStack::fullscreenElementFrom(Document& document)
101 {
102     if (FullscreenElementStack* found = fromIfExists(document))
103         return found->webkitFullscreenElement();
104     return 0;
105 }
106 
currentFullScreenElementFrom(Document & document)107 Element* FullscreenElementStack::currentFullScreenElementFrom(Document& document)
108 {
109     if (FullscreenElementStack* found = fromIfExists(document))
110         return found->webkitCurrentFullScreenElement();
111     return 0;
112 }
113 
isFullScreen(Document & document)114 bool FullscreenElementStack::isFullScreen(Document& document)
115 {
116     if (FullscreenElementStack* found = fromIfExists(document))
117         return found->webkitIsFullScreen();
118     return false;
119 }
120 
FullscreenElementStack(Document & document)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 
~FullscreenElementStack()130 FullscreenElementStack::~FullscreenElementStack()
131 {
132 }
133 
document()134 inline Document* FullscreenElementStack::document()
135 {
136     return lifecycleContext();
137 }
138 
documentWasDetached()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)
documentWasDisposed()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 
fullScreenIsAllowedForElement(Element * element) const164 bool FullscreenElementStack::fullScreenIsAllowedForElement(Element* element) const
165 {
166     ASSERT(element);
167     return fullscreenIsAllowedForAllOwners(element->document());
168 }
169 
requestFullScreenForElement(Element * element,unsigned short flags,FullScreenCheckType checkType)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 
webkitCancelFullScreen()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 
webkitExitFullscreen()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 
webkitFullscreenEnabled(Document & document)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 
webkitWillEnterFullScreenForElement(Element * element)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 
webkitDidEnterFullScreenForElement(Element *)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 
webkitWillExitFullScreenForElement(Element *)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 
webkitDidExitFullScreenForElement(Element *)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 
setFullScreenRenderer(RenderFullScreen * renderer)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 
fullScreenRendererDestroyed()502 void FullscreenElementStack::fullScreenRendererDestroyed()
503 {
504     m_fullScreenRenderer = 0;
505 }
506 
fullScreenChangeDelayTimerFired(Timer<FullscreenElementStack> *)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 
fullScreenElementRemoved()551 void FullscreenElementStack::fullScreenElementRemoved()
552 {
553     m_fullScreenElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
554     webkitCancelFullScreen();
555 }
556 
removeFullScreenElementOfSubtree(Node * node,bool amongChildrenOnly)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 
clearFullscreenElementStack()576 void FullscreenElementStack::clearFullscreenElementStack()
577 {
578     m_fullScreenElementStack.clear();
579 }
580 
popFullscreenElementStack()581 void FullscreenElementStack::popFullscreenElementStack()
582 {
583     if (m_fullScreenElementStack.isEmpty())
584         return;
585 
586     m_fullScreenElementStack.removeLast();
587 }
588 
pushFullscreenElementStack(Element * element)589 void FullscreenElementStack::pushFullscreenElementStack(Element* element)
590 {
591     m_fullScreenElementStack.append(element);
592 }
593 
addDocumentToFullScreenChangeEventQueue(Document * doc)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 
trace(Visitor * visitor)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