Upstream version 7.35.139.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / dom / FullscreenElementStack.cpp
index 89c6851..cb44806 100644 (file)
 
 #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"
 
@@ -45,13 +46,18 @@ namespace WebCore {
 
 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;
 }
@@ -61,7 +67,7 @@ const char* FullscreenElementStack::supplementName()
     return "FullscreenElementStack";
 }
 
-FullscreenElementStack* FullscreenElementStack::from(Document* document)
+FullscreenElementStack& FullscreenElementStack::from(Document& document)
 {
     FullscreenElementStack* fullscreen = fromIfExists(document);
     if (!fullscreen) {
@@ -69,42 +75,42 @@ FullscreenElementStack* FullscreenElementStack::from(Document* document)
         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()
@@ -127,18 +133,22 @@ void FullscreenElementStack::documentWasDetached()
 
 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);
@@ -171,8 +181,9 @@ void FullscreenElementStack::requestFullScreenForElement(Element* element, unsig
 
         // 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;
             }
@@ -185,7 +196,7 @@ void FullscreenElementStack::requestFullScreenForElement(Element* element, unsig
         //   - 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.
@@ -218,19 +229,19 @@ void FullscreenElementStack::requestFullScreenForElement(Element* element, unsig
             // 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;
             }
@@ -241,14 +252,14 @@ void FullscreenElementStack::requestFullScreenForElement(Element* element, unsig
         // 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()
@@ -264,9 +275,9 @@ 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()
@@ -275,6 +286,8 @@ 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())
@@ -284,15 +297,17 @@ void FullscreenElementStack::webkitExitFullscreen()
     // 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());
     }
 
@@ -300,11 +315,11 @@ void FullscreenElementStack::webkitExitFullscreen()
     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;
 
@@ -326,41 +341,41 @@ void FullscreenElementStack::webkitExitFullscreen()
     // 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)
@@ -384,7 +399,9 @@ void FullscreenElementStack::webkitWillEnterFullScreenForElement(Element* elemen
 
     m_fullScreenElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(true);
 
-    document()->recalcStyle(Force);
+    // FIXME: This should not call updateStyleIfNeeded.
+    document()->setNeedsStyleRecalc(SubtreeStyleChange);
+    document()->updateRenderTreeIfNeeded();
 }
 
 void FullscreenElementStack::webkitDidEnterFullScreenForElement(Element*)
@@ -397,7 +414,7 @@ void FullscreenElementStack::webkitDidEnterFullScreenForElement(Element*)
 
     m_fullScreenElement->didBecomeFullscreenElement();
 
-    m_fullScreenChangeDelayTimer.startOneShot(0);
+    m_fullScreenChangeDelayTimer.startOneShot(0, FROM_HERE);
 }
 
 void FullscreenElementStack::webkitWillExitFullScreenForElement(Element*)
@@ -426,16 +443,17 @@ void FullscreenElementStack::webkitDidExitFullScreenForElement(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)
@@ -517,6 +535,10 @@ void FullscreenElementStack::removeFullScreenElementOfSubtree(Node* node, bool a
     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);
@@ -550,7 +572,7 @@ void FullscreenElementStack::addDocumentToFullScreenChangeEventQueue(Document* d
     ASSERT(doc);
 
     Node* target = 0;
-    if (FullscreenElementStack* fullscreen = fromIfExists(doc)) {
+    if (FullscreenElementStack* fullscreen = fromIfExists(*doc)) {
         target = fullscreen->webkitFullscreenElement();
         if (!target)
             target = fullscreen->webkitCurrentFullScreenElement();