Merge m-c to b2ginbound a=merge CLOSED TREE

This commit is contained in:
Wes Kocher 2015-02-26 18:53:42 -08:00
commit 64d28ca0a8
381 changed files with 46922 additions and 8459 deletions

View File

@ -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

View File

@ -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,

View File

@ -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);
}

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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;
};

View File

@ -475,6 +475,9 @@ GetClosestInterestingAccessible(id anObject)
case roles::DEFINITION:
return @"AXDefinition";
case roles::SWITCH:
return @"AXSwitch";
default:
break;
}

View File

@ -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>

View File

@ -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>

View File

@ -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;

View File

@ -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"/>

View File

@ -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>

View File

@ -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

View File

@ -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.

View File

@ -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");
});

View File

@ -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");

View File

@ -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]

View 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");
}
});

View File

@ -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",

View File

@ -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",

View 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;
}

View File

@ -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',
]

View 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");

View 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();
}

View File

@ -0,0 +1,5 @@
[DEFAULT]
head = head.js
firefox-appdir = browser
[test_scheduler.js]

View File

@ -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)");

View File

@ -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
});

View File

@ -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
});

View File

@ -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
});
}

View File

@ -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
*/

View File

@ -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);
});

View File

@ -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.");

View File

@ -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.");

View File

@ -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
});
}
};

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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.

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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()

View File

@ -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;";

View File

@ -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()

View File

@ -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;

View File

@ -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";

View File

@ -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;

View File

@ -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;

View File

@ -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]

View File

@ -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");
}
}

View File

@ -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()) {

View File

@ -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();

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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)

View File

@ -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;
}

View File

@ -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]

View File

@ -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);

View File

@ -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?");

View File

@ -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)

View File

@ -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);

View File

@ -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")

View File

@ -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);

View File

@ -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();

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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.

View File

@ -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():

View File

@ -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;
}

View File

@ -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 */

View File

@ -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]

View 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>

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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,

View File

@ -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;
};
}

View File

@ -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

View File

@ -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);

View File

@ -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) {

View File

@ -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();
}
};

View File

@ -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;
}

View File

@ -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

View File

@ -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) {

View File

@ -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.

View File

@ -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;

View File

@ -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:

View File

@ -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));

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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) {

View File

@ -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