Support W3C Full Screen API proposal
authorjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 16 Mar 2012 18:12:14 +0000 (18:12 +0000)
committerjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 16 Mar 2012 18:12:14 +0000 (18:12 +0000)
https://bugs.webkit.org/show_bug.cgi?id=80660

Reviewed by Alexey Proskuryakov.

Source/WebCore:

Tests: fullscreen/full-screen-element-stack.html
       fullscreen/full-screen-enabled.html
       fullscreen/full-screen-restrictions.html

The W3C proposal for taking arbitrary elements into full-screen mode is significantly
different than the Mozilla proposal. For example, the W3C has proposed a lower-case "s"
in "Fullscreen", which means the W3C and Mozilla "requestFullscreen" APIs differ only by
in that lower-case "s". Annoying as this is, it does allow us to retain the semantics for
the Mozilla case (har!).

A significant difficulty is obeying the new W3C spec rules is that we would like to apply the
fullscreen CSS rules while exiting fullscreen mode, though the W3C spec insists that the
webkitFullscreenElement returns the new value immediately.  As such, we retain the m_fullScreenElement
variable (distinct from the top of the m_fullScreenElements stack) which is controlled by the
webkit{Will,Did}{Enter,Exit}FullScreen functions.

New APIs for the W3C Fullscreen spec:
* dom/Document.h:
(WebCore::Document::webkitFullscreenElement):
(WebCore::Document::webkitFullscreenEnabled):
* dom/Document.idl:
* dom/Element.cpp:
(WebCore::Element::webkitRequestFullscreen):
* dom/Element.h:
* dom/Element.idl:

* dom/Document.cpp:
(WebCore::Document::removedLastRef): Clear m_fullScreenElementStack.
(WebCore::Document::requestFullScreenForElement): Implement the W3C requirements.
(WebCore::Document::webkitExitFullscreen): Ditto.
(WebCore::Document::webkitCancelFullScreen): Implement in terms of webkitCancelFullscreen.
(WebCore::Document::webkitDidEnterFullScreenForElement):
(WebCore::Document::webkitWillExitFullScreenForElement):
(WebCore::Document::webkitDidExitFullScreenForElement):
(WebCore::Document::fullScreenChangeDelayTimerFired): Protect against items being
    added to the event and error queue by swapping out empty queues before starting.
(WebCore::Document::clearFullscreenElementStack): Simple accessor.
(WebCore::Document::popFullscreenElementStack): Ditto.
(WebCore::Document::pushFullscreenElementStack): Ditto.
* dom/Element.cpp:
(WebCore::Element::webkitRequestFullScreen):

Add new RuntimeEnabledFeatures functions for the added Document and Element functions.
* bindings/generic/RuntimeEnabledFeatures.h:
(RuntimeEnabledFeatures):
(WebCore::RuntimeEnabledFeatures::webkitFullscreenEnabledEnabled):
(WebCore::RuntimeEnabledFeatures::webkitFullscreenElementEnabled):
(WebCore::RuntimeEnabledFeatures::webkitExitFullscreenEnabled):
(WebCore::RuntimeEnabledFeatures::webkitRequestFullscreenEnabled):

Source/WebKit/mac:

Allow full screen elements to access the keyboard.

* WebView/WebView.mm:
(-[WebView _supportsFullScreenForElement:WebCore::withKeyboard:]):

Source/WebKit2:

Allow full screen elements to access the keyboard.

* UIProcess/WebFullScreenManagerProxy.cpp:
(WebKit::WebFullScreenManagerProxy::supportsFullScreen):
* WebProcess/FullScreen/WebFullScreenManager.cpp:
(WebKit::WebFullScreenManager::exitFullScreenForElement):

LayoutTests:

* fullscreen/full-screen-element-stack-expected.txt: Added.
* fullscreen/full-screen-element-stack.html: Added.
* fullscreen/full-screen-enabled-expected.txt: Added.
* fullscreen/full-screen-enabled.html: Added.
* fullscreen/full-screen-request-rejected.html:
* fullscreen/full-screen-request-removed.html:
* fullscreen/full-screen-restrictions-expected.txt: Added.
* fullscreen/full-screen-restrictions.html: Added.
* fullscreen/full-screen-test.js:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@111028 268f45cc-cd09-0410-ab3c-d52691b4dbfc

24 files changed:
LayoutTests/ChangeLog
LayoutTests/fullscreen/full-screen-element-stack-expected.txt [new file with mode: 0644]
LayoutTests/fullscreen/full-screen-element-stack.html [new file with mode: 0644]
LayoutTests/fullscreen/full-screen-enabled-expected.txt [new file with mode: 0644]
LayoutTests/fullscreen/full-screen-enabled.html [new file with mode: 0644]
LayoutTests/fullscreen/full-screen-request-rejected.html
LayoutTests/fullscreen/full-screen-request-removed.html
LayoutTests/fullscreen/full-screen-restrictions-expected.txt [new file with mode: 0644]
LayoutTests/fullscreen/full-screen-restrictions.html [new file with mode: 0644]
LayoutTests/fullscreen/full-screen-test.js
Source/WebCore/ChangeLog
Source/WebCore/bindings/generic/RuntimeEnabledFeatures.h
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/dom/Document.idl
Source/WebCore/dom/Element.cpp
Source/WebCore/dom/Element.h
Source/WebCore/dom/Element.idl
Source/WebKit/mac/ChangeLog
Source/WebKit/mac/WebView/WebView.mm
Source/WebKit2/ChangeLog
Source/WebKit2/UIProcess/WebFullScreenManagerProxy.cpp
Source/WebKit2/WebProcess/FullScreen/WebFullScreenManager.cpp
Tools/DumpRenderTree/mac/UIDelegate.mm

index 11e2290012caf8e9d72d82838317261b5b6c89e0..ae8a434502064472ee6b7492d6a0cc3e74ac358c 100644 (file)
@@ -1,3 +1,20 @@
+2012-03-08  Jer Noble  <jer.noble@apple.com>
+
+        Support W3C Full Screen API proposal
+        https://bugs.webkit.org/show_bug.cgi?id=80660
+
+        Reviewed by Alexey Proskuryakov.
+
+        * fullscreen/full-screen-element-stack-expected.txt: Added.
+        * fullscreen/full-screen-element-stack.html: Added.
+        * fullscreen/full-screen-enabled-expected.txt: Added.
+        * fullscreen/full-screen-enabled.html: Added.
+        * fullscreen/full-screen-request-rejected.html:
+        * fullscreen/full-screen-request-removed.html:
+        * fullscreen/full-screen-restrictions-expected.txt: Added.
+        * fullscreen/full-screen-restrictions.html: Added.
+        * fullscreen/full-screen-test.js:
+
 2012-03-16  Xiaomei Ji  <xji@chromium.org>
 
         Skip visual word movement tests in gtk and qt after r110965 since isWordTextBreak is not implemented.
diff --git a/LayoutTests/fullscreen/full-screen-element-stack-expected.txt b/LayoutTests/fullscreen/full-screen-element-stack-expected.txt
new file mode 100644 (file)
index 0000000..5a06ce1
--- /dev/null
@@ -0,0 +1,15 @@
+Test for W3C Fullscreen element stack.
+
+To test manually, click the "Go full screen" button - the page should enter full screen mode.
+
+Go full screen (one)
+Go full screen (two)
+Exit full screen (two)
+EVENT(webkitfullscreenchange)
+EXPECTED (document.webkitFullscreenElement.id == 'one') OK
+EVENT(webkitfullscreenchange)
+EXPECTED (document.webkitFullscreenElement.id == 'two') OK
+EVENT(webkitfullscreenchange)
+EXPECTED (document.webkitFullscreenElement.id == 'one') OK
+END OF TEST
+
diff --git a/LayoutTests/fullscreen/full-screen-element-stack.html b/LayoutTests/fullscreen/full-screen-element-stack.html
new file mode 100644 (file)
index 0000000..78770ef
--- /dev/null
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script src="full-screen-test.js"></script>
+    <script>
+    var one;
+    var two;
+
+    function runTest() {
+        one = document.getElementById('one');
+        two = document.getElementById('two');
+
+        var callback;
+        var fullscreenChanged = function(event) {
+            if (callback)
+                callback(event)
+        };
+        waitForEvent(document, 'webkitfullscreenchange', fullscreenChanged);
+
+        var oneEnteredFullscreen = function() {
+            testExpected("document.webkitFullscreenElement.id", "one");
+            callback = twoEnteredFullscreen;
+            if (window.layoutTestController)
+                runWithKeyDown(function() { two.webkitRequestFullscreen(); });
+        };
+
+        var twoEnteredFullscreen = function() {
+            testExpected("document.webkitFullscreenElement.id", "two");
+            callback = twoExitedFullscreen;
+            if (window.layoutTestController)
+                document.webkitExitFullscreen();
+        };
+
+        var twoExitedFullscreen = function() { 
+            testExpected("document.webkitFullscreenElement.id", "one");
+            endTest();
+        };
+
+        callback = oneEnteredFullscreen;
+        if (window.layoutTestController)
+            runWithKeyDown(function() { one.webkitRequestFullscreen(); });
+    }
+    </script>
+</head>
+<body onload="runTest()">
+    <p>Test for <a href="http://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html#fullscreen-element-stack">W3C Fullscreen element stack</a>.</p>
+    <p>To test manually, click the "Go full screen" button - the page should enter full screen mode.</p>
+    <div>
+        <button onclick="one.webkitRequestFullscreen()">Go full screen (one)</button>
+    </div>
+    <div id=one>
+        <button onclick="two.webkitRequestFullscreen()">Go full screen (two)</button>
+        <div id=two>
+            <button onclick="document.webkitExitFullscreen()">Exit full screen (two)</button>
+        </div>
+    </div>
+</body>
\ No newline at end of file
diff --git a/LayoutTests/fullscreen/full-screen-enabled-expected.txt b/LayoutTests/fullscreen/full-screen-enabled-expected.txt
new file mode 100644 (file)
index 0000000..012c0d2
--- /dev/null
@@ -0,0 +1,5 @@
+This tests the fullscreenEnabled property laid out in section 4 of the W3C Full Screen API
+EXPECTED (iframe.contentDocument.webkitFullscreenEnabled == 'true') OK
+EXPECTED (iframe2.contentDocument.webkitFullscreenEnabled == 'false') OK
+END OF TEST
+
diff --git a/LayoutTests/fullscreen/full-screen-enabled.html b/LayoutTests/fullscreen/full-screen-enabled.html
new file mode 100644 (file)
index 0000000..6d10be5
--- /dev/null
@@ -0,0 +1,12 @@
+<body>
+<div>This tests the <code>fullscreenEnabled</code> property laid out in section 4 of the W3C 
+<a href="http://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html">Full Screen API</a></div>
+<script src="full-screen-test.js"></script>
+<script>
+    var iframe = document.documentElement.appendChild(document.createElement('iframe'));
+    iframe.setAttribute('webkitallowfullscreen', 'true');
+    var iframe2 = document.documentElement.appendChild(document.createElement('iframe'));
+    testExpected('iframe.contentDocument.webkitFullscreenEnabled', true);
+    testExpected('iframe2.contentDocument.webkitFullscreenEnabled', false);
+    endTest();
+</script>
index 779832a7f238bf3e6c41d27007be423cec96dc14..0e7819b9ad52f864f88d329e12bd7d66ad941934 100644 (file)
@@ -9,7 +9,10 @@
     } else {
         waitForEvent(document, 'webkitfullscreenchange', function() {
            logResult("Entered full screen.", false); 
+           endTest();
         });
+        if (layoutTestController)
+            layoutTestController.setPopupBlockingEnabled(true);
         waitForEventAndEnd(document, 'webkitfullscreenerror');
         document.documentElement.webkitRequestFullScreen();
     }
index 9264219c246b6fa8bff3aca006b65779a265ce8b..2330ffbe91471caf9fcde6b228f32987d3c9df48 100644 (file)
@@ -12,6 +12,9 @@ function runTest() {
         consoleWrite("SUCCEED - did not enter full screen!");
     });
 
+    if (layoutTestController)
+        layoutTestController.setPopupBlockingEnabled(true);
+
     var div = document.createElement('div');
     document.documentElement.appendChild(div);
     consoleWrite("Added child element.")
diff --git a/LayoutTests/fullscreen/full-screen-restrictions-expected.txt b/LayoutTests/fullscreen/full-screen-restrictions-expected.txt
new file mode 100644 (file)
index 0000000..84030de
--- /dev/null
@@ -0,0 +1,19 @@
+This tests the restrictions to entering full screen mode laid out in section 4.1 of the W3C Full Screen API
+"The context object is not in a document."
+EVENT(webkitfullscreenerror)
+"The context object's node document, or an ancestor browsing context's document does not have the fullscreen enabled flag set."
+EVENT(webkitfullscreenerror)
+"The context object's node document fullscreen element stack is not empty and its top element is not an ancestor of the context object."
+EVENT(webkitfullscreenchange)
+EVENT(webkitfullscreenerror)
+EVENT(webkitfullscreenchange)
+"A descendant browsing context's document has a non-empty fullscreen element stack."
+EVENT(webkitfullscreenchange)
+EVENT(webkitfullscreenerror)
+EVENT(webkitfullscreenchange)
+"This algorithm is not allowed to show a pop-up."
+EVENT(webkitfullscreenerror)
+END OF TEST
+
+
+
diff --git a/LayoutTests/fullscreen/full-screen-restrictions.html b/LayoutTests/fullscreen/full-screen-restrictions.html
new file mode 100644 (file)
index 0000000..e924b03
--- /dev/null
@@ -0,0 +1,67 @@
+<body onload="runTest();">
+<div>This tests the restrictions to entering full screen mode laid out in section 4.1 of the W3C 
+<a href="http://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html">Full Screen API</a></div>
+<script src="full-screen-test.js"></script>
+<script>
+    // Bail out early if the full screen API is not enabled or is missing:
+    if (Element.prototype.webkitRequestFullScreen == undefined) {
+        logResult(false, "Element.prototype.webkitRequestFullScreen == undefined");
+        endTest();
+    } else {
+        var runTest = function() {
+            consoleWrite('"The context object is not in a document."');
+            var div = document.createElement('div');
+            waitForEventOnce(document, 'webkitfullscreenerror', step2);
+            runWithKeyDown(function(){div.webkitRequestFullscreen()});
+        };
+
+        var step2 = function() {
+            consoleWrite('"The context object\'s node document, or an ancestor browsing context\'s document does not have the fullscreen enabled flag set."')
+            var iframe = document.documentElement.appendChild(document.createElement('iframe'));
+            var div = iframe.contentDocument.documentElement.appendChild(iframe.contentDocument.createElement('div'));
+
+            waitForEventOnce(iframe.contentDocument, 'webkitfullscreenerror', step3);
+            runWithKeyDown(function(){div.webkitRequestFullscreen()});
+        };
+
+        var step3 = function() {
+            consoleWrite('"The context object\'s node document fullscreen element stack is not empty and its top element is not an ancestor of the context object."');
+            var div = document.documentElement.appendChild(document.createElement('div'));
+            var div2 = document.documentElement.appendChild(document.createElement('div'));
+            waitForEventOnce(document, 'webkitfullscreenchange', function() {
+                waitForEventOnce(document, 'webkitfullscreenerror', function() {
+                   waitForEventOnce(document, 'webkitfullscreenchange', step4);
+                   document.webkitExitFullscreen(); 
+                });
+                runWithKeyDown(function(){div2.webkitRequestFullscreen()});
+            });
+            runWithKeyDown(function(){div.webkitRequestFullscreen()});
+        };
+
+        var step4 = function() {
+            consoleWrite('"A descendant browsing context\'s document has a non-empty fullscreen element stack."');
+            var iframe = document.documentElement.appendChild(document.createElement('iframe'));
+            iframe.setAttribute('webkitallowfullscreen', 'true');
+            var div = iframe.contentDocument.documentElement.appendChild(iframe.contentDocument.createElement('div'));
+            var div2 = document.documentElement.appendChild(document.createElement('div'));
+            waitForEventOnce(iframe.contentDocument, 'webkitfullscreenchange', function() {
+                waitForEventOnce(document, 'webkitfullscreenerror', function(){
+                    waitForEventOnce(iframe.contentDocument, 'webkitfullscreenchange', step5);
+                    iframe.contentDocument.webkitExitFullscreen(); 
+                });
+                runWithKeyDown(function(){div2.webkitRequestFullscreen()});
+            });
+            runWithKeyDown(function(){div.webkitRequestFullscreen()});
+        };
+
+        var step5 = function() {
+            consoleWrite('"This algorithm is not allowed to show a pop-up."');
+            var div = document.documentElement.appendChild(document.createElement('div'));
+            waitForEventOnce(document, 'webkitfullscreenerror', endTest);
+            div.webkitRequestFullscreen();
+        };
+
+        if (typeof(layoutTestController) != 'undefined')
+            layoutTestController.setPopupBlockingEnabled(true);
+    }
+</script>
index 5e5bb1af7a782acfec925c6d28b7ef8a22243749..801b5ba9e3a612baf3673c6da5718c3585ba3f92 100644 (file)
@@ -98,10 +98,18 @@ function waitForEventAndEnd(element, eventName, funcString)
     waitForEvent(element, eventName, funcString, true)
 }
 
-function waitForEvent(element, eventName, func, endit)
+function waitForEventOnce(element, eventName, func, endit)
+{
+    waitForEvent(element, eventName, func, endit, true)
+}
+
+function waitForEvent(element, eventName, func, endit, once)
 {
     function _eventCallback(event)
     {
+        if (once)
+            element.removeEventListener(eventName, _eventCallback);
+
         consoleWrite("EVENT(" + eventName + ")");
 
         if (func)
index 863860b249d6d47c06200ddd6f76905f118431fd..82c933a1510df4884d35dc2f1a9a6e85b40c8654 100644 (file)
@@ -1,3 +1,61 @@
+2012-03-08  Jer Noble  <jer.noble@apple.com>
+
+        Support W3C Full Screen API proposal
+        https://bugs.webkit.org/show_bug.cgi?id=80660
+
+        Reviewed by Alexey Proskuryakov.
+
+        Tests: fullscreen/full-screen-element-stack.html
+               fullscreen/full-screen-enabled.html
+               fullscreen/full-screen-restrictions.html
+
+        The W3C proposal for taking arbitrary elements into full-screen mode is significantly
+        different than the Mozilla proposal. For example, the W3C has proposed a lower-case "s"
+        in "Fullscreen", which means the W3C and Mozilla "requestFullscreen" APIs differ only by
+        in that lower-case "s". Annoying as this is, it does allow us to retain the semantics for
+        the Mozilla case (har!).
+
+        A significant difficulty is obeying the new W3C spec rules is that we would like to apply the
+        fullscreen CSS rules while exiting fullscreen mode, though the W3C spec insists that the
+        webkitFullscreenElement returns the new value immediately.  As such, we retain the m_fullScreenElement
+        variable (distinct from the top of the m_fullScreenElements stack) which is controlled by the
+        webkit{Will,Did}{Enter,Exit}FullScreen functions.
+
+        New APIs for the W3C Fullscreen spec:
+        * dom/Document.h:
+        (WebCore::Document::webkitFullscreenElement):
+        (WebCore::Document::webkitFullscreenEnabled):
+        * dom/Document.idl:
+        * dom/Element.cpp:
+        (WebCore::Element::webkitRequestFullscreen):
+        * dom/Element.h:
+        * dom/Element.idl:
+
+        * dom/Document.cpp:
+        (WebCore::Document::removedLastRef): Clear m_fullScreenElementStack.
+        (WebCore::Document::requestFullScreenForElement): Implement the W3C requirements.
+        (WebCore::Document::webkitExitFullscreen): Ditto.
+        (WebCore::Document::webkitCancelFullScreen): Implement in terms of webkitCancelFullscreen.
+        (WebCore::Document::webkitDidEnterFullScreenForElement): 
+        (WebCore::Document::webkitWillExitFullScreenForElement):
+        (WebCore::Document::webkitDidExitFullScreenForElement):
+        (WebCore::Document::fullScreenChangeDelayTimerFired): Protect against items being
+            added to the event and error queue by swapping out empty queues before starting.
+        (WebCore::Document::clearFullscreenElementStack): Simple accessor.
+        (WebCore::Document::popFullscreenElementStack): Ditto.
+        (WebCore::Document::pushFullscreenElementStack): Ditto.
+        * dom/Element.cpp:
+        (WebCore::Element::webkitRequestFullScreen):
+
+        Add new RuntimeEnabledFeatures functions for the added Document and Element functions.
+        * bindings/generic/RuntimeEnabledFeatures.h:
+        (RuntimeEnabledFeatures):
+        (WebCore::RuntimeEnabledFeatures::webkitFullscreenEnabledEnabled):
+        (WebCore::RuntimeEnabledFeatures::webkitFullscreenElementEnabled):
+        (WebCore::RuntimeEnabledFeatures::webkitExitFullscreenEnabled):
+        (WebCore::RuntimeEnabledFeatures::webkitRequestFullscreenEnabled):
+
+
 2012-03-16  Adam Klein  <adamk@chromium.org>
 
         Make HTMLInputElement::isRadioButton non-virtual and remove unused HTMLFormControlElement::isRadioButton method
index d0cce5f670aa7806c1cf55d74f85b866d37ee333..dd580891c5b81a141e43f1bf6564da63f28f4265 100644 (file)
@@ -71,6 +71,7 @@ public:
     static bool webkitIDBTransactionEnabled() { return isIndexedDBEnabled; }
 
 #if ENABLE(FULLSCREEN_API)
+    // Mozilla version
     static bool webkitFullScreenAPIEnabled() { return isFullScreenAPIEnabled; }
     static void setWebkitFullScreenAPIEnabled(bool isEnabled) { isFullScreenAPIEnabled = isEnabled; }
     static bool webkitRequestFullScreenEnabled() { return isFullScreenAPIEnabled; }
@@ -78,6 +79,12 @@ public:
     static bool webkitFullScreenKeyboardInputAllowedEnabled() { return isFullScreenAPIEnabled; }
     static bool webkitCurrentFullScreenElementEnabled() { return isFullScreenAPIEnabled; }
     static bool webkitCancelFullScreenEnabled() { return isFullScreenAPIEnabled; }
+
+    // W3C version
+    static bool webkitFullscreenEnabledEnabled() { return isFullScreenAPIEnabled; }
+    static bool webkitFullscreenElementEnabled() { return isFullScreenAPIEnabled; }
+    static bool webkitExitFullscreenEnabled() { return isFullScreenAPIEnabled; }
+    static bool webkitRequestFullscreenEnabled() { return isFullScreenAPIEnabled; }
 #endif
 
 #if ENABLE(POINTER_LOCK)
index 190c98027f35ba636b8c510ae18ff9a329f6e5e3..3c8d6cffdd27e9e9f5bd2e497f745d3a6504a607 100644 (file)
@@ -607,6 +607,7 @@ void Document::removedLastRef()
         m_documentElement = 0;
 #if ENABLE(FULLSCREEN_API)
         m_fullScreenElement = 0;
+        m_fullScreenElementStack.clear();
 #endif
 
         // removeAllChildren() doesn't always unregister IDs,
@@ -5056,43 +5057,231 @@ bool Document::fullScreenIsAllowedForElement(Element* element) const
 
 void Document::requestFullScreenForElement(Element* element, unsigned short flags, FullScreenCheckType checkType)
 {
-    do {
-        if (!page() || !page()->settings()->fullScreenEnabled())
-            break;
+    // 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);
 
+    do {
         if (!element)
             element = documentElement();
-        
+        // 1. If any of the following conditions are true, terminate these steps and queue a task to fire
+        // an event named fullscreenerror with its bubbles attribute set to true on the context object's 
+        // node document:
+
+        // The context object is not in a document.
+        if (!element->inDocument())
+            break;
+
+        // The context object's node document, or an ancestor browsing context's document does not have
+        // the fullscreen enabled flag set.
         if (checkType == EnforceIFrameAllowFulScreenRequirement && !fullScreenIsAllowedForElement(element))
             break;
-        
-        if (!ScriptController::processingUserGesture())
+
+        // The context object's node document fullscreen element stack is not empty and its top element
+        // is not an ancestor of the context object. (NOTE: Ignore this requirement if the request was
+        // made via the legacy Mozilla-style API.)
+        if (!m_fullScreenElementStack.isEmpty() && !m_fullScreenElementStack.first()->contains(element) && !inLegacyMozillaMode)
+            break;
+
+        // A descendant browsing context's document has a non-empty fullscreen element stack.
+        bool descendentHasNonEmptyStack = false;
+        for (Frame* descendant = frame() ? frame()->tree()->traverseNext() : 0; descendant; descendant = descendant->tree()->traverseNext()) {
+            if (descendant->document()->webkitFullscreenElement()) {
+                descendentHasNonEmptyStack = true;
+                break;
+            }
+        }
+        if (descendentHasNonEmptyStack && !inLegacyMozillaMode)
+            break;
+
+        // This algorithm is not allowed to show a pop-up.
+        if (!domWindow()->allowPopUp())
+            break;
+
+        // There is a previously-established user preference, security risk, or platform limitation.
+        if (!page() || !page()->settings()->fullScreenEnabled())
             break;
         
         if (!page()->chrome()->client()->supportsFullScreenForElement(element, flags & Element::ALLOW_KEYBOARD_INPUT))
             break;
-        
+
+        // 2. Let doc be element's node document. (i.e. "this")
+        Document* currentDoc = this;
+
+        // 3. Let docs be all doc's ancestor browsing context's documents (if any) and doc.
+        Deque<Document*> docs;
+
+        do {
+            docs.prepend(currentDoc);
+            currentDoc = currentDoc->ownerElement() ? currentDoc->ownerElement()->document() : 0;
+        } while (currentDoc);
+
+        // 4. For each document in docs, run these substeps:
+        Deque<Document*>::iterator current = docs.begin(), following = docs.begin();
+
+        do {
+            ++following;
+
+            // 1. Let following document be the document after document in docs, or null if there is no
+            // such document.
+            Document* currentDoc = *current;
+            Document* followingDoc = following != docs.end() ? *following : 0;
+
+            // 2. If following document is null, push context object on document's fullscreen element
+            // stack, and queue a task to fire an event named fullscreenchange with its bubbles attribute
+            // set to true on the document.
+            if (!followingDoc) {
+                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 = currentDoc->webkitFullscreenElement();
+            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.
+                currentDoc->pushFullscreenElementStack(followingDoc->ownerElement());
+                addDocumentToFullScreenChangeEventQueue(currentDoc);
+                continue;
+            }
+
+            // 4. Otherwise, do nothing for this document. It stays the same.
+        } while (++current != docs.end());
+
+        // 5. Return, and run the remaining steps asynchronously.
+        // 6. Optionally, perform some animation.
         m_areKeysEnabledInFullScreen = flags & Element::ALLOW_KEYBOARD_INPUT;
         page()->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 : documentElement());
     m_fullScreenChangeDelayTimer.startOneShot(0);
 }
 
 void Document::webkitCancelFullScreen()
 {
-    if (!page() || !m_fullScreenElement)
+    // The Mozilla "cancelFullScreen()" API behaves like the W3C "fully exit fullscreen" behavior, which
+    // is defined as: 
+    // "To fully exit fullscreen act as if the exitFullscreen() method was invoked on the top-level browsing
+    // context's document and subsequently empty that document's fullscreen element stack."
+    if (!topDocument()->webkitFullscreenElement())
+        return;
+
+    // To achieve that aim, remove all the elements from the top document's stack except for the first before
+    // calling webkitExitFullscreen():
+    Deque<RefPtr<Element> > replacementFullscreenElementStack;
+    replacementFullscreenElementStack.prepend(topDocument()->webkitFullscreenElement());
+    topDocument()->m_fullScreenElementStack.swap(replacementFullscreenElementStack);
+
+    topDocument()->webkitExitFullscreen();
+}
+
+void Document::webkitExitFullscreen()
+{
+    // The exitFullscreen() method must run these steps:
+    
+    // 1. Let doc be the context object. (i.e. "this")
+    Document* currentDoc = this;
+
+    // 2. If doc's fullscreen element stack is empty, terminate these steps.
+    if (m_fullScreenElementStack.isEmpty())
         return;
     
-    page()->chrome()->client()->exitFullScreenForElement(m_fullScreenElement.get());
+    // 3. Let descendants be all the doc's descendant browsing context's documents with a non-empty fullscreen
+    // 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 = frame() ? frame()->tree()->traverseNext() : 0; descendant; descendant = descendant->tree()->traverseNext()) {
+        if (descendant->document()->webkitFullscreenElement())
+            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) {
+        (*i)->clearFullscreenElementStack();
+        addDocumentToFullScreenChangeEventQueue(i->get());
+    }
+
+    // 5. While doc is not null, run these substeps:
+    Element* newTop = 0;
+    while (currentDoc) {
+        // 1. Pop the top element of doc's fullscreen element stack.
+        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 = currentDoc->webkitFullscreenElement();
+        if (newTop && (!newTop->inDocument() || newTop->document() != currentDoc))
+            continue;
+
+        // 2. Queue a task to fire an event named fullscreenchange with its bubbles attribute set to true
+        // on doc.
+        Node* target = currentDoc->m_fullScreenElement.get();
+        if (!target)
+            target = currentDoc;
+        addDocumentToFullScreenChangeEventQueue(currentDoc);
+
+        // 3. If doc's fullscreen element stack is empty and doc's browsing context has a browsing context
+        // container, set doc to that browsing context container's node document.
+        if (!newTop && currentDoc->ownerElement())
+            currentDoc = currentDoc->ownerElement()->document();
+
+        // 4. Otherwise, set doc to null.
+        currentDoc = 0;
+    }
+
+    // 6. Return, and run the remaining steps asynchronously.
+    // 7. Optionally, perform some animation.
+
+    // Only exit out of full screen window mode if there are no remaining elements in the 
+    // full screen stack.
+    if (!newTop) {
+        page()->chrome()->client()->exitFullScreenForElement(m_fullScreenElement.get());
+        return;
+    }
+
+    // Otherwise, notify the chrome of the new full screen element.
+    page()->chrome()->client()->enterFullScreenForElement(newTop);      
+}
+
+bool Document::webkitFullscreenEnabled() const
+{
+    // 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.
+    HTMLFrameOwnerElement* owner = ownerElement();
+    if (!owner)
+        return true;
+
+    do {
+        if (!owner->isFrameElementBase())
+            continue;
+
+        if (!static_cast<HTMLFrameElementBase*>(owner)->allowFullScreen())
+            return false;
+    } while ((owner = owner->document()->ownerElement()));
+
+    return true;        
 }
 
 void Document::webkitWillEnterFullScreenForElement(Element* element)
 {
     ASSERT(element);
-    ASSERT(page() && page()->settings()->fullScreenEnabled());
+
+    // Protect against being called after the document has been removed from the page.
+    if (!page())
+        return;
+
+    ASSERT(page()->settings()->fullScreenEnabled());
 
     if (m_fullScreenRenderer)
         m_fullScreenRenderer->unwrapRenderer();
@@ -5120,14 +5309,19 @@ void Document::webkitWillEnterFullScreenForElement(Element* element)
     
 void Document::webkitDidEnterFullScreenForElement(Element*)
 {
+    if (!m_fullScreenElement)
+        return;
+    
     m_fullScreenElement->didBecomeFullscreenElement();
 
-    m_fullScreenChangeEventTargetQueue.append(m_fullScreenElement);
     m_fullScreenChangeDelayTimer.startOneShot(0);
 }
 
 void Document::webkitWillExitFullScreenForElement(Element*)
 {
+    if (!m_fullScreenElement)
+        return;
+
     m_fullScreenElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
     
     m_fullScreenElement->willStopBeingFullscreenElement();
@@ -5140,7 +5334,7 @@ void Document::webkitDidExitFullScreenForElement(Element*)
     if (m_fullScreenRenderer)
         m_fullScreenRenderer->unwrapRenderer();
 
-    m_fullScreenChangeEventTargetQueue.append(m_fullScreenElement.release());
+    m_fullScreenElement = 0;
     scheduleForcedStyleRecalc();
     
     m_fullScreenChangeDelayTimer.startOneShot(0);
@@ -5206,28 +5400,34 @@ void Document::setFullScreenRendererBackgroundColor(Color backgroundColor)
     
 void Document::fullScreenChangeDelayTimerFired(Timer<Document>*)
 {
-    while (!m_fullScreenChangeEventTargetQueue.isEmpty()) {
-        RefPtr<Element> element = m_fullScreenChangeEventTargetQueue.takeFirst();
-        if (!element)
-            element = documentElement();
+    Deque<RefPtr<Node> > changeQueue;
+    m_fullScreenChangeEventTargetQueue.swap(changeQueue);
+
+    while (!changeQueue.isEmpty()) {
+        RefPtr<Node> node = changeQueue.takeFirst();
+        if (!node)
+            node = documentElement();
 
         // If the element was removed from our tree, also message the documentElement.
-        if (!contains(element.get()))
-            m_fullScreenChangeEventTargetQueue.append(documentElement());
+        if (!contains(node.get()))
+            changeQueue.append(documentElement());
         
-        element->dispatchEvent(Event::create(eventNames().webkitfullscreenchangeEvent, true, false));
+        node->dispatchEvent(Event::create(eventNames().webkitfullscreenchangeEvent, true, false));
     }
 
-    while (!m_fullScreenErrorEventTargetQueue.isEmpty()) {
-        RefPtr<Element> element = m_fullScreenErrorEventTargetQueue.takeFirst();
-        if (!element)
-            element = documentElement();
+    Deque<RefPtr<Node> > errorQueue;
+    m_fullScreenErrorEventTargetQueue.swap(errorQueue);
+    
+    while (!errorQueue.isEmpty()) {
+        RefPtr<Node> node = errorQueue.takeFirst();
+        if (!node)
+            node = documentElement();
         
-        // If the element was removed from our tree, also message the documentElement.
-        if (!contains(element.get()))
-            m_fullScreenErrorEventTargetQueue.append(documentElement());
+        // If the node was removed from our tree, also message the documentElement.
+        if (!contains(node.get()))
+            errorQueue.append(documentElement());
         
-        element->dispatchEvent(Event::create(eventNames().webkitfullscreenerrorEvent, true, false));
+        node->dispatchEvent(Event::create(eventNames().webkitfullscreenerrorEvent, true, false));
     }
 }
 
@@ -5268,6 +5468,35 @@ void Document::setAnimatingFullScreen(bool flag)
         scheduleForcedStyleRecalc();
     }
 }
+
+void Document::clearFullscreenElementStack()
+{
+    m_fullScreenElementStack.clear();
+}
+
+void Document::popFullscreenElementStack()
+{
+    if (m_fullScreenElementStack.isEmpty())
+        return;
+
+    m_fullScreenElementStack.removeFirst();
+}
+
+void Document::pushFullscreenElementStack(Element* element)
+{
+    m_fullScreenElementStack.prepend(element);
+}
+
+void Document::addDocumentToFullScreenChangeEventQueue(Document* doc)
+{
+    ASSERT(doc);
+    Node* target = doc->webkitFullscreenElement();
+    if (!target)
+        target = doc->webkitCurrentFullScreenElement();
+    if (!target)
+        target = doc;
+    m_fullScreenChangeEventTargetQueue.append(target);
+}
 #endif
 
 void Document::decrementLoadEventDelayCount()
index 4aac16f12587983e2c7faedfe8a3645e8935bf4d..f79d45c1b239092f40e7c98e5f9588f735fa1a20 100644 (file)
@@ -1079,6 +1079,11 @@ public:
     void removeFullScreenElementOfSubtree(Node*, bool amongChildrenOnly = false);
     bool isAnimatingFullScreen() const;
     void setAnimatingFullScreen(bool);
+
+    // W3C API
+    bool webkitFullscreenEnabled() const;
+    Element* webkitFullscreenElement() const { return !m_fullScreenElementStack.isEmpty() ? m_fullScreenElementStack.first().get() : 0; }
+    void webkitExitFullscreen();
 #endif
 
     // Used to allow element that loads data without going through a FrameLoader to delay the 'load' event.
@@ -1189,6 +1194,13 @@ private:
 
     HTMLCollection* cachedCollection(CollectionType);
 
+#if ENABLE(FULLSCREEN_API)
+    void clearFullscreenElementStack();
+    void popFullscreenElementStack();
+    void pushFullscreenElementStack(Element*);
+    void addDocumentToFullScreenChangeEventQueue(Document*);
+#endif
+
     int m_guardRefCount;
 
     OwnPtr<CSSStyleSelector> m_styleSelector;
@@ -1421,10 +1433,11 @@ private:
 #if ENABLE(FULLSCREEN_API)
     bool m_areKeysEnabledInFullScreen;
     RefPtr<Element> m_fullScreenElement;
+    Deque<RefPtr<Element> > m_fullScreenElementStack;
     RenderFullScreen* m_fullScreenRenderer;
     Timer<Document> m_fullScreenChangeDelayTimer;
-    Deque<RefPtr<Element> > m_fullScreenChangeEventTargetQueue;
-    Deque<RefPtr<Element> > m_fullScreenErrorEventTargetQueue;
+    Deque<RefPtr<Node> > m_fullScreenChangeEventTargetQueue;
+    Deque<RefPtr<Node> > m_fullScreenErrorEventTargetQueue;
     bool m_isAnimatingFullScreen;
     LayoutRect m_savedPlaceholderFrameRect;
     RefPtr<RenderStyle> m_savedPlaceholderRenderStyle;
index b3e0aa3a0ba292888442fe550c4dd1539dd7d26e..4e4b7e3bfe1b1fc9cdb324fe9d8b4715b1debc07 100644 (file)
@@ -241,10 +241,16 @@ module core {
             raises(DOMException);
 
 #if defined(ENABLE_FULLSCREEN_API) && ENABLE_FULLSCREEN_API
+        // Mozilla version
         readonly attribute [V8EnabledAtRuntime] boolean webkitIsFullScreen;
         readonly attribute [V8EnabledAtRuntime] boolean webkitFullScreenKeyboardInputAllowed;
         readonly attribute [V8EnabledAtRuntime] Element webkitCurrentFullScreenElement;
         [V8EnabledAtRuntime] void webkitCancelFullScreen();
+
+        // W3C version
+        readonly attribute [V8EnabledAtRuntime] boolean webkitFullscreenEnabled;
+        readonly attribute [V8EnabledAtRuntime] Element webkitFullscreenElement;
+        [V8EnabledAtRuntime] void webkitExitFullscreen();
 #endif
 
         WebKitNamedFlow webkitGetFlowByName(in DOMString name);
index 2ba7ebf81a9572027a6e4909ba85258768bd5d1f..2df6d4e4426830a11a233fe4dfd43ebdf99f9db0 100644 (file)
@@ -1858,9 +1858,14 @@ bool Element::childShouldCreateRenderer(const NodeRenderingContext& childContext
 #endif
     
 #if ENABLE(FULLSCREEN_API)
+void Element::webkitRequestFullscreen()
+{
+    document()->requestFullScreenForElement(this, ALLOW_KEYBOARD_INPUT, Document::EnforceIFrameAllowFulScreenRequirement);
+}
+
 void Element::webkitRequestFullScreen(unsigned short flags)
 {
-    document()->requestFullScreenForElement(this, flags, Document::EnforceIFrameAllowFulScreenRequirement);
+    document()->requestFullScreenForElement(this, (flags | LEGACY_MOZILLA_REQUEST), Document::EnforceIFrameAllowFulScreenRequirement);
 }
 
 bool Element::containsFullScreenElement() const
index 4e563ff01affcabe7b2f23486c202c72939788b3..def48e94cd89f0dfc2a24fcdd466683111e4af37 100644 (file)
@@ -377,13 +377,17 @@ public:
     
 #if ENABLE(FULLSCREEN_API)
     enum {
-        ALLOW_KEYBOARD_INPUT = 1
+        ALLOW_KEYBOARD_INPUT = 1 << 0,
+        LEGACY_MOZILLA_REQUEST = 1 << 1,
     };
     
     void webkitRequestFullScreen(unsigned short flags);
     virtual bool containsFullScreenElement() const;
     virtual void setContainsFullScreenElement(bool);
     virtual void setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(bool);
+
+    // W3C API
+    void webkitRequestFullscreen();
 #endif
 
     virtual bool isSpellCheckingEnabled() const;
index 8725bcbc19cbc2b81a7bbe61d8089ef985996806..955bd470aa686735715bf4604b8db56c0ba4072d 100644 (file)
@@ -129,8 +129,12 @@ module core {
 #endif
 
 #if defined(ENABLE_FULLSCREEN_API) && ENABLE_FULLSCREEN_API
+        // Mozilla version
         const unsigned short ALLOW_KEYBOARD_INPUT = 1;
         [V8EnabledAtRuntime] void webkitRequestFullScreen(in [Optional=DefaultIsUndefined] unsigned short flags);
+
+        // W3C version
+        [V8EnabledAtRuntime] void webkitRequestFullscreen();
 #endif
 
         // CSS Regions API
index 6ae662e14a2d5bf0c9013d60770898a44fca1fb7..675c2927f4793d4bc9b5ce049fa9ffcf7d94644c 100644 (file)
@@ -1,3 +1,15 @@
+2012-03-08  Jer Noble  <jer.noble@apple.com>
+
+        Support W3C Full Screen API proposal
+        https://bugs.webkit.org/show_bug.cgi?id=80660
+
+        Reviewed by Alexey Proskuryakov.
+
+        Allow full screen elements to access the keyboard.
+
+        * WebView/WebView.mm:
+        (-[WebView _supportsFullScreenForElement:WebCore::withKeyboard:]):
+
 2012-03-07  Jon Lee  <jonlee@apple.com>
 
         Move NotificationContents into Notification
index 88a11dc2e4eed58e66b37943a547b99462d96684..9281148f51ed8d26a851052af2d05cfa287dd209 100644 (file)
@@ -6284,14 +6284,9 @@ bool LayerFlushController::flushLayers()
 #if ENABLE(FULLSCREEN_API)
 - (BOOL)_supportsFullScreenForElement:(const WebCore::Element*)element withKeyboard:(BOOL)withKeyboard
 {
-    if (withKeyboard)
-        return NO;
-
     if (![[WebPreferences standardPreferences] fullScreenEnabled])
         return NO;
 
-    // FIXME: If the element is in an IFrame, we should ensure it has 
-    // an AllowsFullScreen=YES attribute before allowing fullscreen access.
     return YES;
 }
 
index 0476f8384b283a2c3ab94dcf820597aeabce1208..502c8a6961bc7d6afe63c750433c5815fd2edb17 100644 (file)
@@ -1,3 +1,17 @@
+2012-03-08  Jer Noble  <jer.noble@apple.com>
+
+        Support W3C Full Screen API proposal
+        https://bugs.webkit.org/show_bug.cgi?id=80660
+
+        Reviewed by Alexey Proskuryakov.
+
+        Allow full screen elements to access the keyboard.
+
+        * UIProcess/WebFullScreenManagerProxy.cpp:
+        (WebKit::WebFullScreenManagerProxy::supportsFullScreen):
+        * WebProcess/FullScreen/WebFullScreenManager.cpp:
+        (WebKit::WebFullScreenManager::exitFullScreenForElement):
+
 2012-03-16  Andras Becsi  <andras.becsi@nokia.com>
 
         [Qt][WK2] Fix bounce-back behaviour for panning
index 17c070b6ddb09334acf6d54ee2ca4cad056ea040..21fe9cdae4fd23d04ae0c1806910e78f566881d4 100644 (file)
@@ -91,10 +91,7 @@ void WebFullScreenManagerProxy::setAnimatingFullScreen(bool animating)
 
 void WebFullScreenManagerProxy::supportsFullScreen(bool withKeyboard, bool& supports)
 {
-    if (withKeyboard)
-        supports = false;
-    else
-        supports = true;
+    supports = true;
 }
 
 } // namespace WebKit
index 30fd4b38d7169bcb76ccfa40ff97f94bed4d00cc..bbe64e8929262f3d42529f839567fdd2ba46549a 100644 (file)
@@ -101,8 +101,6 @@ void WebFullScreenManager::enterFullScreenForElement(WebCore::Element* element)
 
 void WebFullScreenManager::exitFullScreenForElement(WebCore::Element* element)
 {
-    ASSERT(element);
-    ASSERT(m_element == element);
     m_page->injectedBundleFullScreenClient().exitFullScreenForElement(m_page.get(), element);
 }
 
index 87eece441a918d8919af7c33b41660d001bf4824..e855791db3f71bbab3cfef238108183e6ab6e910 100644 (file)
@@ -252,18 +252,28 @@ DumpRenderTreeDraggingInfo *draggingInfo = nil;
     return YES;
 }
 
-- (void)webView:(WebView *)webView enterFullScreenForElement:(DOMElement*)element listener:(NSObject<WebKitFullScreenListener>*)listener
+- (void)enterFullScreenWithListener:(NSObject<WebKitFullScreenListener>*)listener
 {
     [listener webkitWillEnterFullScreen];
     [listener webkitDidEnterFullScreen];
 }
 
-- (void)webView:(WebView *)webView exitFullScreenForElement:(DOMElement*)element listener:(NSObject<WebKitFullScreenListener>*)listener
+- (void)webView:(WebView *)webView enterFullScreenForElement:(DOMElement*)element listener:(NSObject<WebKitFullScreenListener>*)listener
+{
+    [self performSelector:@selector(enterFullScreenWithListener:) withObject:listener afterDelay:0];
+}
+
+- (void)exitFullScreenWithListener:(NSObject<WebKitFullScreenListener>*)listener
 {
     [listener webkitWillExitFullScreen];
     [listener webkitDidExitFullScreen];
 }
 
+- (void)webView:(WebView *)webView exitFullScreenForElement:(DOMElement*)element listener:(NSObject<WebKitFullScreenListener>*)listener
+{
+    [self performSelector:@selector(exitFullScreenWithListener:) withObject:listener afterDelay:0];
+}
+
 - (BOOL)webView:(WebView *)webView didPressMissingPluginButton:(DOMElement *)element
 {
     printf("MISSING PLUGIN BUTTON PRESSED\n");