Merge mozilla-central to mozilla-inbound

This commit is contained in:
Ed Morley 2013-02-08 11:17:50 +00:00
Родитель 943b369860 558f4d9a53
Коммит 66af3f5307
48 изменённых файлов: 762 добавлений и 756 удалений

Просмотреть файл

@ -135,7 +135,7 @@ let styleEditorDefinition = {
tooltip: l10n("ToolboxStyleEditor.tooltip", styleEditorStrings),
isTargetSupported: function(target) {
return target.isLocalTab;
return !target.isRemote;
},
build: function(iframeWindow, toolbox) {
@ -146,6 +146,10 @@ let styleEditorDefinition = {
let profilerDefinition = {
id: "jsprofiler",
accesskey: l10n("profiler.accesskey", profilerStrings),
key: l10n("profiler.commandkey", profilerStrings),
ordinal: 4,
modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
killswitch: "devtools.profiler.enabled",
url: "chrome://browser/content/profiler.xul",
label: l10n("profiler.label", profilerStrings),
@ -153,7 +157,7 @@ let profilerDefinition = {
tooltip: l10n("profiler.tooltip", profilerStrings),
isTargetSupported: function (target) {
return !target.isRemote;
return true;
},
build: function (frame, target) {

Просмотреть файл

@ -22,14 +22,21 @@ XPCOMUtils.defineLazyGetter(this, "DebuggerServer", function () {
* Object acting as a mediator between the ProfilerController and
* DebuggerServer.
*/
function ProfilerConnection() {
function ProfilerConnection(client) {
if (!DebuggerServer.initialized) {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
let transport = DebuggerServer.connectPipe();
this.client = new DebuggerClient(transport);
this.isRemote = true;
if (!client) {
let transport = DebuggerServer.connectPipe();
client = new DebuggerClient(transport);
this.isRemote = false;
}
this.client = client;
}
ProfilerConnection.prototype = {
@ -44,12 +51,20 @@ ProfilerConnection.prototype = {
connect: function PCn_connect(aCallback) {
let client = this.client;
client.connect(function (aType, aTraits) {
let listTabs = function () {
client.listTabs(function (aResponse) {
this.actor = aResponse.profilerActor;
aCallback();
}.bind(this));
}.bind(this));
}.bind(this);
if (this.isRemote) {
return void listTabs();
}
client.connect(function (aType, aTraits) {
listTabs();
});
},
/**
@ -123,8 +138,14 @@ ProfilerConnection.prototype = {
/**
* Object defining the profiler controller components.
*/
function ProfilerController() {
this.profiler = new ProfilerConnection();
function ProfilerController(target) {
let client;
if (target.isRemote) {
client = target.client;
}
this.profiler = new ProfilerConnection(client);
this._connected = false;
}

Просмотреть файл

@ -27,10 +27,13 @@ XPCOMUtils.defineLazyGetter(this, "DebuggerServer", function () {
* Its main function is to talk to the Cleopatra instance
* inside of the iframe.
*
* ProfileUI is also an event emitter. Currently, it emits
* only one event, 'ready', when Cleopatra is done loading.
* You can also check 'isReady' property to see if a
* particular instance has been loaded yet.
* ProfileUI is also an event emitter. It emits the following events:
* - ready, when Cleopatra is done loading (you can also check the isReady
* property to see if a particular instance has been loaded yet.
* - disabled, when Cleopatra gets disabled. Happens when another ProfileUI
* instance starts the profiler.
* - enabled, when Cleopatra gets enabled. Happens when another ProfileUI
* instance stops the profiler.
*
* @param number uid
* Unique ID for this profile.
@ -63,27 +66,45 @@ function ProfileUI(uid, panel) {
return;
}
let label = doc.querySelector("li#profile-" + this.uid + " > h1");
switch (event.data.status) {
case "loaded":
if (this.panel._runningUid !== null) {
this.iframe.contentWindow.postMessage(JSON.stringify({
uid: this._runningUid,
isCurrent: this._runningUid === uid,
task: "onStarted"
}), "*");
}
this.isReady = true;
this.emit("ready");
break;
case "start":
// Start profiling and, once started, notify the
// underlying page so that it could update the UI.
// Start profiling and, once started, notify the underlying page
// so that it could update the UI. Also, once started, we add a
// star to the profile name to indicate which profile is currently
// running.
this.panel.startProfiling(function onStart() {
var data = JSON.stringify({task: "onStarted"});
this.iframe.contentWindow.postMessage(data, "*");
this.panel.broadcast(this.uid, {task: "onStarted"});
label.textContent = label.textContent + " *";
}.bind(this));
break;
case "stop":
// Stop profiling and, once stopped, notify the
// underlying page so that it could update the UI.
// Stop profiling and, once stopped, notify the underlying page so
// that it could update the UI and remove a star from the profile
// name.
this.panel.stopProfiling(function onStop() {
var data = JSON.stringify({task: "onStopped"});
this.iframe.contentWindow.postMessage(data, "*");
this.panel.broadcast(this.uid, {task: "onStopped"});
label.textContent = label.textContent.replace(/\s\*$/, "");
}.bind(this));
break;
case "disabled":
this.emit("disabled");
break;
case "enabled":
this.emit("enabled");
}
}.bind(this));
}
@ -179,7 +200,7 @@ function ProfilerPanel(frame, toolbox) {
this.window = frame.window;
this.document = frame.document;
this.target = toolbox.target;
this.controller = new ProfilerController();
this.controller = new ProfilerController(this.target);
this.profiles = new Map();
this._uid = 0;
@ -188,15 +209,16 @@ function ProfilerPanel(frame, toolbox) {
}
ProfilerPanel.prototype = {
isReady: null,
window: null,
document: null,
target: null,
controller: null,
profiles: null,
isReady: null,
window: null,
document: null,
target: null,
controller: null,
profiles: null,
_uid: null,
_activeUid: null,
_uid: null,
_activeUid: null,
_runningUid: null,
get activeProfile() {
return this.profiles.get(this._activeUid);
@ -207,8 +229,8 @@ ProfilerPanel.prototype = {
},
/**
* Open a debug connection and, on success, switch to
* the newly created profile.
* Open a debug connection and, on success, switch to the newly created
* profile.
*
* @return Promise
*/
@ -359,6 +381,41 @@ ProfilerPanel.prototype = {
}.bind(this));
},
/**
* Broadcast messages to all Cleopatra instances.
*
* @param number target
* UID of the recepient profile. All profiles will receive the message
* but the profile specified by 'target' will have a special property,
* isCurrent, set to true.
* @param object data
* An object with a property 'task' that will be sent over to Cleopatra.
*/
broadcast: function PP_broadcast(target, data) {
if (!this.profiles) {
return;
}
if (data.task === "onStarted") {
this._runningUid = target;
} else {
this._runningUid = null;
}
let uid = this._uid;
while (uid >= 0) {
if (this.profiles.has(uid)) {
let iframe = this.profiles.get(uid).iframe;
iframe.contentWindow.postMessage(JSON.stringify({
uid: target,
isCurrent: target === uid,
task: data.task
}), "*");
}
uid -= 1;
}
},
/**
* Cleanup.
*/

Просмотреть файл

@ -10,4 +10,9 @@
#stopWrapper {
display: none;
}
#profilerMessage {
color: #999;
display: none;
}

Просмотреть файл

@ -13,6 +13,8 @@ var gInstanceUID;
* - loaded, when page is loaded.
* - start, when user wants to start profiling.
* - stop, when user wants to stop profiling.
* - disabled, when the profiler was disabled
* - enabled, when the profiler was enabled
*/
function notifyParent(status) {
if (!gInstanceUID) {
@ -45,18 +47,34 @@ function notifyParent(status) {
function onParentMessage(event) {
var start = document.getElementById("startWrapper");
var stop = document.getElementById("stopWrapper");
var profilerMessage = document.getElementById("profilerMessage");
var msg = JSON.parse(event.data);
switch (msg.task) {
case "onStarted":
start.style.display = "none";
start.querySelector("button").removeAttribute("disabled");
stop.style.display = "inline";
if (msg.isCurrent) {
start.style.display = "none";
start.querySelector("button").removeAttribute("disabled");
stop.style.display = "inline";
} else {
start.querySelector("button").setAttribute("disabled", true);
var text = gStrings.getFormatStr("profiler.alreadyRunning", [msg.uid]);
profilerMessage.textContent = text;
profilerMessage.style.display = "block";
notifyParent("disabled");
}
break;
case "onStopped":
stop.style.display = "none";
stop.querySelector("button").removeAttribute("disabled");
start.style.display = "inline";
if (msg.isCurrent) {
stop.style.display = "none";
stop.querySelector("button").removeAttribute("disabled");
start.style.display = "inline";
} else {
start.querySelector("button").removeAttribute("disabled");
profilerMessage.textContent = "";
profilerMessage.style.display = "none";
notifyParent("enabled");
}
break;
case "receiveProfileData":
loadProfile(JSON.stringify(msg.rawProfile));
@ -107,7 +125,8 @@ function initUI() {
controlPane.className = "controlPane";
controlPane.innerHTML =
"<p id='startWrapper'>" + startProfiling + "</p>" +
"<p id='stopWrapper'>" + stopProfiling + "</p>";
"<p id='stopWrapper'>" + stopProfiling + "</p>" +
"<p id='profilerMessage'></p>";
controlPane.querySelector("#startWrapper > span.btn").appendChild(startButton);
controlPane.querySelector("#stopWrapper > span.btn").appendChild(stopButton);
@ -213,4 +232,35 @@ function enterFinishedProfileUI() {
}
toggleJavascriptOnly();
}
function enterProgressUI() {
var pane = document.createElement("div");
var label = document.createElement("a");
var bar = document.createElement("progress");
var string = gStrings.getStr("profiler.loading");
pane.className = "profileProgressPane";
pane.appendChild(label);
pane.appendChild(bar);
var reporter = new ProgressReporter();
reporter.addListener(function (rep) {
var progress = rep.getProgress();
if (label.textContent !== string) {
label.textContent = string;
}
if (isNaN(progress)) {
bar.removeAttribute("value");
} else {
bar.value = progress;
}
});
gMainArea.appendChild(pane);
Parser.updateLogSetting();
return reporter;
}

Просмотреть файл

@ -14,6 +14,8 @@ MOCHITEST_BROWSER_FILES = \
browser_profiler_run.js \
browser_profiler_controller.js \
browser_profiler_profiles.js \
browser_profiler_remote.js \
browser_profiler_bug_830664_multiple_profiles.js \
head.js \
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -0,0 +1,83 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const URL = "data:text/html;charset=utf8,<p>JavaScript Profiler test</p>";
let gTab, gPanel, gUid;
function test() {
waitForExplicitFinish();
setUp(URL, function onSetUp(tab, browser, panel) {
gTab = tab;
gPanel = panel;
gPanel.once("profileCreated", function (_, uid) {
gUid = uid;
let profile = gPanel.profiles.get(uid);
if (profile.isReady) {
startProfiling();
} else {
profile.once("ready", startProfiling);
}
});
gPanel.createProfile();
});
}
function getCleoControls(doc) {
return [
doc.querySelector("#startWrapper button"),
doc.querySelector("#profilerMessage")
];
}
function sendFromActiveProfile(msg) {
let [win, doc] = getProfileInternals();
win.parent.postMessage({
uid: gPanel.activeProfile.uid,
status: msg
}, "*");
}
function startProfiling() {
gPanel.profiles.get(gUid).once("disabled", stopProfiling);
sendFromActiveProfile("start");
}
function stopProfiling() {
let [win, doc] = getProfileInternals(gUid);
let [btn, msg] = getCleoControls(doc);
ok(msg.textContent.match("Profile 1") !== null, "Message is visible");
ok(btn.hasAttribute("disabled"), "Button is disabled");
is(gPanel.document.querySelector("li#profile-1 > h1").textContent,
"Profile 1 *", "Profile 1 has a star next to it.");
is(gPanel.document.querySelector("li#profile-2 > h1").textContent,
"Profile 2", "Profile 2 doesn't have a star next to it.");
gPanel.profiles.get(gUid).once("enabled", confirmAndFinish);
sendFromActiveProfile("stop");
}
function confirmAndFinish() {
let [win, doc] = getProfileInternals(gUid);
let [btn, msg] = getCleoControls(doc);
ok(msg.style.display === "none", "Message is hidden");
ok(!btn.hasAttribute("disabled"), "Button is enabled");
is(gPanel.document.querySelector("li#profile-1 > h1").textContent,
"Profile 1", "Profile 1 doesn't have a star next to it.");
is(gPanel.document.querySelector("li#profile-2 > h1").textContent,
"Profile 2", "Profile 2 doesn't have a star next to it.");
tearDown(gTab, function onTearDown() {
gPanel = null;
gTab = null;
gUid = null;
});
}

Просмотреть файл

@ -0,0 +1,55 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const URL = "data:text/html;charset=utf8,<p>JavaScript Profiler test</p>";
let temp = {};
Cu.import("resource://gre/modules/devtools/dbg-server.jsm", temp);
let DebuggerServer = temp.DebuggerServer;
Cu.import("resource://gre/modules/devtools/dbg-client.jsm", temp);
let DebuggerClient = temp.DebuggerClient;
let debuggerSocketConnect = temp.debuggerSocketConnect;
Cu.import("resource:///modules/devtools/ProfilerController.jsm", temp);
let ProfilerController = temp.ProfilerController;
function test() {
waitForExplicitFinish();
Services.prefs.setBoolPref(REMOTE_ENABLED, true);
loadTab(URL, function onTabLoad(tab, browser) {
DebuggerServer.init(function () true);
DebuggerServer.addBrowserActors();
is(DebuggerServer._socketConnections, 0);
DebuggerServer.openListener(2929);
is(DebuggerServer._socketConnections, 1);
let transport = debuggerSocketConnect("127.0.0.1", 2929);
let client = new DebuggerClient(transport);
client.connect(function onClientConnect() {
let target = { isRemote: true, client: client };
let controller = new ProfilerController(target);
controller.connect(function onControllerConnect() {
// If this callback is called, this means listTabs call worked.
// Which means that the transport worked. Time to finish up this
// test.
function onShutdown() {
window.removeEventListener("Debugger:Shutdown", onShutdown, true);
transport = client = null;
finish();
}
window.addEventListener("Debugger:Shutdown", onShutdown, true);
client.close(function () {
gBrowser.removeTab(tab);
});
});
});
});
}

Просмотреть файл

@ -33,13 +33,6 @@ function attemptTearDown() {
});
}
function getProfileInternals() {
let win = gPanel.activeProfile.iframe.contentWindow;
let doc = win.document;
return [win, doc];
}
function testUI() {
ok(gPanel, "Profiler panel exists");
ok(gPanel.activeProfile, "Active profile exists");

Просмотреть файл

@ -3,6 +3,7 @@
let temp = {};
const PROFILER_ENABLED = "devtools.profiler.enabled";
const REMOTE_ENABLED = "devtools.debugger.remote-enabled";
Cu.import("resource:///modules/devtools/Target.jsm", temp);
let TargetFactory = temp.TargetFactory;
@ -10,6 +11,23 @@ let TargetFactory = temp.TargetFactory;
Cu.import("resource:///modules/devtools/gDevTools.jsm", temp);
let gDevTools = temp.gDevTools;
Cu.import("resource://gre/modules/devtools/dbg-server.jsm", temp);
let DebuggerServer = temp.DebuggerServer;
registerCleanupFunction(function () {
Services.prefs.clearUserPref(PROFILER_ENABLED);
Services.prefs.clearUserPref(REMOTE_ENABLED);
DebuggerServer.destroy();
});
function getProfileInternals(uid) {
let profile = (uid != null) ? gPanel.profiles.get(uid) : gPanel.activeProfile;
let win = profile.iframe.contentWindow;
let doc = win.document;
return [win, doc];
}
function loadTab(url, callback) {
let tab = gBrowser.addTab();
gBrowser.selectedTab = tab;
@ -63,6 +81,5 @@ function tearDown(tab, callback=function(){}) {
}
finish();
Services.prefs.setBoolPref(PROFILER_ENABLED, false);
});
}
}

Просмотреть файл

@ -27,6 +27,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "CmdCommands",
XPCOMUtils.defineLazyModuleGetter(this, "PageErrorListener",
"resource://gre/modules/devtools/WebConsoleUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
"resource://gre/modules/PluralForm.jsm");
XPCOMUtils.defineLazyGetter(this, "prefBranch", function() {
let prefService = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService);
@ -622,7 +625,19 @@ function DT__updateErrorsCount(aChangedTabId)
let warnings = this._warningsCount[tabId];
let btn = this._errorCounterButton;
if (errors) {
let tooltiptext = toolboxStrings.formatStringFromName("toolboxDockButtons.errorsCount.tooltip", [errors, warnings], 2);
let errorsText = toolboxStrings
.GetStringFromName("toolboxToggleButton.errorsCount");
errorsText = PluralForm.get(errors, errorsText);
let warningsText = toolboxStrings
.GetStringFromName("toolboxToggleButton.warningsCount");
warningsText = PluralForm.get(warnings, warningsText);
let tooltiptext = toolboxStrings
.formatStringFromName("toolboxToggleButton.tooltiptext",
[errors, errorsText, warnings,
warningsText], 4);
btn.setAttribute("error-count", errors);
btn.setAttribute("tooltiptext", tooltiptext);
} else {

Просмотреть файл

@ -2140,8 +2140,20 @@ create({ constructor: Variable, proto: Scope.prototype }, {
separatorLabel.hidden = true;
valueLabel.hidden = true;
this.delete = VariablesView.getterOrSetterDeleteCallback;
this.evaluationMacro = VariablesView.overrideValueEvalMacro;
// Changing getter/setter names is never allowed.
this.switch = null;
// Getter/setter properties require special handling when it comes to
// evaluation and deletion.
if (this.ownerView.eval) {
this.delete = VariablesView.getterOrSetterDeleteCallback;
this.evaluationMacro = VariablesView.overrideValueEvalMacro;
}
// Deleting getters and setters individually is not allowed if no
// evaluation method is provided.
else {
this.delete = null;
}
let getter = this.addProperty("get", { value: descriptor.get });
let setter = this.addProperty("set", { value: descriptor.set });

Просмотреть файл

@ -44,7 +44,7 @@ function test() {
function getTooltipValues() {
let matches = webconsole.getAttribute("tooltiptext")
.match(/(\d+) errors, (\d+) warnings/);
.match(/(\d+) errors?, (\d+) warnings?/);
return matches ? [matches[1], matches[2]] : [0, 0];
}

Просмотреть файл

@ -130,7 +130,7 @@ StyleEditor.prototype = {
{
let document = this.contentDocument;
if (this._styleSheetIndex == -1) {
for (let i = 0; i < document.styleSheets.length; ++i) {
for (let i = 0; i < document.styleSheets.length; i++) {
if (document.styleSheets[i] == this.styleSheet) {
this._styleSheetIndex = i;
break;
@ -1004,8 +1004,9 @@ StyleEditor.prototype = {
// copy the list of listeners to allow adding/removing listeners in handlers
let listeners = this._actionListeners.concat();
// trigger all listeners that have this action handler
for (let i = 0; i < listeners.length; ++i) {
for (let i = 0; i < listeners.length; i++) {
let listener = listeners[i];
let actionHandler = listener["on" + aName];
if (actionHandler) {

Просмотреть файл

@ -16,6 +16,7 @@ Cu.import("resource://gre/modules/PluralForm.jsm");
Cu.import("resource:///modules/devtools/StyleEditor.jsm");
Cu.import("resource:///modules/devtools/StyleEditorUtil.jsm");
Cu.import("resource:///modules/devtools/SplitView.jsm");
Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
const STYLE_EDITOR_TEMPLATE = "stylesheet";
@ -43,34 +44,43 @@ this.StyleEditorChrome = function StyleEditorChrome(aRoot, aContentWindow)
this._editors = [];
this._listeners = []; // @see addChromeListener
// Store the content window so that we can call the real contentWindow setter
// in the open method.
this._contentWindowTemp = aContentWindow;
this._contentWindow = null;
this._isContentAttached = false;
let initializeUI = function (aEvent) {
if (aEvent) {
this._window.removeEventListener("load", initializeUI, false);
}
let viewRoot = this._root.parentNode.querySelector(".splitview-root");
this._view = new SplitView(viewRoot);
this._setupChrome();
// attach to the content window
this.contentWindow = aContentWindow;
this._contentWindowID = null;
}.bind(this);
if (this._document.readyState == "complete") {
initializeUI();
} else {
this._window.addEventListener("load", initializeUI, false);
}
}
StyleEditorChrome.prototype = {
_styleSheetToSelect: null,
open: function() {
let deferred = Promise.defer();
let initializeUI = function (aEvent) {
if (aEvent) {
this._window.removeEventListener("load", initializeUI, false);
}
let viewRoot = this._root.parentNode.querySelector(".splitview-root");
this._view = new SplitView(viewRoot);
this._setupChrome();
// We need to juggle arount the contentWindow items because we need to
// trigger the setter at the appropriate time.
this.contentWindow = this._contentWindowTemp; // calls setter
this._contentWindowTemp = null;
deferred.resolve();
}.bind(this);
if (this._document.readyState == "complete") {
initializeUI();
} else {
this._window.addEventListener("load", initializeUI, false);
}
return deferred.promise;
},
/**
* Retrieve the content window attached to this chrome.
*
@ -150,15 +160,6 @@ StyleEditorChrome.prototype = {
return this._contentWindow ? this._contentWindow.document : null;
},
/**
* Retrieve whether the content has been attached and StyleEditor instances
* exist for all of its stylesheets.
*
* @return boolean
* @see addChromeListener
*/
get isContentAttached() this._isContentAttached,
/**
* Retrieve an array with the StyleEditor instance for each live style sheet,
* ordered by style sheet index.
@ -180,14 +181,6 @@ StyleEditorChrome.prototype = {
* Add a listener for StyleEditorChrome events.
*
* The listener implements IStyleEditorChromeListener := {
* onContentAttach: Called when a content window has been attached.
* All editors are instantiated, though they might
* not be loaded yet.
* Arguments: (StyleEditorChrome aChrome)
* @see contentWindow
* @see StyleEditor.isLoaded
* @see StyleEditor.addActionListener
*
* onContentDetach: Called when the content window has been detached.
* Arguments: (StyleEditorChrome aChrome)
* @see contentWindow
@ -287,7 +280,7 @@ StyleEditorChrome.prototype = {
// (re)enable UI
let matches = this._root.querySelectorAll("toolbarbutton,input,select");
for (let i = 0; i < matches.length; ++i) {
for (let i = 0; i < matches.length; i++) {
matches[i].removeAttribute("disabled");
}
},
@ -305,7 +298,7 @@ StyleEditorChrome.prototype = {
this._document.title = _("chromeWindowTitle",
document.title || document.location.href);
for (let i = 0; i < document.styleSheets.length; ++i) {
for (let i = 0; i < document.styleSheets.length; i++) {
let styleSheet = document.styleSheets[i];
let editor = new StyleEditor(document, styleSheet);
@ -313,8 +306,6 @@ StyleEditorChrome.prototype = {
this._editors.push(editor);
}
this._triggerChromeListeners("ContentAttach");
// Queue editors loading so that ContentAttach is consistently triggered
// right after all editor instances are available (this.editors) but are
// NOT loaded/ready yet. This also helps responsivity during loading when
@ -353,43 +344,36 @@ StyleEditorChrome.prototype = {
let select = function DEC_select(aEditor) {
let sheet = this._styleSheetToSelect.sheet;
let line = this._styleSheetToSelect.line;
let col = this._styleSheetToSelect.col;
let summary = sheet ? this.getSummaryElementForEditor(aEditor)
: this._view.getSummaryElementByOrdinal(0);
let line = this._styleSheetToSelect.line || 1;
let col = this._styleSheetToSelect.col || 1;
if (line) {
col = col || 1;
if (!aEditor.sourceEditor) {
// If a line or column was specified we move the caret appropriately.
let self = this;
aEditor.addActionListener({
onAttach: function SEC_selectSheet_onAttach()
{
aEditor.removeActionListener(this);
self.selectedStyleSheetIndex = aEditor.styleSheetIndex;
aEditor.sourceEditor.setCaretPosition(line - 1, col - 1);
let newSheet = self._styleSheetToSelect.sheet;
let newLine = self._styleSheetToSelect.line;
let newCol = self._styleSheetToSelect.col;
self._styleSheetToSelect = null;
if (newSheet != sheet) {
self._window.setTimeout(self.selectStyleSheet.bind(self, newSheet, newLine, newCol), 0);
}
}
});
} else {
// If a line or column was specified we move the caret appropriately.
if (!aEditor.sourceEditor) {
let onAttach = function SEC_selectSheet_onAttach() {
aEditor.removeActionListener(this);
this.selectedStyleSheetIndex = aEditor.styleSheetIndex;
aEditor.sourceEditor.setCaretPosition(line - 1, col - 1);
let newSheet = this._styleSheetToSelect.sheet;
let newLine = this._styleSheetToSelect.line;
let newCol = this._styleSheetToSelect.col;
this._styleSheetToSelect = null;
}
if (newSheet != sheet) {
this.selectStyleSheet.bind(this, newSheet, newLine, newCol);
}
}.bind(this);
aEditor.addActionListener({
onAttach: onAttach
});
} else {
// If a line or column was specified we move the caret appropriately.
aEditor.sourceEditor.setCaretPosition(line - 1, col - 1);
this._styleSheetToSelect = null;
}
this._view.activeSummary = summary;
let summary = sheet ? this.getSummaryElementForEditor(aEditor)
: this._view.getSummaryElementByOrdinal(0);
this._view.activeSummary = summary;
this.selectedStyleSheetIndex = aEditor.styleSheetIndex;
}.bind(this);
@ -404,6 +388,7 @@ StyleEditorChrome.prototype = {
if ((sheet && aEditor.styleSheet == sheet) ||
(aEditor.styleSheetIndex == 0 && sheet == null)) {
aChrome.removeChromeListener(this);
aEditor.addActionListener(self);
select(aEditor);
}
}
@ -430,7 +415,7 @@ StyleEditorChrome.prototype = {
_disableChrome: function SEC__disableChrome()
{
let matches = this._root.querySelectorAll("button,toolbarbutton,textbox");
for (let i = 0; i < matches.length; ++i) {
for (let i = 0; i < matches.length; i++) {
matches[i].setAttribute("disabled", "disabled");
}

Просмотреть файл

@ -39,10 +39,14 @@ StyleEditorPanel.prototype = {
*/
open: function StyleEditor_open() {
let contentWin = this._toolbox.target.window;
this.setPage(contentWin);
this.isReady = true;
let deferred = Promise.defer();
return Promise.resolve(this);
this.setPage(contentWin).then(function() {
this.isReady = true;
deferred.resolve(this);
}.bind(this));
return deferred.promise;
},
/**
@ -66,12 +70,16 @@ StyleEditorPanel.prototype = {
setPage: function StyleEditor_setPage(contentWindow) {
if (this._panelWin.styleEditorChrome) {
this._panelWin.styleEditorChrome.contentWindow = contentWindow;
this.selectStyleSheet(null, null, null);
} else {
let chromeRoot = this._panelDoc.getElementById("style-editor-chrome");
let chrome = new StyleEditorChrome(chromeRoot, contentWindow);
let promise = chrome.open();
this._panelWin.styleEditorChrome = chrome;
this.selectStyleSheet(null, null, null);
return promise;
}
this.selectStyleSheet(null, null, null);
},
/**

Просмотреть файл

@ -152,7 +152,7 @@ this.wire = function wire(aRoot, aSelectorOrElement, aDescriptor)
aDescriptor = {events: {click: aDescriptor}};
}
for (let i = 0; i < matches.length; ++i) {
for (let i = 0; i < matches.length; i++) {
let element = matches[i];
forEach(aDescriptor.events, function (aName, aHandler) {
element.addEventListener(aName, aHandler, false);

Просмотреть файл

@ -18,7 +18,7 @@ _BROWSER_TEST_FILES = \
browser_styleeditor_cmd_edit.html \
browser_styleeditor_import.js \
browser_styleeditor_init.js \
$(filter disabled-temporarily--bug-817294, browser_styleeditor_loading.js) \
browser_styleeditor_loading.js \
browser_styleeditor_new.js \
browser_styleeditor_passedinsheet.js \
browser_styleeditor_pretty.js \
@ -32,6 +32,7 @@ _BROWSER_TEST_FILES = \
four.html \
head.js \
helpers.js \
longload.html \
media.html \
media-small.css \
minified.html \

Просмотреть файл

@ -19,12 +19,9 @@ function test()
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
aChrome.addChromeListener({
onContentAttach: run,
onEditorAdded: testEditorAdded
});
if (aChrome.isContentAttached) {
run(aChrome);
}
run(aChrome);
});
content.location = TESTCASE_URI;

Просмотреть файл

@ -9,23 +9,17 @@ function test()
{
waitForExplicitFinish();
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
launchStyleEditorChrome(function(aChrome) {
aChrome.addChromeListener({
onContentAttach: run,
onEditorAdded: testEditorAdded
});
if (aChrome.isContentAttached) {
run(aChrome);
}
run(aChrome);
});
content.location = TESTCASE_URI;
}
let gContentAttachHandled = false;
function run(aChrome)
{
gContentAttachHandled = true;
is(aChrome.contentWindow.document.readyState, "complete",
"content document is complete");
@ -42,11 +36,6 @@ function run(aChrome)
let gEditorAddedCount = 0;
function testEditorAdded(aChrome, aEditor)
{
if (!gEditorAddedCount) {
is(gContentAttachHandled, true,
"ContentAttach event triggered before EditorAdded");
}
if (aEditor.styleSheetIndex == 0) {
gEditorAddedCount++;
testFirstStyleSheetEditor(aChrome, aEditor);

Просмотреть файл

@ -2,7 +2,7 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TESTCASE_URI = TEST_BASE + "simple.html";
const TESTCASE_URI = TEST_BASE + "longload.html";
function test()
@ -13,52 +13,25 @@ function test()
// launch Style Editor right when the tab is created (before load)
// this checks that the Style Editor still launches correctly when it is opened
// *while* the page is still loading
// *while* the page is still loading. The Style Editor should not signal that
// it is loaded until the accompanying content page is loaded.
launchStyleEditorChrome(function (aChrome) {
content.location = TESTCASE_URI;
executeSoon(function() {
isnot(gBrowser.selectedBrowser.contentWindow.document.readyState, "complete",
"content document is still loading");
is(aChrome.contentWindow.document.readyState, "complete",
"content document is complete");
let root = gChromeWindow.document.querySelector(".splitview-root");
ok(root.classList.contains("loading"),
"style editor root element has 'loading' class name");
ok(!root.classList.contains("loading"),
"style editor root element does not have 'loading' class name anymore");
let button = gChromeWindow.document.querySelector(".style-editor-newButton");
ok(button.hasAttribute("disabled"),
"new style sheet button is disabled");
ok(!button.hasAttribute("disabled"),
"new style sheet button is enabled");
button = gChromeWindow.document.querySelector(".style-editor-importButton");
ok(button.hasAttribute("disabled"),
"import button is disabled");
ok(!button.hasAttribute("disabled"),
"import button is enabled");
if (!aChrome.isContentAttached) {
aChrome.addChromeListener({
onContentAttach: run
});
} else {
run(aChrome);
}
});
finish();
});
}
function run(aChrome)
{
is(aChrome.contentWindow.document.readyState, "complete",
"content document is complete");
let root = gChromeWindow.document.querySelector(".splitview-root");
ok(!root.classList.contains("loading"),
"style editor root element does not have 'loading' class name anymore");
let button = gChromeWindow.document.querySelector(".style-editor-newButton");
ok(!button.hasAttribute("disabled"),
"new style sheet button is enabled");
button = gChromeWindow.document.querySelector(".style-editor-importButton");
ok(!button.hasAttribute("disabled"),
"import button is enabled");
finish();
}

Просмотреть файл

@ -13,12 +13,9 @@ function test()
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
aChrome.addChromeListener({
onContentAttach: run,
onEditorAdded: testEditorAdded
});
if (aChrome.isContentAttached) {
run(aChrome);
}
run(aChrome);
});
content.location = TESTCASE_URI;

Просмотреть файл

@ -19,13 +19,7 @@ function test() {
aWindow.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
cache.evictEntries(Ci.nsICache.STORE_ANYWHERE);
launchStyleEditorChromeFromWindow(aWindow, function(aChrome) {
if (aChrome.isContentAttached) {
onEditorAdded(aChrome, aChrome.editors[0]);
} else {
aChrome.addChromeListener({
onEditorAdded: onEditorAdded
});
}
onEditorAdded(aChrome, aChrome.editors[0]);
});
}, true);

Просмотреть файл

@ -10,12 +10,7 @@ function test()
waitForExplicitFinish();
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
aChrome.addChromeListener({
onContentAttach: run
});
if (aChrome.isContentAttached) {
run(aChrome);
}
run(aChrome);
});
content.location = TESTCASE_URI;

Просмотреть файл

@ -12,15 +12,8 @@ function test()
waitForExplicitFinish();
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
if (aChrome.isContentAttached) {
run(aChrome);
} else {
aChrome.addChromeListener({
onContentAttach: run
});
}
run(aChrome);
});
content.location = TESTCASE_URI;
}
@ -31,13 +24,13 @@ function run(aChrome)
aChrome.editors[0].addActionListener({
onAttach: function onEditorAttached(aEditor) {
let originalSourceEditor = aEditor.sourceEditor;
aEditor.sourceEditor.setCaretOffset(4); // to check the caret is preserved
// queue a resize to inverse aspect ratio
// this will trigger a detach and reattach (to workaround bug 254144)
executeSoon(function () {
waitForFocus(function () {
// queue a resize to inverse aspect ratio
// this will trigger a detach and reattach (to workaround bug 254144)
let originalSourceEditor = aEditor.sourceEditor;
aEditor.sourceEditor.setCaretOffset(4); // to check the caret is preserved
gOriginalWidth = gChromeWindow.outerWidth;
gOriginalHeight = gChromeWindow.outerHeight;
gChromeWindow.resizeTo(120, 480);

Просмотреть файл

@ -0,0 +1,28 @@
<!doctype html>
<html>
<head>
<title>Long load</title>
<link rel="stylesheet" charset="UTF-8" type="text/css" media="screen" href="simple.css"/>
<style type="text/css">
body {
background: white;
}
div {
font-size: 4em;
}
div > span {
text-decoration: underline;
}
</style>
</head>
<body>
Time passes:
<script>
for (i = 0; i < 30000; i++) {
document.write("<br>...");
}
</script>
</body>
</html>

Просмотреть файл

@ -216,9 +216,8 @@ XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function() {
});
CssHtmlTree.prototype = {
// Cache the list of properties that have matched and unmatched properties.
// Cache the list of properties that match the selected element.
_matchedProperties: null,
_unmatchedProperties: null,
htmlComplete: false,
@ -228,8 +227,8 @@ CssHtmlTree.prototype = {
// The search filter
searchField: null,
// Reference to the "Only user Styles" checkbox.
onlyUserStylesCheckbox: null,
// Reference to the "Include browser styles" checkbox.
includeBrowserStylesCheckbox: null,
// Holds the ID of the panelRefresh timeout.
_panelRefreshTimeout: null,
@ -240,9 +239,9 @@ CssHtmlTree.prototype = {
// Number of visible properties
numVisibleProperties: 0,
get showOnlyUserStyles()
get includeBrowserStyles()
{
return this.onlyUserStylesCheckbox.checked;
return this.includeBrowserStylesCheckbox.checked;
},
/**
@ -253,7 +252,6 @@ CssHtmlTree.prototype = {
highlight: function CssHtmlTree_highlight(aElement)
{
this.viewedElement = aElement;
this._unmatchedProperties = null;
this._matchedProperties = null;
if (!aElement) {
@ -288,7 +286,7 @@ CssHtmlTree.prototype = {
if (propView.visible) {
this.numVisibleProperties++;
}
propView.refreshAllSelectors();
propView.refreshMatchedSelectors();
this.propertyViews.push(propView);
}.bind(this),
onDone: function() {
@ -371,28 +369,29 @@ CssHtmlTree.prototype = {
},
/**
* The change event handler for the onlyUserStyles checkbox.
* The change event handler for the includeBrowserStyles checkbox.
*
* @param {Event} aEvent the DOM Event object.
*/
onlyUserStylesChanged: function CssHtmltree_onlyUserStylesChanged(aEvent)
includeBrowserStylesChanged:
function CssHtmltree_includeBrowserStylesChanged(aEvent)
{
this.refreshSourceFilter();
this.refreshPanel();
},
/**
* When onlyUserStyles.checked is true we only display properties that have
* matched selectors and have been included by the document or one of the
* When includeBrowserStyles.checked is false we only display properties that
* have matched selectors and have been included by the document or one of the
* document's stylesheets. If .checked is false we display all properties
* including those that come from UA stylesheets.
*/
refreshSourceFilter: function CssHtmlTree_setSourceFilter()
{
this._matchedProperties = null;
this.cssLogic.sourceFilter = this.showOnlyUserStyles ?
CssLogic.FILTER.ALL :
CssLogic.FILTER.UA;
this.cssLogic.sourceFilter = this.includeBrowserStyles ?
CssLogic.FILTER.UA :
CssLogic.FILTER.ALL;
},
/**
@ -439,41 +438,6 @@ CssHtmlTree.prototype = {
return this._matchedProperties;
},
/**
* Check if a property has unmatched selectors. Result is cached.
*
* @param {string} aProperty the name of the property you want to check.
* @return {boolean} true if the property has unmatched selectors, false
* otherwise.
*/
hasUnmatchedSelectors: function CssHtmlTree_hasUnmatchedSelectors(aProperty)
{
// Initially check all of the properties that return false for
// hasMatchedSelectors(). This speeds-up the UI.
if (!this._unmatchedProperties) {
let properties = [];
CssHtmlTree.propertyNames.forEach(function(aName) {
if (!this.matchedProperties[aName]) {
properties.push(aName);
}
}, this);
if (properties.indexOf(aProperty) == -1) {
properties.push(aProperty);
}
this._unmatchedProperties = this.cssLogic.hasUnmatchedSelectors(properties);
}
// Lazy-get the result for properties we do not have cached.
if (!(aProperty in this._unmatchedProperties)) {
let result = this.cssLogic.hasUnmatchedSelectors([aProperty]);
this._unmatchedProperties[aProperty] = result[aProperty];
}
return this._unmatchedProperties[aProperty];
},
/**
* Create a context menu.
*/
@ -688,8 +652,8 @@ CssHtmlTree.prototype = {
delete this.viewedElement;
// Remove event listeners
this.onlyUserStylesCheckbox.removeEventListener("command",
this.onlyUserStylesChanged);
this.includeBrowserStylesCheckbox.removeEventListener("command",
this.includeBrowserStylesChanged);
this.searchField.removeEventListener("command", this.filterChanged);
// Cancel tree construction
@ -777,33 +741,15 @@ PropertyView.prototype = {
// Are matched rules expanded?
matchedExpanded: false,
// Are unmatched rules expanded?
unmatchedExpanded: false,
// Unmatched selector table
unmatchedSelectorTable: null,
// Matched selector container
matchedSelectorsContainer: null,
// Matched selector expando
matchedExpander: null,
// Unmatched selector expando
unmatchedExpander: null,
// Unmatched selector container
unmatchedSelectorsContainer: null,
// Unmatched title block
unmatchedTitleBlock: null,
// Cache for matched selector views
_matchedSelectorViews: null,
// Cache for unmatched selector views
_unmatchedSelectorViews: null,
// The previously selected element used for the selector view caches
prevViewedElement: null,
@ -834,20 +780,12 @@ PropertyView.prototype = {
return this.name in this.tree.matchedProperties;
},
/**
* Does the property have any unmatched selectors?
*/
get hasUnmatchedSelectors()
{
return this.name in this.tree.hasUnmatchedSelectors;
},
/**
* Should this property be visible?
*/
get visible()
{
if (this.tree.showOnlyUserStyles && !this.hasMatchedSelectors) {
if (!this.tree.includeBrowserStyles && !this.hasMatchedSelectors) {
return false;
}
@ -969,7 +907,6 @@ PropertyView.prototype = {
if (this.prevViewedElement != this.tree.viewedElement) {
this._matchedSelectorViews = null;
this._unmatchedSelectorViews = null;
this.prevViewedElement = this.tree.viewedElement;
}
@ -983,7 +920,7 @@ PropertyView.prototype = {
this.tree.numVisibleProperties++;
this.valueNode.textContent = this.propertyInfo.value;
this.refreshAllSelectors();
this.refreshMatchedSelectors();
},
/**
@ -1010,51 +947,6 @@ PropertyView.prototype = {
}
},
/**
* Refresh the panel unmatched rules.
*/
refreshUnmatchedSelectors: function PropertyView_refreshUnmatchedSelectors()
{
let hasMatchedSelectors = this.hasMatchedSelectors;
this.unmatchedSelectorTable.hidden = !this.unmatchedExpanded;
if (hasMatchedSelectors) {
this.unmatchedSelectorsContainer.hidden = !this.matchedExpanded ||
!this.hasUnmatchedSelectors;
this.unmatchedTitleBlock.hidden = false;
} else {
this.unmatchedSelectorsContainer.hidden = !this.unmatchedExpanded;
this.unmatchedTitleBlock.hidden = true;
}
if (this.unmatchedExpanded && this.hasUnmatchedSelectors) {
CssHtmlTree.processTemplate(this.templateUnmatchedSelectors,
this.unmatchedSelectorTable, this);
if (!hasMatchedSelectors) {
this.matchedExpander.setAttribute("open", "");
this.unmatchedSelectorTable.classList.add("only-unmatched");
} else {
this.unmatchedExpander.setAttribute("open", "");
this.unmatchedSelectorTable.classList.remove("only-unmatched");
}
} else {
if (!hasMatchedSelectors) {
this.matchedExpander.removeAttribute("open");
}
this.unmatchedExpander.removeAttribute("open");
this.unmatchedSelectorTable.innerHTML = "";
}
},
/**
* Refresh the panel matched and unmatched rules
*/
refreshAllSelectors: function PropertyView_refreshAllSelectors()
{
this.refreshMatchedSelectors();
},
/**
* Provide access to the matched SelectorViews that we are currently
* displaying.
@ -1072,23 +964,6 @@ PropertyView.prototype = {
return this._matchedSelectorViews;
},
/**
* Provide access to the unmatched SelectorViews that we are currently
* displaying.
*/
get unmatchedSelectorViews()
{
if (!this._unmatchedSelectorViews) {
this._unmatchedSelectorViews = [];
this.propertyInfo.unmatchedSelectors.forEach(
function unmatchedSelectorViews_convert(aSelectorInfo) {
this._unmatchedSelectorViews.push(new SelectorView(this.tree, aSelectorInfo));
}, this);
}
return this._unmatchedSelectorViews;
},
/**
* The action when a user expands matched selectors.
*
@ -1098,17 +973,7 @@ PropertyView.prototype = {
matchedExpanderClick: function PropertyView_matchedExpanderClick(aEvent)
{
this.matchedExpanded = !this.matchedExpanded;
this.refreshAllSelectors();
aEvent.preventDefault();
},
/**
* The action when a user expands unmatched selectors.
*/
unmatchedSelectorsClick: function PropertyView_unmatchedSelectorsClick(aEvent)
{
this.unmatchedExpanded = !this.unmatchedExpanded;
this.refreshUnmatchedSelectors();
this.refreshMatchedSelectors();
aEvent.preventDefault();
},
@ -1145,11 +1010,11 @@ function SelectorView(aTree, aSelectorInfo)
* @see CssLogic.STATUS
*/
SelectorView.STATUS_NAMES = [
// "Unmatched", "Parent Match", "Matched", "Best Match"
// "Parent Match", "Matched", "Best Match"
];
SelectorView.CLASS_NAMES = [
"unmatched", "parentmatch", "matched", "bestmatch"
"parentmatch", "matched", "bestmatch"
];
SelectorView.prototype = {
@ -1170,7 +1035,7 @@ SelectorView.prototype = {
for (let status in CssLogic.STATUS) {
let i = CssLogic.STATUS[status];
if (i > -1) {
if (i > CssLogic.STATUS.UNMATCHED) {
let value = CssHtmlTree.l10n("rule.status." + status);
// Replace normal spaces with non-breaking spaces
SelectorView.STATUS_NAMES[i] = value.replace(/ /g, '\u00A0');

Просмотреть файл

@ -23,8 +23,7 @@
* standard DOM API, but more inline with the definition in the spec.
*
* - CssPropertyInfo contains style information for a single property for the
* highlighted element. It divides the CSS rules on the page into matched and
* unmatched rules.
* highlighted element.
* - CssSelectorInfo is a wrapper around CssSelector, which adds sorting with
* reference to the selected element.
*/
@ -120,13 +119,11 @@ CssLogic.prototype = {
// processMatchedSelectors().
_passId: 0,
// Used for tracking matched CssSelector objects, such that we can skip them
// in processUnmatchedSelectors().
// Used for tracking matched CssSelector objects.
_matchId: 0,
_matchedRules: null,
_matchedSelectors: null,
_unmatchedSelectors: null,
domUtils: Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils),
@ -142,7 +139,6 @@ CssLogic.prototype = {
this._sheetsCached = false;
this._matchedRules = null;
this._matchedSelectors = null;
this._unmatchedSelectors = null;
},
/**
@ -177,7 +173,6 @@ CssLogic.prototype = {
this._matchedRules = null;
this._matchedSelectors = null;
this._unmatchedSelectors = null;
let win = this.viewedDocument.defaultView;
this._computedStyle = win.getComputedStyle(this.viewedElement, "");
},
@ -220,7 +215,6 @@ CssLogic.prototype = {
if (needFullUpdate) {
this._matchedRules = null;
this._matchedSelectors = null;
this._unmatchedSelectors = null;
this._propertyInfos = {};
} else {
// Update the CssPropertyInfo objects.
@ -468,7 +462,6 @@ CssLogic.prototype = {
}
this._matchedSelectors = [];
this._unmatchedSelectors = null;
this._passId++;
for (let i = 0; i < this._matchedRules.length; i++) {
@ -513,52 +506,6 @@ CssLogic.prototype = {
return false;
},
/**
* Process the CssSelector object that do not match the highlighted elements,
* nor its parents. Your callback function is invoked for every such
* CssSelector object. You receive one argument: the CssSelector object.
*
* The list of unmatched selectors is cached.
*
* @param {function} aCallback the function you want to execute for each of
* the unmatched selectors.
* @param {object} aScope the scope you want for the callback function. aScope
* will be the this object when aCallback executes.
*/
processUnmatchedSelectors: function CL_processUnmatchedSelectors(aCallback, aScope)
{
if (this._unmatchedSelectors) {
if (aCallback) {
this._unmatchedSelectors.forEach(aCallback, aScope);
}
return;
}
if (!this._matchedSelectors) {
this.processMatchedSelectors();
}
this._unmatchedSelectors = [];
this.forEachSheet(function (aSheet) {
// We do not show unmatched selectors from system stylesheets
if (!aSheet.contentSheet || aSheet.disabled || !aSheet.mediaMatches) {
return;
}
aSheet.forEachRule(function (aRule) {
aRule.selectors.forEach(function (aSelector) {
if (aSelector._matchId !== this._matchId) {
this._unmatchedSelectors.push(aSelector);
if (aCallback) {
aCallback.call(aScope, aSelector);
}
}
}, this);
}, this);
}, this);
},
/**
* Check if the highlighted element or it's parents have matched selectors.
*
@ -667,95 +614,6 @@ CssLogic.prototype = {
element.nodeType === Ci.nsIDOMNode.ELEMENT_NODE);
},
/**
* Check if the highlighted element or it's parents have unmatched selectors.
*
* Please note that this method is far slower than hasMatchedSelectors()
* because it needs to do a lot more checks in the DOM.
*
* @param {array} aProperties The list of properties you want to check if they
* have unmatched selectors or not.
* @return {object} An object that tells for each property if it has unmatched
* selectors or not. Object keys are property names and values are booleans.
*/
hasUnmatchedSelectors: function CL_hasUnmatchedSelectors(aProperties)
{
if (!this._matchedRules) {
this._buildMatchedRules();
}
let result = {};
this.forSomeSheets(function (aSheet) {
if (!aSheet.contentSheet || aSheet.disabled || !aSheet.mediaMatches) {
return false;
}
return aSheet.forSomeRules(function (aRule) {
let unmatched = aRule._matchId !== this._matchId ||
this._ruleHasUnmatchedSelector(aRule);
if (!unmatched) {
return false;
}
aProperties = aProperties.filter(function(aProperty) {
if (!aRule.getPropertyValue(aProperty)) {
// Keep this property for the next rule. We need to find a rule
// which has the property.
return true;
}
result[aProperty] = true;
// We found a rule that has the current property while it does not
// match the current element. We can remove this property from the
// array.
return false;
});
return aProperties.length == 0;
}, this);
}, this);
aProperties.forEach(function(aProperty) { result[aProperty] = false; });
return result;
},
/**
* Check if a CssRule has an unmatched selector for the highlighted element or
* its parents.
*
* @private
* @param {CssRule} aRule The rule you want to check if it has an unmatched
* selector.
* @return {boolean} True if the rule has an unmatched selector, false
* otherwise.
*/
_ruleHasUnmatchedSelector: function CL__ruleHasUnmatchedSelector(aRule)
{
if (!aRule._cssSheet && aRule.sourceElement) {
// CssRule wraps element.style, which never has unmatched selectors.
return false;
}
let element = this.viewedElement;
let selectors = aRule.selectors;
do {
selectors = selectors.filter(function(aSelector) {
return !element.mozMatchesSelector(aSelector);
});
if (selectors.length == 0) {
break;
}
} while ((element = element.parentNode) &&
element.nodeType === Ci.nsIDOMNode.ELEMENT_NODE);
return selectors.length > 0;
},
/**
* Tells if the given DOM CSS object matches the current view media.
*
@ -1546,9 +1404,9 @@ CssSelector.prototype = {
* A cache of information about the matched rules, selectors and values attached
* to a CSS property, for the highlighted element.
*
* The heart of the CssPropertyInfo object is the _findMatchedSelectors() and
* _findUnmatchedSelectors() methods. These are invoked when the PropertyView
* tries to access the .matchedSelectors and .unmatchedSelectors arrays.
* The heart of the CssPropertyInfo object is the _findMatchedSelectors()
* method. This are invoked when the PropertyView tries to access the
* .matchedSelectors array.
* Results are cached, for later reuse.
*
* @param {CssLogic} aCssLogic Reference to the parent CssLogic instance
@ -1570,7 +1428,6 @@ function CssPropertyInfo(aCssLogic, aProperty)
// counted. This includes rules that come from filtered stylesheets (those
// that have sheetAllowed = false).
this._matchedSelectors = null;
this._unmatchedSelectors = null;
}
CssPropertyInfo.prototype = {
@ -1613,23 +1470,6 @@ CssPropertyInfo.prototype = {
return this._matchedRuleCount;
},
/**
* Retrieve the number of unmatched rules.
*
* @return {number} the number of rules that do not match the highlighted
* element or its parents.
*/
get unmatchedRuleCount()
{
if (!this._unmatchedSelectors) {
this._findUnmatchedSelectors();
} else if (this.needRefilter) {
this._refilterSelectors();
}
return this._unmatchedRuleCount;
},
/**
* Retrieve the array holding CssSelectorInfo objects for each of the matched
* selectors, from each of the matched rules. Only selectors coming from
@ -1649,25 +1489,6 @@ CssPropertyInfo.prototype = {
return this._matchedSelectors;
},
/**
* Retrieve the array holding CssSelectorInfo objects for each of the
* unmatched selectors, from each of the unmatched rules. Only selectors
* coming from allowed stylesheets are included in the array.
*
* @return {array} the list of CssSelectorInfo objects of selectors that do
* not match the highlighted element or its parents.
*/
get unmatchedSelectors()
{
if (!this._unmatchedSelectors) {
this._findUnmatchedSelectors();
} else if (this.needRefilter) {
this._refilterSelectors();
}
return this._unmatchedSelectors;
},
/**
* Find the selectors that match the highlighted element and its parents.
* Uses CssLogic.processMatchedSelectors() to find the matched selectors,
@ -1726,53 +1547,8 @@ CssPropertyInfo.prototype = {
},
/**
* Find the selectors that do not match the highlighted element and its
* parents.
* @private
*/
_findUnmatchedSelectors: function CssPropertyInfo_findUnmatchedSelectors()
{
this._unmatchedSelectors = [];
this._unmatchedRuleCount = 0;
this.needRefilter = false;
this._cssLogic._passId++;
this._cssLogic.processUnmatchedSelectors(this._processUnmatchedSelector,
this);
// Sort the selectors by specificity.
this._unmatchedSelectors.sort(function(aSelectorInfo1, aSelectorInfo2) {
return aSelectorInfo1.compareTo(aSelectorInfo2);
});
},
/**
* Process an unmatched CssSelector object. Note that in order to avoid
* information overload we DO NOT show unmatched system rules.
*
* @private
* @param {CssSelector} aSelector the unmatched CssSelector object.
*/
_processUnmatchedSelector: function CPI_processUnmatchedSelector(aSelector)
{
let cssRule = aSelector._cssRule;
let value = cssRule.getPropertyValue(this.property);
if (value) {
let selectorInfo = new CssSelectorInfo(aSelector, this.property, value,
CssLogic.STATUS.UNMATCHED);
this._unmatchedSelectors.push(selectorInfo);
if (this._cssLogic._passId != cssRule._passId) {
if (cssRule.sheetAllowed) {
this._unmatchedRuleCount++;
}
cssRule._passId = this._cssLogic._passId;
}
}
},
/**
* Refilter the matched and unmatched selectors arrays when the
* CssLogic.sourceFilter changes. This allows for quick filter changes.
* Refilter the matched selectors array when the CssLogic.sourceFilter
* changes. This allows for quick filter changes.
* @private
*/
_refilterSelectors: function CssPropertyInfo_refilterSelectors()
@ -1795,12 +1571,6 @@ CssPropertyInfo.prototype = {
this._matchedRuleCount = ruleCount;
}
if (this._unmatchedSelectors) {
ruleCount = 0;
this._unmatchedSelectors.forEach(iterator);
this._unmatchedRuleCount = ruleCount;
}
this.needRefilter = false;
},
@ -1813,10 +1583,10 @@ CssPropertyInfo.prototype = {
/**
* A class that holds information about a given CssSelector object.
*
* Instances of this class are given to CssHtmlTree in the arrays of matched and
* unmatched selectors. Each such object represents a displayable row in the
* PropertyView objects. The information given by this object blends data coming
* from the CssSheet, CssRule and from the CssSelector that own this object.
* Instances of this class are given to CssHtmlTree in the array of matched
* selectors. Each such object represents a displayable row in the PropertyView
* objects. The information given by this object blends data coming from the
* CssSheet, CssRule and from the CssSelector that own this object.
*
* @param {CssSelector} aSelector The CssSelector object for which to present information.
* @param {string} aProperty The property for which information should be retrieved.

Просмотреть файл

@ -67,9 +67,10 @@ To visually debug the templates without running firefox, alter the display:none
-->
<div id="templateRoot">
<xul:hbox class="headerControls" flex="1" align="center">
<xul:checkbox class="onlyuserstyles" save="${onlyUserStylesCheckbox}"
oncommand="${onlyUserStylesChanged}" checked="true"
label="&userStylesLabel;"/>
<xul:checkbox class="includebrowserstyles"
save="${includeBrowserStylesCheckbox}"
oncommand="${includeBrowserStylesChanged}" checked="false"
label="&browserStylesLabel;"/>
<xul:textbox class="searchfield" type="search" save="${searchField}"
placeholder="&userStylesSearch;" flex="1"
oncommand="${filterChanged}"/>

Просмотреть файл

@ -7,10 +7,6 @@
color: #000;
}
.unmatched1, .unmatched2, .unmatched3, .unmatched4, .unmatched5, .unmatched6, .unmatched7 {
color: #f00;
}
div {
position: absolute;
top: 40px;
@ -29,4 +25,4 @@
<div></div>
</div>
</body>
</html>
</html>

Просмотреть файл

@ -50,7 +50,6 @@ function selectNode(aInspector)
function runTests()
{
testMatchedSelectors();
//testUnmatchedSelectors();
info("finishing up");
finishUp();
@ -73,28 +72,6 @@ function testMatchedSelectors()
"hasMatchedSelectors returns true");
}
function testUnmatchedSelectors()
{
info("checking selector counts, unmatched rules and titles");
let body = content.document.body;
ok(body, "captain, we have a body");
info("selecting content.document.body");
inspector.selection.setNode(body);
is(body, computedView.viewedElement,
"style inspector node matches the selected node");
let propertyView = new PropertyView(computedView, "color");
let numUnmatchedSelectors = propertyView.propertyInfo.unmatchedSelectors.length;
is(numUnmatchedSelectors, 13,
"CssLogic returns the correct number of unmatched selectors for body");
is(propertyView.hasUnmatchedSelectors, true,
"hasUnmatchedSelectors returns true");
}
function finishUp()
{
doc = inspector = div = computedView = null;

Просмотреть файл

@ -66,7 +66,6 @@ function SI_CheckProperty()
let cssLogic = computedView.cssLogic;
let propertyInfo = cssLogic.getPropertyInfo("color");
ok(propertyInfo.matchedRuleCount > 0, "color property has matching rules");
//ok(propertyInfo.unmatchedRuleCount > 0, "color property has unmatched rules");
}
function finishUp()

Просмотреть файл

@ -54,10 +54,10 @@ function SI_toggleDefaultStyles()
{
Services.obs.removeObserver(SI_toggleDefaultStyles, "StyleInspector-populated", false);
info("clearing \"only user styles\" checkbox");
info("checking \"Browser styles\" checkbox");
let doc = computedView.styleDocument;
let checkbox = doc.querySelector(".onlyuserstyles");
let checkbox = doc.querySelector(".includebrowserstyles");
Services.obs.addObserver(SI_AddFilterText, "StyleInspector-populated", false);
checkbox.click();
}

Просмотреть файл

@ -2,7 +2,7 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Tests that the checkbox to show only user styles works properly.
// Tests that the checkbox to include browser styles works properly.
let doc;
let inspector;
@ -65,7 +65,7 @@ function SI_toggleDefaultStyles()
{
// Click on the checkbox.
let doc = computedView.styleDocument;
let checkbox = doc.querySelector(".onlyuserstyles");
let checkbox = doc.querySelector(".includebrowserstyles");
Services.obs.addObserver(SI_checkDefaultStyles, "StyleInspector-populated", false);
checkbox.click();

Просмотреть файл

@ -114,6 +114,7 @@ MOCHITEST_BROWSER_FILES = \
browser_bug_638949_copy_link_location.js \
browser_output_longstring_expand.js \
browser_netpanel_longstring_expand.js \
browser_repeated_messages_accuracy.js \
head.js \
$(NULL)
@ -202,6 +203,7 @@ MOCHITEST_BROWSER_FILES += \
test_bug_770099_bad_policy_uri.html^headers^ \
test-result-format-as-string.html \
test-bug-737873-mixedcontent.html \
test-repeated-messages.html \
$(NULL)
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -0,0 +1,109 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Tests that makes sure messages are not considered repeated when coming from
// different lines of code, or from different severities, etc.
// See bugs 720180 and 800510.
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-repeated-messages.html";
function test() {
addTab(TEST_URI);
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
openConsole(null, consoleOpened);
}, true);
}
function consoleOpened(hud) {
// Check that css warnings are not coalesced if they come from different lines.
waitForSuccess({
name: "css warnings displayed",
validatorFn: function()
{
return hud.outputNode.querySelectorAll(".webconsole-msg-cssparser")
.length == 2;
},
successFn: testCSSRepeats.bind(null, hud),
failureFn: finishTest,
});
}
function repeatCountForNode(aNode) {
return aNode.querySelector(".webconsole-msg-repeat").getAttribute("value");
}
function testCSSRepeats(hud) {
let msgs = hud.outputNode.querySelectorAll(".webconsole-msg-cssparser");
is(repeatCountForNode(msgs[0]), 1, "no repeats for the first css warning");
is(repeatCountForNode(msgs[1]), 1, "no repeats for the second css warning");
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
testAfterReload(hud);
}, true);
content.location.reload();
}
function testAfterReload(hud) {
waitForSuccess({
name: "message repeats increased",
validatorFn: function()
{
return hud.outputNode.querySelector(".webconsole-msg-repeat")
.getAttribute("value") == 2;
},
successFn: testCSSRepeatsAfterReload.bind(null, hud),
failureFn: finishTest,
});
}
function testCSSRepeatsAfterReload(hud) {
let msgs = hud.outputNode.querySelectorAll(".webconsole-msg-cssparser");
is(msgs.length, 2, "two css warnings after reload");
is(repeatCountForNode(msgs[0]), 2, "two repeats for the first css warning");
is(repeatCountForNode(msgs[1]), 2, "two repeats for the second css warning");
hud.jsterm.clearOutput();
content.wrappedJSObject.testConsole();
waitForSuccess({
name: "console API messages displayed",
validatorFn: function()
{
return hud.outputNode.querySelectorAll(".webconsole-msg-console")
.length == 3;
},
successFn: testConsoleRepeats.bind(null, hud),
failureFn: finishTest,
});
}
function testConsoleRepeats(hud) {
let msgs = hud.outputNode.querySelectorAll(".webconsole-msg-console");
is(repeatCountForNode(msgs[0]), 2, "repeats for the first console message");
is(repeatCountForNode(msgs[1]), 1,
"no repeats for the second console log message");
is(repeatCountForNode(msgs[2]), 1, "no repeats for the console.error message");
hud.jsterm.clearOutput();
hud.jsterm.execute("undefined");
content.console.log("undefined");
waitForSuccess({
name: "messages displayed",
validatorFn: function()
{
return hud.outputNode.querySelector(".webconsole-msg-console");
},
successFn: function() {
is(hud.outputNode.childNodes.length, 3,
"correct number of messages displayed");
executeSoon(finishTest);
},
failureFn: finishTest,
});
}

Просмотреть файл

@ -55,8 +55,14 @@ function testCSSPruning(hudRef) {
},
successFn: function()
{
ok(!hudRef.ui._cssNodes["css log x"],
is(Object.keys(hudRef.ui._cssNodes).length, LOG_LIMIT,
"repeated nodes pruned from cssNodes");
let msg = hudRef.outputNode.querySelector(".webconsole-msg-cssparser " +
".webconsole-msg-repeat");
is(msg.getAttribute("value"), 1,
"repeated nodes pruned from cssNodes (confirmed)");
finishTest();
},
failureFn: finishTest,
@ -66,7 +72,12 @@ function testCSSPruning(hudRef) {
name: "repeated nodes in cssNodes",
validatorFn: function()
{
return hudRef.ui._cssNodes["css log x"];
let msg = hudRef.outputNode.querySelector(".webconsole-msg-cssparser " +
".webconsole-msg-repeat");
if (msg) {
console.debug(msg, msg.getAttribute("value"));
}
return msg && msg.getAttribute("value") == 5;
},
successFn: function()
{

Просмотреть файл

@ -0,0 +1,29 @@
<!DOCTYPE HTML>
<html dir="ltr" xml:lang="en-US" lang="en-US">
<head>
<meta charset="utf8">
<title>Test for bugs 720180 and 800510</title>
<script>
function testConsole() {
console.log("foo repeat"); console.log("foo repeat");
console.log("foo repeat"); console.error("foo repeat");
}
</script>
<style>
body {
background-image: foobarz;
}
p {
background-image: foobarz;
}
</style>
<!--
- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/
-->
</head>
<body>
<p>Hello world!</p>
</body>
</html>

Просмотреть файл

@ -867,83 +867,49 @@ WebConsoleFrame.prototype = {
},
/**
* Filter the css node from the output node if it is a repeat. CSS messages
* are merged with previous messages if they occurred in the past.
* Filter the message node from the output if it is a repeat.
*
* @private
* @param nsIDOMNode aNode
* The message node to be filtered or not.
* @returns boolean
* True if the message is filtered, false otherwise.
*/
filterRepeatedCSS: function WCF_filterRepeatedCSS(aNode)
_filterRepeatedMessage: function WCF__filterRepeatedMessage(aNode)
{
// childNodes[2] is the description node containing the text of the message.
let description = aNode.childNodes[2].textContent;
let location;
// childNodes[4] represents the location (source URL) of the error message.
// The full source URL is stored in the title attribute.
if (aNode.childNodes[4]) {
// browser_webconsole_bug_595934_message_categories.js
location = aNode.childNodes[4].getAttribute("title");
}
else {
location = "";
}
let dupe = this._cssNodes[description + location];
if (!dupe) {
// no matching nodes
this._cssNodes[description + location] = aNode;
let repeatNode = aNode.getElementsByClassName("webconsole-msg-repeat")[0];
if (!repeatNode) {
return false;
}
this.mergeFilteredMessageNode(dupe, aNode);
let uid = repeatNode._uid;
let dupeNode = null;
return true;
},
/**
* Filter the console node from the output node if it is a repeat. Console
* messages are filtered from the output if they match the immediately
* preceding message that came from the same source. The output node's
* last occurrence should have its timestamp updated.
*
* @param nsIDOMNode aNode
* The message node to be filtered or not.
* @return boolean
* True if the message is filtered, false otherwise.
*/
filterRepeatedConsole: function WCF_filterRepeatedConsole(aNode)
{
let lastMessage = this.outputNode.lastChild;
if (!lastMessage) {
return false;
if (aNode.classList.contains("webconsole-msg-cssparser")) {
dupeNode = this._cssNodes[uid];
if (!dupeNode) {
this._cssNodes[uid] = aNode;
}
}
let body = aNode.querySelector(".webconsole-msg-body");
let lastBody = lastMessage.querySelector(".webconsole-msg-body");
if (aNode.classList.contains("webconsole-msg-inspector")) {
return false;
}
if (!body || !lastBody) {
return false;
}
if (body.textContent == lastBody.textContent) {
let loc = aNode.querySelector(".webconsole-location");
let lastLoc = lastMessage.querySelector(".webconsole-location");
if (loc && lastLoc) {
if (loc.getAttribute("value") !== lastLoc.getAttribute("value")) {
return false;
}
else if (!aNode.classList.contains("webconsole-msg-network") &&
!aNode.classList.contains("webconsole-msg-inspector") &&
(aNode.classList.contains("webconsole-msg-console") ||
aNode.classList.contains("webconsole-msg-exception") ||
aNode.classList.contains("webconsole-msg-error"))) {
let lastMessage = this.outputNode.lastChild;
if (!lastMessage) {
return false;
}
this.mergeFilteredMessageNode(lastMessage, aNode);
let lastRepeatNode = lastMessage
.getElementsByClassName("webconsole-msg-repeat")[0];
if (lastRepeatNode && lastRepeatNode._uid == uid) {
dupeNode = lastMessage;
}
}
if (dupeNode) {
this.mergeFilteredMessageNode(dupeNode, aNode);
return true;
}
@ -1880,18 +1846,7 @@ WebConsoleFrame.prototype = {
let isFiltered = this.filterMessageNode(node);
let isRepeated = false;
if (node.classList.contains("webconsole-msg-cssparser")) {
isRepeated = this.filterRepeatedCSS(node);
}
if (!isRepeated &&
!node.classList.contains("webconsole-msg-network") &&
(node.classList.contains("webconsole-msg-console") ||
node.classList.contains("webconsole-msg-exception") ||
node.classList.contains("webconsole-msg-error"))) {
isRepeated = this.filterRepeatedConsole(node);
}
let isRepeated = this._filterRepeatedMessage(node);
let lastVisible = !isRepeated && !isFiltered;
if (!isRepeated) {
@ -2066,12 +2021,10 @@ WebConsoleFrame.prototype = {
}
if (aNode.classList.contains("webconsole-msg-cssparser")) {
let desc = aNode.childNodes[2].textContent;
let location = "";
if (aNode.childNodes[4]) {
location = aNode.childNodes[4].getAttribute("title");
let repeatNode = aNode.getElementsByClassName("webconsole-msg-repeat")[0];
if (repeatNode && repeatNode._uid) {
delete this._cssNodes[repeatNode._uid];
}
delete this._cssNodes[desc + location];
}
else if (aNode._connectionId &&
aNode.classList.contains("webconsole-msg-network")) {
@ -2210,6 +2163,8 @@ WebConsoleFrame.prototype = {
let repeatNode = this.document.createElementNS(XUL_NS, "label");
repeatNode.setAttribute("value", "1");
repeatNode.classList.add("webconsole-msg-repeat");
repeatNode._uid = [bodyNode.textContent, aCategory, aSeverity, aLevel,
aSourceURL, aSourceLine].join(":");
repeatContainer.appendChild(repeatNode);
// Create the timestamp.

Просмотреть файл

@ -15,6 +15,11 @@
# displayed inside the developer tools window and in the Developer Tools Menu.
profiler.label=Profiler
# LOCALIZATION NOTE (profiler.commandkey, profiler.accesskey)
# Used for the menuitem in the tool menu
profiler.commandkey=Y
profiler.accesskey=Y
# LOCALIZATION NOTE (profiler.tooltip):
# This string is displayed in the tooltip of the tab when the profiler is
# displayed inside the developer tools window.
@ -41,6 +46,9 @@ profiler.runningTime=Running Time
# LOCALIZATION NOTE (profiler.self):
# This string is displayed as a table header in the profiler UI.
# "Self" is how much time was spent doing work directly in that function.
# As opposed to the total time which is how much time was spent in that
# function and in functions it called.
profiler.self=Self
# LOCALIZATION NOTE (profiler.symbolName)
@ -63,4 +71,14 @@ profiler.start=Start
# LOCALIZATION NOTE (profiler.stop)
# This string is displayed on the button that stops the profiler.
profiler.stop=Stop
profiler.stop=Stop
# LOCALIZATION NOTE (profiler.loading)
# This string is displayed above the progress bar when the profiler
# is busy loading and parsing the report.
profiler.loading=Loading profile…
# LOCALIZATION NOTE (profiler.alreadyRunning)
# This string is displayed in the profiler whenever there is already
# another running profile. Users can run only one profile at a time.
profiler.alreadyRunning=Profiler is already running. If you want to run this profile stop Profile %S first.

Просмотреть файл

@ -8,10 +8,10 @@
- A good criteria is the language in which you'd find the best
- documentation on web development on the web. -->
<!-- LOCALIZATION NOTE (userStylesLabel): This is the label for the checkbox
<!-- LOCALIZATION NOTE (browserStylesLabel): This is the label for the checkbox
- that specifies whether the styles that are not from the user's stylesheet
- should be displayed or not. -->
<!ENTITY userStylesLabel "Only user styles">
<!ENTITY browserStylesLabel "Browser styles">
<!-- LOCALIZATION NOTE (userStylesSearch): This is the placeholder that goes in
- the search box when no search term has been entered. -->
@ -27,20 +27,13 @@
- displayed. -->
<!ENTITY noPropertiesFound "No CSS properties found.">
<!-- LOCALIZATION NOTE (unmatchedSelectors): For each style property
- the panel shows whether there are any selectors that do not match the
- currently selected element. -->
<!ENTITY unmatchedSelectors "Unmatched selectors">
<!-- LOCALIZATION NOTE (bestMatch, matched, parentMatch & unmatched): For each
- style property the panel shows the rules which hold that specific property.
- For every rule, the rule status is also displayed: a rule can be the best
- match, a match, a parent match, or a rule did not match the element the
- user has highlighted. -->
<!-- LOCALIZATION NOTE (bestMatch, matched, parentMatch): For each style
- property the panel shows the rules containing that property. For every
- rule, the rule status is also displayed: a rule can be the best match, a
- match or a parent match. -->
<!ENTITY bestMatch "Best Match">
<!ENTITY matched "Matched">
<!ENTITY parentMatch "Parent Match">
<!ENTITY unmatched "Unmatched">
<!-- FIXME: notes -->
<!ENTITY computedViewTitle "Computed">

Просмотреть файл

@ -21,7 +21,6 @@ panelTitle=Style Inspector
rule.status.BEST=Best Match
rule.status.MATCHED=Matched
rule.status.PARENT_MATCH=Parent Match
rule.status.UNMATCHED=Unmatched
# LOCALIZATION NOTE (rule.sourceElement, rule.sourceInline): For each
# style property the panel shows the rules which hold that specific property.

Просмотреть файл

@ -1,4 +1,10 @@
toolboxDockButtons.bottom.tooltip=Dock to bottom of browser window
toolboxDockButtons.side.tooltip=Dock to side of browser window
toolboxDockButtons.window.tooltip=Show in separate window
toolboxDockButtons.errorsCount.tooltip=%S errors, %S warnings.\nClick to toggle developer tools.
# LOCALIZATION NOTE (toolboxToggleButton): These strings are used for the button
# that allows users to open/close the developer tools. You can find this button
# on the developer toolbar.
toolboxToggleButton.errorsCount=error;errors
toolboxToggleButton.warningsCount=warning;warnings
toolboxToggleButton.tooltiptext=%S %S, %S %S.\nClick to toggle the developer tools.

Просмотреть файл

@ -124,7 +124,7 @@
background-color: -moz-dialog;
}
.onlyuserstyles {
.includebrowserstyles {
cursor: pointer;
font-size: 11px;
}

Просмотреть файл

@ -126,7 +126,7 @@
background-color: -moz-dialog;
}
.onlyuserstyles {
.includebrowserstyles {
cursor: pointer;
font-size: 11px;
}

Просмотреть файл

@ -126,7 +126,7 @@
background-color: -moz-dialog;
}
.onlyuserstyles {
.includebrowserstyles {
cursor: pointer;
font-size: 11px;
}

Просмотреть файл

@ -428,7 +428,6 @@
.property > .title > .value {
-moz-padding-start: 6px;
-moz-padding-end: 4px;
cursor: text;
}
.property[editable] > .title > .value {

Просмотреть файл

@ -1242,6 +1242,8 @@ function checkUpdateLogContents(aCompareLogFile) {
// Skip the source/destination lines since they contain absolute paths.
updateLogContents = updateLogContents.replace(/SOURCE DIRECTORY.*/g, "");
updateLogContents = updateLogContents.replace(/DESTINATION DIRECTORY.*/g, "");
// Skip lines that log failed attempts to open the callback executable.
updateLogContents = updateLogContents.replace(/NS_main: callback app open attempt .*/g, "");
if (gSwitchApp) {
// Remove the lines which contain absolute paths
updateLogContents = updateLogContents.replace(/^Begin moving.*$/mg, "");