Bug 737100 - Extend Pointer Lock (Mouse Lock) for non-fullscreen elements, p=smaug,dolske, r=cpearce,dolske,smaug
|
@ -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.
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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.">
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
После Ширина: | Высота: | Размер: 249 B |
После Ширина: | Высота: | Размер: 1.1 KiB |
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
После Ширина: | Высота: | Размер: 249 B |
После Ширина: | Высота: | Размер: 411 B |
После Ширина: | Высота: | Размер: 1.1 KiB |
После Ширина: | Высота: | Размер: 2.1 KiB |
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
После Ширина: | Высота: | Размер: 249 B |
После Ширина: | Высота: | Размер: 1.1 KiB |
|
@ -1882,6 +1882,12 @@ public:
|
|||
*/
|
||||
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.
|
||||
|
|
|
@ -969,7 +969,7 @@ public:
|
|||
|
||||
virtual void RequestPointerLock(Element* aElement) = 0;
|
||||
|
||||
static void UnlockPointer();
|
||||
static void UnlockPointer(nsIDocument* aDoc = nullptr);
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
@ -2021,7 +2021,7 @@ public:
|
|||
Element* GetMozPointerLockElement();
|
||||
void MozExitPointerLock()
|
||||
{
|
||||
UnlockPointer();
|
||||
UnlockPointer(this);
|
||||
}
|
||||
bool Hidden() const
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 @@ public:
|
|||
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 @@ public:
|
|||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
if (doc->mIsApprovedForFullscreen || doc->mAllowRelocking) {
|
||||
Allow();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
static void Request(Element* aElement, nsIDocument* aDocument)
|
||||
void Handled()
|
||||
{
|
||||
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);
|
||||
mElement = nullptr;
|
||||
mDocument = nullptr;
|
||||
if (gPendingPointerLockRequest == this) {
|
||||
gPendingPointerLockRequest = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
nsAsyncPointerLockRequest(Element* aElement, nsIDocument* aDocument)
|
||||
: mElement(aElement),
|
||||
mDocument(aDocument)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsAsyncPointerLockRequest);
|
||||
}
|
||||
|
||||
~nsAsyncPointerLockRequest()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsAsyncPointerLockRequest);
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
if (!sPendingPointerLockDoc) {
|
||||
// No pending request.
|
||||
return;
|
||||
}
|
||||
nsCOMPtr<nsIDocument> doc(do_QueryReferent(sPendingPointerLockDoc));
|
||||
if (aDispatchErrorEvents) {
|
||||
DispatchPointerLockError(doc);
|
||||
}
|
||||
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();
|
||||
}
|
||||
sPendingPointerLockDoc = nullptr;
|
||||
sPendingPointerLockElement = nullptr;
|
||||
aType = "pointerLock";
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
nsDocument::SetPendingPointerLockRequest(Element* aElement)
|
||||
NS_IMETHODIMP
|
||||
nsPointerLockPermissionRequest::GetAccess(nsACString& aAccess)
|
||||
{
|
||||
// If there's an existing pending pointer lock request, deny it.
|
||||
ClearPendingPointerLockRequest(true);
|
||||
aAccess = "unused";
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(aElement != nullptr, NS_ERROR_FAILURE);
|
||||
NS_IMETHODIMP
|
||||
nsPointerLockPermissionRequest::GetPrincipal(nsIPrincipal** aPrincipal)
|
||||
{
|
||||
nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
|
||||
if (d) {
|
||||
NS_ADDREF(*aPrincipal = d->NodePrincipal());
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
sPendingPointerLockDoc = do_GetWeakReference(aElement->OwnerDoc());
|
||||
sPendingPointerLockElement = do_GetWeakReference(aElement);
|
||||
NS_IMETHODIMP
|
||||
nsPointerLockPermissionRequest::GetWindow(nsIDOMWindow** aWindow)
|
||||
{
|
||||
nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
|
||||
if (d) {
|
||||
NS_IF_ADDREF(*aWindow = d->GetInnerWindow());
|
||||
}
|
||||
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();
|
||||
NS_IMETHODIMP
|
||||
nsPointerLockPermissionRequest::GetElement(nsIDOMElement** aElement)
|
||||
{
|
||||
// It is enough to implement GetWindow.
|
||||
*aElement = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPointerLockPermissionRequest::Cancel()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// Mark handled here so that we don't need to call it everywhere below.
|
||||
Handled();
|
||||
|
||||
nsCOMPtr<Element> pointerLockedElement =
|
||||
do_QueryReferent(nsEventStateManager::sPointerLockedElement);
|
||||
if (e == pointerLockedElement) {
|
||||
DispatchPointerLockChange(d);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// 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,42 +10682,23 @@ 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;
|
||||
}
|
||||
bool userInputOrChromeCaller = nsEventStateManager::IsHandlingUserInput() ||
|
||||
nsContentUtils::IsCallerChrome();
|
||||
|
||||
// If there's an existing pending pointer lock request, deny it.
|
||||
nsDocument::ClearPendingPointerLockRequest(true);
|
||||
|
||||
if (!SetPointerLock(aElement, NS_STYLE_CURSOR_NONE)) {
|
||||
DispatchPointerLockError(this);
|
||||
return;
|
||||
}
|
||||
|
||||
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")) {
|
||||
|
@ -10632,8 +10706,8 @@ nsDocument::ShouldLockPointer(Element* aElement)
|
|||
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); \
|
||||
|
|
|
@ -94,6 +94,7 @@ class nsDOMNavigationTiming;
|
|||
class nsWindowSizes;
|
||||
class nsHtml5TreeOpExecutor;
|
||||
class nsDocumentOnStack;
|
||||
class nsPointerLockPermissionRequest;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -1002,9 +1003,10 @@ public:
|
|||
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 @@ public:
|
|||
// 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 @@ protected:
|
|||
// 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 @@ protected:
|
|||
// 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 @@ private:
|
|||
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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -481,11 +481,14 @@ private:
|
|||
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
|
||||
|
|
|
@ -70,6 +70,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
|
|||
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 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
|
|||
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();
|
||||
|
|
|
@ -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 (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);
|
||||
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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|