mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-13 05:15:45 +00:00
Merge inbound to mozilla-central r=merge a=merge
This commit is contained in:
commit
fe75164c55
@ -27,7 +27,6 @@
|
||||
#include "nsIURI.h"
|
||||
#include "nsTextFormatter.h"
|
||||
#include "OuterDocAccessible.h"
|
||||
#include "Platform.h"
|
||||
#include "Role.h"
|
||||
#ifdef MOZ_ACCESSIBILITY_ATK
|
||||
#include "RootAccessibleWrap.h"
|
||||
@ -96,6 +95,15 @@ using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
/**
|
||||
* Accessibility service force enable/disable preference.
|
||||
* Supported values:
|
||||
* Accessibility is force enabled (accessibility should always be enabled): -1
|
||||
* Accessibility is enabled (will be started upon a request, default value): 0
|
||||
* Accessibility is force disabled (never enable accessibility): 1
|
||||
*/
|
||||
#define PREF_ACCESSIBILITY_FORCE_DISABLED "accessibility.force_disabled"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Statics
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -269,6 +277,11 @@ New_MaybeImageOrToolbarButtonAccessible(nsIContent* aContent,
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Cached value of the PREF_ACCESSIBILITY_FORCE_DISABLED preference.
|
||||
*/
|
||||
static int32_t sPlatformDisabledState = 0;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Markup maps array.
|
||||
|
||||
@ -1970,17 +1983,39 @@ XPCApplicationAcc()
|
||||
EPlatformDisabledState
|
||||
PlatformDisabledState()
|
||||
{
|
||||
static int disabledState = 0xff;
|
||||
|
||||
if (disabledState == 0xff) {
|
||||
disabledState = Preferences::GetInt("accessibility.force_disabled", 0);
|
||||
if (disabledState < ePlatformIsForceEnabled)
|
||||
disabledState = ePlatformIsForceEnabled;
|
||||
else if (disabledState > ePlatformIsDisabled)
|
||||
disabledState = ePlatformIsDisabled;
|
||||
static bool platformDisabledStateCached = false;
|
||||
if (platformDisabledStateCached) {
|
||||
return static_cast<EPlatformDisabledState>(sPlatformDisabledState);
|
||||
}
|
||||
|
||||
return (EPlatformDisabledState)disabledState;
|
||||
platformDisabledStateCached = true;
|
||||
Preferences::RegisterCallback(PrefChanged, PREF_ACCESSIBILITY_FORCE_DISABLED);
|
||||
return ReadPlatformDisabledState();
|
||||
}
|
||||
|
||||
EPlatformDisabledState
|
||||
ReadPlatformDisabledState()
|
||||
{
|
||||
sPlatformDisabledState = Preferences::GetInt(PREF_ACCESSIBILITY_FORCE_DISABLED, 0);
|
||||
if (sPlatformDisabledState < ePlatformIsForceEnabled) {
|
||||
sPlatformDisabledState = ePlatformIsForceEnabled;
|
||||
} else if (sPlatformDisabledState > ePlatformIsDisabled){
|
||||
sPlatformDisabledState = ePlatformIsDisabled;
|
||||
}
|
||||
|
||||
return static_cast<EPlatformDisabledState>(sPlatformDisabledState);
|
||||
}
|
||||
|
||||
void
|
||||
PrefChanged(const char* aPref, void* aClosure)
|
||||
{
|
||||
if (ReadPlatformDisabledState() == ePlatformIsDisabled) {
|
||||
// Force shut down accessibility.
|
||||
nsAccessibilityService* accService = nsAccessibilityService::gAccessibilityService;
|
||||
if (accService && !accService->IsShutdown()) {
|
||||
accService->Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "mozilla/a11y/DocManager.h"
|
||||
#include "mozilla/a11y/FocusManager.h"
|
||||
#include "mozilla/a11y/Platform.h"
|
||||
#include "mozilla/a11y/Role.h"
|
||||
#include "mozilla/a11y/SelectionManager.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
@ -74,6 +75,16 @@ struct XULMarkupMapInfo {
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* PREF_ACCESSIBILITY_FORCE_DISABLED preference change callback.
|
||||
*/
|
||||
void PrefChanged(const char* aPref, void* aClosure);
|
||||
|
||||
/**
|
||||
* Read and normalize PREF_ACCESSIBILITY_FORCE_DISABLED preference.
|
||||
*/
|
||||
EPlatformDisabledState ReadPlatformDisabledState();
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
@ -340,6 +351,7 @@ private:
|
||||
friend nsAccessibilityService* GetAccService();
|
||||
friend nsAccessibilityService* GetOrCreateAccService(uint32_t);
|
||||
friend void MaybeShutdownAccService(uint32_t);
|
||||
friend void mozilla::a11y::PrefChanged(const char*, void*);
|
||||
friend mozilla::a11y::FocusManager* mozilla::a11y::FocusMgr();
|
||||
friend mozilla::a11y::SelectionManager* mozilla::a11y::SelectionMgr();
|
||||
friend mozilla::a11y::ApplicationAccessible* mozilla::a11y::ApplicationAcc();
|
||||
|
@ -12,6 +12,7 @@ support-files =
|
||||
[browser_shutdown_multi_reference.js]
|
||||
[browser_shutdown_parent_own_reference.js]
|
||||
skip-if = !e10s || (os == 'win' && os_version == '5.1') # e10s specific test for a11y start/shutdown between parent and content.
|
||||
[browser_shutdown_pref.js]
|
||||
[browser_shutdown_proxy_acc_reference.js]
|
||||
skip-if = !e10s || (os == 'win') # e10s specific test for a11y start/shutdown between parent and content.
|
||||
[browser_shutdown_proxy_doc_acc_reference.js]
|
||||
|
55
accessible/tests/browser/browser_shutdown_pref.js
Normal file
55
accessible/tests/browser/browser_shutdown_pref.js
Normal file
@ -0,0 +1,55 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const PREF_ACCESSIBILITY_FORCE_DISABLED = "accessibility.force_disabled";
|
||||
|
||||
add_task(async function testForceDisable() {
|
||||
ok(!Services.appinfo.accessibilityEnabled, "Accessibility is disabled by default");
|
||||
|
||||
info("Reset force disabled preference");
|
||||
Services.prefs.clearUserPref(PREF_ACCESSIBILITY_FORCE_DISABLED);
|
||||
|
||||
info("Enable accessibility service via XPCOM");
|
||||
let a11yInit = initPromise();
|
||||
let accService = Cc["@mozilla.org/accessibilityService;1"].getService(
|
||||
Ci.nsIAccessibilityService);
|
||||
await a11yInit;
|
||||
ok(Services.appinfo.accessibilityEnabled, "Accessibility is enabled");
|
||||
|
||||
info("Force disable a11y service via preference");
|
||||
let a11yShutdown = shutdownPromise();
|
||||
Services.prefs.setIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED, 1);
|
||||
await a11yShutdown;
|
||||
ok(!Services.appinfo.accessibilityEnabled, "Accessibility is disabled");
|
||||
|
||||
info("Attempt to get an instance of a11y service and call its method.");
|
||||
accService = Cc["@mozilla.org/accessibilityService;1"].getService(
|
||||
Ci.nsIAccessibilityService);
|
||||
try {
|
||||
accService.getAccesssibleFor(document);
|
||||
ok(false, "getAccesssibleFor should've triggered an exception.");
|
||||
} catch (e) {
|
||||
ok(true, "getAccesssibleFor triggers an exception as a11y service is shutdown.");
|
||||
}
|
||||
ok(!Services.appinfo.accessibilityEnabled, "Accessibility is disabled");
|
||||
|
||||
info("Reset force disabled preference");
|
||||
Services.prefs.clearUserPref(PREF_ACCESSIBILITY_FORCE_DISABLED);
|
||||
|
||||
info("Create a11y service again");
|
||||
a11yInit = initPromise();
|
||||
accService = Cc["@mozilla.org/accessibilityService;1"].getService(
|
||||
Ci.nsIAccessibilityService);
|
||||
await a11yInit;
|
||||
ok(Services.appinfo.accessibilityEnabled, "Accessibility is enabled");
|
||||
|
||||
info("Remove all references to a11y service");
|
||||
a11yShutdown = shutdownPromise();
|
||||
accService = null;
|
||||
forceGC();
|
||||
await a11yShutdown;
|
||||
ok(!Services.appinfo.accessibilityEnabled, "Accessibility is disabled");
|
||||
});
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "nsAccessiblePivot.h"
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "Platform.h"
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
#include "Logging.h"
|
||||
@ -43,7 +44,9 @@ xpcAccessibilityService::AddRef(void)
|
||||
nsrefcnt count = ++mRefCnt;
|
||||
NS_LOG_ADDREF(this, count, "xpcAccessibilityService", sizeof(*this));
|
||||
|
||||
if (mRefCnt > 1) {
|
||||
// We want refcount to be > 1 because one reference is added in the XPCOM
|
||||
// accessibility service getter.
|
||||
if (mRefCnt > 1 && PlatformDisabledState() != ePlatformIsDisabled) {
|
||||
GetOrCreateAccService(nsAccessibilityService::eXPCOM);
|
||||
}
|
||||
|
||||
@ -114,7 +117,12 @@ xpcAccessibilityService::GetAccessibleFor(nsIDOMNode *aNode,
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
DocAccessible* document = GetAccService()->GetDocAccessible(node->OwnerDoc());
|
||||
nsAccessibilityService* accService = GetAccService();
|
||||
if (!accService) {
|
||||
return NS_ERROR_SERVICE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
DocAccessible* document = accService->GetDocAccessible(node->OwnerDoc());
|
||||
if (document) {
|
||||
NS_IF_ADDREF(*aAccessible = ToXPC(document->GetAccessible(node)));
|
||||
}
|
||||
@ -125,7 +133,12 @@ xpcAccessibilityService::GetAccessibleFor(nsIDOMNode *aNode,
|
||||
NS_IMETHODIMP
|
||||
xpcAccessibilityService::GetStringRole(uint32_t aRole, nsAString& aString)
|
||||
{
|
||||
GetAccService()->GetStringRole(aRole, aString);
|
||||
nsAccessibilityService* accService = GetAccService();
|
||||
if (!accService) {
|
||||
return NS_ERROR_SERVICE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
accService->GetStringRole(aRole, aString);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -133,7 +146,12 @@ NS_IMETHODIMP
|
||||
xpcAccessibilityService::GetStringStates(uint32_t aState, uint32_t aExtraState,
|
||||
nsISupports **aStringStates)
|
||||
{
|
||||
GetAccService()->GetStringStates(aState, aExtraState, aStringStates);
|
||||
nsAccessibilityService* accService = GetAccService();
|
||||
if (!accService) {
|
||||
return NS_ERROR_SERVICE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
accService->GetStringStates(aState, aExtraState, aStringStates);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -141,7 +159,12 @@ NS_IMETHODIMP
|
||||
xpcAccessibilityService::GetStringEventType(uint32_t aEventType,
|
||||
nsAString& aString)
|
||||
{
|
||||
GetAccService()->GetStringEventType(aEventType, aString);
|
||||
nsAccessibilityService* accService = GetAccService();
|
||||
if (!accService) {
|
||||
return NS_ERROR_SERVICE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
accService->GetStringEventType(aEventType, aString);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -149,7 +172,12 @@ NS_IMETHODIMP
|
||||
xpcAccessibilityService::GetStringRelationType(uint32_t aRelationType,
|
||||
nsAString& aString)
|
||||
{
|
||||
GetAccService()->GetStringRelationType(aRelationType, aString);
|
||||
nsAccessibilityService* accService = GetAccService();
|
||||
if (!accService) {
|
||||
return NS_ERROR_SERVICE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
accService->GetStringRelationType(aRelationType, aString);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -168,13 +196,18 @@ xpcAccessibilityService::GetAccessibleFromCache(nsIDOMNode* aNode,
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsAccessibilityService* accService = GetAccService();
|
||||
if (!accService) {
|
||||
return NS_ERROR_SERVICE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
// Search for an accessible in each of our per document accessible object
|
||||
// caches. If we don't find it, and the given node is itself a document, check
|
||||
// our cache of document accessibles (document cache). Note usually shutdown
|
||||
// document accessibles are not stored in the document cache, however an
|
||||
// "unofficially" shutdown document (i.e. not from DocManager) can still
|
||||
// exist in the document cache.
|
||||
Accessible* accessible = GetAccService()->FindAccessibleInCache(node);
|
||||
Accessible* accessible = accService->FindAccessibleInCache(node);
|
||||
if (!accessible) {
|
||||
nsCOMPtr<nsIDocument> document(do_QueryInterface(node));
|
||||
if (document) {
|
||||
@ -235,6 +268,11 @@ NS_GetAccessibilityService(nsIAccessibilityService** aResult)
|
||||
NS_ENSURE_TRUE(aResult, NS_ERROR_NULL_POINTER);
|
||||
*aResult = nullptr;
|
||||
|
||||
// Do not initialize accessibility if it is force disabled.
|
||||
if (PlatformDisabledState() == ePlatformIsDisabled) {
|
||||
return NS_ERROR_SERVICE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
GetOrCreateAccService(nsAccessibilityService::eXPCOM);
|
||||
|
||||
xpcAccessibilityService* service = new xpcAccessibilityService();
|
||||
|
@ -2590,7 +2590,12 @@ CustomizeMode.prototype = {
|
||||
|
||||
function updatePlayers() {
|
||||
if (keydown) {
|
||||
p1 += (keydown == 37 ? -1 : 1) * 10 * keydownAdj;
|
||||
let p1Adj = 1;
|
||||
if ((keydown == 37 && !isRTL) ||
|
||||
(keydown == 39 && isRTL)) {
|
||||
p1Adj = -1;
|
||||
}
|
||||
p1 += p1Adj * 10 * keydownAdj;
|
||||
}
|
||||
|
||||
let sign = Math.sign(ballDxDy[0]);
|
||||
@ -2622,15 +2627,16 @@ CustomizeMode.prototype = {
|
||||
}
|
||||
|
||||
function draw() {
|
||||
elements.player1.style.transform = "translate(" + p1 + "px, -37px)";
|
||||
elements.player2.style.transform = "translate(" + p2 + "px, 300px)";
|
||||
elements.ball.style.transform = "translate(" + ball[0] + "px, " + ball[1] + "px)";
|
||||
elements.score.textContent = score;
|
||||
elements.lives.setAttribute("lives", lives);
|
||||
let xAdj = isRTL ? -1 : 1;
|
||||
elements["wp-player1"].style.transform = "translate(" + (xAdj * p1) + "px, -37px)";
|
||||
elements["wp-player2"].style.transform = "translate(" + (xAdj * p2) + "px, " + gameSide + "px)";
|
||||
elements["wp-ball"].style.transform = "translate(" + (xAdj * ball[0]) + "px, " + ball[1] + "px)";
|
||||
elements["wp-score"].textContent = score;
|
||||
elements["wp-lives"].setAttribute("lives", lives);
|
||||
if (score >= winScore) {
|
||||
let arena = elements.arena;
|
||||
let image = "url(chrome://browser/skin/customizableui/whimsy.png)";
|
||||
let position = `${ball[0] - 10}px ${ball[1] - 10}px`;
|
||||
let position = `${(isRTL ? gameSide : 0) + (xAdj * ball[0]) - 10}px ${ball[1] - 10}px`;
|
||||
let repeat = "no-repeat";
|
||||
let size = "20px";
|
||||
if (arena.style.backgroundImage) {
|
||||
@ -2651,6 +2657,22 @@ CustomizeMode.prototype = {
|
||||
}
|
||||
|
||||
function onkeydown(event) {
|
||||
keys.push(event.which);
|
||||
if (keys.length > 10) {
|
||||
keys.shift();
|
||||
let codeEntered = true;
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
if (keys[i] != keysCode[i]) {
|
||||
codeEntered = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (codeEntered) {
|
||||
elements.arena.setAttribute("kcode", "true");
|
||||
let spacer = document.querySelector("#customization-palette > toolbarpaletteitem");
|
||||
spacer.setAttribute("kcode", "true");
|
||||
}
|
||||
}
|
||||
if (event.which == 37 /* left */ ||
|
||||
event.which == 39 /* right */) {
|
||||
keydown = event.which;
|
||||
@ -2677,10 +2699,13 @@ CustomizeMode.prototype = {
|
||||
}
|
||||
arena.removeAttribute("score");
|
||||
arena.removeAttribute("lives");
|
||||
arena.removeAttribute("kcode");
|
||||
arena.style.removeProperty("background-image");
|
||||
arena.style.removeProperty("background-position");
|
||||
arena.style.removeProperty("background-repeat");
|
||||
arena.style.removeProperty("background-size");
|
||||
let spacer = document.querySelector("#customization-palette > toolbarpaletteitem");
|
||||
spacer.removeAttribute("kcode");
|
||||
elements = null;
|
||||
document = null;
|
||||
quit = true;
|
||||
@ -2702,6 +2727,8 @@ CustomizeMode.prototype = {
|
||||
let paddleWidth = 84;
|
||||
let keydownAdj = 1;
|
||||
let keydown = 0;
|
||||
let keys = [];
|
||||
let keysCode = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65];
|
||||
let lives = 5;
|
||||
let winScore = 11;
|
||||
let quit = false;
|
||||
@ -2710,18 +2737,19 @@ CustomizeMode.prototype = {
|
||||
let elements = {
|
||||
arena: document.getElementById("customization-pong-arena")
|
||||
};
|
||||
let isRTL = document.documentElement.matches(":-moz-locale-dir(rtl)");
|
||||
|
||||
document.addEventListener("keydown", onkeydown);
|
||||
document.addEventListener("keyup", onkeyup);
|
||||
|
||||
for (let id of ["player1", "player2", "ball", "score", "lives"]) {
|
||||
let el = document.createElement("box");
|
||||
el.id = id;
|
||||
elements[id] = elements.arena.appendChild(el);
|
||||
el.id = "wp-" + id;
|
||||
elements[el.id] = elements.arena.appendChild(el);
|
||||
}
|
||||
|
||||
let spacer = this.visiblePalette.querySelector("toolbarpaletteitem");
|
||||
for (let player of ["#player1", "#player2"]) {
|
||||
for (let player of ["#wp-player1", "#wp-player2"]) {
|
||||
let val = "-moz-element(#" + spacer.id + ") no-repeat";
|
||||
elements.arena.querySelector(player).style.background = val;
|
||||
}
|
||||
@ -2731,8 +2759,8 @@ CustomizeMode.prototype = {
|
||||
update();
|
||||
draw();
|
||||
if (quit) {
|
||||
elements.score.textContent = score;
|
||||
elements.lives && elements.lives.setAttribute("lives", lives);
|
||||
elements["wp-score"].textContent = score;
|
||||
elements["wp-lives"] && elements["wp-lives"].setAttribute("lives", lives);
|
||||
elements.arena.setAttribute("score", score);
|
||||
elements.arena.setAttribute("lives", lives);
|
||||
} else {
|
||||
|
@ -580,8 +580,8 @@ toolbarpaletteitem[place=toolbar] > toolbarspring {
|
||||
margin: 0 -7px;
|
||||
}
|
||||
|
||||
#lives,
|
||||
#ball {
|
||||
#wp-lives,
|
||||
#wp-ball {
|
||||
/* Don't need HiDPI versions since the size used will be scaled down to 20x20. */
|
||||
background-image: url("chrome://browser/skin/customizableui/whimsy.png");
|
||||
background-size: contain;
|
||||
@ -594,57 +594,73 @@ toolbarpaletteitem[place=toolbar] > toolbarspring {
|
||||
border-left: 1px solid currentColor;
|
||||
border-right: 1px solid currentColor;
|
||||
margin: 16px auto 0;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
#ball {
|
||||
margin-left: -10px;
|
||||
#customization-palette[whimsypong] > toolbarpaletteitem[kcode],
|
||||
#customization-pong-arena[kcode] {
|
||||
animation-name: kcode;
|
||||
animation-timing-function: steps(5);
|
||||
animation-duration: 1s;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
#wp-ball {
|
||||
margin-inline-start: -10px;
|
||||
margin-top: -10px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
#player1,
|
||||
#player2 {
|
||||
#wp-player1,
|
||||
#wp-player2 {
|
||||
width: 84px;
|
||||
height: calc(39px + 3em);
|
||||
background-color: rgba(255,255,0,.5);
|
||||
}
|
||||
|
||||
#player1,
|
||||
#player2,
|
||||
#ball,
|
||||
#score {
|
||||
#wp-player1,
|
||||
#wp-player2,
|
||||
#wp-ball,
|
||||
#wp-score {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#score {
|
||||
#wp-score {
|
||||
transform: translateX(-4ch);
|
||||
}
|
||||
|
||||
#lives {
|
||||
#wp-score:-moz-locale-dir(rtl) {
|
||||
transform: translateX(4ch);
|
||||
}
|
||||
|
||||
#wp-lives {
|
||||
transform: translate(-4ch, 1ch);
|
||||
}
|
||||
|
||||
#lives[lives="5"] {
|
||||
#wp-lives:-moz-locale-dir(rtl) {
|
||||
transform: translate(4ch, 1ch);
|
||||
}
|
||||
|
||||
#wp-lives[lives="5"] {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
#lives[lives="4"] {
|
||||
#wp-lives[lives="4"] {
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
#lives[lives="3"] {
|
||||
#wp-lives[lives="3"] {
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
#lives[lives="2"] {
|
||||
#wp-lives[lives="2"] {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
#lives[lives="1"] {
|
||||
#wp-lives[lives="1"] {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
#customization-pong-arena[lives="0"] > #ball {
|
||||
#customization-pong-arena[lives="0"] > #wp-ball {
|
||||
animation: game-over 4s forwards ease;
|
||||
}
|
||||
|
||||
@ -663,3 +679,12 @@ toolbarpaletteitem[place=toolbar] > toolbarspring {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes kcode {
|
||||
0% { border-color: rgb(195,17,206); color: rgb(195,17,206); }
|
||||
20% { border-color: rgb(252,82,27); color: rgb(252,82,27); }
|
||||
40% { border-color: rgb(251,179,0); color: rgb(251,179,0); }
|
||||
60% { border-color: rgb(105,211,0); color: rgb(105,211,0); }
|
||||
80% { border-color: rgb(20,155,249); color: rgb(20,155,249); }
|
||||
100% { border-color: rgb(195,17,206); color: rgb(195,17,206); }
|
||||
}
|
||||
|
@ -35,25 +35,34 @@ try {
|
||||
let prefix = msg.data.prefix;
|
||||
let addonId = msg.data.addonId;
|
||||
|
||||
let conn = DebuggerServer.connectToParent(prefix, mm);
|
||||
conn.parentMessageManager = mm;
|
||||
connections.set(prefix, conn);
|
||||
// Using the JS debugger causes problems when we're trying to
|
||||
// schedule those zone groups across different threads. Calling
|
||||
// blockThreadedExecution causes Gecko to switch to a simpler
|
||||
// single-threaded model until unblockThreadedExecution is
|
||||
// called later. We cannot start the debugger until the callback
|
||||
// passed to blockThreadedExecution has run, signaling that
|
||||
// we're running single-threaded.
|
||||
Cu.blockThreadedExecution(() => {
|
||||
let conn = DebuggerServer.connectToParent(prefix, mm);
|
||||
conn.parentMessageManager = mm;
|
||||
connections.set(prefix, conn);
|
||||
|
||||
let actor;
|
||||
let actor;
|
||||
|
||||
if (addonId) {
|
||||
const { WebExtensionChildActor } = require("devtools/server/actors/webextension");
|
||||
actor = new WebExtensionChildActor(conn, chromeGlobal, prefix, addonId);
|
||||
} else {
|
||||
const { ContentActor } = require("devtools/server/actors/childtab");
|
||||
actor = new ContentActor(conn, chromeGlobal, prefix);
|
||||
}
|
||||
if (addonId) {
|
||||
const { WebExtensionChildActor } = require("devtools/server/actors/webextension");
|
||||
actor = new WebExtensionChildActor(conn, chromeGlobal, prefix, addonId);
|
||||
} else {
|
||||
const { ContentActor } = require("devtools/server/actors/childtab");
|
||||
actor = new ContentActor(conn, chromeGlobal, prefix);
|
||||
}
|
||||
|
||||
let actorPool = new ActorPool(conn);
|
||||
actorPool.addActor(actor);
|
||||
conn.addActorPool(actorPool);
|
||||
let actorPool = new ActorPool(conn);
|
||||
actorPool.addActor(actor);
|
||||
conn.addActorPool(actorPool);
|
||||
|
||||
sendAsyncMessage("debug:actor", {actor: actor.form(), prefix: prefix});
|
||||
sendAsyncMessage("debug:actor", {actor: actor.form(), prefix: prefix});
|
||||
});
|
||||
});
|
||||
|
||||
addMessageListener("debug:connect", onConnect);
|
||||
@ -100,6 +109,8 @@ try {
|
||||
return;
|
||||
}
|
||||
|
||||
Cu.unblockThreadedExecution();
|
||||
|
||||
removeMessageListener("debug:disconnect", onDisconnect);
|
||||
// Call DebuggerServerConnection.close to destroy all child actors. It should end up
|
||||
// calling DebuggerServerConnection.onClosed that would actually cleanup all actor
|
||||
|
@ -41,6 +41,8 @@ function setupServer(mm) {
|
||||
mm.addMessageListener("debug:content-process-destroy", function onDestroy() {
|
||||
mm.removeMessageListener("debug:content-process-destroy", onDestroy);
|
||||
|
||||
Cu.unblockThreadedExecution();
|
||||
|
||||
DebuggerServer.destroy();
|
||||
gLoader.destroy();
|
||||
gLoader = null;
|
||||
@ -54,23 +56,32 @@ function init(msg) {
|
||||
mm.QueryInterface(Ci.nsISyncMessageSender);
|
||||
let prefix = msg.data.prefix;
|
||||
|
||||
// Setup a server if none started yet
|
||||
let loader = setupServer(mm);
|
||||
// Using the JS debugger causes problems when we're trying to
|
||||
// schedule those zone groups across different threads. Calling
|
||||
// blockThreadedExecution causes Gecko to switch to a simpler
|
||||
// single-threaded model until unblockThreadedExecution is called
|
||||
// later. We cannot start the debugger until the callback passed to
|
||||
// blockThreadedExecution has run, signaling that we're running
|
||||
// single-threaded.
|
||||
Cu.blockThreadedExecution(() => {
|
||||
// Setup a server if none started yet
|
||||
let loader = setupServer(mm);
|
||||
|
||||
// Connect both parent/child processes debugger servers RDP via message
|
||||
// managers
|
||||
let { DebuggerServer } = loader.require("devtools/server/main");
|
||||
let conn = DebuggerServer.connectToParent(prefix, mm);
|
||||
conn.parentMessageManager = mm;
|
||||
// Connect both parent/child processes debugger servers RDP via message
|
||||
// managers
|
||||
let { DebuggerServer } = loader.require("devtools/server/main");
|
||||
let conn = DebuggerServer.connectToParent(prefix, mm);
|
||||
conn.parentMessageManager = mm;
|
||||
|
||||
let { ChildProcessActor } =
|
||||
loader.require("devtools/server/actors/child-process");
|
||||
let { ActorPool } = loader.require("devtools/server/main");
|
||||
let actor = new ChildProcessActor(conn);
|
||||
let actorPool = new ActorPool(conn);
|
||||
actorPool.addActor(actor);
|
||||
conn.addActorPool(actorPool);
|
||||
let { ChildProcessActor } =
|
||||
loader.require("devtools/server/actors/child-process");
|
||||
let { ActorPool } = loader.require("devtools/server/main");
|
||||
let actor = new ChildProcessActor(conn);
|
||||
let actorPool = new ActorPool(conn);
|
||||
actorPool.addActor(actor);
|
||||
conn.addActorPool(actorPool);
|
||||
|
||||
let response = { actor: actor.form() };
|
||||
mm.sendAsyncMessage("debug:content-process-actor", response);
|
||||
let response = { actor: actor.form() };
|
||||
mm.sendAsyncMessage("debug:content-process-actor", response);
|
||||
});
|
||||
}
|
||||
|
@ -6335,6 +6335,10 @@ nsDocShell::SetIsActive(bool aIsActive)
|
||||
// Keep track ourselves.
|
||||
mIsActive = aIsActive;
|
||||
|
||||
if (TabChild* tc = TabChild::GetFrom(this)) {
|
||||
tc->OnDocShellActivated(aIsActive);
|
||||
}
|
||||
|
||||
// Clear prerender flag if necessary.
|
||||
if (mIsPrerendered && aIsActive) {
|
||||
MOZ_ASSERT(mPrerenderGlobalHistory.get());
|
||||
|
@ -2668,6 +2668,23 @@ TabChild::RemovePendingDocShellBlocker()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TabChild::OnDocShellActivated(bool aIsActive)
|
||||
{
|
||||
if (aIsActive) {
|
||||
if (!sActiveTabs) {
|
||||
sActiveTabs = new nsTHashtable<nsPtrHashKey<TabChild>>();
|
||||
}
|
||||
sActiveTabs->PutEntry(this);
|
||||
} else {
|
||||
if (sActiveTabs) {
|
||||
sActiveTabs->RemoveEntry(this);
|
||||
// We don't delete sActiveTabs here when it's empty since that
|
||||
// could cause a lot of churn. Instead, we wait until ~TabChild.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TabChild::InternalSetDocShellIsActive(bool aIsActive, bool aPreserveLayers)
|
||||
{
|
||||
@ -2712,19 +2729,6 @@ TabChild::InternalSetDocShellIsActive(bool aIsActive, bool aPreserveLayers)
|
||||
docShell->SetIsActive(aIsActive);
|
||||
}
|
||||
|
||||
if (aIsActive) {
|
||||
if (!sActiveTabs) {
|
||||
sActiveTabs = new nsTHashtable<nsPtrHashKey<TabChild>>();
|
||||
}
|
||||
sActiveTabs->PutEntry(this);
|
||||
} else {
|
||||
if (sActiveTabs) {
|
||||
sActiveTabs->RemoveEntry(this);
|
||||
// We don't delete sActiveTabs here when it's empty since that
|
||||
// could cause a lot of churn. Instead, we wait until ~TabChild.
|
||||
}
|
||||
}
|
||||
|
||||
if (aIsActive) {
|
||||
MakeVisible();
|
||||
|
||||
|
@ -580,6 +580,8 @@ public:
|
||||
void MakeVisible();
|
||||
void MakeHidden();
|
||||
|
||||
void OnDocShellActivated(bool aIsActive);
|
||||
|
||||
nsIContentChild* Manager() const { return mManager; }
|
||||
|
||||
static inline TabChild*
|
||||
|
@ -516,7 +516,8 @@
|
||||
gczeal(Number(properties.gczeal));
|
||||
}
|
||||
|
||||
document.write(`<title>${ properties.test }<\/title>`);
|
||||
// Display the test path in the title.
|
||||
document.title = properties.test;
|
||||
|
||||
// Output script tags for shell.js, then browser.js, at each level of the
|
||||
// test path hierarchy.
|
||||
@ -537,10 +538,6 @@
|
||||
scripts.push({src: "js-test-driver-end.js", module: false});
|
||||
|
||||
if (!moduleTest) {
|
||||
// XXX bc - the first document.written script is ignored if the protocol
|
||||
// is file:. insert an empty script tag, to work around it.
|
||||
document.write("<script></script>");
|
||||
|
||||
var key, value;
|
||||
if (properties.language !== "type") {
|
||||
key = "language";
|
||||
|
@ -441,7 +441,7 @@ ProfileEntry::script() const
|
||||
// AutoSuppressProfilerSampling prohibits the runtime's active context from
|
||||
// being changed while it exists.
|
||||
JSContext* cx = script->runtimeFromAnyThread()->activeContext();
|
||||
if (!cx->isProfilerSamplingEnabled())
|
||||
if (!cx || !cx->isProfilerSamplingEnabled())
|
||||
return nullptr;
|
||||
|
||||
MOZ_ASSERT(!IsForwarded(script));
|
||||
|
@ -124,6 +124,15 @@ interface ScheduledGCCallback : nsISupports
|
||||
void callback();
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface for callback to be passed to Cu.blockThreadedExecution.
|
||||
*/
|
||||
[scriptable, function, uuid(c3b85a5c-c328-47d4-aaaf-384c4ff9d77d)]
|
||||
interface nsIBlockThreadedExecutionCallback : nsISupports
|
||||
{
|
||||
void callback();
|
||||
};
|
||||
|
||||
/**
|
||||
* interface of Components.utils
|
||||
*/
|
||||
@ -708,6 +717,21 @@ interface nsIXPCComponents_Utils : nsISupports
|
||||
* same semantics of readFile.
|
||||
*/
|
||||
ACString readURI(in nsIURI url);
|
||||
|
||||
/**
|
||||
* If the main thread is using any kind of fancy cooperative
|
||||
* scheduling (e.g., Quantum DOM scheduling),
|
||||
* blockThreadedExecution disables it temporarily. The
|
||||
* aBlockedCallback is called when it has been completely disabled
|
||||
* and events are back to running sequentially on a single main
|
||||
* thread. Calling unblockThreadedExecution will re-enable thread
|
||||
* scheduling of the main thread. Multiple calls to
|
||||
* blockThreadedExecution will require the same number of calls to
|
||||
* unblockThreadedExecution in order to resume cooperative
|
||||
* scheduling.
|
||||
*/
|
||||
void blockThreadedExecution(in nsIBlockThreadedExecutionCallback aBlockedCallback);
|
||||
void unblockThreadedExecution();
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/StructuredCloneTags.h"
|
||||
#include "mozilla/dom/WindowBinding.h"
|
||||
#include "mozilla/Scheduler.h"
|
||||
#include "nsZipArchive.h"
|
||||
#include "nsIDOMFileList.h"
|
||||
#include "nsWindowMemoryReporter.h"
|
||||
@ -3212,6 +3213,20 @@ nsXPCComponents_Utils::Now(double* aRetval)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::BlockThreadedExecution(nsIBlockThreadedExecutionCallback* aCallback)
|
||||
{
|
||||
Scheduler::BlockThreadedExecution(aCallback);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::UnblockThreadedExecution()
|
||||
{
|
||||
Scheduler::UnblockThreadedExecution();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/***************************************************************************/
|
||||
/***************************************************************************/
|
||||
|
@ -3103,7 +3103,9 @@ nsChildView::GetDocumentAccessible()
|
||||
if (!mozilla::a11y::ShouldA11yBeEnabled())
|
||||
return nullptr;
|
||||
|
||||
if (mAccessible) {
|
||||
// mAccessible might be dead if accessibility was previously disabled and is
|
||||
// now being enabled again.
|
||||
if (mAccessible && mAccessible->IsAlive()) {
|
||||
RefPtr<a11y::Accessible> ret;
|
||||
CallQueryReferent(mAccessible.get(),
|
||||
static_cast<a11y::Accessible**>(getter_AddRefs(ret)));
|
||||
|
@ -31,7 +31,7 @@ CooperativeThreadPool::CooperativeThreadPool(size_t aNumThreads,
|
||||
, mController(aController)
|
||||
, mSelectedThread(size_t(0))
|
||||
{
|
||||
MOZ_ASSERT(aNumThreads < kMaxThreads);
|
||||
MOZ_ASSERT(aNumThreads <= kMaxThreads);
|
||||
|
||||
gCooperativeSchedulingEnabled = true;
|
||||
sTlsCurrentThread.infallibleInit();
|
||||
|
@ -124,8 +124,10 @@ PrioritizedEventQueue<InnerQueueT>::SelectQueue(bool aUpdateState,
|
||||
bool normalPending = !mNormalQueue->IsEmpty(aProofOfLock);
|
||||
size_t inputCount = mInputQueue->Count(aProofOfLock);
|
||||
|
||||
if (mInputQueueState == STATE_ENABLED &&
|
||||
mInputHandlingStartTime.IsNull() && inputCount > 0) {
|
||||
if (aUpdateState &&
|
||||
mInputQueueState == STATE_ENABLED &&
|
||||
mInputHandlingStartTime.IsNull() &&
|
||||
inputCount > 0) {
|
||||
mInputHandlingStartTime =
|
||||
InputEventStatistics::Get()
|
||||
.GetInputHandlingStartTime(inputCount);
|
||||
@ -156,6 +158,7 @@ PrioritizedEventQueue<InnerQueueT>::SelectQueue(bool aUpdateState,
|
||||
queue = EventPriority::High;
|
||||
} else if (inputCount > 0 && (mInputQueueState == STATE_FLUSHING ||
|
||||
(mInputQueueState == STATE_ENABLED &&
|
||||
!mInputHandlingStartTime.IsNull() &&
|
||||
TimeStamp::Now() > mInputHandlingStartTime))) {
|
||||
queue = EventPriority::Input;
|
||||
} else if (normalPending) {
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "nsThreadManager.h"
|
||||
#include "PrioritizedEventQueue.h"
|
||||
#include "xpcpublic.h"
|
||||
#include "xpccomponents.h"
|
||||
|
||||
// Windows silliness. winbase.h defines an empty no-argument Yield macro.
|
||||
#undef Yield
|
||||
@ -93,6 +94,7 @@ public:
|
||||
explicit SchedulerImpl(SchedulerEventQueue* aQueue);
|
||||
|
||||
void Start();
|
||||
void Stop(already_AddRefed<nsIRunnable> aStoppedCallback);
|
||||
void Shutdown();
|
||||
|
||||
void Dispatch(already_AddRefed<nsIRunnable> aEvent);
|
||||
@ -118,6 +120,9 @@ public:
|
||||
static bool UnlabeledEventRunning() { return sUnlabeledEventRunning; }
|
||||
static bool AnyEventRunning() { return sNumThreadsRunning > 0; }
|
||||
|
||||
void BlockThreadedExecution(nsIBlockThreadedExecutionCallback* aCallback);
|
||||
void UnblockThreadedExecution();
|
||||
|
||||
CooperativeThreadPool::Resource* GetQueueResource() { return &mQueueResource; }
|
||||
bool UseCooperativeScheduling() const { return mQueue->UseCooperativeScheduling(); }
|
||||
|
||||
@ -143,6 +148,9 @@ private:
|
||||
|
||||
bool mShuttingDown;
|
||||
|
||||
// Runnable to call when the scheduler has finished shutting down.
|
||||
nsTArray<nsCOMPtr<nsIRunnable>> mShutdownCallbacks;
|
||||
|
||||
UniquePtr<CooperativeThreadPool> mThreadPool;
|
||||
|
||||
RefPtr<SchedulerEventQueue> mQueue;
|
||||
@ -202,6 +210,11 @@ private:
|
||||
static size_t sNumThreadsRunning;
|
||||
static bool sUnlabeledEventRunning;
|
||||
|
||||
// Number of times that BlockThreadedExecution has been called without
|
||||
// corresponding calls to UnblockThreadedExecution. If this is non-zero,
|
||||
// scheduling is disabled.
|
||||
size_t mNumSchedulerBlocks = 0;
|
||||
|
||||
JSContext* mContexts[CooperativeThreadPool::kMaxThreads];
|
||||
};
|
||||
|
||||
@ -443,6 +456,8 @@ SchedulerImpl::SwitcherThread(void* aData)
|
||||
void
|
||||
SchedulerImpl::Start()
|
||||
{
|
||||
MOZ_ASSERT(mNumSchedulerBlocks == 0);
|
||||
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction("Scheduler::Start", [this]() -> void {
|
||||
// Let's pretend the runnable here isn't actually running.
|
||||
MOZ_ASSERT(sUnlabeledEventRunning);
|
||||
@ -492,16 +507,40 @@ SchedulerImpl::Start()
|
||||
MOZ_ASSERT(sNumThreadsRunning == 0);
|
||||
sNumThreadsRunning = 1;
|
||||
|
||||
// Delete the SchedulerImpl. Don't use it after this point.
|
||||
Scheduler::sScheduler = nullptr;
|
||||
mShuttingDown = false;
|
||||
nsTArray<nsCOMPtr<nsIRunnable>> callbacks = Move(mShutdownCallbacks);
|
||||
for (nsIRunnable* runnable : callbacks) {
|
||||
runnable->Run();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
void
|
||||
SchedulerImpl::Stop(already_AddRefed<nsIRunnable> aStoppedCallback)
|
||||
{
|
||||
MOZ_ASSERT(mNumSchedulerBlocks > 0);
|
||||
|
||||
// Note that this may be called when mShuttingDown is already true. We still
|
||||
// want to invoke the callback in that case.
|
||||
|
||||
MutexAutoLock lock(mLock);
|
||||
mShuttingDown = true;
|
||||
mShutdownCallbacks.AppendElement(aStoppedCallback);
|
||||
mShutdownCondVar.Notify();
|
||||
}
|
||||
|
||||
void
|
||||
SchedulerImpl::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(mNumSchedulerBlocks == 0);
|
||||
|
||||
MutexAutoLock lock(mLock);
|
||||
mShuttingDown = true;
|
||||
|
||||
// Delete the SchedulerImpl once shutdown is complete.
|
||||
mShutdownCallbacks.AppendElement(NS_NewRunnableFunction("SchedulerImpl::Shutdown",
|
||||
[] { Scheduler::sScheduler = nullptr; }));
|
||||
|
||||
mShutdownCondVar.Notify();
|
||||
}
|
||||
|
||||
@ -519,7 +558,9 @@ SchedulerImpl::SystemZoneResource::IsAvailable(const MutexAutoLock& aProofOfLock
|
||||
{
|
||||
mScheduler->mLock.AssertCurrentThreadOwns();
|
||||
|
||||
JSContext* cx = dom::danger::GetJSContext();
|
||||
// It doesn't matter which context we pick; we really just some main-thread
|
||||
// JSContext.
|
||||
JSContext* cx = mScheduler->mContexts[0];
|
||||
return js::SystemZoneAvailable(cx);
|
||||
}
|
||||
|
||||
@ -681,6 +722,27 @@ SchedulerImpl::Yield()
|
||||
CooperativeThreadPool::Yield(nullptr, lock);
|
||||
}
|
||||
|
||||
void
|
||||
SchedulerImpl::BlockThreadedExecution(nsIBlockThreadedExecutionCallback* aCallback)
|
||||
{
|
||||
if (mNumSchedulerBlocks++ == 0 || mShuttingDown) {
|
||||
Stop(NewRunnableMethod("BlockThreadedExecution", aCallback,
|
||||
&nsIBlockThreadedExecutionCallback::Callback));
|
||||
} else {
|
||||
// The scheduler is already blocked.
|
||||
nsCOMPtr<nsIBlockThreadedExecutionCallback> kungFuDeathGrip(aCallback);
|
||||
aCallback->Callback();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SchedulerImpl::UnblockThreadedExecution()
|
||||
{
|
||||
if (--mNumSchedulerBlocks == 0) {
|
||||
Start();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<nsThread>
|
||||
Scheduler::Init(nsIIdlePeriod* aIdlePeriod)
|
||||
{
|
||||
@ -789,3 +851,25 @@ Scheduler::AnyEventRunning()
|
||||
{
|
||||
return SchedulerImpl::AnyEventRunning();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
Scheduler::BlockThreadedExecution(nsIBlockThreadedExecutionCallback* aCallback)
|
||||
{
|
||||
if (!sScheduler) {
|
||||
nsCOMPtr<nsIBlockThreadedExecutionCallback> kungFuDeathGrip(aCallback);
|
||||
aCallback->Callback();
|
||||
return;
|
||||
}
|
||||
|
||||
sScheduler->BlockThreadedExecution(aCallback);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
Scheduler::UnblockThreadedExecution()
|
||||
{
|
||||
if (!sScheduler) {
|
||||
return;
|
||||
}
|
||||
|
||||
sScheduler->UnblockThreadedExecution();
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
// Windows silliness. winbase.h defines an empty no-argument Yield macro.
|
||||
#undef Yield
|
||||
|
||||
class nsIBlockThreadedExecutionCallback;
|
||||
class nsIIdlePeriod;
|
||||
class nsThread;
|
||||
|
||||
@ -74,6 +75,9 @@ public:
|
||||
static bool UnlabeledEventRunning();
|
||||
static bool AnyEventRunning();
|
||||
|
||||
static void BlockThreadedExecution(nsIBlockThreadedExecutionCallback* aCallback);
|
||||
static void UnblockThreadedExecution();
|
||||
|
||||
class MOZ_RAII EventLoopActivation
|
||||
{
|
||||
public:
|
||||
|
Loading…
Reference in New Issue
Block a user