#include "HTMLNames.h"
#include "core/dom/Document.h"
-#include "core/dom/Element.h"
#include "core/events/Event.h"
+#include "core/frame/FrameHost.h"
+#include "core/frame/LocalFrame.h"
+#include "core/frame/Settings.h"
+#include "core/frame/UseCounter.h"
#include "core/html/HTMLFrameOwnerElement.h"
+#include "core/html/HTMLMediaElement.h"
#include "core/page/Chrome.h"
#include "core/page/ChromeClient.h"
-#include "core/frame/Frame.h"
-#include "core/page/Page.h"
-#include "core/page/Settings.h"
#include "core/rendering/RenderFullScreen.h"
#include "platform/UserGestureIndicator.h"
using namespace HTMLNames;
-static bool isAttributeOnAllOwners(const WebCore::QualifiedName& attribute, const WebCore::QualifiedName& prefixedAttribute, const HTMLFrameOwnerElement* owner)
+static bool fullscreenIsAllowedForAllOwners(const Document& document)
{
+ const HTMLFrameOwnerElement* owner = document.ownerElement();
if (!owner)
return true;
do {
- if (!(owner->hasAttribute(attribute) || owner->hasAttribute(prefixedAttribute)))
- return false;
+ if (!owner->hasAttribute(allowfullscreenAttr)) {
+ if (owner->hasAttribute(webkitallowfullscreenAttr))
+ UseCounter::count(document, UseCounter::PrefixedAllowFullscreenAttribute);
+ else
+ return false;
+ }
} while ((owner = owner->document().ownerElement()));
return true;
}
return "FullscreenElementStack";
}
-FullscreenElementStack* FullscreenElementStack::from(Document* document)
+FullscreenElementStack& FullscreenElementStack::from(Document& document)
{
FullscreenElementStack* fullscreen = fromIfExists(document);
if (!fullscreen) {
DocumentSupplement::provideTo(document, supplementName(), adoptPtr(fullscreen));
}
- return fullscreen;
+ return *fullscreen;
}
-FullscreenElementStack* FullscreenElementStack::fromIfExistsSlow(Document* document)
+FullscreenElementStack* FullscreenElementStack::fromIfExistsSlow(Document& document)
{
return static_cast<FullscreenElementStack*>(DocumentSupplement::from(document, supplementName()));
}
-Element* FullscreenElementStack::fullscreenElementFrom(Document* document)
+Element* FullscreenElementStack::fullscreenElementFrom(Document& document)
{
if (FullscreenElementStack* found = fromIfExists(document))
return found->webkitFullscreenElement();
return 0;
}
-Element* FullscreenElementStack::currentFullScreenElementFrom(Document* document)
+Element* FullscreenElementStack::currentFullScreenElementFrom(Document& document)
{
if (FullscreenElementStack* found = fromIfExists(document))
return found->webkitCurrentFullScreenElement();
return 0;
}
-bool FullscreenElementStack::isFullScreen(Document* document)
+bool FullscreenElementStack::isFullScreen(Document& document)
{
if (FullscreenElementStack* found = fromIfExists(document))
return found->webkitIsFullScreen();
return false;
}
-FullscreenElementStack::FullscreenElementStack(Document* document)
- : DocumentLifecycleObserver(document)
+FullscreenElementStack::FullscreenElementStack(Document& document)
+ : DocumentLifecycleObserver(&document)
, m_areKeysEnabledInFullScreen(false)
, m_fullScreenRenderer(0)
, m_fullScreenChangeDelayTimer(this, &FullscreenElementStack::fullScreenChangeDelayTimerFired)
{
- document->setHasFullscreenElementStack();
+ document.setHasFullscreenElementStack();
}
FullscreenElementStack::~FullscreenElementStack()
void FullscreenElementStack::documentWasDisposed()
{
- m_fullScreenElement = 0;
+ m_fullScreenElement = nullptr;
m_fullScreenElementStack.clear();
}
bool FullscreenElementStack::fullScreenIsAllowedForElement(Element* element) const
{
ASSERT(element);
- return isAttributeOnAllOwners(allowfullscreenAttr, webkitallowfullscreenAttr, element->document().ownerElement());
+ return fullscreenIsAllowedForAllOwners(element->document());
}
void FullscreenElementStack::requestFullScreenForElement(Element* element, unsigned short flags, FullScreenCheckType checkType)
{
+ // Ignore this request if the document is not in a live frame.
+ if (!document()->isActive())
+ return;
+
// The Mozilla Full Screen API <https://wiki.mozilla.org/Gecko:FullScreenAPI> has different requirements
// for full screen mode, and do not have the concept of a full screen element stack.
bool inLegacyMozillaMode = (flags & Element::LEGACY_MOZILLA_REQUEST);
// A descendant browsing context's document has a non-empty fullscreen element stack.
bool descendentHasNonEmptyStack = false;
- for (Frame* descendant = document()->frame() ? document()->frame()->tree().traverseNext() : 0; descendant; descendant = descendant->tree().traverseNext()) {
- if (fullscreenElementFrom(descendant->document())) {
+ 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;
}
// - an activation behavior is currently being processed whose click event was trusted, or
// - the event listener for a trusted click event is being handled.
// FIXME: Does this need to null-check settings()?
- if (!UserGestureIndicator::processingUserGesture() && (!element->isMediaElement() || document()->settings()->mediaFullscreenRequiresUserGesture()))
+ if (!UserGestureIndicator::processingUserGesture() && (!isHTMLMediaElement(*element) || document()->settings()->mediaFullscreenRequiresUserGesture()))
break;
// There is a previously-established user preference, security risk, or platform limitation.
// stack, and queue a task to fire an event named fullscreenchange with its bubbles attribute
// set to true on the document.
if (!followingDoc) {
- from(currentDoc)->pushFullscreenElementStack(element);
+ from(*currentDoc).pushFullscreenElementStack(element);
addDocumentToFullScreenChangeEventQueue(currentDoc);
continue;
}
// 3. Otherwise, if document's fullscreen element stack is either empty or its top element
// is not following document's browsing context container,
- Element* topElement = fullscreenElementFrom(currentDoc);
+ Element* topElement = fullscreenElementFrom(*currentDoc);
if (!topElement || topElement != followingDoc->ownerElement()) {
// ...push following document's browsing context container on document's fullscreen element
// stack, and queue a task to fire an event named fullscreenchange with its bubbles attribute
// set to true on document.
- from(currentDoc)->pushFullscreenElementStack(followingDoc->ownerElement());
+ from(*currentDoc).pushFullscreenElementStack(followingDoc->ownerElement());
addDocumentToFullScreenChangeEventQueue(currentDoc);
continue;
}
// 5. Return, and run the remaining steps asynchronously.
// 6. Optionally, perform some animation.
m_areKeysEnabledInFullScreen = flags & Element::ALLOW_KEYBOARD_INPUT;
- document()->page()->chrome().client().enterFullScreenForElement(element);
+ document()->frameHost()->chrome().client().enterFullScreenForElement(element);
// 7. Optionally, display a message indicating how the user can exit displaying the context object fullscreen.
return;
} while (0);
m_fullScreenErrorEventTargetQueue.append(element ? element : document()->documentElement());
- m_fullScreenChangeDelayTimer.startOneShot(0);
+ m_fullScreenChangeDelayTimer.startOneShot(0, FROM_HERE);
}
void FullscreenElementStack::webkitCancelFullScreen()
// calling webkitExitFullscreen():
Vector<RefPtr<Element> > replacementFullscreenElementStack;
replacementFullscreenElementStack.append(fullscreenElementFrom(document()->topDocument()));
- FullscreenElementStack* topFullscreenElementStack = from(document()->topDocument());
- topFullscreenElementStack->m_fullScreenElementStack.swap(replacementFullscreenElementStack);
- topFullscreenElementStack->webkitExitFullscreen();
+ FullscreenElementStack& topFullscreenElementStack = from(document()->topDocument());
+ topFullscreenElementStack.m_fullScreenElementStack.swap(replacementFullscreenElementStack);
+ topFullscreenElementStack.webkitExitFullscreen();
}
void FullscreenElementStack::webkitExitFullscreen()
// 1. Let doc be the context object. (i.e. "this")
Document* currentDoc = document();
+ if (!currentDoc->isActive())
+ return;
// 2. If doc's fullscreen element stack is empty, terminate these steps.
if (m_fullScreenElementStack.isEmpty())
// element stack (if any), ordered so that the child of the doc is last and the document furthest
// away from the doc is first.
Deque<RefPtr<Document> > descendants;
- for (Frame* descendant = document()->frame() ? document()->frame()->tree().traverseNext() : 0; descendant; descendant = descendant->tree().traverseNext()) {
- if (fullscreenElementFrom(descendant->document()))
+ 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());
}
// 4. For each descendant in descendants, empty descendant's fullscreen element stack, and queue a
// task to fire an event named fullscreenchange with its bubbles attribute set to true on descendant.
for (Deque<RefPtr<Document> >::iterator i = descendants.begin(); i != descendants.end(); ++i) {
- from(i->get())->clearFullscreenElementStack();
+ ASSERT(*i);
+ from(**i).clearFullscreenElementStack();
addDocumentToFullScreenChangeEventQueue(i->get());
}
Element* newTop = 0;
while (currentDoc) {
// 1. Pop the top element of doc's fullscreen element stack.
- from(currentDoc)->popFullscreenElementStack();
+ from(*currentDoc).popFullscreenElementStack();
// If doc's fullscreen element stack is non-empty and the element now at the top is either
// not in a document or its node document is not doc, repeat this substep.
- newTop = fullscreenElementFrom(currentDoc);
+ newTop = fullscreenElementFrom(*currentDoc);
if (newTop && (!newTop->inDocument() || newTop->document() != currentDoc))
continue;
// 6. Return, and run the remaining steps asynchronously.
// 7. Optionally, perform some animation.
- if (!document()->page())
+ FrameHost* host = document()->frameHost();
+
+ // Speculative fix for engaget.com/videos per crbug.com/336239.
+ // FIXME: This check is wrong. We ASSERT(document->isActive()) above
+ // so this should be redundant and should be removed!
+ if (!host)
return;
// Only exit out of full screen window mode if there are no remaining elements in the
// full screen stack.
if (!newTop) {
- document()->page()->chrome().client().exitFullScreenForElement(m_fullScreenElement.get());
+ host->chrome().client().exitFullScreenForElement(m_fullScreenElement.get());
return;
}
// Otherwise, notify the chrome of the new full screen element.
- document()->page()->chrome().client().enterFullScreenForElement(newTop);
+ host->chrome().client().enterFullScreenForElement(newTop);
}
-bool FullscreenElementStack::webkitFullscreenEnabled(Document* document)
+bool FullscreenElementStack::webkitFullscreenEnabled(Document& document)
{
// 4. The fullscreenEnabled attribute must return true if the context object and all ancestor
// browsing context's documents have their fullscreen enabled flag set, or false otherwise.
// Top-level browsing contexts are implied to have their allowFullScreen attribute set.
- return isAttributeOnAllOwners(allowfullscreenAttr, webkitallowfullscreenAttr, document->ownerElement());
-
+ return fullscreenIsAllowedForAllOwners(document);
}
void FullscreenElementStack::webkitWillEnterFullScreenForElement(Element* element)
{
- if (!document()->isActive())
- return;
-
ASSERT(element);
-
- // Protect against being called after the document has been removed from the page.
- if (!document()->settings())
+ if (!document()->isActive())
return;
+ ASSERT(document()->settings()); // If we're active we must have settings.
ASSERT(document()->settings()->fullScreenEnabled());
if (m_fullScreenRenderer)
m_fullScreenElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(true);
- document()->recalcStyle(Force);
+ // FIXME: This should not call updateStyleIfNeeded.
+ document()->setNeedsStyleRecalc(SubtreeStyleChange);
+ document()->updateRenderTreeIfNeeded();
}
void FullscreenElementStack::webkitDidEnterFullScreenForElement(Element*)
m_fullScreenElement->didBecomeFullscreenElement();
- m_fullScreenChangeDelayTimer.startOneShot(0);
+ m_fullScreenChangeDelayTimer.startOneShot(0, FROM_HERE);
}
void FullscreenElementStack::webkitWillExitFullScreenForElement(Element*)
if (m_fullScreenRenderer)
m_fullScreenRenderer->unwrapRenderer();
- m_fullScreenElement = 0;
- document()->setNeedsStyleRecalc();
+ m_fullScreenElement = nullptr;
+ document()->setNeedsStyleRecalc(SubtreeStyleChange);
// When webkitCancelFullScreen is called, we call webkitExitFullScreen on the topDocument(). That
// means that the events will be queued there. So if we have no events here, start the timer on
// the exiting document.
Document* exitingDocument = document();
if (m_fullScreenChangeEventTargetQueue.isEmpty() && m_fullScreenErrorEventTargetQueue.isEmpty())
- exitingDocument = document()->topDocument();
- from(exitingDocument)->m_fullScreenChangeDelayTimer.startOneShot(0);
+ exitingDocument = &document()->topDocument();
+ ASSERT(exitingDocument);
+ from(*exitingDocument).m_fullScreenChangeDelayTimer.startOneShot(0, FROM_HERE);
}
void FullscreenElementStack::setFullScreenRenderer(RenderFullScreen* renderer)
if (!m_fullScreenElement)
return;
+ // If the node isn't in a document it can't have a fullscreen'd child.
+ if (!node->inDocument())
+ return;
+
bool elementInSubtree = false;
if (amongChildrenOnly)
elementInSubtree = m_fullScreenElement->isDescendantOf(node);
ASSERT(doc);
Node* target = 0;
- if (FullscreenElementStack* fullscreen = fromIfExists(doc)) {
+ if (FullscreenElementStack* fullscreen = fromIfExists(*doc)) {
target = fullscreen->webkitFullscreenElement();
if (!target)
target = fullscreen->webkitCurrentFullScreenElement();