This source file includes following definitions.
- fullscreenIsAllowedForAllOwners
- supplementName
- from
- fromIfExistsSlow
- fullscreenElementFrom
- currentFullScreenElementFrom
- isFullScreen
- m_fullScreenChangeDelayTimer
- document
- documentWasDetached
- documentWasDisposed
- fullScreenIsAllowedForElement
- requestFullScreenForElement
- webkitCancelFullScreen
- webkitExitFullscreen
- webkitFullscreenEnabled
- webkitWillEnterFullScreenForElement
- webkitDidEnterFullScreenForElement
- webkitWillExitFullScreenForElement
- webkitDidExitFullScreenForElement
- setFullScreenRenderer
- fullScreenRendererDestroyed
- fullScreenChangeDelayTimerFired
- fullScreenElementRemoved
- removeFullScreenElementOfSubtree
- clearFullscreenElementStack
- popFullscreenElementStack
- pushFullscreenElementStack
- addDocumentToFullScreenChangeEventQueue
#include "config.h"
#include "core/dom/FullscreenElementStack.h"
#include "HTMLNames.h"
#include "core/dom/Document.h"
#include "core/events/Event.h"
#include "core/frame/FrameHost.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/UseCounter.h"
#include "core/html/HTMLFrameOwnerElement.h"
#include "core/page/Chrome.h"
#include "core/page/ChromeClient.h"
#include "core/rendering/RenderFullScreen.h"
#include "platform/UserGestureIndicator.h"
namespace WebCore {
using namespace HTMLNames;
static bool fullscreenIsAllowedForAllOwners(const Document& document)
{
const HTMLFrameOwnerElement* owner = document.ownerElement();
if (!owner)
return true;
do {
if (!owner->hasAttribute(allowfullscreenAttr)) {
if (owner->hasAttribute(webkitallowfullscreenAttr))
UseCounter::count(document, UseCounter::PrefixedAllowFullscreenAttribute);
else
return false;
}
} while ((owner = owner->document().ownerElement()));
return true;
}
const char* FullscreenElementStack::supplementName()
{
return "FullscreenElementStack";
}
FullscreenElementStack& FullscreenElementStack::from(Document& document)
{
FullscreenElementStack* fullscreen = fromIfExists(document);
if (!fullscreen) {
fullscreen = new FullscreenElementStack(document);
DocumentSupplement::provideTo(document, supplementName(), adoptPtr(fullscreen));
}
return *fullscreen;
}
FullscreenElementStack* FullscreenElementStack::fromIfExistsSlow(Document& document)
{
return static_cast<FullscreenElementStack*>(DocumentSupplement::from(document, supplementName()));
}
Element* FullscreenElementStack::fullscreenElementFrom(Document& document)
{
if (FullscreenElementStack* found = fromIfExists(document))
return found->webkitFullscreenElement();
return 0;
}
Element* FullscreenElementStack::currentFullScreenElementFrom(Document& document)
{
if (FullscreenElementStack* found = fromIfExists(document))
return found->webkitCurrentFullScreenElement();
return 0;
}
bool FullscreenElementStack::isFullScreen(Document& document)
{
if (FullscreenElementStack* found = fromIfExists(document))
return found->webkitIsFullScreen();
return false;
}
FullscreenElementStack::FullscreenElementStack(Document& document)
: DocumentLifecycleObserver(&document)
, m_areKeysEnabledInFullScreen(false)
, m_fullScreenRenderer(0)
, m_fullScreenChangeDelayTimer(this, &FullscreenElementStack::fullScreenChangeDelayTimerFired)
{
document.setHasFullscreenElementStack();
}
FullscreenElementStack::~FullscreenElementStack()
{
}
inline Document* FullscreenElementStack::document()
{
return lifecycleContext();
}
void FullscreenElementStack::documentWasDetached()
{
m_fullScreenChangeEventTargetQueue.clear();
m_fullScreenErrorEventTargetQueue.clear();
if (m_fullScreenRenderer)
setFullScreenRenderer(0);
}
void FullscreenElementStack::documentWasDisposed()
{
m_fullScreenElement = nullptr;
m_fullScreenElementStack.clear();
}
bool FullscreenElementStack::fullScreenIsAllowedForElement(Element* element) const
{
ASSERT(element);
return fullscreenIsAllowedForAllOwners(element->document());
}
void FullscreenElementStack::requestFullScreenForElement(Element* element, unsigned short flags, FullScreenCheckType checkType)
{
if (!document()->isActive())
return;
bool inLegacyMozillaMode = (flags & Element::LEGACY_MOZILLA_REQUEST);
do {
if (!element)
element = document()->documentElement();
if (!element->inDocument())
break;
if (checkType == EnforceIFrameAllowFullScreenRequirement && !fullScreenIsAllowedForElement(element))
break;
if (!m_fullScreenElementStack.isEmpty() && !inLegacyMozillaMode) {
Element* lastElementOnStack = m_fullScreenElementStack.last().get();
if (lastElementOnStack == element || !lastElementOnStack->contains(element))
break;
}
bool descendentHasNonEmptyStack = false;
for (LocalFrame* descendant = document()->frame() ? document()->frame()->tree().traverseNext() : 0; descendant; descendant = descendant->tree().traverseNext()) {
ASSERT(descendant->document());
if (fullscreenElementFrom(*descendant->document())) {
descendentHasNonEmptyStack = true;
break;
}
}
if (descendentHasNonEmptyStack && !inLegacyMozillaMode)
break;
if (!UserGestureIndicator::processingUserGesture())
break;
Document* currentDoc = document();
Deque<Document*> docs;
do {
docs.prepend(currentDoc);
currentDoc = currentDoc->ownerElement() ? ¤tDoc->ownerElement()->document() : 0;
} while (currentDoc);
Deque<Document*>::iterator current = docs.begin(), following = docs.begin();
do {
++following;
Document* currentDoc = *current;
Document* followingDoc = following != docs.end() ? *following : 0;
if (!followingDoc) {
from(*currentDoc).pushFullscreenElementStack(element);
addDocumentToFullScreenChangeEventQueue(currentDoc);
continue;
}
Element* topElement = fullscreenElementFrom(*currentDoc);
if (!topElement || topElement != followingDoc->ownerElement()) {
from(*currentDoc).pushFullscreenElementStack(followingDoc->ownerElement());
addDocumentToFullScreenChangeEventQueue(currentDoc);
continue;
}
} while (++current != docs.end());
m_areKeysEnabledInFullScreen = flags & Element::ALLOW_KEYBOARD_INPUT;
document()->frameHost()->chrome().client().enterFullScreenForElement(element);
return;
} while (0);
m_fullScreenErrorEventTargetQueue.append(element ? element : document()->documentElement());
m_fullScreenChangeDelayTimer.startOneShot(0, FROM_HERE);
}
void FullscreenElementStack::webkitCancelFullScreen()
{
if (!fullscreenElementFrom(document()->topDocument()))
return;
Vector<RefPtr<Element> > replacementFullscreenElementStack;
replacementFullscreenElementStack.append(fullscreenElementFrom(document()->topDocument()));
FullscreenElementStack& topFullscreenElementStack = from(document()->topDocument());
topFullscreenElementStack.m_fullScreenElementStack.swap(replacementFullscreenElementStack);
topFullscreenElementStack.webkitExitFullscreen();
}
void FullscreenElementStack::webkitExitFullscreen()
{
Document* currentDoc = document();
ASSERT(currentDoc->isActive());
if (m_fullScreenElementStack.isEmpty())
return;
Deque<RefPtr<Document> > descendants;
for (LocalFrame* descendant = document()->frame() ? document()->frame()->tree().traverseNext() : 0; descendant; descendant = descendant->tree().traverseNext()) {
ASSERT(descendant->document());
if (fullscreenElementFrom(*descendant->document()))
descendants.prepend(descendant->document());
}
for (Deque<RefPtr<Document> >::iterator i = descendants.begin(); i != descendants.end(); ++i) {
ASSERT(*i);
from(**i).clearFullscreenElementStack();
addDocumentToFullScreenChangeEventQueue(i->get());
}
Element* newTop = 0;
while (currentDoc) {
from(*currentDoc).popFullscreenElementStack();
newTop = fullscreenElementFrom(*currentDoc);
if (newTop && (!newTop->inDocument() || newTop->document() != currentDoc))
continue;
addDocumentToFullScreenChangeEventQueue(currentDoc);
if (!newTop && currentDoc->ownerElement()) {
currentDoc = ¤tDoc->ownerElement()->document();
continue;
}
currentDoc = 0;
}
FrameHost* host = document()->frameHost();
if (!host)
return;
if (!newTop) {
host->chrome().client().exitFullScreenForElement(m_fullScreenElement.get());
return;
}
host->chrome().client().enterFullScreenForElement(newTop);
}
bool FullscreenElementStack::webkitFullscreenEnabled(Document& document)
{
return fullscreenIsAllowedForAllOwners(document);
}
void FullscreenElementStack::webkitWillEnterFullScreenForElement(Element* element)
{
ASSERT(element);
if (!document()->isActive())
return;
if (m_fullScreenRenderer)
m_fullScreenRenderer->unwrapRenderer();
m_fullScreenElement = element;
RenderObject* renderer = m_fullScreenElement->renderer();
bool shouldCreatePlaceholder = renderer && renderer->isBox();
if (shouldCreatePlaceholder) {
m_savedPlaceholderFrameRect = toRenderBox(renderer)->frameRect();
m_savedPlaceholderRenderStyle = RenderStyle::clone(renderer->style());
}
if (m_fullScreenElement != document()->documentElement())
RenderFullScreen::wrapRenderer(renderer, renderer ? renderer->parent() : 0, document());
m_fullScreenElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(true);
document()->setNeedsStyleRecalc(SubtreeStyleChange);
document()->updateRenderTreeIfNeeded();
}
void FullscreenElementStack::webkitDidEnterFullScreenForElement(Element*)
{
if (!m_fullScreenElement)
return;
if (!document()->isActive())
return;
m_fullScreenElement->didBecomeFullscreenElement();
m_fullScreenChangeDelayTimer.startOneShot(0, FROM_HERE);
}
void FullscreenElementStack::webkitWillExitFullScreenForElement(Element*)
{
if (!m_fullScreenElement)
return;
if (!document()->isActive())
return;
m_fullScreenElement->willStopBeingFullscreenElement();
}
void FullscreenElementStack::webkitDidExitFullScreenForElement(Element*)
{
if (!m_fullScreenElement)
return;
if (!document()->isActive())
return;
m_fullScreenElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
m_areKeysEnabledInFullScreen = false;
if (m_fullScreenRenderer)
m_fullScreenRenderer->unwrapRenderer();
m_fullScreenElement = nullptr;
document()->setNeedsStyleRecalc(SubtreeStyleChange);
Document* exitingDocument = document();
if (m_fullScreenChangeEventTargetQueue.isEmpty() && m_fullScreenErrorEventTargetQueue.isEmpty())
exitingDocument = &document()->topDocument();
ASSERT(exitingDocument);
from(*exitingDocument).m_fullScreenChangeDelayTimer.startOneShot(0, FROM_HERE);
}
void FullscreenElementStack::setFullScreenRenderer(RenderFullScreen* renderer)
{
if (renderer == m_fullScreenRenderer)
return;
if (renderer && m_savedPlaceholderRenderStyle) {
renderer->createPlaceholder(m_savedPlaceholderRenderStyle.release(), m_savedPlaceholderFrameRect);
} else if (renderer && m_fullScreenRenderer && m_fullScreenRenderer->placeholder()) {
RenderBlock* placeholder = m_fullScreenRenderer->placeholder();
renderer->createPlaceholder(RenderStyle::clone(placeholder->style()), placeholder->frameRect());
}
if (m_fullScreenRenderer)
m_fullScreenRenderer->destroy();
ASSERT(!m_fullScreenRenderer);
m_fullScreenRenderer = renderer;
}
void FullscreenElementStack::fullScreenRendererDestroyed()
{
m_fullScreenRenderer = 0;
}
void FullscreenElementStack::fullScreenChangeDelayTimerFired(Timer<FullscreenElementStack>*)
{
RefPtr<Document> protectDocument(document());
Deque<RefPtr<Node> > changeQueue;
m_fullScreenChangeEventTargetQueue.swap(changeQueue);
Deque<RefPtr<Node> > errorQueue;
m_fullScreenErrorEventTargetQueue.swap(errorQueue);
while (!changeQueue.isEmpty()) {
RefPtr<Node> node = changeQueue.takeFirst();
if (!node)
node = document()->documentElement();
if (!node)
continue;
if (!document()->contains(node.get()) && !node->inDocument())
changeQueue.append(document()->documentElement());
node->dispatchEvent(Event::createBubble(EventTypeNames::webkitfullscreenchange));
}
while (!errorQueue.isEmpty()) {
RefPtr<Node> node = errorQueue.takeFirst();
if (!node)
node = document()->documentElement();
if (!node)
continue;
if (!document()->contains(node.get()) && !node->inDocument())
errorQueue.append(document()->documentElement());
node->dispatchEvent(Event::createBubble(EventTypeNames::webkitfullscreenerror));
}
}
void FullscreenElementStack::fullScreenElementRemoved()
{
m_fullScreenElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
webkitCancelFullScreen();
}
void FullscreenElementStack::removeFullScreenElementOfSubtree(Node* node, bool amongChildrenOnly)
{
if (!m_fullScreenElement)
return;
if (!node->inDocument())
return;
bool elementInSubtree = false;
if (amongChildrenOnly)
elementInSubtree = m_fullScreenElement->isDescendantOf(node);
else
elementInSubtree = (m_fullScreenElement == node) || m_fullScreenElement->isDescendantOf(node);
if (elementInSubtree)
fullScreenElementRemoved();
}
void FullscreenElementStack::clearFullscreenElementStack()
{
m_fullScreenElementStack.clear();
}
void FullscreenElementStack::popFullscreenElementStack()
{
if (m_fullScreenElementStack.isEmpty())
return;
m_fullScreenElementStack.removeLast();
}
void FullscreenElementStack::pushFullscreenElementStack(Element* element)
{
m_fullScreenElementStack.append(element);
}
void FullscreenElementStack::addDocumentToFullScreenChangeEventQueue(Document* doc)
{
ASSERT(doc);
Node* target = 0;
if (FullscreenElementStack* fullscreen = fromIfExists(*doc)) {
target = fullscreen->webkitFullscreenElement();
if (!target)
target = fullscreen->webkitCurrentFullScreenElement();
}
if (!target)
target = doc;
m_fullScreenChangeEventTargetQueue.append(target);
}
}