Permalink
Browse files

Bug 737100 - Extend Pointer Lock (Mouse Lock) for non-fullscreen elem…

…ents, p=smaug,dolske, r=cpearce,dolske,smaug
  • Loading branch information...
1 parent 03842d8 commit 4adf4078bce79a4dfd7fa747099b7d7b2886c0f3 Olli Pettay committed Mar 24, 2013
View
8 browser/base/content/browser.xul
@@ -450,6 +450,13 @@
</popupnotificationcontent>
</popupnotification>
+ <popupnotification id="pointerLock-notification" hidden="true">
+ <popupnotificationcontent orient="vertical" align="start">
+ <separator class="thin"/>
+ <label id="pointerLock-cancel" value="&pointerLock.notification.message;"/>
+ </popupnotificationcontent>
+ </popupnotification>
+
<popupnotification id="mixed-content-blocked-notification" hidden="true">
<popupnotificationcontent orient="vertical" align="start">
<separator/>
@@ -580,6 +587,7 @@
<image id="mixed-content-blocked-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="webRTC-sharingDevices-notification-icon" class="notification-anchor-icon" role="button"/>
+ <image id="pointerLock-notification-icon" class="notification-anchor-icon" role="button"/>
</box>
<!-- Use onclick instead of normal popup= syntax since the popup
code fires onmousedown, and hence eats our favicon drag events.
View
14 browser/base/content/pageinfo/pageInfo.xul
@@ -64,6 +64,8 @@
<command id="cmd_geoToggle" oncommand="onRadioClick('geo');"/>
<command id="cmd_indexedDBToggle" oncommand="onRadioClick('indexedDB');"/>
<command id="cmd_pluginsToggle" oncommand="onPluginRadioClick(event);"/>
+ <command id="cmd_pointerLockDef" oncommand="onCheckboxClick('pointerLock');"/>
+ <command id="cmd_pointerLockToggle" oncommand="onRadioClick('pointerLock');"/>
</commandset>
<keyset id="pageInfoKeySet">
@@ -413,6 +415,18 @@
</radiogroup>
</hbox>
</vbox>
+ <vbox class="permission" id="permPointerLockRow" >
+ <label class="permissionLabel" id="permPointerLockLabel"
+ value="&permPointerLock;" control="pointerLockRadioGroup"/>
+ <hbox id="permPointerLockBox" role="group" aria-labelledby="permPointerLockLabel">
+ <checkbox id="pointerLockDef" command="cmd_pointerLockDef" label="&permAskAlways;"/>
+ <spacer flex="1"/>
+ <radiogroup id="pointerLockRadioGroup" orient="horizontal">
+ <radio id="pointerLock#1" command="cmd_pointerLockToggle" label="&permAllow;"/>
+ <radio id="pointerLock#2" command="cmd_pointerLockToggle" label="&permBlock;"/>
+ </radiogroup>
+ </hbox>
+ </vbox>
</vbox>
</vbox>
View
16 browser/base/content/pageinfo/permissions.js
@@ -64,7 +64,11 @@ var gPermObj = {
fullscreen: function getFullscreenDefaultPermissions()
{
return UNKNOWN;
- }
+ },
+ pointerLock: function getPointerLockPermissions()
+ {
+ return BLOCK;
+ },
};
var permissionObserver = {
@@ -129,9 +133,13 @@ function initRow(aPartId)
var checkbox = document.getElementById(aPartId + "Def");
var command = document.getElementById("cmd_" + aPartId + "Toggle");
- // Geolocation permission consumers use testExactPermission, not testPermission.
- var perm = aPartId == "geo" ? permissionManager.testExactPermission(gPermURI, aPartId) :
- permissionManager.testPermission(gPermURI, aPartId);
+ // Geolocation and PointerLock permission consumers use testExactPermission, not testPermission.
+ var perm;
+ if (aPartId == "geo" || aPartId == "pointerLock")
+ perm = permissionManager.testExactPermission(gPermURI, aPartId);
+ else
+ perm = permissionManager.testPermission(gPermURI, aPartId);
+
if (perm) {
checkbox.checked = false;
command.removeAttribute("disabled");
View
99 browser/components/nsBrowserGlue.js
@@ -1640,9 +1640,14 @@ ContentPermissionPrompt.prototype = {
*/
_showPrompt: function CPP_showPrompt(aRequest, aMessage, aPermission, aActions,
aNotificationId, aAnchorId) {
+ function onFullScreen() {
+ popup.remove();
+ }
+
var browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
var requestingWindow = aRequest.window.top;
+ var topDoc = requestingWindow.document;
var chromeWin = this._getChromeWindow(requestingWindow).wrappedJSObject;
var browser = chromeWin.gBrowser.getBrowserForDocument(requestingWindow.document);
var requestPrincipal = aRequest.principal;
@@ -1685,10 +1690,34 @@ ContentPermissionPrompt.prototype = {
popupNotificationActions.push(action);
}
- var mainAction = popupNotificationActions[0];
+ var mainAction = popupNotificationActions.length ?
+ popupNotificationActions[0] : null;
var secondaryActions = popupNotificationActions.splice(1);
- chromeWin.PopupNotifications.show(browser, aNotificationId, aMessage, aAnchorId,
- mainAction, secondaryActions);
+ var options = null;
+
+ if (aRequest.type == "pointerLock") {
+ // If there's no mainAction, this is the autoAllow warning prompt.
+ let autoAllow = !mainAction;
+ options = { removeOnDismissal: autoAllow,
+ eventCallback: function (type) {
+ if (type == "removed") {
+ topDoc.removeEventListener("mozfullscreenchange", onFullScreen);
+ if (autoAllow)
+ aRequest.allow();
+ }
+ },
+ };
+ }
+
+ var popup = chromeWin.PopupNotifications.show(browser, aNotificationId, aMessage, aAnchorId,
+ mainAction, secondaryActions, options);
+ if (aRequest.type == "pointerLock") {
+ // pointerLock is automatically allowed in fullscreen mode (and revoked
+ // upon exit), so if the page enters fullscreen mode after requesting
+ // pointerLock (but before the user has granted permission), we should
+ // remove the now-impotent notification.
+ topDoc.addEventListener("mozfullscreenchange", onFullScreen);
+ }
},
_promptGeo : function(aRequest) {
@@ -1779,9 +1808,50 @@ ContentPermissionPrompt.prototype = {
"web-notifications-notification-icon");
},
+ _promptPointerLock: function CPP_promtPointerLock(aRequest, autoAllow) {
+
+ let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
+ let requestingURI = aRequest.principal.URI;
+
+ let originString = requestingURI.schemeIs("file") ? requestingURI.path : requestingURI.host;
+ let message = browserBundle.formatStringFromName(autoAllow ?
+ "pointerLock.autoLock.title" : "pointerLock.title",
+ [originString], 1);
+ // If this is an autoAllow info prompt, offer no actions.
+ // _showPrompt() will allow the request when it's dismissed.
+ let actions = [];
+ if (!autoAllow) {
+ actions = [
+ {
+ stringId: "pointerLock.allow",
+ action: null,
+ expireType: null,
+ callback: function() {},
+ },
+ {
+ stringId: "pointerLock.alwaysAllow",
+ action: Ci.nsIPermissionManager.ALLOW_ACTION,
+ expireType: null,
+ callback: function() {},
+ },
+ {
+ stringId: "pointerLock.neverAllow",
+ action: Ci.nsIPermissionManager.DENY_ACTION,
+ expireType: null,
+ callback: function() {},
+ },
+ ];
+ }
+
+ this._showPrompt(aRequest, message, "pointerLock", actions, "pointerLock", "pointerLock-notification-icon");
+ },
+
prompt: function CPP_prompt(request) {
+
const kFeatureKeys = { "geolocation" : "geo",
- "desktop-notification" : "desktop-notification" };
+ "desktop-notification" : "desktop-notification",
+ "pointerLock" : "pointerLock",
+ };
// Make sure that we support the request.
if (!(request.type in kFeatureKeys)) {
@@ -1795,19 +1865,24 @@ ContentPermissionPrompt.prototype = {
if (!(requestingURI instanceof Ci.nsIStandardURL))
return;
+ var autoAllow = false;
var permissionKey = kFeatureKeys[request.type];
var result = Services.perms.testExactPermissionFromPrincipal(requestingPrincipal, permissionKey);
- if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
- request.allow();
- return;
- }
-
if (result == Ci.nsIPermissionManager.DENY_ACTION) {
request.cancel();
return;
}
+ if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
+ autoAllow = true;
+ // For pointerLock, we still want to show a warning prompt.
+ if (request.type != "pointerLock") {
+ request.allow();
+ return;
+ }
+ }
+
// Show the prompt.
switch (request.type) {
case "geolocation":
@@ -1816,8 +1891,12 @@ ContentPermissionPrompt.prototype = {
case "desktop-notification":
this._promptWebNotifications(request);
break;
+ case "pointerLock":
+ this._promptPointerLock(request, autoAllow);
+ break;
}
- }
+ },
+
};
var components = [BrowserGlue, ContentPermissionPrompt];
View
2 browser/locales/en-US/chrome/browser/browser.dtd
@@ -647,3 +647,5 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!ENTITY mixedContentBlocked.helplink "Learn more">
<!ENTITY mixedContentBlocked.moreinfo "Most websites will still work properly even when this content is blocked.">
+
+<!ENTITY pointerLock.notification.message "Press ESC at any time to show it again.">
View
11 browser/locales/en-US/chrome/browser/browser.properties
@@ -275,6 +275,17 @@ webNotifications.neverShow=Always Block Notifications
webNotifications.neverShow.accesskey=N
webNotifications.showFromSite=Would you like to show notifications from %S?
+# Pointer lock UI
+
+pointerLock.allow=Hide mouse cursor
+pointerLock.allow.accesskey=H
+pointerLock.alwaysAllow=Always allow hiding
+pointerLock.alwaysAllow.accesskey=A
+pointerLock.neverAllow=Never allow hiding
+pointerLock.neverAllow.accesskey=N
+pointerLock.title=Would you like to allow the mouse cursor to be hidden on %S?
+pointerLock.autoLock.title=%S will hide the mouse cursor.
+
# Phishing/Malware Notification Bar.
# LOCALIZATION NOTE (notAForgery, notAnAttack)
# The two button strings will never be shown at the same time, so
View
1 browser/locales/en-US/chrome/browser/pageInfo.dtd
@@ -66,6 +66,7 @@
<!ENTITY permGeo "Share Location">
<!ENTITY permPlugins "Activate Plugins">
<!ENTITY permFullscreen "Enter Fullscreen">
+<!ENTITY permPointerLock "Hide the Mouse Cursor">
<!ENTITY permIndexedDB "Maintain Offline Storage">
<!ENTITY permClearStorage "Clear Storage">
View
11 browser/themes/linux/browser.css
@@ -1226,6 +1226,10 @@ toolbar[iconsize="small"] #webrtc-status-button {
list-style-image: url(chrome://browser/skin/webRTC-shareDevice-64.png);
}
+.popup-notification-icon[popupid="pointerLock"] {
+ list-style-image: url(chrome://browser/skin/pointerLock-64.png);
+}
+
/* Notification icon box */
#notification-popup-box {
position: relative;
@@ -1333,6 +1337,13 @@ toolbar[iconsize="small"] #webrtc-status-button {
list-style-image: url(chrome://browser/skin/notification-16.png);
}
+#pointerLock-notification-icon {
+ list-style-image: url(chrome://browser/skin/pointerLock-16.png);
+}
+#pointerLock-cancel {
+ margin: 0px;
+}
+
#treecolAutoCompleteImage {
max-width : 36px;
}
View
2 browser/themes/linux/jar.mn
@@ -39,6 +39,8 @@ browser.jar:
* skin/classic/browser/pageInfo.css
skin/classic/browser/pageInfo.png
skin/classic/browser/page-livemarks.png
+ skin/classic/browser/pointerLock-16.png
+ skin/classic/browser/pointerLock-64.png
skin/classic/browser/Privacy-16.png
skin/classic/browser/Privacy-48.png
skin/classic/browser/privatebrowsing-mask.png
View
BIN browser/themes/linux/pointerLock-16.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN browser/themes/linux/pointerLock-64.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
24 browser/themes/osx/browser.css
@@ -3155,6 +3155,17 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
}
}
+#pointerLock-notification-icon {
+ list-style-image: url(chrome://browser/skin/pointerLock-16.png);
+}
+@media (min-resolution: 2dppx) {
+ #pointerLock-notification-icon {
+ list-style-image: url(chrome://browser/skin/pointerLock-16@2x.png);
+ }
+}
+
+
+
.popup-notification-icon {
width: 64px;
height: 64px;
@@ -3248,6 +3259,19 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
}
}
+.popup-notification-icon[popupid="pointerLock"] {
+ list-style-image: url(chrome://browser/skin/pointerLock-64.png);
+}
+@media (min-resolution: 2dppx) {
+ .popup-notification-icon[popupid="pointerLock"] {
+ list-style-image: url(chrome://browser/skin/pointerLock-64@2x.png);
+ }
+}
+#pointerLock-cancel {
+ margin: 0px;
+}
+
+
#mixed-content-blocked-helplink {
margin: 0px;
}
View
4 browser/themes/osx/jar.mn
@@ -57,6 +57,10 @@ browser.jar:
skin/classic/browser/page-livemarks.png
skin/classic/browser/page-livemarks@2x.png
skin/classic/browser/pageInfo.css
+ skin/classic/browser/pointerLock-16.png
+ skin/classic/browser/pointerLock-16@2x.png
+ skin/classic/browser/pointerLock-64.png
+ skin/classic/browser/pointerLock-64@2x.png
skin/classic/browser/Privacy-16.png
skin/classic/browser/Privacy-48.png
skin/classic/browser/privatebrowsing-mask.png
View
BIN browser/themes/osx/pointerLock-16.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN browser/themes/osx/pointerLock-16@2x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN browser/themes/osx/pointerLock-64.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN browser/themes/osx/pointerLock-64@2x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
11 browser/themes/windows/browser.css
@@ -2283,6 +2283,10 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
list-style-image: url(chrome://browser/skin/webRTC-shareDevice-64.png);
}
+.popup-notification-icon[popupid="pointerLock"] {
+ list-style-image: url(chrome://browser/skin/pointerLock-64.png);
+}
+
/* Notification icon box */
#notification-popup-box {
position: relative;
@@ -2388,6 +2392,13 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
list-style-image: url(chrome://browser/skin/notification-16.png);
}
+#pointerLock-notification-icon {
+ list-style-image: url(chrome://browser/skin/pointerLock-16.png);
+}
+#pointerLock-cancel {
+ margin: 0px;
+}
+
#identity-popup-container {
min-width: 280px;
}
View
4 browser/themes/windows/jar.mn
@@ -47,6 +47,8 @@ browser.jar:
skin/classic/browser/pageInfo.css
skin/classic/browser/pageInfo.png
skin/classic/browser/page-livemarks.png (feeds/feedIcon16.png)
+ skin/classic/browser/pointerLock-16.png
+ skin/classic/browser/pointerLock-64.png
skin/classic/browser/Privacy-16.png
skin/classic/browser/Privacy-48.png
skin/classic/browser/privatebrowsing-light.png
@@ -280,6 +282,8 @@ browser.jar:
skin/classic/aero/browser/pageInfo.css
skin/classic/aero/browser/pageInfo.png (pageInfo-aero.png)
skin/classic/aero/browser/page-livemarks.png (feeds/feedIcon16-aero.png)
+ skin/classic/aero/browser/pointerLock-16.png (pointerLock-16.png)
+ skin/classic/aero/browser/pointerLock-64.png (pointerLock-64.png)
skin/classic/aero/browser/Privacy-16.png (Privacy-16-aero.png)
skin/classic/aero/browser/Privacy-48.png (Privacy-48-aero.png)
skin/classic/aero/browser/privatebrowsing-light.png
View
BIN browser/themes/windows/pointerLock-16.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN browser/themes/windows/pointerLock-64.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
6 content/base/public/nsContentUtils.h
@@ -1883,6 +1883,12 @@ class nsContentUtils
static nsIDocument* GetFullscreenAncestor(nsIDocument* aDoc);
/**
+ * Returns true if aWin and the current pointer lock document
+ * have common scriptable top window.
+ */
+ static bool IsInPointerLockContext(nsIDOMWindow* aWin);
+
+ /**
* Returns the time limit on handling user input before
* nsEventStateManager::IsHandlingUserInput() stops returning true.
* This enables us to detect long running user-generated event handlers.
View
4 content/base/public/nsIDocument.h
@@ -969,7 +969,7 @@ class nsIDocument : public nsINode
virtual void RequestPointerLock(Element* aElement) = 0;
- static void UnlockPointer();
+ static void UnlockPointer(nsIDocument* aDoc = nullptr);
//----------------------------------------------------------------------
@@ -2021,7 +2021,7 @@ class nsIDocument : public nsINode
Element* GetMozPointerLockElement();
void MozExitPointerLock()
{
- UnlockPointer();
+ UnlockPointer(this);
}
bool Hidden() const
{
View
23 content/base/src/nsContentUtils.cpp
@@ -6651,6 +6651,29 @@ nsContentUtils::GetFullscreenAncestor(nsIDocument* aDoc)
return nullptr;
}
+/* static */
+bool
+nsContentUtils::IsInPointerLockContext(nsIDOMWindow* aWin)
+{
+ if (!aWin) {
+ return false;
+ }
+
+ nsCOMPtr<nsIDocument> pointerLockedDoc =
+ do_QueryReferent(nsEventStateManager::sPointerLockedDoc);
+ if (!pointerLockedDoc || !pointerLockedDoc->GetWindow()) {
+ return false;
+ }
+
+ nsCOMPtr<nsIDOMWindow> lockTop;
+ pointerLockedDoc->GetWindow()->GetScriptableTop(getter_AddRefs(lockTop));
+
+ nsCOMPtr<nsIDOMWindow> top;
+ aWin->GetScriptableTop(getter_AddRefs(top));
+
+ return top == lockTop;
+}
+
// static
void
nsContentUtils::ReleaseWrapper(void* aScriptObjectHolder,
View
361 content/base/src/nsDocument.cpp
@@ -198,6 +198,8 @@
#include "nsIDOMHTMLTextAreaElement.h"
#include "nsViewportInfo.h"
#include "nsDOMEvent.h"
+#include "nsIContentPermissionPrompt.h"
+#include "mozilla/StaticPtr.h"
using namespace mozilla;
using namespace mozilla::dom;
@@ -8042,6 +8044,8 @@ nsDocument::OnPageHide(bool aPersisted,
SetImagesNeedAnimating(false);
}
+ MozExitPointerLock();
+
// Now send out a PageHide event.
nsCOMPtr<nsIDOMEventTarget> target = aDispatchStartTarget;
if (!target) {
@@ -9858,12 +9862,18 @@ class nsCallRequestFullScreen : public nsRunnable
nsCallRequestFullScreen(Element* aElement)
: mElement(aElement),
mDoc(aElement->OwnerDoc()),
- mWasCallerChrome(nsContentUtils::IsCallerChrome())
+ mWasCallerChrome(nsContentUtils::IsCallerChrome()),
+ mHadRequestPending(static_cast<nsDocument*>(mDoc.get())->
+ mAsyncFullscreenPending)
{
+ static_cast<nsDocument*>(mDoc.get())->
+ mAsyncFullscreenPending = true;
}
NS_IMETHOD Run()
{
+ static_cast<nsDocument*>(mDoc.get())->
+ mAsyncFullscreenPending = mHadRequestPending;
nsDocument* doc = static_cast<nsDocument*>(mDoc.get());
doc->RequestFullScreen(mElement,
mWasCallerChrome,
@@ -9874,6 +9884,7 @@ class nsCallRequestFullScreen : public nsRunnable
nsRefPtr<Element> mElement;
nsCOMPtr<nsIDocument> mDoc;
bool mWasCallerChrome;
+ bool mHadRequestPending;
};
void
@@ -10413,6 +10424,10 @@ nsDocument::IsFullScreenEnabled(bool aCallerIsChrome, bool aLogFailure)
static void
DispatchPointerLockChange(nsIDocument* aTarget)
{
+ if (!aTarget) {
+ return;
+ }
+
nsRefPtr<nsAsyncDOMEvent> e =
new nsAsyncDOMEvent(aTarget,
NS_LITERAL_STRING("mozpointerlockchange"),
@@ -10424,6 +10439,10 @@ DispatchPointerLockChange(nsIDocument* aTarget)
static void
DispatchPointerLockError(nsIDocument* aTarget)
{
+ if (!aTarget) {
+ return;
+ }
+
nsRefPtr<nsAsyncDOMEvent> e =
new nsAsyncDOMEvent(aTarget,
NS_LITERAL_STRING("mozpointerlockerror"),
@@ -10432,118 +10451,182 @@ DispatchPointerLockError(nsIDocument* aTarget)
e->PostDOMEvent();
}
-// Manages asynchronously requesting pointer lock. Used to dispatch an
-// event to request pointer lock once fullscreen has been approved.
-class nsAsyncPointerLockRequest : public nsRunnable
+mozilla::StaticRefPtr<nsPointerLockPermissionRequest> gPendingPointerLockRequest;
+
+class nsPointerLockPermissionRequest : public nsRunnable,
+ public nsIContentPermissionRequest
{
public:
+ nsPointerLockPermissionRequest(Element* aElement, bool aUserInputOrChromeCaller)
+ : mElement(do_GetWeakReference(aElement)),
+ mDocument(do_GetWeakReference(aElement->OwnerDoc())),
+ mUserInputOrChromeCaller(aUserInputOrChromeCaller) {}
+
+ virtual ~nsPointerLockPermissionRequest() {}
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICONTENTPERMISSIONREQUEST
+
NS_IMETHOD Run()
{
- sInstance = nullptr;
- if (mDocument && mElement) {
- mDocument->RequestPointerLock(mElement);
+ nsCOMPtr<Element> e = do_QueryReferent(mElement);
+ nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
+ if (!e || !d || gPendingPointerLockRequest != this ||
+ e->GetCurrentDoc() != d) {
+ Handled();
+ DispatchPointerLockError(d);
+ return NS_OK;
}
- return NS_OK;
- }
- static void Request(Element* aElement, nsIDocument* aDocument)
- {
- if (sInstance) {
- // We already have an event instance pending. Change the requestee
- // to the new pointer lock requestee.
- sInstance->mElement = aElement;
- sInstance->mDocument = aDocument;
- } else {
- // Create a new event instance. Owning ref is held by the nsIEventTarget
- // to which this is dispatched.
- sInstance = new nsAsyncPointerLockRequest(aElement, aDocument);
- NS_DispatchToCurrentThread(sInstance);
+ // We're about to enter fullscreen mode.
+ nsDocument* doc = static_cast<nsDocument*>(d.get());
+ if (doc->mAsyncFullscreenPending ||
+ (doc->mHasFullscreenApprovedObserver && !doc->mIsApprovedForFullscreen)) {
+ // We're still waiting for approval.
+ return NS_OK;
}
- }
- static void Cancel()
- {
- if (sInstance) {
- // Revoke references to requesting element/document, when the
- // dispatched event runs. The event will do nothing, and then be
- // destroyed.
- sInstance->mElement = nullptr;
- sInstance->mDocument = nullptr;
+ if (doc->mIsApprovedForFullscreen || doc->mAllowRelocking) {
+ Allow();
+ return NS_OK;
}
- }
-private:
- nsAsyncPointerLockRequest(Element* aElement, nsIDocument* aDocument)
- : mElement(aElement),
- mDocument(aDocument)
- {
- MOZ_COUNT_CTOR(nsAsyncPointerLockRequest);
+ // In non-fullscreen mode user input (or chrome caller) is required!
+ // Also, don't let the page to try to get the permission too many times.
+ if (!mUserInputOrChromeCaller ||
+ doc->mCancelledPointerLockRequests > 2) {
+ Handled();
+ DispatchPointerLockError(d);
+ return NS_OK;
+ }
+
+ // Handling a request from user input in non-fullscreen mode.
+ // Do a normal permission check.
+ nsCOMPtr<nsIContentPermissionPrompt> prompt =
+ do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
+ if (prompt) {
+ prompt->Prompt(this);
+ }
+
+ return NS_OK;
}
- ~nsAsyncPointerLockRequest()
+ void Handled()
{
- MOZ_COUNT_DTOR(nsAsyncPointerLockRequest);
+ mElement = nullptr;
+ mDocument = nullptr;
+ if (gPendingPointerLockRequest == this) {
+ gPendingPointerLockRequest = nullptr;
+ }
}
- // Reference to the instance of any pending event. This is not an owning
- // reference; the nsIEventTarget to which this is dispatched holds the only
- // owning reference to this instance. This reference is valid between
- // an instance being created, and its Run() method being called.
- static nsAsyncPointerLockRequest* sInstance;
-
- // Element and document which reqested pointer lock.
- nsCOMPtr<Element> mElement;
- nsCOMPtr<nsIDocument> mDocument;
+ nsWeakPtr mElement;
+ nsWeakPtr mDocument;
+ bool mUserInputOrChromeCaller;
};
-nsAsyncPointerLockRequest* nsAsyncPointerLockRequest::sInstance = nullptr;
-nsWeakPtr nsDocument::sPendingPointerLockDoc;
-nsWeakPtr nsDocument::sPendingPointerLockElement;
+NS_IMPL_ISUPPORTS_INHERITED1(nsPointerLockPermissionRequest,
+ nsRunnable,
+ nsIContentPermissionRequest)
-/* static */
-void
-nsDocument::ClearPendingPointerLockRequest(bool aDispatchErrorEvents)
+NS_IMETHODIMP
+nsPointerLockPermissionRequest::GetType(nsACString& aType)
{
- nsAsyncPointerLockRequest::Cancel();
+ aType = "pointerLock";
+ return NS_OK;
+}
- if (!sPendingPointerLockDoc) {
- // No pending request.
- return;
- }
- nsCOMPtr<nsIDocument> doc(do_QueryReferent(sPendingPointerLockDoc));
- if (aDispatchErrorEvents) {
- DispatchPointerLockError(doc);
+NS_IMETHODIMP
+nsPointerLockPermissionRequest::GetAccess(nsACString& aAccess)
+{
+ aAccess = "unused";
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPointerLockPermissionRequest::GetPrincipal(nsIPrincipal** aPrincipal)
+{
+ nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
+ if (d) {
+ NS_ADDREF(*aPrincipal = d->NodePrincipal());
}
- nsCOMPtr<Element> element(do_QueryReferent(sPendingPointerLockElement));
-#ifdef DEBUG
- nsCOMPtr<Element> pointerLockedElement =
- do_QueryReferent(nsEventStateManager::sPointerLockedElement);
- NS_ASSERTION(pointerLockedElement != element,
- "We shouldn't be clearing pointer locked flag on pointer locked element!");
-#endif
- if (element) {
- element->ClearPointerLock();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPointerLockPermissionRequest::GetWindow(nsIDOMWindow** aWindow)
+{
+ nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
+ if (d) {
+ NS_IF_ADDREF(*aWindow = d->GetInnerWindow());
}
- sPendingPointerLockDoc = nullptr;
- sPendingPointerLockElement = nullptr;
+ return NS_OK;
}
-/* static */
-nsresult
-nsDocument::SetPendingPointerLockRequest(Element* aElement)
+NS_IMETHODIMP
+nsPointerLockPermissionRequest::GetElement(nsIDOMElement** aElement)
+{
+ // It is enough to implement GetWindow.
+ *aElement = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPointerLockPermissionRequest::Cancel()
{
- // If there's an existing pending pointer lock request, deny it.
- ClearPendingPointerLockRequest(true);
+ nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
+ Handled();
+ if (d) {
+ static_cast<nsDocument*>(d.get())->mCancelledPointerLockRequests++;
+ DispatchPointerLockError(d);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPointerLockPermissionRequest::Allow()
+{
+ nsCOMPtr<Element> e = do_QueryReferent(mElement);
+ nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
+ nsDocument* d = static_cast<nsDocument*>(doc.get());
+ if (!e || !d || gPendingPointerLockRequest != this ||
+ e->GetCurrentDoc() != d ||
+ (!mUserInputOrChromeCaller && !d->mIsApprovedForFullscreen)) {
+ Handled();
+ DispatchPointerLockError(d);
+ return NS_OK;
+ }
- NS_ENSURE_TRUE(aElement != nullptr, NS_ERROR_FAILURE);
+ // Mark handled here so that we don't need to call it everywhere below.
+ Handled();
- sPendingPointerLockDoc = do_GetWeakReference(aElement->OwnerDoc());
- sPendingPointerLockElement = do_GetWeakReference(aElement);
+ nsCOMPtr<Element> pointerLockedElement =
+ do_QueryReferent(nsEventStateManager::sPointerLockedElement);
+ if (e == pointerLockedElement) {
+ DispatchPointerLockChange(d);
+ return NS_OK;
+ }
- // Set the pointer lock flag, so that if the element is removed from
- // its document we know to cancel the pending request.
- aElement->SetPointerLock();
+ // Note, we must bypass focus change, so pass true as the last parameter!
+ if (!d->ShouldLockPointer(e, pointerLockedElement, true)) {
+ DispatchPointerLockError(d);
+ return NS_OK;
+ }
+ if (!d->SetPointerLock(e, NS_STYLE_CURSOR_NONE)) {
+ DispatchPointerLockError(d);
+ return NS_OK;
+ }
+
+ d->mCancelledPointerLockRequests = 0;
+ e->SetPointerLock();
+ nsEventStateManager::sPointerLockedElement = do_GetWeakReference(e);
+ nsEventStateManager::sPointerLockedDoc = do_GetWeakReference(doc);
+ NS_ASSERTION(nsEventStateManager::sPointerLockedElement &&
+ nsEventStateManager::sPointerLockedDoc,
+ "aElement and this should support weak references!");
+
+ DispatchPointerLockChange(d);
return NS_OK;
}
@@ -10564,13 +10647,23 @@ nsDocument::Observe(nsISupports *aSubject,
return NS_OK;
}
SetApprovedForFullscreen(true);
- nsCOMPtr<nsIDocument> doc(do_QueryReferent(sPendingPointerLockDoc));
- if (this == doc) {
- // This doc has a pointer lock request, waiting for fullscreen to be
- // approved before it can be granted. Process the pointer lock request.
- nsCOMPtr<Element> element(do_QueryReferent(sPendingPointerLockElement));
- nsDocument::ClearPendingPointerLockRequest(false);
- nsAsyncPointerLockRequest::Request(element, this);
+ if (gPendingPointerLockRequest) {
+ // We have a request pending. Create a clone of it and re-dispatch so that
+ // Run() method gets called again.
+ nsCOMPtr<Element> el =
+ do_QueryReferent(gPendingPointerLockRequest->mElement);
+ nsCOMPtr<nsIDocument> doc =
+ do_QueryReferent(gPendingPointerLockRequest->mDocument);
+ bool userInputOrChromeCaller =
+ gPendingPointerLockRequest->mUserInputOrChromeCaller;
+ gPendingPointerLockRequest->Handled();
+ if (doc == this && el && el->GetCurrentDoc() == doc) {
+ nsPointerLockPermissionRequest* clone =
+ new nsPointerLockPermissionRequest(el, userInputOrChromeCaller);
+ gPendingPointerLockRequest = clone;
+ nsCOMPtr<nsIRunnable> r = gPendingPointerLockRequest.get();
+ NS_DispatchToMainThread(r);
+ }
}
}
return NS_OK;
@@ -10589,51 +10682,32 @@ nsDocument::RequestPointerLock(Element* aElement)
return;
}
- if (!ShouldLockPointer(aElement)) {
+ if (!ShouldLockPointer(aElement, pointerLockedElement)) {
DispatchPointerLockError(this);
return;
}
- if (!mIsApprovedForFullscreen) {
- // Document isn't yet approved for fullscreen, so we must wait until
- // it's been approved.
- if (NS_FAILED(SetPendingPointerLockRequest(aElement))) {
- NS_WARNING("Failed to make pointer lock request pending!");
- DispatchPointerLockError(this);
- }
- return;
- }
-
- // If there's an existing pending pointer lock request, deny it.
- nsDocument::ClearPendingPointerLockRequest(true);
-
- if (!SetPointerLock(aElement, NS_STYLE_CURSOR_NONE)) {
- DispatchPointerLockError(this);
- return;
- }
+ bool userInputOrChromeCaller = nsEventStateManager::IsHandlingUserInput() ||
+ nsContentUtils::IsCallerChrome();
- aElement->SetPointerLock();
- nsEventStateManager::sPointerLockedElement = do_GetWeakReference(aElement);
- nsEventStateManager::sPointerLockedDoc =
- do_GetWeakReference(static_cast<nsIDocument*>(this));
- NS_ASSERTION(nsEventStateManager::sPointerLockedElement &&
- nsEventStateManager::sPointerLockedDoc,
- "aElement and this should support weak references!");
-
- DispatchPointerLockChange(this);
+ gPendingPointerLockRequest =
+ new nsPointerLockPermissionRequest(aElement, userInputOrChromeCaller);
+ nsCOMPtr<nsIRunnable> r = gPendingPointerLockRequest.get();
+ NS_DispatchToMainThread(r);
}
bool
-nsDocument::ShouldLockPointer(Element* aElement)
+nsDocument::ShouldLockPointer(Element* aElement, Element* aCurrentLock,
+ bool aNoFocusCheck)
{
// Check if pointer lock pref is enabled
if (!Preferences::GetBool("full-screen-api.pointer-lock.enabled")) {
NS_WARNING("ShouldLockPointer(): Pointer Lock pref not enabled");
return false;
}
- if (aElement != GetFullScreenElement()) {
- NS_WARNING("ShouldLockPointer(): Element not in fullscreen");
+ if (aCurrentLock && aCurrentLock->OwnerDoc() != aElement->OwnerDoc()) {
+ NS_WARNING("ShouldLockPointer(): Existing pointer lock element in a different document");
return false;
}
@@ -10649,9 +10723,6 @@ nsDocument::ShouldLockPointer(Element* aElement)
// Check if the element is in a document with a docshell.
nsCOMPtr<nsIDocument> ownerDoc = aElement->OwnerDoc();
- if (!ownerDoc) {
- return false;
- }
if (!nsCOMPtr<nsISupports>(ownerDoc->GetContainer())) {
return false;
}
@@ -10667,6 +10738,23 @@ nsDocument::ShouldLockPointer(Element* aElement)
return false;
}
+ nsCOMPtr<nsIDOMWindow> top;
+ ownerWindow->GetScriptableTop(getter_AddRefs(top));
+ nsCOMPtr<nsPIDOMWindow> piTop = do_QueryInterface(top);
+ if (!piTop || !piTop->GetExtantDoc() ||
+ piTop->GetExtantDoc()->Hidden()) {
+ NS_WARNING("ShouldLockPointer(): Top document isn't visible.");
+ return false;
+ }
+
+ if (!aNoFocusCheck) {
+ mozilla::ErrorResult rv;
+ if (!piTop->GetExtantDoc()->HasFocus(rv)) {
+ NS_WARNING("ShouldLockPointer(): Top document isn't focused.");
+ return false;
+ }
+ }
+
return true;
}
@@ -10728,19 +10816,15 @@ nsDocument::SetPointerLock(Element* aElement, int aCursorStyle)
}
void
-nsDocument::UnlockPointer()
+nsDocument::UnlockPointer(nsIDocument* aDoc)
{
- // If our pointer lock request is pending awaiting authorization, deny the
- // request.
- ClearPendingPointerLockRequest(true);
-
if (!nsEventStateManager::sIsPointerLocked) {
return;
}
nsCOMPtr<nsIDocument> pointerLockedDoc =
do_QueryReferent(nsEventStateManager::sPointerLockedDoc);
- if (!pointerLockedDoc) {
+ if (!pointerLockedDoc || (aDoc && aDoc != pointerLockedDoc)) {
return;
}
nsDocument* doc = static_cast<nsDocument*>(pointerLockedDoc.get());
@@ -10750,20 +10834,21 @@ nsDocument::UnlockPointer()
nsCOMPtr<Element> pointerLockedElement =
do_QueryReferent(nsEventStateManager::sPointerLockedElement);
- if (!pointerLockedElement) {
- return;
+ if (pointerLockedElement) {
+ pointerLockedElement->ClearPointerLock();
}
nsEventStateManager::sPointerLockedElement = nullptr;
nsEventStateManager::sPointerLockedDoc = nullptr;
- pointerLockedElement->ClearPointerLock();
+ static_cast<nsDocument*>(pointerLockedDoc.get())->mAllowRelocking = !!aDoc;
+ gPendingPointerLockRequest = nullptr;
DispatchPointerLockChange(pointerLockedDoc);
}
void
-nsIDocument::UnlockPointer()
+nsIDocument::UnlockPointer(nsIDocument* aDoc)
{
- nsDocument::UnlockPointer();
+ nsDocument::UnlockPointer(aDoc);
}
NS_IMETHODIMP
@@ -10805,6 +10890,12 @@ nsIDocument::GetMozPointerLockElement()
return pointerLockedElement;
}
+void
+nsDocument::XPCOMShutdown()
+{
+ gPendingPointerLockRequest = nullptr;
+}
+
#define EVENT(name_, id_, type_, struct_) \
NS_IMETHODIMP nsDocument::GetOn##name_(JSContext *cx, jsval *vp) { \
return nsINode::GetOn##name_(cx, vp); \
View
36 content/base/src/nsDocument.h
@@ -94,6 +94,7 @@ class nsDOMNavigationTiming;
class nsWindowSizes;
class nsHtml5TreeOpExecutor;
class nsDocumentOnStack;
+class nsPointerLockPermissionRequest;
namespace mozilla {
namespace dom {
@@ -1002,9 +1003,10 @@ class nsDocument : public nsIDocument,
virtual Element* GetMozFullScreenElement(mozilla::ErrorResult& rv);
void RequestPointerLock(Element* aElement);
- bool ShouldLockPointer(Element* aElement);
+ bool ShouldLockPointer(Element* aElement, Element* aCurrentLock,
+ bool aNoFocusCheck = false);
bool SetPointerLock(Element* aElement, int aCursorStyle);
- static void UnlockPointer();
+ static void UnlockPointer(nsIDocument* aDoc = nullptr);
// This method may fire a DOM event; if it does so it will happen
// synchronously.
@@ -1113,6 +1115,7 @@ class nsDocument : public nsIDocument,
// Set our title
virtual void SetTitle(const nsAString& aTitle, mozilla::ErrorResult& rv);
+ static void XPCOMShutdown();
protected:
nsresult doCreateShell(nsPresContext* aContext,
nsViewManager* aViewManager, nsStyleSet* aStyleSet,
@@ -1183,15 +1186,6 @@ class nsDocument : public nsIDocument,
// is a weak reference to avoid leaks due to circular references.
nsWeakPtr mScopeObject;
- // Weak reference to the document which owned the pending pointer lock
- // element, at the time it requested pointer lock.
- static nsWeakPtr sPendingPointerLockDoc;
-
- // Weak reference to the element which requested pointer lock. This request
- // is "pending", and will be processed once the element's document has had
- // the "fullscreen" permission granted.
- static nsWeakPtr sPendingPointerLockElement;
-
// Stack of full-screen elements. When we request full-screen we push the
// full-screen element onto this stack, and when we cancel full-screen we
// pop one off this stack, restoring the previous full-screen state
@@ -1279,6 +1273,16 @@ class nsDocument : public nsIDocument,
// fullscreen will have an observer.
bool mHasFullscreenApprovedObserver:1;
+ friend class nsPointerLockPermissionRequest;
+ friend class nsCallRequestFullScreen;
+ // When set, trying to lock the pointer doesn't require permission from the
+ // user.
+ bool mAllowRelocking:1;
+
+ bool mAsyncFullscreenPending:1;
+
+ uint32_t mCancelledPointerLockRequests;
+
uint8_t mXMLDeclarationBits;
nsInterfaceHashtable<nsPtrHashKey<nsIContent>, nsPIBoxObject> *mBoxObjectTable;
@@ -1311,16 +1315,6 @@ class nsDocument : public nsIDocument,
nsresult CheckFrameOptions();
nsresult InitCSP(nsIChannel* aChannel);
- // Sets aElement to be the pending pointer lock element. Once this document's
- // node principal's URI is granted the "fullscreen" permission, the pointer
- // lock request will be processed. At any one time there can be only one
- // pending pointer lock request; calling this clears the previous pending
- // request.
- static nsresult SetPendingPointerLockRequest(Element* aElement);
-
- // Clears any pending pointer lock request.
- static void ClearPendingPointerLockRequest(bool aDispatchErrorEvents);
-
/**
* Find the (non-anonymous) content in this document for aFrame. It will
* be aFrame's content node if that content is in this document and not
View
57 dom/base/nsFocusManager.cpp
@@ -982,7 +982,7 @@ nsFocusManager::WindowHidden(nsIDOMWindow* aWindow)
parentWindow->SetFocusedNode(nullptr);
}
- mFocusedWindow = window;
+ SetFocusedWindowInternal(window);
}
return NS_OK;
@@ -1601,7 +1601,7 @@ nsFocusManager::Blur(nsPIDOMWindow* aWindowToClear,
if (aAncestorWindowToFocus)
aAncestorWindowToFocus->SetFocusedNode(nullptr, 0, true);
- mFocusedWindow = nullptr;
+ SetFocusedWindowInternal(nullptr);
mFocusedContent = nullptr;
// pass 1 for the focus method when calling SendFocusOrBlurEvent just so
@@ -1709,7 +1709,7 @@ nsFocusManager::Focus(nsPIDOMWindow* aWindow,
if (aWindow->TakeFocus(true, focusMethod))
aIsNewDocument = true;
- mFocusedWindow = aWindow;
+ SetFocusedWindowInternal(aWindow);
// Update the system focus by focusing the root widget. But avoid this
// if 1) aAdjustWidgets is false or 2) aContent is a plugin that has its
@@ -3362,6 +3362,57 @@ nsFocusManager::GetFocusInSelection(nsPIDOMWindow* aWindow,
while (selectionNode && selectionNode != endSelectionNode);
}
+class PointerUnlocker : public nsRunnable
+{
+public:
+ PointerUnlocker()
+ {
+ MOZ_ASSERT(!PointerUnlocker::sActiveUnlocker);
+ PointerUnlocker::sActiveUnlocker = this;
+ }
+
+ ~PointerUnlocker()
+ {
+ if (PointerUnlocker::sActiveUnlocker == this) {
+ PointerUnlocker::sActiveUnlocker = nullptr;
+ }
+ }
+
+ NS_IMETHOD Run()
+ {
+ if (PointerUnlocker::sActiveUnlocker == this) {
+ PointerUnlocker::sActiveUnlocker = nullptr;
+ }
+ NS_ENSURE_STATE(nsFocusManager::GetFocusManager());
+ nsPIDOMWindow* focused =
+ nsFocusManager::GetFocusManager()->GetFocusedWindow();
+ nsCOMPtr<nsIDocument> pointerLockedDoc =
+ do_QueryReferent(nsEventStateManager::sPointerLockedDoc);
+ if (pointerLockedDoc &&
+ !nsContentUtils::IsInPointerLockContext(focused)) {
+ nsIDocument::UnlockPointer();
+ }
+ return NS_OK;
+ }
+
+ static PointerUnlocker* sActiveUnlocker;
+};
+
+PointerUnlocker*
+PointerUnlocker::sActiveUnlocker = nullptr;
+
+void
+nsFocusManager::SetFocusedWindowInternal(nsPIDOMWindow* aWindow)
+{
+ if (!PointerUnlocker::sActiveUnlocker &&
+ nsContentUtils::IsInPointerLockContext(mFocusedWindow) &&
+ !nsContentUtils::IsInPointerLockContext(aWindow)) {
+ nsCOMPtr<nsIRunnable> runnable = new PointerUnlocker();
+ NS_DispatchToCurrentThread(runnable);
+ }
+ mFocusedWindow = aWindow;
+}
+
nsresult
NS_NewFocusManager(nsIFocusManager** aResult)
{
View
3 dom/base/nsFocusManager.h
@@ -481,11 +481,14 @@ class nsFocusManager MOZ_FINAL : public nsIFocusManager,
bool aWindowShouldShowFocusRing,
bool aGettingFocus);
+ void SetFocusedWindowInternal(nsPIDOMWindow* aWindow);
+
// the currently active and front-most top-most window
nsCOMPtr<nsPIDOMWindow> mActiveWindow;
// the child or top-level window that is currently focused. This window will
// either be the same window as mActiveWindow or a descendant of it.
+ // Except during shutdown use SetFocusedWindowInternal to set mFocusedWindow!
nsCOMPtr<nsPIDOMWindow> mFocusedWindow;
// the currently focused content, which is always inside mFocusedWindow. This
View
6 dom/tests/mochitest/pointerlock/test_pointerlock-api.html
@@ -70,6 +70,7 @@
const isWinXP = navigator.userAgent.indexOf("Windows NT 5.1") != -1;
const isOSXLion = navigator.userAgent.indexOf("Mac OS X 10.7") != -1;
const isOSXMtnLion = navigator.userAgent.indexOf("Mac OS X 10.8") != -1;
+ const isWin8 = navigator.userAgent.indexOf("Windows NT 6.2") != -1;
function finish() {
SpecialPowers.clearUserPref("full-screen-api.enabled");
@@ -84,6 +85,11 @@
finish();
return;
}
+ if (isWin8) {
+ todo(false, "Can't reliably run full-screen + pointer lock tests on Windows 8");
+ finish();
+ return;
+ }
if (isOSXLion || isOSXMtnLion) {
todo(false, "Can't reliably run full-screen tests on OS X Lion or Mountain Lion, see bug 744125");
finish();
View
53 layout/base/nsPresShell.cpp
@@ -6059,6 +6059,7 @@ PresShell::HandleEvent(nsIFrame *aFrame,
nsIFrame* frame = aFrame;
if (aEvent->eventStructType == NS_TOUCH_EVENT) {
+ nsIDocument::UnlockPointer();
FlushPendingNotifications(Flush_Layout);
frame = GetNearestFrameContainingPresShell(this);
}
@@ -6659,27 +6660,37 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsEventStatus* aStatus)
nsIDocument* doc = GetCurrentEventContent() ?
mCurrentEventContent->OwnerDoc() : nullptr;
nsIDocument* fullscreenAncestor = nullptr;
- if (static_cast<const nsKeyEvent*>(aEvent)->keyCode == NS_VK_ESCAPE &&
- (fullscreenAncestor = nsContentUtils::GetFullscreenAncestor(doc))) {
- // Prevent default action on ESC key press when exiting
- // DOM fullscreen mode. This prevents the browser ESC key
- // handler from stopping all loads in the document, which
- // would cause <video> loads to stop.
- aEvent->mFlags.mDefaultPrevented = true;
- aEvent->mFlags.mOnlyChromeDispatch = true;
-
- if (aEvent->message == NS_KEY_UP) {
- // ESC key released while in DOM fullscreen mode.
- // If fullscreen is running in content-only mode, exit the target
- // doctree branch from fullscreen, otherwise fully exit all
- // browser windows and documents from fullscreen mode.
- // Note: in the content-only fullscreen case, we pass the
- // fullscreenAncestor since |doc| may not actually be fullscreen
- // here, and ExitFullscreen() has no affect when passed a
- // non-fullscreen document.
- nsIDocument::ExitFullscreen(
- nsContentUtils::IsFullscreenApiContentOnly() ? fullscreenAncestor : nullptr,
- /* async */ true);
+ if (static_cast<const nsKeyEvent*>(aEvent)->keyCode == NS_VK_ESCAPE) {
+ if ((fullscreenAncestor = nsContentUtils::GetFullscreenAncestor(doc))) {
+ // Prevent default action on ESC key press when exiting
+ // DOM fullscreen mode. This prevents the browser ESC key
+ // handler from stopping all loads in the document, which
+ // would cause <video> loads to stop.
+ aEvent->mFlags.mDefaultPrevented = true;
+ aEvent->mFlags.mOnlyChromeDispatch = true;
+
+ if (aEvent->message == NS_KEY_UP) {
+ // ESC key released while in DOM fullscreen mode.
+ // If fullscreen is running in content-only mode, exit the target
+ // doctree branch from fullscreen, otherwise fully exit all
+ // browser windows and documents from fullscreen mode.
+ // Note: in the content-only fullscreen case, we pass the
+ // fullscreenAncestor since |doc| may not actually be fullscreen
+ // here, and ExitFullscreen() has no affect when passed a
+ // non-fullscreen document.
+ nsIDocument::ExitFullscreen(
+ nsContentUtils::IsFullscreenApiContentOnly() ? fullscreenAncestor : nullptr,
+ /* async */ true);
+ }
+ }
+ nsCOMPtr<nsIDocument> pointerLockedDoc =
+ do_QueryReferent(nsEventStateManager::sPointerLockedDoc);
+ if (pointerLockedDoc) {
+ aEvent->mFlags.mDefaultPrevented = true;
+ aEvent->mFlags.mOnlyChromeDispatch = true;
+ if (aEvent->message == NS_KEY_UP) {
+ nsIDocument::UnlockPointer();
+ }
}
}
// Else not full-screen mode or key code is unrestricted, fall
View
3 layout/build/nsLayoutStatics.cpp
@@ -113,6 +113,7 @@ using namespace mozilla::system;
#include "nsApplicationCacheService.h"
#include "mozilla/dom/time/DateCacheCleaner.h"
#include "nsIMEStateManager.h"
+#include "nsDocument.h"
extern void NS_ShutdownEventTargetChainItemRecyclePool();
@@ -387,4 +388,6 @@ nsLayoutStatics::Shutdown()
ContentParent::ShutDown();
nsRefreshDriver::Shutdown();
+
+ nsDocument::XPCOMShutdown();
}

0 comments on commit 4adf407

Please sign in to comment.