зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound
This commit is contained in:
Коммит
66af3f5307
|
@ -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, "");
|
||||
|
|
Загрузка…
Ссылка в новой задаче