mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to b2ginbound a=merge CLOSED TREE
This commit is contained in:
commit
64d28ca0a8
@ -249,6 +249,9 @@ endif # MOZ_CRASHREPORTER
|
||||
|
||||
uploadsymbols:
|
||||
ifdef MOZ_CRASHREPORTER
|
||||
ifdef SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE
|
||||
$(PYTHON) $(topsrcdir)/toolkit/crashreporter/tools/upload_symbols.py '$(DIST)/$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip'
|
||||
endif
|
||||
$(SHELL) $(topsrcdir)/toolkit/crashreporter/tools/upload_symbols.sh $(SYMBOL_INDEX_NAME) '$(DIST)/$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip'
|
||||
endif
|
||||
|
||||
|
@ -540,6 +540,17 @@ static nsRoleMapEntry sWAIRoleMaps[] =
|
||||
kGenericAccType,
|
||||
kNoReqStates
|
||||
},
|
||||
{ // switch
|
||||
&nsGkAtoms::_switch,
|
||||
roles::SWITCH,
|
||||
kUseMapRole,
|
||||
eNoValue,
|
||||
eCheckUncheckAction,
|
||||
eNoLiveAttr,
|
||||
kGenericAccType,
|
||||
kNoReqStates,
|
||||
eARIACheckableBool
|
||||
},
|
||||
{ // tab
|
||||
&nsGkAtoms::tab,
|
||||
roles::PAGETAB,
|
||||
|
@ -139,7 +139,7 @@ AccTextSelChangeEvent::~AccTextSelChangeEvent() { }
|
||||
bool
|
||||
AccTextSelChangeEvent::IsCaretMoveOnly() const
|
||||
{
|
||||
return mSel->GetRangeCount() == 1 && mSel->IsCollapsed() &&
|
||||
return mSel->RangeCount() == 1 && mSel->IsCollapsed() &&
|
||||
((mReason & (nsISelectionListener::COLLAPSETOSTART_REASON |
|
||||
nsISelectionListener::COLLAPSETOEND_REASON)) == 0);
|
||||
}
|
||||
|
@ -785,7 +785,12 @@ enum Role {
|
||||
*/
|
||||
KEY = 129,
|
||||
|
||||
LAST_ROLE = KEY
|
||||
/**
|
||||
* Represent a switch control widget (ARIA role "switch").
|
||||
*/
|
||||
SWITCH = 130,
|
||||
|
||||
LAST_ROLE = SWITCH
|
||||
};
|
||||
|
||||
} // namespace role
|
||||
|
@ -1055,3 +1055,11 @@ ROLE(KEY,
|
||||
ROLE_SYSTEM_PUSHBUTTON,
|
||||
ROLE_SYSTEM_PUSHBUTTON,
|
||||
eNameFromSubtreeRule)
|
||||
|
||||
ROLE(SWITCH,
|
||||
"switch",
|
||||
ATK_ROLE_TOGGLE_BUTTON,
|
||||
NSAccessibilityCheckBoxRole,
|
||||
ROLE_SYSTEM_CHECKBUTTON,
|
||||
IA2_ROLE_TOGGLE_BUTTON,
|
||||
eNameFromSubtreeRule)
|
||||
|
@ -49,7 +49,7 @@ HyperTextAccessible::AddToSelection(int32_t aStartOffset, int32_t aEndOffset)
|
||||
{
|
||||
dom::Selection* domSel = DOMSelection();
|
||||
return domSel &&
|
||||
SetSelectionBoundsAt(domSel->GetRangeCount(), aStartOffset, aEndOffset);
|
||||
SetSelectionBoundsAt(domSel->RangeCount(), aStartOffset, aEndOffset);
|
||||
}
|
||||
|
||||
inline void
|
||||
|
@ -1169,7 +1169,7 @@ HyperTextAccessible::SetSelectionRange(int32_t aStartPos, int32_t aEndPos)
|
||||
NS_ENSURE_STATE(domSel);
|
||||
|
||||
// Set up the selection.
|
||||
for (int32_t idx = domSel->GetRangeCount() - 1; idx > 0; idx--)
|
||||
for (int32_t idx = domSel->RangeCount() - 1; idx > 0; idx--)
|
||||
domSel->RemoveRange(domSel->GetRangeAt(idx));
|
||||
SetSelectionBoundsAt(0, aStartPos, aEndPos);
|
||||
|
||||
@ -1474,7 +1474,7 @@ HyperTextAccessible::SetSelectionBoundsAt(int32_t aSelectionNum,
|
||||
return false;
|
||||
|
||||
nsRefPtr<nsRange> range;
|
||||
uint32_t rangeCount = domSel->GetRangeCount();
|
||||
uint32_t rangeCount = domSel->RangeCount();
|
||||
if (aSelectionNum == static_cast<int32_t>(rangeCount))
|
||||
range = new nsRange(mContent);
|
||||
else
|
||||
@ -1502,7 +1502,7 @@ HyperTextAccessible::RemoveFromSelection(int32_t aSelectionNum)
|
||||
if (!domSel)
|
||||
return false;
|
||||
|
||||
if (aSelectionNum < 0 || aSelectionNum >= domSel->GetRangeCount())
|
||||
if (aSelectionNum < 0 || aSelectionNum >= static_cast<int32_t>(domSel->RangeCount()))
|
||||
return false;
|
||||
|
||||
domSel->RemoveRange(domSel->GetRangeAt(aSelectionNum));
|
||||
@ -1948,7 +1948,7 @@ HyperTextAccessible::GetSpellTextAttr(nsINode* aNode,
|
||||
if (!domSel)
|
||||
return;
|
||||
|
||||
int32_t rangeCount = domSel->GetRangeCount();
|
||||
int32_t rangeCount = domSel->RangeCount();
|
||||
if (rangeCount <= 0)
|
||||
return;
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
/**
|
||||
* Defines cross platform (Gecko) roles.
|
||||
*/
|
||||
[scriptable, uuid(50db5e86-9a45-4637-a5c3-4ff148c33270)]
|
||||
[scriptable, uuid(76ce835f-ef86-47c0-ac7b-e871417f1b6e)]
|
||||
interface nsIAccessibleRole : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -778,4 +778,9 @@ interface nsIAccessibleRole : nsISupports
|
||||
* A keyboard or keypad key.
|
||||
*/
|
||||
const unsigned long ROLE_KEY = 129;
|
||||
|
||||
/**
|
||||
* A switch control widget.
|
||||
*/
|
||||
const unsigned long ROLE_SWITCH = 130;
|
||||
};
|
||||
|
@ -475,6 +475,9 @@ GetClosestInterestingAccessible(id anObject)
|
||||
case roles::DEFINITION:
|
||||
return @"AXDefinition";
|
||||
|
||||
case roles::SWITCH:
|
||||
return @"AXSwitch";
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -85,6 +85,16 @@
|
||||
actionName: "select",
|
||||
events: CLICK_EVENTS
|
||||
},
|
||||
{
|
||||
ID: "switch_unchecked",
|
||||
actionName: "check",
|
||||
events: CLICK_EVENTS
|
||||
},
|
||||
{
|
||||
ID: "switch_checked",
|
||||
actionName: "uncheck",
|
||||
events: CLICK_EVENTS
|
||||
},
|
||||
{
|
||||
ID: "tab",
|
||||
actionName: "switch",
|
||||
@ -166,6 +176,10 @@
|
||||
<div id="radio" role="radio">Radio</div>
|
||||
</div>
|
||||
|
||||
<div id="switch_unchecked" role="switch">Switch</div>
|
||||
|
||||
<div id="switch_checked" role="switch" aria-checked="true">Switch</div>
|
||||
|
||||
<div role="tablist">
|
||||
<div id="tab" role="tab">Tab</div>
|
||||
</div>
|
||||
|
@ -107,17 +107,28 @@
|
||||
new setAttrOfMixedType(aID, "aria-checked", STATE_CHECKED, aValue);
|
||||
}
|
||||
|
||||
function buildQueueForAttr(aList, aQueue, aID, aInvokerFunc)
|
||||
{
|
||||
for (var i = 0; i < aList.length; i++) {
|
||||
for (var j = i + 1; j < aList.length; j++) {
|
||||
// XXX: changes from/to "undefined"/"" shouldn't fire state change
|
||||
// events, bug 472142.
|
||||
aQueue.push(new aInvokerFunc(aID, aList[i]));
|
||||
aQueue.push(new aInvokerFunc(aID, aList[j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function buildQueueForAttrOfMixedType(aQueue, aID, aInvokerFunc)
|
||||
{
|
||||
var list = [ "", "undefined", "false", "true", "mixed" ];
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
for (var j = i + 1; j < list.length; j++) {
|
||||
// XXX: changes from/to "undefined"/"" shouldn't fire state change
|
||||
// events, bug 472142.
|
||||
aQueue.push(new aInvokerFunc(aID, list[i]));
|
||||
aQueue.push(new aInvokerFunc(aID, list[j]));
|
||||
}
|
||||
}
|
||||
buildQueueForAttr(list, aQueue, aID, aInvokerFunc);
|
||||
}
|
||||
|
||||
function buildQueueForAttrOfBoolType(aQueue, aID, aInvokerFunc)
|
||||
{
|
||||
var list = [ "", "undefined", "false", "true" ];
|
||||
buildQueueForAttr(list, aQueue, aID, aInvokerFunc);
|
||||
}
|
||||
|
||||
function doTests()
|
||||
@ -135,6 +146,7 @@
|
||||
buildQueueForAttrOfMixedType(gQueue, "pressable", setPressed);
|
||||
buildQueueForAttrOfMixedType(gQueue, "pressable_native", setPressed);
|
||||
buildQueueForAttrOfMixedType(gQueue, "checkable", setChecked);
|
||||
buildQueueForAttrOfBoolType(gQueue, "checkableBool", setChecked);
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
@ -166,6 +178,11 @@
|
||||
title="Pressed state is not exposed on a button element with aria-pressed attribute"
|
||||
Mozilla Bug 989958
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1136563"
|
||||
title="Support ARIA 1.1 switch role"
|
||||
Mozilla Bug 1136563
|
||||
</a>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
@ -186,5 +203,6 @@
|
||||
|
||||
<!-- aria-checked -->
|
||||
<div id="checkable" role="checkbox"></div>
|
||||
<div id="checkableBool" role="switch"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -70,6 +70,7 @@ const ROLE_SLIDER = nsIAccessibleRole.ROLE_SLIDER;
|
||||
const ROLE_SPINBUTTON = nsIAccessibleRole.ROLE_SPINBUTTON;
|
||||
const ROLE_STATICTEXT = nsIAccessibleRole.ROLE_STATICTEXT;
|
||||
const ROLE_STATUSBAR = nsIAccessibleRole.ROLE_STATUSBAR;
|
||||
const ROLE_SWITCH = nsIAccessibleRole.ROLE_SWITCH;
|
||||
const ROLE_TABLE = nsIAccessibleRole.ROLE_TABLE;
|
||||
const ROLE_TERM = nsIAccessibleRole.ROLE_TERM;
|
||||
const ROLE_TEXT_CONTAINER = nsIAccessibleRole.ROLE_TEXT_CONTAINER;
|
||||
|
@ -60,6 +60,7 @@
|
||||
testRole("aria_slider", ROLE_SLIDER);
|
||||
testRole("aria_spinbutton", ROLE_SPINBUTTON);
|
||||
testRole("aria_status", ROLE_STATUSBAR);
|
||||
testRole("aria_switch", ROLE_SWITCH);
|
||||
testRole("aria_tab", ROLE_PAGETAB);
|
||||
testRole("aria_tablist", ROLE_PAGETABLIST);
|
||||
testRole("aria_tabpanel", ROLE_PROPERTYPAGE);
|
||||
@ -178,6 +179,11 @@
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=735645">
|
||||
Bug 735645
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1136563"
|
||||
title="Support ARIA 1.1 switch role"
|
||||
Bug 1136563
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
@ -225,6 +231,7 @@
|
||||
<span id="aria_slider" role="slider"/>
|
||||
<span id="aria_spinbutton" role="spinbutton"/>
|
||||
<span id="aria_status" role="status"/>
|
||||
<span id="aria_switch" role="switch"/>
|
||||
<span id="aria_tab" role="tab"/>
|
||||
<span id="aria_tablist" role="tablist"/>
|
||||
<span id="aria_tabpanel" role="tabpanel"/>
|
||||
|
@ -92,6 +92,8 @@
|
||||
// aria-checked
|
||||
testStates("aria_checked_checkbox", STATE_CHECKED);
|
||||
testStates("aria_mixed_checkbox", STATE_MIXED);
|
||||
testStates("aria_checked_switch", STATE_CHECKED);
|
||||
testStates("aria_mixed_switch", 0, 0, STATE_MIXED); // unsupported
|
||||
|
||||
// test disabled group and all its descendants to see if they are
|
||||
// disabled, too. See bug 429285.
|
||||
@ -350,6 +352,11 @@
|
||||
title="Pressed state is not exposed on a button element with aria-pressed attribute"
|
||||
Mozilla Bug 989958
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1136563"
|
||||
title="Support ARIA 1.1 switch role"
|
||||
Mozilla Bug 1136563
|
||||
</a>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
@ -383,6 +390,15 @@
|
||||
<div id="aria_mixed_checkbox" role="checkbox" aria-checked="mixed">
|
||||
I might agree
|
||||
</div>
|
||||
|
||||
<div id="aria_checked_switch" role="switch" aria-checked="true">
|
||||
I am switched on
|
||||
</div>
|
||||
|
||||
<div id="aria_mixed_switch" role="switch" aria-checked="mixed">
|
||||
I am unsupported
|
||||
</div>
|
||||
|
||||
<div id="aria_modal" aria-modal="true">modal stuff</div>
|
||||
<div id="aria_modal_false" aria-modal="false">non modal stuff</div>div>
|
||||
<div id="aria_multiline_textbox" role="textbox" aria-multiline="true"></div>
|
||||
|
@ -537,6 +537,8 @@
|
||||
@BINPATH@/components/formautofill.manifest
|
||||
@BINPATH@/components/FormAutofillContentService.js
|
||||
@BINPATH@/components/FormAutofillStartup.js
|
||||
@BINPATH@/components/CSSUnprefixingService.js
|
||||
@BINPATH@/components/CSSUnprefixingService.manifest
|
||||
@BINPATH@/components/contentAreaDropListener.manifest
|
||||
@BINPATH@/components/contentAreaDropListener.js
|
||||
@BINPATH@/components/messageWakeupService.js
|
||||
|
@ -1577,6 +1577,9 @@ pref("devtools.browserconsole.filter.secwarn", true);
|
||||
// Text size in the Web Console. Use 0 for the system default size.
|
||||
pref("devtools.webconsole.fontSize", 0);
|
||||
|
||||
// Max number of inputs to store in web console history.
|
||||
pref("devtools.webconsole.inputHistoryCount", 50);
|
||||
|
||||
// Persistent logging: |true| if you want the Web Console to keep all of the
|
||||
// logged messages after reloading the page, |false| if you want the output to
|
||||
// be cleared each time page navigation happens.
|
||||
|
@ -11,13 +11,19 @@ XPCOMUtils.defineLazyModuleGetter(this, "CloudSync",
|
||||
let CloudSync = null;
|
||||
#endif
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ReadingListScheduler",
|
||||
"resource:///modules/readinglist/Scheduler.jsm");
|
||||
|
||||
// gSyncUI handles updating the tools menu and displaying notifications.
|
||||
let gSyncUI = {
|
||||
_obs: ["weave:service:sync:start",
|
||||
"weave:service:sync:finish",
|
||||
"weave:service:sync:error",
|
||||
"weave:service:quota:remaining",
|
||||
"weave:service:setup-complete",
|
||||
"weave:service:login:start",
|
||||
"weave:service:login:finish",
|
||||
"weave:service:login:error",
|
||||
"weave:service:logout:finish",
|
||||
"weave:service:start-over",
|
||||
"weave:service:start-over:finish",
|
||||
@ -25,9 +31,15 @@ let gSyncUI = {
|
||||
"weave:ui:sync:error",
|
||||
"weave:ui:sync:finish",
|
||||
"weave:ui:clear-error",
|
||||
|
||||
"readinglist:sync:start",
|
||||
"readinglist:sync:finish",
|
||||
"readinglist:sync:error",
|
||||
],
|
||||
|
||||
_unloaded: false,
|
||||
// The number of "active" syncs - while this is non-zero, our button will spin
|
||||
_numActiveSyncTasks: 0,
|
||||
|
||||
init: function () {
|
||||
Cu.import("resource://services-common/stringbundle.js");
|
||||
@ -95,21 +107,25 @@ let gSyncUI = {
|
||||
}
|
||||
},
|
||||
|
||||
_needsSetup: function SUI__needsSetup() {
|
||||
_needsSetup() {
|
||||
// We want to treat "account needs verification" as "needs setup". So
|
||||
// "reach in" to Weave.Status._authManager to check whether we the signed-in
|
||||
// user is verified.
|
||||
// Referencing Weave.Status spins a nested event loop to initialize the
|
||||
// authManager, so this should always return a value directly.
|
||||
// This only applies to fxAccounts-based Sync.
|
||||
if (Weave.Status._authManager._signedInUser) {
|
||||
// If we have a signed in user already, and that user is not verified,
|
||||
// revert to the "needs setup" state.
|
||||
if (!Weave.Status._authManager._signedInUser.verified) {
|
||||
return true;
|
||||
}
|
||||
if (Weave.Status._authManager._signedInUser !== undefined) {
|
||||
// So we are using Firefox accounts - in this world, checking Sync isn't
|
||||
// enough as reading list may be configured but not Sync.
|
||||
// We consider ourselves setup if we have a verified user.
|
||||
// XXX - later we should consider checking preferences to ensure at least
|
||||
// one engine is enabled?
|
||||
return !Weave.Status._authManager._signedInUser ||
|
||||
!Weave.Status._authManager._signedInUser.verified;
|
||||
}
|
||||
|
||||
// So we are using legacy sync, and reading-list isn't supported for such
|
||||
// users, so check sync itself.
|
||||
let firstSync = "";
|
||||
try {
|
||||
firstSync = Services.prefs.getCharPref("services.sync.firstSync");
|
||||
@ -120,7 +136,8 @@ let gSyncUI = {
|
||||
},
|
||||
|
||||
_loginFailed: function () {
|
||||
return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED;
|
||||
return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED ||
|
||||
ReadingListScheduler.state == ReadingListScheduler.STATE_ERROR_AUTHENTICATION;
|
||||
},
|
||||
|
||||
updateUI: function SUI_updateUI() {
|
||||
@ -136,6 +153,7 @@ let gSyncUI = {
|
||||
document.getElementById("sync-syncnow-state").hidden = false;
|
||||
} else if (loginFailed) {
|
||||
document.getElementById("sync-reauth-state").hidden = false;
|
||||
this.showLoginError();
|
||||
} else if (needsSetup) {
|
||||
document.getElementById("sync-setup-state").hidden = false;
|
||||
} else {
|
||||
@ -146,14 +164,6 @@ let gSyncUI = {
|
||||
return;
|
||||
|
||||
let syncButton = document.getElementById("sync-button");
|
||||
if (syncButton) {
|
||||
syncButton.removeAttribute("status");
|
||||
}
|
||||
let panelHorizontalButton = document.getElementById("PanelUI-fxa-status");
|
||||
if (panelHorizontalButton) {
|
||||
panelHorizontalButton.removeAttribute("syncstatus");
|
||||
}
|
||||
|
||||
if (needsSetup && syncButton)
|
||||
syncButton.removeAttribute("tooltiptext");
|
||||
|
||||
@ -162,17 +172,45 @@ let gSyncUI = {
|
||||
|
||||
|
||||
// Functions called by observers
|
||||
onActivityStart: function SUI_onActivityStart() {
|
||||
onActivityStart() {
|
||||
if (!gBrowser)
|
||||
return;
|
||||
|
||||
let button = document.getElementById("sync-button");
|
||||
if (button) {
|
||||
button.setAttribute("status", "active");
|
||||
this.log.debug("onActivityStart with numActive", this._numActiveSyncTasks);
|
||||
if (++this._numActiveSyncTasks == 1) {
|
||||
let button = document.getElementById("sync-button");
|
||||
if (button) {
|
||||
button.setAttribute("status", "active");
|
||||
}
|
||||
button = document.getElementById("PanelUI-fxa-status");
|
||||
if (button) {
|
||||
button.setAttribute("syncstatus", "active");
|
||||
}
|
||||
}
|
||||
button = document.getElementById("PanelUI-fxa-status");
|
||||
if (button) {
|
||||
button.setAttribute("syncstatus", "active");
|
||||
},
|
||||
|
||||
onActivityStop() {
|
||||
if (!gBrowser)
|
||||
return;
|
||||
this.log.debug("onActivityStop with numActive", this._numActiveSyncTasks);
|
||||
if (--this._numActiveSyncTasks) {
|
||||
if (this._numActiveSyncTasks < 0) {
|
||||
// This isn't particularly useful (it seems more likely we'll set a
|
||||
// "start" without a "stop" meaning it forever remains > 0) but it
|
||||
// might offer some value...
|
||||
this.log.error("mismatched onActivityStart/Stop calls",
|
||||
new Error("active=" + this._numActiveSyncTasks));
|
||||
}
|
||||
return; // active tasks are still ongoing...
|
||||
}
|
||||
|
||||
let syncButton = document.getElementById("sync-button");
|
||||
if (syncButton) {
|
||||
syncButton.removeAttribute("status");
|
||||
}
|
||||
let panelHorizontalButton = document.getElementById("PanelUI-fxa-status");
|
||||
if (panelHorizontalButton) {
|
||||
panelHorizontalButton.removeAttribute("syncstatus");
|
||||
}
|
||||
},
|
||||
|
||||
@ -187,6 +225,7 @@ let gSyncUI = {
|
||||
},
|
||||
|
||||
onLoginError: function SUI_onLoginError() {
|
||||
// Note: This is used for *both* Sync and ReadingList login errors.
|
||||
// if login fails, any other notifications are essentially moot
|
||||
Weave.Notifications.removeAll();
|
||||
|
||||
@ -200,11 +239,18 @@ let gSyncUI = {
|
||||
this.updateUI();
|
||||
return;
|
||||
}
|
||||
this.showLoginError();
|
||||
this.updateUI();
|
||||
},
|
||||
|
||||
showLoginError() {
|
||||
// Note: This is used for *both* Sync and ReadingList login errors.
|
||||
let title = this._stringBundle.GetStringFromName("error.login.title");
|
||||
|
||||
let description;
|
||||
if (Weave.Status.sync == Weave.PROLONGED_SYNC_FAILURE) {
|
||||
if (Weave.Status.sync == Weave.PROLONGED_SYNC_FAILURE ||
|
||||
this.isProlongedReadingListError()) {
|
||||
this.log.debug("showLoginError has a prolonged login error");
|
||||
// Convert to days
|
||||
let lastSync =
|
||||
Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout") / 86400;
|
||||
@ -214,6 +260,7 @@ let gSyncUI = {
|
||||
let reason = Weave.Utils.getErrorString(Weave.Status.login);
|
||||
description =
|
||||
this._stringBundle.formatStringFromName("error.sync.description", [reason], 1);
|
||||
this.log.debug("showLoginError has a non-prolonged error", reason);
|
||||
}
|
||||
|
||||
let buttons = [];
|
||||
@ -226,7 +273,6 @@ let gSyncUI = {
|
||||
let notification = new Weave.Notification(title, description, null,
|
||||
Weave.Notifications.PRIORITY_WARNING, buttons);
|
||||
Weave.Notifications.replaceTitle(notification);
|
||||
this.updateUI();
|
||||
},
|
||||
|
||||
onLogout: function SUI_onLogout() {
|
||||
@ -271,6 +317,7 @@ let gSyncUI = {
|
||||
}
|
||||
|
||||
Services.obs.notifyObservers(null, "cloudsync:user-sync", null);
|
||||
Services.obs.notifyObservers(null, "readinglist:user-sync", null);
|
||||
},
|
||||
|
||||
handleToolbarButton: function SUI_handleStatusbarButton() {
|
||||
@ -367,7 +414,15 @@ let gSyncUI = {
|
||||
|
||||
let lastSync;
|
||||
try {
|
||||
lastSync = Services.prefs.getCharPref("services.sync.lastSync");
|
||||
lastSync = new Date(Services.prefs.getCharPref("services.sync.lastSync"));
|
||||
}
|
||||
catch (e) { };
|
||||
// and reading-list time - we want whatever one is the most recent.
|
||||
try {
|
||||
let lastRLSync = new Date(Services.prefs.getCharPref("readinglist.scheduler.lastSync"));
|
||||
if (!lastSync || lastRLSync > lastSync) {
|
||||
lastSync = lastRLSync;
|
||||
}
|
||||
}
|
||||
catch (e) { };
|
||||
if (!lastSync || this._needsSetup()) {
|
||||
@ -376,9 +431,9 @@ let gSyncUI = {
|
||||
}
|
||||
|
||||
// Show the day-of-week and time (HH:MM) of last sync
|
||||
let lastSyncDate = new Date(lastSync).toLocaleFormat("%a %H:%M");
|
||||
let lastSyncDateString = lastSync.toLocaleFormat("%a %H:%M");
|
||||
let lastSyncLabel =
|
||||
this._stringBundle.formatStringFromName("lastSync2.label", [lastSyncDate], 1);
|
||||
this._stringBundle.formatStringFromName("lastSync2.label", [lastSyncDateString], 1);
|
||||
|
||||
syncButton.setAttribute("tooltiptext", lastSyncLabel);
|
||||
},
|
||||
@ -395,7 +450,69 @@ let gSyncUI = {
|
||||
this.clearError(title);
|
||||
},
|
||||
|
||||
// Return true if the reading-list is in a "prolonged" error state. That
|
||||
// engine doesn't impose what that means, so calculate it here. For
|
||||
// consistency, we just use the sync prefs.
|
||||
isProlongedReadingListError() {
|
||||
let lastSync, threshold, prolonged;
|
||||
try {
|
||||
lastSync = new Date(Services.prefs.getCharPref("readinglist.scheduler.lastSync"));
|
||||
threshold = new Date(Date.now() - Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout"));
|
||||
prolonged = lastSync <= threshold;
|
||||
} catch (ex) {
|
||||
// no pref, assume not prolonged.
|
||||
prolonged = false;
|
||||
}
|
||||
this.log.debug("isProlongedReadingListError has last successful sync at ${lastSync}, threshold is ${threshold}, prolonged=${prolonged}",
|
||||
{lastSync, threshold, prolonged});
|
||||
return prolonged;
|
||||
},
|
||||
|
||||
onRLSyncError() {
|
||||
// Like onSyncError, but from the reading-list engine.
|
||||
// However, the current UX around Sync is that error notifications should
|
||||
// generally *not* be seen as they typically aren't actionable - so only
|
||||
// authentication errors (which require user action) and "prolonged" errors
|
||||
// (which technically aren't actionable, but user really should know anyway)
|
||||
// are shown.
|
||||
this.log.debug("onRLSyncError with readingList state", ReadingListScheduler.state);
|
||||
if (ReadingListScheduler.state == ReadingListScheduler.STATE_ERROR_AUTHENTICATION) {
|
||||
this.onLoginError();
|
||||
return;
|
||||
}
|
||||
// If it's not prolonged there's nothing to do.
|
||||
if (!this.isProlongedReadingListError()) {
|
||||
this.log.debug("onRLSyncError has a non-authentication, non-prolonged error, so not showing any error UI");
|
||||
return;
|
||||
}
|
||||
// So it's a prolonged error.
|
||||
// Unfortunate duplication from below...
|
||||
this.log.debug("onRLSyncError has a prolonged error");
|
||||
let title = this._stringBundle.GetStringFromName("error.sync.title");
|
||||
// XXX - this is somewhat wrong - we are reporting the threshold we consider
|
||||
// to be prolonged, not how long it actually has been. (ie, lastSync below
|
||||
// is effectively constant) - bit it too is copied from below.
|
||||
let lastSync =
|
||||
Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout") / 86400;
|
||||
let description =
|
||||
this._stringBundle.formatStringFromName("error.sync.prolonged_failure", [lastSync], 1);
|
||||
let priority = Weave.Notifications.PRIORITY_INFO;
|
||||
let buttons = [
|
||||
new Weave.NotificationButton(
|
||||
this._stringBundle.GetStringFromName("error.sync.tryAgainButton.label"),
|
||||
this._stringBundle.GetStringFromName("error.sync.tryAgainButton.accesskey"),
|
||||
function() { gSyncUI.doSync(); return true; }
|
||||
),
|
||||
];
|
||||
let notification =
|
||||
new Weave.Notification(title, description, null, priority, buttons);
|
||||
Weave.Notifications.replaceTitle(notification);
|
||||
|
||||
this.updateUI();
|
||||
},
|
||||
|
||||
onSyncError: function SUI_onSyncError() {
|
||||
this.log.debug("onSyncError");
|
||||
let title = this._stringBundle.GetStringFromName("error.sync.title");
|
||||
|
||||
if (Weave.Status.login != Weave.LOGIN_SUCCEEDED) {
|
||||
@ -418,7 +535,9 @@ let gSyncUI = {
|
||||
let priority = Weave.Notifications.PRIORITY_WARNING;
|
||||
let buttons = [];
|
||||
|
||||
// Check if the client is outdated in some way
|
||||
// Check if the client is outdated in some way (but note: we've never in the
|
||||
// past, and probably never will, bump the relevent version numbers, so
|
||||
// this is effectively dead code!)
|
||||
let outdated = Weave.Status.sync == Weave.VERSION_OUT_OF_DATE;
|
||||
for (let [engine, reason] in Iterator(Weave.Status.engines))
|
||||
outdated = outdated || reason == Weave.VERSION_OUT_OF_DATE;
|
||||
@ -468,6 +587,7 @@ let gSyncUI = {
|
||||
},
|
||||
|
||||
observe: function SUI_observe(subject, topic, data) {
|
||||
this.log.debug("observed", topic);
|
||||
if (this._unloaded) {
|
||||
Cu.reportError("SyncUI observer called after unload: " + topic);
|
||||
return;
|
||||
@ -480,10 +600,26 @@ let gSyncUI = {
|
||||
subject = subject.wrappedJSObject.object;
|
||||
}
|
||||
|
||||
// First handle "activity" only.
|
||||
switch (topic) {
|
||||
case "weave:service:sync:start":
|
||||
case "weave:service:login:start":
|
||||
case "readinglist:sync:start":
|
||||
this.onActivityStart();
|
||||
break;
|
||||
case "weave:service:sync:finish":
|
||||
case "weave:service:sync:error":
|
||||
case "weave:service:login:finish":
|
||||
case "weave:service:login:error":
|
||||
case "readinglist:sync:finish":
|
||||
case "readinglist:sync:error":
|
||||
this.onActivityStop();
|
||||
break;
|
||||
}
|
||||
// Now non-activity state (eg, enabled, errors, etc)
|
||||
// Note that sync uses the ":ui:" notifications for errors because sync.
|
||||
// ReadingList has no such concept (yet?; hopefully the :error is enough!)
|
||||
switch (topic) {
|
||||
case "weave:ui:sync:finish":
|
||||
this.onSyncFinish();
|
||||
break;
|
||||
@ -496,9 +632,6 @@ let gSyncUI = {
|
||||
case "weave:service:setup-complete":
|
||||
this.onSetupComplete();
|
||||
break;
|
||||
case "weave:service:login:start":
|
||||
this.onActivityStart();
|
||||
break;
|
||||
case "weave:service:login:finish":
|
||||
this.onLoginFinish();
|
||||
break;
|
||||
@ -523,6 +656,13 @@ let gSyncUI = {
|
||||
case "weave:ui:clear-error":
|
||||
this.clearError();
|
||||
break;
|
||||
|
||||
case "readinglist:sync:error":
|
||||
this.onRLSyncError();
|
||||
break;
|
||||
case "readinglist:sync:finish":
|
||||
this.clearError();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
@ -540,3 +680,6 @@ XPCOMUtils.defineLazyGetter(gSyncUI, "_stringBundle", function() {
|
||||
createBundle("chrome://weave/locale/services/sync.properties");
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(gSyncUI, "log", function() {
|
||||
return Log.repository.getLogger("browserwindow.syncui");
|
||||
});
|
||||
|
@ -37,6 +37,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
|
||||
"resource:///modules/ContentSearch.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AboutHome",
|
||||
"resource:///modules/AboutHome.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Log",
|
||||
"resource://gre/modules/Log.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "Favicons",
|
||||
"@mozilla.org/browser/favicon-service;1",
|
||||
"mozIAsyncFavicons");
|
||||
|
@ -403,6 +403,8 @@ support-files =
|
||||
[browser_ssl_error_reports.js]
|
||||
[browser_star_hsts.js]
|
||||
[browser_subframe_favicons_not_used.js]
|
||||
[browser_syncui.js]
|
||||
skip-if = e10s # Bug 1137087 - browser_tabopen_reflows.js fails if this was previously run with e10s
|
||||
[browser_tabDrop.js]
|
||||
skip-if = buildapp == 'mulet' || e10s
|
||||
[browser_tabMatchesInAwesomebar.js]
|
||||
|
244
browser/base/content/test/general/browser_syncui.js
Normal file
244
browser/base/content/test/general/browser_syncui.js
Normal file
@ -0,0 +1,244 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
let {Log} = Cu.import("resource://gre/modules/Log.jsm", {});
|
||||
let {Weave} = Cu.import("resource://services-sync/main.js", {});
|
||||
let {Notifications} = Cu.import("resource://services-sync/notifications.js", {});
|
||||
// The BackStagePass allows us to get this test-only non-exported function.
|
||||
let {getInternalScheduler} = Cu.import("resource:///modules/readinglist/Scheduler.jsm", {});
|
||||
|
||||
let stringBundle = Cc["@mozilla.org/intl/stringbundle;1"]
|
||||
.getService(Ci.nsIStringBundleService)
|
||||
.createBundle("chrome://weave/locale/services/sync.properties");
|
||||
|
||||
// ensure test output sees log messages.
|
||||
Log.repository.getLogger("browserwindow.syncui").addAppender(new Log.DumpAppender());
|
||||
|
||||
function promiseObserver(topic) {
|
||||
return new Promise(resolve => {
|
||||
let obs = (subject, topic, data) => {
|
||||
Services.obs.removeObserver(obs, topic);
|
||||
resolve(subject);
|
||||
}
|
||||
Services.obs.addObserver(obs, topic, false);
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function* prepare() {
|
||||
let xps = Components.classes["@mozilla.org/weave/service;1"]
|
||||
.getService(Components.interfaces.nsISupports)
|
||||
.wrappedJSObject;
|
||||
yield xps.whenLoaded();
|
||||
// mock out the "_needsSetup()" function so we don't short-circuit.
|
||||
let oldNeedsSetup = window.gSyncUI._needsSetup;
|
||||
window.gSyncUI._needsSetup = () => false;
|
||||
registerCleanupFunction(() => {
|
||||
window.gSyncUI._needsSetup = oldNeedsSetup;
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* testProlongedSyncError() {
|
||||
let promiseNotificationAdded = promiseObserver("weave:notification:added");
|
||||
Assert.equal(Notifications.notifications.length, 0, "start with no notifications");
|
||||
|
||||
// Pretend we are in the "prolonged error" state.
|
||||
Weave.Status.sync = Weave.PROLONGED_SYNC_FAILURE;
|
||||
Weave.Status.login = Weave.LOGIN_SUCCEEDED;
|
||||
Services.obs.notifyObservers(null, "weave:ui:sync:error", null);
|
||||
|
||||
let subject = yield promiseNotificationAdded;
|
||||
let notification = subject.wrappedJSObject.object; // sync's observer abstraction is abstract!
|
||||
Assert.equal(notification.title, stringBundle.GetStringFromName("error.sync.title"));
|
||||
Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
|
||||
|
||||
// Now pretend we just had a successful sync - the error notification should go away.
|
||||
let promiseNotificationRemoved = promiseObserver("weave:notification:removed");
|
||||
Weave.Status.sync = Weave.STATUS_OK;
|
||||
Services.obs.notifyObservers(null, "weave:ui:sync:finish", null);
|
||||
yield promiseNotificationRemoved;
|
||||
Assert.equal(Notifications.notifications.length, 0, "no notifications left");
|
||||
});
|
||||
|
||||
add_task(function* testProlongedRLError() {
|
||||
let promiseNotificationAdded = promiseObserver("weave:notification:added");
|
||||
Assert.equal(Notifications.notifications.length, 0, "start with no notifications");
|
||||
|
||||
// Pretend the reading-list is in the "prolonged error" state.
|
||||
let longAgo = new Date(Date.now() - 100 * 24 * 60 * 60 * 1000); // 100 days ago.
|
||||
Services.prefs.setCharPref("readinglist.scheduler.lastSync", longAgo.toString());
|
||||
getInternalScheduler().state = ReadingListScheduler.STATE_ERROR_OTHER;
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:error", null);
|
||||
|
||||
let subject = yield promiseNotificationAdded;
|
||||
let notification = subject.wrappedJSObject.object; // sync's observer abstraction is abstract!
|
||||
Assert.equal(notification.title, stringBundle.GetStringFromName("error.sync.title"));
|
||||
Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
|
||||
|
||||
// Now pretend we just had a successful sync - the error notification should go away.
|
||||
let promiseNotificationRemoved = promiseObserver("weave:notification:removed");
|
||||
Services.prefs.setCharPref("readinglist.scheduler.lastSync", Date.now().toString());
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:finish", null);
|
||||
yield promiseNotificationRemoved;
|
||||
Assert.equal(Notifications.notifications.length, 0, "no notifications left");
|
||||
});
|
||||
|
||||
add_task(function* testSyncLoginError() {
|
||||
let promiseNotificationAdded = promiseObserver("weave:notification:added");
|
||||
Assert.equal(Notifications.notifications.length, 0, "start with no notifications");
|
||||
|
||||
// Pretend we are in the "prolonged error" state.
|
||||
Weave.Status.sync = Weave.LOGIN_FAILED;
|
||||
Weave.Status.login = Weave.LOGIN_FAILED_LOGIN_REJECTED;
|
||||
Services.obs.notifyObservers(null, "weave:ui:sync:error", null);
|
||||
|
||||
let subject = yield promiseNotificationAdded;
|
||||
let notification = subject.wrappedJSObject.object; // sync's observer abstraction is abstract!
|
||||
Assert.equal(notification.title, stringBundle.GetStringFromName("error.login.title"));
|
||||
Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
|
||||
|
||||
// Now pretend we just had a successful login - the error notification should go away.
|
||||
Weave.Status.sync = Weave.STATUS_OK;
|
||||
Weave.Status.login = Weave.LOGIN_SUCCEEDED;
|
||||
let promiseNotificationRemoved = promiseObserver("weave:notification:removed");
|
||||
Services.obs.notifyObservers(null, "weave:service:login:start", null);
|
||||
Services.obs.notifyObservers(null, "weave:service:login:finish", null);
|
||||
yield promiseNotificationRemoved;
|
||||
Assert.equal(Notifications.notifications.length, 0, "no notifications left");
|
||||
});
|
||||
|
||||
add_task(function* testRLLoginError() {
|
||||
let promiseNotificationAdded = promiseObserver("weave:notification:added");
|
||||
Assert.equal(Notifications.notifications.length, 0, "start with no notifications");
|
||||
|
||||
// Pretend RL is in an auth error state
|
||||
getInternalScheduler().state = ReadingListScheduler.STATE_ERROR_AUTHENTICATION;
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:error", null);
|
||||
|
||||
let subject = yield promiseNotificationAdded;
|
||||
let notification = subject.wrappedJSObject.object; // sync's observer abstraction is abstract!
|
||||
Assert.equal(notification.title, stringBundle.GetStringFromName("error.login.title"));
|
||||
Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
|
||||
|
||||
// Now pretend we just had a successful sync - the error notification should go away.
|
||||
getInternalScheduler().state = ReadingListScheduler.STATE_OK;
|
||||
let promiseNotificationRemoved = promiseObserver("weave:notification:removed");
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:finish", null);
|
||||
yield promiseNotificationRemoved;
|
||||
Assert.equal(Notifications.notifications.length, 0, "no notifications left");
|
||||
});
|
||||
|
||||
// Here we put readinglist into an "authentication error" state (should see
|
||||
// the error bar reflecting this), then report a prolonged error from Sync (an
|
||||
// infobar to reflect the sync error should replace it), then resolve the sync
|
||||
// error - the authentication error from readinglist should remain.
|
||||
add_task(function* testRLLoginErrorRemains() {
|
||||
let promiseNotificationAdded = promiseObserver("weave:notification:added");
|
||||
Assert.equal(Notifications.notifications.length, 0, "start with no notifications");
|
||||
|
||||
// Pretend RL is in an auth error state
|
||||
getInternalScheduler().state = ReadingListScheduler.STATE_ERROR_AUTHENTICATION;
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:error", null);
|
||||
|
||||
let subject = yield promiseNotificationAdded;
|
||||
let notification = subject.wrappedJSObject.object; // sync's observer abstraction is abstract!
|
||||
Assert.equal(notification.title, stringBundle.GetStringFromName("error.login.title"));
|
||||
Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
|
||||
|
||||
// Now Sync into a prolonged auth error state.
|
||||
promiseNotificationAdded = promiseObserver("weave:notification:added");
|
||||
Weave.Status.sync = Weave.PROLONGED_SYNC_FAILURE;
|
||||
Weave.Status.login = Weave.LOGIN_FAILED_LOGIN_REJECTED;
|
||||
Services.obs.notifyObservers(null, "weave:ui:sync:error", null);
|
||||
subject = yield promiseNotificationAdded;
|
||||
// still exactly 1 notification with the "login" title.
|
||||
notification = subject.wrappedJSObject.object;
|
||||
Assert.equal(notification.title, stringBundle.GetStringFromName("error.login.title"));
|
||||
Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
|
||||
|
||||
// Resolve the sync problem.
|
||||
promiseNotificationAdded = promiseObserver("weave:notification:added");
|
||||
Weave.Status.sync = Weave.STATUS_OK;
|
||||
Weave.Status.login = Weave.LOGIN_SUCCEEDED;
|
||||
Services.obs.notifyObservers(null, "weave:ui:sync:finish", null);
|
||||
|
||||
// Expect one notification - the RL login problem.
|
||||
subject = yield promiseNotificationAdded;
|
||||
// still exactly 1 notification with the "login" title.
|
||||
notification = subject.wrappedJSObject.object;
|
||||
Assert.equal(notification.title, stringBundle.GetStringFromName("error.login.title"));
|
||||
Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
|
||||
|
||||
// and cleanup - resolve the readinglist error.
|
||||
getInternalScheduler().state = ReadingListScheduler.STATE_OK;
|
||||
let promiseNotificationRemoved = promiseObserver("weave:notification:removed");
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:finish", null);
|
||||
yield promiseNotificationRemoved;
|
||||
Assert.equal(Notifications.notifications.length, 0, "no notifications left");
|
||||
});
|
||||
|
||||
function checkButtonsStatus(shouldBeActive) {
|
||||
let button = document.getElementById("sync-button");
|
||||
let panelbutton = document.getElementById("PanelUI-fxa-status");
|
||||
if (shouldBeActive) {
|
||||
Assert.equal(button.getAttribute("status"), "active");
|
||||
Assert.equal(panelbutton.getAttribute("syncstatus"), "active");
|
||||
} else {
|
||||
Assert.ok(!button.hasAttribute("status"));
|
||||
Assert.ok(!panelbutton.hasAttribute("syncstatus"));
|
||||
}
|
||||
}
|
||||
|
||||
function testButtonActions(startNotification, endNotification) {
|
||||
checkButtonsStatus(false);
|
||||
// pretend a sync is starting.
|
||||
Services.obs.notifyObservers(null, startNotification, null);
|
||||
checkButtonsStatus(true);
|
||||
// and has stopped
|
||||
Services.obs.notifyObservers(null, endNotification, null);
|
||||
checkButtonsStatus(false);
|
||||
}
|
||||
|
||||
add_task(function* testButtonActivities() {
|
||||
// add the Sync button to the panel so we can get it!
|
||||
CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_PANEL);
|
||||
// check the button's functionality
|
||||
yield PanelUI.show();
|
||||
try {
|
||||
testButtonActions("weave:service:login:start", "weave:service:login:finish");
|
||||
testButtonActions("weave:service:login:start", "weave:service:login:error");
|
||||
|
||||
testButtonActions("weave:service:sync:start", "weave:service:sync:finish");
|
||||
testButtonActions("weave:service:sync:start", "weave:service:sync:error");
|
||||
|
||||
testButtonActions("readinglist:sync:start", "readinglist:sync:finish");
|
||||
testButtonActions("readinglist:sync:start", "readinglist:sync:error");
|
||||
|
||||
// and ensure the counters correctly handle multiple in-flight syncs
|
||||
Services.obs.notifyObservers(null, "weave:service:sync:start", null);
|
||||
checkButtonsStatus(true);
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
|
||||
checkButtonsStatus(true);
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:finish", null);
|
||||
// sync is still going...
|
||||
checkButtonsStatus(true);
|
||||
// another reading list starts
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
|
||||
checkButtonsStatus(true);
|
||||
// The initial sync stops.
|
||||
Services.obs.notifyObservers(null, "weave:service:sync:finish", null);
|
||||
// RL is still going...
|
||||
checkButtonsStatus(true);
|
||||
// RL finishes with an error, so no longer active.
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:error", null);
|
||||
checkButtonsStatus(false);
|
||||
} finally {
|
||||
PanelUI.hide();
|
||||
CustomizableUI.removeWidgetFromArea("sync-button");
|
||||
}
|
||||
});
|
@ -4,6 +4,8 @@
|
||||
|
||||
let { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils",
|
||||
"resource://gre/modules/DownloadUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
|
||||
|
@ -66,6 +66,8 @@
|
||||
|
||||
let { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
|
||||
"resource:///modules/DownloadsCommon.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsViewUI",
|
||||
|
338
browser/components/readinglist/Scheduler.jsm
Normal file
338
browser/components/readinglist/Scheduler.jsm
Normal file
@ -0,0 +1,338 @@
|
||||
/* 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 {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'LogManager',
|
||||
'resource://services-common/logmanager.js');
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'Log',
|
||||
'resource://gre/modules/Log.jsm');
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'Preferences',
|
||||
'resource://gre/modules/Preferences.jsm');
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'setTimeout',
|
||||
'resource://gre/modules/Timer.jsm');
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'clearTimeout',
|
||||
'resource://gre/modules/Timer.jsm');
|
||||
|
||||
Cu.import('resource://gre/modules/Task.jsm');
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["ReadingListScheduler"];
|
||||
|
||||
// A list of "external" observer topics that may cause us to change when we
|
||||
// sync.
|
||||
const OBSERVERS = [
|
||||
// We don't sync when offline and restart when online.
|
||||
"network:offline-status-changed",
|
||||
// FxA notifications also cause us to check if we should sync.
|
||||
"fxaccounts:onverified",
|
||||
// When something notices a local change to an item.
|
||||
"readinglist:item-changed",
|
||||
// some notifications the engine might send if we have been requested to backoff.
|
||||
"readinglist:backoff-requested",
|
||||
// request to sync now
|
||||
"readinglist:user-sync",
|
||||
|
||||
];
|
||||
|
||||
///////// A temp object until we get our "engine"
|
||||
let engine = {
|
||||
ERROR_AUTHENTICATION: "authentication error",
|
||||
sync: Task.async(function* () {
|
||||
}),
|
||||
}
|
||||
|
||||
let prefs = new Preferences("readinglist.scheduler.");
|
||||
|
||||
// A helper to manage our interval values.
|
||||
let intervals = {
|
||||
// Getters for our intervals.
|
||||
_fixupIntervalPref(prefName, def) {
|
||||
// All pref values are seconds, but we return ms.
|
||||
return prefs.get(prefName, def) * 1000;
|
||||
},
|
||||
|
||||
// How long after startup do we do an initial sync?
|
||||
get initial() this._fixupIntervalPref("initial", 20), // 20 seconds.
|
||||
// Every interval after the first.
|
||||
get schedule() this._fixupIntervalPref("schedule", 2 * 60 * 60), // 2 hours
|
||||
// After we've been told an item has changed
|
||||
get dirty() this._fixupIntervalPref("dirty", 2 * 60), // 2 mins
|
||||
// After an error
|
||||
get retry() this._fixupIntervalPref("retry", 2 * 60), // 2 mins
|
||||
};
|
||||
|
||||
// This is the implementation, but it's not exposed directly.
|
||||
function InternalScheduler() {
|
||||
// oh, I don't know what logs yet - let's guess!
|
||||
let logs = ["readinglist", "FirefoxAccounts", "browserwindow.syncui"];
|
||||
this._logManager = new LogManager("readinglist.", logs, "readinglist");
|
||||
this.log = Log.repository.getLogger("readinglist.scheduler");
|
||||
this.log.info("readinglist scheduler created.")
|
||||
this.state = this.STATE_OK;
|
||||
|
||||
// don't this.init() here, but instead at the module level - tests want to
|
||||
// add hooks before it is called.
|
||||
}
|
||||
|
||||
InternalScheduler.prototype = {
|
||||
// When the next scheduled sync should happen. If we can sync, there will
|
||||
// be a timer set to fire then. If we can't sync there will not be a timer,
|
||||
// but it will be set to fire then as soon as we can.
|
||||
_nextScheduledSync: null,
|
||||
// The time when the most-recent "backoff request" expires - we will never
|
||||
// schedule a new timer before this.
|
||||
_backoffUntil: 0,
|
||||
// Our current timer.
|
||||
_timer: null,
|
||||
// Our timer fires a promise - _timerRunning is true until it resolves or
|
||||
// rejects.
|
||||
_timerRunning: false,
|
||||
// Our sync engine - XXX - maybe just a callback?
|
||||
_engine: engine,
|
||||
|
||||
// Our state variable and constants.
|
||||
state: null,
|
||||
STATE_OK: "ok",
|
||||
STATE_ERROR_AUTHENTICATION: "authentication error",
|
||||
STATE_ERROR_OTHER: "other error",
|
||||
|
||||
init() {
|
||||
this.log.info("scheduler initialzing");
|
||||
this._observe = this.observe.bind(this);
|
||||
for (let notification of OBSERVERS) {
|
||||
Services.obs.addObserver(this._observe, notification, false);
|
||||
}
|
||||
this._nextScheduledSync = Date.now() + intervals.initial;
|
||||
this._setupTimer();
|
||||
},
|
||||
|
||||
// Note: only called by tests.
|
||||
finalize() {
|
||||
this.log.info("scheduler finalizing");
|
||||
this._clearTimer();
|
||||
for (let notification of OBSERVERS) {
|
||||
Services.obs.removeObserver(this._observe, notification);
|
||||
}
|
||||
this._observe = null;
|
||||
},
|
||||
|
||||
observe(subject, topic, data) {
|
||||
this.log.debug("observed ${}", topic);
|
||||
switch (topic) {
|
||||
case "readinglist:backoff-requested": {
|
||||
// The subject comes in as a string, a number of seconds.
|
||||
let interval = parseInt(data, 10);
|
||||
if (isNaN(interval)) {
|
||||
this.log.warn("Backoff request had non-numeric value", data);
|
||||
return;
|
||||
}
|
||||
this.log.info("Received a request to backoff for ${} seconds", interval);
|
||||
this._backoffUntil = Date.now() + interval * 1000;
|
||||
this._maybeReschedule(0);
|
||||
break;
|
||||
}
|
||||
case "readinglist:local:dirty":
|
||||
this._maybeReschedule(intervals.dirty);
|
||||
break;
|
||||
case "readinglist:user-sync":
|
||||
this._syncNow();
|
||||
break;
|
||||
case "fxaccounts:onverified":
|
||||
// If we were in an authentication error state, reset that now.
|
||||
if (this.state == this.STATE_ERROR_AUTHENTICATION) {
|
||||
this.state = this.STATE_OK;
|
||||
}
|
||||
break;
|
||||
|
||||
// The rest just indicate that now is probably a good time to check if
|
||||
// we can sync as normal using whatever schedule was previously set.
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// When observers fire we ignore the current sync error state as the
|
||||
// notification may indicate it's been resolved.
|
||||
this._setupTimer(true);
|
||||
},
|
||||
|
||||
// Is the current error state such that we shouldn't schedule a new sync.
|
||||
_isBlockedOnError() {
|
||||
// this needs more thought...
|
||||
return this.state == this.STATE_ERROR_AUTHENTICATION;
|
||||
},
|
||||
|
||||
// canSync indicates if we can currently sync.
|
||||
_canSync(ignoreBlockingErrors = false) {
|
||||
if (Services.io.offline) {
|
||||
this.log.info("canSync=false - we are offline");
|
||||
return false;
|
||||
}
|
||||
if (!ignoreBlockingErrors && this._isBlockedOnError()) {
|
||||
this.log.info("canSync=false - we are in a blocked error state", this.state);
|
||||
return false;
|
||||
}
|
||||
this.log.info("canSync=true");
|
||||
return true;
|
||||
},
|
||||
|
||||
// _setupTimer checks the current state and the environment to see when
|
||||
// we should next sync and creates the timer with the appropriate delay.
|
||||
_setupTimer(ignoreBlockingErrors = false) {
|
||||
if (!this._canSync(ignoreBlockingErrors)) {
|
||||
this._clearTimer();
|
||||
return;
|
||||
}
|
||||
if (this._timer) {
|
||||
let when = new Date(this._nextScheduledSync);
|
||||
let delay = this._nextScheduledSync - Date.now();
|
||||
this.log.info("checkStatus - already have a timer - will fire in ${delay}ms at ${when}",
|
||||
{delay, when});
|
||||
return;
|
||||
}
|
||||
if (this._timerRunning) {
|
||||
this.log.info("checkStatus - currently syncing");
|
||||
return;
|
||||
}
|
||||
// no timer and we can sync, so start a new one.
|
||||
let now = Date.now();
|
||||
let delay = Math.max(0, this._nextScheduledSync - now);
|
||||
let when = new Date(now + delay);
|
||||
this.log.info("next scheduled sync is in ${delay}ms (at ${when})", {delay, when})
|
||||
this._timer = this._setTimeout(delay);
|
||||
},
|
||||
|
||||
// Something (possibly naively) thinks the next sync should happen in
|
||||
// delay-ms. If there's a backoff in progress, ignore the requested delay
|
||||
// and use the back-off. If there's already a timer scheduled for earlier
|
||||
// than delay, let the earlier timer remain. Otherwise, use the requested
|
||||
// delay.
|
||||
_maybeReschedule(delay) {
|
||||
// If there's no delay specified and there's nothing currently scheduled,
|
||||
// it means a backoff request while the sync is actually running - there's
|
||||
// no need to do anything here - the next reschedule after the sync
|
||||
// completes will take the backoff into account.
|
||||
if (!delay && !this._nextScheduledSync) {
|
||||
this.log.debug("_maybeReschedule ignoring a backoff request while running");
|
||||
return;
|
||||
}
|
||||
let now = Date.now();
|
||||
if (!this._nextScheduledSync) {
|
||||
this._nextScheduledSync = now + delay;
|
||||
}
|
||||
// If there is something currently scheduled before the requested delay,
|
||||
// keep the existing value (eg, if we have a timer firing in 1 second, and
|
||||
// get a "dirty" notification that says we should sync in 2 seconds, we
|
||||
// keep the 1 second value)
|
||||
this._nextScheduledSync = Math.min(this._nextScheduledSync, now + delay);
|
||||
// But we still need to honor a backoff.
|
||||
this._nextScheduledSync = Math.max(this._nextScheduledSync, this._backoffUntil);
|
||||
// And always create a new timer next time _setupTimer is called.
|
||||
this._clearTimer();
|
||||
},
|
||||
|
||||
// callback for when the timer fires.
|
||||
_doSync() {
|
||||
this.log.debug("starting sync");
|
||||
this._timer = null;
|
||||
this._timerRunning = true;
|
||||
// flag that there's no new schedule yet, so a request coming in while
|
||||
// we are running does the right thing.
|
||||
this._nextScheduledSync = 0;
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
|
||||
this._engine.sync().then(() => {
|
||||
this.log.info("Sync completed successfully");
|
||||
// Write a pref in the same format used to services/sync to indicate
|
||||
// the last success.
|
||||
prefs.set("lastSync", new Date().toString());
|
||||
this.state = this.STATE_OK;
|
||||
this._logManager.resetFileLog(this._logManager.REASON_SUCCESS);
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:finish", null);
|
||||
return intervals.schedule;
|
||||
}).catch(err => {
|
||||
this.log.error("Sync failed", err);
|
||||
// XXX - how to detect an auth error?
|
||||
this.state = err == this._engine.ERROR_AUTHENTICATION ?
|
||||
this.STATE_ERROR_AUTHENTICATION : this.STATE_ERROR_OTHER;
|
||||
this._logManager.resetFileLog(this._logManager.REASON_ERROR);
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:error", null);
|
||||
return intervals.retry;
|
||||
}).then(nextDelay => {
|
||||
this._timerRunning = false;
|
||||
// ensure a new timer is setup for the appropriate next time.
|
||||
this._maybeReschedule(nextDelay);
|
||||
this._setupTimer();
|
||||
this._onAutoReschedule(); // just for tests...
|
||||
}).catch(err => {
|
||||
// We should never get here, but better safe than sorry...
|
||||
this.log.error("Failed to reschedule after sync completed", err);
|
||||
});
|
||||
},
|
||||
|
||||
_clearTimer() {
|
||||
if (this._timer) {
|
||||
clearTimeout(this._timer);
|
||||
this._timer = null;
|
||||
}
|
||||
},
|
||||
|
||||
// A function to "sync now", but not allowing it to start if one is
|
||||
// already running, and rescheduling the timer.
|
||||
// To call this, just send a "readinglist:user-sync" notification.
|
||||
_syncNow() {
|
||||
if (this._timerRunning) {
|
||||
this.log.info("syncNow() but a sync is already in progress - ignoring");
|
||||
return;
|
||||
}
|
||||
this._clearTimer();
|
||||
this._doSync();
|
||||
},
|
||||
|
||||
// A couple of hook-points for testing.
|
||||
// xpcshell tests hook this so (a) it can check the expected delay is set
|
||||
// and (b) to ignore the delay and set a timeout of 0 so the test is fast.
|
||||
_setTimeout(delay) {
|
||||
return setTimeout(() => this._doSync(), delay);
|
||||
},
|
||||
// xpcshell tests hook this to make sure that the correct state etc exist
|
||||
// after a sync has been completed and a new timer created (or not).
|
||||
_onAutoReschedule() {},
|
||||
};
|
||||
|
||||
let internalScheduler = new InternalScheduler();
|
||||
internalScheduler.init();
|
||||
|
||||
// The public interface into this module is tiny, so a simple object that
|
||||
// delegates to the implementation.
|
||||
let ReadingListScheduler = {
|
||||
get STATE_OK() internalScheduler.STATE_OK,
|
||||
get STATE_ERROR_AUTHENTICATION() internalScheduler.STATE_ERROR_AUTHENTICATION,
|
||||
get STATE_ERROR_OTHER() internalScheduler.STATE_ERROR_OTHER,
|
||||
|
||||
get state() internalScheduler.state,
|
||||
};
|
||||
|
||||
// These functions are exposed purely for tests, which manage to grab them
|
||||
// via a BackstagePass.
|
||||
function createTestableScheduler() {
|
||||
// kill the "real" scheduler as we don't want it listening to notifications etc.
|
||||
if (internalScheduler) {
|
||||
internalScheduler.finalize();
|
||||
internalScheduler = null;
|
||||
}
|
||||
// No .init() call - that's up to the tests after hooking.
|
||||
return new InternalScheduler();
|
||||
}
|
||||
|
||||
// mochitests want the internal state of the real scheduler for various things.
|
||||
function getInternalScheduler() {
|
||||
return internalScheduler;
|
||||
}
|
@ -13,3 +13,9 @@ TESTING_JS_MODULES += [
|
||||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini']
|
||||
|
||||
EXTRA_JS_MODULES.readinglist += [
|
||||
'Scheduler.jsm',
|
||||
]
|
||||
|
7
browser/components/readinglist/test/xpcshell/head.js
Normal file
7
browser/components/readinglist/test/xpcshell/head.js
Normal file
@ -0,0 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
146
browser/components/readinglist/test/xpcshell/test_scheduler.js
Normal file
146
browser/components/readinglist/test/xpcshell/test_scheduler.js
Normal file
@ -0,0 +1,146 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'setTimeout',
|
||||
'resource://gre/modules/Timer.jsm');
|
||||
|
||||
// Setup logging prefs before importing the scheduler module.
|
||||
Services.prefs.setCharPref("readinglist.log.appender.dump", "Trace");
|
||||
|
||||
let {createTestableScheduler} = Cu.import("resource:///modules/readinglist/Scheduler.jsm", {});
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/Timer.jsm");
|
||||
|
||||
// Log rotation needs a profile dir.
|
||||
do_get_profile();
|
||||
|
||||
let prefs = new Preferences("readinglist.scheduler.");
|
||||
|
||||
function promiseObserver(topic) {
|
||||
return new Promise(resolve => {
|
||||
let obs = (subject, topic, data) => {
|
||||
Services.obs.removeObserver(obs, topic);
|
||||
resolve(data);
|
||||
}
|
||||
Services.obs.addObserver(obs, topic, false);
|
||||
});
|
||||
}
|
||||
|
||||
function createScheduler(options) {
|
||||
// avoid typos in the test and other footguns in the options.
|
||||
let allowedOptions = ["expectedDelay", "expectNewTimer", "syncFunction"];
|
||||
for (let key of Object.keys(options)) {
|
||||
if (!allowedOptions.includes(key)) {
|
||||
throw new Error("Invalid option " + key);
|
||||
}
|
||||
}
|
||||
let scheduler = createTestableScheduler();
|
||||
// make our hooks
|
||||
let syncFunction = options.syncFunction || Promise.resolve;
|
||||
scheduler._engine.sync = syncFunction;
|
||||
// we expect _setTimeout to be called *twice* - first is the initial sync,
|
||||
// and there's no need to test the delay used for that. options.expectedDelay
|
||||
// is to check the *subsequent* timer.
|
||||
let numCalls = 0;
|
||||
scheduler._setTimeout = function(delay) {
|
||||
++numCalls;
|
||||
print("Test scheduler _setTimeout call number " + numCalls + " with delay=" + delay);
|
||||
switch (numCalls) {
|
||||
case 1:
|
||||
// this is the first and boring schedule as it initializes - do nothing
|
||||
// other than return a timer that fires immediately.
|
||||
return setTimeout(() => scheduler._doSync(), 0);
|
||||
break;
|
||||
case 2:
|
||||
// This is the one we are interested in, so check things.
|
||||
if (options.expectedDelay) {
|
||||
// a little slop is OK as it takes a few ms to actually set the timer
|
||||
ok(Math.abs(options.expectedDelay * 1000 - delay) < 500, [options.expectedDelay * 1000, delay]);
|
||||
}
|
||||
// and return a timeout that "never" fires
|
||||
return setTimeout(() => scheduler._doSync(), 10000000);
|
||||
break;
|
||||
default:
|
||||
// This is unexpected!
|
||||
ok(false, numCalls);
|
||||
}
|
||||
};
|
||||
// And a callback made once we've determined the next delay. This is always
|
||||
// called even if _setTimeout isn't (due to no timer being created)
|
||||
scheduler._onAutoReschedule = () => {
|
||||
// Most tests expect a new timer, so this is "opt out"
|
||||
let expectNewTimer = options.expectNewTimer === undefined ? true : options.expectNewTimer;
|
||||
ok(expectNewTimer ? scheduler._timer : !scheduler._timer);
|
||||
}
|
||||
// calling .init fires things off...
|
||||
scheduler.init();
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
add_task(function* testSuccess() {
|
||||
// promises which resolve once we've got all the expected notifications.
|
||||
let allNotifications = [
|
||||
promiseObserver("readinglist:sync:start"),
|
||||
promiseObserver("readinglist:sync:finish"),
|
||||
];
|
||||
// New delay should be "as regularly scheduled".
|
||||
prefs.set("schedule", 100);
|
||||
let scheduler = createScheduler({expectedDelay: 100});
|
||||
yield Promise.all(allNotifications);
|
||||
scheduler.finalize();
|
||||
});
|
||||
|
||||
add_task(function* testOffline() {
|
||||
let scheduler = createScheduler({expectNewTimer: false});
|
||||
Services.io.offline = true;
|
||||
ok(!scheduler._canSync(), "_canSync is false when offline.")
|
||||
ok(!scheduler._timer, "there is no current timer while offline.")
|
||||
Services.io.offline = false;
|
||||
ok(scheduler._canSync(), "_canSync is true when online.")
|
||||
ok(scheduler._timer, "there is a new timer when back online.")
|
||||
scheduler.finalize();
|
||||
});
|
||||
|
||||
add_task(function* testRetryableError() {
|
||||
let allNotifications = [
|
||||
promiseObserver("readinglist:sync:start"),
|
||||
promiseObserver("readinglist:sync:error"),
|
||||
];
|
||||
prefs.set("retry", 10);
|
||||
let scheduler = createScheduler({
|
||||
expectedDelay: 10,
|
||||
syncFunction: () => Promise.reject("transient"),
|
||||
});
|
||||
yield Promise.all(allNotifications);
|
||||
scheduler.finalize();
|
||||
});
|
||||
|
||||
add_task(function* testAuthError() {
|
||||
prefs.set("retry", 10);
|
||||
// We expect an auth error to result in no new timer (as it's waiting for
|
||||
// some indication it can proceed), but with the next delay being a normal
|
||||
// "retry" interval (so when we can proceed it is probably already stale, so
|
||||
// is effectively "immediate")
|
||||
let scheduler = createScheduler({
|
||||
expectedDelay: 10,
|
||||
syncFunction: () => {
|
||||
return Promise.reject(ReadingListScheduler._engine.ERROR_AUTHENTICATION);
|
||||
},
|
||||
expectNewTimer: false
|
||||
});
|
||||
// XXX - TODO - send an observer that "unblocks" us and ensure we actually
|
||||
// do unblock.
|
||||
scheduler.finalize();
|
||||
});
|
||||
|
||||
add_task(function* testBackoff() {
|
||||
let scheduler = createScheduler({expectedDelay: 1000});
|
||||
Services.obs.notifyObservers(null, "readinglist:backoff-requested", 1000);
|
||||
// XXX - this needs a little love as nothing checks createScheduler actually
|
||||
// made the checks we think it does.
|
||||
scheduler.finalize();
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
[DEFAULT]
|
||||
head = head.js
|
||||
firefox-appdir = browser
|
||||
|
||||
[test_scheduler.js]
|
@ -452,6 +452,28 @@ add_task(function* dont_rollup_oncaretmove() {
|
||||
is(textbox.selectionEnd, 9, "Should have moved the caret (selectionEnd after right)");
|
||||
is(searchPopup.state, "open", "Popup should still be open");
|
||||
|
||||
// Ensure caret movement works while a suggestion is selected.
|
||||
is(textbox.popup.selectedIndex, -1, "No selected item in list");
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
is(textbox.popup.selectedIndex, 0, "Selected item in list");
|
||||
is(textbox.selectionStart, 9, "Should have moved the caret to the end (selectionStart after selection)");
|
||||
is(textbox.selectionEnd, 9, "Should have moved the caret to the end (selectionEnd after selection)");
|
||||
|
||||
EventUtils.synthesizeKey("VK_LEFT", {});
|
||||
is(textbox.selectionStart, 8, "Should have moved the caret again (selectionStart after left)");
|
||||
is(textbox.selectionEnd, 8, "Should have moved the caret again (selectionEnd after left)");
|
||||
is(searchPopup.state, "open", "Popup should still be open");
|
||||
|
||||
EventUtils.synthesizeKey("VK_LEFT", {});
|
||||
is(textbox.selectionStart, 7, "Should have moved the caret (selectionStart after left)");
|
||||
is(textbox.selectionEnd, 7, "Should have moved the caret (selectionEnd after left)");
|
||||
is(searchPopup.state, "open", "Popup should still be open");
|
||||
|
||||
EventUtils.synthesizeKey("VK_RIGHT", {});
|
||||
is(textbox.selectionStart, 8, "Should have moved the caret (selectionStart after right)");
|
||||
is(textbox.selectionEnd, 8, "Should have moved the caret (selectionEnd after right)");
|
||||
is(searchPopup.state, "open", "Popup should still be open");
|
||||
|
||||
if (navigator.platform.indexOf("Mac") == -1) {
|
||||
EventUtils.synthesizeKey("VK_HOME", {});
|
||||
is(textbox.selectionStart, 0, "Should have moved the caret (selectionStart after home)");
|
||||
|
@ -48,7 +48,7 @@ function test() {
|
||||
statusText: "OK",
|
||||
type: "json",
|
||||
fullMimeType: "application/json; charset=utf-8",
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(4),
|
||||
@ -67,7 +67,7 @@ function test() {
|
||||
statusText: "OK",
|
||||
type: "png",
|
||||
fullMimeType: "image/png",
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.75),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.76),
|
||||
time: true
|
||||
});
|
||||
|
||||
|
@ -228,8 +228,8 @@ function test() {
|
||||
statusText: "Meh",
|
||||
type: "2",
|
||||
fullMimeType: "text/2",
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(c),
|
||||
@ -239,8 +239,8 @@ function test() {
|
||||
statusText: "Meh",
|
||||
type: "3",
|
||||
fullMimeType: "text/3",
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(d),
|
||||
@ -250,8 +250,8 @@ function test() {
|
||||
statusText: "Meh",
|
||||
type: "4",
|
||||
fullMimeType: "text/4",
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(e),
|
||||
@ -261,8 +261,8 @@ function test() {
|
||||
statusText: "Meh",
|
||||
type: "5",
|
||||
fullMimeType: "text/5",
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.05),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.05),
|
||||
time: true
|
||||
});
|
||||
|
||||
|
@ -143,8 +143,8 @@ function test() {
|
||||
statusText: "Meh",
|
||||
type: "2",
|
||||
fullMimeType: "text/2",
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
|
||||
time: true
|
||||
});
|
||||
}
|
||||
@ -156,8 +156,8 @@ function test() {
|
||||
statusText: "Meh",
|
||||
type: "3",
|
||||
fullMimeType: "text/3",
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
|
||||
time: true
|
||||
});
|
||||
}
|
||||
@ -169,8 +169,8 @@ function test() {
|
||||
statusText: "Meh",
|
||||
type: "4",
|
||||
fullMimeType: "text/4",
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
|
||||
time: true
|
||||
});
|
||||
}
|
||||
@ -182,8 +182,8 @@ function test() {
|
||||
statusText: "Meh",
|
||||
type: "5",
|
||||
fullMimeType: "text/5",
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.05),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.05),
|
||||
time: true
|
||||
});
|
||||
}
|
||||
|
@ -47,11 +47,6 @@ var Resource = Class({
|
||||
this.uri = uri;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the trailing name component of this.uri.
|
||||
*/
|
||||
get basename() { return this.uri.path.replace(/\/+$/, '').replace(/\\/g,'/').replace( /.*\//, '' ); },
|
||||
|
||||
/**
|
||||
* Is there more than 1 child Resource?
|
||||
*/
|
||||
@ -238,6 +233,13 @@ var FileResource = Class({
|
||||
return this._refreshDeferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the trailing name component of this Resource
|
||||
*/
|
||||
get basename() {
|
||||
return this.path.replace(/\/+$/, '').replace(/\\/g,'/').replace( /.*\//, '' );
|
||||
},
|
||||
|
||||
/**
|
||||
* A string to be used when displaying this Resource in views
|
||||
*/
|
||||
|
@ -13,7 +13,29 @@ add_task(function*() {
|
||||
let root = [...projecteditor.project.allStores()][0].root;
|
||||
is(root.path, TEMP_PATH, "The root store is set to the correct temp path.");
|
||||
for (let child of root.children) {
|
||||
yield renameWithContextMenu(projecteditor, projecteditor.projectTree.getViewContainer(child));
|
||||
yield renameWithContextMenu(projecteditor,
|
||||
projecteditor.projectTree.getViewContainer(child),
|
||||
".renamed");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function*() {
|
||||
let projecteditor = yield addProjectEditorTabForTempDirectory();
|
||||
ok(true, "ProjectEditor has loaded");
|
||||
|
||||
let root = [...projecteditor.project.allStores()][0].root;
|
||||
is(root.path, TEMP_PATH, "The root store is set to the correct temp path.");
|
||||
|
||||
let childrenList = new Array();
|
||||
for (let child of root.children) {
|
||||
yield renameWithContextMenu(projecteditor,
|
||||
projecteditor.projectTree.getViewContainer(child),
|
||||
".ren\u0061\u0308med");
|
||||
childrenList.push(child.basename + ".ren\u0061\u0308med");
|
||||
}
|
||||
for (let child of root.children) {
|
||||
is (childrenList.indexOf(child.basename) == -1, false,
|
||||
"Failed to update tree with non-ascii character");
|
||||
}
|
||||
});
|
||||
|
||||
@ -25,7 +47,7 @@ function openContextMenuOn(node) {
|
||||
);
|
||||
}
|
||||
|
||||
function renameWithContextMenu(projecteditor, container) {
|
||||
function renameWithContextMenu(projecteditor, container, newName) {
|
||||
let defer = promise.defer();
|
||||
let popup = projecteditor.contextMenuPopup;
|
||||
let resource = container.resource;
|
||||
@ -39,7 +61,7 @@ function renameWithContextMenu(projecteditor, container) {
|
||||
|
||||
projecteditor.project.on("refresh-complete", function refreshComplete() {
|
||||
projecteditor.project.off("refresh-complete", refreshComplete);
|
||||
OS.File.stat(resource.path + ".renamed").then(() => {
|
||||
OS.File.stat(resource.path + newName).then(() => {
|
||||
ok (true, "File is renamed");
|
||||
defer.resolve();
|
||||
}, (ex) => {
|
||||
@ -50,7 +72,8 @@ function renameWithContextMenu(projecteditor, container) {
|
||||
|
||||
renameCommand.click();
|
||||
popup.hidePopup();
|
||||
EventUtils.sendString(resource.basename + ".renamed", projecteditor.window);
|
||||
let input = container.elt.previousElementSibling;
|
||||
input.value = resource.basename + newName;
|
||||
EventUtils.synthesizeKey("VK_RETURN", {}, projecteditor.window);
|
||||
});
|
||||
|
||||
|
@ -54,7 +54,7 @@ function* testGraph(graph) {
|
||||
|
||||
is(graph._maxTooltip.querySelector("[text=value]").textContent, "60",
|
||||
"The maximum tooltip displays the correct value.");
|
||||
is(graph._avgTooltip.querySelector("[text=value]").textContent, "41.71",
|
||||
is(graph._avgTooltip.querySelector("[text=value]").textContent, "41.72",
|
||||
"The average tooltip displays the correct value.");
|
||||
is(graph._minTooltip.querySelector("[text=value]").textContent, "10",
|
||||
"The minimum tooltip displays the correct value.");
|
||||
|
@ -8,7 +8,7 @@ let {ViewHelpers} = Cu.import("resource:///modules/devtools/ViewHelpers.jsm", {}
|
||||
function test() {
|
||||
let l10n = new ViewHelpers.L10N();
|
||||
|
||||
is(l10n.numberWithDecimals(1234.56789, 2), "1,234.56",
|
||||
is(l10n.numberWithDecimals(1234.56789, 2), "1,234.57",
|
||||
"The first number was properly localized.");
|
||||
is(l10n.numberWithDecimals(0.0001, 2), "0",
|
||||
"The second number was properly localized.");
|
||||
|
@ -369,10 +369,6 @@ ViewHelpers.L10N.prototype = {
|
||||
if (isNaN(aNumber) || aNumber == null) {
|
||||
return "0";
|
||||
}
|
||||
// Remove {n} trailing decimals. Can't use toFixed(n) because
|
||||
// toLocaleString converts the number to a string. Also can't use
|
||||
// toLocaleString(, { maximumFractionDigits: n }) because it's not
|
||||
// implemented on OS X (bug 368838). Gross.
|
||||
let localized = aNumber.toLocaleString(); // localize
|
||||
|
||||
// If no grouping or decimal separators are available, bail out, because
|
||||
@ -381,9 +377,10 @@ ViewHelpers.L10N.prototype = {
|
||||
return localized;
|
||||
}
|
||||
|
||||
let padded = localized + new Array(aDecimals).join("0"); // pad with zeros
|
||||
let match = padded.match("([^]*?\\d{" + aDecimals + "})\\d*$");
|
||||
return match.pop();
|
||||
return aNumber.toLocaleString(undefined, {
|
||||
maximumFractionDigits: aDecimals,
|
||||
minimumFractionDigits: aDecimals
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -447,13 +447,15 @@ StyleEditorUI.prototype = {
|
||||
* Editor to create UI for.
|
||||
*/
|
||||
_sourceLoaded: function(editor) {
|
||||
let ordinal = editor.styleSheet.styleSheetIndex;
|
||||
ordinal = ordinal == -1 ? Number.MAX_SAFE_INTEGER : ordinal;
|
||||
// add new sidebar item and editor to the UI
|
||||
this._view.appendTemplatedItem(STYLE_EDITOR_TEMPLATE, {
|
||||
data: {
|
||||
editor: editor
|
||||
},
|
||||
disableAnimations: this._alwaysDisableAnimations,
|
||||
ordinal: editor.styleSheet.styleSheetIndex,
|
||||
ordinal: ordinal,
|
||||
onCreate: function(summary, details, data) {
|
||||
let editor = data.editor;
|
||||
editor.summary = summary;
|
||||
|
@ -15,6 +15,7 @@ const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devt
|
||||
const Editor = require("devtools/sourceeditor/editor");
|
||||
const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
const {CssLogic} = require("devtools/styleinspector/css-logic");
|
||||
const {console} = require("resource://gre/modules/devtools/Console.jsm");
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
@ -251,11 +252,27 @@ StyleSheetEditor.prototype = {
|
||||
callback(source);
|
||||
}
|
||||
return source;
|
||||
}, e => {
|
||||
if (this._isDestroyed) {
|
||||
console.warn("Could not fetch the source for " +
|
||||
this.styleSheet.href +
|
||||
", the editor was destroyed");
|
||||
Cu.reportError(e);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}, e => {
|
||||
this.emit("error", { key: LOAD_ERROR, append: this.styleSheet.href });
|
||||
throw e;
|
||||
})
|
||||
if (this._isDestroyed) {
|
||||
console.warn("Could not fetch the source for " +
|
||||
this.styleSheet.href +
|
||||
", the editor was destroyed");
|
||||
Cu.reportError(e);
|
||||
} else {
|
||||
this.emit("error", { key: LOAD_ERROR, append: this.styleSheet.href });
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@ -712,6 +729,7 @@ StyleSheetEditor.prototype = {
|
||||
this.cssSheet.off("property-change", this._onPropertyChange);
|
||||
this.cssSheet.off("media-rules-changed", this._onMediaRulesChanged);
|
||||
this.styleSheet.off("error", this._onError);
|
||||
this._isDestroyed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
//
|
||||
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
|
||||
|
||||
const TESTCASE_URI = TEST_BASE + "autocomplete.html";
|
||||
const TESTCASE_URI = TEST_BASE_HTTP + "autocomplete.html";
|
||||
const MAX_SUGGESTIONS = 15;
|
||||
|
||||
// Pref which decides if CSS autocompletion is enabled in Style Editor or not.
|
||||
|
@ -8,7 +8,7 @@
|
||||
//
|
||||
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
|
||||
|
||||
const TESTCASE_URI = TEST_BASE + "four.html";
|
||||
const TESTCASE_URI = TEST_BASE_HTTP + "four.html";
|
||||
|
||||
let gUI;
|
||||
|
||||
|
@ -2,8 +2,8 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const TESTCASE_URI_HTML = TEST_BASE + "simple.html";
|
||||
const TESTCASE_URI_CSS = TEST_BASE + "simple.css";
|
||||
const TESTCASE_URI_HTML = TEST_BASE_HTTP + "simple.html";
|
||||
const TESTCASE_URI_CSS = TEST_BASE_HTTP + "simple.css";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
@ -9,7 +9,7 @@
|
||||
//
|
||||
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: summary is undefined");
|
||||
|
||||
const TESTCASE_URI = TEST_BASE + "simple.html";
|
||||
const TESTCASE_URI = TEST_BASE_HTTP + "simple.html";
|
||||
|
||||
let gUI;
|
||||
|
||||
|
@ -11,8 +11,8 @@ thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
|
||||
|
||||
let gUI;
|
||||
|
||||
const FIRST_TEST_PAGE = TEST_BASE + "inline-1.html"
|
||||
const SECOND_TEST_PAGE = TEST_BASE + "inline-2.html"
|
||||
const FIRST_TEST_PAGE = TEST_BASE_HTTP + "inline-1.html"
|
||||
const SECOND_TEST_PAGE = TEST_BASE_HTTP + "inline-2.html"
|
||||
const SAVE_PATH = "test.css";
|
||||
|
||||
function test()
|
||||
|
@ -9,7 +9,7 @@
|
||||
//
|
||||
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
|
||||
|
||||
const TESTCASE_URI = TEST_BASE + "simple.html";
|
||||
const TESTCASE_URI = TEST_BASE_HTTP + "simple.html";
|
||||
|
||||
let TESTCASE_CSS_SOURCE = "body{background-color:red;";
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const TESTCASE_URI = TEST_BASE + "nostyle.html";
|
||||
const TESTCASE_URI = TEST_BASE_HTTP + "nostyle.html";
|
||||
|
||||
|
||||
function test()
|
||||
|
@ -9,7 +9,7 @@
|
||||
//
|
||||
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
|
||||
|
||||
const TESTCASE_URI = TEST_BASE + "minified.html";
|
||||
const TESTCASE_URI = TEST_BASE_HTTP + "minified.html";
|
||||
|
||||
let gUI;
|
||||
|
||||
|
@ -6,11 +6,11 @@ Components.utils.import("resource://gre/modules/Task.jsm");
|
||||
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
|
||||
const TESTCASE_URI_HTML = TEST_BASE + "sourcemaps-watching.html";
|
||||
const TESTCASE_URI_CSS = TEST_BASE + "sourcemap-css/sourcemaps.css";
|
||||
const TESTCASE_URI_REG_CSS = TEST_BASE + "simple.css";
|
||||
const TESTCASE_URI_SCSS = TEST_BASE + "sourcemap-sass/sourcemaps.scss";
|
||||
const TESTCASE_URI_MAP = TEST_BASE + "sourcemap-css/sourcemaps.css.map";
|
||||
const TESTCASE_URI_HTML = TEST_BASE_HTTP + "sourcemaps-watching.html";
|
||||
const TESTCASE_URI_CSS = TEST_BASE_HTTP + "sourcemap-css/sourcemaps.css";
|
||||
const TESTCASE_URI_REG_CSS = TEST_BASE_HTTP + "simple.css";
|
||||
const TESTCASE_URI_SCSS = TEST_BASE_HTTP + "sourcemap-sass/sourcemaps.scss";
|
||||
const TESTCASE_URI_MAP = TEST_BASE_HTTP + "sourcemap-css/sourcemaps.css.map";
|
||||
const TESTCASE_SCSS_NAME = "sourcemaps.scss";
|
||||
|
||||
const TRANSITIONS_PREF = "devtools.styleeditor.transitions";
|
||||
|
@ -9,7 +9,7 @@
|
||||
//
|
||||
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
|
||||
|
||||
const TESTCASE_URI = TEST_BASE + "four.html";
|
||||
const TESTCASE_URI = TEST_BASE_HTTP + "four.html";
|
||||
|
||||
let gUI;
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const TESTCASE_URI = TEST_BASE + "simple.html";
|
||||
const TESTCASE_URI = TEST_BASE_HTTP + "simple.html";
|
||||
|
||||
let gOriginalWidth; // these are set by runTests()
|
||||
let gOriginalHeight;
|
||||
|
@ -354,6 +354,7 @@ skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js]
|
||||
[browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js]
|
||||
[browser_console_history_persist.js]
|
||||
[browser_webconsole_output_01.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole e10s tests
|
||||
[browser_webconsole_output_02.js]
|
||||
|
@ -0,0 +1,96 @@
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* 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/. */
|
||||
|
||||
// Test that console command input is persisted across toolbox loads.
|
||||
// See Bug 943306.
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_URI = "data:text/html;charset=utf-8,Web Console test for persisting history - bug 943306";
|
||||
const INPUT_HISTORY_COUNT = 10;
|
||||
|
||||
let test = asyncTest(function* () {
|
||||
info ("Setting custom input history pref to " + INPUT_HISTORY_COUNT);
|
||||
Services.prefs.setIntPref("devtools.webconsole.inputHistoryCount", INPUT_HISTORY_COUNT);
|
||||
|
||||
// First tab: run a bunch of commands and then make sure that you can
|
||||
// navigate through their history.
|
||||
yield loadTab(TEST_URI);
|
||||
let hud1 = yield openConsole();
|
||||
is (JSON.stringify(hud1.jsterm.history), "[]", "No history on first tab initially");
|
||||
yield populateInputHistory(hud1);
|
||||
is (JSON.stringify(hud1.jsterm.history), '["0","1","2","3","4","5","6","7","8","9"]',
|
||||
"First tab has populated history");
|
||||
|
||||
// Second tab: Just make sure that you can navigate through the history
|
||||
// generated by the first tab.
|
||||
yield loadTab(TEST_URI);
|
||||
let hud2 = yield openConsole();
|
||||
is (JSON.stringify(hud2.jsterm.history), '["0","1","2","3","4","5","6","7","8","9"]',
|
||||
"Second tab has populated history");
|
||||
yield testNaviatingHistoryInUI(hud2);
|
||||
is (JSON.stringify(hud2.jsterm.history), '["0","1","2","3","4","5","6","7","8","9",""]',
|
||||
"An empty entry has been added in the second tab due to history perusal");
|
||||
|
||||
// Third tab: Should have the same history as first tab, but if we run a
|
||||
// command, then the history of the first and second shouldn't be affected
|
||||
yield loadTab(TEST_URI);
|
||||
let hud3 = yield openConsole();
|
||||
is (JSON.stringify(hud3.jsterm.history), '["0","1","2","3","4","5","6","7","8","9"]',
|
||||
"Third tab has populated history");
|
||||
|
||||
// Set input value separately from execute so UP arrow accurately navigates history.
|
||||
hud3.jsterm.setInputValue('"hello from third tab"');
|
||||
hud3.jsterm.execute();
|
||||
|
||||
is (JSON.stringify(hud1.jsterm.history), '["0","1","2","3","4","5","6","7","8","9"]',
|
||||
"First tab history hasn't changed due to command in third tab");
|
||||
is (JSON.stringify(hud2.jsterm.history), '["0","1","2","3","4","5","6","7","8","9",""]',
|
||||
"Second tab history hasn't changed due to command in third tab");
|
||||
is (JSON.stringify(hud3.jsterm.history), '["1","2","3","4","5","6","7","8","9","\\"hello from third tab\\""]',
|
||||
"Third tab has updated history (and purged the first result) after running a command");
|
||||
|
||||
// Fourth tab: Should have the latest command from the third tab, followed
|
||||
// by the rest of the history from the first tab.
|
||||
yield loadTab(TEST_URI);
|
||||
let hud4 = yield openConsole();
|
||||
is (JSON.stringify(hud4.jsterm.history), '["1","2","3","4","5","6","7","8","9","\\"hello from third tab\\""]',
|
||||
"Fourth tab has most recent history");
|
||||
|
||||
info ("Clearing custom input history pref");
|
||||
Services.prefs.clearUserPref("devtools.webconsole.inputHistoryCount");
|
||||
});
|
||||
|
||||
/**
|
||||
* Populate the history by running the following commands:
|
||||
* [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
*/
|
||||
function* populateInputHistory(hud) {
|
||||
let jsterm = hud.jsterm;
|
||||
let {inputNode} = jsterm;
|
||||
|
||||
for (let i = 0; i < INPUT_HISTORY_COUNT; i++) {
|
||||
// Set input value separately from execute so UP arrow accurately navigates history.
|
||||
jsterm.setInputValue(i);
|
||||
jsterm.execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check pressing up results in history traversal like:
|
||||
* [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
|
||||
*/
|
||||
function* testNaviatingHistoryInUI(hud) {
|
||||
let jsterm = hud.jsterm;
|
||||
let {inputNode} = jsterm;
|
||||
inputNode.focus();
|
||||
|
||||
// Count backwards from original input and make sure that pressing up
|
||||
// restores this.
|
||||
for (let i = INPUT_HISTORY_COUNT - 1; i >= 0; i--) {
|
||||
EventUtils.synthesizeKey("VK_UP", {});
|
||||
is(inputNode.value, i, "Pressing up restores last input");
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
let {require, TargetFactory} = devtools;
|
||||
let {Utils: WebConsoleUtils} = require("devtools/toolkit/webconsole/utils");
|
||||
let {Messages} = require("devtools/webconsole/console-output");
|
||||
const asyncStorage = require("devtools/toolkit/shared/async-storage");
|
||||
|
||||
// promise._reportErrors = true; // please never leave me.
|
||||
//Services.prefs.setBoolPref("devtools.debugger.log", true);
|
||||
@ -322,6 +323,9 @@ let finishTest = Task.async(function* () {
|
||||
registerCleanupFunction(function*() {
|
||||
gDevTools.testing = false;
|
||||
|
||||
// Remove stored console commands in between tests
|
||||
yield asyncStorage.removeItem("webConsoleHistory");
|
||||
|
||||
dumpConsoles();
|
||||
|
||||
if (HUDService.getBrowserConsole()) {
|
||||
|
@ -26,6 +26,8 @@ loader.lazyGetter(this, "ConsoleOutput",
|
||||
() => require("devtools/webconsole/console-output").ConsoleOutput);
|
||||
loader.lazyGetter(this, "Messages",
|
||||
() => require("devtools/webconsole/console-output").Messages);
|
||||
loader.lazyGetter(this, "asyncStorage",
|
||||
() => require("devtools/toolkit/shared/async-storage"));
|
||||
loader.lazyImporter(this, "EnvironmentClient", "resource://gre/modules/devtools/dbg-client.jsm");
|
||||
loader.lazyImporter(this, "ObjectClient", "resource://gre/modules/devtools/dbg-client.jsm");
|
||||
loader.lazyImporter(this, "VariablesView", "resource:///modules/devtools/VariablesView.jsm");
|
||||
@ -176,6 +178,7 @@ const MIN_FONT_SIZE = 10;
|
||||
const PREF_CONNECTION_TIMEOUT = "devtools.debugger.remote-timeout";
|
||||
const PREF_PERSISTLOG = "devtools.webconsole.persistlog";
|
||||
const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
|
||||
const PREF_INPUT_HISTORY_COUNT = "devtools.webconsole.inputHistoryCount";
|
||||
|
||||
/**
|
||||
* A WebConsoleFrame instance is an interactive console initialized *per target*
|
||||
@ -443,12 +446,29 @@ WebConsoleFrame.prototype = {
|
||||
/**
|
||||
* Initialize the WebConsoleFrame instance.
|
||||
* @return object
|
||||
* A promise object for the initialization.
|
||||
* A promise object that resolves once the frame is ready to use.
|
||||
*/
|
||||
init: function WCF_init()
|
||||
init: function()
|
||||
{
|
||||
this._initUI();
|
||||
return this._initConnection();
|
||||
let connectionInited = this._initConnection();
|
||||
|
||||
// Don't reject if the history fails to load for some reason.
|
||||
// This would be fine, the panel will just start with empty history.
|
||||
let allReady = this.jsterm.historyLoaded.catch(() => {}).then(() => {
|
||||
return connectionInited;
|
||||
});
|
||||
|
||||
// This notification is only used in tests. Don't chain it onto
|
||||
// the returned promise because the console panel needs to be attached
|
||||
// to the toolbox before the web-console-created event is receieved.
|
||||
let notifyObservers = () => {
|
||||
let id = WebConsoleUtils.supportsString(this.hudId);
|
||||
Services.obs.notifyObservers(id, "web-console-created", null);
|
||||
};
|
||||
allReady.then(notifyObservers, notifyObservers);
|
||||
|
||||
return allReady;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -475,9 +495,6 @@ WebConsoleFrame.prototype = {
|
||||
aReason.error + ": " + aReason.message);
|
||||
this.outputMessage(CATEGORY_JS, node, [aReason]);
|
||||
this._initDefer.reject(aReason);
|
||||
}).then(() => {
|
||||
let id = WebConsoleUtils.supportsString(this.hudId);
|
||||
Services.obs.notifyObservers(id, "web-console-created", null);
|
||||
});
|
||||
|
||||
return this._initDefer.promise;
|
||||
@ -3054,17 +3071,11 @@ function JSTerm(aWebConsoleFrame)
|
||||
{
|
||||
this.hud = aWebConsoleFrame;
|
||||
this.hudId = this.hud.hudId;
|
||||
this.inputHistoryCount = Services.prefs.getIntPref(PREF_INPUT_HISTORY_COUNT);
|
||||
|
||||
this.lastCompletion = { value: null };
|
||||
this.history = [];
|
||||
this._loadHistory();
|
||||
|
||||
// Holds the number of entries in history. This value is incremented in
|
||||
// this.execute().
|
||||
this.historyIndex = 0; // incremented on this.execute()
|
||||
|
||||
// Holds the index of the history entry that the user is currently viewing.
|
||||
// This is reset to this.history.length when this.execute() is invoked.
|
||||
this.historyPlaceHolder = 0;
|
||||
this._objectActorsInVariablesViews = new Map();
|
||||
|
||||
this._keyPress = this._keyPress.bind(this);
|
||||
@ -3079,6 +3090,38 @@ function JSTerm(aWebConsoleFrame)
|
||||
JSTerm.prototype = {
|
||||
SELECTED_FRAME: -1,
|
||||
|
||||
/**
|
||||
* Load the console history from previous sessions.
|
||||
* @private
|
||||
*/
|
||||
_loadHistory: function() {
|
||||
this.history = [];
|
||||
this.historyIndex = this.historyPlaceHolder = 0;
|
||||
|
||||
this.historyLoaded = asyncStorage.getItem("webConsoleHistory").then(value => {
|
||||
if (Array.isArray(value)) {
|
||||
// Since it was gotten asynchronously, there could be items already in
|
||||
// the history. It's not likely but stick them onto the end anyway.
|
||||
this.history = value.concat(this.history);
|
||||
|
||||
// Holds the number of entries in history. This value is incremented in
|
||||
// this.execute().
|
||||
this.historyIndex = this.history.length;
|
||||
|
||||
// Holds the index of the history entry that the user is currently viewing.
|
||||
// This is reset to this.history.length when this.execute() is invoked.
|
||||
this.historyPlaceHolder = this.history.length;
|
||||
}
|
||||
}, console.error);
|
||||
},
|
||||
|
||||
/**
|
||||
* Stores the console history for future sessions.
|
||||
*/
|
||||
storeHistory: function() {
|
||||
asyncStorage.setItem("webConsoleHistory", this.history);
|
||||
},
|
||||
|
||||
/**
|
||||
* Stores the data for the last completion.
|
||||
* @type object
|
||||
@ -3388,6 +3431,12 @@ JSTerm.prototype = {
|
||||
// value that was not evaluated yet.
|
||||
this.history[this.historyIndex++] = aExecuteString;
|
||||
this.historyPlaceHolder = this.history.length;
|
||||
|
||||
if (this.history.length > this.inputHistoryCount) {
|
||||
this.history.splice(0, this.history.length - this.inputHistoryCount);
|
||||
this.historyIndex = this.historyPlaceHolder = this.history.length;
|
||||
}
|
||||
this.storeHistory();
|
||||
WebConsoleUtils.usageCount++;
|
||||
this.setInputValue("");
|
||||
this.clearCompletion();
|
||||
|
@ -476,6 +476,8 @@
|
||||
@RESPATH@/components/formautofill.manifest
|
||||
@RESPATH@/components/FormAutofillContentService.js
|
||||
@RESPATH@/components/FormAutofillStartup.js
|
||||
@RESPATH@/components/CSSUnprefixingService.js
|
||||
@RESPATH@/components/CSSUnprefixingService.manifest
|
||||
@RESPATH@/components/contentAreaDropListener.manifest
|
||||
@RESPATH@/components/contentAreaDropListener.js
|
||||
@RESPATH@/browser/components/BrowserProfileMigrators.manifest
|
||||
|
@ -495,9 +495,14 @@ label.requests-menu-status-code {
|
||||
}
|
||||
|
||||
.tabpanel-summary-value {
|
||||
color: inherit;
|
||||
-moz-padding-start: 3px;
|
||||
}
|
||||
|
||||
.theme-dark .tabpanel-summary-value {
|
||||
color: var(--theme-selection-color);
|
||||
}
|
||||
|
||||
/* Headers tabpanel */
|
||||
|
||||
#headers-summary-status,
|
||||
|
@ -26,3 +26,4 @@ gyp.pth:media/webrtc/trunk/tools/gyp/pylib
|
||||
pyasn1.pth:python/pyasn1
|
||||
bitstring.pth:python/bitstring
|
||||
redo.pth:python/redo
|
||||
requests.pth:python/requests
|
||||
|
@ -8892,6 +8892,7 @@ AC_SUBST(LIBJPEG_TURBO_MIPS_ASM)
|
||||
|
||||
AC_SUBST(MOZ_PACKAGE_JSSHELL)
|
||||
AC_SUBST(MOZ_FOLD_LIBS)
|
||||
AC_SUBST(SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE)
|
||||
|
||||
AC_SUBST(MOZ_ENABLE_SZIP)
|
||||
AC_SUBST(MOZ_SZIP_FLAGS)
|
||||
|
@ -13818,10 +13818,6 @@ nsDocShell::GetAppManifestURL(nsAString& aAppManifestURL)
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::GetAsyncPanZoomEnabled(bool* aOut)
|
||||
{
|
||||
if (TabChild* tabChild = TabChild::GetFrom(this)) {
|
||||
*aOut = tabChild->IsAsyncPanZoomEnabled();
|
||||
return NS_OK;
|
||||
}
|
||||
*aOut = Preferences::GetBool("layers.async-pan-zoom.enabled", false);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -22,9 +22,9 @@ support-files =
|
||||
parent.html
|
||||
|
||||
[test_bug13871.html]
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #RANDOM
|
||||
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' || e10s #RANDOM # Bug 1136180 disabled on B2G Desktop and Mulet for intermittent failures
|
||||
[test_bug270414.html]
|
||||
skip-if = buildapp == 'b2g' || toolkit == "android" || e10s
|
||||
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == "android" || e10s # Bug 1136180 disabled on B2G Desktop and Mulet for intermittent failures
|
||||
[test_bug278916.html]
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
|
||||
[test_bug279495.html]
|
||||
|
@ -6772,7 +6772,7 @@ nsContentUtils::GetSelectionInTextControl(Selection* aSelection,
|
||||
{
|
||||
MOZ_ASSERT(aSelection && aRoot);
|
||||
|
||||
if (!aSelection->GetRangeCount()) {
|
||||
if (!aSelection->RangeCount()) {
|
||||
// Nothing selected
|
||||
aOutStartOffset = aOutEndOffset = 0;
|
||||
return;
|
||||
@ -6833,7 +6833,7 @@ nsContentUtils::GetSelectionBoundingRect(Selection* aSel)
|
||||
res = nsLayoutUtils::TransformFrameRectToAncestor(frame, res, relativeTo);
|
||||
}
|
||||
} else {
|
||||
int32_t rangeCount = aSel->GetRangeCount();
|
||||
int32_t rangeCount = aSel->RangeCount();
|
||||
nsLayoutUtils::RectAccumulator accumulator;
|
||||
for (int32_t idx = 0; idx < rangeCount; ++idx) {
|
||||
nsRange* range = aSel->GetRangeAt(idx);
|
||||
|
@ -6199,7 +6199,7 @@ nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
|
||||
}
|
||||
|
||||
if (!aOptions.mPrototype) {
|
||||
protoObject = JS_NewObjectWithGivenProto(aCx, nullptr, htmlProto, JS::NullPtr());
|
||||
protoObject = JS_NewObjectWithGivenProto(aCx, nullptr, htmlProto);
|
||||
if (!protoObject) {
|
||||
rv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
@ -6348,15 +6348,12 @@ nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
|
||||
// Make sure that the element name matches the name in the definition.
|
||||
// (e.g. a definition for x-button extending button should match
|
||||
// <button is="x-button"> but not <x-button>.
|
||||
// Note: we also check the tag name, because if it's not the above
|
||||
// mentioned case, it can be that only the |is| property has been
|
||||
// changed, which we should ignore by the spec.
|
||||
if (elem->NodeInfo()->NameAtom() != nameAtom &&
|
||||
elem->Tag() == nameAtom) {
|
||||
if (elem->Tag() != nameAtom) {
|
||||
//Skip over this element because definition does not apply.
|
||||
continue;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(elem->IsHTML(nameAtom));
|
||||
nsWrapperCache* cache;
|
||||
CallQueryInterface(elem, &cache);
|
||||
MOZ_ASSERT(cache, "Element doesn't support wrapper cache?");
|
||||
|
@ -1354,7 +1354,7 @@ nsHTMLCopyEncoder::SetSelection(nsISelection* aSelection)
|
||||
nsCOMPtr<nsIDOMRange> range;
|
||||
nsCOMPtr<nsIDOMNode> commonParent;
|
||||
Selection* selection = static_cast<Selection*>(aSelection);
|
||||
uint32_t rangeCount = selection->GetRangeCount();
|
||||
uint32_t rangeCount = selection->RangeCount();
|
||||
|
||||
// if selection is uninitialized return
|
||||
if (!rangeCount)
|
||||
|
@ -2146,24 +2146,15 @@ nsFrameLoader::TryRemoteBrowser()
|
||||
MutableTabContext context;
|
||||
nsCOMPtr<mozIApplication> ownApp = GetOwnApp();
|
||||
nsCOMPtr<mozIApplication> containingApp = GetContainingApp();
|
||||
ScrollingBehavior scrollingBehavior = DEFAULT_SCROLLING;
|
||||
|
||||
if (Preferences::GetBool("layers.async-pan-zoom.enabled", false) ||
|
||||
mOwnerContent->AttrValueIs(kNameSpaceID_None,
|
||||
nsGkAtoms::mozasyncpanzoom,
|
||||
nsGkAtoms::_true,
|
||||
eCaseMatters)) {
|
||||
scrollingBehavior = ASYNC_PAN_ZOOM;
|
||||
}
|
||||
|
||||
bool rv = true;
|
||||
if (ownApp) {
|
||||
rv = context.SetTabContextForAppFrame(ownApp, containingApp, scrollingBehavior);
|
||||
rv = context.SetTabContextForAppFrame(ownApp, containingApp);
|
||||
} else if (OwnerIsBrowserFrame()) {
|
||||
// The |else| above is unnecessary; OwnerIsBrowserFrame() implies !ownApp.
|
||||
rv = context.SetTabContextForBrowserFrame(containingApp, scrollingBehavior);
|
||||
rv = context.SetTabContextForBrowserFrame(containingApp);
|
||||
} else {
|
||||
rv = context.SetTabContextForNormalFrame(scrollingBehavior);
|
||||
rv = context.SetTabContextForNormalFrame();
|
||||
}
|
||||
NS_ENSURE_TRUE(rv, false);
|
||||
|
||||
|
@ -605,7 +605,6 @@ GK_ATOM(mouseover, "mouseover")
|
||||
GK_ATOM(mousethrough, "mousethrough")
|
||||
GK_ATOM(mouseup, "mouseup")
|
||||
GK_ATOM(mozaudiochannel, "mozaudiochannel")
|
||||
GK_ATOM(mozasyncpanzoom, "mozasyncpanzoom")
|
||||
GK_ATOM(mozfullscreenchange, "mozfullscreenchange")
|
||||
GK_ATOM(mozfullscreenerror, "mozfullscreenerror")
|
||||
GK_ATOM(mozpasspointerevents, "mozpasspointerevents")
|
||||
@ -2297,6 +2296,7 @@ GK_ATOM(setsize, "setsize")
|
||||
GK_ATOM(spelling, "spelling")
|
||||
GK_ATOM(spinbutton, "spinbutton")
|
||||
GK_ATOM(status, "status")
|
||||
GK_ATOM(_switch, "switch")
|
||||
GK_ATOM(tableCellIndex, "table-cell-index")
|
||||
GK_ATOM(tablist, "tablist")
|
||||
GK_ATOM(textIndent, "text-indent")
|
||||
|
@ -107,6 +107,10 @@ const size_t gStackSize = 8192;
|
||||
|
||||
#define NS_FULL_GC_DELAY 60000 // ms
|
||||
|
||||
// The amount of time to wait from the user being idle to starting a shrinking
|
||||
// GC.
|
||||
#define NS_SHRINKING_GC_DELAY 15000 // ms
|
||||
|
||||
// Maximum amount of time that should elapse between incremental GC slices
|
||||
#define NS_INTERSLICE_GC_DELAY 100 // ms
|
||||
|
||||
@ -148,6 +152,7 @@ static const uint32_t kMaxICCDuration = 2000; // ms
|
||||
|
||||
static nsITimer *sGCTimer;
|
||||
static nsITimer *sShrinkGCBuffersTimer;
|
||||
static nsITimer *sShrinkingGCTimer;
|
||||
static nsITimer *sCCTimer;
|
||||
static nsITimer *sICCTimer;
|
||||
static nsITimer *sFullGCTimer;
|
||||
@ -212,6 +217,7 @@ static nsIScriptSecurityManager *sSecurityManager;
|
||||
// the appropriate pref is set.
|
||||
|
||||
static bool sGCOnMemoryPressure;
|
||||
static bool sCompactOnUserInactive;
|
||||
|
||||
// In testing, we call RunNextCollectorTimer() to ensure that the collectors are run more
|
||||
// aggressively than they would be in regular browsing. sExpensiveCollectorPokes keeps
|
||||
@ -234,6 +240,7 @@ static void
|
||||
KillTimers()
|
||||
{
|
||||
nsJSContext::KillGCTimer();
|
||||
nsJSContext::KillShrinkingGCTimer();
|
||||
nsJSContext::KillShrinkGCBuffersTimer();
|
||||
nsJSContext::KillCCTimer();
|
||||
nsJSContext::KillICCTimer();
|
||||
@ -266,22 +273,30 @@ NS_IMETHODIMP
|
||||
nsJSEnvironmentObserver::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
if (sGCOnMemoryPressure && !nsCRT::strcmp(aTopic, "memory-pressure")) {
|
||||
if(StringBeginsWith(nsDependentString(aData),
|
||||
NS_LITERAL_STRING("low-memory-ongoing"))) {
|
||||
// Don't GC/CC if we are in an ongoing low-memory state since its very
|
||||
// slow and it likely won't help us anyway.
|
||||
return NS_OK;
|
||||
}
|
||||
nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE,
|
||||
nsJSContext::NonIncrementalGC,
|
||||
nsJSContext::ShrinkingGC);
|
||||
nsJSContext::CycleCollectNow();
|
||||
if (NeedsGCAfterCC()) {
|
||||
if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
|
||||
if (sGCOnMemoryPressure) {
|
||||
if(StringBeginsWith(nsDependentString(aData),
|
||||
NS_LITERAL_STRING("low-memory-ongoing"))) {
|
||||
// Don't GC/CC if we are in an ongoing low-memory state since its very
|
||||
// slow and it likely won't help us anyway.
|
||||
return NS_OK;
|
||||
}
|
||||
nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE,
|
||||
nsJSContext::NonIncrementalGC,
|
||||
nsJSContext::ShrinkingGC);
|
||||
nsJSContext::CycleCollectNow();
|
||||
if (NeedsGCAfterCC()) {
|
||||
nsJSContext::GarbageCollectNow(JS::gcreason::MEM_PRESSURE,
|
||||
nsJSContext::NonIncrementalGC,
|
||||
nsJSContext::ShrinkingGC);
|
||||
}
|
||||
}
|
||||
} else if (!nsCRT::strcmp(aTopic, "user-interaction-inactive")) {
|
||||
if (sCompactOnUserInactive) {
|
||||
nsJSContext::PokeShrinkingGC();
|
||||
}
|
||||
} else if (!nsCRT::strcmp(aTopic, "user-interaction-active")) {
|
||||
nsJSContext::KillShrinkingGCTimer();
|
||||
} else if (!nsCRT::strcmp(aTopic, "quit-application") ||
|
||||
!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
|
||||
sShuttingDown = true;
|
||||
@ -1284,12 +1299,11 @@ nsJSContext::GarbageCollectNow(JS::gcreason::Reason aReason,
|
||||
return;
|
||||
}
|
||||
|
||||
JSGCInvocationKind gckind = aShrinking == ShrinkingGC ? GC_SHRINK : GC_NORMAL;
|
||||
JS::PrepareForFullGC(sRuntime);
|
||||
if (aIncremental == IncrementalGC) {
|
||||
MOZ_ASSERT(aShrinking == NonShrinkingGC);
|
||||
JS::StartIncrementalGC(sRuntime, GC_NORMAL, aReason, aSliceMillis);
|
||||
JS::StartIncrementalGC(sRuntime, gckind, aReason, aSliceMillis);
|
||||
} else {
|
||||
JSGCInvocationKind gckind = aShrinking == ShrinkingGC ? GC_SHRINK : GC_NORMAL;
|
||||
JS::GCForReason(sRuntime, gckind, aReason);
|
||||
}
|
||||
}
|
||||
@ -1798,6 +1812,16 @@ ShrinkGCBuffersTimerFired(nsITimer *aTimer, void *aClosure)
|
||||
nsJSContext::ShrinkGCBuffersNow();
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
ShrinkingGCTimerFired(nsITimer* aTimer, void* aClosure)
|
||||
{
|
||||
nsJSContext::KillShrinkingGCTimer();
|
||||
nsJSContext::GarbageCollectNow(JS::gcreason::USER_INACTIVE,
|
||||
nsJSContext::IncrementalGC,
|
||||
nsJSContext::ShrinkingGC);
|
||||
}
|
||||
|
||||
static bool
|
||||
ShouldTriggerCC(uint32_t aSuspected)
|
||||
{
|
||||
@ -2030,6 +2054,26 @@ nsJSContext::PokeShrinkGCBuffers()
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
nsJSContext::PokeShrinkingGC()
|
||||
{
|
||||
if (sShrinkingGCTimer || sShuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
CallCreateInstance("@mozilla.org/timer;1", &sShrinkingGCTimer);
|
||||
|
||||
if (!sShrinkingGCTimer) {
|
||||
// Failed to create timer (probably because we're in XPCOM shutdown)
|
||||
return;
|
||||
}
|
||||
|
||||
sShrinkingGCTimer->InitWithFuncCallback(ShrinkingGCTimerFired, nullptr,
|
||||
NS_SHRINKING_GC_DELAY,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
nsJSContext::MaybePokeCC()
|
||||
@ -2091,6 +2135,16 @@ nsJSContext::KillShrinkGCBuffersTimer()
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
void
|
||||
nsJSContext::KillShrinkingGCTimer()
|
||||
{
|
||||
if (sShrinkingGCTimer) {
|
||||
sShrinkingGCTimer->Cancel();
|
||||
NS_RELEASE(sShrinkingGCTimer);
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
void
|
||||
nsJSContext::KillCCTimer()
|
||||
@ -2282,7 +2336,7 @@ void
|
||||
mozilla::dom::StartupJSEnvironment()
|
||||
{
|
||||
// initialize all our statics, so that we can restart XPCOM
|
||||
sGCTimer = sFullGCTimer = sCCTimer = sICCTimer = nullptr;
|
||||
sGCTimer = sShrinkingGCTimer = sFullGCTimer = sCCTimer = sICCTimer = nullptr;
|
||||
sCCLockedOut = false;
|
||||
sCCLockedOutTime = 0;
|
||||
sLastCCEndTime = TimeStamp();
|
||||
@ -2687,8 +2741,14 @@ nsJSContext::EnsureStatics()
|
||||
"javascript.options.gc_on_memory_pressure",
|
||||
true);
|
||||
|
||||
Preferences::AddBoolVarCache(&sCompactOnUserInactive,
|
||||
"javascript.options.compact_on_user_inactive",
|
||||
true);
|
||||
|
||||
nsIObserver* observer = new nsJSEnvironmentObserver();
|
||||
obs->AddObserver(observer, "memory-pressure", false);
|
||||
obs->AddObserver(observer, "user-interaction-inactive", false);
|
||||
obs->AddObserver(observer, "user-interaction-active", false);
|
||||
obs->AddObserver(observer, "quit-application", false);
|
||||
obs->AddObserver(observer, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
||||
|
||||
|
@ -116,6 +116,9 @@ public:
|
||||
static void PokeShrinkGCBuffers();
|
||||
static void KillShrinkGCBuffersTimer();
|
||||
|
||||
static void PokeShrinkingGC();
|
||||
static void KillShrinkingGCTimer();
|
||||
|
||||
static void MaybePokeCC();
|
||||
static void KillCCTimer();
|
||||
static void KillICCTimer();
|
||||
|
@ -497,7 +497,7 @@ CreateInterfaceObject(JSContext* cx, JS::Handle<JSObject*> global,
|
||||
if (constructorClass) {
|
||||
MOZ_ASSERT(constructorProto);
|
||||
constructor = JS_NewObjectWithGivenProto(cx, Jsvalify(constructorClass),
|
||||
constructorProto, global);
|
||||
constructorProto);
|
||||
} else {
|
||||
MOZ_ASSERT(constructorNative);
|
||||
MOZ_ASSERT(constructorProto == JS_GetFunctionPrototype(cx, global));
|
||||
@ -1615,7 +1615,7 @@ XrayOwnPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
obj, flags, props);
|
||||
}
|
||||
|
||||
NativePropertyHooks sWorkerNativePropertyHooks = {
|
||||
NativePropertyHooks sEmptyNativePropertyHooks = {
|
||||
nullptr,
|
||||
nullptr,
|
||||
{
|
||||
@ -1774,17 +1774,19 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
|
||||
JS::Rooted<JSObject*> aObj(aCx, aObjArg);
|
||||
const DOMJSClass* domClass = GetDOMClass(aObj);
|
||||
|
||||
// DOM things are always parented to globals.
|
||||
JS::Rooted<JSObject*> oldParent(aCx, JS_GetParent(aObj));
|
||||
MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(oldParent) == oldParent);
|
||||
|
||||
JS::Rooted<JSObject*> newParent(aCx, domClass->mGetParent(aCx, aObj));
|
||||
MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(newParent) == newParent);
|
||||
|
||||
JSAutoCompartment oldAc(aCx, oldParent);
|
||||
|
||||
JSCompartment* oldCompartment = js::GetObjectCompartment(oldParent);
|
||||
JSCompartment* newCompartment = js::GetObjectCompartment(newParent);
|
||||
if (oldCompartment == newCompartment) {
|
||||
if (!JS_SetParent(aCx, aObj, newParent)) {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
MOZ_ASSERT(oldParent == newParent);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1806,9 +1808,7 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
|
||||
// return early we must avoid ending up with two reflectors pointing to the
|
||||
// same native. Other than that, the objects we create will just go away.
|
||||
|
||||
JS::Rooted<JSObject*> global(aCx,
|
||||
js::GetGlobalForObjectCrossCompartment(newParent));
|
||||
JS::Handle<JSObject*> proto = (domClass->mGetProto)(aCx, global);
|
||||
JS::Handle<JSObject*> proto = (domClass->mGetProto)(aCx, newParent);
|
||||
if (!proto) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -1833,8 +1833,7 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
|
||||
|
||||
JS::Rooted<JSObject*> copyFrom(aCx, isProxy ? expandoObject : aObj);
|
||||
if (copyFrom) {
|
||||
propertyHolder = JS_NewObjectWithGivenProto(aCx, nullptr, JS::NullPtr(),
|
||||
newParent);
|
||||
propertyHolder = JS_NewObjectWithGivenProto(aCx, nullptr, JS::NullPtr());
|
||||
if (!propertyHolder) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
@ -1903,12 +1902,7 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
|
||||
htmlobject->SetupProtoChain(aCx, aObj);
|
||||
}
|
||||
|
||||
// Now we can just fix up the parent and return the wrapper
|
||||
|
||||
if (newParent && !JS_SetParent(aCx, aObj, newParent)) {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
// Now we can just return the wrapper
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -2502,7 +2502,7 @@ XrayGetNativeProto(JSContext* cx, JS::Handle<JSObject*> obj,
|
||||
return JS_WrapObject(cx, protop);
|
||||
}
|
||||
|
||||
extern NativePropertyHooks sWorkerNativePropertyHooks;
|
||||
extern NativePropertyHooks sEmptyNativePropertyHooks;
|
||||
|
||||
// We use one constructor JSNative to represent all DOM interface objects (so
|
||||
// we can easily detect when we need to wrap them in an Xray wrapper). We store
|
||||
@ -2795,15 +2795,14 @@ public:
|
||||
void
|
||||
CreateProxyObject(JSContext* aCx, const js::Class* aClass,
|
||||
const DOMProxyHandler* aHandler,
|
||||
JS::Handle<JSObject*> aProto,
|
||||
JS::Handle<JSObject*> aParent, T* aNative,
|
||||
JS::Handle<JSObject*> aProto, T* aNative,
|
||||
JS::MutableHandle<JSObject*> aReflector)
|
||||
{
|
||||
js::ProxyOptions options;
|
||||
options.setClass(aClass);
|
||||
JS::Rooted<JS::Value> proxyPrivateVal(aCx, JS::PrivateValue(aNative));
|
||||
aReflector.set(js::NewProxyObject(aCx, aHandler, proxyPrivateVal, aProto,
|
||||
aParent, options));
|
||||
/* parent= */nullptr, options));
|
||||
if (aReflector) {
|
||||
mNative = aNative;
|
||||
mReflector = aReflector;
|
||||
@ -2812,10 +2811,10 @@ public:
|
||||
|
||||
void
|
||||
CreateObject(JSContext* aCx, const JSClass* aClass,
|
||||
JS::Handle<JSObject*> aProto, JS::Handle<JSObject*> aParent,
|
||||
JS::Handle<JSObject*> aProto,
|
||||
T* aNative, JS::MutableHandle<JSObject*> aReflector)
|
||||
{
|
||||
aReflector.set(JS_NewObjectWithGivenProto(aCx, aClass, aProto, aParent));
|
||||
aReflector.set(JS_NewObjectWithGivenProto(aCx, aClass, aProto));
|
||||
if (aReflector) {
|
||||
js::SetReservedSlot(aReflector, DOM_OBJECT_SLOT, JS::PrivateValue(aNative));
|
||||
mNative = aNative;
|
||||
|
@ -44,9 +44,13 @@
|
||||
# interfaces. Defaults to true for non-worker non-callback
|
||||
# descriptors.
|
||||
#
|
||||
# The following fields are either a string, an array (defaults to an empty
|
||||
# array) or a dictionary with three possible keys (all, getterOnly and
|
||||
# setterOnly) each having such an array as the value
|
||||
# A non-worker descriptor can have 'wantsXrays': False specified if it
|
||||
# should not have Xray hooks generated. Make sure to have someone
|
||||
# familiar with Xrays review any use of this!
|
||||
#
|
||||
# The following fields are either a string, an array (defaults to an empty
|
||||
# array) or a dictionary with three possible keys (all, getterOnly and
|
||||
# setterOnly) each having such an array as the value
|
||||
#
|
||||
# * implicitJSContext - attributes and methods specified in the .webidl file
|
||||
# that require a JSContext as the first argument
|
||||
|
@ -255,7 +255,7 @@ class CGNativePropertyHooks(CGThing):
|
||||
self.properties = properties
|
||||
|
||||
def declare(self):
|
||||
if self.descriptor.workers:
|
||||
if not self.descriptor.wantsXrays:
|
||||
return ""
|
||||
return dedent("""
|
||||
// We declare this as an array so that retrieving a pointer to this
|
||||
@ -269,7 +269,7 @@ class CGNativePropertyHooks(CGThing):
|
||||
""")
|
||||
|
||||
def define(self):
|
||||
if self.descriptor.workers:
|
||||
if not self.descriptor.wantsXrays:
|
||||
return ""
|
||||
if self.descriptor.concrete and self.descriptor.proxy:
|
||||
resolveOwnProperty = "ResolveOwnProperty"
|
||||
@ -323,7 +323,7 @@ class CGNativePropertyHooks(CGThing):
|
||||
|
||||
|
||||
def NativePropertyHooks(descriptor):
|
||||
return "&sWorkerNativePropertyHooks" if descriptor.workers else "sNativePropertyHooks"
|
||||
return "&sEmptyNativePropertyHooks" if not descriptor.wantsXrays else "sNativePropertyHooks"
|
||||
|
||||
|
||||
def DOMClass(descriptor):
|
||||
@ -523,69 +523,9 @@ def MemberIsUnforgeable(member, descriptor):
|
||||
descriptor.interface.getExtendedAttribute("Unforgeable")))
|
||||
|
||||
|
||||
def UseHolderForUnforgeable(descriptor):
|
||||
return (descriptor.concrete and
|
||||
descriptor.proxy and
|
||||
any(m for m in descriptor.interface.members
|
||||
if MemberIsUnforgeable(m, descriptor)))
|
||||
|
||||
|
||||
def CallOnUnforgeableHolder(descriptor, code, isXrayCheck=None,
|
||||
useSharedRoot=False):
|
||||
"""
|
||||
Generate the code to execute the code in "code" on an unforgeable holder if
|
||||
needed. code should be a string containing the code to execute. If it
|
||||
contains a ${holder} string parameter it will be replaced with the
|
||||
unforgeable holder object.
|
||||
|
||||
If isXrayCheck is not None it should be a string that contains a statement
|
||||
returning whether proxy is an Xray. If isXrayCheck is None the generated
|
||||
code won't try to unwrap Xrays.
|
||||
|
||||
If useSharedRoot is true, we will use an existing
|
||||
JS::Rooted<JSObject*> sharedRoot for storing our unforgeable holder instead
|
||||
of declaring a new Rooted.
|
||||
"""
|
||||
if isXrayCheck is not None:
|
||||
pre = fill(
|
||||
"""
|
||||
// Scope for 'global', 'ac' and 'unforgeableHolder'
|
||||
{
|
||||
JS::Rooted<JSObject*> global(cx);
|
||||
Maybe<JSAutoCompartment> ac;
|
||||
if (${isXrayCheck}) {
|
||||
global = js::GetGlobalForObjectCrossCompartment(js::UncheckedUnwrap(proxy));
|
||||
ac.emplace(cx, global);
|
||||
} else {
|
||||
global = js::GetGlobalForObjectCrossCompartment(proxy);
|
||||
}
|
||||
""",
|
||||
isXrayCheck=isXrayCheck)
|
||||
else:
|
||||
pre = dedent("""
|
||||
// Scope for 'global' and 'unforgeableHolder'
|
||||
{
|
||||
JSObject* global = js::GetGlobalForObjectCrossCompartment(proxy);
|
||||
""")
|
||||
|
||||
if useSharedRoot:
|
||||
holderDecl = "JS::Rooted<JSObject*>& unforgeableHolder(sharedRoot);\n"
|
||||
else:
|
||||
holderDecl = "JS::Rooted<JSObject*> unforgeableHolder(cx);\n"
|
||||
|
||||
code = string.Template(code).substitute({"holder": "unforgeableHolder"})
|
||||
return fill(
|
||||
"""
|
||||
$*{pre}
|
||||
$*{holderDecl}
|
||||
unforgeableHolder = GetUnforgeableHolder(global, prototypes::id::${name});
|
||||
$*{code}
|
||||
}
|
||||
""",
|
||||
pre=pre,
|
||||
holderDecl=holderDecl,
|
||||
name=descriptor.name,
|
||||
code=code)
|
||||
def HasUnforgeableMembers(descriptor):
|
||||
return any(MemberIsUnforgeable(m, descriptor) for m in
|
||||
descriptor.interface.members)
|
||||
|
||||
|
||||
def InterfacePrototypeObjectProtoGetter(descriptor):
|
||||
@ -631,8 +571,6 @@ class CGPrototypeJSClass(CGThing):
|
||||
def define(self):
|
||||
prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
|
||||
slotCount = "DOM_INTERFACE_PROTO_SLOTS_BASE"
|
||||
if UseHolderForUnforgeable(self.descriptor):
|
||||
slotCount += " + 1 /* slot for the JSObject holding the unforgeable properties */"
|
||||
(protoGetter, _) = InterfacePrototypeObjectProtoGetter(self.descriptor)
|
||||
type = "eGlobalInterfacePrototype" if self.descriptor.isGlobal() else "eInterfacePrototype"
|
||||
return fill(
|
||||
@ -1958,8 +1896,7 @@ class PropertyDefiner:
|
||||
return "nullptr"
|
||||
|
||||
def usedForXrays(self):
|
||||
# No Xrays in workers.
|
||||
return not self.descriptor.workers
|
||||
return self.descriptor.wantsXrays
|
||||
|
||||
def __str__(self):
|
||||
# We only need to generate id arrays for things that will end
|
||||
@ -2619,9 +2556,8 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
||||
assert needInterfaceObject or needInterfacePrototypeObject
|
||||
|
||||
idsToInit = []
|
||||
# There is no need to init any IDs in workers, because worker bindings
|
||||
# don't have Xrays.
|
||||
if not self.descriptor.workers:
|
||||
# There is no need to init any IDs in bindings that don't want Xrays.
|
||||
if self.descriptor.wantsXrays:
|
||||
for var in self.properties.arrayNames():
|
||||
props = getattr(self.properties, var)
|
||||
# We only have non-chrome ids to init if we have no chrome ids.
|
||||
@ -2658,22 +2594,6 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
||||
else:
|
||||
prefCache = None
|
||||
|
||||
if UseHolderForUnforgeable(self.descriptor):
|
||||
createUnforgeableHolder = CGGeneric(dedent("""
|
||||
JS::Rooted<JSObject*> unforgeableHolder(aCx,
|
||||
JS_NewObjectWithGivenProto(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
|
||||
if (!unforgeableHolder) {
|
||||
return;
|
||||
}
|
||||
"""))
|
||||
defineUnforgeables = InitUnforgeablePropertiesOnObject(self.descriptor,
|
||||
"unforgeableHolder",
|
||||
self.properties)
|
||||
createUnforgeableHolder = CGList(
|
||||
[createUnforgeableHolder, defineUnforgeables])
|
||||
else:
|
||||
createUnforgeableHolder = None
|
||||
|
||||
getParentProto = fill(
|
||||
"""
|
||||
JS::${type}<JSObject*> parentProto(${getParentProto});
|
||||
@ -2758,22 +2678,9 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
||||
chromeProperties=chromeProperties,
|
||||
name='"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "nullptr")
|
||||
|
||||
if UseHolderForUnforgeable(self.descriptor):
|
||||
assert needInterfacePrototypeObject
|
||||
setUnforgeableHolder = CGGeneric(fill(
|
||||
"""
|
||||
JSObject* proto = aProtoAndIfaceCache.EntrySlotOrCreate(prototypes::id::${name});
|
||||
if (proto) {
|
||||
js::SetReservedSlot(proto, DOM_INTERFACE_PROTO_SLOTS_BASE,
|
||||
JS::ObjectValue(*unforgeableHolder));
|
||||
}
|
||||
""",
|
||||
name=self.descriptor.name))
|
||||
else:
|
||||
setUnforgeableHolder = None
|
||||
return CGList(
|
||||
[CGGeneric(getParentProto), CGGeneric(getConstructorProto), initIds,
|
||||
prefCache, createUnforgeableHolder, CGGeneric(call), setUnforgeableHolder],
|
||||
prefCache, CGGeneric(call)],
|
||||
"\n").define()
|
||||
|
||||
|
||||
@ -3053,7 +2960,7 @@ def CreateBindingJSObject(descriptor, properties):
|
||||
create = dedent(
|
||||
"""
|
||||
creator.CreateProxyObject(aCx, &Class.mBase, DOMProxyHandler::getInstance(),
|
||||
proto, global, aObject, aReflector);
|
||||
proto, aObject, aReflector);
|
||||
if (!aReflector) {
|
||||
return false;
|
||||
}
|
||||
@ -3068,7 +2975,7 @@ def CreateBindingJSObject(descriptor, properties):
|
||||
else:
|
||||
create = dedent(
|
||||
"""
|
||||
creator.CreateObject(aCx, Class.ToJSClass(), proto, global, aObject, aReflector);
|
||||
creator.CreateObject(aCx, Class.ToJSClass(), proto, aObject, aReflector);
|
||||
if (!aReflector) {
|
||||
return false;
|
||||
}
|
||||
@ -3076,33 +2983,68 @@ def CreateBindingJSObject(descriptor, properties):
|
||||
return objDecl + create
|
||||
|
||||
|
||||
def InitUnforgeablePropertiesOnObject(descriptor, obj, properties, failureReturnValue=""):
|
||||
def InitUnforgeableProperties(descriptor, properties, wrapperCache):
|
||||
"""
|
||||
properties is a PropertyArrays instance
|
||||
"""
|
||||
unforgeables = []
|
||||
unforgeableAttrs = properties.unforgeableAttrs
|
||||
if not unforgeableAttrs.hasNonChromeOnly() and not unforgeableAttrs.hasChromeOnly():
|
||||
return ""
|
||||
|
||||
if failureReturnValue:
|
||||
failureReturnValue = " " + failureReturnValue
|
||||
unforgeables = [
|
||||
CGGeneric(dedent(
|
||||
"""
|
||||
// Important: do unforgeable property setup after we have handed
|
||||
// over ownership of the C++ object to obj as needed, so that if
|
||||
// we fail and it ends up GCed it won't have problems in the
|
||||
// finalizer trying to drop its ownership of the C++ object.
|
||||
"""))
|
||||
]
|
||||
|
||||
if wrapperCache:
|
||||
cleanup = dedent(
|
||||
"""
|
||||
aCache->ReleaseWrapper(aObject);
|
||||
aCache->ClearWrapper();
|
||||
""")
|
||||
else:
|
||||
failureReturnValue = ""
|
||||
cleanup = ""
|
||||
|
||||
# For proxies, we want to define on the expando object, not directly on the
|
||||
# reflector, so we can make sure we don't get confused by named getters.
|
||||
if descriptor.proxy:
|
||||
unforgeables.append(CGGeneric(fill(
|
||||
"""
|
||||
JS::Rooted<JSObject*> expando(aCx,
|
||||
DOMProxyHandler::EnsureExpandoObject(aCx, aReflector));
|
||||
if (!expando) {
|
||||
$*{cleanup}
|
||||
return false;
|
||||
}
|
||||
""",
|
||||
cleanup=cleanup)))
|
||||
obj = "expando"
|
||||
else:
|
||||
obj = "aReflector"
|
||||
|
||||
defineUnforgeableAttrs = fill(
|
||||
"""
|
||||
if (!DefineUnforgeableAttributes(aCx, ${obj}, %s)) {
|
||||
return${rv};
|
||||
$*{cleanup}
|
||||
return false;
|
||||
}
|
||||
""",
|
||||
obj=obj,
|
||||
rv=failureReturnValue)
|
||||
cleanup=cleanup)
|
||||
defineUnforgeableMethods = fill(
|
||||
"""
|
||||
if (!DefineUnforgeableMethods(aCx, ${obj}, %s)) {
|
||||
return${rv};
|
||||
$*{cleanup}
|
||||
return false;
|
||||
}
|
||||
""",
|
||||
obj=obj,
|
||||
rv=failureReturnValue)
|
||||
cleanup=cleanup)
|
||||
|
||||
unforgeableMembers = [
|
||||
(defineUnforgeableAttrs, properties.unforgeableAttrs),
|
||||
@ -3123,36 +3065,14 @@ def InitUnforgeablePropertiesOnObject(descriptor, obj, properties, failureReturn
|
||||
"""
|
||||
if (!JS_DefineProperty(aCx, ${obj}, "toJSON", JS::UndefinedHandleValue,
|
||||
JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
|
||||
return${rv};
|
||||
$*{cleanup}
|
||||
return false;
|
||||
}
|
||||
""",
|
||||
obj=obj,
|
||||
rv=failureReturnValue)))
|
||||
cleanup=cleanup)))
|
||||
|
||||
return CGList(unforgeables)
|
||||
|
||||
|
||||
def InitUnforgeableProperties(descriptor, properties):
|
||||
"""
|
||||
properties is a PropertyArrays instance
|
||||
"""
|
||||
unforgeableAttrs = properties.unforgeableAttrs
|
||||
if not unforgeableAttrs.hasNonChromeOnly() and not unforgeableAttrs.hasChromeOnly():
|
||||
return ""
|
||||
|
||||
if descriptor.proxy:
|
||||
unforgeableProperties = CGGeneric(
|
||||
"// Unforgeable properties on proxy-based bindings are stored in an object held\n"
|
||||
"// by the interface prototype object.\n")
|
||||
else:
|
||||
unforgeableProperties = CGWrapper(
|
||||
InitUnforgeablePropertiesOnObject(descriptor, "aReflector", properties, "false"),
|
||||
pre=(
|
||||
"// Important: do unforgeable property setup after we have handed\n"
|
||||
"// over ownership of the C++ object to obj as needed, so that if\n"
|
||||
"// we fail and it ends up GCed it won't have problems in the\n"
|
||||
"// finalizer trying to drop its ownership of the C++ object.\n"))
|
||||
return CGWrapper(unforgeableProperties, pre="\n").define()
|
||||
return CGWrapper(CGList(unforgeables), pre="\n").define()
|
||||
|
||||
|
||||
def AssertInheritanceChain(descriptor):
|
||||
@ -3181,13 +3101,21 @@ def InitMemberSlots(descriptor, wrapperCache):
|
||||
if not descriptor.interface.hasMembersInSlots():
|
||||
return ""
|
||||
if wrapperCache:
|
||||
clearWrapper = " aCache->ClearWrapper();\n"
|
||||
clearWrapper = dedent(
|
||||
"""
|
||||
aCache->ReleaseWrapper(aObject);
|
||||
aCache->ClearWrapper();
|
||||
""")
|
||||
else:
|
||||
clearWrapper = ""
|
||||
return ("if (!UpdateMemberSlots(aCx, aReflector, aObject)) {\n"
|
||||
"%s"
|
||||
" return false;\n"
|
||||
"}\n" % clearWrapper)
|
||||
return fill(
|
||||
"""
|
||||
if (!UpdateMemberSlots(aCx, aReflector, aObject)) {
|
||||
$*{clearWrapper}
|
||||
return false;
|
||||
}
|
||||
""",
|
||||
clearWrapper=clearWrapper)
|
||||
|
||||
|
||||
class CGWrapWithCacheMethod(CGAbstractMethod):
|
||||
@ -3206,6 +3134,16 @@ class CGWrapWithCacheMethod(CGAbstractMethod):
|
||||
self.properties = properties
|
||||
|
||||
def definition_body(self):
|
||||
# For proxies, we have to SetWrapper() before we init unforgeables. But
|
||||
# for non-proxies we'd rather do it the other way around, so our
|
||||
# unforgeables don't force preservation of the wrapper.
|
||||
setWrapper = "aCache->SetWrapper(aReflector);\n"
|
||||
if self.descriptor.proxy:
|
||||
setWrapperProxy = setWrapper
|
||||
setWrapperNonProxy = ""
|
||||
else:
|
||||
setWrapperProxy = ""
|
||||
setWrapperNonProxy = setWrapper
|
||||
return fill(
|
||||
"""
|
||||
$*{assertion}
|
||||
@ -3234,16 +3172,18 @@ class CGWrapWithCacheMethod(CGAbstractMethod):
|
||||
|
||||
$*{createObject}
|
||||
|
||||
$*{setWrapperProxy}
|
||||
$*{unforgeable}
|
||||
|
||||
aCache->SetWrapper(aReflector);
|
||||
$*{setWrapperNonProxy}
|
||||
$*{slots}
|
||||
creator.InitializationSucceeded();
|
||||
return true;
|
||||
""",
|
||||
assertion=AssertInheritanceChain(self.descriptor),
|
||||
createObject=CreateBindingJSObject(self.descriptor, self.properties),
|
||||
unforgeable=InitUnforgeableProperties(self.descriptor, self.properties),
|
||||
setWrapperProxy=setWrapperProxy,
|
||||
unforgeable=InitUnforgeableProperties(self.descriptor, self.properties, True),
|
||||
setWrapperNonProxy=setWrapperNonProxy,
|
||||
slots=InitMemberSlots(self.descriptor, True))
|
||||
|
||||
|
||||
@ -3300,7 +3240,7 @@ class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
|
||||
""",
|
||||
assertions=AssertInheritanceChain(self.descriptor),
|
||||
createObject=CreateBindingJSObject(self.descriptor, self.properties),
|
||||
unforgeable=InitUnforgeableProperties(self.descriptor, self.properties),
|
||||
unforgeable=InitUnforgeableProperties(self.descriptor, self.properties, False),
|
||||
slots=InitMemberSlots(self.descriptor, False))
|
||||
|
||||
|
||||
@ -3377,7 +3317,7 @@ class CGWrapGlobalMethod(CGAbstractMethod):
|
||||
nativeType=self.descriptor.nativeType,
|
||||
properties=properties,
|
||||
chromeProperties=chromeProperties,
|
||||
unforgeable=InitUnforgeableProperties(self.descriptor, self.properties),
|
||||
unforgeable=InitUnforgeableProperties(self.descriptor, self.properties, True),
|
||||
slots=InitMemberSlots(self.descriptor, True),
|
||||
fireOnNewGlobal=fireOnNewGlobal)
|
||||
|
||||
@ -10079,31 +10019,6 @@ class CGDOMJSProxyHandler_getOwnPropDescriptor(ClassMethod):
|
||||
else:
|
||||
getIndexed = ""
|
||||
|
||||
if UseHolderForUnforgeable(self.descriptor):
|
||||
tryHolder = dedent("""
|
||||
if (!JS_GetPropertyDescriptorById(cx, ${holder}, id, desc)) {
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT_IF(desc.object(), desc.object() == ${holder});
|
||||
""")
|
||||
|
||||
# We don't want to look at the unforgeable holder at all
|
||||
# in the xray case; that part got handled already.
|
||||
getUnforgeable = fill(
|
||||
"""
|
||||
if (!isXray) {
|
||||
$*{callOnUnforgeable}
|
||||
if (desc.object()) {
|
||||
desc.object().set(proxy);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
""",
|
||||
callOnUnforgeable=CallOnUnforgeableHolder(self.descriptor, tryHolder))
|
||||
else:
|
||||
getUnforgeable = ""
|
||||
|
||||
if self.descriptor.supportsNamedProperties():
|
||||
operations = self.descriptor.operations
|
||||
readonly = toStringBool(operations['NamedSetter'] is None)
|
||||
@ -10160,7 +10075,6 @@ class CGDOMJSProxyHandler_getOwnPropDescriptor(ClassMethod):
|
||||
"""
|
||||
bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);
|
||||
$*{getIndexed}
|
||||
$*{getUnforgeable}
|
||||
JS::Rooted<JSObject*> expando(cx);
|
||||
if (!isXray && (expando = GetExpandoObject(proxy))) {
|
||||
if (!JS_GetPropertyDescriptorById(cx, expando, id, desc)) {
|
||||
@ -10178,7 +10092,6 @@ class CGDOMJSProxyHandler_getOwnPropDescriptor(ClassMethod):
|
||||
return true;
|
||||
""",
|
||||
getIndexed=getIndexed,
|
||||
getUnforgeable=getUnforgeable,
|
||||
namedGet=namedGet)
|
||||
|
||||
|
||||
@ -10223,25 +10136,12 @@ class CGDOMJSProxyHandler_defineProperty(ClassMethod):
|
||||
""",
|
||||
name=self.descriptor.name)
|
||||
|
||||
if UseHolderForUnforgeable(self.descriptor):
|
||||
# It's OK to do the defineOnUnforgeable thing even in the Xray case,
|
||||
# since in practice it won't let us change anything; we just want
|
||||
# the js_DefineOwnProperty call for error reporting purposes.
|
||||
defineOnUnforgeable = ("bool hasUnforgeable;\n"
|
||||
"if (!JS_HasPropertyById(cx, ${holder}, id, &hasUnforgeable)) {\n"
|
||||
" return false;\n"
|
||||
"}\n"
|
||||
"if (hasUnforgeable) {\n"
|
||||
" *defined = true;\n"
|
||||
" bool unused;\n"
|
||||
" return js_DefineOwnProperty(cx, ${holder}, id, desc, &unused);\n"
|
||||
"}\n")
|
||||
set += CallOnUnforgeableHolder(self.descriptor,
|
||||
defineOnUnforgeable,
|
||||
"xpc::WrapperFactory::IsXrayWrapper(proxy)")
|
||||
|
||||
namedSetter = self.descriptor.operations['NamedSetter']
|
||||
if namedSetter:
|
||||
if HasUnforgeableMembers(self.descriptor):
|
||||
raise TypeError("Can't handle a named setter on an interface "
|
||||
"that has unforgeables. Figure out how that "
|
||||
"should work!")
|
||||
if self.descriptor.operations['NamedCreator'] is not namedSetter:
|
||||
raise TypeError("Can't handle creator that's different from the setter")
|
||||
# If we support indexed properties, we won't get down here for
|
||||
@ -10295,6 +10195,10 @@ class CGDOMJSProxyHandler_delete(ClassMethod):
|
||||
assert type in ("Named", "Indexed")
|
||||
deleter = self.descriptor.operations[type + 'Deleter']
|
||||
if deleter:
|
||||
if HasUnforgeableMembers(self.descriptor):
|
||||
raise TypeError("Can't handle a deleter on an interface "
|
||||
"that has unforgeables. Figure out how "
|
||||
"that should work!")
|
||||
decls = ""
|
||||
if (not deleter.signatures()[0][0].isPrimitive() or
|
||||
deleter.signatures()[0][0].nullable() or
|
||||
@ -10356,20 +10260,6 @@ class CGDOMJSProxyHandler_delete(ClassMethod):
|
||||
""",
|
||||
indexedBody=indexedBody)
|
||||
|
||||
if UseHolderForUnforgeable(self.descriptor):
|
||||
unforgeable = dedent("""
|
||||
bool hasUnforgeable;
|
||||
if (!JS_HasPropertyById(cx, ${holder}, id, &hasUnforgeable)) {
|
||||
return false;
|
||||
}
|
||||
if (hasUnforgeable) {
|
||||
*bp = false;
|
||||
return true;
|
||||
}
|
||||
""")
|
||||
delete += CallOnUnforgeableHolder(self.descriptor, unforgeable)
|
||||
delete += "\n"
|
||||
|
||||
namedBody = getDeleterBody("Named", foundVar="found")
|
||||
if namedBody is not None:
|
||||
# We always return above for an index id in the case when we support
|
||||
@ -10430,18 +10320,6 @@ class CGDOMJSProxyHandler_ownPropNames(ClassMethod):
|
||||
else:
|
||||
addIndices = ""
|
||||
|
||||
if UseHolderForUnforgeable(self.descriptor):
|
||||
addUnforgeable = dedent("""
|
||||
if (!js::GetPropertyKeys(cx, ${holder}, flags, &props)) {
|
||||
return false;
|
||||
}
|
||||
""")
|
||||
addUnforgeable = CallOnUnforgeableHolder(self.descriptor,
|
||||
addUnforgeable,
|
||||
"isXray")
|
||||
else:
|
||||
addUnforgeable = ""
|
||||
|
||||
if self.descriptor.supportsNamedProperties():
|
||||
if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
|
||||
shadow = "!isXray"
|
||||
@ -10464,7 +10342,6 @@ class CGDOMJSProxyHandler_ownPropNames(ClassMethod):
|
||||
"""
|
||||
bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);
|
||||
$*{addIndices}
|
||||
$*{addUnforgeable}
|
||||
$*{addNames}
|
||||
|
||||
JS::Rooted<JSObject*> expando(cx);
|
||||
@ -10476,7 +10353,6 @@ class CGDOMJSProxyHandler_ownPropNames(ClassMethod):
|
||||
return true;
|
||||
""",
|
||||
addIndices=addIndices,
|
||||
addUnforgeable=addUnforgeable,
|
||||
addNames=addNames)
|
||||
|
||||
|
||||
@ -10508,19 +10384,6 @@ class CGDOMJSProxyHandler_hasOwn(ClassMethod):
|
||||
else:
|
||||
indexed = ""
|
||||
|
||||
if UseHolderForUnforgeable(self.descriptor):
|
||||
unforgeable = dedent("""
|
||||
bool b = true;
|
||||
bool ok = JS_AlreadyHasOwnPropertyById(cx, ${holder}, id, &b);
|
||||
*bp = !!b;
|
||||
if (!ok || *bp) {
|
||||
return ok;
|
||||
}
|
||||
""")
|
||||
unforgeable = CallOnUnforgeableHolder(self.descriptor, unforgeable)
|
||||
else:
|
||||
unforgeable = ""
|
||||
|
||||
if self.descriptor.supportsNamedProperties():
|
||||
# If we support indexed properties we always return above for index
|
||||
# property names, so no need to check for those here.
|
||||
@ -10556,7 +10419,6 @@ class CGDOMJSProxyHandler_hasOwn(ClassMethod):
|
||||
"Should not have a XrayWrapper here");
|
||||
|
||||
$*{indexed}
|
||||
$*{unforgeable}
|
||||
|
||||
JS::Rooted<JSObject*> expando(cx, GetExpandoObject(proxy));
|
||||
if (expando) {
|
||||
@ -10572,7 +10434,6 @@ class CGDOMJSProxyHandler_hasOwn(ClassMethod):
|
||||
return true;
|
||||
""",
|
||||
indexed=indexed,
|
||||
unforgeable=unforgeable,
|
||||
named=named)
|
||||
|
||||
|
||||
@ -10588,24 +10449,9 @@ class CGDOMJSProxyHandler_get(ClassMethod):
|
||||
self.descriptor = descriptor
|
||||
|
||||
def getBody(self):
|
||||
getUnforgeableOrExpando = "JS::Rooted<JSObject*> sharedRoot(cx);\n"
|
||||
if UseHolderForUnforgeable(self.descriptor):
|
||||
hasUnforgeable = dedent("""
|
||||
bool hasUnforgeable;
|
||||
if (!JS_AlreadyHasOwnPropertyById(cx, ${holder}, id, &hasUnforgeable)) {
|
||||
return false;
|
||||
}
|
||||
if (hasUnforgeable) {
|
||||
return JS_ForwardGetPropertyTo(cx, ${holder}, id, proxy, vp);
|
||||
}
|
||||
""")
|
||||
getUnforgeableOrExpando += CallOnUnforgeableHolder(self.descriptor,
|
||||
hasUnforgeable,
|
||||
useSharedRoot=True)
|
||||
getUnforgeableOrExpando += dedent("""
|
||||
getUnforgeableOrExpando = dedent("""
|
||||
{ // Scope for expando
|
||||
JS::Rooted<JSObject*>& expando(sharedRoot);
|
||||
expando = DOMProxyHandler::GetExpandoObject(proxy);
|
||||
JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy));
|
||||
if (expando) {
|
||||
bool hasProp;
|
||||
if (!JS_HasPropertyById(cx, expando, id, &hasProp)) {
|
||||
@ -10705,7 +10551,7 @@ class CGDOMJSProxyHandler_setCustom(ClassMethod):
|
||||
if self.descriptor.operations['NamedCreator'] is not namedSetter:
|
||||
raise ValueError("In interface " + self.descriptor.name + ": " +
|
||||
"Can't cope with named setter that is not also a named creator")
|
||||
if UseHolderForUnforgeable(self.descriptor):
|
||||
if HasUnforgeableMembers(self.descriptor):
|
||||
raise ValueError("In interface " + self.descriptor.name + ": " +
|
||||
"Can't cope with [OverrideBuiltins] and unforgeable members")
|
||||
|
||||
@ -10775,7 +10621,8 @@ class CGDOMJSProxyHandler_finalize(ClassMethod):
|
||||
self.descriptor = descriptor
|
||||
|
||||
def getBody(self):
|
||||
return ("%s* self = UnwrapProxy(proxy);\n\n" % self.descriptor.nativeType +
|
||||
return (("%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(proxy);\n" %
|
||||
(self.descriptor.nativeType, self.descriptor.nativeType)) +
|
||||
finalizeHook(self.descriptor, FINALIZE_HOOK_NAME, self.args[0].name).define())
|
||||
|
||||
|
||||
@ -11165,14 +11012,14 @@ class CGDescriptor(CGThing):
|
||||
cgThings.append(CGGeneric(define=str(properties)))
|
||||
cgThings.append(CGNativeProperties(descriptor, properties))
|
||||
|
||||
# Set up our Xray callbacks as needed. Note that we don't need to do
|
||||
# it in workers.
|
||||
if not descriptor.workers and descriptor.concrete and descriptor.proxy:
|
||||
cgThings.append(CGResolveOwnProperty(descriptor))
|
||||
cgThings.append(CGEnumerateOwnProperties(descriptor))
|
||||
elif descriptor.needsXrayResolveHooks():
|
||||
cgThings.append(CGResolveOwnPropertyViaResolve(descriptor))
|
||||
cgThings.append(CGEnumerateOwnPropertiesViaGetOwnPropertyNames(descriptor))
|
||||
# Set up our Xray callbacks as needed.
|
||||
if descriptor.wantsXrays:
|
||||
if descriptor.concrete and descriptor.proxy:
|
||||
cgThings.append(CGResolveOwnProperty(descriptor))
|
||||
cgThings.append(CGEnumerateOwnProperties(descriptor))
|
||||
elif descriptor.needsXrayResolveHooks():
|
||||
cgThings.append(CGResolveOwnPropertyViaResolve(descriptor))
|
||||
cgThings.append(CGEnumerateOwnPropertiesViaGetOwnPropertyNames(descriptor))
|
||||
|
||||
# Now that we have our ResolveOwnProperty/EnumerateOwnProperties stuff
|
||||
# done, set up our NativePropertyHooks.
|
||||
|
@ -302,6 +302,12 @@ class Descriptor(DescriptorProvider):
|
||||
DescriptorProvider.__init__(self, config, desc.get('workers', False))
|
||||
self.interface = interface
|
||||
|
||||
if self.workers:
|
||||
assert 'wantsXrays' not in desc
|
||||
self.wantsXrays = False
|
||||
else:
|
||||
self.wantsXrays = desc.get('wantsXrays', True)
|
||||
|
||||
# Read the desc, and fill in the relevant defaults.
|
||||
ifaceName = self.interface.identifier.name
|
||||
if self.interface.isExternal():
|
||||
|
@ -33,17 +33,22 @@ const char DOMProxyHandler::family = 0;
|
||||
js::DOMProxyShadowsResult
|
||||
DOMProxyShadows(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id)
|
||||
{
|
||||
JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy));
|
||||
JS::Value v = js::GetProxyExtra(proxy, JSPROXYSLOT_EXPANDO);
|
||||
if (v.isObject()) {
|
||||
bool isOverrideBuiltins = !v.isObject() && !v.isUndefined();
|
||||
if (expando) {
|
||||
bool hasOwn;
|
||||
Rooted<JSObject*> object(cx, &v.toObject());
|
||||
if (!JS_AlreadyHasOwnPropertyById(cx, object, id, &hasOwn))
|
||||
if (!JS_AlreadyHasOwnPropertyById(cx, expando, id, &hasOwn))
|
||||
return js::ShadowCheckFailed;
|
||||
|
||||
return hasOwn ? js::Shadows : js::DoesntShadow;
|
||||
if (hasOwn) {
|
||||
return isOverrideBuiltins ?
|
||||
js::ShadowsViaIndirectExpando : js::ShadowsViaDirectExpando;
|
||||
}
|
||||
}
|
||||
|
||||
if (v.isUndefined()) {
|
||||
if (!isOverrideBuiltins) {
|
||||
// Our expando, if any, didn't shadow, so we're not shadowing at all.
|
||||
return js::DoesntShadow;
|
||||
}
|
||||
|
||||
|
@ -26,8 +26,7 @@
|
||||
#define DOM_INTERFACE_SLOTS_BASE 0
|
||||
|
||||
// Interface prototype objects store a number of reserved slots equal to
|
||||
// DOM_INTERFACE_PROTO_SLOTS_BASE or DOM_INTERFACE_PROTO_SLOTS_BASE + 1 if a
|
||||
// slot for the unforgeable holder is needed.
|
||||
// DOM_INTERFACE_PROTO_SLOTS_BASE.
|
||||
#define DOM_INTERFACE_PROTO_SLOTS_BASE 0
|
||||
|
||||
#endif /* mozilla_dom_DOMSlots_h */
|
||||
|
@ -58,4 +58,5 @@ skip-if = debug == false
|
||||
[test_promise_rejections_from_jsimplemented.html]
|
||||
skip-if = debug == false
|
||||
[test_worker_UnwrapArg.html]
|
||||
[test_unforgeablesonexpando.html]
|
||||
[test_crossOriginWindowSymbolAccess.html]
|
||||
|
18
dom/bindings/test/test_unforgeablesonexpando.html
Normal file
18
dom/bindings/test/test_unforgeablesonexpando.html
Normal file
@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<title>Test for making sure named getters don't override the unforgeable location on HTMLDocument</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<img name="location">
|
||||
<script>
|
||||
test(function() {
|
||||
assert_equals(document.location, window.location,
|
||||
'The <img name="location"> should not override the location getter');
|
||||
}, "document.location is the right thing");
|
||||
test(function() {
|
||||
var doc = new DOMParser().parseFromString("<img name='location'>", "text/html");
|
||||
assert_equals(doc.location, null,
|
||||
'The <img name="location"> should not override the location getter on a data document');
|
||||
}, "document.location is the right thing on non-rendered document");
|
||||
</script>
|
@ -1,10 +1,10 @@
|
||||
# This is a GENERATED FILE. Do not edit it directly.
|
||||
# Regenerated it by using `python generate-wrapper-and-manifest.py`.
|
||||
# Regenerated it by using `python generate-wrappers-and-manifest.py`.
|
||||
# Mark skipped tests in mochitest-errata.ini.
|
||||
# Mark failing tests in mochi-single.html.
|
||||
|
||||
[DEFAULT]
|
||||
skip-if = e10s || os == 'b2g' || ((os == 'linux') && (buildapp == 'b2g'))
|
||||
skip-if = e10s || os == 'b2g' || ((os == 'linux') && (buildapp == 'b2g')) || ((os == 'linux') && (buildapp == 'mulet')) # Bug 1136181 disabled on B2G Desktop and Mulet for intermittent failures
|
||||
|
||||
support-files = webgl-conformance/../webgl-mochitest/driver-info.js
|
||||
webgl-conformance/always-fail.html
|
||||
|
@ -36,7 +36,7 @@ ACCEPTABLE_ERRATA_KEYS = set([
|
||||
|
||||
GENERATED_HEADER = '''
|
||||
# This is a GENERATED FILE. Do not edit it directly.
|
||||
# Regenerated it by using `python generate-wrapper-and-manifest.py`.
|
||||
# Regenerated it by using `python generate-wrappers-and-manifest.py`.
|
||||
# Mark skipped tests in mochitest-errata.ini.
|
||||
# Mark failing tests in mochi-single.html.
|
||||
'''.strip()
|
||||
|
@ -5,7 +5,8 @@
|
||||
# 'B2G Desktop Linux' fails to create WebGL contexts.
|
||||
# Also skip B2G for now, until we get a handle on the longer tail of emulator
|
||||
# bugs.
|
||||
skip-if = e10s || os == 'b2g' || ((os == 'linux') && (buildapp == 'b2g'))
|
||||
# Bug 1136181 disabled on B2G Desktop and Mulet for intermittent failures
|
||||
skip-if = e10s || os == 'b2g' || ((os == 'linux') && (buildapp == 'b2g')) || ((os == 'linux') && (buildapp == 'mulet'))
|
||||
|
||||
########################################################################
|
||||
# All
|
||||
|
@ -31,7 +31,6 @@ using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
|
||||
using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
|
||||
using struct mozilla::layers::ZoomConstraints from "FrameMetrics.h";
|
||||
using FrameMetrics::ViewID from "FrameMetrics.h";
|
||||
using mozilla::layout::ScrollingBehavior from "mozilla/layout/RenderFrameUtils.h";
|
||||
using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
|
||||
using mozilla::WindowsHandle from "ipc/IPCMessageUtils.h";
|
||||
using nscolor from "nsColor.h";
|
||||
@ -474,8 +473,7 @@ parent:
|
||||
async RemotePaintIsReady();
|
||||
|
||||
sync GetRenderFrameInfo(PRenderFrame aRenderFrame)
|
||||
returns (ScrollingBehavior scrolling,
|
||||
TextureFactoryIdentifier textureFactoryIdentifier,
|
||||
returns (TextureFactoryIdentifier textureFactoryIdentifier,
|
||||
uint64_t layersId);
|
||||
|
||||
/**
|
||||
@ -496,7 +494,6 @@ child:
|
||||
*/
|
||||
Show(nsIntSize size,
|
||||
ShowInfo info,
|
||||
ScrollingBehavior scrolling,
|
||||
TextureFactoryIdentifier textureFactoryIdentifier,
|
||||
uint64_t layersId,
|
||||
nullable PRenderFrame renderFrame,
|
||||
|
@ -7,8 +7,6 @@
|
||||
include protocol PBrowser;
|
||||
include PBrowserOrId;
|
||||
|
||||
using mozilla::layout::ScrollingBehavior from "mozilla/layout/RenderFrameUtils.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
@ -70,7 +68,6 @@ union IPCTabAppBrowserContext
|
||||
|
||||
struct IPCTabContext {
|
||||
IPCTabAppBrowserContext appBrowserContext;
|
||||
ScrollingBehavior scrollingBehavior;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -267,7 +267,7 @@ CSSToParentLayerScale ConvertScaleForRoot(CSSToScreenScale aScale) {
|
||||
bool
|
||||
TabChildBase::HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize)
|
||||
{
|
||||
if (!IsAsyncPanZoomEnabled()) {
|
||||
if (!gfxPrefs::AsyncPanZoomEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -556,12 +556,6 @@ TabChildBase::ProcessUpdateFrame(const FrameMetrics& aFrameMetrics)
|
||||
return newMetrics;
|
||||
}
|
||||
|
||||
bool
|
||||
TabChildBase::IsAsyncPanZoomEnabled()
|
||||
{
|
||||
return mScrolling == ASYNC_PAN_ZOOM;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContentListener::HandleEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
@ -907,7 +901,7 @@ TabChild::Observe(nsISupports *aSubject,
|
||||
}
|
||||
}
|
||||
} else if (!strcmp(aTopic, BEFORE_FIRST_PAINT)) {
|
||||
if (IsAsyncPanZoomEnabled()) {
|
||||
if (gfxPrefs::AsyncPanZoomEnabled()) {
|
||||
nsCOMPtr<nsIDocument> subject(do_QueryInterface(aSubject));
|
||||
nsCOMPtr<nsIDocument> doc(GetDocument());
|
||||
|
||||
@ -962,7 +956,7 @@ TabChild::OnLocationChange(nsIWebProgress* aWebProgress,
|
||||
nsIURI *aLocation,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
if (!IsAsyncPanZoomEnabled()) {
|
||||
if (!gfxPrefs::AsyncPanZoomEnabled()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1437,7 +1431,7 @@ TabChild::ProvideWindowCommon(nsIDOMWindow* aOpener,
|
||||
context.opener() = openerTabId;
|
||||
context.isBrowserElement() = IsBrowserElement();
|
||||
|
||||
IPCTabContext ipcContext(context, mScrolling);
|
||||
IPCTabContext ipcContext(context);
|
||||
|
||||
TabId tabId;
|
||||
cc->SendAllocateTabId(openerTabId,
|
||||
@ -1455,7 +1449,7 @@ TabChild::ProvideWindowCommon(nsIDOMWindow* aOpener,
|
||||
unused << Manager()->SendPBrowserConstructor(
|
||||
// We release this ref in DeallocPBrowserChild
|
||||
nsRefPtr<TabChild>(newChild).forget().take(),
|
||||
tabId, IPCTabContext(context, mScrolling), aChromeFlags,
|
||||
tabId, IPCTabContext(context), aChromeFlags,
|
||||
cc->GetID(), cc->IsForApp(), cc->IsForBrowser());
|
||||
|
||||
nsAutoCString spec;
|
||||
@ -1517,12 +1511,10 @@ TabChild::ProvideWindowCommon(nsIDOMWindow* aOpener,
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
ScrollingBehavior scrolling = DEFAULT_SCROLLING;
|
||||
TextureFactoryIdentifier textureFactoryIdentifier;
|
||||
uint64_t layersId = 0;
|
||||
PRenderFrameChild* renderFrame = newChild->SendPRenderFrameConstructor();
|
||||
newChild->SendGetRenderFrameInfo(renderFrame,
|
||||
&scrolling,
|
||||
&textureFactoryIdentifier,
|
||||
&layersId);
|
||||
if (layersId == 0) { // if renderFrame is invalid.
|
||||
@ -1532,7 +1524,7 @@ TabChild::ProvideWindowCommon(nsIDOMWindow* aOpener,
|
||||
|
||||
// Unfortunately we don't get a window unless we've shown the frame. That's
|
||||
// pretty bogus; see bug 763602.
|
||||
newChild->DoFakeShow(scrolling, textureFactoryIdentifier, layersId, renderFrame);
|
||||
newChild->DoFakeShow(textureFactoryIdentifier, layersId, renderFrame);
|
||||
|
||||
for (size_t i = 0; i < frameScripts.Length(); i++) {
|
||||
FrameScriptInfo& info = frameScripts[i];
|
||||
@ -1854,13 +1846,12 @@ TabChild::CancelCachedFileDescriptorCallback(
|
||||
}
|
||||
|
||||
void
|
||||
TabChild::DoFakeShow(const ScrollingBehavior& aScrolling,
|
||||
const TextureFactoryIdentifier& aTextureFactoryIdentifier,
|
||||
TabChild::DoFakeShow(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
|
||||
const uint64_t& aLayersId,
|
||||
PRenderFrameChild* aRenderFrame)
|
||||
{
|
||||
ShowInfo info(EmptyString(), false, false, 0, 0);
|
||||
RecvShow(nsIntSize(0, 0), info, aScrolling, aTextureFactoryIdentifier,
|
||||
RecvShow(nsIntSize(0, 0), info, aTextureFactoryIdentifier,
|
||||
aLayersId, aRenderFrame, mParentIsActive);
|
||||
mDidFakeShow = true;
|
||||
}
|
||||
@ -1960,7 +1951,6 @@ TabChild::MaybeRequestPreinitCamera()
|
||||
bool
|
||||
TabChild::RecvShow(const nsIntSize& aSize,
|
||||
const ShowInfo& aInfo,
|
||||
const ScrollingBehavior& aScrolling,
|
||||
const TextureFactoryIdentifier& aTextureFactoryIdentifier,
|
||||
const uint64_t& aLayersId,
|
||||
PRenderFrameChild* aRenderFrame,
|
||||
@ -1980,7 +1970,7 @@ TabChild::RecvShow(const nsIntSize& aSize,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!InitRenderingState(aScrolling, aTextureFactoryIdentifier, aLayersId, aRenderFrame)) {
|
||||
if (!InitRenderingState(aTextureFactoryIdentifier, aLayersId, aRenderFrame)) {
|
||||
// We can fail to initialize our widget if the <browser
|
||||
// remote> has already been destroyed, and we couldn't hook
|
||||
// into the parent-process's layer system. That's not a fatal
|
||||
@ -2177,7 +2167,7 @@ TabChild::RecvMouseWheelEvent(const WidgetWheelEvent& aEvent,
|
||||
const ScrollableLayerGuid& aGuid,
|
||||
const uint64_t& aInputBlockId)
|
||||
{
|
||||
if (IsAsyncPanZoomEnabled()) {
|
||||
if (gfxPrefs::AsyncPanZoomEnabled()) {
|
||||
nsCOMPtr<nsIDocument> document(GetDocument());
|
||||
APZCCallbackHelper::SendSetTargetAPZCNotification(WebWidget(), document, aEvent, aGuid,
|
||||
aInputBlockId, mSetTargetAPZCCallback);
|
||||
@ -2187,7 +2177,7 @@ TabChild::RecvMouseWheelEvent(const WidgetWheelEvent& aEvent,
|
||||
event.widget = mWidget;
|
||||
APZCCallbackHelper::DispatchWidgetEvent(event);
|
||||
|
||||
if (IsAsyncPanZoomEnabled()) {
|
||||
if (gfxPrefs::AsyncPanZoomEnabled()) {
|
||||
mAPZEventState->ProcessWheelEvent(event, aGuid, aInputBlockId);
|
||||
}
|
||||
return true;
|
||||
@ -2361,7 +2351,7 @@ TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
|
||||
APZCCallbackHelper::ApplyCallbackTransform(localEvent, aGuid,
|
||||
mWidget->GetDefaultScale(), GetPresShellResolution());
|
||||
|
||||
if (localEvent.message == NS_TOUCH_START && IsAsyncPanZoomEnabled()) {
|
||||
if (localEvent.message == NS_TOUCH_START && gfxPrefs::AsyncPanZoomEnabled()) {
|
||||
nsCOMPtr<nsIDocument> document = GetDocument();
|
||||
APZCCallbackHelper::SendSetTargetAPZCNotification(WebWidget(), document,
|
||||
localEvent, aGuid, aInputBlockId, mSetTargetAPZCCallback);
|
||||
@ -2370,7 +2360,7 @@ TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
|
||||
// Dispatch event to content (potentially a long-running operation)
|
||||
nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent);
|
||||
|
||||
if (!IsAsyncPanZoomEnabled()) {
|
||||
if (!gfxPrefs::AsyncPanZoomEnabled()) {
|
||||
UpdateTapState(localEvent, status);
|
||||
return true;
|
||||
}
|
||||
@ -2793,8 +2783,7 @@ TabChild::InitTabChildGlobal(FrameScriptLoading aScriptLoading)
|
||||
}
|
||||
|
||||
bool
|
||||
TabChild::InitRenderingState(const ScrollingBehavior& aScrolling,
|
||||
const TextureFactoryIdentifier& aTextureFactoryIdentifier,
|
||||
TabChild::InitRenderingState(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
|
||||
const uint64_t& aLayersId,
|
||||
PRenderFrameChild* aRenderFrame)
|
||||
{
|
||||
@ -2807,7 +2796,6 @@ TabChild::InitRenderingState(const ScrollingBehavior& aScrolling,
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aLayersId != 0);
|
||||
mScrolling = aScrolling;
|
||||
mTextureFactoryIdentifier = aTextureFactoryIdentifier;
|
||||
|
||||
// Pushing layers transactions directly to a separate
|
||||
|
@ -177,7 +177,6 @@ public:
|
||||
virtual nsIWebNavigation* WebNavigation() const = 0;
|
||||
virtual nsIWidget* WebWidget() = 0;
|
||||
nsIPrincipal* GetPrincipal() { return mPrincipal; }
|
||||
bool IsAsyncPanZoomEnabled();
|
||||
// Recalculates the display state, including the CSS
|
||||
// viewport. This should be called whenever we believe the
|
||||
// viewport data on a document may have changed. If it didn't
|
||||
@ -222,7 +221,6 @@ protected:
|
||||
nsRefPtr<TabChildGlobal> mTabChildGlobal;
|
||||
ScreenIntSize mInnerSize;
|
||||
mozilla::layers::FrameMetrics mLastRootMetrics;
|
||||
mozilla::layout::ScrollingBehavior mScrolling;
|
||||
nsCOMPtr<nsIWebBrowserChrome3> mWebBrowserChrome;
|
||||
};
|
||||
|
||||
@ -243,7 +241,6 @@ class TabChild MOZ_FINAL : public TabChildBase,
|
||||
{
|
||||
typedef mozilla::dom::ClonedMessageData ClonedMessageData;
|
||||
typedef mozilla::layout::RenderFrameChild RenderFrameChild;
|
||||
typedef mozilla::layout::ScrollingBehavior ScrollingBehavior;
|
||||
typedef mozilla::layers::APZEventState APZEventState;
|
||||
typedef mozilla::layers::SetTargetAPZCCallback SetTargetAPZCCallback;
|
||||
|
||||
@ -313,7 +310,6 @@ public:
|
||||
MOZ_OVERRIDE;
|
||||
virtual bool RecvShow(const nsIntSize& aSize,
|
||||
const ShowInfo& aInfo,
|
||||
const ScrollingBehavior& aScrolling,
|
||||
const TextureFactoryIdentifier& aTextureFactoryIdentifier,
|
||||
const uint64_t& aLayersId,
|
||||
PRenderFrameChild* aRenderFrame,
|
||||
@ -537,16 +533,14 @@ private:
|
||||
|
||||
enum FrameScriptLoading { DONT_LOAD_SCRIPTS, DEFAULT_LOAD_SCRIPTS };
|
||||
bool InitTabChildGlobal(FrameScriptLoading aScriptLoading = DEFAULT_LOAD_SCRIPTS);
|
||||
bool InitRenderingState(const ScrollingBehavior& aScrolling,
|
||||
const TextureFactoryIdentifier& aTextureFactoryIdentifier,
|
||||
bool InitRenderingState(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
|
||||
const uint64_t& aLayersId,
|
||||
PRenderFrameChild* aRenderFrame);
|
||||
void DestroyWindow();
|
||||
void SetProcessNameToAppName();
|
||||
|
||||
// Call RecvShow(nsIntSize(0, 0)) and block future calls to RecvShow().
|
||||
void DoFakeShow(const ScrollingBehavior& aScrolling,
|
||||
const TextureFactoryIdentifier& aTextureFactoryIdentifier,
|
||||
void DoFakeShow(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
|
||||
const uint64_t& aLayersId,
|
||||
PRenderFrameChild* aRenderFrame);
|
||||
|
||||
|
@ -24,7 +24,6 @@ TabContext::TabContext()
|
||||
: mInitialized(false)
|
||||
, mOwnAppId(NO_APP_ID)
|
||||
, mContainingAppId(NO_APP_ID)
|
||||
, mScrollingBehavior(DEFAULT_SCROLLING)
|
||||
, mIsBrowser(false)
|
||||
{
|
||||
}
|
||||
@ -156,8 +155,7 @@ TabContext::SetTabContext(const TabContext& aContext)
|
||||
|
||||
bool
|
||||
TabContext::SetTabContextForAppFrame(mozIApplication* aOwnApp,
|
||||
mozIApplication* aAppFrameOwnerApp,
|
||||
ScrollingBehavior aRequestedBehavior)
|
||||
mozIApplication* aAppFrameOwnerApp)
|
||||
{
|
||||
NS_ENSURE_FALSE(mInitialized, false);
|
||||
|
||||
@ -181,15 +179,13 @@ TabContext::SetTabContextForAppFrame(mozIApplication* aOwnApp,
|
||||
mIsBrowser = false;
|
||||
mOwnAppId = ownAppId;
|
||||
mContainingAppId = containingAppId;
|
||||
mScrollingBehavior = aRequestedBehavior;
|
||||
mOwnApp = aOwnApp;
|
||||
mContainingApp = aAppFrameOwnerApp;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TabContext::SetTabContextForBrowserFrame(mozIApplication* aBrowserFrameOwnerApp,
|
||||
ScrollingBehavior aRequestedBehavior)
|
||||
TabContext::SetTabContextForBrowserFrame(mozIApplication* aBrowserFrameOwnerApp)
|
||||
{
|
||||
NS_ENSURE_FALSE(mInitialized, false);
|
||||
|
||||
@ -204,18 +200,16 @@ TabContext::SetTabContextForBrowserFrame(mozIApplication* aBrowserFrameOwnerApp,
|
||||
mIsBrowser = true;
|
||||
mOwnAppId = NO_APP_ID;
|
||||
mContainingAppId = containingAppId;
|
||||
mScrollingBehavior = aRequestedBehavior;
|
||||
mContainingApp = aBrowserFrameOwnerApp;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TabContext::SetTabContextForNormalFrame(ScrollingBehavior aRequestedBehavior)
|
||||
TabContext::SetTabContextForNormalFrame()
|
||||
{
|
||||
NS_ENSURE_FALSE(mInitialized, false);
|
||||
|
||||
mInitialized = true;
|
||||
mScrollingBehavior = aRequestedBehavior;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -223,12 +217,10 @@ IPCTabContext
|
||||
TabContext::AsIPCTabContext() const
|
||||
{
|
||||
if (mIsBrowser) {
|
||||
return IPCTabContext(BrowserFrameIPCTabContext(mContainingAppId),
|
||||
mScrollingBehavior);
|
||||
return IPCTabContext(BrowserFrameIPCTabContext(mContainingAppId));
|
||||
}
|
||||
|
||||
return IPCTabContext(AppFrameIPCTabContext(mOwnAppId, mContainingAppId),
|
||||
mScrollingBehavior);
|
||||
return IPCTabContext(AppFrameIPCTabContext(mOwnAppId, mContainingAppId));
|
||||
}
|
||||
|
||||
static already_AddRefed<mozIApplication>
|
||||
@ -342,12 +334,10 @@ MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams)
|
||||
|
||||
bool rv;
|
||||
if (isBrowser) {
|
||||
rv = mTabContext.SetTabContextForBrowserFrame(containingApp,
|
||||
aParams.scrollingBehavior());
|
||||
rv = mTabContext.SetTabContextForBrowserFrame(containingApp);
|
||||
} else {
|
||||
rv = mTabContext.SetTabContextForAppFrame(ownApp,
|
||||
containingApp,
|
||||
aParams.scrollingBehavior());
|
||||
containingApp);
|
||||
}
|
||||
|
||||
if (!rv) {
|
||||
|
@ -7,7 +7,6 @@
|
||||
#ifndef mozilla_dom_TabContext_h
|
||||
#define mozilla_dom_TabContext_h
|
||||
|
||||
#include "mozilla/layout/RenderFrameUtils.h"
|
||||
#include "mozIApplication.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
@ -30,9 +29,6 @@ class IPCTabContext;
|
||||
*/
|
||||
class TabContext
|
||||
{
|
||||
protected:
|
||||
typedef mozilla::layout::ScrollingBehavior ScrollingBehavior;
|
||||
|
||||
public:
|
||||
TabContext();
|
||||
|
||||
@ -106,11 +102,6 @@ public:
|
||||
already_AddRefed<mozIApplication> GetOwnOrContainingApp() const;
|
||||
bool HasOwnOrContainingApp() const;
|
||||
|
||||
/**
|
||||
* Return the requested scrolling behavior for this frame.
|
||||
*/
|
||||
ScrollingBehavior GetScrollingBehavior() const { return mScrollingBehavior; }
|
||||
|
||||
protected:
|
||||
friend class MaybeInvalidTabContext;
|
||||
|
||||
@ -133,20 +124,18 @@ protected:
|
||||
* given app. Either or both apps may be null.
|
||||
*/
|
||||
bool SetTabContextForAppFrame(mozIApplication* aOwnApp,
|
||||
mozIApplication* aAppFrameOwnerApp,
|
||||
ScrollingBehavior aRequestedBehavior);
|
||||
mozIApplication* aAppFrameOwnerApp);
|
||||
|
||||
/**
|
||||
* Set this TabContext to be a browser frame inside the given app (which may
|
||||
* be null).
|
||||
*/
|
||||
bool SetTabContextForBrowserFrame(mozIApplication* aBrowserFrameOwnerApp,
|
||||
ScrollingBehavior aRequestedBehavior);
|
||||
bool SetTabContextForBrowserFrame(mozIApplication* aBrowserFrameOwnerApp);
|
||||
|
||||
/**
|
||||
* Set this TabContext to be a normal non-browser non-app frame.
|
||||
*/
|
||||
bool SetTabContextForNormalFrame(ScrollingBehavior aRequestedBehavior);
|
||||
bool SetTabContextForNormalFrame();
|
||||
|
||||
private:
|
||||
/**
|
||||
@ -178,11 +167,6 @@ private:
|
||||
*/
|
||||
uint32_t mContainingAppId;
|
||||
|
||||
/**
|
||||
* The requested scrolling behavior for this frame.
|
||||
*/
|
||||
ScrollingBehavior mScrollingBehavior;
|
||||
|
||||
/**
|
||||
* Does this TabContext correspond to a browser element?
|
||||
*
|
||||
@ -204,23 +188,20 @@ public:
|
||||
return TabContext::SetTabContext(aContext);
|
||||
}
|
||||
|
||||
bool SetTabContextForAppFrame(mozIApplication* aOwnApp, mozIApplication* aAppFrameOwnerApp,
|
||||
ScrollingBehavior aRequestedBehavior)
|
||||
bool SetTabContextForAppFrame(mozIApplication* aOwnApp,
|
||||
mozIApplication* aAppFrameOwnerApp)
|
||||
{
|
||||
return TabContext::SetTabContextForAppFrame(aOwnApp, aAppFrameOwnerApp,
|
||||
aRequestedBehavior);
|
||||
return TabContext::SetTabContextForAppFrame(aOwnApp, aAppFrameOwnerApp);
|
||||
}
|
||||
|
||||
bool SetTabContextForBrowserFrame(mozIApplication* aBrowserFrameOwnerApp,
|
||||
ScrollingBehavior aRequestedBehavior)
|
||||
bool SetTabContextForBrowserFrame(mozIApplication* aBrowserFrameOwnerApp)
|
||||
{
|
||||
return TabContext::SetTabContextForBrowserFrame(aBrowserFrameOwnerApp,
|
||||
aRequestedBehavior);
|
||||
return TabContext::SetTabContextForBrowserFrame(aBrowserFrameOwnerApp);
|
||||
}
|
||||
|
||||
bool SetTabContextForNormalFrame(ScrollingBehavior aRequestedBehavior)
|
||||
bool SetTabContextForNormalFrame()
|
||||
{
|
||||
return TabContext::SetTabContextForNormalFrame(aRequestedBehavior);
|
||||
return TabContext::SetTabContextForNormalFrame();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -805,7 +805,6 @@ TabParent::Show(const nsIntSize& size, bool aParentIsActive)
|
||||
return;
|
||||
}
|
||||
|
||||
ScrollingBehavior scrolling = UseAsyncPanZoom() ? ASYNC_PAN_ZOOM : DEFAULT_SCROLLING;
|
||||
TextureFactoryIdentifier textureFactoryIdentifier;
|
||||
uint64_t layersId = 0;
|
||||
bool success = false;
|
||||
@ -819,7 +818,6 @@ TabParent::Show(const nsIntSize& size, bool aParentIsActive)
|
||||
if (frameLoader) {
|
||||
renderFrame =
|
||||
new RenderFrameParent(frameLoader,
|
||||
scrolling,
|
||||
&textureFactoryIdentifier,
|
||||
&layersId,
|
||||
&success);
|
||||
@ -842,7 +840,7 @@ TabParent::Show(const nsIntSize& size, bool aParentIsActive)
|
||||
info = ShowInfo(name, allowFullscreen, isPrivate, mDPI, mDefaultScale.scale);
|
||||
}
|
||||
|
||||
unused << SendShow(size, info, scrolling, textureFactoryIdentifier,
|
||||
unused << SendShow(size, info, textureFactoryIdentifier,
|
||||
layersId, renderFrame, aParentIsActive);
|
||||
}
|
||||
|
||||
@ -2334,14 +2332,12 @@ TabParent::AllocPRenderFrameParent()
|
||||
{
|
||||
MOZ_ASSERT(ManagedPRenderFrameParent().IsEmpty());
|
||||
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
|
||||
ScrollingBehavior scrolling = UseAsyncPanZoom() ? ASYNC_PAN_ZOOM : DEFAULT_SCROLLING;
|
||||
TextureFactoryIdentifier textureFactoryIdentifier;
|
||||
uint64_t layersId = 0;
|
||||
bool success = false;
|
||||
if(frameLoader) {
|
||||
PRenderFrameParent* renderFrame =
|
||||
new RenderFrameParent(frameLoader,
|
||||
scrolling,
|
||||
&textureFactoryIdentifier,
|
||||
&layersId,
|
||||
&success);
|
||||
@ -2362,12 +2358,10 @@ TabParent::DeallocPRenderFrameParent(PRenderFrameParent* aFrame)
|
||||
|
||||
bool
|
||||
TabParent::RecvGetRenderFrameInfo(PRenderFrameParent* aRenderFrame,
|
||||
ScrollingBehavior* aScrolling,
|
||||
TextureFactoryIdentifier* aTextureFactoryIdentifier,
|
||||
uint64_t* aLayersId)
|
||||
{
|
||||
RenderFrameParent* renderFrame = static_cast<RenderFrameParent*>(aRenderFrame);
|
||||
*aScrolling = renderFrame->UseAsyncPanZoom() ? ASYNC_PAN_ZOOM : DEFAULT_SCROLLING;
|
||||
renderFrame->GetTextureFactoryIdentifier(aTextureFactoryIdentifier);
|
||||
*aLayersId = renderFrame->GetLayersId();
|
||||
return true;
|
||||
@ -2429,14 +2423,6 @@ TabParent::GetWidget() const
|
||||
return widget.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
TabParent::UseAsyncPanZoom()
|
||||
{
|
||||
bool usingOffMainThreadCompositing = !!CompositorParent::CompositorLoop();
|
||||
return (usingOffMainThreadCompositing && gfxPrefs::AsyncPanZoomEnabled() &&
|
||||
GetScrollingBehavior() == ASYNC_PAN_ZOOM);
|
||||
}
|
||||
|
||||
void
|
||||
TabParent::ApzAwareEventRoutingToChild(ScrollableLayerGuid* aOutTargetGuid,
|
||||
uint64_t* aOutInputBlockId)
|
||||
@ -2618,7 +2604,7 @@ TabParent::InjectTouchEvent(const nsAString& aType,
|
||||
NS_IMETHODIMP
|
||||
TabParent::GetUseAsyncPanZoom(bool* useAsyncPanZoom)
|
||||
{
|
||||
*useAsyncPanZoom = UseAsyncPanZoom();
|
||||
*useAsyncPanZoom = gfxPrefs::AsyncPanZoomEnabled();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,6 @@ class TabParent : public PBrowserParent
|
||||
, public TabContext
|
||||
{
|
||||
typedef mozilla::dom::ClonedMessageData ClonedMessageData;
|
||||
typedef mozilla::layout::ScrollingBehavior ScrollingBehavior;
|
||||
|
||||
virtual ~TabParent();
|
||||
|
||||
@ -400,7 +399,6 @@ protected:
|
||||
virtual bool RecvRemotePaintIsReady() MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvGetRenderFrameInfo(PRenderFrameParent* aRenderFrame,
|
||||
ScrollingBehavior* aScrolling,
|
||||
TextureFactoryIdentifier* aTextureFactoryIdentifier,
|
||||
uint64_t* aLayersId) MOZ_OVERRIDE;
|
||||
|
||||
@ -453,9 +451,6 @@ private:
|
||||
|
||||
CSSPoint AdjustTapToChildWidget(const CSSPoint& aPoint);
|
||||
|
||||
// When true, we create a pan/zoom controller for our frame and
|
||||
// notify it of input events targeting us.
|
||||
bool UseAsyncPanZoom();
|
||||
// Update state prior to routing an APZ-aware event to the child process.
|
||||
// |aOutTargetGuid| will contain the identifier
|
||||
// of the APZC instance that handled the event. aOutTargetGuid may be
|
||||
|
@ -1285,7 +1285,7 @@ PeerConnectionObserver.prototype = {
|
||||
var pc = this._dompc;
|
||||
pc._onReplaceTrackWithTrack = null;
|
||||
pc._onReplaceTrackSender = null;
|
||||
pc._onReplaceTrackError(this.newError(message, code));
|
||||
pc._onReplaceTrackFailure(this.newError(message, code));
|
||||
},
|
||||
|
||||
foundIceCandidate: function(cand) {
|
||||
|
@ -65,7 +65,7 @@ OmxDecoder::OmxDecoder(MediaResource *aResource,
|
||||
mAudioChannels(-1),
|
||||
mAudioSampleRate(-1),
|
||||
mDurationUs(-1),
|
||||
mVideoLastFrameTime(-1),
|
||||
mLastSeekTime(-1),
|
||||
mVideoBuffer(nullptr),
|
||||
mAudioBuffer(nullptr),
|
||||
mIsVideoSeeking(false),
|
||||
@ -386,6 +386,10 @@ void OmxDecoder::ReleaseMediaResources() {
|
||||
|
||||
mNativeWindowClient.clear();
|
||||
mNativeWindow.clear();
|
||||
|
||||
// Reset this variable to make the first seek go to the previous keyframe
|
||||
// when resuming
|
||||
mLastSeekTime = -1;
|
||||
}
|
||||
|
||||
bool OmxDecoder::SetVideoFormat() {
|
||||
@ -564,12 +568,13 @@ bool OmxDecoder::ReadVideo(VideoFrame *aFrame, int64_t aTimeUs,
|
||||
MediaSource::ReadOptions::SeekMode seekMode;
|
||||
// If the last timestamp of decoded frame is smaller than seekTime,
|
||||
// seek to next key frame. Otherwise seek to the previos one.
|
||||
if (mVideoLastFrameTime > aTimeUs || mVideoLastFrameTime == -1) {
|
||||
OD_LOG("SeekTime: %lld, mLastSeekTime:%lld", aTimeUs, mLastSeekTime);
|
||||
if (mLastSeekTime == -1 || mLastSeekTime > aTimeUs) {
|
||||
seekMode = MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC;
|
||||
} else {
|
||||
seekMode = MediaSource::ReadOptions::SEEK_NEXT_SYNC;
|
||||
}
|
||||
|
||||
mLastSeekTime = aTimeUs;
|
||||
bool findNextBuffer = true;
|
||||
while (findNextBuffer) {
|
||||
options.setSeekTo(aTimeUs, seekMode);
|
||||
@ -675,7 +680,6 @@ bool OmxDecoder::ReadVideo(VideoFrame *aFrame, int64_t aTimeUs,
|
||||
if ((aKeyframeSkip && timeUs < aTimeUs) || length == 0) {
|
||||
aFrame->mShouldSkip = true;
|
||||
}
|
||||
mVideoLastFrameTime = timeUs;
|
||||
}
|
||||
else if (err == INFO_FORMAT_CHANGED) {
|
||||
// If the format changed, update our cached info.
|
||||
|
@ -60,7 +60,7 @@ class OmxDecoder : public OMXCodecProxy::EventListener {
|
||||
int32_t mAudioChannels;
|
||||
int32_t mAudioSampleRate;
|
||||
int64_t mDurationUs;
|
||||
int64_t mVideoLastFrameTime;
|
||||
int64_t mLastSeekTime;
|
||||
|
||||
VideoFrame mVideoFrame;
|
||||
AudioFrame mAudioFrame;
|
||||
|
@ -88,7 +88,7 @@ par.removeChild(del);
|
||||
del.setAttribute("is", "foobar");
|
||||
par.appendChild(del);
|
||||
is(getComputedStyle(del).color, "rgb(0, 0, 255)", "del - color (after reappend)");
|
||||
var Del = document.registerElement('x-del', { prototype: Object.create(HTMLSpanElement.prototype) });
|
||||
var Del = document.registerElement('x-del', { extends: 'span', prototype: Object.create(HTMLSpanElement.prototype) });
|
||||
// [is="x-del"] will not match any longer so the rule of span will apply
|
||||
is(getComputedStyle(del).color, "rgb(0, 255, 0)", "del - color (after registerElement)");
|
||||
// but the element should have been upgraded:
|
||||
|
@ -2297,7 +2297,7 @@ ServiceWorkerManager::DispatchFetchEvent(nsIDocument* aDoc, nsIInterceptedChanne
|
||||
|
||||
if (!isNavigation) {
|
||||
MOZ_ASSERT(aDoc);
|
||||
rv = GetDocumentController(aDoc->GetWindow(), getter_AddRefs(serviceWorker));
|
||||
rv = GetDocumentController(aDoc->GetInnerWindow(), getter_AddRefs(serviceWorker));
|
||||
} else {
|
||||
nsCOMPtr<nsIChannel> internalChannel;
|
||||
rv = aChannel->GetChannel(getter_AddRefs(internalChannel));
|
||||
|
@ -916,6 +916,8 @@ GetOrCreateMapEntryForPrototype(JSContext *cx, JS::Handle<JSObject*> proto)
|
||||
// content, and the Window for chrome (whether add-ons are involved or not).
|
||||
JS::Rooted<JSObject*> scope(cx, xpc::GetXBLScopeOrGlobal(cx, proto));
|
||||
NS_ENSURE_TRUE(scope, nullptr);
|
||||
MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(scope) == scope);
|
||||
|
||||
JS::Rooted<JSObject*> wrappedProto(cx, proto);
|
||||
JSAutoCompartment ac(cx, scope);
|
||||
if (!JS_WrapObject(cx, &wrappedProto)) {
|
||||
@ -939,7 +941,7 @@ GetOrCreateMapEntryForPrototype(JSContext *cx, JS::Handle<JSObject*> proto)
|
||||
|
||||
// We don't have an entry. Create one and stick it in the map.
|
||||
JS::Rooted<JSObject*> entry(cx);
|
||||
entry = JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(), scope);
|
||||
entry = JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr());
|
||||
if (!entry) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -1013,7 +1015,7 @@ nsXBLBinding::DoInitJSClass(JSContext *cx,
|
||||
// We need to create the prototype. First, enter the compartment where it's
|
||||
// going to live, and create it.
|
||||
JSAutoCompartment ac2(cx, global);
|
||||
proto = JS_NewObjectWithGivenProto(cx, &gPrototypeJSClass, parent_proto, global);
|
||||
proto = JS_NewObjectWithGivenProto(cx, &gPrototypeJSClass, parent_proto);
|
||||
if (!proto) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
@ -87,6 +87,7 @@ nsXBLProtoImpl::InstallImplementation(nsXBLPrototypeBinding* aPrototypeBinding,
|
||||
GetGlobalForObjectCrossCompartment(targetClassObject));
|
||||
JS::Rooted<JSObject*> scopeObject(cx, xpc::GetScopeForXBLExecution(cx, globalObject, addonId));
|
||||
NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY);
|
||||
MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(scopeObject) == scopeObject);
|
||||
JSAutoCompartment ac(cx, scopeObject);
|
||||
|
||||
// Determine the appropriate property holder.
|
||||
@ -112,7 +113,7 @@ nsXBLProtoImpl::InstallImplementation(nsXBLPrototypeBinding* aPrototypeBinding,
|
||||
} else if (scopeObject != globalObject) {
|
||||
|
||||
// This is just a property holder, so it doesn't need any special JSClass.
|
||||
propertyHolder = JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(), scopeObject);
|
||||
propertyHolder = JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr());
|
||||
NS_ENSURE_TRUE(propertyHolder, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
// Define it as a property on the scopeObject, using the same name used on
|
||||
@ -245,9 +246,8 @@ nsXBLProtoImpl::CompilePrototypeMembers(nsXBLPrototypeBinding* aBinding)
|
||||
return NS_ERROR_FAILURE;
|
||||
jsapi.TakeOwnershipOfErrorReporting();
|
||||
JSContext* cx = jsapi.cx();
|
||||
JS::Rooted<JSObject*> compilationGlobal(cx, xpc::CompilationScope());
|
||||
|
||||
mPrecompiledMemberHolder = JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(), compilationGlobal);
|
||||
mPrecompiledMemberHolder = JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr());
|
||||
if (!mPrecompiledMemberHolder)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
@ -366,8 +366,7 @@ nsXBLProtoImpl::Read(nsIObjectInputStream* aStream,
|
||||
AssertInCompilationScope();
|
||||
AutoJSContext cx;
|
||||
// Set up a class object first so that deserialization is possible
|
||||
JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
|
||||
mPrecompiledMemberHolder = JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(), global);
|
||||
mPrecompiledMemberHolder = JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr());
|
||||
if (!mPrecompiledMemberHolder)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
|
@ -86,7 +86,7 @@ NS_IMETHODIMP TypeInState::NotifySelectionChanged(nsIDOMDocument *, nsISelection
|
||||
nsRefPtr<Selection> selection = static_cast<Selection*>(aSelection);
|
||||
|
||||
if (aSelection) {
|
||||
int32_t rangeCount = selection->GetRangeCount();
|
||||
int32_t rangeCount = selection->RangeCount();
|
||||
|
||||
if (selection->Collapsed() && rangeCount) {
|
||||
nsCOMPtr<nsIDOMNode> selNode;
|
||||
|
@ -3561,7 +3561,7 @@ nsEditor::GetStartNodeAndOffset(Selection* aSelection, nsINode** aStartNode,
|
||||
*aStartNode = nullptr;
|
||||
*aStartOffset = 0;
|
||||
|
||||
NS_ENSURE_TRUE(aSelection->GetRangeCount(), NS_ERROR_FAILURE);
|
||||
NS_ENSURE_TRUE(aSelection->RangeCount(), NS_ERROR_FAILURE);
|
||||
|
||||
const nsRange* range = aSelection->GetRangeAt(0);
|
||||
NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
|
||||
@ -3608,7 +3608,7 @@ nsEditor::GetEndNodeAndOffset(Selection* aSelection, nsINode** aEndNode,
|
||||
*aEndNode = nullptr;
|
||||
*aEndOffset = 0;
|
||||
|
||||
NS_ENSURE_TRUE(aSelection->GetRangeCount(), NS_ERROR_FAILURE);
|
||||
NS_ENSURE_TRUE(aSelection->RangeCount(), NS_ERROR_FAILURE);
|
||||
|
||||
const nsRange* range = aSelection->GetRangeAt(0);
|
||||
NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
|
||||
@ -4162,7 +4162,7 @@ nsEditor::CreateTxnForDeleteSelection(EDirection aAction,
|
||||
// allocate the out-param transaction
|
||||
nsRefPtr<EditAggregateTxn> aggTxn = new EditAggregateTxn();
|
||||
|
||||
for (int32_t rangeIdx = 0; rangeIdx < selection->GetRangeCount(); ++rangeIdx) {
|
||||
for (uint32_t rangeIdx = 0; rangeIdx < selection->RangeCount(); ++rangeIdx) {
|
||||
nsRefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
|
||||
NS_ENSURE_STATE(range);
|
||||
|
||||
|
@ -315,7 +315,7 @@ nsHTMLEditRules::BeforeEdit(EditAction action,
|
||||
nsRefPtr<Selection> selection = mHTMLEditor->GetSelection();
|
||||
|
||||
// get the selection location
|
||||
NS_ENSURE_STATE(selection->GetRangeCount());
|
||||
NS_ENSURE_STATE(selection->RangeCount());
|
||||
mRangeItem->startNode = selection->GetRangeAt(0)->GetStartParent();
|
||||
mRangeItem->startOffset = selection->GetRangeAt(0)->StartOffset();
|
||||
mRangeItem->endNode = selection->GetRangeAt(0)->GetEndParent();
|
||||
@ -582,7 +582,7 @@ nsHTMLEditRules::WillDoAction(Selection* aSelection,
|
||||
if (!aSelection) {
|
||||
return NS_OK;
|
||||
}
|
||||
NS_ENSURE_TRUE(aSelection->GetRangeCount(), NS_OK);
|
||||
NS_ENSURE_TRUE(aSelection->RangeCount(), NS_OK);
|
||||
|
||||
nsRefPtr<nsRange> range = aSelection->GetRangeAt(0);
|
||||
nsCOMPtr<nsINode> selStartNode = range->GetStartParent();
|
||||
@ -2425,7 +2425,7 @@ nsHTMLEditRules::WillDeleteSelection(Selection* aSelection,
|
||||
// except table elements.
|
||||
join = true;
|
||||
|
||||
uint32_t rangeCount = aSelection->GetRangeCount();
|
||||
uint32_t rangeCount = aSelection->RangeCount();
|
||||
for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
|
||||
nsRefPtr<nsRange> range = aSelection->GetRangeAt(rangeIdx);
|
||||
|
||||
@ -6050,7 +6050,7 @@ nsHTMLEditRules::GetListActionNodes(nsCOMArray<nsIDOMNode> &outArrayOfNodes,
|
||||
// is only in part of it. used by list item dialog.
|
||||
if (aEntireList)
|
||||
{
|
||||
uint32_t rangeCount = selection->GetRangeCount();
|
||||
uint32_t rangeCount = selection->RangeCount();
|
||||
for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
|
||||
nsRefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
|
||||
nsCOMPtr<nsIDOMNode> commonParent, parent, tmp;
|
||||
@ -7519,7 +7519,7 @@ nsHTMLEditRules::ReapplyCachedStyles()
|
||||
NS_ENSURE_STATE(mHTMLEditor);
|
||||
nsRefPtr<Selection> selection = mHTMLEditor->GetSelection();
|
||||
MOZ_ASSERT(selection);
|
||||
if (!selection->GetRangeCount()) {
|
||||
if (!selection->RangeCount()) {
|
||||
// Nothing to do
|
||||
return NS_OK;
|
||||
}
|
||||
@ -8179,7 +8179,7 @@ nsHTMLEditRules::SelectionEndpointInNode(nsINode* aNode, bool* aResult)
|
||||
nsRefPtr<Selection> selection = mHTMLEditor->GetSelection();
|
||||
NS_ENSURE_STATE(selection);
|
||||
|
||||
uint32_t rangeCount = selection->GetRangeCount();
|
||||
uint32_t rangeCount = selection->RangeCount();
|
||||
for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
|
||||
nsRefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
|
||||
nsCOMPtr<nsIDOMNode> startParent, endParent;
|
||||
|
@ -3275,7 +3275,7 @@ nsHTMLEditor::GetIsSelectionEditable(bool* aIsSelectionEditable)
|
||||
// Per the editing spec as of June 2012: we have to have a selection whose
|
||||
// start and end nodes are editable, and which share an ancestor editing
|
||||
// host. (Bug 766387.)
|
||||
*aIsSelectionEditable = selection->GetRangeCount() &&
|
||||
*aIsSelectionEditable = selection->RangeCount() &&
|
||||
selection->GetAnchorNode()->IsEditable() &&
|
||||
selection->GetFocusNode()->IsEditable();
|
||||
|
||||
@ -4547,7 +4547,7 @@ nsHTMLEditor::SetCSSBackgroundColor(const nsAString& aColor)
|
||||
{
|
||||
// loop thru the ranges in the selection
|
||||
nsAutoString bgcolor; bgcolor.AssignLiteral("bgcolor");
|
||||
uint32_t rangeCount = selection->GetRangeCount();
|
||||
uint32_t rangeCount = selection->RangeCount();
|
||||
for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
|
||||
nsCOMPtr<nsIDOMNode> cachedBlockParent = nullptr;
|
||||
nsRefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
|
||||
|
@ -144,7 +144,7 @@ nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty,
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
if (!cancel && !handled) {
|
||||
// loop thru the ranges in the selection
|
||||
uint32_t rangeCount = selection->GetRangeCount();
|
||||
uint32_t rangeCount = selection->RangeCount();
|
||||
for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
|
||||
nsRefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
|
||||
|
||||
@ -1369,7 +1369,7 @@ nsresult nsHTMLEditor::RemoveInlinePropertyImpl(nsIAtom *aProperty, const nsAStr
|
||||
if (!cancel && !handled)
|
||||
{
|
||||
// loop thru the ranges in the selection
|
||||
uint32_t rangeCount = selection->GetRangeCount();
|
||||
uint32_t rangeCount = selection->RangeCount();
|
||||
for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
|
||||
nsRefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
|
||||
if (aProperty == nsGkAtoms::name) {
|
||||
@ -1542,7 +1542,7 @@ nsHTMLEditor::RelativeFontChange( int32_t aSizeChange)
|
||||
nsAutoTxnsConserveSelection dontSpazMySelection(this);
|
||||
|
||||
// loop thru the ranges in the selection
|
||||
uint32_t rangeCount = selection->GetRangeCount();
|
||||
uint32_t rangeCount = selection->RangeCount();
|
||||
for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
|
||||
nsRefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
|
||||
|
||||
|
@ -55,7 +55,7 @@ nsSelectionState::SaveSelection(Selection* aSel)
|
||||
{
|
||||
MOZ_ASSERT(aSel);
|
||||
int32_t arrayCount = mArray.Length();
|
||||
int32_t rangeCount = aSel->GetRangeCount();
|
||||
int32_t rangeCount = aSel->RangeCount();
|
||||
|
||||
// if we need more items in the array, new them
|
||||
if (arrayCount < rangeCount) {
|
||||
|
@ -2956,7 +2956,7 @@ nsHTMLEditor::GetNextSelectedCell(nsIDOMRange **aRange, nsIDOMElement **aCell)
|
||||
nsRefPtr<Selection> selection = GetSelection();
|
||||
NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
|
||||
|
||||
int32_t rangeCount = selection->GetRangeCount();
|
||||
int32_t rangeCount = selection->RangeCount();
|
||||
|
||||
// Don't even try if index exceeds range count
|
||||
if (mSelectedCellIndex >= rangeCount)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user