Merge m-c to b2g-inbound.

This commit is contained in:
Ryan VanderMeulen 2014-02-13 10:32:21 -05:00
commit 41ac0e7e12
196 changed files with 4915 additions and 2847 deletions

View File

@ -104,3 +104,4 @@ ba2cc1eda988a1614d8986ae145d28e1268409b9 Tagging for mozilla-central version bum
ba2cc1eda988a1614d8986ae145d28e1268409b9 FIREFOX_AURORA_29_BASE-m
0000000000000000000000000000000000000000 FIREFOX_AURORA_29_BASE-m
ba2cc1eda988a1614d8986ae145d28e1268409b9 FIREFOX_AURORA_29_BASE
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE

View File

@ -13,7 +13,7 @@ ac_add_options --with-gonk-toolchain-prefix="$topsrcdir/gonk-toolchain/prebuilt/
ac_add_options --disable-elf-hack
ac_add_options --enable-debug-symbols
ac_add_options --enable-debug
#ac_add_options --with-ccache
#. "$topsrcdir/build/mozconfig.cache"
ENABLE_MARIONETTE=1
# Enable dump() from JS.

View File

@ -14,7 +14,7 @@ ac_add_options --with-gonk-toolchain-prefix="$topsrcdir/gonk-toolchain/prebuilt/
ac_add_options --disable-elf-hack
ac_add_options --enable-debug-symbols
# ac_add_options --enable-profiling
#ac_add_options --with-ccache
#. "$topsrcdir/build/mozconfig.cache"
ENABLE_MARIONETTE=1
# Enable dump() from JS.

View File

@ -22,7 +22,7 @@ export MOZ_TELEMETRY_REPORTING=1
# DISABLED WHILE NOT ON TRY ac_add_options --enable-warnings-as-errors
# Use ccache
ac_add_options --with-ccache=/usr/bin/ccache
. "$topsrcdir/build/mozconfig.cache"
#B2G options
ac_add_options --enable-application=b2g

View File

@ -22,7 +22,7 @@ export MOZ_TELEMETRY_REPORTING=1
# DISABLED WHILE NOT ON TRY ac_add_options --enable-warnings-as-errors
# Use ccache
ac_add_options --with-ccache=/usr/bin/ccache
. "$topsrcdir/build/mozconfig.cache"
#B2G options
ac_add_options --enable-application=b2g

View File

@ -20,7 +20,7 @@ ac_add_options --enable-warnings-as-errors
# B2G Stuff
ac_add_options --enable-application=b2g
ac_add_options --enable-debug-symbols
ac_add_options --with-ccache
. "$topsrcdir/build/mozconfig.cache"
ENABLE_MARIONETTE=1
export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP

View File

@ -195,8 +195,12 @@ let gFxAccounts = {
},
onMenuPanelCommand: function (event) {
if (event.originalTarget.hasAttribute("signedin")) {
let button = event.originalTarget;
if (button.hasAttribute("signedin")) {
this.openPreferences();
} else if (button.hasAttribute("failed")) {
this.openSignInAgainPage();
} else {
this.openAccountsPage();
}

View File

@ -30,6 +30,13 @@
<menupopup id="customization-toolbar-menu" onpopupshowing="onViewToolbarsPopupShowing(event)"/>
</button>
<spacer flex="1"/>
<label id="customization-undo-reset"
hidden="true"
onclick="gCustomizeMode.undoReset();"
onkeypress="gCustomizeMode.undoReset();"
class="text-link">
&undoCmd.label;
</label>
<button id="customization-reset-button" oncommand="gCustomizeMode.reset();" label="&customizeMode.restoreDefaults;" class="customizationmode-button"/>
</hbox>
</vbox>

View File

@ -174,13 +174,6 @@
this.setAttribute("viewtype", "main");
}
this._mainViewObserver.observe(this._mainView, {
attributes: true,
characterData: true,
childList: true,
subtree: true
});
this._shiftMainView();
]]></body>
</method>
@ -289,7 +282,21 @@
break;
case "popupshowing":
this.setAttribute("panelopen", "true");
// Bug 941196 - The panel can get taller when opening a subview. Disabling
// autoPositioning means that the panel won't jump around if an opened
// subview causes the panel to exceed the dimensions of the screen in the
// direction that the panel originally opened in. This property resets
// every time the popup closes, which is why we have to set it each time.
this._panel.autoPosition = false;
this._syncContainerWithMainView();
this._mainViewObserver.observe(this._mainView, {
attributes: true,
characterData: true,
childList: true,
subtree: true
});
break;
case "popupshown":
this._setMaxHeight();
@ -298,6 +305,7 @@
this.removeAttribute("panelopen");
this._mainView.style.removeProperty("height");
this.showMainView();
this._mainViewObserver.disconnect();
break;
}
]]></body>

View File

@ -128,6 +128,8 @@ let gGroupWrapperCache = new Map();
let gSingleWrapperCache = new WeakMap();
let gListeners = new Set();
let gUIStateBeforeReset = null;
let gModuleName = "[CustomizableUI]";
#include logging.js
@ -693,6 +695,10 @@ let CustomizableUIInternal = {
onWidgetAdded: function(aWidgetId, aArea, aPosition) {
this.insertNode(aWidgetId, aArea, aPosition, true);
if (!gResetting) {
gUIStateBeforeReset = null;
}
},
onWidgetRemoved: function(aWidgetId, aArea) {
@ -749,10 +755,20 @@ let CustomizableUIInternal = {
windowCache.delete(aWidgetId);
}
}
if (!gResetting) {
gUIStateBeforeReset = null;
}
},
onWidgetMoved: function(aWidgetId, aArea, aOldPosition, aNewPosition) {
this.insertNode(aWidgetId, aArea, aNewPosition);
if (!gResetting) {
gUIStateBeforeReset = null;
}
},
onCustomizeEnd: function(aWindow) {
gUIStateBeforeReset = null;
},
registerBuildArea: function(aArea, aNode) {
@ -2049,6 +2065,20 @@ let CustomizableUIInternal = {
reset: function() {
gResetting = true;
this._resetUIState();
// Rebuild each registered area (across windows) to reflect the state that
// was reset above.
this._rebuildRegisteredAreas();
gResetting = false;
},
_resetUIState: function() {
try {
gUIStateBeforeReset = Services.prefs.getCharPref(kPrefCustomizationState);
} catch(e) { }
Services.prefs.clearUserPref(kPrefCustomizationState);
LOG("State reset");
@ -2062,9 +2092,9 @@ let CustomizableUIInternal = {
for (let [areaId,] of gAreas) {
this.restoreStateForArea(areaId);
}
},
// Rebuild each registered area (across windows) to reflect the state that
// was reset above.
_rebuildRegisteredAreas: function() {
for (let [areaId, areaNodes] of gBuildAreas) {
let placements = gPlacements.get(areaId);
for (let areaNode of areaNodes) {
@ -2078,7 +2108,23 @@ let CustomizableUIInternal = {
}
}
}
gResetting = false;
},
/**
* Undoes a previous reset, restoring the state of the UI to the state prior to the reset.
*/
undoReset: function() {
if (!gUIStateBeforeReset) {
return;
}
Services.prefs.setCharPref(kPrefCustomizationState, gUIStateBeforeReset);
this.loadSavedState();
for (let areaId of Object.keys(gSavedState.placements)) {
let placements = gSavedState.placements[areaId];
gPlacements.set(areaId, placements);
}
this._rebuildRegisteredAreas();
gUIStateBeforeReset = null;
},
/**
@ -2832,6 +2878,25 @@ this.CustomizableUI = {
reset: function() {
CustomizableUIInternal.reset();
},
/**
* Undo the previous reset, can only be called immediately after a reset.
* @return a promise that will be resolved when the operation is complete.
*/
undoReset: function() {
CustomizableUIInternal.undoReset();
},
/**
* Can the last Restore Defaults operation be undone.
*
* @return A boolean stating whether an undo of the
* Restore Defaults can be performed.
*/
get canUndoReset() {
return !!gUIStateBeforeReset;
},
/**
* Get the placement of a widget. This is by far the best way to obtain
* information about what the state of your widget is. The internals of

View File

@ -239,6 +239,7 @@ CustomizeMode.prototype = {
document.getElementById("PanelUI-quit").setAttribute("disabled", true);
this._updateResetButton();
this._updateUndoResetButton();
this._skipSourceNodeCheck = Services.prefs.getPrefType(kSkipSourceNodePref) == Ci.nsIPrefBranch.PREF_BOOL &&
Services.prefs.getBoolPref(kSkipSourceNodePref);
@ -854,12 +855,35 @@ CustomizeMode.prototype = {
this.persistCurrentSets(true);
this._updateResetButton();
this._updateUndoResetButton();
this._updateEmptyPaletteNotice();
this._showPanelCustomizationPlaceholders();
this.resetting = false;
}.bind(this)).then(null, ERROR);
},
undoReset: function() {
this.resetting = true;
return Task.spawn(function() {
this._removePanelCustomizationPlaceholders();
yield this.depopulatePalette();
yield this._unwrapToolbarItems();
CustomizableUI.undoReset();
yield this._wrapToolbarItems();
yield this.populatePalette();
this.persistCurrentSets(true);
this._updateResetButton();
this._updateUndoResetButton();
this._updateEmptyPaletteNotice();
this.resetting = false;
}.bind(this)).then(null, ERROR);
},
_onToolbarVisibilityChange: function(aEvent) {
let toolbar = aEvent.target;
if (aEvent.detail.visible && toolbar.getAttribute("customizable") == "true") {
@ -958,6 +982,7 @@ CustomizeMode.prototype = {
this._changed = true;
if (!this.resetting) {
this._updateResetButton();
this._updateUndoResetButton();
this._updateEmptyPaletteNotice();
}
this.dispatchToolboxEvent("customizationchange");
@ -973,6 +998,11 @@ CustomizeMode.prototype = {
btn.disabled = CustomizableUI.inDefaultState;
},
_updateUndoResetButton: function() {
let undoReset = this.document.getElementById("customization-undo-reset");
undoReset.hidden = !CustomizableUI.canUndoReset;
},
handleEvent: function(aEvent) {
switch(aEvent.type) {
case "toolbarvisibilitychange":

View File

@ -65,4 +65,5 @@ skip-if = os == "linux"
[browser_956602_remove_special_widget.js]
[browser_969427_recreate_destroyed_widget_after_reset.js]
[browser_969661_character_encoding_navbar_disabled.js]
[browser_970511_undo_restore_default.js]
[browser_panel_toggle.js]

View File

@ -0,0 +1,65 @@
/* 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";
// Restoring default should show an "undo" option which undoes the restoring operation.
add_task(function() {
let homeButtonId = "home-button";
CustomizableUI.removeWidgetFromArea(homeButtonId);
yield startCustomizing();
ok(!CustomizableUI.inDefaultState, "Not in default state to begin with");
is(CustomizableUI.getPlacementOfWidget(homeButtonId), null, "Home button is in palette");
let undoReset = document.getElementById("customization-undo-reset");
is(undoReset.hidden, true, "The undo button is hidden before reset");
yield gCustomizeMode.reset();
ok(CustomizableUI.inDefaultState, "In default state after reset");
is(undoReset.hidden, false, "The undo button is visible after reset");
undoReset.click();
yield waitForCondition(function() !gCustomizeMode.resetting);
ok(!CustomizableUI.inDefaultState, "Not in default state after reset-undo");
is(undoReset.hidden, true, "The undo button is hidden after clicking on the undo button");
is(CustomizableUI.getPlacementOfWidget(homeButtonId), null, "Home button is in palette");
yield gCustomizeMode.reset();
});
// Performing an action after a reset will hide the reset button.
add_task(function() {
let homeButtonId = "home-button";
CustomizableUI.removeWidgetFromArea(homeButtonId);
ok(!CustomizableUI.inDefaultState, "Not in default state to begin with");
is(CustomizableUI.getPlacementOfWidget(homeButtonId), null, "Home button is in palette");
let undoReset = document.getElementById("customization-undo-reset");
is(undoReset.hidden, true, "The undo button is hidden before reset");
yield gCustomizeMode.reset();
ok(CustomizableUI.inDefaultState, "In default state after reset");
is(undoReset.hidden, false, "The undo button is visible after reset");
CustomizableUI.addWidgetToArea(homeButtonId, CustomizableUI.AREA_PANEL);
is(undoReset.hidden, true, "The undo button is hidden after another change");
});
// "Restore defaults", exiting customize, and re-entering shouldn't show the Undo button
add_task(function() {
let undoReset = document.getElementById("customization-undo-reset");
is(undoReset.hidden, true, "The undo button is hidden before a reset");
ok(!CustomizableUI.inDefaultState, "The browser should not be in default state");
yield gCustomizeMode.reset();
is(undoReset.hidden, false, "The undo button is hidden after a reset");
yield endCustomizing();
yield startCustomizing();
is(undoReset.hidden, true, "The undo reset button should be hidden after entering customization mode");
});
add_task(function asyncCleanup() {
yield gCustomizeMode.reset();
yield endCustomizing();
});

View File

@ -10,7 +10,7 @@ ac_add_options --disable-unified-compilation
export MOZILLA_OFFICIAL=1
#Use ccache
ac_add_options --with-ccache=/usr/bin/ccache
. "$topsrcdir/build/mozconfig.cache"
# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
ac_add_options --enable-warnings-as-errors

View File

@ -13,6 +13,6 @@ ac_add_options --enable-js-diagnostics
STRIP_FLAGS="--strip-debug"
# Use ccache
ac_add_options --with-ccache=/usr/bin/ccache
. "$topsrcdir/build/mozconfig.cache"
. "$topsrcdir/build/mozconfig.common.override"

View File

@ -8,7 +8,7 @@ ac_add_options --enable-signmar
export MOZILLA_OFFICIAL=1
# Use ccache
ac_add_options --with-ccache=/usr/bin/ccache
. "$topsrcdir/build/mozconfig.cache"
# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
ac_add_options --enable-warnings-as-errors

View File

@ -13,6 +13,6 @@ ac_add_options --enable-js-diagnostics
STRIP_FLAGS="--strip-debug"
# Use ccache
ac_add_options --with-ccache=/usr/bin/ccache
. "$topsrcdir/build/mozconfig.cache"
. "$topsrcdir/build/mozconfig.common.override"

View File

@ -4,7 +4,7 @@ ac_add_options --with-l10n-base=../../../l10n
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging
ac_add_options --with-macbundlename-prefix=Firefox
ac_add_options --with-ccache
. "$topsrcdir/build/mozconfig.cache"
export MOZILLA_OFFICIAL=1

View File

@ -3,6 +3,6 @@
ac_add_options --with-l10n-base=../../l10n
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging
ac_add_options --with-ccache
. "$topsrcdir/build/mozconfig.cache"
. "$topsrcdir/build/mozconfig.common.override"

View File

@ -29,6 +29,7 @@ whitelist['nightly']['linux32'] += [
'CC="ccache $REAL_CC"',
'mk_add_options PROFILE_GEN_SCRIPT=@TOPSRCDIR@/build/profile_pageloader.pl',
'ac_add_options --with-ccache=/usr/bin/ccache',
'. "$topsrcdir/build/mozconfig.cache"',
'export MOZILLA_OFFICIAL=1',
'export MOZ_TELEMETRY_REPORTING=1',
"mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) @MOZ_OBJDIR@/_profile/pgo/profileserver.py 10'",
@ -42,6 +43,7 @@ whitelist['nightly']['linux64'] += [
"mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) @MOZ_OBJDIR@/_profile/pgo/profileserver.py 10'",
'STRIP_FLAGS="--strip-debug"',
'ac_add_options --with-ccache=/usr/bin/ccache',
'. "$topsrcdir/build/mozconfig.cache"',
'ac_add_options --disable-elf-hack # --enable-elf-hack conflicts with --enable-profiling',
]
@ -49,6 +51,7 @@ whitelist['nightly']['macosx-universal'] += [
'ac_add_options --with-macbundlename-prefix=Firefox',
'mk_add_options MOZ_MAKE_FLAGS="-j12"',
'ac_add_options --with-ccache',
'. "$topsrcdir/build/mozconfig.cache"',
'ac_add_options --disable-install-strip',
'ac_add_options --enable-instruments',
'ac_add_options --enable-dtrace',

View File

@ -1,7 +1,7 @@
[
{
"size": 40,
"digest": "459b332864aece4742cd1a6886e56cf3f202e5c27bb481cfae6145ce3e2e52fb34d1448788c6618e58a26a64e415341895326d293e0d2968e56efc0ae990acd0",
"size": 81,
"digest": "59002eae04fa3534df3bbb0026dff50d3872313551514f9ccdf33080c8b8e34a6295b3b6f2c078b3ddef099023897a42adb65d34c9364f84dac5b8e7d022bf39",
"algorithm": "sha512",
"filename": "setup.sh"
},
@ -10,5 +10,11 @@
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
"algorithm": "sha512",
"filename": "gcc.tar.xz"
},
{
"size": 150816,
"digest": "af25ecf03b65795d21f011939984b130db167a4efc4f306700f373854f9d7ae664662cb7812c3d737eace7f3735324daa6eb540b5e42f90189b0d9a8dd5f4c9f",
"algorithm": "sha512",
"filename": "sccache.tar.xz"
}
]

View File

@ -1,7 +1,7 @@
[
{
"size": 40,
"digest": "459b332864aece4742cd1a6886e56cf3f202e5c27bb481cfae6145ce3e2e52fb34d1448788c6618e58a26a64e415341895326d293e0d2968e56efc0ae990acd0",
"size": 81,
"digest": "59002eae04fa3534df3bbb0026dff50d3872313551514f9ccdf33080c8b8e34a6295b3b6f2c078b3ddef099023897a42adb65d34c9364f84dac5b8e7d022bf39",
"algorithm": "sha512",
"filename": "setup.sh"
},
@ -10,5 +10,11 @@
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
"algorithm": "sha512",
"filename": "gcc.tar.xz"
},
{
"size": 150816,
"digest": "af25ecf03b65795d21f011939984b130db167a4efc4f306700f373854f9d7ae664662cb7812c3d737eace7f3735324daa6eb540b5e42f90189b0d9a8dd5f4c9f",
"algorithm": "sha512",
"filename": "sccache.tar.xz"
}
]

View File

@ -240,7 +240,11 @@ let DebuggerController = {
} else {
this._startDebuggingTab(startedDebugging.resolve);
const startedTracing = promise.defer();
this._startTracingTab(traceActor, startedTracing.resolve);
if (Prefs.tracerEnabled && traceActor) {
this._startTracingTab(traceActor, startedTracing.resolve);
} else {
startedTracing.resolve();
}
return promise.all([startedDebugging.promise, startedTracing.promise]);
}

View File

@ -214,6 +214,7 @@ support-files =
[browser_dbg_tracing-03.js]
[browser_dbg_tracing-04.js]
[browser_dbg_tracing-05.js]
[browser_dbg_tracing-06.js]
[browser_dbg_variables-view-01.js]
[browser_dbg_variables-view-02.js]
[browser_dbg_variables-view-03.js]

View File

@ -0,0 +1,39 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that the tracer doesn't connect to the backend when tracing is disabled.
*/
const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
const TRACER_PREF = "devtools.debugger.tracer";
let gTab, gDebuggee, gPanel, gDebugger;
let gOriginalPref = Services.prefs.getBoolPref(TRACER_PREF);
Services.prefs.setBoolPref(TRACER_PREF, false);
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
gTab = aTab;
gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
waitForSourceShown(gPanel, "code_tracing-01.js")
.then(() => {
ok(!gDebugger.DebuggerController.traceClient, "Should not have a trace client");
closeDebuggerAndFinish(gPanel);
})
.then(null, aError => {
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
});
});
}
registerCleanupFunction(function() {
gTab = null;
gDebuggee = null;
gPanel = null;
gDebugger = null;
Services.prefs.setBoolPref(TRACER_PREF, gOriginalPref);
});

View File

@ -13,8 +13,10 @@ support-files =
# Bug 916763 - too many intermittent failures
skip-if = true
[browser_inspector_markup_edit.js]
# Bug 904953 - too many intermittent failures on Linux
skip-if = os == "linux"
[browser_inspector_markup_edit_2.js]
[browser_inspector_markup_edit_3.js]
[browser_inspector_markup_edit_4.js]
[browser_inspector_markup_add_attributes.js]
[browser_inspector_markup_edit_outerhtml.js]
[browser_inspector_markup_edit_outerhtml2.js]
[browser_inspector_markup_mutation.js]

View File

@ -0,0 +1,170 @@
/* Any copyright", " is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests that adding various types of attributes to nodes in the markup-view
* works as expected. Also checks that the changes are properly undoable and
* redoable. For each step in the test, we:
* - Create a new DIV
* - Make the change, check that the change was made as we expect
* - Undo the change, check that the node is back in its original state
* - Redo the change, check that the node change was made again correctly.
*/
waitForExplicitFinish();
let TEST_URL = "data:text/html,<div>markup-view attributes addition test</div>";
let TEST_DATA = [{
desc: "Add an attribute value without closing \"",
enteredText: 'style="display: block;',
expectedAttributes: {
style: "display: block;"
}
}, {
desc: "Add an attribute value without closing '",
enteredText: "style='display: inline;",
expectedAttributes: {
style: "display: inline;"
}
}, {
desc: "Add an attribute wrapped with with double quotes double quote in it",
enteredText: 'style="display: "inline',
expectedAttributes: {
style: "display: ",
inline: ""
}
}, {
desc: "Add an attribute wrapped with single quotes with single quote in it",
enteredText: "style='display: 'inline",
expectedAttributes: {
style: "display: ",
inline: ""
}
}, {
desc: "Add an attribute with no value",
enteredText: "disabled",
expectedAttributes: {
disabled: ""
}
}, {
desc: "Add multiple attributes with no value",
enteredText: "disabled autofocus",
expectedAttributes: {
disabled: "",
autofocus: ""
}
}, {
desc: "Add multiple attributes with no value, and some with value",
enteredText: "disabled name='name' data-test='test' autofocus",
expectedAttributes: {
disabled: "",
autofocus: "",
name: "name",
'data-test': "test"
}
}, {
desc: "Add attribute with xmlns",
enteredText: "xmlns:edi='http://ecommerce.example.org/schema'",
expectedAttributes: {
'xmlns:edi': "http://ecommerce.example.org/schema"
}
}, {
desc: "Mixed single and double quotes",
enteredText: "name=\"hi\" maxlength='not a number'",
expectedAttributes: {
maxlength: "not a number",
name: "hi"
}
}, {
desc: "Invalid attribute name",
enteredText: "x='y' <why-would-you-do-this>=\"???\"",
expectedAttributes: {
x: "y"
}
}, {
desc: "Double quote wrapped in single quotes",
enteredText: "x='h\"i'",
expectedAttributes: {
x: "h\"i"
}
}, {
desc: "Single quote wrapped in double quotes",
enteredText: "x=\"h'i\"",
expectedAttributes: {
x: "h'i"
}
}, {
desc: "No quote wrapping",
enteredText: "a=b x=y data-test=Some spaced data",
expectedAttributes: {
a: "b",
x: "y",
"data-test": "Some",
spaced: "",
data: ""
}
}, {
desc: "Duplicate Attributes",
enteredText: "a=b a='c' a=\"d\"",
expectedAttributes: {
a: "b"
}
}, {
desc: "Inline styles",
enteredText: "style=\"font-family: 'Lucida Grande', sans-serif; font-size: 75%;\"",
expectedAttributes: {
style: "font-family: 'Lucida Grande', sans-serif; font-size: 75%;"
}
}, {
desc: "Object attribute names",
enteredText: "toString=\"true\" hasOwnProperty=\"false\"",
expectedAttributes: {
toString: "true",
hasOwnProperty: "false"
}
}, {
desc: "Add event handlers",
enteredText: "onclick=\"javascript: throw new Error('wont fire');\" onload=\"alert('here');\"",
expectedAttributes: {
onclick: "javascript: throw new Error('wont fire');",
onload: "alert('here');"
}
}];
function test() {
Task.spawn(function() {
info("Opening the inspector on the test URL");
let args = yield addTab(TEST_URL).then(openInspector);
let inspector = args.inspector;
let markup = inspector.markup;
info("Selecting the test node");
let div = getNode("div");
yield selectNode(div, inspector);
let editor = getContainerForRawNode(markup, div).editor;
for (let test of TEST_DATA) {
info("Starting test: " + test.desc);
info("Enter the new attribute(s) test: " + test.enteredText);
let nodeMutated = inspector.once("markupmutation");
setEditableFieldValue(editor.newAttr, test.enteredText, inspector);
yield nodeMutated;
info("Assert that the attribute(s) has/have been applied correctly");
assertAttributes(div, test.expectedAttributes);
info("Undo the change");
yield undoChange(inspector);
info("Assert that the attribute(s) has/have been removed correctly");
assertAttributes(div, {});
}
yield inspector.once("inspector-updated");
gBrowser.removeCurrentTab();
}).then(null, ok.bind(null, false)).then(finish);
}

View File

@ -0,0 +1,51 @@
/* Any copyright", " is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests that an existing attribute can be modified
waitForExplicitFinish();
const TEST_URL = "data:text/html,<div id='test-div'>Test modifying my ID attribute</div>";
function test() {
Task.spawn(function() {
info("Opening the inspector on the test page");
let {toolbox, inspector} = yield addTab(TEST_URL).then(openInspector);
info("Selecting the test node");
let node = content.document.getElementById("test-div");
yield selectNode(node, inspector);
info("Verify attributes, only ID should be there for now");
assertAttributes(node, {
id: "test-div"
});
info("Focus the ID attribute and change its content");
let editor = getContainerForRawNode(inspector.markup, node).editor;
let attr = editor.attrs["id"].querySelector(".editable");
let mutated = inspector.once("markupmutation");
setEditableFieldValue(attr,
attr.textContent + ' class="newclass" style="color:green"', inspector);
yield mutated;
info("Verify attributes, should have ID, class and style");
assertAttributes(node, {
id: "test-div",
class: "newclass",
style: "color:green"
});
info("Trying to undo the change");
yield undoChange(inspector);
assertAttributes(node, {
id: "test-div"
});
yield inspector.once("inspector-updated");
gBrowser.removeCurrentTab();
}).then(null, ok.bind(null, false)).then(finish);
}

View File

@ -0,0 +1,45 @@
/* Any copyright", " is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests that a node's tagname can be edited in the markup-view
waitForExplicitFinish();
const TEST_URL = "data:text/html,<div id='retag-me'><div id='retag-me-2'></div></div>";
function test() {
Task.spawn(function() {
info("Opening the inspector on the test page");
let {toolbox, inspector} = yield addTab(TEST_URL).then(openInspector);
yield inspector.markup.expandAll();
info("Selecting the test node");
let node = content.document.getElementById("retag-me");
let child = content.document.querySelector("#retag-me-2");
yield selectNode(node, inspector);
let container = getContainerForRawNode(inspector.markup, node);
is(node.tagName, "DIV", "We've got #retag-me element, it's a DIV");
ok(container.expanded, "It is expanded");
is(child.parentNode, node, "Child #retag-me-2 is inside #retag-me");
info("Changing the tagname");
let mutated = inspector.once("markupmutation");
let tagEditor = container.editor.tag;
setEditableFieldValue(tagEditor, "p", inspector);
yield mutated;
info("Checking that the tagname change was done");
let node = content.document.getElementById("retag-me");
let container = getContainerForRawNode(inspector.markup, node);
is(node.tagName, "P", "We've got #retag-me, it should now be a P");
ok(container.expanded, "It is still expanded");
ok(container.selected, "It is still selected");
is(child.parentNode, node, "Child #retag-me-2 is still inside #retag-me");
gBrowser.removeCurrentTab();
}).then(null, ok.bind(null, false)).then(finish);
}

View File

@ -0,0 +1,36 @@
/* Any copyright", " is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests that a node can be deleted from the markup-view with the delete key
waitForExplicitFinish();
const TEST_URL = "data:text/html,<div id='delete-me'></div>";
function test() {
Task.spawn(function() {
info("Opening the inspector on the test page");
let {toolbox, inspector} = yield addTab(TEST_URL).then(openInspector);
info("Selecting the test node by clicking on it to make sure it receives focus");
let node = content.document.getElementById("delete-me");
yield clickContainer(node, inspector);
info("Deleting the element with the keyboard");
let mutated = inspector.once("markupmutation");
EventUtils.sendKey("delete", inspector.panelWin);
yield mutated;
info("Checking that it's gone, baby gone!");
ok(!content.document.getElementById("delete-me"), "The test node does not exist");
yield undoChange(inspector);
ok(content.document.getElementById("delete-me"), "The test node is back!");
yield inspector.once("inspector-updated");
gBrowser.removeCurrentTab();
}).then(null, ok.bind(null, false)).then(finish);
}

View File

@ -8,6 +8,7 @@ let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
let TargetFactory = devtools.TargetFactory;
let {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
let promise = devtools.require("sdk/core/promise");
let {getInplaceEditorForSpan: inplaceEditor} = devtools.require("devtools/shared/inplace-editor");
// Clear preferences that may be set during the course of tests.
function clearUserPrefs() {
@ -18,15 +19,23 @@ function clearUserPrefs() {
registerCleanupFunction(clearUserPrefs);
Services.prefs.setBoolPref("devtools.debugger.log", true);
SimpleTest.registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.debugger.log");
});
/**
* Add a new test tab in the browser and load the given url.
* @param {String} url The url to be loaded in the new tab
* @return a promise that resolves when the url is loaded
*/
function addTab(url) {
let def = promise.defer();
function getContainerForRawNode(markupView, rawNode) {
let front = markupView.walker.frontForRawNode(rawNode);
let container = markupView.getContainer(front);
return container;
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onload() {
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
info("URL " + url + " loading complete into new test tab");
waitForFocus(def.resolve, content);
}, true);
content.location = url;
return def.promise;
}
/**
@ -34,19 +43,41 @@ function getContainerForRawNode(markupView, rawNode) {
* @return a promise that resolves when the inspector is ready
*/
function openInspector() {
let deferred = promise.defer();
let def = promise.defer();
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
info("Toolbox open");
let inspector = toolbox.getCurrentPanel();
inspector.once("inspector-updated", () => {
deferred.resolve({toolbox: toolbox, inspector: inspector});
info("Inspector panel active and ready");
def.resolve({toolbox: toolbox, inspector: inspector});
});
}).then(null, console.error);
return deferred.promise;
return def.promise;
}
/**
* Get the MarkupContainer object instance that corresponds to the given
* HTML node
* @param {MarkupView} markupView The instance of MarkupView currently loaded into the inspector panel
* @param {DOMNode} rawNode The DOM node for which the container is required
* @return {MarkupContainer}
*/
function getContainerForRawNode(markupView, rawNode) {
let front = markupView.walker.frontForRawNode(rawNode);
let container = markupView.getContainer(front);
ok(container, "A markup-container object was found");
return container;
}
/**
* Simple DOM node accesor function that takes either a node or a string css
* selector as argument and returns the corresponding node
* @param {String|DOMNode} nodeOrSelector
* @return {DOMNode}
*/
function getNode(nodeOrSelector) {
let node = nodeOrSelector;
@ -61,23 +92,30 @@ function getNode(nodeOrSelector) {
/**
* Set the inspector's current selection to a node or to the first match of the
* given css selector
* @param {String|DOMNode} nodeOrSelector
* @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
* @param {String} reason Defaults to "test" which instructs the inspector not to highlight the node upon selection
* @return a promise that resolves when the inspector is updated with the new
* node
*/
function selectNode(nodeOrSelector, inspector) {
function selectNode(nodeOrSelector, inspector, reason="test") {
info("Selecting the node " + nodeOrSelector);
let node = getNode(nodeOrSelector);
let updated = inspector.once("inspector-updated");
inspector.selection.setNode(node, "test");
inspector.selection.setNode(node, reason);
return updated;
}
/**
* Simulate a mouse-over on the markup-container (a line in the markup-view)
* that corresponds to the node or selector passed.
* @param {String|DOMNode} nodeOrSelector
* @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
* @return a promise that resolves when the container is hovered and the higlighter
* is shown on the corresponding node
*/
function hoverContainer(nodeOrSelector, inspector) {
info("Hovering over the markup-container for node " + nodeOrSelector);
let highlit = inspector.toolbox.once("node-highlight");
let container = getContainerForRawNode(inspector.markup, getNode(nodeOrSelector));
EventUtils.synthesizeMouse(container.tagLine, 2, 2, {type: "mousemove"},
@ -88,9 +126,12 @@ function hoverContainer(nodeOrSelector, inspector) {
/**
* Simulate a click on the markup-container (a line in the markup-view)
* that corresponds to the node or selector passed.
* @param {String|DOMNode} nodeOrSelector
* @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
* @return a promise that resolves when the node has been selected.
*/
function clickContainer(nodeOrSelector, inspector) {
info("Clicking on the markup-container for node " + nodeOrSelector);
let updated = inspector.once("inspector-updated");
let container = getContainerForRawNode(inspector.markup, getNode(nodeOrSelector));
EventUtils.synthesizeMouseAtCenter(container.tagLine, {type: "mousedown"},
@ -102,6 +143,7 @@ function clickContainer(nodeOrSelector, inspector) {
/**
* Checks if the highlighter is visible currently
* @return {Boolean}
*/
function isHighlighterVisible() {
let outline = gBrowser.selectedBrowser.parentNode.querySelector(".highlighter-container .highlighter-outline");
@ -110,18 +152,91 @@ function isHighlighterVisible() {
/**
* Simulate the mouse leaving the markup-view area
* @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
* @return a promise when done
*/
function mouseLeaveMarkupView(inspector) {
let deferred = promise.defer();
info("Leaving the markup-view area");
let def = promise.defer();
// Find another element to mouseover over in order to leave the markup-view
let btn = inspector.toolbox.doc.querySelector(".toolbox-dock-button");
EventUtils.synthesizeMouse(btn, 2, 2, {type: "mousemove"},
inspector.toolbox.doc.defaultView);
executeSoon(deferred.resolve);
executeSoon(def.resolve);
return deferred.promise;
return def.promise;
}
/**
* Focus a given editable element, enter edit mode, set value, and commit
* @param {DOMNode} field The element that gets editable after receiving focus and <ENTER> keypress
* @param {String} value The string value to be set into the edited field
* @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
*/
function setEditableFieldValue(field, value, inspector) {
field.focus();
EventUtils.sendKey("return", inspector.panelWin);
let input = inplaceEditor(field).input;
ok(input, "Found editable field for setting value: " + value);
input.value = value;
EventUtils.sendKey("return", inspector.panelWin);
}
/**
* Checks that a node has the given attributes
*
* @param {HTMLNode} element The node to check.
* @param {Object} attrs An object containing the attributes to check.
* e.g. {id: "id1", class: "someclass"}
*
* Note that node.getAttribute() returns attribute values provided by the HTML
* parser. The parser only provides unescaped entities so &amp; will return &.
*/
function assertAttributes(element, attrs) {
is(element.attributes.length, Object.keys(attrs).length,
"Node has the correct number of attributes.");
for (let attr in attrs) {
is(element.getAttribute(attr), attrs[attr],
"Node has the correct " + attr + " attribute.");
}
}
/**
* Undo the last markup-view action and wait for the corresponding mutation to
* occur
* @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
* @return a promise that resolves when the markup-mutation has been treated or
* rejects if no undo action is possible
*/
function undoChange(inspector) {
let canUndo = inspector.markup.undo.canUndo();
ok(canUndo, "The last change in the markup-view can be undone");
if (!canUndo) {
return promise.reject();
}
let mutated = inspector.once("markupmutation");
inspector.markup.undo.undo();
return mutated;
}
/**
* Redo the last markup-view action and wait for the corresponding mutation to
* occur
* @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
* @return a promise that resolves when the markup-mutation has been treated or
* rejects if no redo action is possible
*/
function redoChange(inspector) {
let canRedo = inspector.markup.undo.canRedo();
ok(canRedo, "The last change in the markup-view can be redone");
if (!canRedo) {
return promise.reject();
}
let mutated = inspector.once("markupmutation");
inspector.markup.undo.redo();
return mutated;
}

View File

@ -0,0 +1,153 @@
/* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 cssTokenizer = require("devtools/sourceeditor/css-tokenizer");
/**
* Returns the string enclosed in quotes
*/
function quoteString(string) {
let hasDoubleQuotes = string.contains('"');
let hasSingleQuotes = string.contains("'");
if (hasDoubleQuotes && !hasSingleQuotes) {
// In this case, no escaping required, just enclose in single-quotes
return "'" + string + "'";
}
// In all other cases, enclose in double-quotes, and escape any double-quote
// that may be in the string
return '"' + string.replace(/"/g, '\"') + '"';
}
/**
* Returns an array of CSS declarations given an string.
* For example, parseDeclarations("width: 1px; height: 1px") would return
* [{name:"width", value: "1px"}, {name: "height", "value": "1px"}]
*
* The input string is assumed to only contain declarations so { and } characters
* will be treated as part of either the property or value, depending where it's
* found.
*
* @param {string} inputString
* An input string of CSS
* @return {Array} an array of objects with the following signature:
* [{"name": string, "value": string, "priority": string}, ...]
*/
function parseDeclarations(inputString) {
let tokens = cssTokenizer(inputString);
let declarations = [{name: "", value: "", priority: ""}];
let current = "", hasBang = false, lastProp;
for (let token of tokens) {
lastProp = declarations[declarations.length - 1];
if (token.tokenType === ":") {
if (!lastProp.name) {
// Set the current declaration name if there's no name yet
lastProp.name = current.trim();
current = "";
hasBang = false;
} else {
// Otherwise, just append ':' to the current value (declaration value
// with colons)
current += ":";
}
} else if (token.tokenType === ";") {
lastProp.value = current.trim();
current = "";
hasBang = false;
declarations.push({name: "", value: "", priority: ""});
} else {
switch(token.tokenType) {
case "IDENT":
if (token.value === "important" && hasBang) {
lastProp.priority = "important";
hasBang = false;
} else {
if (hasBang) {
current += "!";
}
current += token.value;
}
break;
case "WHITESPACE":
current += " ";
break;
case "DIMENSION":
current += token.repr;
break;
case "HASH":
current += "#" + token.value;
break;
case "URL":
current += "url(" + quoteString(token.value) + ")";
break;
case "FUNCTION":
current += token.value + "(";
break;
case ")":
current += token.tokenType;
break;
case "EOF":
break;
case "DELIM":
if (token.value === "!") {
hasBang = true;
} else {
current += token.value;
}
break;
case "STRING":
current += quoteString(token.value);
break;
case "{":
case "}":
current += token.tokenType;
break;
default:
current += token.value;
break;
}
}
}
// Handle whatever trailing properties or values might still be there
if (current) {
if (!lastProp.name) {
// Trailing property found, e.g. p1:v1;p2:v2;p3
lastProp.name = current.trim();
} else {
// Trailing value found, i.e. value without an ending ;
lastProp.value += current.trim();
}
}
// Remove declarations that have neither a name nor a value
declarations = declarations.filter(prop => prop.name || prop.value);
return declarations;
};
exports.parseDeclarations = parseDeclarations;
/**
* Expects a single CSS value to be passed as the input and parses the value
* and priority.
*
* @param {string} value The value from the text editor.
* @return {object} an object with 'value' and 'priority' properties.
*/
function parseSingleValue(value) {
let declaration = parseDeclarations("a: " + value + ";")[0];
return {
value: declaration ? declaration.value : "",
priority: declaration ? declaration.priority : ""
};
};
exports.parseSingleValue = parseSingleValue;

View File

@ -14,7 +14,8 @@ const {ELEMENT_STYLE, PSEUDO_ELEMENTS} = require("devtools/server/actors/styles"
const {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
const {Tooltip, SwatchColorPickerTooltip} = require("devtools/shared/widgets/Tooltip");
const {OutputParser} = require("devtools/output-parser");
const { PrefObserver, PREF_ORIG_SOURCES } = require("devtools/styleeditor/utils");
const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/styleeditor/utils");
const {parseSingleValue, parseDeclarations} = require("devtools/styleinspector/css-parsing-utils");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -582,7 +583,7 @@ Rule.prototype = {
let promise = aModifications.apply().then(() => {
let cssProps = {};
for (let cssProp of parseCSSText(this.style.cssText)) {
for (let cssProp of parseDeclarations(this.style.cssText)) {
cssProps[cssProp.name] = cssProp;
}
@ -692,7 +693,7 @@ Rule.prototype = {
_getTextProperties: function() {
let textProps = [];
let store = this.elementStyle.store;
let props = parseCSSText(this.style.cssText);
let props = parseDeclarations(this.style.cssText);
for (let prop of props) {
let name = prop.name;
if (this.inherited && !domUtils.isInheritedProperty(name)) {
@ -1850,17 +1851,13 @@ RuleEditor.prototype = {
return;
}
// Deal with adding declarations later (once editor has been destroyed).
// If aValue is just a name, will make a new property with empty value.
this.multipleAddedProperties = parseCSSText(aValue);
if (!this.multipleAddedProperties.length) {
this.multipleAddedProperties = [{
name: aValue,
value: "",
priority: ""
}];
}
// parseDeclarations allows for name-less declarations, but in the present
// case, we're creating a new declaration, it doesn't make sense to accept
// these entries
this.multipleAddedProperties = parseDeclarations(aValue).filter(d => d.name);
// Blur the editor field now and deal with adding declarations later when
// the field gets destroyed (see _newPropertyDestroy)
this.editor.input.blur();
},
@ -2263,17 +2260,16 @@ TextPropertyEditor.prototype = {
if (aValue.trim() === "") {
this.remove();
} else {
// Adding multiple rules inside of name field overwrites the current
// property with the first, then adds any more onto the property list.
let properties = parseCSSText(aValue);
if (properties.length > 0) {
this.prop.setName(properties[0].name);
this.prop.setValue(properties[0].value, properties[0].priority);
let properties = parseDeclarations(aValue);
this.ruleEditor.addProperties(properties.slice(1), this.prop);
} else {
this.prop.setName(aValue);
if (properties.length) {
this.prop.setName(properties[0].name);
if (properties.length > 1) {
this.prop.setValue(properties[0].value, properties[0].priority);
this.ruleEditor.addProperties(properties.slice(1), this.prop);
}
}
}
}
@ -2320,7 +2316,7 @@ TextPropertyEditor.prototype = {
let {propertiesToAdd,firstValue} = this._getValueAndExtraProperties(aValue);
// First, set this property value (common case, only modified a property)
let val = parseCSSValue(firstValue);
let val = parseSingleValue(firstValue);
this.prop.setValue(val.value, val.priority);
this.removeOnRevert = false;
this.committed.value = this.prop.value;
@ -2356,36 +2352,31 @@ TextPropertyEditor.prototype = {
* firstValue: A string containing a simple value, like
* "red" or "100px!important"
* propertiesToAdd: An array with additional properties, following the
* parseCSSText format of {name,value,priority}
* parseDeclarations format of {name,value,priority}
*/
_getValueAndExtraProperties: function(aValue) {
// The inplace editor will prevent manual typing of multiple properties,
// but we need to deal with the case during a paste event.
// Adding multiple properties inside of value editor sets value with the
// first, then adds any more onto the property list (below this property).
let properties = parseCSSText(aValue);
let propertiesToAdd = [];
let firstValue = aValue;
let propertiesToAdd = [];
if (properties.length > 0) {
// If text like "red; width: 1px;" was entered in, handle this as two
// separate properties (setting value here to red and adding a new prop).
let propertiesNoName = parseCSSText("a:" + aValue);
let enteredValueFirst = propertiesNoName.length > properties.length;
let properties = parseDeclarations(aValue);
let firstProp = properties[0];
propertiesToAdd = properties.slice(1);
if (enteredValueFirst) {
firstProp = propertiesNoName[0];
propertiesToAdd = propertiesNoName.slice(1);
// Check to see if the input string can be parsed as multiple properties
if (properties.length) {
// Get the first property value (if any), and any remaining properties (if any)
if (!properties[0].name && properties[0].value) {
firstValue = properties[0].value;
propertiesToAdd = properties.slice(1);
}
// In some cases, the value could be a property:value pair itself.
// Join them as one value string and append potentially following properties
else if (properties[0].name && properties[0].value) {
firstValue = properties[0].name + ": " + properties[0].value;
propertiesToAdd = properties.slice(1);
}
// If "red; width: 1px", then set value to "red"
// If "color: red; width: 1px;", then set value to "color: red;"
firstValue = enteredValueFirst ?
firstProp.value + "!" + firstProp.priority :
firstProp.name + ": " + firstProp.value + "!" + firstProp.priority;
}
return {
@ -2395,7 +2386,7 @@ TextPropertyEditor.prototype = {
},
_applyNewValue: function(aValue) {
let val = parseCSSValue(aValue);
let val = parseSingleValue(aValue);
// Any property should be removed if has an empty value.
if (val.value.trim() === "") {
this.remove();
@ -2419,7 +2410,7 @@ TextPropertyEditor.prototype = {
return;
}
let val = parseCSSValue(aValue);
let val = parseSingleValue(aValue);
// Live previewing the change without committing just yet, that'll be done in _onValueDone
// If it was not a valid value, apply an empty string to reset the live preview
@ -2439,7 +2430,7 @@ TextPropertyEditor.prototype = {
isValid: function(aValue) {
let name = this.prop.name;
let value = typeof aValue == "undefined" ? this.prop.value : aValue;
let val = parseCSSValue(value);
let val = parseSingleValue(value);
let style = this.doc.createElementNS(HTML_NS, "div").style;
let prefs = Services.prefs;
@ -2605,64 +2596,14 @@ function throttle(func, wait, scope) {
};
}
/**
* Pull priority (!important) out of the value provided by a
* value editor.
*
* @param {string} aValue
* The value from the text editor.
* @return {object} an object with 'value' and 'priority' properties.
*/
function parseCSSValue(aValue) {
let pieces = aValue.split("!", 2);
return {
value: pieces[0].trim(),
priority: (pieces.length > 1 ? pieces[1].trim() : "")
};
}
/**
* Return an array of CSS properties given an input string
* For example, parseCSSText("width: 1px; height: 1px") would return
* [{name:"width", value: "1px"}, {name: "height", "value": "1px"}]
*
* @param {string} aCssText
* An input string of CSS
* @return {Array} an array of objects with the following signature:
* [{"name": string, "value": string, "priority": string}, ...]
*/
function parseCSSText(aCssText) {
let lines = aCssText.match(CSS_LINE_RE);
let props = [];
[].forEach.call(lines, (line, i) => {
let [, name, value, priority] = CSS_PROP_RE.exec(line) || [];
// If this is ending with an unfinished line, add it onto the end
// with an empty value
if (!name && line && i > 0) {
name = line;
}
if (name) {
props.push({
name: name.trim(),
value: value || "",
priority: priority || ""
});
}
});
return props;
}
/**
* Event handler that causes a blur on the target if the input has
* multiple CSS properties as the value.
*/
function blurOnMultipleProperties(e) {
setTimeout(() => {
if (parseCSSText(e.target.value).length) {
let props = parseDeclarations(e.target.value);
if (props.length > 1) {
e.target.blur();
}
}, 0);

View File

@ -55,7 +55,6 @@ support-files = browser_ruleview_pseudoelement.html
[browser_bug765105_background_image_tooltip.js]
[browser_bug889638_rule_view_color_picker.js]
[browser_bug726427_csstransform_tooltip.js]
[browser_bug940500_rule_view_pick_gradient_color.js]
[browser_ruleview_original_source_link.js]
support-files =
@ -67,3 +66,4 @@ support-files =
[browser_bug946331_close_tooltip_on_new_selection.js]
[browser_bug942297_user_property_reset.js]
[browser_styleinspector_outputparser.js]
[browser_bug970532_mathml_element.js]

View File

@ -0,0 +1,70 @@
/* Any copyright", " is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests that the rule-view displays correctly on MathML elements
waitForExplicitFinish();
const TEST_URL = [
"data:text/html,",
"<div>",
" <math xmlns=\"http://www.w3.org/1998/Math/MathML\">",
" <mfrac>",
" <msubsup>",
" <mi>a</mi>",
" <mi>i</mi>",
" <mi>j</mi>",
" </msubsup>",
" <msub>",
" <mi>x</mi>",
" <mn>0</mn>",
" </msub>",
" </mfrac>",
" </math>",
"</div>"
].join("");
function test() {
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onload(evt) {
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
waitForFocus(runTests, content);
}, true);
content.location = TEST_URL;
}
function runTests() {
openRuleView((inspector, ruleView) => {
Task.spawn(function() {
info("Select the DIV node and verify the rule-view shows rules");
yield selectNode("div", inspector);
ok(ruleView.element.querySelectorAll(".ruleview-rule").length,
"The rule-view shows rules for the div element");
info("Select various MathML nodes and verify the rule-view is empty");
yield selectNode("math", inspector);
ok(!ruleView.element.querySelectorAll(".ruleview-rule").length,
"The rule-view is empty for the math element");
yield selectNode("msubsup", inspector);
ok(!ruleView.element.querySelectorAll(".ruleview-rule").length,
"The rule-view is empty for the msubsup element");
yield selectNode("mn", inspector);
ok(!ruleView.element.querySelectorAll(".ruleview-rule").length,
"The rule-view is empty for the mn element");
info("Select again the DIV node and verify the rule-view shows rules");
yield selectNode("div", inspector);
ok(ruleView.element.querySelectorAll(".ruleview-rule").length,
"The rule-view shows rules for the div element");
}).then(null, ok.bind(null, false)).then(finishUp);
});
}
function finishUp() {
gBrowser.removeCurrentTab();
finish();
}

View File

@ -6,6 +6,8 @@ let doc;
let ruleWindow;
let ruleView;
let inspector;
let TEST_URL = 'url("http://example.com/browser/browser/devtools/' +
'styleinspector/test/test-image.png")';
function startTest()
{
@ -140,7 +142,7 @@ function testEditProperty()
let value = idRuleEditor.rule.domRule._rawStyle().getPropertyValue("border-color");
is(value, "red", "border-color should have been set.");
is(propEditor.isValid(), true, "red should be a valid entry");
finishTest();
testEditPropertyWithColon();
}));
});
@ -159,6 +161,43 @@ function testEditProperty()
ruleWindow);
}
function testEditPropertyWithColon()
{
let idRuleEditor = ruleView.element.children[1]._ruleEditor;
let propEditor = idRuleEditor.rule.textProps[0].editor;
waitForEditorFocus(propEditor.element, function onNewElement(aEditor) {
is(inplaceEditor(propEditor.nameSpan), aEditor, "Next focused editor should be the name editor.");
let input = aEditor.input;
waitForEditorFocus(propEditor.element, function onNewName(aEditor) {
promiseDone(expectRuleChange(idRuleEditor.rule).then(() => {
input = aEditor.input;
is(inplaceEditor(propEditor.valueSpan), aEditor, "Focus should have moved to the value.");
waitForEditorBlur(aEditor, function() {
promiseDone(expectRuleChange(idRuleEditor.rule).then(() => {
let value = idRuleEditor.rule.domRule._rawStyle().getPropertyValue("background-image");
is(value, TEST_URL, "background-image should have been set.");
is(propEditor.isValid(), true, "the test URL should be a valid entry");
finishTest();
}));
});
for (let ch of (TEST_URL + ";")) {
EventUtils.sendChar(ch, ruleWindow);
}
}));
});
for (let ch of "background-image:") {
EventUtils.sendChar(ch, ruleWindow);
}
});
EventUtils.synthesizeMouse(propEditor.nameSpan, 32, 1,
{ },
ruleWindow);
}
function finishTest()
{
inspector = ruleWindow = ruleView = null;

View File

@ -28,13 +28,15 @@ function selectNewElement()
let newElement = doc.createElement("div");
newElement.textContent = "Test Element";
doc.body.appendChild(newElement);
inspector.selection.setNode(newElement);
inspector.selection.setNode(newElement, "test");
let def = promise.defer();
ruleView.element.addEventListener("CssRuleViewRefreshed", function changed() {
ruleView.element.removeEventListener("CssRuleViewRefreshed", changed);
elementRuleEditor = ruleView.element.children[0]._ruleEditor;
def.resolve();
});
return def.promise;
}

View File

@ -79,6 +79,42 @@ function openComputedView(callback)
});
}
/**
* Simple DOM node accesor function that takes either a node or a string css
* selector as argument and returns the corresponding node
* @param {String|DOMNode} nodeOrSelector
* @return {DOMNode}
*/
function getNode(nodeOrSelector)
{
let node = nodeOrSelector;
if (typeof nodeOrSelector === "string") {
node = content.document.querySelector(nodeOrSelector);
ok(node, "A node was found for selector " + nodeOrSelector);
}
return node;
}
/**
* Set the inspector's current selection to a node or to the first match of the
* given css selector
* @param {String|DOMNode} nodeOrSelector
* @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
* @param {String} reason Defaults to "test" which instructs the inspector not to highlight the node upon selection
* @return a promise that resolves when the inspector is updated with the new
* node
*/
function selectNode(nodeOrSelector, inspector, reason="test")
{
info("Selecting the node " + nodeOrSelector);
let node = getNode(nodeOrSelector);
let updated = inspector.once("inspector-updated");
inspector.selection.setNode(node, reason);
return updated;
}
function addStyle(aDocument, aString)
{
let node = aDocument.createElement('style');

View File

@ -5,3 +5,4 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
BROWSER_CHROME_MANIFESTS += ['browser.ini']
XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']

View File

@ -0,0 +1,206 @@
/* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const Cu = Components.utils;
Cu.import("resource://gre/modules/devtools/Loader.jsm");
const {parseDeclarations} = devtools.require("devtools/styleinspector/css-parsing-utils");
const TEST_DATA = [
// Simple test
{
input: "p:v;",
expected: [{name: "p", value: "v", priority: ""}]
},
// Simple test
{
input: "this:is;a:test;",
expected: [
{name: "this", value: "is", priority: ""},
{name: "a", value: "test", priority: ""}
]
},
// Test a single declaration with semi-colon
{
input: "name:value;",
expected: [{name: "name", value: "value", priority: ""}]
},
// Test a single declaration without semi-colon
{
input: "name:value",
expected: [{name: "name", value: "value", priority: ""}]
},
// Test multiple declarations separated by whitespaces and carriage returns and tabs
{
input: "p1 : v1 ; \t\t \n p2:v2; \n\n\n\n\t p3 : v3;",
expected: [
{name: "p1", value: "v1", priority: ""},
{name: "p2", value: "v2", priority: ""},
{name: "p3", value: "v3", priority: ""},
]
},
// Test simple priority
{
input: "p1: v1; p2: v2 !important;",
expected: [
{name: "p1", value: "v1", priority: ""},
{name: "p2", value: "v2", priority: "important"}
]
},
// Test simple priority
{
input: "p1: v1 !important; p2: v2",
expected: [
{name: "p1", value: "v1", priority: "important"},
{name: "p2", value: "v2", priority: ""}
]
},
// Test simple priority
{
input: "p1: v1 ! important; p2: v2 ! important;",
expected: [
{name: "p1", value: "v1", priority: "important"},
{name: "p2", value: "v2", priority: "important"}
]
},
// Test invalid priority
{
input: "p1: v1 important;",
expected: [
{name: "p1", value: "v1 important", priority: ""}
]
},
// Test various types of background-image urls
{
input: "background-image: url(../../relative/image.png)",
expected: [{name: "background-image", value: "url(\"../../relative/image.png\")", priority: ""}]
},
{
input: "background-image: url(http://site.com/test.png)",
expected: [{name: "background-image", value: "url(\"http://site.com/test.png\")", priority: ""}]
},
{
input: "background-image: url(wow.gif)",
expected: [{name: "background-image", value: "url(\"wow.gif\")", priority: ""}]
},
// Test that urls with :;{} characters in them are parsed correctly
{
input: "background: red url(\"http://site.com/image{}:;.png?id=4#wat\") repeat top right",
expected: [
{name: "background", value: "red url(\"http://site.com/image{}:;.png?id=4#wat\") repeat top right", priority: ""}
]
},
// Test that an empty string results in an empty array
{input: "", expected: []},
// Test that a string comprised only of whitespaces results in an empty array
{input: " \n \n \n \n \t \t\t\t ", expected: []},
// Test that a null input throws an exception
{input: null, throws: true},
// Test that a undefined input throws an exception
{input: undefined, throws: true},
// Test that :;{} characters in quoted content are not parsed as multiple declarations
{
input: "content: \";color:red;}selector{color:yellow;\"",
expected: [
{name: "content", value: "\";color:red;}selector{color:yellow;\"", priority: ""}
]
},
// Test that rules aren't parsed, just declarations. So { and } found after a
// property name should be part of the property name, same for values.
{
input: "body {color:red;} p {color: blue;}",
expected: [
{name: "body {color", value: "red", priority: ""},
{name: "} p {color", value: "blue", priority: ""},
{name: "}", value: "", priority: ""}
]
},
// Test unbalanced : and ;
{
input: "color :red : font : arial;",
expected : [
{name: "color", value: "red : font : arial", priority: ""}
]
},
{input: "background: red;;;;;", expected: [{name: "background", value: "red", priority: ""}]},
{input: "background:;", expected: [{name: "background", value: "", priority: ""}]},
{input: ";;;;;", expected: []},
{input: ":;:;", expected: []},
// Test name only
{input: "color", expected: [
{name: "color", value: "", priority: ""}
]},
// Test trailing name without :
{input: "color:blue;font", expected: [
{name: "color", value: "blue", priority: ""},
{name: "font", value: "", priority: ""}
]},
// Test trailing name with :
{input: "color:blue;font:", expected: [
{name: "color", value: "blue", priority: ""},
{name: "font", value: "", priority: ""}
]},
// Test leading value
{input: "Arial;color:blue;", expected: [
{name: "", value: "Arial", priority: ""},
{name: "color", value: "blue", priority: ""}
]},
// Test hex colors
{input: "color: #333", expected: [{name: "color", value: "#333", priority: ""}]},
{input: "color: #456789", expected: [{name: "color", value: "#456789", priority: ""}]},
{input: "wat: #XYZ", expected: [{name: "wat", value: "#XYZ", priority: ""}]},
// Test string/url quotes escaping
{input: "content: \"this is a 'string'\"", expected: [{name: "content", value: "\"this is a 'string'\"", priority: ""}]},
{input: 'content: "this is a \\"string\\""', expected: [{name: "content", value: '\'this is a "string"\'', priority: ""}]},
{input: "content: 'this is a \"string\"'", expected: [{name: "content", value: '\'this is a "string"\'', priority: ""}]},
{input: "content: 'this is a \\'string\\'", expected: [{name: "content", value: '"this is a \'string\'"', priority: ""}]},
{input: "content: 'this \\' is a \" really strange string'", expected: [{name: "content", value: '"this \' is a \" really strange string"', priority: ""}]},
{
input: "content: \"a not s\\\
o very long title\"",
expected: [
{name: "content", value: '"a not s\
o very long title"', priority: ""}
]
}
];
function run_test() {
for (let test of TEST_DATA) {
do_print("Test input string " + test.input);
let output;
try {
output = parseDeclarations(test.input);
} catch (e) {
do_print("parseDeclarations threw an exception with the given input string");
if (test.throws) {
do_print("Exception expected");
do_check_true(true);
} else {
do_print("Exception unexpected\n" + e);
do_check_true(false);
}
}
if (output) {
assertOutput(output, test.expected);
}
}
}
function assertOutput(actual, expected) {
if (actual.length === expected.length) {
for (let i = 0; i < expected.length; i ++) {
do_check_true(!!actual[i]);
do_print("Check that the output item has the expected name, value and priority");
do_check_eq(expected[i].name, actual[i].name);
do_check_eq(expected[i].value, actual[i].value);
do_check_eq(expected[i].priority, actual[i].priority);
}
} else {
for (let prop of actual) {
do_print("Actual output contained: {name: "+prop.name+", value: "+prop.value+", priority: "+prop.priority+"}");
}
do_check_eq(actual.length, expected.length);
}
}

View File

@ -0,0 +1,76 @@
/* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const Cu = Components.utils;
Cu.import("resource://gre/modules/devtools/Loader.jsm");
const {parseSingleValue} = devtools.require("devtools/styleinspector/css-parsing-utils");
const TEST_DATA = [
{input: null, throws: true},
{input: undefined, throws: true},
{input: "", expected: {value: "", priority: ""}},
{input: " \t \t \n\n ", expected: {value: "", priority: ""}},
{input: "blue", expected: {value: "blue", priority: ""}},
{input: "blue !important", expected: {value: "blue", priority: "important"}},
{input: "blue!important", expected: {value: "blue", priority: "important"}},
{input: "blue ! important", expected: {value: "blue", priority: "important"}},
{input: "blue ! important", expected: {value: "blue", priority: "important"}},
{input: "blue !", expected: {value: "blue", priority: ""}},
{input: "blue !mportant", expected: {value: "blue !mportant", priority: ""}},
{input: " blue !important ", expected: {value: "blue", priority: "important"}},
{
input: "url(\"http://url.com/whyWouldYouDoThat!important.png\") !important",
expected: {
value: "url(\"http://url.com/whyWouldYouDoThat!important.png\")",
priority: "important"
}
},
{
input: "url(\"http://url.com/whyWouldYouDoThat!important.png\")",
expected: {
value: "url(\"http://url.com/whyWouldYouDoThat!important.png\")",
priority: ""
}
},
{
input: "\"content!important\" !important",
expected: {
value: "\"content!important\"",
priority: "important"
}
},
{
input: "\"content!important\"",
expected: {
value: "\"content!important\"",
priority: ""
}
}
];
function run_test() {
for (let test of TEST_DATA) {
do_print("Test input value " + test.input);
try {
let output = parseSingleValue(test.input);
assertOutput(output, test.expected);
} catch (e) {
do_print("parseSingleValue threw an exception with the given input value");
if (test.throws) {
do_print("Exception expected");
do_check_true(true);
} else {
do_print("Exception unexpected\n" + e);
do_check_true(false);
}
}
}
}
function assertOutput(actual, expected) {
do_print("Check that the output has the expected value and priority");
do_check_eq(expected.value, actual.value);
do_check_eq(expected.priority, actual.priority);
}

View File

@ -0,0 +1,7 @@
[DEFAULT]
head =
tail =
firefox-appdir = browser
[test_parseDeclarations.js]
[test_parseSingleValue.js]

View File

@ -12,51 +12,36 @@
const TEST_URI = "data:text/html;charset=utf-8,<div style='font-size:3em;" +
"foobarCssParser:baz'>test CSS parser filter</div>";
function onContentLoaded()
{
browser.removeEventListener("load", onContentLoaded, true);
let HUD = HUDService.getHudByWindow(content);
let hudId = HUD.hudId;
let outputNode = HUD.outputNode;
HUD.jsterm.clearOutput();
waitForSuccess({
name: "css error displayed",
validatorFn: function()
{
return outputNode.textContent.indexOf("foobarCssParser") > -1;
},
successFn: function()
{
HUD.setFilterState("cssparser", false);
let msg = "the unknown CSS property warning is not displayed, " +
"after filtering";
testLogEntry(outputNode, "foobarCssParser", msg, true, true);
HUD.setFilterState("cssparser", true);
finishTest();
},
failureFn: finishTest,
});
}
/**
* Unit test for bug 589162:
* CSS filtering on the console does not work
*/
function test()
{
addTab(TEST_URI);
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
function test() {
Task.spawn(runner).then(finishTest);
openConsole(null, function() {
browser.addEventListener("load", onContentLoaded, true);
content.location.reload();
function* runner() {
let {tab} = yield loadTab(TEST_URI);
let hud = yield openConsole(tab);
// CSS warnings are disabled by default.
hud.setFilterState("cssparser", true);
hud.jsterm.clearOutput();
content.location.reload();
yield waitForMessages({
webconsole: hud,
messages: [{
text: "foobarCssParser",
category: CATEGORY_CSS,
severity: SEVERITY_WARNING,
}],
});
}, true);
}
hud.setFilterState("cssparser", false);
let msg = "the unknown CSS property warning is not displayed, " +
"after filtering";
testLogEntry(hud.outputNode, "foobarCssParser", msg, true, true);
}
}

View File

@ -16,6 +16,9 @@ XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
"@mozilla.org/xre/app-info;1", "nsICrashReporter");
#endif
XPCOMUtils.defineLazyModuleGetter(this, "CrashMonitor",
"resource://gre/modules/CrashMonitor.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
"@mozilla.org/uuid-generator;1", "nsIUUIDGenerator");
@ -76,6 +79,25 @@ SessionStore.prototype = {
// swallow exception that occurs if metro-tabs measure is already set up
}
CrashMonitor.previousCheckpoints.then(checkpoints => {
let previousSessionCrashed = false;
if (checkpoints) {
// If the previous session finished writing the final state, we'll
// assume there was no crash.
previousSessionCrashed = !checkpoints["sessionstore-final-state-write-complete"];
} else {
// If no checkpoints are saved, this is the first run with CrashMonitor or the
// metroSessionCheckpoints file was corrupted/deleted, so fallback to defining
// a crash as init-ing with an unexpected previousExecutionState
// 1 == RUNNING, 2 == SUSPENDED
previousSessionCrashed = Services.metro.previousExecutionState == 1 ||
Services.metro.previousExecutionState == 2;
}
Services.telemetry.getHistogramById("SHUTDOWN_OK").add(!previousSessionCrashed);
});
try {
let shutdownWasUnclean = false;
@ -291,8 +313,8 @@ SessionStore.prototype = {
if (this._saveTimer) {
this._saveTimer.cancel();
this._saveTimer = null;
this.saveState();
}
this.saveState();
break;
case "browser:purge-session-history": // catch sanitization
this._clearDisk();
@ -644,6 +666,9 @@ SessionStore.prototype = {
let istream = converter.convertToInputStream(aData);
NetUtil.asyncCopy(istream, ostream, function(rc) {
if (Components.isSuccessCode(rc)) {
if (Services.startup.shuttingDown) {
Services.obs.notifyObservers(null, "sessionstore-final-state-write-complete", "");
}
Services.obs.notifyObservers(null, "sessionstore-state-write-complete", "");
}
});

View File

@ -640,12 +640,6 @@ tabmodalprompt:not([promptType="promptUserAndPass"]) .infoContainer {
.meta {
background-color: @panel_light_color@;
/* bug 969354
background-image: url("chrome://browser/skin/images/firefox-watermark.png");
background-repeat: no-repeat;
background-position: center center;
background-attachment: fixed;
*/
}
/* needs to observe the viewstate */

View File

@ -38,3 +38,19 @@
#start-container[viewstate="snapped"] .meta-section:not([expanded]) > richgrid {
visibility: collapse;
}
/* Watermark */
#startui-body::after {
content: '';
width: 256px;
height: 256px;
position: fixed;
left: 50%;
top: 50%;
margin-top: -128px;
margin-left: -128px;
z-index: -1;
background-image: url("chrome://browser/skin/images/firefox-watermark.png");
background-repeat: no-repeat;
background-position: center center;
}

View File

@ -697,9 +697,13 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
#sync-button {
-moz-image-region: rect(0px 144px 24px 120px);
}
#sync-button[status="active"] {
list-style-image: url("chrome://browser/skin/sync-24-throbber.png");
-moz-image-region: rect(0px 24px 24px 0px);
#sync-button[cui-areatype="toolbar"][status="active"] {
list-style-image: url("chrome://browser/skin/syncProgress-toolbar.png");
-moz-image-region: rect(0px 18px 18px 0px);
}
#sync-button[cui-areatype="menu-panel"][status="active"] {
list-style-image: url("chrome://browser/skin/syncProgress-menuPanel.png");
-moz-image-region: rect(0px 32px 32px 0px);
}
#feed-button {

View File

@ -109,6 +109,10 @@
text-decoration: line-through;
}
.theme-light .ruleview-overridden {
-moz-text-decoration-color: #667380; /* Content (Text) - Dark Grey */
}
.styleinspector-propertyeditor {
border: 1px solid #CCC;
padding: 0;

View File

@ -269,15 +269,15 @@ browser.jar:
skin/classic/browser/devtools/app-manager/noise.png (../shared/devtools/app-manager/images/noise.png)
skin/classic/browser/devtools/app-manager/default-app-icon.png (../shared/devtools/app-manager/images/default-app-icon.png)
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/sync-16-throbber.png
skin/classic/browser/sync-16.png
skin/classic/browser/sync-24-throbber.png
skin/classic/browser/sync-32.png
skin/classic/browser/sync-bg.png
skin/classic/browser/sync-128.png
skin/classic/browser/sync-desktopIcon.png
skin/classic/browser/sync-mobileIcon.png
skin/classic/browser/sync-notification-24.png
skin/classic/browser/syncProgress-menuPanel.png
skin/classic/browser/syncProgress-toolbar.png
skin/classic/browser/syncSetup.css
skin/classic/browser/syncCommon.css
skin/classic/browser/syncQuota.css

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 B

View File

@ -113,6 +113,10 @@
text-decoration: line-through;
}
.theme-light .ruleview-overridden {
-moz-text-decoration-color: #667380; /* Content (Text) - Dark Grey */
}
.styleinspector-propertyeditor {
border: 1px solid #CCC;
padding: 0;

View File

@ -104,6 +104,15 @@
inset 0 1px rgb(196, 196, 196);
}
#customization-undo-reset {
padding-left: 12px;
padding-right: 12px;
%ifdef XP_MACOSX
padding-top: 6px;
%else
padding-top: 7px;
%endif
}
#main-window[customize-entered] #customization-panel-container {
background-image: url("chrome://browser/skin/customizableui/customizeMode-separatorHorizontal.png"),

View File

@ -109,6 +109,10 @@
text-decoration: line-through;
}
.theme-light .ruleview-overridden {
-moz-text-decoration-color: #667380; /* Content (Text) - Dark Grey */
}
.styleinspector-propertyeditor {
border: 1px solid #CCC;
padding: 0;

View File

@ -68,7 +68,7 @@ ac_echo_options() {
# Main
#--------------------------------------------------
topsrcdir=`dirname $0`
topsrcdir=$(cd `dirname $0`; pwd -W 2>/dev/null || pwd)
ac_options=
mozconfig_ac_options=

View File

@ -73,7 +73,7 @@ crashreporter
Always defined.
datareporting
Whether data reporting (MOZ_DATA_REPORTING) is enabled for this build.
Whether data reporting (MOZ_DATA_REPORTING) is enabled for this build.
Values are ``true`` and ``false``.
@ -86,6 +86,13 @@ debug
Always defined.
healthreport
Whether the Health Report feature is enabled.
Values are ``true`` and ``false``.
Always defined.
mozconfig
The path of the :ref:`mozconfig file <mozconfig>` used to produce this build.
@ -133,3 +140,16 @@ topsrcdir
Always defined.
wave
Whether Wave audio support is enabled.
Values are ``true`` and ``false``.
Always defined.
webm
Whether WebM support is enabled.
Values are ``true`` and ``false``.
Always defined.

26
build/mozconfig.cache Normal file
View File

@ -0,0 +1,26 @@
# 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/.
# Setup for build cache
bucket=
if test -f "$topsrcdir/sccache/sccache.py"; then
case `hostname` in
try*spot*.use1.mozilla.com|try*ec2*.use1.mozilla.com)
bucket=mozilla-releng-s3-cache-us-east-1-try
;;
try*spot*.usw2.mozilla.com|try*ec2*.usw2.mozilla.com)
bucket=mozilla-releng-s3-cache-us-west-2-try
;;
esac
fi
if test -z "$bucket"; then
ac_add_options --with-ccache
else
mk_add_options "export SCCACHE_BUCKET=$bucket"
mk_add_options "export SCCACHE_NAMESERVER=169.254.169.253"
export CC="python2.7 $topsrcdir/sccache/sccache.py $CC"
export CXX="python2.7 $topsrcdir/sccache/sccache.py $CXX"
fi

View File

@ -1455,8 +1455,12 @@ if test "$GNU_CXX"; then
# Turn off the following warnings that -Wall turns on:
# -Wno-invalid-offsetof - we use offsetof on non-POD types frequently
# -Wno-inline-new-delete - we inline 'new' and 'delete' in mozalloc
# for performance reasons, and because GCC and clang accept it (though
# clang warns about it).
#
MOZ_CXX_SUPPORTS_WARNING(-Wno-, invalid-offsetof, ac_cxx_has_wno_invalid_offsetof)
MOZ_CXX_SUPPORTS_WARNING(-Wno-, inline-new-delete, ac_cxx_has_wno_inline_new_delete)
if test -z "$INTEL_CXX" -a -z "$CLANG_CXX"; then
# Don't use -Wcast-align with ICC or clang
@ -5509,6 +5513,8 @@ MOZ_ARG_ENABLE_STRING(gstreamer,
# API version, eg 0.10, 1.0 etc
if test -z "$enableval" -o "$enableval" = "yes"; then
GST_API_VERSION=0.10
elif test "$enableval" = "no"; then
MOZ_GSTREAMER=
else
GST_API_VERSION=$enableval
fi],
@ -7683,14 +7689,6 @@ dnl =
dnl ========================================================
MOZ_ARG_HEADER(Static build options)
if test -n "$JS_SHARED_LIBRARY"; then
MOZ_JS_LIBS="$MOZ_JS_SHARED_LIBS"
else
MOZ_JS_LIBS="$MOZ_JS_STATIC_LIBS"
AC_DEFINE(MOZ_STATIC_JS)
fi
AC_SUBST(JS_SHARED_LIBRARY)
AC_SUBST(LIBXUL_LIBS)
XPCOM_LIBS="$LIBXUL_LIBS"
@ -8746,6 +8744,14 @@ if test -n "$MOZ_NATIVE_ICU"; then
MOZ_JS_STATIC_LIBS="$MOZ_JS_STATIC_LIBS $MOZ_ICU_LIBS"
fi
if test -n "$JS_SHARED_LIBRARY"; then
MOZ_JS_LIBS="$MOZ_JS_SHARED_LIBS"
else
MOZ_JS_LIBS="$MOZ_JS_STATIC_LIBS"
AC_DEFINE(MOZ_STATIC_JS)
fi
AC_SUBST(JS_SHARED_LIBRARY)
MOZ_CREATE_CONFIG_STATUS()
# No need to run subconfigures when building with LIBXUL_SDK_DIR

View File

@ -1640,17 +1640,16 @@ public:
MOZ_WARN_UNUSED_RESULT
static nsresult WrapNative(JSContext *cx, JS::Handle<JSObject*> scope,
nsISupports *native, const nsIID* aIID,
JS::MutableHandle<JS::Value> vp,
bool aAllowWrapping = false)
JS::MutableHandle<JS::Value> vp)
{
return WrapNative(cx, scope, native, nullptr, aIID, vp, aAllowWrapping);
return WrapNative(cx, scope, native, nullptr, aIID, vp, true);
}
// Same as the WrapNative above, but use this one if aIID is nsISupports' IID.
MOZ_WARN_UNUSED_RESULT
static nsresult WrapNative(JSContext *cx, JS::Handle<JSObject*> scope,
nsISupports *native, JS::MutableHandle<JS::Value> vp,
bool aAllowWrapping = false)
bool aAllowWrapping = true)
{
return WrapNative(cx, scope, native, nullptr, nullptr, vp, aAllowWrapping);
}
@ -1659,7 +1658,7 @@ public:
static nsresult WrapNative(JSContext *cx, JS::Handle<JSObject*> scope,
nsISupports *native, nsWrapperCache *cache,
JS::MutableHandle<JS::Value> vp,
bool aAllowWrapping = false)
bool aAllowWrapping = true)
{
return WrapNative(cx, scope, native, cache, nullptr, vp, aAllowWrapping);
}

View File

@ -5672,7 +5672,7 @@ nsContentUtils::CreateBlobBuffer(JSContext* aCx,
return NS_ERROR_OUT_OF_MEMORY;
}
JS::Rooted<JSObject*> scope(aCx, JS::CurrentGlobalOrNull(aCx));
return nsContentUtils::WrapNative(aCx, scope, blob, aBlob, true);
return nsContentUtils::WrapNative(aCx, scope, blob, aBlob);
}
void

View File

@ -11447,7 +11447,7 @@ nsDocument::Evaluate(const nsAString& aExpression, nsIDOMNode* aContextNode,
{
return XPathEvaluator()->Evaluate(aExpression, aContextNode, aResolver, aType,
aInResult, aResult);
}
}
// This is just a hack around the fact that window.document is not
// [Unforgeable] yet.
@ -11477,13 +11477,15 @@ nsIDocument::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aScope)
JS::Rooted<JS::Value> winVal(aCx);
nsresult rv = nsContentUtils::WrapNative(aCx, obj, win,
&NS_GET_IID(nsIDOMWindow),
&winVal,
false);
&winVal);
if (NS_FAILED(rv)) {
Throw(aCx, rv);
return nullptr;
}
MOZ_ASSERT(&winVal.toObject() == js::UncheckedUnwrap(&winVal.toObject()),
"WrapNative shouldn't create a cross-compartment wrapper");
NS_NAMED_LITERAL_STRING(doc_str, "document");
if (!JS_DefineUCProperty(aCx, JSVAL_TO_OBJECT(winVal), doc_str.get(),

View File

@ -927,7 +927,7 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
JS::Rooted<JS::Value> targetv(cx);
JS::Rooted<JSObject*> global(cx, JS_GetGlobalForObject(cx, object));
nsresult rv = nsContentUtils::WrapNative(cx, global, aTarget, &targetv, true);
nsresult rv = nsContentUtils::WrapNative(cx, global, aTarget, &targetv);
NS_ENSURE_SUCCESS(rv, rv);
JS::Rooted<JSObject*> cpows(cx);
@ -1018,7 +1018,7 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
defaultThisValue = aTarget;
}
JS::Rooted<JSObject*> global(cx, JS_GetGlobalForObject(cx, object));
nsresult rv = nsContentUtils::WrapNative(cx, global, defaultThisValue, &thisValue, true);
nsresult rv = nsContentUtils::WrapNative(cx, global, defaultThisValue, &thisValue);
NS_ENSURE_SUCCESS(rv, rv);
} else {
// If the listener is a JS object which has receiveMessage function:

View File

@ -992,8 +992,7 @@ nsXMLHttpRequest::GetResponse(JSContext* aCx, ErrorResult& aRv)
JS::Rooted<JS::Value> result(aCx, JSVAL_NULL);
JS::Rooted<JSObject*> scope(aCx, JS::CurrentGlobalOrNull(aCx));
aRv = nsContentUtils::WrapNative(aCx, scope, mResponseBlob, &result,
true);
aRv = nsContentUtils::WrapNative(aCx, scope, mResponseBlob, &result);
return result;
}
case XML_HTTP_RESPONSE_TYPE_DOCUMENT:
@ -1004,8 +1003,7 @@ nsXMLHttpRequest::GetResponse(JSContext* aCx, ErrorResult& aRv)
JS::Rooted<JSObject*> scope(aCx, JS::CurrentGlobalOrNull(aCx));
JS::Rooted<JS::Value> result(aCx, JSVAL_NULL);
aRv = nsContentUtils::WrapNative(aCx, scope, mResponseXML, &result,
true);
aRv = nsContentUtils::WrapNative(aCx, scope, mResponseXML, &result);
return result;
}
case XML_HTTP_RESPONSE_TYPE_JSON:

View File

@ -37,10 +37,11 @@ const JSClass sHTMLDocumentAllClass = {
JS_DeletePropertyStub, /* delProperty */
nsHTMLDocumentSH::DocumentAllGetProperty, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub, /* enumerate */
(JSResolveOp)nsHTMLDocumentSH::DocumentAllNewResolve, /* resolve */
JS_ConvertStub, /* convert */
nsHTMLDocumentSH::ReleaseDocument /* finalize */
JS_EnumerateStub,
(JSResolveOp)nsHTMLDocumentSH::DocumentAllNewResolve,
JS_ConvertStub,
nsHTMLDocumentSH::ReleaseDocument,
nsHTMLDocumentSH::CallToGetPropMapper
};
namespace mozilla {
@ -371,12 +372,14 @@ bool
nsHTMLDocumentSH::CallToGetPropMapper(JSContext *cx, unsigned argc, jsval *vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
// Handle document.all("foo") style access to document.all.
if (args.length() != 1) {
// XXX: Should throw NS_ERROR_XPC_NOT_ENOUGH_ARGS for argc < 1,
// and create a new NS_ERROR_XPC_TOO_MANY_ARGS for argc > 1? IE
// accepts nothing other than one arg.
xpc::Throw(cx, NS_ERROR_INVALID_ARG);
return false;
}
@ -386,9 +389,16 @@ nsHTMLDocumentSH::CallToGetPropMapper(JSContext *cx, unsigned argc, jsval *vp)
return false;
}
JS::Rooted<JSObject*> self(cx, JS_THIS_OBJECT(cx, vp));
if (!self) {
return false;
// If we are called via document.all(id) instead of document.all.item(i) or
// another method, use the document.all callee object as self.
JS::Rooted<JSObject*> self(cx);
if (args.calleev().isObject() &&
JS_GetClass(&args.calleev().toObject()) == &sHTMLDocumentAllClass) {
self = &args.calleev().toObject();
} else {
self = JS_THIS_OBJECT(cx, vp);
if (!self)
return false;
}
size_t length;

View File

@ -153,6 +153,16 @@ ChannelMediaResource::OnStartRequest(nsIRequest* aRequest)
nsresult rv = aRequest->GetStatus(&status);
NS_ENSURE_SUCCESS(rv, rv);
if (status == NS_BINDING_ABORTED) {
// Request was aborted before we had a chance to receive any data, or
// even an OnStartRequest(). Close the channel. This is important, as
// we don't want to mess up our state, as if we're cloned that would
// cause the clone to copy incorrect metadata (like whether we're
// infinite for example).
CloseChannel();
return status;
}
if (element->ShouldCheckAllowOrigin()) {
// If the request was cancelled by nsCORSListenerProxy due to failing
// the CORS security check, send an error through to the media element.

View File

@ -1602,7 +1602,7 @@ Navigator::DoNewResolve(JSContext* aCx, JS::Handle<JSObject*> aObject,
}
if (JSVAL_IS_PRIMITIVE(prop_val) && !JSVAL_IS_NULL(prop_val)) {
rv = nsContentUtils::WrapNative(aCx, aObject, native, &prop_val, true);
rv = nsContentUtils::WrapNative(aCx, aObject, native, &prop_val);
if (NS_FAILED(rv)) {
return Throw(aCx, rv);

View File

@ -15,7 +15,7 @@
"use strict";
try {
document.all.item();
document.all();
} catch (e) {
is(typeof e, "object");
is(e.filename, location);

View File

@ -276,6 +276,25 @@ static uint64_t gContentChildID = 1;
// Can't be a static constant.
#define MAGIC_PREALLOCATED_APP_MANIFEST_URL NS_LITERAL_STRING("{{template}}")
static const char* sObserverTopics[] = {
"xpcom-shutdown",
NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC,
"child-memory-reporter-request",
"memory-pressure",
"child-gc-request",
"child-cc-request",
"child-mmu-request",
"last-pb-context-exited",
"file-watcher-update",
#ifdef MOZ_WIDGET_GONK
NS_VOLUME_STATE_CHANGED,
"phone-state-changed",
#endif
#ifdef ACCESSIBILITY
"a11y-init-or-shutdown",
#endif
};
/* static */ already_AddRefed<ContentParent>
ContentParent::RunNuwaProcess()
{
@ -652,22 +671,10 @@ ContentParent::Init()
{
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->AddObserver(this, "xpcom-shutdown", false);
obs->AddObserver(this, NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC, false);
obs->AddObserver(this, "child-memory-reporter-request", false);
obs->AddObserver(this, "memory-pressure", false);
obs->AddObserver(this, "child-gc-request", false);
obs->AddObserver(this, "child-cc-request", false);
obs->AddObserver(this, "child-mmu-request", false);
obs->AddObserver(this, "last-pb-context-exited", false);
obs->AddObserver(this, "file-watcher-update", false);
#ifdef MOZ_WIDGET_GONK
obs->AddObserver(this, NS_VOLUME_STATE_CHANGED, false);
obs->AddObserver(this, "phone-state-changed", false);
#endif
#ifdef ACCESSIBILITY
obs->AddObserver(this, "a11y-init-or-shutdown", false);
#endif
size_t length = ArrayLength(sObserverTopics);
for (size_t i = 0; i < length; ++i) {
obs->AddObserver(this, sObserverTopics[i], false);
}
}
Preferences::AddStrongObserver(this, "");
nsCOMPtr<nsIThreadInternal>
@ -1036,22 +1043,11 @@ ContentParent::ActorDestroy(ActorDestroyReason why)
kungFuDeathGrip(static_cast<nsIThreadObserver*>(this));
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->RemoveObserver(static_cast<nsIObserver*>(this), "xpcom-shutdown");
obs->RemoveObserver(static_cast<nsIObserver*>(this), "memory-pressure");
obs->RemoveObserver(static_cast<nsIObserver*>(this), "child-memory-reporter-request");
obs->RemoveObserver(static_cast<nsIObserver*>(this), NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC);
obs->RemoveObserver(static_cast<nsIObserver*>(this), "child-gc-request");
obs->RemoveObserver(static_cast<nsIObserver*>(this), "child-cc-request");
obs->RemoveObserver(static_cast<nsIObserver*>(this), "child-mmu-request");
obs->RemoveObserver(static_cast<nsIObserver*>(this), "last-pb-context-exited");
obs->RemoveObserver(static_cast<nsIObserver*>(this), "file-watcher-update");
#ifdef MOZ_WIDGET_GONK
obs->RemoveObserver(static_cast<nsIObserver*>(this), NS_VOLUME_STATE_CHANGED);
obs->RemoveObserver(static_cast<nsIObserver*>(this), "phone-state-changed");
#endif
#ifdef ACCESSIBILITY
obs->RemoveObserver(static_cast<nsIObserver*>(this), "a11y-init-or-shutdown");
#endif
size_t length = ArrayLength(sObserverTopics);
for (size_t i = 0; i < length; ++i) {
obs->RemoveObserver(static_cast<nsIObserver*>(this),
sObserverTopics[i]);
}
}
if (ppm) {

View File

@ -178,7 +178,8 @@ nsXBLProtoImpl::InitTargetObjects(nsXBLPrototypeBinding* aBinding,
bool defineOnGlobal = dom::XULElementBinding::ConstructorEnabled(cx, global);
dom::XULElementBinding::GetConstructorObject(cx, global, defineOnGlobal);
rv = nsContentUtils::WrapNative(cx, global, aBoundElement, &v);
rv = nsContentUtils::WrapNative(cx, global, aBoundElement, &v,
/* aAllowWrapping = */ false);
NS_ENSURE_SUCCESS(rv, rv);
JS::Rooted<JSObject*> value(cx, &v.toObject());

View File

@ -307,8 +307,7 @@ nsXBLPrototypeHandler::ExecuteHandler(EventTarget* aTarget,
// scope if one doesn't already exist, and potentially wraps it cross-
// compartment into our scope (via aAllowWrapping=true).
JS::Rooted<JS::Value> targetV(cx, JS::UndefinedValue());
rv = nsContentUtils::WrapNative(cx, scopeObject, scriptTarget, &targetV,
/* aAllowWrapping = */ true);
rv = nsContentUtils::WrapNative(cx, scopeObject, scriptTarget, &targetV);
NS_ENSURE_SUCCESS(rv, rv);
// Next, clone the generic handler to be parented to the target.

View File

@ -444,10 +444,12 @@ LayerManagerComposite::Render()
/** Our more efficient but less powerful alter ego, if one is available. */
nsRefPtr<Composer2D> composer2D = mCompositor->GetWidget()->GetComposer2D();
if (mFPS && composer2D && composer2D->TryRender(mRoot, mWorldMatrix)) {
double fps = mFPS->mCompositionFps.AddFrameAndGetFps(TimeStamp::Now());
if (gfxPlatform::GetPrefLayersDrawFPS()) {
printf_stderr("HWComposer: FPS is %g\n", fps);
if (composer2D && composer2D->TryRender(mRoot, mWorldMatrix)) {
if (mFPS) {
double fps = mFPS->mCompositionFps.AddFrameAndGetFps(TimeStamp::Now());
if (gfxPlatform::GetPrefLayersDrawFPS()) {
printf_stderr("HWComposer: FPS is %g\n", fps);
}
}
mCompositor->EndFrameForExternalComposition(mWorldMatrix);
return;

View File

@ -1274,12 +1274,30 @@ CairoTextureClientD3D9::Lock(OpenMode)
if (!IsValid() || !IsAllocated()) {
return false;
}
if (!gfxWindowsPlatform::GetPlatform()->GetD3D9Device()) {
// If the device has failed then we should not lock the surface,
// even if we could.
mD3D9Surface = nullptr;
return false;
}
if (!mD3D9Surface) {
HRESULT hr = mTexture->GetSurfaceLevel(0, getter_AddRefs(mD3D9Surface));
if (FAILED(hr)) {
NS_WARNING("Failed to get texture surface level.");
return false;
}
}
mIsLocked = true;
if (mNeedsClear) {
mDrawTarget = GetAsDrawTarget();
mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height));
mNeedsClear = false;
}
mIsLocked = true;
return true;
}
@ -1318,27 +1336,11 @@ CairoTextureClientD3D9::ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor)
TemporaryRef<gfx::DrawTarget>
CairoTextureClientD3D9::GetAsDrawTarget()
{
MOZ_ASSERT(mIsLocked && mTexture);
MOZ_ASSERT(mIsLocked && mD3D9Surface);
if (mDrawTarget) {
return mDrawTarget;
}
if (!gfxWindowsPlatform::GetPlatform()->GetD3D9Device()) {
// If the device has failed then we should not lock the surface,
// even if we could.
mD3D9Surface = nullptr;
mTexture = nullptr;
return nullptr;
}
if (!mD3D9Surface) {
HRESULT hr = mTexture->GetSurfaceLevel(0, getter_AddRefs(mD3D9Surface));
if (FAILED(hr)) {
NS_WARNING("Failed to get texture surface level.");
return nullptr;
}
}
if (ContentForFormat(mFormat) == gfxContentType::COLOR) {
mSurface = new gfxWindowsSurface(mD3D9Surface);
if (!mSurface || mSurface->CairoStatus()) {

View File

@ -0,0 +1,10 @@
// |jit-test| slow;
load(libdir + "asserts.js");
function f() {
Array.buildPar(6, function() {});
f();
}
if (getBuildConfiguration().parallelJS)
assertThrowsInstanceOf(f, InternalError);

View File

@ -975,6 +975,16 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
IonSpew(IonSpew_BaselineBailouts, " Set resumeAddr=%p", opReturnAddr);
}
if (cx->runtime()->spsProfiler.enabled() && blFrame->hasPushedSPSFrame()) {
// Set PC index to 0 for the innermost frame to match what the
// interpreter and Baseline do: they update the SPS pc for
// JSOP_CALL ops but set it to 0 when running other ops. Ion code
// can set the pc to NullPCIndex and this will confuse SPS when
// Baseline calls into the VM at non-CALL ops and re-enters JS.
IonSpew(IonSpew_BaselineBailouts, " Setting PCidx for last frame to 0");
cx->runtime()->spsProfiler.updatePC(script, script->code());
}
return true;
}

View File

@ -1213,7 +1213,7 @@ GetPropertyIC::tryAttachNative(JSContext *cx, IonScript *ion, HandleObject obj,
*emitted = true;
MacroAssembler masm(cx, ion);
MacroAssembler masm(cx, ion, script_, pc_);
SkipRoot skip(cx, &masm);
RepatchStubAppender attacher(*this);
@ -1370,7 +1370,7 @@ GetPropertyIC::tryAttachDOMProxyShadowed(JSContext *cx, IonScript *ion,
*emitted = true;
Label failures;
MacroAssembler masm(cx, ion);
MacroAssembler masm(cx, ion, script_, pc_);
RepatchStubAppender attacher(*this);
// Guard on the shape of the object.
@ -1437,7 +1437,7 @@ GetPropertyIC::tryAttachDOMProxyUnshadowed(JSContext *cx, IonScript *ion, Handle
}
Label failures;
MacroAssembler masm(cx, ion);
MacroAssembler masm(cx, ion, script_, pc_);
RepatchStubAppender attacher(*this);
// Guard on the shape of the object.
@ -1552,7 +1552,7 @@ GetPropertyIC::tryAttachGenericProxy(JSContext *cx, IonScript *ion, HandleObject
*emitted = true;
Label failures;
MacroAssembler masm(cx, ion);
MacroAssembler masm(cx, ion, script_, pc_);
RepatchStubAppender attacher(*this);
Register scratchReg = output().valueReg().scratchReg();
@ -2120,7 +2120,7 @@ SetPropertyIC::attachGenericProxy(JSContext *cx, IonScript *ion, void *returnAdd
{
JS_ASSERT(!hasGenericProxyStub());
MacroAssembler masm(cx, ion);
MacroAssembler masm(cx, ion, script_, pc_);
RepatchStubAppender attacher(*this);
Label failures;
@ -2177,7 +2177,7 @@ SetPropertyIC::attachDOMProxyShadowed(JSContext *cx, IonScript *ion, HandleObjec
JS_ASSERT(IsCacheableDOMProxy(obj));
Label failures;
MacroAssembler masm(cx, ion);
MacroAssembler masm(cx, ion, script_, pc_);
RepatchStubAppender attacher(*this);
// Guard on the shape of the object.
@ -2407,7 +2407,7 @@ SetPropertyIC::attachDOMProxyUnshadowed(JSContext *cx, IonScript *ion, HandleObj
JS_ASSERT(IsCacheableDOMProxy(obj));
Label failures;
MacroAssembler masm(cx, ion);
MacroAssembler masm(cx, ion, script_, pc_);
RepatchStubAppender attacher(*this);
// Guard on the shape of the object.
@ -2462,7 +2462,7 @@ SetPropertyIC::attachCallSetter(JSContext *cx, IonScript *ion,
{
JS_ASSERT(obj->isNative());
MacroAssembler masm(cx, ion);
MacroAssembler masm(cx, ion, script_, pc_);
RepatchStubAppender attacher(*this);
Label failure;
@ -4243,7 +4243,7 @@ bool
NameIC::attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder,
HandleShape shape, void *returnAddr)
{
MacroAssembler masm(cx, ion);
MacroAssembler masm(cx, ion, script_, pc_);
RepatchStubAppender attacher(*this);
if (!GenerateCallGetter(cx, ion, masm, attacher, obj, name(), holder, shape, liveRegs_,

View File

@ -172,6 +172,10 @@ class MacroAssembler : public MacroAssemblerSpecific
bool enoughMemory_;
bool embedsNurseryPointers_;
// SPS instrumentation, only used for Ion caches.
mozilla::Maybe<IonInstrumentation> spsInstrumentation_;
jsbytecode *spsPc_;
private:
// This field is used to manage profiling instrumentation output. If
// provided and enabled, then instrumentation will be emitted around call
@ -212,7 +216,8 @@ class MacroAssembler : public MacroAssemblerSpecific
// This constructor should only be used when there is no IonContext active
// (for example, Trampoline-$(ARCH).cpp and IonCaches.cpp).
MacroAssembler(JSContext *cx, IonScript *ion = nullptr)
MacroAssembler(JSContext *cx, IonScript *ion = nullptr,
JSScript *script = nullptr, jsbytecode *pc = nullptr)
: enoughMemory_(true),
embedsNurseryPointers_(false),
sps_(nullptr)
@ -225,8 +230,17 @@ class MacroAssembler : public MacroAssemblerSpecific
initWithAllocator();
m_buffer.id = GetIonContext()->getNextAssemblerId();
#endif
if (ion)
if (ion) {
setFramePushed(ion->frameSize());
if (pc && cx->runtime()->spsProfiler.enabled()) {
// We have to update the SPS pc when this IC stub calls into
// the VM.
spsPc_ = pc;
spsInstrumentation_.construct(&cx->runtime()->spsProfiler, &spsPc_);
sps_ = spsInstrumentation_.addr();
sps_->setPushed(script);
}
}
}
// asm.js compilation handles its own IonContet-pushing

View File

@ -29,6 +29,7 @@ UNIFIED_SOURCES += [
'testException.cpp',
'testExternalStrings.cpp',
'testFindSCCs.cpp',
'testFreshGlobalEvalRedefinition.cpp',
'testFuncCallback.cpp',
'testFunctionProperties.cpp',
'testGCExactRooting.cpp',

View File

@ -0,0 +1,47 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*/
/* 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/. */
#include "jsapi-tests/tests.h"
static bool
GlobalEnumerate(JSContext *cx, JS::Handle<JSObject*> obj)
{
return JS_EnumerateStandardClasses(cx, obj);
}
static bool
GlobalResolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id)
{
bool resolved = false;
return JS_ResolveStandardClass(cx, obj, id, &resolved);
}
BEGIN_TEST(testRedefineGlobalEval)
{
static const JSClass cls = {
"global", JSCLASS_GLOBAL_FLAGS,
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
GlobalEnumerate, GlobalResolve, JS_ConvertStub
};
/* Create the global object. */
JS::CompartmentOptions options;
options.setVersion(JSVERSION_LATEST);
JS::Rooted<JSObject*> g(cx, JS_NewGlobalObject(cx, &cls, nullptr, JS::FireOnNewGlobalHook, options));
if (!g)
return false;
JSAutoCompartment ac(cx, g);
JS::Rooted<JS::Value> v(cx);
CHECK(JS_GetProperty(cx, g, "Object", &v));
static const char data[] = "Object.defineProperty(this, 'eval', { configurable: false });";
CHECK(JS_EvaluateScript(cx, g, data, mozilla::ArrayLength(data) - 1, __FILE__, __LINE__, v.address()));
return true;
}
END_TEST(testRedefineGlobalEval)

View File

@ -27,7 +27,9 @@ CONFIGURE_SUBST_FILES += [
'js.pc',
]
if not CONFIG['JS_STANDALONE']:
if CONFIG['JS_STANDALONE']:
DEFINES['IMPL_MFBT'] = True
else:
CONFIGURE_SUBST_FILES += [
'../../config/autoconf-js.mk',
'../../config/emptyvars-js.mk',

View File

@ -0,0 +1,28 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var gTestfile = "error-expando-reconfigure.js"
//-----------------------------------------------------------------------------
var BUGNUMBER = 961494;
var summary =
"Reconfiguring the first expando property added to an Error object " +
"shouldn't assert";
print(BUGNUMBER + ": " + summary);
/**************
* BEGIN TEST *
**************/
var err = new Error(); // no message argument => no err.message property
err.expando = 17;
Object.defineProperty(err, "expando", { configurable: false });
/******************************************************************************/
if (typeof reportCompare === "function")
reportCompare(true, true);
print("Tests complete");

View File

@ -130,8 +130,17 @@ SPSProfiler::enter(JSScript *script, JSFunction *maybeFun)
if (str == nullptr)
return false;
JS_ASSERT_IF(*size_ > 0 && *size_ - 1 < max_ && stack_[*size_ - 1].js(),
stack_[*size_ - 1].pc() != nullptr);
#ifdef DEBUG
// In debug builds, assert the JS pseudo frames already on the stack
// have a non-null pc. Only look at the top frames to avoid quadratic
// behavior.
if (*size_ > 0 && *size_ - 1 < max_) {
size_t start = (*size_ > 4) ? *size_ - 4 : 0;
for (size_t i = start; i < *size_ - 1; i++)
MOZ_ASSERT_IF(stack_[i].js(), stack_[i].pc() != nullptr);
}
#endif
push(str, nullptr, script, script->code());
return true;
}

View File

@ -352,10 +352,20 @@ JSObject::getChildPropertyOnDictionary(ThreadSafeContext *cx, JS::HandleObject o
return nullptr;
child.setSlot(slot);
} else {
/* Slots can only be allocated out of order on objects in dictionary mode. */
/*
* Slots can only be allocated out of order on objects in
* dictionary mode. Otherwise the child's slot must be after the
* parent's slot (if it has one), because slot number determines
* slot span for objects with that shape. Usually child slot
* *immediately* follows parent slot, but there may be a slot gap
* when the object uses some -- but not all -- of its reserved
* slots to store properties.
*/
JS_ASSERT(obj->inDictionaryMode() ||
parent->hasMissingSlot() ||
child.slot() == parent->maybeSlot() + 1);
child.slot() == parent->maybeSlot() + 1 ||
(parent->maybeSlot() + 1 < JSSLOT_FREE(obj->getClass()) &&
child.slot() == JSSLOT_FREE(obj->getClass())));
}
}

View File

@ -549,6 +549,12 @@ ScriptFrameIter::settleOnActivation()
data_.state_ = JIT;
return;
}
// ForkJoin activations don't contain iterable frames, so skip them.
if (activation->isForkJoin()) {
++data_.activations_;
continue;
}
#endif
JS_ASSERT(activation->isInterpreter());

View File

@ -32,7 +32,7 @@ nsTArrayToJSArray(JSContext* aCx, const nsTArray<T>& aSourceArray,
NS_ENSURE_SUCCESS(rv, rv);
JS::RootedValue wrappedVal(aCx);
rv = nsContentUtils::WrapNative(aCx, global, obj, &wrappedVal, true);
rv = nsContentUtils::WrapNative(aCx, global, obj, &wrappedVal);
NS_ENSURE_SUCCESS(rv, rv);
if (!JS_SetElement(aCx, arrayObj, index, wrappedVal)) {

View File

@ -379,8 +379,6 @@ XPCConvert::JSData2Native(void* d, HandleValue s,
{
NS_PRECONDITION(d, "bad param");
bool isDOMString = true;
AutoJSContext cx;
if (pErr)
*pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
@ -473,81 +471,62 @@ XPCConvert::JSData2Native(void* d, HandleValue s,
case nsXPTType::T_ASTRING:
{
isDOMString = false;
if (JSVAL_IS_VOID(s)) {
if (useAllocator)
*((const nsAString**)d) = &NullString();
else
(**((nsAString**)d)).SetIsVoid(true);
return true;
}
// Fall through to T_DOMSTRING case.
}
case nsXPTType::T_DOMSTRING:
{
static const char16_t EMPTY_STRING[] = { '\0' };
static const char16_t VOID_STRING[] = { 'u', 'n', 'd', 'e', 'f', 'i', 'n', 'e', 'd', '\0' };
if (JSVAL_IS_NULL(s)) {
if (useAllocator)
*((const nsAString**)d) = &NullString();
else
(**((nsAString**)d)).SetIsVoid(true);
return true;
}
size_t length = 0;
const char16_t* chars = nullptr;
JSString* str = nullptr;
bool isNewString = false;
uint32_t length = 0;
if (JSVAL_IS_VOID(s)) {
if (isDOMString) {
chars = VOID_STRING;
length = ArrayLength(VOID_STRING) - 1;
} else {
chars = EMPTY_STRING;
length = 0;
}
} else if (!JSVAL_IS_NULL(s)) {
if (!JSVAL_IS_VOID(s)) {
str = ToString(cx, s);
if (!str)
return false;
length = (uint32_t) JS_GetStringLength(str);
if (length) {
chars = JS_GetStringCharsZ(cx, str);
if (!chars)
return false;
if (STRING_TO_JSVAL(str) != s)
isNewString = true;
} else {
str = nullptr;
chars = EMPTY_STRING;
chars = useAllocator ? JS_GetStringCharsZAndLength(cx, str, &length)
: JS_GetStringCharsAndLength(cx, str, &length);
if (!chars)
return false;
if (!length) {
if (useAllocator)
*((const nsAString**)d) = &EmptyString();
else
(**((nsAString**)d)).Truncate();
return true;
}
}
nsString* ws;
if (useAllocator) {
// XXX extra string copy when isNewString
if (str && !isNewString) {
size_t strLength;
const jschar *strChars = JS_GetStringCharsZAndLength(cx, str, &strLength);
if (!strChars)
return false;
XPCReadableJSStringWrapper *wrapper =
nsXPConnect::GetRuntimeInstance()->NewStringWrapper(strChars, strLength);
if (!wrapper)
return false;
*((const nsAString**)d) = wrapper;
} else if (JSVAL_IS_NULL(s)) {
XPCReadableJSStringWrapper *wrapper =
new XPCReadableJSStringWrapper();
if (!wrapper)
return false;
*((const nsAString**)d) = wrapper;
} else {
// use nsString to encourage sharing
const nsAString *rs = new nsString(chars, length);
if (!rs)
return false;
*((const nsAString**)d) = rs;
}
ws = nsXPConnect::GetRuntimeInstance()->NewShortLivedString();
*((const nsString**)d) = ws;
} else {
nsAString* ws = *((nsAString**)d);
ws = *((nsString**)d);
}
if (JSVAL_IS_NULL(s) || (!isDOMString && JSVAL_IS_VOID(s))) {
ws->Truncate();
ws->SetIsVoid(true);
} else
ws->Assign(chars, length);
if (!str) {
ws->AssignLiteral(MOZ_UTF16("undefined"));
} else if (useAllocator && STRING_TO_JSVAL(str) == s) {
// The JS string will exist over the function call.
// We don't need to copy the characters in this case.
ws->Rebind(chars, length);
} else {
ws->Assign(chars, length);
}
return true;
}
@ -622,20 +601,14 @@ XPCConvert::JSData2Native(void* d, HandleValue s,
case nsXPTType::T_UTF8STRING:
{
const jschar* chars;
uint32_t length;
size_t length;
JSString* str;
if (JSVAL_IS_NULL(s) || JSVAL_IS_VOID(s)) {
if (useAllocator) {
nsACString *rs = new nsCString();
if (!rs)
return false;
rs->SetIsVoid(true);
*((nsACString**)d) = rs;
*((const nsACString**)d) = &NullCString();
} else {
nsCString* rs = *((nsCString**)d);
rs->Truncate();
rs->SetIsVoid(true);
}
return true;
@ -644,11 +617,19 @@ XPCConvert::JSData2Native(void* d, HandleValue s,
// The JS val is neither null nor void...
if (!(str = ToString(cx, s))||
!(chars = JS_GetStringCharsZ(cx, str))) {
!(chars = JS_GetStringCharsAndLength(cx, str, &length))) {
return false;
}
length = JS_GetStringLength(str);
if (!length) {
if (useAllocator) {
*((const nsACString**)d) = &EmptyCString();
} else {
nsCString* rs = *((nsCString**)d);
rs->Truncate();
}
return true;
}
nsCString *rs;
if (useAllocator) {
@ -661,9 +642,7 @@ XPCConvert::JSData2Native(void* d, HandleValue s,
} else {
rs = *((nsCString**)d);
}
const char16_t* start = (const char16_t*)chars;
const char16_t* end = start + length;
CopyUTF16toUTF8(nsDependentSubstring(start, end), *rs);
CopyUTF16toUTF8(Substring(chars, length), *rs);
return true;
}
@ -696,6 +675,16 @@ XPCConvert::JSData2Native(void* d, HandleValue s,
return false;
}
if (!length) {
if (useAllocator) {
*((const nsACString**)d) = &EmptyCString();
} else {
nsCString* rs = *((nsCString**)d);
rs->Truncate();
}
return true;
}
nsACString *rs;
if (useAllocator) {
rs = new nsCString();

View File

@ -1415,38 +1415,32 @@ XPCJSRuntime::SizeOfIncludingThis(MallocSizeOf mallocSizeOf)
return n;
}
XPCReadableJSStringWrapper *
XPCJSRuntime::NewStringWrapper(const char16_t *str, uint32_t len)
nsString*
XPCJSRuntime::NewShortLivedString()
{
for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
StringWrapperEntry& ent = mScratchStrings[i];
if (!ent.mInUse) {
ent.mInUse = true;
// Construct the string using placement new.
return new (ent.mString.addr()) XPCReadableJSStringWrapper(str, len);
if (mScratchStrings[i].empty()) {
mScratchStrings[i].construct();
return mScratchStrings[i].addr();
}
}
// All our internal string wrappers are used, allocate a new string.
return new XPCReadableJSStringWrapper(str, len);
return new nsString();
}
void
XPCJSRuntime::DeleteString(nsAString *string)
XPCJSRuntime::DeleteShortLivedString(nsString *string)
{
if (string == &EmptyString() || string == &NullString())
return;
for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
StringWrapperEntry& ent = mScratchStrings[i];
if (string == ent.mString.addr()) {
if (!mScratchStrings[i].empty() &&
mScratchStrings[i].addr() == string) {
// One of our internal strings is no longer in use, mark
// it as such and destroy the string.
ent.mInUse = false;
ent.mString.addr()->~XPCReadableJSStringWrapper();
// it as such and free its data.
mScratchStrings[i].destroy();
return;
}
}
@ -1566,7 +1560,7 @@ XPCJSRuntime::~XPCJSRuntime()
#ifdef DEBUG
for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
MOZ_ASSERT(!mScratchStrings[i].mInUse, "Uh, string wrapper still in use!");
MOZ_ASSERT(mScratchStrings[i].empty(), "Short lived string still in use");
}
#endif
}

View File

@ -51,6 +51,7 @@
#endif
#ifdef MOZ_CRASHREPORTER
#include "nsExceptionHandler.h"
#include "nsICrashReporter.h"
#endif
@ -68,7 +69,7 @@ public:
~XPCShellDirProvider() { }
// The platform resource folder
bool SetGREDir(const char *dir);
void SetGREDir(nsIFile *greDir);
void ClearGREDir() { mGREDir = nullptr; }
// The application resource folder
void SetAppDir(nsIFile *appFile);
@ -1364,16 +1365,32 @@ XRE_XPCShellMain(int argc, char **argv, char **envp)
dirprovider.SetAppFile(appFile);
nsCOMPtr<nsIFile> greDir;
if (argc > 1 && !strcmp(argv[1], "-g")) {
if (argc < 3)
return usage();
if (!dirprovider.SetGREDir(argv[2])) {
printf("SetGREDir failed.\n");
rv = XRE_GetFileFromPath(argv[2], getter_AddRefs(greDir));
if (NS_FAILED(rv)) {
printf("Couldn't use given GRE dir.\n");
return 1;
}
dirprovider.SetGREDir(greDir);
argc -= 2;
argv += 2;
} else {
nsAutoString workingDir;
if (!GetCurrentWorkingDirectory(workingDir)) {
printf("GetCurrentWorkingDirectory failed.\n");
return 1;
}
rv = NS_NewLocalFile(workingDir, true, getter_AddRefs(greDir));
if (NS_FAILED(rv)) {
printf("NS_NewLocalFile failed.\n");
return 1;
}
}
if (argc > 1 && !strcmp(argv[1], "-a")) {
@ -1411,10 +1428,15 @@ XRE_XPCShellMain(int argc, char **argv, char **envp)
}
#ifdef MOZ_CRASHREPORTER
// This is needed during startup and also shutdown, so keep it out
// of the nested scope.
// Special exception: will remain usable after NS_ShutdownXPCOM
nsCOMPtr<nsICrashReporter> crashReporter;
const char *val = getenv("MOZ_CRASHREPORTER");
if (val && *val) {
rv = CrashReporter::SetExceptionHandler(greDir, true);
if (NS_FAILED(rv)) {
printf("CrashReporter::SetExceptionHandler failed!\n");
return 1;
}
MOZ_ASSERT(CrashReporter::GetEnabled());
}
#endif
{
@ -1442,14 +1464,6 @@ XRE_XPCShellMain(int argc, char **argv, char **envp)
return 1;
}
#ifdef MOZ_CRASHREPORTER
const char *val = getenv("MOZ_CRASHREPORTER");
crashReporter = do_GetService("@mozilla.org/toolkit/crash-reporter;1");
if (val && *val) {
crashReporter->SetEnabled(true);
}
#endif
nsCOMPtr<nsIJSRuntimeService> rtsvc = do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
// get the JSRuntime from the runtime svc
if (!rtsvc) {
@ -1618,10 +1632,8 @@ XRE_XPCShellMain(int argc, char **argv, char **envp)
#ifdef MOZ_CRASHREPORTER
// Shut down the crashreporter service to prevent leaking some strings it holds.
if (crashReporter) {
crashReporter->SetEnabled(false);
crashReporter = nullptr;
}
if (CrashReporter::GetEnabled())
CrashReporter::UnsetExceptionHandler();
#endif
NS_LogTerm();
@ -1629,11 +1641,10 @@ XRE_XPCShellMain(int argc, char **argv, char **envp)
return result;
}
bool
XPCShellDirProvider::SetGREDir(const char *dir)
void
XPCShellDirProvider::SetGREDir(nsIFile* greDir)
{
nsresult rv = XRE_GetFileFromPath(dir, getter_AddRefs(mGREDir));
return NS_SUCCEEDED(rv);
mGREDir = greDir;
}
void

View File

@ -2311,11 +2311,15 @@ CallMethodHelper::CleanupParam(nsXPTCMiniVariant& param, nsXPTType& type)
break;
case nsXPTType::T_ASTRING:
case nsXPTType::T_DOMSTRING:
nsXPConnect::GetRuntimeInstance()->DeleteString((nsAString*)param.val.p);
nsXPConnect::GetRuntimeInstance()->DeleteShortLivedString((nsString*)param.val.p);
break;
case nsXPTType::T_UTF8STRING:
case nsXPTType::T_CSTRING:
delete (nsCString*) param.val.p;
{
nsCString* rs = (nsCString*)param.val.p;
if (rs != &EmptyCString() && rs != &NullCString())
delete rs;
}
break;
default:
MOZ_ASSERT(!type.IsArithmetic(), "Cleanup requested on unexpected type.");
@ -2363,10 +2367,10 @@ CallMethodHelper::HandleDipperParam(nsXPTCVariant* dp,
type_tag == nsXPTType::T_CSTRING,
"Unexpected dipper type!");
// ASTRING and DOMSTRING are very similar, and both use nsAutoString.
// ASTRING and DOMSTRING are very similar, and both use nsString.
// UTF8_STRING and CSTRING are also quite similar, and both use nsCString.
if (type_tag == nsXPTType::T_ASTRING || type_tag == nsXPTType::T_DOMSTRING)
dp->val.p = new nsAutoString();
dp->val.p = nsXPConnect::GetRuntimeInstance()->NewShortLivedString();
else
dp->val.p = new nsCString();

View File

@ -548,7 +548,7 @@ nsXPConnect::WrapNative(JSContext * aJSContext,
RootedObject aScope(aJSContext, aScopeArg);
RootedValue v(aJSContext);
return NativeInterface2JSObject(aScope, aCOMObj, nullptr, &aIID,
false, &v, aHolder);
true, &v, aHolder);
}
/* void wrapNativeToJSVal (in JSContextPtr aJSContext, in JSObjectPtr aScope, in nsISupports aCOMObj, in nsIIDPtr aIID, out jsval aVal, out nsIXPConnectJSObjectHolder aHolder); */

View File

@ -80,6 +80,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/GuardObjects.h"
#include "mozilla/Maybe.h"
#include "mozilla/MemoryReporting.h"
#include <math.h>
@ -393,33 +394,6 @@ private:
/***************************************************************************/
// class to export a JSString as an const nsAString, no refcounting :(
class XPCReadableJSStringWrapper : public nsDependentString
{
public:
typedef nsDependentString::char_traits char_traits;
XPCReadableJSStringWrapper(const char16_t *chars, size_t length) :
nsDependentString(chars, length)
{ }
XPCReadableJSStringWrapper() :
nsDependentString(char_traits::sEmptyBuffer, char_traits::sEmptyBuffer)
{ SetIsVoid(true); }
bool init(JSContext* aContext, JSString* str)
{
size_t length;
const jschar* chars = JS_GetStringCharsZAndLength(aContext, str, &length);
if (!chars)
return false;
MOZ_ASSERT(IsEmpty(), "init() on initialized string");
new(static_cast<nsDependentString *>(this)) nsDependentString(chars, length);
return true;
}
};
// In the current xpconnect system there can only be one XPCJSRuntime.
// So, xpconnect can only be used on one JSRuntime within the process.
@ -580,8 +554,8 @@ public:
~XPCJSRuntime();
XPCReadableJSStringWrapper *NewStringWrapper(const char16_t *str, uint32_t len);
void DeleteString(nsAString *string);
nsString* NewShortLivedString();
void DeleteShortLivedString(nsString *string);
void AddGCCallback(xpcGCCallback cb);
void RemoveGCCallback(xpcGCCallback cb);
@ -646,20 +620,7 @@ private:
#define XPCCCX_STRING_CACHE_SIZE 2
// String wrapper entry, holds a string, and a boolean that tells
// whether the string is in use or not.
//
// NB: The string is not stored by value so that we avoid the cost of
// construction/destruction.
struct StringWrapperEntry
{
StringWrapperEntry() : mInUse(false) { }
mozilla::AlignedStorage2<XPCReadableJSStringWrapper> mString;
bool mInUse;
};
StringWrapperEntry mScratchStrings[XPCCCX_STRING_CACHE_SIZE];
mozilla::Maybe<nsString> mScratchStrings[XPCCCX_STRING_CACHE_SIZE];
friend class Watchdog;
friend class AutoLockWatchdog;

View File

@ -2392,68 +2392,64 @@ nsCSSFrameConstructor::ConstructDocElementFrame(Element* aDocEle
else
#endif
if (aDocElement->IsSVG()) {
if (aDocElement->Tag() == nsGkAtoms::svg) {
// We're going to call the right function ourselves, so no need to give a
// function to this FrameConstructionData.
// XXXbz on the other hand, if we converted this whole function to
// FrameConstructionData/Item, then we'd need the right function
// here... but would probably be able to get away with less code in this
// function in general.
// Use a null PendingBinding, since our binding is not in fact pending.
static const FrameConstructionData rootSVGData = FCDATA_DECL(0, nullptr);
nsRefPtr<nsStyleContext> extraRef(styleContext);
FrameConstructionItem item(&rootSVGData, aDocElement,
aDocElement->Tag(), kNameSpaceID_SVG,
nullptr, extraRef.forget(), true, nullptr);
nsFrameItems frameItems;
contentFrame = ConstructOuterSVG(state, item, mDocElementContainingBlock,
styleContext->StyleDisplay(),
frameItems);
newFrame = frameItems.FirstChild();
NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
} else {
if (aDocElement->Tag() != nsGkAtoms::svg) {
return nullptr;
}
// We're going to call the right function ourselves, so no need to give a
// function to this FrameConstructionData.
// XXXbz on the other hand, if we converted this whole function to
// FrameConstructionData/Item, then we'd need the right function
// here... but would probably be able to get away with less code in this
// function in general.
// Use a null PendingBinding, since our binding is not in fact pending.
static const FrameConstructionData rootSVGData = FCDATA_DECL(0, nullptr);
nsRefPtr<nsStyleContext> extraRef(styleContext);
FrameConstructionItem item(&rootSVGData, aDocElement,
aDocElement->Tag(), kNameSpaceID_SVG,
nullptr, extraRef.forget(), true, nullptr);
nsFrameItems frameItems;
contentFrame = ConstructOuterSVG(state, item, mDocElementContainingBlock,
styleContext->StyleDisplay(),
frameItems);
newFrame = frameItems.FirstChild();
NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
} else if (display->mDisplay == NS_STYLE_DISPLAY_TABLE) {
// We're going to call the right function ourselves, so no need to give a
// function to this FrameConstructionData.
// XXXbz on the other hand, if we converted this whole function to
// FrameConstructionData/Item, then we'd need the right function
// here... but would probably be able to get away with less code in this
// function in general.
// Use a null PendingBinding, since our binding is not in fact pending.
static const FrameConstructionData rootTableData = FCDATA_DECL(0, nullptr);
nsRefPtr<nsStyleContext> extraRef(styleContext);
FrameConstructionItem item(&rootTableData, aDocElement,
aDocElement->Tag(), kNameSpaceID_None,
nullptr, extraRef.forget(), true, nullptr);
nsFrameItems frameItems;
// if the document is a table then just populate it.
contentFrame = ConstructTable(state, item, mDocElementContainingBlock,
styleContext->StyleDisplay(),
frameItems);
newFrame = frameItems.FirstChild();
NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
} else {
bool docElemIsTable = (display->mDisplay == NS_STYLE_DISPLAY_TABLE);
if (docElemIsTable) {
// We're going to call the right function ourselves, so no need to give a
// function to this FrameConstructionData.
// XXXbz on the other hand, if we converted this whole function to
// FrameConstructionData/Item, then we'd need the right function
// here... but would probably be able to get away with less code in this
// function in general.
// Use a null PendingBinding, since our binding is not in fact pending.
static const FrameConstructionData rootTableData = FCDATA_DECL(0, nullptr);
nsRefPtr<nsStyleContext> extraRef(styleContext);
FrameConstructionItem item(&rootTableData, aDocElement,
aDocElement->Tag(), kNameSpaceID_None,
nullptr, extraRef.forget(), true, nullptr);
nsFrameItems frameItems;
// if the document is a table then just populate it.
contentFrame = ConstructTable(state, item, mDocElementContainingBlock,
styleContext->StyleDisplay(),
frameItems);
newFrame = frameItems.FirstChild();
NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
} else {
contentFrame = NS_NewBlockFormattingContext(mPresShell, styleContext);
nsFrameItems frameItems;
// Use a null PendingBinding, since our binding is not in fact pending.
ConstructBlock(state, display, aDocElement,
state.GetGeometricParent(display,
mDocElementContainingBlock),
mDocElementContainingBlock, styleContext,
&contentFrame, frameItems,
display->IsPositioned(contentFrame) ? contentFrame : nullptr,
nullptr);
newFrame = frameItems.FirstChild();
NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
}
contentFrame = NS_NewBlockFormattingContext(mPresShell, styleContext);
nsFrameItems frameItems;
// Use a null PendingBinding, since our binding is not in fact pending.
ConstructBlock(state, display, aDocElement,
state.GetGeometricParent(display,
mDocElementContainingBlock),
mDocElementContainingBlock, styleContext,
&contentFrame, frameItems,
display->IsPositioned(contentFrame) ? contentFrame : nullptr,
nullptr);
newFrame = frameItems.FirstChild();
NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
}
MOZ_ASSERT(newFrame);

View File

@ -192,6 +192,21 @@ public:
}
}
// For unicode-bidi: plaintext, reset the direction of the writing mode from
// the bidi paragraph level of the content
//XXX change uint8_t to UBiDiLevel after bug 924851
void SetDirectionFromBidiLevel(uint8_t level)
{
if (level & 1) {
// odd level, set RTL
mWritingMode |= eBidiMask;
} else {
// even level, set LTR
mWritingMode &= ~eBidiMask;
}
}
/**
* Compare two WritingModes for equality.
*/

View File

@ -3087,9 +3087,11 @@ nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext,
/* virtual */ nscoord
nsFlexContainerFrame::GetMinWidth(nsRenderingContext* aRenderingContext)
{
nscoord minWidth = 0;
DISPLAY_MIN_WIDTH(this, minWidth);
FlexboxAxisTracker axisTracker(this);
nscoord minWidth = 0;
for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
nscoord childMinWidth =
nsLayoutUtils::IntrinsicForContainer(aRenderingContext, e.get(),
@ -3111,6 +3113,9 @@ nsFlexContainerFrame::GetMinWidth(nsRenderingContext* aRenderingContext)
/* virtual */ nscoord
nsFlexContainerFrame::GetPrefWidth(nsRenderingContext* aRenderingContext)
{
nscoord prefWidth = 0;
DISPLAY_PREF_WIDTH(this, prefWidth);
// XXXdholbert Optimization: We could cache our intrinsic widths like
// nsBlockFrame does (and return it early from this function if it's set).
// Whenever anything happens that might change it, set it to
@ -3118,7 +3123,6 @@ nsFlexContainerFrame::GetPrefWidth(nsRenderingContext* aRenderingContext)
// does)
FlexboxAxisTracker axisTracker(this);
nscoord prefWidth = 0;
for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
nscoord childPrefWidth =
nsLayoutUtils::IntrinsicForContainer(aRenderingContext, e.get(),

View File

@ -686,6 +686,15 @@ public:
nsRect GetRectRelativeToSelf() const {
return nsRect(nsPoint(0, 0), mRect.Size());
}
/**
* Rect and position in logical coordinates in the frame's writing mode
*/
mozilla::LogicalRect GetLogicalRect(nscoord aContainerWidth) const {
return mozilla::LogicalRect(GetWritingMode(), GetRect(), aContainerWidth);
}
mozilla::LogicalPoint GetLogicalPosition(nscoord aContainerWidth) const {
return GetLogicalRect(aContainerWidth).Origin(GetWritingMode());
}
/**
* When we change the size of the frame's border-box rect, we may need to
@ -703,6 +712,22 @@ public:
mRect = aRect;
}
}
/**
* Set this frame's rect from a logical rect in its own writing direction
*/
void SetRectFromLogicalRect(const mozilla::LogicalRect& aRect,
nscoord aContainerWidth) {
SetRectFromLogicalRect(GetWritingMode(), aRect, aContainerWidth);
}
/**
* Set this frame's rect from a logical rect in a different writing direction
* (GetPhysicalRect will assert if the writing mode doesn't match)
*/
void SetRectFromLogicalRect(mozilla::WritingMode aWritingMode,
const mozilla::LogicalRect& aRect,
nscoord aContainerWidth) {
SetRect(aRect.GetPhysicalRect(aWritingMode, aContainerWidth));
}
void SetSize(const nsSize& aSize) {
SetRect(nsRect(mRect.TopLeft(), aSize));
}

View File

@ -2890,7 +2890,7 @@ public:
const gfxSkipCharsIterator& GetEndHint() { return mTempIterator; }
protected:
void SetupJustificationSpacing();
void SetupJustificationSpacing(bool aPostReflow);
void InitFontGroupAndFontMetrics() {
float inflation = (mWhichTextRun == nsTextFrame::eInflated)
@ -3274,7 +3274,7 @@ PropertyProvider::InitializeForDisplay(bool aTrimAfter)
mFrame->GetTrimmedOffsets(mFrag, aTrimAfter);
mStart.SetOriginalOffset(trimmed.mStart);
mLength = trimmed.mLength;
SetupJustificationSpacing();
SetupJustificationSpacing(true);
}
void
@ -3284,7 +3284,7 @@ PropertyProvider::InitializeForMeasure()
mFrame->GetTrimmedOffsets(mFrag, true, false);
mStart.SetOriginalOffset(trimmed.mStart);
mLength = trimmed.mLength;
SetupJustificationSpacing();
SetupJustificationSpacing(false);
}
@ -3326,7 +3326,7 @@ PropertyProvider::FindJustificationRange(gfxSkipCharsIterator* aStart,
}
void
PropertyProvider::SetupJustificationSpacing()
PropertyProvider::SetupJustificationSpacing(bool aPostReflow)
{
NS_PRECONDITION(mLength != INT32_MAX, "Can't call this with undefined length");
@ -3338,7 +3338,7 @@ PropertyProvider::SetupJustificationSpacing()
// called with false for aTrimAfter, we still shouldn't be assigning
// justification space to any trailing whitespace.
nsTextFrame::TrimmedOffsets trimmed =
mFrame->GetTrimmedOffsets(mFrag, true);
mFrame->GetTrimmedOffsets(mFrag, true, aPostReflow);
end.AdvanceOriginal(trimmed.mLength);
gfxSkipCharsIterator realEnd(end);
FindJustificationRange(&start, &end);

View File

@ -249,6 +249,7 @@
android:label="@string/crash_reporter_title"
android:icon="@drawable/crash_reporter"
android:theme="@style/Gecko"
android:exported="false"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="org.mozilla.gecko.reportCrash" />

View File

@ -6,6 +6,7 @@
package org.mozilla.gecko;
import org.mozilla.gecko.SiteIdentity.SecurityMode;
import org.mozilla.gecko.db.BrowserContract.Bookmarks;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.gfx.Layer;
import org.mozilla.gecko.util.ThreadUtils;
@ -418,9 +419,9 @@ public class Tab {
return;
}
mBookmark = BrowserDB.isBookmark(getContentResolver(), url);
mReadingListItem = BrowserDB.isReadingListItem(getContentResolver(), url);
final int flags = BrowserDB.getItemFlags(getContentResolver(), url);
mBookmark = (flags & Bookmarks.FLAG_BOOKMARK) > 0;
mReadingListItem = (flags & Bookmarks.FLAG_READING) > 0;
Tabs.getInstance().notifyListeners(Tab.this, Tabs.TabEvents.MENU_UPDATED);
}
});

Some files were not shown because too many files have changed in this diff Show More