merge fx-team to mozilla-central

This commit is contained in:
Blair McBride 2011-11-18 14:52:59 +13:00
commit ef823f4481
91 changed files with 5387 additions and 587 deletions

View File

@ -54,9 +54,8 @@ pref("browser.hiddenWindowChromeURL", "chrome://browser/content/hiddenWindow.xul
// Enables some extra Extension System Logging (can reduce performance)
pref("extensions.logging.enabled", false);
// Enables strict compatibility. To be toggled in bug 698653, to make addons
// compatibile by default.
pref("extensions.strictCompatibility", true);
// Disables strict compatibility, making addons compatible-by-default.
pref("extensions.strictCompatibility", false);
// Specifies a minimum maxVersion an addon needs to say it's compatible with
// for it to be compatible by default.
@ -1019,6 +1018,9 @@ pref("devtools.ruleview.enabled", true);
// Enable the Scratchpad tool.
pref("devtools.scratchpad.enabled", true);
// Enable the Style Editor.
pref("devtools.styleeditor.enabled", true);
// Enable tools for Chrome development.
pref("devtools.chrome.enabled", false);

View File

@ -192,6 +192,11 @@
label="&scratchpad.label;"
key="key_scratchpad"
command="Tools:Scratchpad"/>
<menuitem id="appmenu_styleeditor"
hidden="true"
label="&styleeditor.label;"
key="key_styleeditor"
command="Tools:StyleEditor"/>
<menuitem id="appmenu_pageSource"
label="&viewPageSourceCmd.label;"
command="View:PageSource"

View File

@ -550,6 +550,12 @@
accesskey="&scratchpad.accesskey;"
key="key_scratchpad"
command="Tools:Scratchpad"/>
<menuitem id="menu_styleeditor"
hidden="true"
label="&styleeditor.label;"
accesskey="&styleeditor.accesskey;"
key="key_styleeditor"
command="Tools:StyleEditor"/>
<menuitem id="menu_pageSource"
accesskey="&pageSourceCmd.accesskey;"
label="&pageSourceCmd.label;"

View File

@ -126,6 +126,7 @@
<command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/>
<command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();" disabled="true"/>
<command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();" disabled="true"/>
<command id="Tools:StyleEditor" oncommand="StyleEditor.openChrome();" disabled="true"/>
<command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
<command id="Tools:Sanitize"
oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
@ -244,6 +245,8 @@
<key id="key_inspect" key="&inspectMenu.commandkey;" command="Tools:Inspect" modifiers="accel,shift"/>
<key id="key_scratchpad" keycode="&scratchpad.keycode;" modifiers="shift"
keytext="&scratchpad.keytext;" command="Tools:Scratchpad"/>
<key id="key_styleeditor" keycode="&styleeditor.keycode;" modifiers="shift"
keytext="&styleeditor.keytext;" command="Tools:StyleEditor"/>
<key id="openFileKb" key="&openFileCmd.commandkey;" command="Browser:OpenFile" modifiers="accel"/>
<key id="key_savePage" key="&savePageCmd.commandkey;" command="Browser:SavePage" modifiers="accel"/>
<key id="printKb" key="&printCmd.commandkey;" command="cmd_print" modifiers="accel"/>

View File

@ -1721,6 +1721,16 @@ function delayedStartup(isLoadingBlank, mustLoadSidebar) {
#endif
}
// Enable Style Editor?
let styleEditorEnabled = gPrefService.getBoolPref(StyleEditor.prefEnabledName);
if (styleEditorEnabled) {
document.getElementById("menu_styleeditor").hidden = false;
document.getElementById("Tools:StyleEditor").removeAttribute("disabled");
#ifdef MENUBAR_CAN_AUTOHIDE
document.getElementById("appmenu_styleeditor").hidden = false;
#endif
}
#ifdef MENUBAR_CAN_AUTOHIDE
// If the user (or the locale) hasn't enabled the top-level "Character
// Encoding" menu via the "browser.menu.showCharacterEncoding" preference,
@ -8969,6 +8979,34 @@ XPCOMUtils.defineLazyGetter(Scratchpad, "ScratchpadManager", function() {
return tmp.ScratchpadManager;
});
var StyleEditor = {
prefEnabledName: "devtools.styleeditor.enabled",
openChrome: function SE_openChrome()
{
const CHROME_URL = "chrome://browser/content/styleeditor.xul";
const CHROME_WINDOW_TYPE = "Tools:StyleEditor";
const CHROME_WINDOW_FLAGS = "chrome,centerscreen,resizable,dialog=no";
// focus currently open Style Editor window for this document, if any
let contentWindow = gBrowser.selectedBrowser.contentWindow;
let contentWindowID = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
let enumerator = Services.wm.getEnumerator(CHROME_WINDOW_TYPE);
while (enumerator.hasMoreElements()) {
var win = enumerator.getNext();
if (win.styleEditorChrome.contentWindowID == contentWindowID) {
win.focus();
return win;
}
}
let chromeWindow = Services.ww.openWindow(null, CHROME_URL, "_blank",
CHROME_WINDOW_FLAGS,
contentWindow);
chromeWindow.focus();
return chromeWindow;
}
};
XPCOMUtils.defineLazyGetter(window, "gShowPageResizers", function () {
#ifdef XP_WIN

View File

@ -50,6 +50,7 @@ DIRS = \
highlighter \
webconsole \
sourceeditor \
styleeditor \
styleinspector \
scratchpad \
shared \

View File

@ -3,9 +3,13 @@ browser.jar:
content/browser/NetworkPanel.xhtml (webconsole/NetworkPanel.xhtml)
* content/browser/scratchpad.xul (scratchpad/scratchpad.xul)
* content/browser/scratchpad.js (scratchpad/scratchpad.js)
* content/browser/styleeditor.xul (styleeditor/styleeditor.xul)
content/browser/splitview.css (styleeditor/splitview.css)
content/browser/styleeditor.css (styleeditor/styleeditor.css)
content/browser/devtools/csshtmltree.xul (styleinspector/csshtmltree.xul)
content/browser/devtools/cssruleview.xul (styleinspector/cssruleview.xul)
content/browser/devtools/styleinspector.css (styleinspector/styleinspector.css)
content/browser/orion.js (sourceeditor/orion/orion.js)
content/browser/orion.css (sourceeditor/orion/orion.css)
content/browser/orion-mozilla.css (sourceeditor/orion/mozilla.css)

View File

@ -491,10 +491,12 @@ var Scratchpad = {
/**
* Open a new Scratchpad window.
*
* @return nsIWindow
*/
openScratchpad: function SP_openScratchpad()
{
ScratchpadManager.openScratchpad();
return ScratchpadManager.openScratchpad();
},
/**
@ -768,6 +770,8 @@ var Scratchpad = {
else if (this.filename && this.saved) {
this.onTextSaved();
}
this._triggerObservers("Ready");
},
/**
@ -877,6 +881,68 @@ var Scratchpad = {
this.editor.destroy();
this.editor = null;
},
_observers: [],
/**
* Add an observer for Scratchpad events.
*
* The observer implements IScratchpadObserver := {
* onReady: Called when the Scratchpad and its SourceEditor are ready.
* Arguments: (Scratchpad aScratchpad)
* }
*
* All observer handlers are optional.
*
* @param IScratchpadObserver aObserver
* @see removeObserver
*/
addObserver: function SP_addObserver(aObserver)
{
this._observers.push(aObserver);
},
/**
* Remove an observer for Scratchpad events.
*
* @param IScratchpadObserver aObserver
* @see addObserver
*/
removeObserver: function SP_removeObserver(aObserver)
{
let index = this._observers.indexOf(aObserver);
if (index != -1) {
this._observers.splice(index, 1);
}
},
/**
* Trigger named handlers in Scratchpad observers.
*
* @param string aName
* Name of the handler to trigger.
* @param Array aArgs
* Optional array of arguments to pass to the observer(s).
* @see addObserver
*/
_triggerObservers: function SP_triggerObservers(aName, aArgs)
{
// insert this Scratchpad instance as the first argument
if (!aArgs) {
aArgs = [this];
} else {
aArgs.unshift(this);
}
// trigger all observers that implement this named handler
for (let i = 0; i < this._observers.length; ++i) {
let observer = this._observers[i];
let handler = observer["on" + aName];
if (handler) {
handler.apply(observer, aArgs);
}
}
}
};
XPCOMUtils.defineLazyGetter(Scratchpad, "strings", function () {

View File

@ -58,6 +58,7 @@ _BROWSER_TEST_FILES = \
browser_scratchpad_bug_679467_falsy.js \
browser_scratchpad_bug_699130_edit_ui_updates.js \
browser_scratchpad_bug_669612_unsaved.js \
head.js \
libs:: $(_BROWSER_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)

View File

@ -2,9 +2,6 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Reference to the Scratchpad chrome window object.
let gScratchpadWindow;
let gOldPref;
let DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
@ -55,8 +52,5 @@ function runTests()
Services.prefs.setBoolPref(DEVTOOLS_CHROME_ENABLED, gOldPref);
gScratchpadWindow.close();
gScratchpadWindow = null;
gBrowser.removeCurrentTab();
finish();
}

View File

@ -2,23 +2,28 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Reference to the Scratchpad chrome window object.
let gScratchpadWindow;
var ScratchpadManager = Scratchpad.ScratchpadManager;
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.selectedBrowser.addEventListener("load", function onTabLoad() {
gBrowser.selectedBrowser.removeEventListener("load", onTabLoad, true);
ok(window.Scratchpad, "Scratchpad variable exists");
Services.prefs.setIntPref("devtools.editor.tabsize", 5);
gScratchpadWindow = Scratchpad.openScratchpad();
gScratchpadWindow.addEventListener("load", runTests, false);
gScratchpadWindow.addEventListener("load", function onScratchpadLoad() {
gScratchpadWindow.removeEventListener("load", onScratchpadLoad, false);
gScratchpadWindow.Scratchpad.addObserver({
onReady: runTests
});
}, false);
}, true);
content.location = "data:text/html,Scratchpad test for the Tab key, bug 660560";
@ -26,11 +31,12 @@ function test()
function runTests()
{
gScratchpadWindow.removeEventListener("load", arguments.callee, false);
let sp = gScratchpadWindow.Scratchpad;
ok(sp, "Scratchpad object exists in new window");
is(this.onReady, runTests, "the handler runs in the context of the observer");
sp.removeObserver(this);
ok(sp.editor.hasFocus(), "the editor has focus");
sp.setText("window.foo;");
@ -63,15 +69,20 @@ function runTests()
Services.prefs.setIntPref("devtools.editor.tabsize", 6);
Services.prefs.setBoolPref("devtools.editor.expandtab", false);
gScratchpadWindow = Scratchpad.openScratchpad();
gScratchpadWindow.addEventListener("load", runTests2, false);
gScratchpadWindow.addEventListener("load", function onScratchpadLoad() {
gScratchpadWindow.removeEventListener("load", onScratchpadLoad, false);
gScratchpadWindow.Scratchpad.addObserver({
onReady: runTests2
});
}, false);
}
function runTests2()
{
gScratchpadWindow.removeEventListener("load", arguments.callee, false);
let sp = gScratchpadWindow.Scratchpad;
sp.removeObserver(this);
sp.setText("window.foo;");
sp.editor.setCaretOffset(0);
@ -85,9 +96,5 @@ function runTests2()
Services.prefs.clearUserPref("devtools.editor.tabsize");
Services.prefs.clearUserPref("devtools.editor.expandtab");
gScratchpadWindow.close();
gScratchpadWindow = null;
gBrowser.removeCurrentTab();
finish();
}

View File

@ -32,43 +32,55 @@ function testListeners()
{
let win = ScratchpadManager.openScratchpad();
win.addEventListener("load", function() {
let scratchpad = win.Scratchpad;
win.addEventListener("load", function onScratchpadLoad() {
win.removeEventListener("load", onScratchpadLoad, false);
scratchpad.setText("new text");
ok(!isStar(win), "no star if scratchpad isn't from a file");
win.Scratchpad.addObserver({
onReady: function (aScratchpad) {
aScratchpad.removeObserver(this);
scratchpad.onTextSaved();
ok(!isStar(win), "no star before changing text");
aScratchpad.setText("new text");
ok(!isStar(win), "no star if scratchpad isn't from a file");
scratchpad.setText("new text2");
ok(isStar(win), "shows star if scratchpad text changes");
aScratchpad.onTextSaved();
ok(!isStar(win), "no star before changing text");
scratchpad.onTextSaved();
ok(!isStar(win), "no star if scratchpad was just saved");
aScratchpad.setText("new text2");
ok(isStar(win), "shows star if scratchpad text changes");
scratchpad.undo();
ok(isStar(win), "star if scratchpad undo");
aScratchpad.onTextSaved();
ok(!isStar(win), "no star if scratchpad was just saved");
win.close();
done();
});
aScratchpad.undo();
ok(isStar(win), "star if scratchpad undo");
win.close();
done();
}
});
}, false);
}
function testErrorStatus()
{
let win = ScratchpadManager.openScratchpad();
win.addEventListener("load", function() {
let scratchpad = win.Scratchpad;
win.addEventListener("load", function onScratchpadLoad() {
win.removeEventListener("load", onScratchpadLoad, false);
scratchpad.onTextSaved(Components.results.NS_ERROR_FAILURE);
scratchpad.setText("new text");
ok(!isStar(win), "no star if file save failed");
win.Scratchpad.addObserver({
onReady: function (aScratchpad) {
aScratchpad.removeObserver(this);
win.close();
done();
});
aScratchpad.onTextSaved(Components.results.NS_ERROR_FAILURE);
aScratchpad.setText("new text");
ok(!isStar(win), "no star if file save failed");
win.close();
done();
}
});
}, false);
}
@ -80,15 +92,21 @@ function testRestoreNotFromFile()
}];
let [win] = ScratchpadManager.restoreSession(session);
win.addEventListener("load", function() {
let scratchpad = win.Scratchpad;
win.addEventListener("load", function onScratchpadLoad() {
win.removeEventListener("load", onScratchpadLoad, false);
scratchpad.setText("new text");
ok(!isStar(win), "no star if restored scratchpad isn't from a file");
win.Scratchpad.addObserver({
onReady: function (aScratchpad) {
aScratchpad.removeObserver(this);
win.close();
done();
});
aScratchpad.setText("new text");
ok(!isStar(win), "no star if restored scratchpad isn't from a file");
win.close();
done();
}
});
}, false);
}
function testRestoreFromFileSaved()
@ -101,17 +119,23 @@ function testRestoreFromFileSaved()
}];
let [win] = ScratchpadManager.restoreSession(session);
win.addEventListener("load", function() {
let scratchpad = win.Scratchpad;
win.addEventListener("load", function onScratchpadLoad() {
win.removeEventListener("load", onScratchpadLoad, false);
ok(!isStar(win), "no star before changing text in scratchpad restored from file");
win.Scratchpad.addObserver({
onReady: function (aScratchpad) {
aScratchpad.removeObserver(this);
scratchpad.setText("new text");
ok(isStar(win), "star when text changed from scratchpad restored from file");
ok(!isStar(win), "no star before changing text in scratchpad restored from file");
win.close();
done();
});
aScratchpad.setText("new text");
ok(isStar(win), "star when text changed from scratchpad restored from file");
win.close();
done();
}
});
}, false);
}
function testRestoreFromFileUnsaved()
@ -124,14 +148,20 @@ function testRestoreFromFileUnsaved()
}];
let [win] = ScratchpadManager.restoreSession(session);
win.addEventListener("load", function() {
let scratchpad = win.Scratchpad;
win.addEventListener("load", function onScratchpadLoad() {
win.removeEventListener("load", onScratchpadLoad, false);
ok(isStar(win), "star with scratchpad restored with unsaved text");
win.Scratchpad.addObserver({
onReady: function (aScratchpad) {
aScratchpad.removeObserver(this);
win.close();
done();
});
ok(isStar(win), "star with scratchpad restored with unsaved text");
win.close();
done();
}
});
}, false);
}
function isStar(win)

View File

@ -30,9 +30,6 @@ function testFalsy(sp)
sp.setBrowserContext();
verifyFalsies(sp);
gScratchpadWindow.close();
gScratchpadWindow = null;
gBrowser.removeCurrentTab();
finish();
}

View File

@ -6,9 +6,6 @@
Cu.import("resource:///modules/source-editor.jsm");
// Reference to the Scratchpad chrome window object.
let gScratchpadWindow;
function test()
{
waitForExplicitFinish();
@ -18,10 +15,7 @@ function test()
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
gScratchpadWindow = Scratchpad.openScratchpad();
gScratchpadWindow.addEventListener("load", function onScratchpadLoad() {
gScratchpadWindow.removeEventListener("load", onScratchpadLoad, false);
waitForFocus(runTests, gScratchpadWindow);
}, false);
gScratchpadWindow.addEventListener("load", runTests, false);
}, true);
content.location = "data:text/html,test Edit menu updates Scratchpad - bug 699130";
@ -29,6 +23,8 @@ function test()
function runTests()
{
gScratchpadWindow.removeEventListener("load", runTests, false);
let sp = gScratchpadWindow.Scratchpad;
let doc = gScratchpadWindow.document;
let winUtils = gScratchpadWindow.QueryInterface(Ci.nsIInterfaceRequestor).
@ -126,7 +122,12 @@ function runTests()
let hideAfterSelect = function() {
sp.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onCut);
EventUtils.synthesizeKey("x", {accelKey: true}, gScratchpadWindow);
waitForFocus(function () {
let selectedText = sp.editor.getSelectedText();
ok(selectedText.length > 0, "non-empty selected text will be cut");
EventUtils.synthesizeKey("x", {accelKey: true}, gScratchpadWindow);
}, gScratchpadWindow);
};
let onCut = function() {
@ -142,7 +143,9 @@ function runTests()
let hideAfterCut = function() {
sp.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onPaste);
EventUtils.synthesizeKey("v", {accelKey: true}, gScratchpadWindow);
waitForFocus(function () {
EventUtils.synthesizeKey("v", {accelKey: true}, gScratchpadWindow);
}, gScratchpadWindow);
};
let onPaste = function() {
@ -161,7 +164,7 @@ function runTests()
pass++;
testContextMenu();
} else {
finishTest();
finish();
}
};
@ -182,12 +185,5 @@ function runTests()
openMenu(10, 10, firstShow);
};
let finishTest = function() {
gScratchpadWindow.close();
gScratchpadWindow = null;
gBrowser.removeCurrentTab();
finish();
};
openMenu(10, 10, firstShow);
}

View File

@ -123,8 +123,5 @@ function runTests()
is(sp.run()[2], "undefined",
"global variable no longer exists after changing the context");
gScratchpadWindow.close();
gScratchpadWindow = null;
gBrowser.removeCurrentTab();
finish();
}

View File

@ -2,9 +2,6 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Reference to the Scratchpad chrome window object.
let gScratchpadWindow;
function test()
{
waitForExplicitFinish();
@ -129,8 +126,5 @@ function runTests()
sp.redo();
is(sp.getText(), "foo2", "redo() works");
gScratchpadWindow.close();
gScratchpadWindow = null;
gBrowser.removeCurrentTab();
finish();
}

View File

@ -138,8 +138,5 @@ function fileRead(aInputStream, aStatus)
gFile.remove(false);
gFile = null;
gScratchpad = null;
gScratchpadWindow.close();
gScratchpadWindow = null;
gBrowser.removeCurrentTab();
finish();
}

View File

@ -50,8 +50,5 @@ function runTests()
is(chromeContextCommand.getAttribute("disabled"), "true",
"Chrome context command is disabled");
gScratchpadWindow.close();
gScratchpadWindow = null;
gBrowser.removeCurrentTab();
finish();
}

View File

@ -55,9 +55,6 @@ function runTests()
executeSoon(function() {
propPanel.hidePopup();
gScratchpadWindow.close();
gScratchpadWindow = null;
gBrowser.removeCurrentTab();
finish();
});
}, false);

View File

@ -28,7 +28,9 @@ function testOpen()
{
let win = ScratchpadManager.openScratchpad();
win.addEventListener("load", function() {
win.addEventListener("load", function onScratchpadLoad() {
win.removeEventListener("load", onScratchpadLoad, false);
is(win.Scratchpad.filename, undefined, "Default filename is undefined");
is(win.Scratchpad.getText(),
win.Scratchpad.strings.GetStringFromName("scratchpadIntro"),
@ -38,7 +40,7 @@ function testOpen()
win.close();
done();
});
}, false);
}
function testOpenWithState()
@ -51,14 +53,16 @@ function testOpenWithState()
let win = ScratchpadManager.openScratchpad(state);
win.addEventListener("load", function() {
win.addEventListener("load", function onScratchpadLoad() {
win.removeEventListener("load", onScratchpadLoad, false);
is(win.Scratchpad.filename, state.filename, "Filename loaded from state");
is(win.Scratchpad.executionContext, state.executionContext, "Execution context loaded from state");
is(win.Scratchpad.getText(), state.text, "Content loaded from state");
win.close();
done();
});
}, false);
}
function testOpenInvalidState()

View File

@ -49,9 +49,10 @@ function testRestore()
asyncMap(states, function(state, done) {
// Open some scratchpad windows
let win = ScratchpadManager.openScratchpad(state);
win.addEventListener("load", function() {
win.addEventListener("load", function onScratchpadLoad() {
removeEventListener("load", onScratchpadLoad, false);
done(win);
})
}, false)
}, function(wins) {
// Then save the windows to session store
ScratchpadManager.saveOpenWindows();
@ -73,11 +74,12 @@ function testRestore()
is(restoredWins.length, 3, "Three scratchad windows restored");
asyncMap(restoredWins, function(restoredWin, done) {
restoredWin.addEventListener("load", function() {
restoredWin.addEventListener("load", function onScratchpadLoad() {
restoredWin.removeEventListener("load", onScratchpadLoad, false);
let state = restoredWin.Scratchpad.getState();
restoredWin.close();
done(state);
});
}, false);
}, function(restoredStates) {
// Then make sure they were restored with the right states
ok(statesMatch(restoredStates, states),

View File

@ -99,12 +99,8 @@ function runTests3() {
sp.setText("typeof foosbug653108;");
is(sp.run()[2], "undefined", "global variable does not exist");
gScratchpadWindow.close();
gScratchpadWindow = null;
tab1 = null;
tab2 = null;
sp = null;
gBrowser.removeCurrentTab();
gBrowser.removeCurrentTab();
finish();
}

View File

@ -75,8 +75,5 @@ function runTests()
delete sp.__noSuchMethod__;
gScratchpadWindow.close();
gScratchpadWindow = null;
gBrowser.removeCurrentTab();
finish();
}

View File

@ -0,0 +1,20 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
let gScratchpadWindow; // Reference to the Scratchpad chrome window object
function cleanup()
{
if (gScratchpadWindow) {
gScratchpadWindow.close();
gScratchpadWindow = null;
}
while (gBrowser.tabs.length > 1) {
gBrowser.removeCurrentTab();
}
}
registerCleanupFunction(cleanup);

View File

@ -25,6 +25,8 @@ Orion version: git clone from 2011-10-26
+ patch for Eclipse Bug 362835 - Pasted HTML shows twice:
https://github.com/mihaisucan/orion.client/tree/bug-362835
see https://bugs.eclipse.org/bugs/show_bug.cgi?id=362835
+ patch for Eclipse Bug 363508 - Selection is broken after TextView hide/unhide
see https://bugs.eclipse.org/bugs/show_bug.cgi?id=363508
# License

View File

@ -1952,7 +1952,7 @@ if (typeof window !== "undefined" && typeof window.define !== "undefined") {
* Contributors:
* Felipe Heidrich (IBM Corporation) - initial API and implementation
* Silenio Quarti (IBM Corporation) - initial API and implementation
* Mihai Sucan (Mozilla Foundation) - fix for Bug#334583 Bug#348471 Bug#349485 Bug#350595 Bug#360726 Bug#361180 Bug#358623 Bug#362286 Bug#362107 Bug#362428 Bug#362835
* Mihai Sucan (Mozilla Foundation) - fix for Bug#334583 Bug#348471 Bug#349485 Bug#350595 Bug#360726 Bug#361180 Bug#358623 Bug#362286 Bug#362107 Bug#362428 Bug#362835 Bug#363508
******************************************************************************/
/*global window document navigator setTimeout clearTimeout XMLHttpRequest define */
@ -3864,6 +3864,8 @@ orion.textview.TextView = (function() {
if (!this._hasFocus) {
this.focus();
}
}
if (e.preventDefault) {
e.preventDefault();
}
}

View File

@ -222,7 +222,17 @@ SourceEditor.prototype = {
}, this);
if (aCallback) {
aCallback(this);
let iframe = this._view._frame;
let document = iframe.contentDocument;
if (!document || document.readyState != "complete") {
let onIframeLoad = function () {
iframe.contentWindow.removeEventListener("load", onIframeLoad, false);
aCallback(this);
}.bind(this);
iframe.contentWindow.addEventListener("load", onIframeLoad, false);
} else {
aCallback(this);
}
}
},
@ -547,6 +557,28 @@ SourceEditor.prototype = {
this._view.focus();
},
/**
* Get the first visible line number.
*
* @return number
* The line number, counting from 0.
*/
getTopIndex: function SE_getTopIndex()
{
return this._view.getTopIndex();
},
/**
* Set the first visible line number.
*
* @param number aTopIndex
* The line number, counting from 0.
*/
setTopIndex: function SE_setTopIndex(aTopIndex)
{
this._view.setTopIndex(aTopIndex);
},
/**
* Check if the editor has focus.
*

View File

@ -0,0 +1,54 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Style Editor code.
#
# The Initial Developer of the Original Code is Mozilla Foundation.
#
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Cedric Vivier <cedricv@neonux.com> (Original author)
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
ifdef ENABLE_TESTS
ifneq (mobile,$(MOZ_BUILD_APP))
DIRS += test
endif
endif
include $(topsrcdir)/config/rules.mk
libs::
$(NSINSTALL) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools

View File

@ -0,0 +1,489 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Style Editor code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Cedric Vivier <cedricv@neonux.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
"use strict";
const EXPORTED_SYMBOLS = ["SplitView"];
/* this must be kept in sync with CSS (ie. splitview.css) */
const LANDSCAPE_MEDIA_QUERY = "(min-aspect-ratio: 5/3)";
const BINDING_USERDATA = "splitview-binding";
/**
* SplitView constructor
*
* Initialize the split view UI on an existing DOM element.
*
* A split view contains items, each of those having one summary and one details
* elements.
* It is adaptive as it behaves similarly to a richlistbox when there the aspect
* ratio is narrow or as a pair listbox-box otherwise.
*
* @param DOMElement aRoot
* @see appendItem
*/
function SplitView(aRoot)
{
this._root = aRoot;
this._controller = aRoot.querySelector(".splitview-controller");
this._nav = aRoot.querySelector(".splitview-nav");
this._side = aRoot.querySelector(".splitview-side-details");
this._activeSummary = null
this._mql = aRoot.ownerDocument.defaultView.matchMedia(LANDSCAPE_MEDIA_QUERY);
this._filter = aRoot.querySelector(".splitview-filter");
if (this._filter) {
this._setupFilterBox();
}
// items list focus and search-on-type handling
this._nav.addEventListener("keydown", function onKeyCatchAll(aEvent) {
function getFocusedItemWithin(nav) {
let node = nav.ownerDocument.activeElement;
while (node && node.parentNode != nav) {
node = node.parentNode;
}
return node;
}
// do not steal focus from inside iframes or textboxes
if (aEvent.target.ownerDocument != this._nav.ownerDocument ||
aEvent.target.tagName == "input" ||
aEvent.target.tagName == "textbox" ||
aEvent.target.tagName == "textarea" ||
aEvent.target.classList.contains("textbox")) {
return false;
}
// handle keyboard navigation within the items list
let newFocusOrdinal;
if (aEvent.keyCode == aEvent.DOM_VK_PAGE_UP ||
aEvent.keyCode == aEvent.DOM_VK_HOME) {
newFocusOrdinal = 0;
} else if (aEvent.keyCode == aEvent.DOM_VK_PAGE_DOWN ||
aEvent.keyCode == aEvent.DOM_VK_END) {
newFocusOrdinal = this._nav.childNodes.length - 1;
} else if (aEvent.keyCode == aEvent.DOM_VK_UP) {
newFocusOrdinal = getFocusedItemWithin(this._nav).getAttribute("data-ordinal");
newFocusOrdinal--;
} else if (aEvent.keyCode == aEvent.DOM_VK_DOWN) {
newFocusOrdinal = getFocusedItemWithin(this._nav).getAttribute("data-ordinal");
newFocusOrdinal++;
}
if (newFocusOrdinal !== undefined) {
aEvent.stopPropagation();
let el = this.getSummaryElementByOrdinal(newFocusOrdinal);
if (el) {
el.focus();
}
return false;
}
// search-on-type when any non-whitespace character is pressed while list
// has the focus
if (this._filter &&
!/\s/.test(String.fromCharCode(aEvent.which))) {
this._filter.focus();
}
}.bind(this), false);
}
SplitView.prototype = {
/**
* Retrieve whether the UI currently has a landscape orientation.
*
* @return boolean
*/
get isLandscape() this._mql.matches,
/**
* Retrieve the root element.
*
* @return DOMElement
*/
get rootElement() this._root,
/**
* Retrieve the active item's summary element or null if there is none.
*
* @return DOMElement
*/
get activeSummary() this._activeSummary,
/**
* Set the active item's summary element.
*
* @param DOMElement aSummary
*/
set activeSummary(aSummary)
{
if (aSummary == this._activeSummary) {
return;
}
if (this._activeSummary) {
let binding = this._activeSummary.getUserData(BINDING_USERDATA);
if (binding.onHide) {
binding.onHide(this._activeSummary, binding._details, binding.data);
}
this._activeSummary.classList.remove("splitview-active");
binding._details.classList.remove("splitview-active");
}
if (!aSummary) {
return;
}
let binding = aSummary.getUserData(BINDING_USERDATA);
aSummary.classList.add("splitview-active");
binding._details.classList.add("splitview-active");
this._activeSummary = aSummary;
if (binding.onShow) {
binding.onShow(aSummary, binding._details, binding.data);
}
aSummary.scrollIntoView();
},
/**
* Retrieve the active item's details element or null if there is none.
* @return DOMElement
*/
get activeDetails()
{
let summary = this.activeSummary;
return summary ? summary.getUserData(BINDING_USERDATA)._details : null;
},
/**
* Retrieve the summary element for a given ordinal.
*
* @param number aOrdinal
* @return DOMElement
* Summary element with given ordinal or null if not found.
* @see appendItem
*/
getSummaryElementByOrdinal: function SEC_getSummaryElementByOrdinal(aOrdinal)
{
return this._nav.querySelector("* > li[data-ordinal='" + aOrdinal + "']");
},
/**
* Append an item to the split view.
*
* @param DOMElement aSummary
* The summary element for the item.
* @param DOMElement aDetails
* The details element for the item.
* @param object aOptions
* Optional object that defines custom behavior and data for the item.
* All properties are optional :
* - function(DOMElement summary, DOMElement details, object data) onCreate
* Called when the item has been added.
* - function(summary, details, data) onShow
* Called when the item is shown/active.
* - function(summary, details, data) onHide
* Called when the item is hidden/inactive.
* - function(summary, details, data) onDestroy
* Called when the item has been removed.
* - function(summary, details, data, query) onFilterBy
* Called when the user performs a filtering search.
* If the function returns false, the item does not match query
* string and will be hidden.
* - object data
* Object to pass to the callbacks above.
* - boolean disableAnimations
* If true there is no animation or scrolling when this item is
* appended. Set this when batch appending (eg. initial population).
* - number ordinal
* Items with a lower ordinal are displayed before those with a
* higher ordinal.
*/
appendItem: function ASV_appendItem(aSummary, aDetails, aOptions)
{
let binding = aOptions || {};
binding._summary = aSummary;
binding._details = aDetails;
aSummary.setUserData(BINDING_USERDATA, binding, null);
if (!binding.disableAnimations) {
aSummary.classList.add("splitview-slide");
aSummary.classList.add("splitview-flash");
}
this._nav.appendChild(aSummary);
aSummary.addEventListener("click", function onSummaryClick(aEvent) {
aEvent.stopPropagation();
this.activeSummary = aSummary;
}.bind(this), false);
this._side.appendChild(aDetails);
if (binding.onCreate) {
// queue onCreate handler
this._root.ownerDocument.defaultView.setTimeout(function () {
binding.onCreate(aSummary, aDetails, binding.data);
}, 0);
}
if (!binding.disableAnimations) {
scheduleAnimation(aSummary, "splitview-slide", "splitview-flash");
aSummary.scrollIntoView();
}
},
/**
* Append an item to the split view according to two template elements
* (one for the item's summary and the other for the item's details).
*
* @param string aName
* Name of the template elements to instantiate.
* Requires two (hidden) DOM elements with id "splitview-tpl-summary-"
* and "splitview-tpl-details-" suffixed with aName.
* @param object aOptions
* Optional object that defines custom behavior and data for the item.
* See appendItem for full description.
* @return object{summary:,details:}
* Object with the new DOM elements created for summary and details.
* @see appendItem
*/
appendTemplatedItem: function ASV_appendTemplatedItem(aName, aOptions)
{
aOptions = aOptions || {};
let summary = this._root.querySelector("#splitview-tpl-summary-" + aName);
let details = this._root.querySelector("#splitview-tpl-details-" + aName);
summary = summary.cloneNode(true);
summary.id = "";
if (aOptions.ordinal !== undefined) { // can be zero
summary.style.MozBoxOrdinalGroup = aOptions.ordinal;
summary.setAttribute("data-ordinal", aOptions.ordinal);
}
details = details.cloneNode(true);
details.id = "";
this.appendItem(summary, details, aOptions);
return {summary: summary, details: details};
},
/**
* Remove an item from the split view.
*
* @param DOMElement aSummary
* Summary element of the item to remove.
*/
removeItem: function ASV_removeItem(aSummary)
{
if (aSummary == this._activeSummary) {
this.activeSummary = null;
}
let binding = aSummary.getUserData(BINDING_USERDATA);
aSummary.parentNode.removeChild(aSummary);
binding._details.parentNode.removeChild(binding._details);
if (binding.onDestroy) {
binding.onDestroy(aSummary, binding._details, binding.data);
}
},
/**
* Remove all items from the split view.
*/
removeAll: function ASV_removeAll()
{
while (this._nav.hasChildNodes()) {
this.removeItem(this._nav.firstChild);
}
},
/**
* Filter items by given string.
* Matching is performed on every item by calling onFilterBy when defined
* and then by searching aQuery in the summary element's text item.
* Non-matching item is hidden.
*
* If no item matches, 'splitview-all-filtered' class is set on the filter
* input element and the splitview-nav element.
*
* @param string aQuery
* The query string. Use null to reset (no filter).
* @return number
* The number of filtered (non-matching) item.
*/
filterItemsBy: function ASV_filterItemsBy(aQuery)
{
if (!this._nav.hasChildNodes()) {
return 0;
}
if (aQuery) {
aQuery = aQuery.trim();
}
if (!aQuery) {
for (let i = 0; i < this._nav.childNodes.length; ++i) {
this._nav.childNodes[i].classList.remove("splitview-filtered");
}
this._filter.classList.remove("splitview-all-filtered");
this._nav.classList.remove("splitview-all-filtered");
return 0;
}
let count = 0;
let filteredCount = 0;
for (let i = 0; i < this._nav.childNodes.length; ++i) {
let summary = this._nav.childNodes[i];
let matches = false;
let binding = summary.getUserData(BINDING_USERDATA);
if (binding.onFilterBy) {
matches = binding.onFilterBy(summary, binding._details, binding.data, aQuery);
}
if (!matches) { // try text content
let content = summary.textContent.toUpperCase();
matches = (content.indexOf(aQuery.toUpperCase()) > -1);
}
count++;
if (!matches) {
summary.classList.add("splitview-filtered");
filteredCount++;
} else {
summary.classList.remove("splitview-filtered");
}
}
if (count > 0 && filteredCount == count) {
this._filter.classList.add("splitview-all-filtered");
this._nav.classList.add("splitview-all-filtered");
} else {
this._filter.classList.remove("splitview-all-filtered");
this._nav.classList.remove("splitview-all-filtered");
}
return filteredCount;
},
/**
* Set the item's CSS class name.
* This sets the class on both the summary and details elements, retaining
* any SplitView-specific classes.
*
* @param DOMElement aSummary
* Summary element of the item to set.
* @param string aClassName
* One or more space-separated CSS classes.
*/
setItemClassName: function ASV_setItemClassName(aSummary, aClassName)
{
let binding = aSummary.getUserData(BINDING_USERDATA);
let viewSpecific;
viewSpecific = aSummary.className.match(/(splitview\-[\w-]+)/g);
viewSpecific = viewSpecific ? viewSpecific.join(" ") : "";
aSummary.className = viewSpecific + " " + aClassName;
viewSpecific = binding._details.className.match(/(splitview\-[\w-]+)/g);
viewSpecific = viewSpecific ? viewSpecific.join(" ") : "";
binding._details.className = viewSpecific + " " + aClassName;
},
/**
* Set up filter search box.
*/
_setupFilterBox: function ASV__setupFilterBox()
{
let clearFilter = function clearFilter(aEvent) {
this._filter.value = "";
this.filterItemsBy("");
return false;
}.bind(this);
this._filter.addEventListener("command", function onFilterInput(aEvent) {
this.filterItemsBy(this._filter.value);
}.bind(this), false);
this._filter.addEventListener("keyup", function onFilterKeyUp(aEvent) {
if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
clearFilter();
}
if (aEvent.keyCode == aEvent.DOM_VK_ENTER ||
aEvent.keyCode == aEvent.DOM_VK_RETURN) {
// autofocus matching item if there is only one
let matches = this._nav.querySelectorAll("* > li:not(.splitview-filtered)");
if (matches.length == 1) {
this.activeSummary = matches[0];
}
}
}.bind(this), false);
let clearButtons = this._root.querySelectorAll(".splitview-filter-clearButton");
for (let i = 0; i < clearButtons.length; ++i) {
clearButtons[i].addEventListener("click", clearFilter, false);
}
}
};
//
// private helpers
/**
* Schedule one or multiple CSS animation(s) on an element.
*
* @param DOMElement aElement
* @param string ...
* One or multiple animation class name(s).
*/
function scheduleAnimation(aElement)
{
let classes = Array.prototype.slice.call(arguments, 1);
for each (let klass in classes) {
aElement.classList.add(klass);
}
let window = aElement.ownerDocument.defaultView;
window.mozRequestAnimationFrame(function triggerAnimation() {
for each (let klass in classes) {
aElement.classList.remove(klass);
}
});
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,491 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Style Editor code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Cedric Vivier <cedricv@neonux.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
"use strict";
const EXPORTED_SYMBOLS = ["StyleEditorChrome"];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
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");
const STYLE_EDITOR_TEMPLATE = "stylesheet";
/**
* StyleEditorChrome constructor.
*
* The 'chrome' of the Style Editor is all the around the actual editor (textbox).
* Manages the sheet selector, history, and opened editor(s) for the attached
* content window.
*
* @param DOMElement aRoot
* Element that owns the chrome UI.
* @param DOMWindow aContentWindow
* Content DOMWindow to attach to this chrome.
*/
function StyleEditorChrome(aRoot, aContentWindow)
{
assert(aRoot, "Argument 'aRoot' is required to initialize StyleEditorChrome.");
this._root = aRoot;
this._document = this._root.ownerDocument;
this._window = this._document.defaultView;
this._editors = [];
this._listeners = []; // @see addChromeListener
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 = {
/**
* Retrieve the content window attached to this chrome.
*
* @return DOMWindow
* Content window or null if no content window is attached.
*/
get contentWindow() this._contentWindow,
/**
* Retrieve the ID of the content window attached to this chrome.
*
* @return number
* Window ID or -1 if no content window is attached.
*/
get contentWindowID()
{
try {
return this._contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
} catch (ex) {
return -1;
}
},
/**
* Set the content window attached to this chrome.
* Content attach or detach events/notifications are triggered after the
* operation is complete (possibly asynchronous if the content is not fully
* loaded yet).
*
* @param DOMWindow aContentWindow
* @see addChromeListener
*/
set contentWindow(aContentWindow)
{
if (this._contentWindow == aContentWindow) {
return; // no change
}
this._contentWindow = aContentWindow;
if (!aContentWindow) {
this._disableChrome();
return;
}
let onContentUnload = function () {
aContentWindow.removeEventListener("unload", onContentUnload, false);
if (this.contentWindow == aContentWindow) {
this.contentWindow = null; // detach
}
}.bind(this);
aContentWindow.addEventListener("unload", onContentUnload, false);
if (aContentWindow.document.readyState == "complete") {
this._populateChrome();
return;
} else {
let onContentReady = function () {
aContentWindow.removeEventListener("load", onContentReady, false);
this._populateChrome();
}.bind(this);
aContentWindow.addEventListener("load", onContentReady, false);
}
},
/**
* Retrieve the content document attached to this chrome.
*
* @return DOMDocument
*/
get contentDocument()
{
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.
*
* @return Array<StyleEditor>
*/
get editors()
{
let editors = [];
this._editors.forEach(function (aEditor) {
if (aEditor.styleSheetIndex >= 0) {
editors[aEditor.styleSheetIndex] = aEditor;
}
});
return editors;
},
/**
* 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
*
* onEditorAdded: Called when a stylesheet (therefore a StyleEditor
* instance) has been added to the UI.
* Arguments (StyleEditorChrome aChrome,
* StyleEditor aEditor)
* }
*
* All listener methods are optional.
*
* @param IStyleEditorChromeListener aListener
* @see removeChromeListener
*/
addChromeListener: function SEC_addChromeListener(aListener)
{
this._listeners.push(aListener);
},
/**
* Remove a listener for Chrome events from the current list of listeners.
*
* @param IStyleEditorChromeListener aListener
* @see addChromeListener
*/
removeChromeListener: function SEC_removeChromeListener(aListener)
{
let index = this._listeners.indexOf(aListener);
if (index != -1) {
this._listeners.splice(index, 1);
}
},
/**
* Trigger named handlers in StyleEditorChrome listeners.
*
* @param string aName
* Name of the event to trigger.
* @param Array aArgs
* Optional array of arguments to pass to the listener(s).
* @see addActionListener
*/
_triggerChromeListeners: function SE__triggerChromeListeners(aName, aArgs)
{
// insert the origin Chrome instance as first argument
if (!aArgs) {
aArgs = [this];
} else {
aArgs.unshift(this);
}
// trigger all listeners that have this named handler
for (let i = 0; i < this._listeners.length; ++i) {
let listener = this._listeners[i];
let handler = listener["on" + aName];
if (handler) {
handler.apply(listener, aArgs);
}
}
},
/**
* Set up the chrome UI. Install event listeners and so on.
*/
_setupChrome: function SEC__setupChrome()
{
// wire up UI elements
wire(this._view.rootElement, ".style-editor-newButton", function onNewButton() {
let editor = new StyleEditor(this.contentDocument);
this._editors.push(editor);
editor.addActionListener(this);
editor.load();
}.bind(this));
wire(this._view.rootElement, ".style-editor-importButton", function onImportButton() {
let editor = new StyleEditor(this.contentDocument);
this._editors.push(editor);
editor.addActionListener(this);
editor.importFromFile(this._mockImportFile || null, this._window);
}.bind(this));
},
/**
* Reset the chrome UI to an empty state.
*/
_resetChrome: function SEC__resetChrome()
{
this._editors.forEach(function (aEditor) {
aEditor.removeActionListener(this);
}.bind(this));
this._editors = [];
this._view.removeAll();
},
/**
* Populate the chrome UI according to the content document.
*
* @see StyleEditor._setupShadowStyleSheet
*/
_populateChrome: function SEC__populateChrome()
{
this._resetChrome();
this._document.title = _("chromeWindowTitle",
this.contentDocument.title || this.contentDocument.location.href);
let document = this.contentDocument;
for (let i = 0; i < document.styleSheets.length; ++i) {
let styleSheet = document.styleSheets[i];
let editor = new StyleEditor(document, styleSheet);
editor.addActionListener(this);
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
// there are many heavy stylesheets.
this._editors.forEach(function (aEditor) {
this._window.setTimeout(aEditor.load.bind(aEditor), 0);
}, this);
},
/**
* Disable all UI, effectively making editors read-only.
* This is automatically called when no content window is attached.
*
* @see contentWindow
*/
_disableChrome: function SEC__disableChrome()
{
let matches = this._root.querySelectorAll("button,input,select");
for (let i = 0; i < matches.length; ++i) {
matches[i].setAttribute("disabled", "disabled");
}
this.editors.forEach(function onEnterReadOnlyMode(aEditor) {
aEditor.readOnly = true;
});
this._view.rootElement.setAttribute("disabled", "disabled");
this._triggerChromeListeners("ContentDetach");
},
/**
* Retrieve the summary element for a given editor.
*
* @param StyleEditor aEditor
* @return DOMElement
* Item's summary element or null if not found.
* @see SplitView
*/
getSummaryElementForEditor: function SEC_getSummaryElementForEditor(aEditor)
{
return this._view.getSummaryElementByOrdinal(aEditor.styleSheetIndex);
},
/**
* Update split view summary of given StyleEditor instance.
*
* @param StyleEditor aEditor
* @param DOMElement aSummary
* Optional item's summary element to update. If none, item corresponding
* to passed aEditor is used.
*/
_updateSummaryForEditor: function SEC__updateSummaryForEditor(aEditor, aSummary)
{
let summary = aSummary || this.getSummaryElementForEditor(aEditor);
let ruleCount = aEditor.styleSheet.cssRules.length;
this._view.setItemClassName(summary, aEditor.flags);
text(summary, ".stylesheet-name", aEditor.getFriendlyName());
text(summary, ".stylesheet-title", aEditor.styleSheet.title || "");
text(summary, ".stylesheet-rule-count",
PluralForm.get(ruleCount, _("ruleCount.label")).replace("#1", ruleCount));
text(summary, ".stylesheet-error-message", aEditor.errorMessage);
},
/**
* IStyleEditorActionListener implementation
* @See StyleEditor.addActionListener.
*/
/**
* Called when source has been loaded and editor is ready for some action.
*
* @param StyleEditor aEditor
*/
onLoad: function SEAL_onLoad(aEditor)
{
let item = this._view.appendTemplatedItem(STYLE_EDITOR_TEMPLATE, {
data: {
editor: aEditor
},
disableAnimations: this._alwaysDisableAnimations,
ordinal: aEditor.styleSheetIndex,
onCreate: function ASV_onItemCreate(aSummary, aDetails, aData) {
let editor = aData.editor;
wire(aSummary, ".stylesheet-enabled", function onToggleEnabled(aEvent) {
aEvent.stopPropagation();
aEvent.target.blur();
editor.enableStyleSheet(editor.styleSheet.disabled);
});
wire(aSummary, ".stylesheet-saveButton", function onSaveButton(aEvent) {
aEvent.stopPropagation();
aEvent.target.blur();
editor.saveToFile(editor.savedFile);
});
this._updateSummaryForEditor(editor, aSummary);
aSummary.addEventListener("focus", function onSummaryFocus(aEvent) {
if (aEvent.target == aSummary) {
// autofocus the stylesheet name
aSummary.querySelector(".stylesheet-name").focus();
}
}, false);
// autofocus the first or new stylesheet
if (editor.styleSheetIndex == 0 ||
editor.hasFlag(StyleEditorFlags.NEW)) {
this._view.activeSummary = aSummary;
}
this._triggerChromeListeners("EditorAdded", [editor]);
}.bind(this),
onHide: function ASV_onItemShow(aSummary, aDetails, aData) {
aData.editor.onHide();
},
onShow: function ASV_onItemShow(aSummary, aDetails, aData) {
let editor = aData.editor;
if (!editor.inputElement) {
// attach editor to input element the first time it is shown
editor.inputElement = aDetails.querySelector(".stylesheet-editor-input");
}
editor.onShow();
}
});
},
/**
* Called when an editor flag changed.
*
* @param StyleEditor aEditor
* @param string aFlagName
* @see StyleEditor.flags
*/
onFlagChange: function SEAL_onFlagChange(aEditor, aFlagName)
{
this._updateSummaryForEditor(aEditor);
},
/**
* Called when when changes have been committed/applied to the live DOM
* stylesheet.
*
* @param StyleEditor aEditor
*/
onCommit: function SEAL_onCommit(aEditor)
{
this._updateSummaryForEditor(aEditor);
},
};

View File

@ -0,0 +1,196 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Style Editor code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Cedric Vivier <cedricv@neonux.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
"use strict";
const EXPORTED_SYMBOLS = [
"_",
"assert",
"attr",
"getCurrentBrowserTabContentWindow",
"log",
"text",
"wire"
];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
const PROPERTIES_URL = "chrome://browser/locale/devtools/styleeditor.properties";
const console = Services.console;
const gStringBundle = Services.strings.createBundle(PROPERTIES_URL);
/**
* Returns a localized string with the given key name from the string bundle.
*
* @param aName
* @param ...rest
* Optional arguments to format in the string.
* @return string
*/
function _(aName)
{
if (arguments.length == 1) {
return gStringBundle.GetStringFromName(aName);
}
let rest = Array.prototype.slice.call(arguments, 1);
return gStringBundle.formatStringFromName(aName, rest, rest.length);
}
/**
* Assert an expression is true or throw if false.
*
* @param aExpression
* @param aMessage
* Optional message.
* @return aExpression
*/
function assert(aExpression, aMessage)
{
if (!!!(aExpression)) {
let msg = aMessage ? "ASSERTION FAILURE:" + aMessage : "ASSERTION FAILURE";
log(msg);
throw new Error(msg);
}
return aExpression;
}
/**
* Retrieve or set the text content of an element.
*
* @param DOMElement aRoot
* The element to use for querySelector.
* @param string aSelector
* Selector string for the element to get/set the text content.
* @param string aText
* Optional text to set.
* @return string
* Text content of matching element or null if there were no element
* matching aSelector.
*/
function text(aRoot, aSelector, aText)
{
let element = aRoot.querySelector(aSelector);
if (!element) {
return null;
}
if (aText === undefined) {
return element.textContent;
}
element.textContent = aText;
return aText;
}
/**
* Iterates _own_ properties of an object.
*
* @param aObject
* The object to iterate.
* @param function aCallback(aKey, aValue)
*/
function forEach(aObject, aCallback)
{
for (let key in aObject) {
if (aObject.hasOwnProperty(key)) {
aCallback(key, aObject[key]);
}
}
}
/**
* Log a message to the console.
*
* @param ...rest
* One or multiple arguments to log.
* If multiple arguments are given, they will be joined by " " in the log.
*/
function log()
{
console.logStringMessage(Array.prototype.slice.call(arguments).join(" "));
}
/**
* Wire up element(s) matching selector with attributes, event listeners, etc.
*
* @param DOMElement aRoot
* The element to use for querySelectorAll.
* Can be null if aSelector is a DOMElement.
* @param string|DOMElement aSelectorOrElement
* Selector string or DOMElement for the element(s) to wire up.
* @param object aDescriptor
* An object describing how to wire matching selector, supported properties
* are "events", "attributes" and "userData" taking objects themselves.
* Each key of properties above represents the name of the event, attribute
* or userData, with the value being a function used as an event handler,
* string to use as attribute value, or object to use as named userData
* respectively.
* If aDescriptor is a function, the argument is equivalent to :
* {events: {'click': aDescriptor}}
*/
function wire(aRoot, aSelectorOrElement, aDescriptor)
{
let matches;
if (typeof(aSelectorOrElement) == "string") { // selector
matches = aRoot.querySelectorAll(aSelectorOrElement);
if (!matches.length) {
return;
}
} else {
matches = [aSelectorOrElement]; // element
}
if (typeof(aDescriptor) == "function") {
aDescriptor = {events: {click: aDescriptor}};
}
for (let i = 0; i < matches.length; ++i) {
let element = matches[i];
forEach(aDescriptor.events, function (aName, aHandler) {
element.addEventListener(aName, aHandler, false);
});
forEach(aDescriptor.attributes, element.setAttribute);
forEach(aDescriptor.userData, element.setUserData);
}
}

View File

@ -0,0 +1,96 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Style Editor code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Cedric Vivier <cedricv@neonux.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
box,
.splitview-nav {
-moz-box-flex: 1;
-moz-box-orient: vertical;
}
.splitview-main {
-moz-box-flex: 0;
}
.splitview-controller {
min-height: 8em;
max-height: 14em;
}
.splitview-nav {
display: -moz-box;
}
/* only the active details pane is shown */
.splitview-side-details > * {
display: none;
}
.splitview-side-details > .splitview-active {
display: -moz-box;
}
/* this is to keep in sync with SplitView.jsm's LANDSCAPE_MEDIA_QUERY */
@media (min-aspect-ratio: 5/3) {
.splitview-root {
-moz-box-orient: horizontal;
}
.splitview-controller {
-moz-box-flex: 0;
max-height: none;
}
.splitview-details {
display: none;
}
.splitview-details.splitview-active {
display: -moz-box;
}
}
/* filtered items are hidden */
ol.splitview-nav > li.splitview-filtered {
display: none;
}
/* "empty list" and "all filtered" placeholders are hidden */
.splitview-nav:empty,
.splitview-nav.splitview-all-filtered,
.splitview-nav + .splitview-nav.placeholder {
display: none;
}
.splitview-nav.splitview-all-filtered ~ .splitview-nav.placeholder.all-filtered,
.splitview-nav:empty ~ .splitview-nav.placeholder.empty {
display: -moz-box;
}

View File

@ -0,0 +1,43 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Style Editor code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Cedric Vivier <cedricv@neonux.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
.splitview-nav > li hgroup > .stylesheet-error-message {
display: none;
}
.splitview-nav > li.error hgroup > .stylesheet-error-message {
display: block;
}

View File

@ -0,0 +1,130 @@
<?xml version="1.0"?>
<!-- ***** BEGIN LICENSE BLOCK *****
- Version: MPL 1.1/GPL 2.0/LGPL 2.1
-
- The contents of this file are subject to the Mozilla Public License Version
- 1.1 (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
- http://www.mozilla.org/MPL/
-
- Software distributed under the License is distributed on an "AS IS" basis,
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- for the specific language governing rights and limitations under the
- License.
-
- The Original Code is Style Editor code.
-
- The Initial Developer of the Original Code is The Mozilla Foundation.
- Portions created by the Initial Developer are Copyright (C) 2011
- the Initial Developer. All Rights Reserved.
-
- Contributor(s):
- Cedric Vivier <cedricv@neonux.com> (original author)
-
- Alternatively, the contents of this file may be used under the terms of
- either the GNU General Public License Version 2 or later (the "GPL"), or
- the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- in which case the provisions of the GPL or the LGPL are applicable instead
- of those above. If you wish to allow use of your version of this file only
- under the terms of either the GPL or the LGPL, and not to allow others to
- use your version of this file under the terms of the MPL, indicate your
- decision by deleting the provisions above and replace them with the notice
- and other provisions required by the GPL or the LGPL. If you do not delete
- the provisions above, a recipient may use your version of this file under
- the terms of any one of the MPL, the GPL or the LGPL.
-
- ***** END LICENSE BLOCK ***** -->
<!DOCTYPE window [
<!ENTITY % styleEditorDTD SYSTEM "chrome://browser/locale/devtools/styleeditor.dtd" >
%styleEditorDTD;
]>
<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/splitview.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/devtools/splitview.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/styleeditor.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/devtools/styleeditor.css" type="text/css"?>
<xul:window xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns="http://www.w3.org/1999/xhtml"
id="style-editor-chrome-window"
title="&window.title;"
windowtype="Tools:StyleEditor"
width="800" height="280"
persist="screenX screenY width height sizemode">
<xul:script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
<xul:box id="style-editor-chrome" class="splitview-root">
<xul:box class="splitview-side-details"></xul:box>
<xul:box class="splitview-controller">
<xul:box class="splitview-main">
<xul:box class="toolbar">
<xul:button class="style-editor-newButton"
accesskey="&newButton.accesskey;"
tooltiptext="&newButton.tooltip;"
label="&newButton.label;"></xul:button>
<xul:button class="style-editor-importButton"
accesskey="&importButton.accesskey;"
tooltiptext="&importButton.tooltip;"
label="&importButton.label;"></xul:button>
<xul:box class="spacer" flex="1"></xul:box>
<xul:textbox class="splitview-filter"
type="search"
tooltiptext="&searchInput.tooltip;"
placeholder="&searchInput.placeholder;"/>
</xul:box>
</xul:box>
<xul:box class="splitview-nav-container">
<ol class="splitview-nav" tabindex="0"></ol>
<div class="splitview-nav placeholder empty">
<p><strong>&noStyleSheet.label;</strong></p>
<p>&noStyleSheet-tip-start.label;
<a href="#"
class="style-editor-newButton">&noStyleSheet-tip-action.label;</a>
&noStyleSheet-tip-end.label;</p>
</div>
<div class="splitview-nav placeholder all-filtered">
<p><strong>&searchNoResults.label;</strong></p>
<p>
<a href="#"
class="splitview-filter-clearButton">&searchClearButton.label;</a>
</p>
</div>
</xul:box> <!-- .splitview-nav-container -->
</xul:box> <!-- .splitview-controller -->
<div id="splitview-templates" hidden="true">
<li id="splitview-tpl-summary-stylesheet" tabindex="0">
<a class="stylesheet-enabled" tabindex="0" href="#"
title="&visibilityToggle.tooltip;"
accesskey="&saveButton.accesskey;"></a>
<hgroup class="stylesheet-info">
<h1><a class="stylesheet-name" href="#"></a></h1>
<h2 class="stylesheet-title"></h2>
<h3 class="stylesheet-error-message"></h3>
</hgroup>
<div class="stylesheet-more">
<hgroup class="stylesheet-stats">
<h3 class="stylesheet-rule-count"></h3>
</hgroup>
<hgroup class="stylesheet-actions">
<h1><a class="stylesheet-saveButton" href="#"
title="&saveButton.tooltip;"
accesskey="&saveButton.accesskey;">&saveButton.label;</a></h1>
</hgroup>
</div>
</li>
<xul:box id="splitview-tpl-details-stylesheet" class="splitview-details">
<div class="stylesheet-editor-input textbox"
data-placeholder="&editorTextbox.placeholder;"></div>
</xul:box>
</div> <!-- #splitview-templates -->
</xul:box> <!-- .splitview-root -->
<xul:script type="application/javascript"><![CDATA[
Components.utils.import("resource:///modules/devtools/StyleEditorChrome.jsm");
let chromeRoot = document.getElementById("style-editor-chrome");
let contentWindow = window.arguments[0];
let chrome = new StyleEditorChrome(chromeRoot, contentWindow);
window.styleEditorChrome = chrome;
]]></xul:script>
</xul:window>

View File

@ -0,0 +1,72 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Style Editor code.
#
# The Initial Developer of the Original Code is Mozilla Foundation.
#
# Portions created by the Initial Developer are Copyright (C) 2007
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Cedric Vivier <cedricv@neonux.com> (original author)
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = browser/devtools/styleeditor/test
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_BROWSER_TEST_FILES = \
browser_styleeditor_enabled.js \
browser_styleeditor_import.js \
browser_styleeditor_init.js \
browser_styleeditor_loading.js \
browser_styleeditor_new.js \
browser_styleeditor_pretty.js \
browser_styleeditor_readonly.js \
browser_styleeditor_reopen.js \
browser_styleeditor_sv_filter.js \
browser_styleeditor_sv_keynav.js \
browser_styleeditor_sv_resize.js \
four.html \
head.js \
media.html \
media-small.css \
minified.html \
simple.css \
simple.css.gz \
simple.css.gz^headers^ \
simple.gz.html \
simple.html \
$(NULL)
libs:: $(_BROWSER_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)

View File

@ -0,0 +1,101 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// https rather than chrome to improve coverage
const TESTCASE_URI = TEST_BASE_HTTPS + "simple.html";
function test()
{
waitForExplicitFinish();
let count = 0;
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
aChrome.addChromeListener({
onEditorAdded: function (aChrome, aEditor) {
count++;
if (count == 2) {
// we test against first stylesheet after all are ready
let editor = aChrome.editors[0];
if (!editor.sourceEditor) {
editor.addActionListener({
onAttach: function (aEditor) {
run(aChrome, aEditor);
}
});
} else {
run(aChrome, editor);
}
}
}
});
});
content.location = TESTCASE_URI;
}
function run(aChrome, aEditor)
{
testEnabledToggle(aChrome, aEditor);
}
function testEnabledToggle(aChrome, aEditor)
{
is(aEditor, aChrome.editors[0],
"stylesheet with index 0 is the first stylesheet listed in the UI");
let firstStyleSheetEditor = aEditor;
let firstStyleSheetUI = aChrome.getSummaryElementForEditor(aEditor);
let enabledToggle = firstStyleSheetUI.querySelector(".stylesheet-enabled");
is(firstStyleSheetEditor.contentDocument.styleSheets[0].disabled, false,
"first stylesheet is initially enabled");
is(firstStyleSheetEditor.hasFlag("disabled"), false,
"first stylesheet is initially enabled, it does not have DISABLED flag");
is(firstStyleSheetUI.classList.contains("disabled"), false,
"first stylesheet is initially enabled, UI does not have DISABLED class");
let disabledToggleCount = 0;
firstStyleSheetEditor.addActionListener({
onFlagChange: function (aEditor, aFlagName) {
if (aFlagName != "disabled") {
return;
}
disabledToggleCount++;
if (disabledToggleCount == 1) {
is(firstStyleSheetEditor, aEditor,
"FlagChange handler triggered for DISABLED flag on the first editor");
is(firstStyleSheetEditor.styleSheet.disabled, true,
"first stylesheet is now disabled");
is(firstStyleSheetEditor.hasFlag("disabled"), true,
"first stylesheet is now disabled, it has DISABLED flag");
is(firstStyleSheetUI.classList.contains("disabled"), true,
"first stylesheet is now disabled, UI has DISABLED class");
// now toggle it back to enabled
waitForFocus(function () {
EventUtils.synthesizeMouseAtCenter(enabledToggle, {}, gChromeWindow);
}, gChromeWindow);
return;
}
// disabledToggleCount == 2
is(firstStyleSheetEditor, aEditor,
"FlagChange handler triggered for DISABLED flag on the first editor (2)");
is(firstStyleSheetEditor.styleSheet.disabled, false,
"first stylesheet is now enabled again");
is(firstStyleSheetEditor.hasFlag("disabled"), false,
"first stylesheet is now enabled again, it does not have DISABLED flag");
is(firstStyleSheetUI.classList.contains("disabled"), false,
"first stylesheet is now enabled again, UI does not have DISABLED class");
finish();
}
});
waitForFocus(function () {
EventUtils.synthesizeMouseAtCenter(enabledToggle, {}, gChromeWindow);
}, gChromeWindow);
}

View File

@ -0,0 +1,87 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// http rather than chrome to improve coverage
const TESTCASE_URI = TEST_BASE_HTTP + "simple.html";
Components.utils.import("resource://gre/modules/FileUtils.jsm");
const FILENAME = "styleeditor-import-test.css";
const SOURCE = "body{background:red;}";
function test()
{
waitForExplicitFinish();
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
aChrome.addChromeListener({
onContentAttach: run,
onEditorAdded: testEditorAdded
});
if (aChrome.isContentAttached) {
run(aChrome);
}
});
content.location = TESTCASE_URI;
}
function run(aChrome)
{
is(aChrome.editors.length, 2,
"there is 2 stylesheets initially");
}
function testImport(aChrome, aEditor)
{
// create file to import first
let file = FileUtils.getFile("ProfD", [FILENAME]);
let ostream = FileUtils.openSafeFileOutputStream(file);
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
let istream = converter.convertToInputStream(SOURCE);
NetUtil.asyncCopy(istream, ostream, function (status) {
FileUtils.closeSafeFileOutputStream(ostream);
// click the import button now that the file to import is ready
aChrome._mockImportFile = file;
waitForFocus(function () {
let document = gChromeWindow.document
let importButton = document.querySelector(".style-editor-importButton");
EventUtils.synthesizeMouseAtCenter(importButton, {}, gChromeWindow);
}, gChromeWindow);
});
}
let gAddedCount = 0;
function testEditorAdded(aChrome, aEditor)
{
if (++gAddedCount == 2) {
// test import after the 2 initial stylesheets have been loaded
if (!aChrome.editors[0].sourceEditor) {
aChrome.editors[0].addActionListener({
onAttach: function () {
testImport(aChrome);
}
});
} else {
testImport(aChrome);
}
}
if (!aEditor.hasFlag("imported")) {
return;
}
ok(!aEditor.hasFlag("inline"),
"imported stylesheet does not have INLINE flag");
ok(aEditor.savedFile,
"imported stylesheet will be saved directly into the same file");
is(aEditor.getFriendlyName(), FILENAME,
"imported stylesheet has the same name as the filename");
finish();
}

View File

@ -0,0 +1,126 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TESTCASE_URI = TEST_BASE + "simple.html";
function test()
{
waitForExplicitFinish();
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
aChrome.addChromeListener({
onContentAttach: run,
onEditorAdded: testEditorAdded
});
if (aChrome.isContentAttached) {
run(aChrome);
}
});
content.location = TESTCASE_URI;
}
let gContentAttachHandled = false;
function run(aChrome)
{
gContentAttachHandled = true;
is(aChrome.contentWindow.document.readyState, "complete",
"content document is complete");
let SEC = gChromeWindow.styleEditorChrome;
is(SEC, aChrome, "StyleEditorChrome object exists as new window property");
ok(gChromeWindow.document.title.indexOf("simple testcase") >= 0,
"the Style Editor window title contains the document's title");
// check editors are instantiated
is(SEC.editors.length, 2,
"there is two StyleEditor instances managed");
ok(SEC.editors[0].styleSheetIndex < SEC.editors[1].styleSheetIndex,
"editors are ordered by styleSheetIndex");
// check StyleEditorChrome is a singleton wrt to the same DOMWindow
let chromeWindow = StyleEditor.openChrome();
is(chromeWindow, gChromeWindow,
"attempt to edit the same document returns the same Style Editor window");
}
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);
}
if (aEditor.styleSheetIndex == 1) {
gEditorAddedCount++;
testSecondStyleSheetEditor(aChrome, aEditor);
}
if (gEditorAddedCount == 2) {
finish();
}
}
function testFirstStyleSheetEditor(aChrome, aEditor)
{
//testing TESTCASE's simple.css stylesheet
is(aEditor.styleSheetIndex, 0,
"first stylesheet is at index 0");
is(aEditor, aChrome.editors[0],
"first stylesheet corresponds to StyleEditorChrome.editors[0]");
ok(!aEditor.hasFlag("inline"),
"first stylesheet does not have INLINE flag");
let summary = aChrome.getSummaryElementForEditor(aEditor);
ok(!summary.classList.contains("inline"),
"first stylesheet UI does not have INLINE class");
let name = summary.querySelector(".stylesheet-name").textContent;
is(name, "simple.css",
"first stylesheet's name is `simple.css`");
let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
is(parseInt(ruleCount), 1,
"first stylesheet UI shows rule count as 1");
ok(summary.classList.contains("splitview-active"),
"first stylesheet UI is focused/active");
}
function testSecondStyleSheetEditor(aChrome, aEditor)
{
//testing TESTCASE's inline stylesheet
is(aEditor.styleSheetIndex, 1,
"second stylesheet is at index 1");
is(aEditor, aChrome.editors[1],
"second stylesheet corresponds to StyleEditorChrome.editors[1]");
ok(aEditor.hasFlag("inline"),
"second stylesheet has INLINE flag");
let summary = aChrome.getSummaryElementForEditor(aEditor);
ok(summary.classList.contains("inline"),
"second stylesheet UI has INLINE class");
let name = summary.querySelector(".stylesheet-name").textContent;
ok(/^<.*>$/.test(name),
"second stylesheet's name is surrounded by `<>`");
let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
is(parseInt(ruleCount), 3,
"second stylesheet UI shows rule count as 3");
ok(!summary.classList.contains("splitview-active"),
"second stylesheet UI is NOT focused/active");
}

View File

@ -0,0 +1,39 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TESTCASE_URI = TEST_BASE + "simple.html";
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
// 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
launchStyleEditorChrome(function (aChrome) {
isnot(gBrowser.selectedBrowser.contentWindow.document.readyState, "complete",
"content document is still loading");
if (!aChrome.isContentAttached) {
aChrome.addChromeListener({
onContentAttach: run
});
} else {
run(aChrome);
}
});
content.location = TESTCASE_URI;
}
function run(aChrome)
{
is(aChrome.contentWindow.document.readyState, "complete",
"content document is complete");
finish();
}

View File

@ -0,0 +1,117 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TESTCASE_URI = TEST_BASE + "simple.html";
function test()
{
waitForExplicitFinish();
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
aChrome.addChromeListener({
onContentAttach: run,
onEditorAdded: testEditorAdded
});
if (aChrome.isContentAttached) {
run(aChrome);
}
});
content.location = TESTCASE_URI;
}
function run(aChrome)
{
is(aChrome.editors.length, 2,
"there is 2 stylesheets initially");
}
let gAddedCount = 0; // to add new stylesheet after the 2 initial stylesheets
let gNewEditor; // to make sure only one new stylesheet got created
let gCommitCount = 0; // to make sure only one Commit event is triggered
function testEditorAdded(aChrome, aEditor)
{
gAddedCount++;
if (gAddedCount == 2) {
waitForFocus(function () { // create a new style sheet
let newButton = gChromeWindow.document.querySelector(".style-editor-newButton");
EventUtils.synthesizeMouseAtCenter(newButton, {}, gChromeWindow);
}, gChromeWindow);
}
if (gAddedCount != 3) {
return;
}
ok(!gNewEditor, "creating a new stylesheet triggers one EditorAdded event");
gNewEditor = aEditor; // above test will fail if we get a duplicate event
is(aChrome.editors.length, 3,
"creating a new stylesheet added a new StyleEditor instance");
let listener = {
onAttach: function (aEditor) {
waitForFocus(function () {
ok(aEditor.isLoaded,
"new editor is loaded when attached");
ok(aEditor.hasFlag("new"),
"new editor has NEW flag");
ok(!aEditor.hasFlag("unsaved"),
"new editor does not have UNSAVED flag");
ok(aEditor.inputElement,
"new editor has an input element attached");
ok(aEditor.sourceEditor.hasFocus(),
"new editor has focus");
let summary = aChrome.getSummaryElementForEditor(aEditor);
let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
is(parseInt(ruleCount), 0,
"new editor initially shows 0 rules");
let computedStyle = content.getComputedStyle(content.document.body, null);
is(computedStyle.backgroundColor, "rgb(255, 255, 255)",
"content's background color is initially white");
for each (let c in "body{background-color:red;}") {
EventUtils.synthesizeKey(c, {}, gChromeWindow);
}
}, gChromeWindow) ;
},
onCommit: function (aEditor) {
gCommitCount++;
ok(aEditor.hasFlag("new"),
"new editor still has NEW flag");
ok(aEditor.hasFlag("unsaved"),
"new editor has UNSAVED flag after modification");
let summary = aChrome.getSummaryElementForEditor(aEditor);
let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
is(parseInt(ruleCount), 1,
"new editor shows 1 rule after modification");
let computedStyle = content.getComputedStyle(content.document.body, null);
is(computedStyle.backgroundColor, "rgb(255, 0, 0)",
"content's background color has been updated to red");
executeSoon(function () {
is(gCommitCount, 1, "received only one Commit event (throttle)");
aEditor.removeActionListener(listener);
gNewEditor = null;
finish();
});
}
};
aEditor.addActionListener(listener);
if (aEditor.sourceEditor) {
listener.onAttach(aEditor);
}
}

View File

@ -0,0 +1,56 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TESTCASE_URI = TEST_BASE + "minified.html";
function test()
{
waitForExplicitFinish();
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
aChrome.addChromeListener({
onEditorAdded: function (aChrome, aEditor) {
if (aEditor.sourceEditor) {
run(aEditor); // already attached to input element
} else {
aEditor.addActionListener({
onAttach: run
});
}
}
});
});
content.location = TESTCASE_URI;
}
let editorTestedCount = 0;
function run(aEditor)
{
if (aEditor.styleSheetIndex == 0) {
let prettifiedSource = "body\{\r?\n\tbackground\:white;\r?\n\}\r?\n\r?\ndiv\{\r?\n\tfont\-size\:4em;\r?\n\tcolor\:red\r?\n\}\r?\n";
let prettifiedSourceRE = new RegExp(prettifiedSource);
ok(prettifiedSourceRE.test(aEditor.sourceEditor.getText()),
"minified source has been prettified automatically");
editorTestedCount++;
let chrome = gChromeWindow.styleEditorChrome;
let summary = chrome.getSummaryElementForEditor(chrome.editors[1]);
EventUtils.synthesizeMouseAtCenter(summary, {}, gChromeWindow);
}
if (aEditor.styleSheetIndex == 1) {
let originalSource = "body \{ background\: red; \}\r?\ndiv \{\r?\nfont\-size\: 5em;\r?\ncolor\: red\r?\n\}";
let originalSourceRE = new RegExp(originalSource);
ok(originalSourceRE.test(aEditor.sourceEditor.getText()),
"non-minified source has been left untouched");
editorTestedCount++;
}
if (editorTestedCount == 2) {
finish();
}
}

View File

@ -0,0 +1,74 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TESTCASE_URI = TEST_BASE + "simple.html";
let gEditorAddedCount = 0;
let gEditorReadOnlyCount = 0;
let gChromeListener = {
onEditorAdded: function (aChrome, aEditor) {
gEditorAddedCount++;
if (aEditor.readOnly) {
gEditorReadOnlyCount++;
}
if (gEditorAddedCount == aChrome.editors.length) {
// continue testing after all editors are ready
is(gEditorReadOnlyCount, 0,
"all editors are NOT read-only initially");
// all editors have been loaded, queue closing the content tab
executeSoon(function () {
gBrowser.removeCurrentTab();
});
}
},
onContentDetach: function (aChrome) {
// check that the UI has switched to read-only
run(aChrome);
}
};
function test()
{
waitForExplicitFinish();
gBrowser.addTab(); // because we'll close the next one
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
aChrome.addChromeListener(gChromeListener);
});
content.location = TESTCASE_URI;
}
function run(aChrome)
{
let document = gChromeWindow.document;
let disabledCount;
let elements;
disabledCount = 0;
elements = document.querySelectorAll("button,input,select");
for (let i = 0; i < elements.length; ++i) {
if (elements[i].hasAttribute("disabled")) {
disabledCount++;
}
}
ok(elements.length && disabledCount == elements.length,
"all buttons, input and select elements are disabled");
disabledCount = 0;
aChrome.editors.forEach(function (aEditor) {
if (aEditor.readOnly) {
disabledCount++;
}
});
ok(aChrome.editors.length && disabledCount == aChrome.editors.length,
"all editors are read-only");
aChrome.removeChromeListener(gChromeListener);
finish();
}

View File

@ -0,0 +1,155 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// http rather than chrome to improve coverage
const TESTCASE_URI = TEST_BASE_HTTP + "simple.gz.html";
const Cc = Components.classes;
const Ci = Components.interfaces;
Components.utils.import("resource://gre/modules/FileUtils.jsm");
function test()
{
waitForExplicitFinish();
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
aChrome.addChromeListener({
onEditorAdded: function (aChrome, aEditor) {
if (aEditor.styleSheetIndex != 0) {
return; // we want to test against the first stylesheet
}
if (aEditor.sourceEditor) {
run(aEditor); // already attached to input element
} else {
aEditor.addActionListener({
onAttach: run
});
}
}
});
gChromeWindow.addEventListener("unload", function onClose() {
gChromeWindow.removeEventListener("unload", onClose, true);
gChromeWindow = null;
executeSoon(function () {
waitForFocus(function () {
// wait that browser has focus again
// open StyleEditorChrome again (a new one since we closed the previous one)
launchStyleEditorChrome(function (aChrome) {
is(gChromeWindow.document.documentElement.hasAttribute("data-marker"),
false,
"opened a completely new StyleEditorChrome window");
aChrome.addChromeListener({
onEditorAdded: function (aChrome, aEditor) {
if (aEditor.styleSheetIndex != 0) {
return; // we want to test against the first stylesheet
}
if (aEditor.sourceEditor) {
testNewChrome(aEditor); // already attached to input element
} else {
aEditor.addActionListener({
onAttach: testNewChrome
});
}
}
});
});
});
});
}, true);
});
content.location = TESTCASE_URI;
}
let gFilename;
function run(aEditor)
{
gFilename = FileUtils.getFile("ProfD", ["styleeditor-test.css"])
aEditor.saveToFile(gFilename, function (aFile) {
ok(aFile, "file got saved successfully");
aEditor.addActionListener({
onFlagChange: function (aEditor, aFlag) {
if (aFlag != "unsaved") {
return;
}
ok(aEditor.hasFlag("unsaved"),
"first stylesheet has UNSAVED flag after making a change");
// marker used to check it does not exist when we reopen
// ie. the window we opened is indeed a new one
gChromeWindow.document.documentElement.setAttribute("data-marker", "true");
gChromeWindow.close();
}
});
waitForFocus(function () {
// insert char so that this stylesheet has the UNSAVED flag
EventUtils.synthesizeKey("x", {}, gChromeWindow);
}, gChromeWindow);
});
}
function testNewChrome(aEditor)
{
ok(aEditor.savedFile,
"first stylesheet editor will save directly into the same file");
is(aEditor.getFriendlyName(), gFilename.leafName,
"first stylesheet still has the filename as it was saved");
gFilename = null;
ok(aEditor.hasFlag("unsaved"),
"first stylesheet still has UNSAVED flag at reopening");
ok(!aEditor.hasFlag("inline"),
"first stylesheet does not have INLINE flag");
ok(!aEditor.hasFlag("error"),
"editor does not have error flag initially");
let hadError = false;
let onSaveCallback = function (aFile) {
aEditor.addActionListener({
onFlagChange: function (aEditor, aFlag) {
if (!hadError && aFlag == "error") {
ok(aEditor.hasFlag("error"),
"editor has ERROR flag after attempting to save with invalid path");
hadError = true;
// save using source editor key binding (previous successful path)
waitForFocus(function () {
EventUtils.synthesizeKey("S", {accelKey: true}, gChromeWindow);
}, gChromeWindow);
return;
}
if (hadError && aFlag == "unsaved") {
executeSoon(function () {
ok(!aEditor.hasFlag("unsaved"),
"first stylesheet has no UNSAVED flag after successful save");
ok(!aEditor.hasFlag("error"),
"ERROR flag has been removed since last operation succeeded");
finish();
});
}
}
});
}
let os = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
if (os == "WINNT") {
aEditor.saveToFile("C:\\I_DO_NOT_EXIST_42\\bogus.css", onSaveCallback);
} else {
aEditor.saveToFile("/I_DO_NOT_EXIST_42/bogos.css", onSaveCallback);
}
}

View File

@ -0,0 +1,101 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TESTCASE_URI = TEST_BASE + "simple.html";
function test()
{
waitForExplicitFinish();
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
aChrome.addChromeListener({
onContentAttach: run
});
if (aChrome.isContentAttached) {
run(aChrome);
}
});
content.location = TESTCASE_URI;
}
function getFilteredItemsCount(nav)
{
let matches = nav.querySelectorAll("*.splitview-filtered");
return matches ? matches.length : 0;
}
function run(aChrome)
{
aChrome.editors[0].addActionListener({onAttach: onFirstEditorAttach});
aChrome.editors[1].addActionListener({onAttach: onSecondEditorAttach});
}
function onFirstEditorAttach(aEditor)
{
let filter = gChromeWindow.document.querySelector(".splitview-filter");
// force the command event on input since it is not possible to disable
// the search textbox's timeout.
let forceCommandEvent = function forceCommandEvent() {
let evt = gChromeWindow.document.createEvent("XULCommandEvent");
evt.initCommandEvent("command", true, true, gChromeWindow, 0, false, false,
false, false, null);
filter.dispatchEvent(evt);
}
filter.addEventListener("input", forceCommandEvent, false);
let nav = gChromeWindow.document.querySelector(".splitview-nav");
nav.focus();
is(getFilteredItemsCount(nav), 0,
"there is 0 filtered item initially");
waitForFocus(function () {
// Search [s] (type-on-search since we focused nav above - not filter directly)
EventUtils.synthesizeKey("s", {}, gChromeWindow);
// the search space is "simple.css" and "inline stylesheet #1" (2 sheets)
is(getFilteredItemsCount(nav), 0,
"there is 0 filtered item if searching for 's'");
EventUtils.synthesizeKey("i", {}, gChromeWindow); // Search [si]
is(getFilteredItemsCount(nav), 1, // inline stylesheet is filtered
"there is 1 filtered item if searching for 's'");
// use uppercase to check that filtering is case-insensitive
EventUtils.synthesizeKey("X", {}, gChromeWindow); // Search [siX]
is(getFilteredItemsCount(nav), 2,
"there is 2 filtered items if searching for 's'"); // no match
// clear the search
EventUtils.synthesizeKey("VK_ESCAPE", {}, gChromeWindow);
is(filter.value, "",
"filter is back to empty");
is(getFilteredItemsCount(nav), 0,
"there is 0 filtered item when filter is empty again");
for each (let c in "inline") {
EventUtils.synthesizeKey(c, {}, gChromeWindow);
}
is(getFilteredItemsCount(nav), 1, // simple.css is filtered
"there is 1 filtered item if searching for 'inline'");
// auto-select the only result (enter the editor)
EventUtils.synthesizeKey("VK_ENTER", {}, gChromeWindow);
filter.removeEventListener("input", forceCommandEvent, false);
}, gChromeWindow);
}
function onSecondEditorAttach(aEditor)
{
ok(aEditor.sourceEditor.hasFocus(),
"second editor has been selected and focused automatically.");
finish();
}

View File

@ -0,0 +1,80 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TESTCASE_URI = TEST_BASE + "four.html";
function test()
{
waitForExplicitFinish();
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
aChrome.addChromeListener({
onContentAttach: run
});
if (aChrome.isContentAttached) {
run(aChrome);
}
});
content.location = TESTCASE_URI;
}
let gChrome;
function run(aChrome)
{
gChrome = aChrome;
aChrome.editors[0].addActionListener({onAttach: onEditor0Attach});
aChrome.editors[2].addActionListener({onAttach: onEditor2Attach});
}
function getStylesheetNameLinkFor(aEditor)
{
return gChrome.getSummaryElementForEditor(aEditor).querySelector(".stylesheet-name");
}
function onEditor0Attach(aEditor)
{
waitForFocus(function () {
let summary = gChrome.getSummaryElementForEditor(aEditor);
EventUtils.synthesizeMouseAtCenter(summary, {}, gChromeWindow);
let item = getStylesheetNameLinkFor(gChrome.editors[0]);
is(gChromeWindow.document.activeElement, item,
"editor 0 item is the active element");
EventUtils.synthesizeKey("VK_DOWN", {}, gChromeWindow);
item = getStylesheetNameLinkFor(gChrome.editors[1]);
is(gChromeWindow.document.activeElement, item,
"editor 1 item is the active element");
EventUtils.synthesizeKey("VK_HOME", {}, gChromeWindow);
item = getStylesheetNameLinkFor(gChrome.editors[0]);
is(gChromeWindow.document.activeElement, item,
"fist editor item is the active element");
EventUtils.synthesizeKey("VK_END", {}, gChromeWindow);
item = getStylesheetNameLinkFor(gChrome.editors[3]);
is(gChromeWindow.document.activeElement, item,
"last editor item is the active element");
EventUtils.synthesizeKey("VK_UP", {}, gChromeWindow);
item = getStylesheetNameLinkFor(gChrome.editors[2]);
is(gChromeWindow.document.activeElement, item,
"editor 2 item is the active element");
EventUtils.synthesizeKey("VK_RETURN", {}, gChromeWindow);
// this will attach and give focus editor 2
}, gChromeWindow);
}
function onEditor2Attach(aEditor)
{
ok(aEditor.sourceEditor.hasFocus(),
"editor 2 has focus");
gChrome = null;
finish();
}

View File

@ -0,0 +1,63 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TESTCASE_URI = TEST_BASE + "simple.html";
let gOriginalWidth; // these are set by run() when gChromeWindow is ready
let gOriginalHeight;
function test()
{
waitForExplicitFinish();
addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
if (aChrome.isContentAttached) {
run(aChrome);
} else {
aChrome.addChromeListener({
onContentAttach: run
});
}
});
content.location = TESTCASE_URI;
}
function run(aChrome)
{
is(aChrome.editors.length, 2,
"there is 2 stylesheets initially");
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 () {
gOriginalWidth = gChromeWindow.outerWidth;
gOriginalHeight = gChromeWindow.outerHeight;
gChromeWindow.resizeTo(120, 480);
executeSoon(function () {
is(aEditor.sourceEditor, originalSourceEditor,
"the editor still references the same SourceEditor instance");
is(aEditor.sourceEditor.getCaretOffset(), 4,
"the caret position has been preserved");
// queue a resize to original aspect ratio
waitForFocus(function () {
gChromeWindow.resizeTo(gOriginalWidth, gOriginalHeight);
executeSoon(function () {
finish();
});
}, gChromeWindow);
});
}, gChromeWindow);
});
}
});
}

View File

@ -0,0 +1,25 @@
<!doctype html>
<html>
<head>
<title>four stylesheets</title>
<link rel="stylesheet" type="text/css" media="scren" href="simple.css"/>
<style type="text/css">
div {
font-size: 2em;
}
</style>
<style type="text/css">
span {
font-size: 3em;
}
</style>
<style type="text/css">
p {
font-size: 4em;
}
</style>
</head>
<body>
<div>four <span>stylesheets</span></div>
</body>
</html>

View File

@ -0,0 +1,46 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TEST_BASE = "chrome://mochitests/content/browser/browser/devtools/styleeditor/test/";
const TEST_BASE_HTTP = "http://example.com/browser/browser/devtools/styleeditor/test/";
const TEST_BASE_HTTPS = "https://example.com/browser/browser/devtools/styleeditor/test/";
let gChromeWindow; //StyleEditorChrome window
function cleanup()
{
if (gChromeWindow) {
gChromeWindow.close();
gChromeWindow = null;
}
while (gBrowser.tabs.length > 1) {
gBrowser.removeCurrentTab();
}
}
function launchStyleEditorChrome(aCallback)
{
gChromeWindow = StyleEditor.openChrome();
if (gChromeWindow.document.readyState != "complete") {
gChromeWindow.addEventListener("load", function onChromeLoad() {
gChromeWindow.removeEventListener("load", onChromeLoad, true);
gChromeWindow.styleEditorChrome._alwaysDisableAnimations = true;
aCallback(gChromeWindow.styleEditorChrome);
}, true);
} else {
gChromeWindow.styleEditorChrome._alwaysDisableAnimations = true;
aCallback(gChromeWindow.styleEditorChrome);
}
}
function addTabAndLaunchStyleEditorChromeWhenLoaded(aCallback)
{
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
launchStyleEditorChrome(aCallback);
}, true);
}
registerCleanupFunction(cleanup);

View File

@ -0,0 +1,5 @@
/* this stylesheet applies when min-width<400px */
body {
background: red;
}

View File

@ -0,0 +1,11 @@
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="simple.css" media="screen,print"/>
<link rel="stylesheet" type="text/css" href="media-small.css" media="screen and (min-width: 200px)"/>
</head>
<body>
<div>test for media labels</div>
</body>
</html>

View File

@ -0,0 +1,17 @@
<!doctype html>
<html>
<head>
<title>minified testcase</title>
<style type="text/css"><!--
body{background:white;}div{font-size:4em;color:red}
--></style>
<style type="text/css">body { background: red; }
div {
font-size: 5em;
color: red
}</style>
</head>
<body>
<div>minified <span>testcase</span></div>
</body>
</html>

View File

@ -0,0 +1,8 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
body {
margin: 0;
}

Binary file not shown.

View File

@ -0,0 +1,4 @@
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Type: text/css

View File

@ -0,0 +1,23 @@
<!doctype html>
<html>
<head>
<title>simple testcase</title>
<link rel="stylesheet" type="text/css" media="scren" href="simple.css.gz"/>
<style type="text/css">
body {
background: white;
}
div {
font-size: 4em;
}
div > span {
text-decoration: underline;
}
</style>
</head>
<body>
<div>simple <span>testcase</span></div>
</body>
</html>

View File

@ -0,0 +1,23 @@
<!doctype html>
<html>
<head>
<title>simple testcase</title>
<link rel="stylesheet" type="text/css" media="scren" href="simple.css"/>
<style type="text/css">
body {
background: white;
}
div {
font-size: 4em;
}
div > span {
text-decoration: underline;
}
</style>
</head>
<body>
<div>simple <span>testcase</span></div>
</body>
</html>

View File

@ -655,13 +655,10 @@ function CssRuleView(aDoc, aStore)
this.doc = aDoc;
this.store = aStore;
this.element = this.doc.createElementNS(HTML_NS, "div");
this.element = this.doc.createElementNS(XUL_NS, "vbox");
this.element.setAttribute("tabindex", "0");
this.element.classList.add("ruleview");
// Give a relative position for the inplace editor's measurement
// span to be placed absolutely against.
this.element.style.position = "relative";
this.element.flex = 1;
}
CssRuleView.prototype = {
@ -761,6 +758,10 @@ RuleEditor.prototype = {
this.element = this.doc.createElementNS(HTML_NS, "div");
this.element._ruleEditor = this;
// Give a relative position for the inplace editor's measurement
// span to be placed absolutely against.
this.element.style.position = "relative";
// Add the source link.
let source = createChild(this.element, "div", {
class: "ruleview-rule-source",

View File

@ -35,6 +35,10 @@
*
* ***** END LICENSE BLOCK ***** */
.ruleview {
overflow: auto;
}
.ruleview-computedlist:not(.styleinspector-open) {
display: none;
}

View File

@ -47,7 +47,6 @@ include $(topsrcdir)/config/rules.mk
_BROWSER_TEST_FILES = \
browser_styleinspector.js \
browser_styleinspector_webconsole.js \
browser_bug683672.js \
browser_styleinspector_bug_672746_default_styles.js \
browser_styleinspector_bug_672744_search_filter.js \
@ -63,7 +62,6 @@ _BROWSER_TEST_FILES = \
$(NULL)
_BROWSER_TEST_PAGES = \
browser_styleinspector_webconsole.htm \
browser_bug683672.html \
$(NULL)

View File

@ -1,185 +0,0 @@
<!DOCTYPE HTML>
<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
<title>Style inspector test</title>
<style>
.text {
font-family: sans-serif;
}
.container > .text {
font-family: serif;
}
.text2 {
font-family: sans-serif;
}
.text3 {
font-size: 100px;
}
.text[dir=rtl] {
font-family: monospace;
}
.container .text {
font-family: fantasy;
}
#container .text {
font-family: fantasy;
}
.container > .text {
font-family: cursive;
}
#container > .text {
font-family: cursive;
}
#container > .dummy, #container > .dummy2 {
font-family: cursive;
}
div {
font-family: fantasy;
}
#container {
font-family: fantasy;
}
#container > span {
font-family: cursive;
}
#container .dummy {
font-family: fantasy;
}
html + .dummy {
font-family: fantasy;
}
span + span {
font-family: fantasy;
}
span[id=text] {
font-family: cursive;
}
span[att=glue] {
font-family: cursive;
}
span::before {
font-family: cursive;
content: "START ";
}
span::after {
font-family: cursive;
content: " END";
}
spawn::before {
font-family: cursive;
content: "START ";
}
spawn::after {
font-family: cursive;
content: " END";
}
a:link {
font-family: sans-serif;
}
.link:link {
font-family: fantasy;
}
a:visited {
font-family: sans-serif;
}
.link:visited {
font-family: fantasy;
}
a:active {
font-family: sans-serif;
}
.link:active {
font-family: fantasy;
}
a:hover {
font-family: sans-serif;
}
.link:hover {
font-family: fantasy;
}
a:focus {
font-family: sans-serif;
outline: 5px solid #0f0;
}
.link:focus {
font-family: fantasy;
}
span::first-letter {
font-family: sans-serif;
}
.text::first-letter {
font-family: fantasy;
}
span::first-line {
font-family: sans-serif;
}
.text::first-line {
font-family: fantasy;
}
#container:first-child {
font-family: sans-serif;
}
div:first-child {
font-family: fantasy;
}
span:lang(en) {
font-family: sans-serif;
}
span:lang(it) {
font-family: fantasy;
}
html::-moz-selection {
background-color: #f00;
font-family: fantasy;
}
</style>
</head>
<body>
<h2>font-size</h2>
<div id="container">
<span id="text" lang="en" class="text">Use inspectstyle($('text')) to inspect me</span><br />
<span id="text2" class="text2">Use inspectstyle($('text2'))</span><br />
<a class="link" href="#">Some Link</a>
<h2>font-family has a single unmatched rule</h2>
</div>
</body>
</html>

View File

@ -1,194 +0,0 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is DevTools test code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Michael Ratcliffe <mratcliffe@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
// Tests that inspectstyle(node) works properly
const TEST_URI = "http://example.com/browser/browser/devtools/styleinspector/test/browser/browser_styleinspector_webconsole.htm";
let doc;
let jsterm;
let hudBox;
let stylePanels = [];
function test() {
addTab(TEST_URI);
browser.addEventListener("DOMContentLoaded", prepConsole, false);
}
function prepConsole() {
browser.removeEventListener("DOMContentLoaded", prepConsole, false);
doc = content.document;
openConsole();
ok(window.StyleInspector, "StyleInspector exists");
let hud = HUDService.getHudByWindow(content);
ok(hud, "we have a console");
hudBox = hud.HUDBox;
ok(hudBox, "we have the console display");
jsterm = hud.jsterm;
ok(jsterm, "we have a jsterm");
openStyleInspector1();
}
function openStyleInspector1() {
info("opening style inspector instance 1");
Services.obs.addObserver(openStyleInspector2, "StyleInspector-opened", false);
jsterm.execute("inspectstyle($('text'))");
}
function openStyleInspector2() {
Services.obs.removeObserver(openStyleInspector2, "StyleInspector-opened", false);
info("opening style inspector instance 2");
Services.obs.addObserver(openStyleInspector3, "StyleInspector-opened", false);
jsterm.execute("inspectstyle($('text2'))");
}
function openStyleInspector3() {
Services.obs.removeObserver(openStyleInspector3, "StyleInspector-opened", false);
info("opening style inspector instance 3");
Services.obs.addObserver(teststylePanels, "StyleInspector-opened", false);
jsterm.execute("inspectstyle($('container'))");
}
function teststylePanels() {
Services.obs.removeObserver(teststylePanels, "StyleInspector-opened", false);
info("adding style inspector instances to stylePanels array");
let popupSet = document.getElementById("mainPopupSet");
let len = popupSet.childNodes.length - 3;
stylePanels.push(popupSet.childNodes[len++]);
stylePanels.push(popupSet.childNodes[len++]);
stylePanels.push(popupSet.childNodes[len++]);
let eltArray = [
doc.getElementById("text"),
doc.getElementById("text2"),
doc.getElementById("container")
];
// We have 3 style inspector instances, each with an element selected:
// 1. #text
// 2. #text2
// 3. #container
//
// We will loop through each instance and check that the correct node is
// selected and that the correct css selector has been selected as active
info("looping through array to check initialization");
for (let i = 0, max = stylePanels.length; i < max; i++) {
ok(stylePanels[i], "style inspector instance " + i +
" correctly initialized");
is(stylePanels[i].state, "open", "style inspector " + i + " is open");
/* // the following should be tested elsewhere
// TODO bug 696166
let htmlTree = stylePanels[i].cssHtmlTree;
let cssLogic = htmlTree.cssLogic;
let elt = eltArray[i];
let eltId = elt.id;
// Check that the correct node is selected
is(elt, htmlTree.viewedElement,
"style inspector node matches the selected node (id=" + eltId + ")");
is(htmlTree.viewedElement, stylePanels[i].cssLogic.viewedElement,
"cssLogic node matches the cssHtmlTree node (id=" + eltId + ")");
// Check that the correct css selector has been selected as active
let matchedSelectors = cssLogic.getPropertyInfo("font-family").matchedSelectors;
let sel = matchedSelectors[0];
let selector = sel.selector.text;
let value = sel.value;
// Because we know which selectors should be the best match and what their
// values should be we can check them
switch(eltId) {
case "text":
is(selector, "#container > .text", "correct best match for #text");
is(value, "cursive", "correct css property value for #" + eltId);
break;
case "text2":
is(selector, "#container > span", "correct best match for #text2");
is(value, "cursive", "correct css property value for #" + eltId);
break;
case "container":
is(selector, "#container", "correct best match for #container");
is(value, "fantasy", "correct css property value for #" + eltId);
}
*/
}
info("hiding stylePanels[1]");
Services.obs.addObserver(styleInspectorClosedByHide,
"StyleInspector-closed", false);
stylePanels[1].hidePopup();
}
function styleInspectorClosedByHide()
{
Services.obs.removeObserver(styleInspectorClosedByHide, "StyleInspector-closed", false);
is(stylePanels[0].state, "open", "instance stylePanels[0] is still open");
isnot(stylePanels[1].state, "open", "instance stylePanels[1] is not open");
is(stylePanels[2].state, "open", "instance stylePanels[2] is still open");
info("closing web console");
Services.obs.addObserver(styleInspectorClosedFromConsole1,
"StyleInspector-closed", false);
closeConsole();
}
function styleInspectorClosedFromConsole1()
{
Services.obs.removeObserver(styleInspectorClosedFromConsole1,
"StyleInspector-closed", false);
info("Style Inspector 1 and 2 closed");
executeSoon(cleanUp);
}
function cleanUp()
{
let panels = document.querySelector("panel[hudToolId]");
ok(!panels,
"all style inspector panels are now detached and ready for garbage collection");
info("cleaning up");
doc = hudBox = stylePanels = jsterm = null;
finishTest();
}

View File

@ -79,12 +79,6 @@ XPCOMUtils.defineLazyGetter(this, "gcli", function () {
return obj.gcli;
});
XPCOMUtils.defineLazyGetter(this, "StyleInspector", function () {
var obj = {};
Cu.import("resource:///modules/devtools/StyleInspector.jsm", obj);
return obj.StyleInspector;
});
XPCOMUtils.defineLazyGetter(this, "CssRuleView", function() {
let tmp = {};
Cu.import("resource:///modules/devtools/CssRuleView.jsm", tmp);
@ -1860,10 +1854,6 @@ HUD_SERVICE.prototype =
for (let i = 0; i < panels.length; i++) {
panels[i].hidePopup();
}
panels = popupset.querySelectorAll("panel[hudToolId=" + aHUDId + "]");
for (let i = 0; i < panels.length; i++) {
panels[i].hidePopup();
}
let id = ConsoleUtils.supString(aHUDId);
Services.obs.notifyObservers(id, "web-console-destroyed", null);
@ -4598,40 +4588,6 @@ function JSTermHelper(aJSTerm)
propPanel.panel.setAttribute("hudId", aJSTerm.hudId);
};
/**
* Inspects the passed aNode in the style inspector.
*
* @param object aNode
* aNode to inspect.
* @returns void
*/
aJSTerm.sandbox.inspectstyle = function JSTH_inspectstyle(aNode)
{
let errstr = null;
aJSTerm.helperEvaluated = true;
if (!Services.prefs.getBoolPref("devtools.styleinspector.enabled")) {
errstr = HUDService.getStr("inspectStyle.styleInspectorNotEnabled");
} else if (!aNode) {
errstr = HUDService.getStr("inspectStyle.nullObjectPassed");
} else if (!(aNode instanceof Ci.nsIDOMNode)) {
errstr = HUDService.getStr("inspectStyle.mustBeDomNode");
} else if (!(aNode.style instanceof Ci.nsIDOMCSSStyleDeclaration)) {
errstr = HUDService.getStr("inspectStyle.nodeHasNoStyleProps");
}
if (!errstr) {
let chromeWin = HUDService.getHudReferenceById(aJSTerm.hudId).chromeWindow;
let styleInspector = new StyleInspector(chromeWin);
styleInspector.createPanel(false, function() {
styleInspector.panel.setAttribute("hudToolId", aJSTerm.hudId);
styleInspector.open(aNode);
});
} else {
aJSTerm.writeOutput(errstr + "\n", CATEGORY_OUTPUT, SEVERITY_ERROR);
}
};
aJSTerm.sandbox.inspectrules = function JSTH_inspectrules(aNode)
{
aJSTerm.helperEvaluated = true;
@ -5173,6 +5129,7 @@ JSTerm.prototype = {
}
hud.HUDBox.lastTimestamp = 0;
hud.groupDepth = 0;
},
/**

View File

@ -43,6 +43,14 @@ function onLoad(aEvent) {
is(msg.length, 4, "four message nodes displayed");
is(msg[3].style.marginLeft, GROUP_INDENT + "px", "correct group indent found");
// Test that clearing the console removes the indentation.
hud.jsterm.clearOutput();
content.console.log("cleared");
findLogEntry("cleared");
let msg = outputNode.querySelectorAll(".webconsole-msg-icon-container");
is(msg.length, 1, "one message node displayed");
is(msg[0].style.marginLeft, "0px", "correct group indent found");
finishTest();
}

View File

@ -220,6 +220,13 @@ can reach it easily. -->
<!ENTITY inspectStyleButton.label "Style">
<!ENTITY inspectStyleButton.accesskey "S">
<!-- LOCALIZATION NOTE (styleeditor.label): This menu item label appears
- in the Tools menu. -->
<!ENTITY styleeditor.label "Style Editor">
<!ENTITY styleeditor.accesskey "y">
<!ENTITY styleeditor.keycode "VK_F7">
<!ENTITY styleeditor.keytext "F7">
<!ENTITY getMoreDevtoolsCmd.label "Get More Tools">
<!ENTITY getMoreDevtoolsCmd.accesskey "M">

View File

@ -0,0 +1,62 @@
<!-- LOCALIZATION NOTE : FILE This file contains the Style Editor window strings -->
<!-- LOCALIZATION NOTE : FILE Do not translate commandkeys -->
<!-- LOCALIZATION NOTE : The correct localization of this file might be to keep
it in English, or another language commonly spoken among web developers.
You want to make that choice consistent across the developer tools.
A good criteria is the language in which you'd find the best documentation
on web development on the web. -->
<!-- LOCALIZATION NOTE (window.title): This is the default title for Style Editor
main window. NB: the property chromeWindowTitle in styleeditor.properties
is used to dynamically update the Style Editor window title with the title
of the web page it is editing. -->
<!ENTITY window.title "Style Editor">
<!ENTITY newButton.label "New">
<!ENTITY newButton.tooltip "Create and append a new style sheet to the document">
<!ENTITY newButton.accesskey "N">
<!ENTITY newButton.commandkey "n">
<!ENTITY importButton.label "Import…">
<!ENTITY importButton.tooltip "Import and append an existing style sheet to the document">
<!ENTITY importButton.accesskey "I">
<!ENTITY importButton.commandkey "i">
<!ENTITY searchInput.tooltip "Filter style sheets by name">
<!ENTITY searchInput.placeholder "Find style sheet">
<!-- LOCALIZATION NOTE (searchNoResults): This is shown when searching a term
that is not found in any stylesheet or stylesheet name. -->
<!ENTITY searchNoResults.label "No matching style sheet has been found.">
<!-- LOCALIZATION NOTE (searchClearButton): This button clears the search input
box and is visible only when a search term has been typed. -->
<!ENTITY searchClearButton.label "Clear">
<!ENTITY visibilityToggle.tooltip "Toggle style sheet visibility">
<!ENTITY visibilityToggle.accesskey "V">
<!ENTITY saveButton.label "Save">
<!ENTITY saveButton.tooltip "Save this style sheet to a file">
<!ENTITY saveButton.accesskey "S">
<!ENTITY saveButton.commandkey "s">
<!-- LOCALICATION NOTE (scoped.label): This is shown in a stylesheets list item
when the stylesheet uses the scoped attribute on the <style> element. -->
<!ENTITY scoped.label "Scoped.">
<!ENTITY editorTextbox.placeholder "Type CSS here.">
<!-- LOCALICATION NOTE (noStyleSheet.label): This is shown when a page has no
stylesheet. -->
<!ENTITY noStyleSheet.label "This page has no style sheet.">
<!-- LOCALICATION NOTE (noStyleSheet-tip-start.label): This is the start of a
tip sentence shown when there is no stylesheet. It suggests to create a new
stylesheet and provides an action link to do so. -->
<!ENTITY noStyleSheet-tip-start.label "Perhaps you'd like to ">
<!-- LOCALICATION NOTE (noStyleSheet-tip-action.label): This is text for the
link that triggers creation of a new stylesheet. -->
<!ENTITY noStyleSheet-tip-action.label "append a new style sheet">
<!-- LOCALICATION NOTE (noStyleSheet-tip-end.label): End of the tip sentence -->
<!ENTITY noStyleSheet-tip-end.label "?">

View File

@ -0,0 +1,64 @@
# LOCALIZATION NOTE These strings are used inside the Style Editor.
# LOCALIZATION NOTE The correct localization of this file might be to keep it
# in English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best documentation
# on web development on the web.
# LOCALIZATION NOTE (chromeWindowTitle): This is the title of the Style Editor
# 'chrome' window. That is, the main window with the stylesheets list.
# The argument is either the content document's title or its href if no title
# is available.
chromeWindowTitle=Style Editor [%S]
# LOCALIZATION NOTE (inlineStyleSheet): This is the name used for an style sheet
# that is declared inline in the <style> element. Shown in the stylesheets list.
# the argument is the index (order) of the containing <style> element in the
# document.
inlineStyleSheet=<inline style sheet #%S>
# LOCALIZATION NOTE (newStyleSheet): This is the default name for a new
# user-created style sheet.
newStyleSheet=New style sheet #%S
# LOCALIZATION NOTE (ruleCount.label): Semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# This is shown in the style sheets list.
# #1 rule.
# example: 111 rules.
ruleCount.label=#1 rule.;#1 rules.
# LOCALIZATION NOTE (error-load.label): This is shown when loading fails.
error-load=Style sheet could not be loaded.
# LOCALIZATION NOTE (error-save.label): This is shown when saving fails.
error-save=Style sheet could not be saved.
# LOCALIZATION NOTE (importStyleSheet.title): This is the file picker title,
# when you import a style sheet into the Style Editor.
importStyleSheet.title=Import style sheet
# LOCALIZATION NOTE (importStyleSheet.title): This is the *.css filter title
importStyleSheet.filter=CSS files
# LOCALIZATION NOTE (saveStyleSheet.title): This is the file picker title,
# when you save a style sheet from the Style Editor.
saveStyleSheet.title=Save style sheet
# LOCALIZATION NOTE (saveStyleSheet.title): This is the *.css filter title
saveStyleSheet.filter=CSS files
# LOCALIZATION NOTE (saveStyleSheet.commandkey): This the key to use in
# conjunction with accel (Command on Mac or Ctrl on other platforms) to Save
saveStyleSheet.commandkey=S
# LOCALIZATION NOTE (undo.commandkey): This the key to use in
# conjunction with accel (Command on Mac or Ctrl on other platforms) to Undo a
# change in the editor.
undo.commandkey=Z
# LOCALIZATION NOTE (redo.commandkey): This the key to use in
# conjunction with accel+shift (accel is Command on Mac or Ctrl on other
# platforms) to Redo a change in the editor.
redo.commandkey=Z

View File

@ -115,23 +115,6 @@ NetworkPanel.imageSizeDeltaDurationMS=%Sx%Spx, Δ%Sms
NetworkPanel.responseBodyUnableToDisplay.content=Unable to display responses of type "%S"
ConsoleAPIDisabled=The Web Console logging API (console.log, console.info, console.warn, console.error) has been disabled by a script on this page.
# LOCALIZATION NOTE (inspectStyle.nullObjectPassed):
# This message is returned when a null object is passed in to inspectstyle()
inspectStyle.nullObjectPassed=Object is null
# LOCALIZATION NOTE (inspectStyle.mustBeDomNode):
# This message is returned when a non-DOM node is passed in to inspectstyle()
inspectStyle.mustBeDomNode=Object must be a valid DOM node
# LOCALIZATION NOTE (inspectStyle.nodeHasNoStyleProps):
# This message is returned when an unstyleable object is passed in to inspectstyle()
inspectStyle.nodeHasNoStyleProps=Object cannot be styled
# LOCALIZATION NOTE (inspectStyle.styleInspectorNotEnabled):
# This message is returned when devtools.styleinspector.enabled is not set to
# true
inspectStyle.styleInspectorNotEnabled=The style inspector is not enabled. Please set the option devtools.styleinspector.enabled to true in about:config to use this command.
# LOCALIZATION NOTE (webConsolePosition): The label shown for the menu which
# allows the user to toggle between the Web Console positioning types.
webConsolePosition=Position

View File

@ -21,6 +21,8 @@
locale/browser/devtools/inspector.properties (%chrome/browser/devtools/inspector.properties)
locale/browser/devtools/scratchpad.properties (%chrome/browser/devtools/scratchpad.properties)
locale/browser/devtools/scratchpad.dtd (%chrome/browser/devtools/scratchpad.dtd)
locale/browser/devtools/styleeditor.properties (%chrome/browser/devtools/styleeditor.properties)
locale/browser/devtools/styleeditor.dtd (%chrome/browser/devtools/styleeditor.dtd)
locale/browser/devtools/styleinspector.properties (%chrome/browser/devtools/styleinspector.properties)
locale/browser/devtools/styleinspector.dtd (%chrome/browser/devtools/styleinspector.dtd)
locale/browser/devtools/webConsole.dtd (%chrome/browser/devtools/webConsole.dtd)

View File

@ -2030,6 +2030,7 @@ panel[dimmed="true"] {
border-radius: 3px;
background: -moz-linear-gradient(hsla(212,7%,57%,.35), hsla(212,7%,57%,.1)) padding-box;
box-shadow: 0 1px 0 hsla(210,16%,76%,.15) inset, 0 0 0 1px hsla(210,16%,76%,.15) inset, 0 1px 0 hsla(210,16%,76%,.15);
margin: 0 3px;
}
#inspector-inspect-toolbutton:not([checked]):hover:active,
@ -2169,7 +2170,7 @@ panel[dimmed="true"] {
/* Highlighter toolbar - breadcrumbs */
#inspector-breadcrumbs {
padding: 0 6px;
padding: 0 3px;
/* A fake 1px-shadow is included in the border-images of the
inspector-breadcrumbs-buttons, to match toolbar-buttons style.
This negative margin compensate the extra row of pixels created

View File

@ -222,11 +222,11 @@
.ruleview-rule-source {
background-color: -moz-dialog;
padding: 2px;
padding: 2px 5px;
}
.ruleview-code {
padding: 2px;
padding: 2px 5px;
}
.ruleview-propertylist {

Binary file not shown.

After

Width:  |  Height:  |  Size: 784 B

View File

@ -0,0 +1,137 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Style Editor code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Cedric Vivier <cedricv@neonux.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
.splitview-root {
margin-top: 4px;
}
.splitview-filter {
-moz-box-flex: 1;
-moz-margin-start: 1ex;
max-width: 25ex;
}
.splitview-nav-container {
-moz-box-pack: center;
margin: 4px;
border: 1px inset WindowFrame;
border-radius: 4px;
background-color: -moz-default-background-color;
color: -moz-default-color;
}
ol.splitview-nav {
overflow-x: hidden;
overflow-y: auto;
list-style-image: none;
list-style: none;
padding: 0;
margin: 0;
}
ol.splitview-nav > li {
display: -moz-box;
-moz-box-orient: vertical;
outline: 0;
border-bottom: 1px solid ThreeDShadow;
padding: 2px;
padding-top: 4px;
vertical-align: bottom;
color: black;
-moz-transition-property: background-color, max-height;
-moz-transition-duration: 1s;
max-height: 2048px; /* for slide transition */
}
ol.splitview-nav > li.splitview-active {
background-color: #eaf2fe; /* same as orion.css's .line_caret */
}
ol.splitview-nav > li:hover,
ol.splitview-nav > li:focus {
background-color: #f0f5fd; /* slightly lighter .line_caret */
}
ol.splitview-nav > li.splitview-flash {
background-color: #faf0e1; /* complement of .line_caret */
}
ol.splitview-nav > li.splitview-slide {
max-height: 0;
overflow: hidden;
}
.splitview-side-details {
margin: 2px;
}
.splitview-nav.splitview-all-filtered ~ .splitview-nav.placeholder.all-filtered,
.splitview-nav:empty ~ .splitview-nav.placeholder.empty {
-moz-box-flex: 0;
text-align: center;
}
.splitview-main .toolbar,
.splitview-main .toolbar > * {
display: -moz-box;
}
.splitview-main .toolbar {
-moz-box-flex: 1;
-moz-box-orient: horizontal;
-moz-padding-start: 3px;
}
a {
color: -moz-hyperlinktext;
text-decoration: underline;
}
button {
margin: 0;
}
/* limited width mode (hide search unless it has focus [search-on-type]) */
@media (max-width: 250px) {
.splitview-filter {
max-width: none;
position: fixed;
margin: 0;
bottom: -4em;
-moz-transition-property: bottom;
-moz-transition-duration: 0.2s;
}
.splitview-filter[focused="true"] {
bottom: 0;
}
}

View File

@ -0,0 +1,177 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Style Editor code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Cedric Vivier <cedricv@neonux.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
ol.splitview-nav:focus {
outline: 0; /* focus ring is on the stylesheet name */
}
.splitview-nav > li:-moz-locale-dir(ltr),
.splitview-nav > li hgroup:-moz-locale-dir(ltr) {
float: left;
}
.splitview-nav > li:-moz-locale-dir(rtl),
.splitview-nav > li hgroup:-moz-locale-dir(rtl) {
float: right;
}
.splitview-nav > li > hgroup.stylesheet-info {
max-width: 66%;
}
.splitview-nav > li > hgroup .stylesheet-name {
display: inline-block;
width: 100%;
max-width: 34ex;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.splitview-nav > li.unsaved > hgroup .stylesheet-name {
font-style: italic;
}
.splitview-nav > li.unsaved > hgroup .stylesheet-name:before:-moz-locale-dir(ltr),
.splitview-nav > li.unsaved > hgroup .stylesheet-name:after:-moz-locale-dir(rtl) {
font-style: italic;
content: "* ";
}
.splitview-nav > li > .stylesheet-enabled {
float: left;
width: 20px;
height: 20px;
-moz-margin-end: 6px;
border: 1px solid transparent;
background-image: url(eye-toggle.png);
background-position: center center;
background-repeat: no-repeat;
}
.splitview-nav > li > .stylesheet-enabled:-moz-locale-dir(rtl) {
float: right;
}
.splitview-nav > li.disabled > .stylesheet-enabled {
background-image: none;
}
.splitview-nav > li > .stylesheet-enabled:focus,
.splitview-nav > li:hover > .stylesheet-enabled {
outline: 0;
border: 1px inset WindowFrame;
-moz-margin-end: 6px;
}
.splitview-nav > li hgroup .stylesheet-title {
color: GrayText;
font-size: 0.8em;
}
.splitview-nav > li.error hgroup > .stylesheet-error-message {
color: red;
font-size: 0.8em;
}
.splitview-nav > li > .stylesheet-more {
position: relative;
right: 0;
}
.splitview-nav > li > .stylesheet-more:-moz-locale-dir(rtl) {
left: 0;
}
.splitview-nav > li hgroup.stylesheet-stats,
.splitview-nav > li hgroup.stylesheet-actions {
position: absolute;
z-index: 2;
-moz-transition-property: left, right;
-moz-transition-duration: 0.2s;
-moz-transition-delay: 0.2s;
-moz-transition-timing-function: ease-in-out;
}
.splitview-nav > li hgroup.stylesheet-stats {
z-index: 1;
-moz-transition-delay: 0.4s;
color: GrayText;
padding-left: 6px; /* Fitts */
padding-right: 6px;
}
.splitview-nav > li hgroup.stylesheet-actions a {
color: ButtonText;
padding-left: 6px; /* Fitts */
padding-right: 6px;
}
.splitview-nav > li hgroup.stylesheet-actions:-moz-locale-dir(ltr),
.splitview-nav > li:hover hgroup.stylesheet-stats:-moz-locale-dir(ltr),
.splitview-nav > li:focus hgroup.stylesheet-stats:-moz-locale-dir(ltr),
.splitview-nav > li.splitview-active hgroup.stylesheet-stats:-moz-locale-dir(ltr) {
right: -50ex;
}
.splitview-nav > li hgroup.stylesheet-actions:-moz-locale-dir(rtl),
.splitview-nav > li:hover hgroup.stylesheet-stats:-moz-locale-dir(rtl),
.splitview-nav > li:focus hgroup.stylesheet-stats:-moz-locale-dir(rtl),
.splitview-nav > li.splitview-active hgroup.stylesheet-stats:-moz-locale-dir(rtl) {
left: -50ex;
}
.splitview-nav > li hgroup.stylesheet-stats:-moz-locale-dir(ltr),
.splitview-nav > li:hover hgroup.stylesheet-actions:-moz-locale-dir(ltr),
.splitview-nav > li:focus hgroup.stylesheet-actions:-moz-locale-dir(ltr),
.splitview-nav > li.splitview-active hgroup.stylesheet-actions:-moz-locale-dir(ltr) {
right: 0;
}
.splitview-nav > li hgroup.stylesheet-stats:-moz-locale-dir(rtl),
.splitview-nav > li:hover hgroup.stylesheet-actions:-moz-locale-dir(rtl),
.splitview-nav > li:focus hgroup.stylesheet-actions:-moz-locale-dir(rtl),
.splitview-nav > li.splitview-active hgroup.stylesheet-actions:-moz-locale-dir(rtl) {
left: 0;
}
.stylesheet-editor-input {
display: -moz-box;
-moz-box-flex: 1;
overflow: hidden;
min-height: 8em;
margin: 3px;
margin-top: 0;
border: 1px inset WindowFrame;
border-radius: 4px;
background-color: -moz-default-background-color;
}
h1,
h2,
h3 {
font-size: inherit;
font-weight: normal;
margin: 0;
padding: 0;
}

View File

@ -111,7 +111,9 @@ browser.jar:
skin/classic/browser/devtools/breadcrumbs/rtl-start-selected-pressed.png (devtools/breadcrumbs/rtl-start-selected-pressed.png)
skin/classic/browser/devtools/breadcrumbs/rtl-start.png (devtools/breadcrumbs/rtl-start.png)
skin/classic/browser/devtools/breadcrumbs/rtl-start-selected.png (devtools/breadcrumbs/rtl-start-selected.png)
skin/classic/browser/devtools/splitview.css (devtools/splitview.css)
skin/classic/browser/devtools/styleeditor.css (devtools/styleeditor.css)
skin/classic/browser/devtools/eye-toggle.png (devtools/eye-toggle.png)
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/sync-16-throbber.png
skin/classic/browser/sync-16.png

View File

@ -224,11 +224,11 @@
.ruleview-rule-source {
background-color: -moz-dialog;
padding: 2px;
padding: 2px 5px;
}
.ruleview-code {
padding: 2px;
padding: 2px 5px;
}
.ruleview-propertylist {

Binary file not shown.

After

Width:  |  Height:  |  Size: 784 B

View File

@ -0,0 +1,137 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Style Editor code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Cedric Vivier <cedricv@neonux.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
.splitview-root {
margin-top: 4px;
}
.splitview-filter {
-moz-box-flex: 1;
-moz-margin-start: 1ex;
max-width: 25ex;
}
.splitview-nav-container {
-moz-box-pack: center;
margin: 4px;
border: 1px inset WindowFrame;
border-radius: 4px;
background-color: -moz-default-background-color;
color: -moz-default-color;
}
ol.splitview-nav {
overflow-x: hidden;
overflow-y: auto;
list-style-image: none;
list-style: none;
padding: 0;
margin: 0;
}
ol.splitview-nav > li {
display: -moz-box;
-moz-box-orient: vertical;
outline: 0;
border-bottom: 1px solid ThreeDShadow;
padding: 2px;
padding-top: 4px;
vertical-align: bottom;
color: black;
-moz-transition-property: background-color, max-height;
-moz-transition-duration: 1s;
max-height: 2048px; /* for slide transition */
}
ol.splitview-nav > li.splitview-active {
background-color: #eaf2fe; /* same as orion.css's .line_caret */
}
ol.splitview-nav > li:hover,
ol.splitview-nav > li:focus {
background-color: #f0f5fd; /* slightly lighter .line_caret */
}
ol.splitview-nav > li.splitview-flash {
background-color: #faf0e1; /* complement of .line_caret */
}
ol.splitview-nav > li.splitview-slide {
max-height: 0;
overflow: hidden;
}
.splitview-side-details {
margin: 2px;
}
.splitview-nav.splitview-all-filtered ~ .splitview-nav.placeholder.all-filtered,
.splitview-nav:empty ~ .splitview-nav.placeholder.empty {
-moz-box-flex: 0;
text-align: center;
}
.splitview-main .toolbar,
.splitview-main .toolbar > * {
display: -moz-box;
}
.splitview-main .toolbar {
-moz-box-flex: 1;
-moz-box-orient: horizontal;
-moz-padding-start: 3px;
}
a {
color: -moz-hyperlinktext;
text-decoration: underline;
}
button {
margin: 0;
}
/* limited width mode (hide search unless it has focus [search-on-type]) */
@media (max-width: 250px) {
.splitview-filter {
max-width: none;
position: fixed;
margin: 0;
bottom: -4em;
-moz-transition-property: bottom;
-moz-transition-duration: 0.2s;
}
.splitview-filter[focused="true"] {
bottom: 0;
}
}

View File

@ -0,0 +1,177 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Style Editor code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Cedric Vivier <cedricv@neonux.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
ol.splitview-nav:focus {
outline: 0; /* focus ring is on the stylesheet name */
}
.splitview-nav > li:-moz-locale-dir(ltr),
.splitview-nav > li hgroup:-moz-locale-dir(ltr) {
float: left;
}
.splitview-nav > li:-moz-locale-dir(rtl),
.splitview-nav > li hgroup:-moz-locale-dir(rtl) {
float: right;
}
.splitview-nav > li > hgroup.stylesheet-info {
max-width: 66%;
}
.splitview-nav > li > hgroup .stylesheet-name {
display: inline-block;
width: 100%;
max-width: 34ex;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.splitview-nav > li.unsaved > hgroup .stylesheet-name {
font-style: italic;
}
.splitview-nav > li.unsaved > hgroup .stylesheet-name:before:-moz-locale-dir(ltr),
.splitview-nav > li.unsaved > hgroup .stylesheet-name:after:-moz-locale-dir(rtl) {
font-style: italic;
content: "* ";
}
.splitview-nav > li > .stylesheet-enabled {
float: left;
width: 20px;
height: 20px;
-moz-margin-end: 6px;
border: 1px solid transparent;
background-image: url(eye-toggle.png);
background-position: center center;
background-repeat: no-repeat;
}
.splitview-nav > li > .stylesheet-enabled:-moz-locale-dir(rtl) {
float: right;
}
.splitview-nav > li.disabled > .stylesheet-enabled {
background-image: none;
}
.splitview-nav > li > .stylesheet-enabled:focus,
.splitview-nav > li:hover > .stylesheet-enabled {
outline: 0;
border: 1px inset WindowFrame;
-moz-margin-end: 6px;
}
.splitview-nav > li hgroup .stylesheet-title {
color: GrayText;
font-size: 0.8em;
}
.splitview-nav > li.error hgroup > .stylesheet-error-message {
color: red;
font-size: 0.8em;
}
.splitview-nav > li > .stylesheet-more {
position: relative;
right: 0;
}
.splitview-nav > li > .stylesheet-more:-moz-locale-dir(rtl) {
left: 0;
}
.splitview-nav > li hgroup.stylesheet-stats,
.splitview-nav > li hgroup.stylesheet-actions {
position: absolute;
z-index: 2;
-moz-transition-property: left, right;
-moz-transition-duration: 0.2s;
-moz-transition-delay: 0.2s;
-moz-transition-timing-function: ease-in-out;
}
.splitview-nav > li hgroup.stylesheet-stats {
z-index: 1;
-moz-transition-delay: 0.4s;
color: GrayText;
padding-left: 6px; /* Fitts */
padding-right: 6px;
}
.splitview-nav > li hgroup.stylesheet-actions a {
color: ButtonText;
padding-left: 6px; /* Fitts */
padding-right: 6px;
}
.splitview-nav > li hgroup.stylesheet-actions:-moz-locale-dir(ltr),
.splitview-nav > li:hover hgroup.stylesheet-stats:-moz-locale-dir(ltr),
.splitview-nav > li:focus hgroup.stylesheet-stats:-moz-locale-dir(ltr),
.splitview-nav > li.splitview-active hgroup.stylesheet-stats:-moz-locale-dir(ltr) {
right: -50ex;
}
.splitview-nav > li hgroup.stylesheet-actions:-moz-locale-dir(rtl),
.splitview-nav > li:hover hgroup.stylesheet-stats:-moz-locale-dir(rtl),
.splitview-nav > li:focus hgroup.stylesheet-stats:-moz-locale-dir(rtl),
.splitview-nav > li.splitview-active hgroup.stylesheet-stats:-moz-locale-dir(rtl) {
left: -50ex;
}
.splitview-nav > li hgroup.stylesheet-stats:-moz-locale-dir(ltr),
.splitview-nav > li:hover hgroup.stylesheet-actions:-moz-locale-dir(ltr),
.splitview-nav > li:focus hgroup.stylesheet-actions:-moz-locale-dir(ltr),
.splitview-nav > li.splitview-active hgroup.stylesheet-actions:-moz-locale-dir(ltr) {
right: 0;
}
.splitview-nav > li hgroup.stylesheet-stats:-moz-locale-dir(rtl),
.splitview-nav > li:hover hgroup.stylesheet-actions:-moz-locale-dir(rtl),
.splitview-nav > li:focus hgroup.stylesheet-actions:-moz-locale-dir(rtl),
.splitview-nav > li.splitview-active hgroup.stylesheet-actions:-moz-locale-dir(rtl) {
left: 0;
}
.stylesheet-editor-input {
display: -moz-box;
-moz-box-flex: 1;
overflow: hidden;
min-height: 8em;
margin: 3px;
margin-top: 0;
border: 1px inset WindowFrame;
border-radius: 4px;
background-color: -moz-default-background-color;
}
h1,
h2,
h3 {
font-size: inherit;
font-weight: normal;
margin: 0;
padding: 0;
}

View File

@ -151,7 +151,9 @@ browser.jar:
skin/classic/browser/devtools/breadcrumbs/rtl-start-selected-pressed.png (devtools/breadcrumbs/rtl-start-selected-pressed.png)
skin/classic/browser/devtools/breadcrumbs/rtl-start.png (devtools/breadcrumbs/rtl-start.png)
skin/classic/browser/devtools/breadcrumbs/rtl-start-selected.png (devtools/breadcrumbs/rtl-start-selected.png)
skin/classic/browser/devtools/splitview.css (devtools/splitview.css)
skin/classic/browser/devtools/styleeditor.css (devtools/styleeditor.css)
skin/classic/browser/devtools/eye-toggle.png (devtools/eye-toggle.png)
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/sync-throbber.png
skin/classic/browser/sync-16.png

View File

@ -223,11 +223,11 @@
.ruleview-rule-source {
background-color: -moz-dialog;
padding: 2px;
padding: 2px 5px;
}
.ruleview-code {
padding: 2px;
padding: 2px 5px;
}
.ruleview-propertylist {

Binary file not shown.

After

Width:  |  Height:  |  Size: 784 B

View File

@ -0,0 +1,137 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Style Editor code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Cedric Vivier <cedricv@neonux.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
.splitview-root {
margin-top: 4px;
}
.splitview-filter {
-moz-box-flex: 1;
-moz-margin-start: 1ex;
max-width: 25ex;
}
.splitview-nav-container {
-moz-box-pack: center;
margin: 4px;
border: 1px inset WindowFrame;
border-radius: 4px;
background-color: -moz-default-background-color;
color: -moz-default-color;
}
ol.splitview-nav {
overflow-x: hidden;
overflow-y: auto;
list-style-image: none;
list-style: none;
padding: 0;
margin: 0;
}
ol.splitview-nav > li {
display: -moz-box;
-moz-box-orient: vertical;
outline: 0;
border-bottom: 1px solid ThreeDShadow;
padding: 2px;
padding-top: 4px;
vertical-align: bottom;
color: black;
-moz-transition-property: background-color, max-height;
-moz-transition-duration: 1s;
max-height: 2048px; /* for slide transition */
}
ol.splitview-nav > li.splitview-active {
background-color: #eaf2fe; /* same as orion.css's .line_caret */
}
ol.splitview-nav > li:hover,
ol.splitview-nav > li:focus {
background-color: #f0f5fd; /* slightly lighter .line_caret */
}
ol.splitview-nav > li.splitview-flash {
background-color: #faf0e1; /* complement of .line_caret */
}
ol.splitview-nav > li.splitview-slide {
max-height: 0;
overflow: hidden;
}
.splitview-side-details {
margin: 2px;
}
.splitview-nav.splitview-all-filtered ~ .splitview-nav.placeholder.all-filtered,
.splitview-nav:empty ~ .splitview-nav.placeholder.empty {
-moz-box-flex: 0;
text-align: center;
}
.splitview-main .toolbar,
.splitview-main .toolbar > * {
display: -moz-box;
}
.splitview-main .toolbar {
-moz-box-flex: 1;
-moz-box-orient: horizontal;
-moz-padding-start: 3px;
}
a {
color: -moz-hyperlinktext;
text-decoration: underline;
}
button {
margin: 0;
}
/* limited width mode (hide search unless it has focus [search-on-type]) */
@media (max-width: 250px) {
.splitview-filter {
max-width: none;
position: fixed;
margin: 0;
bottom: -4em;
-moz-transition-property: bottom;
-moz-transition-duration: 0.2s;
}
.splitview-filter[focused="true"] {
bottom: 0;
}
}

View File

@ -0,0 +1,177 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Style Editor code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Cedric Vivier <cedricv@neonux.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
ol.splitview-nav:focus {
outline: 0; /* focus ring is on the stylesheet name */
}
.splitview-nav > li:-moz-locale-dir(ltr),
.splitview-nav > li hgroup:-moz-locale-dir(ltr) {
float: left;
}
.splitview-nav > li:-moz-locale-dir(rtl),
.splitview-nav > li hgroup:-moz-locale-dir(rtl) {
float: right;
}
.splitview-nav > li > hgroup.stylesheet-info {
max-width: 66%;
}
.splitview-nav > li > hgroup .stylesheet-name {
display: inline-block;
width: 100%;
max-width: 34ex;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.splitview-nav > li.unsaved > hgroup .stylesheet-name {
font-style: italic;
}
.splitview-nav > li.unsaved > hgroup .stylesheet-name:before:-moz-locale-dir(ltr),
.splitview-nav > li.unsaved > hgroup .stylesheet-name:after:-moz-locale-dir(rtl) {
font-style: italic;
content: "* ";
}
.splitview-nav > li > .stylesheet-enabled {
float: left;
width: 20px;
height: 20px;
-moz-margin-end: 6px;
border: 1px solid transparent;
background-image: url(eye-toggle.png);
background-position: center center;
background-repeat: no-repeat;
}
.splitview-nav > li > .stylesheet-enabled:-moz-locale-dir(rtl) {
float: right;
}
.splitview-nav > li.disabled > .stylesheet-enabled {
background-image: none;
}
.splitview-nav > li > .stylesheet-enabled:focus,
.splitview-nav > li:hover > .stylesheet-enabled {
outline: 0;
border: 1px inset WindowFrame;
-moz-margin-end: 6px;
}
.splitview-nav > li hgroup .stylesheet-title {
color: GrayText;
font-size: 0.8em;
}
.splitview-nav > li.error hgroup > .stylesheet-error-message {
color: red;
font-size: 0.8em;
}
.splitview-nav > li > .stylesheet-more {
position: relative;
right: 0;
}
.splitview-nav > li > .stylesheet-more:-moz-locale-dir(rtl) {
left: 0;
}
.splitview-nav > li hgroup.stylesheet-stats,
.splitview-nav > li hgroup.stylesheet-actions {
position: absolute;
z-index: 2;
-moz-transition-property: left, right;
-moz-transition-duration: 0.2s;
-moz-transition-delay: 0.2s;
-moz-transition-timing-function: ease-in-out;
}
.splitview-nav > li hgroup.stylesheet-stats {
z-index: 1;
-moz-transition-delay: 0.4s;
color: GrayText;
padding-left: 6px; /* Fitts */
padding-right: 6px;
}
.splitview-nav > li hgroup.stylesheet-actions a {
color: ButtonText;
padding-left: 6px; /* Fitts */
padding-right: 6px;
}
.splitview-nav > li hgroup.stylesheet-actions:-moz-locale-dir(ltr),
.splitview-nav > li:hover hgroup.stylesheet-stats:-moz-locale-dir(ltr),
.splitview-nav > li:focus hgroup.stylesheet-stats:-moz-locale-dir(ltr),
.splitview-nav > li.splitview-active hgroup.stylesheet-stats:-moz-locale-dir(ltr) {
right: -50ex;
}
.splitview-nav > li hgroup.stylesheet-actions:-moz-locale-dir(rtl),
.splitview-nav > li:hover hgroup.stylesheet-stats:-moz-locale-dir(rtl),
.splitview-nav > li:focus hgroup.stylesheet-stats:-moz-locale-dir(rtl),
.splitview-nav > li.splitview-active hgroup.stylesheet-stats:-moz-locale-dir(rtl) {
left: -50ex;
}
.splitview-nav > li hgroup.stylesheet-stats:-moz-locale-dir(ltr),
.splitview-nav > li:hover hgroup.stylesheet-actions:-moz-locale-dir(ltr),
.splitview-nav > li:focus hgroup.stylesheet-actions:-moz-locale-dir(ltr),
.splitview-nav > li.splitview-active hgroup.stylesheet-actions:-moz-locale-dir(ltr) {
right: 0;
}
.splitview-nav > li hgroup.stylesheet-stats:-moz-locale-dir(rtl),
.splitview-nav > li:hover hgroup.stylesheet-actions:-moz-locale-dir(rtl),
.splitview-nav > li:focus hgroup.stylesheet-actions:-moz-locale-dir(rtl),
.splitview-nav > li.splitview-active hgroup.stylesheet-actions:-moz-locale-dir(rtl) {
left: 0;
}
.stylesheet-editor-input {
display: -moz-box;
-moz-box-flex: 1;
overflow: hidden;
min-height: 8em;
margin: 3px;
margin-top: 0;
border: 1px inset WindowFrame;
border-radius: 4px;
background-color: -moz-default-background-color;
}
h1,
h2,
h3 {
font-size: inherit;
font-weight: normal;
margin: 0;
padding: 0;
}

View File

@ -135,7 +135,9 @@ browser.jar:
skin/classic/browser/devtools/breadcrumbs/rtl-start-selected-pressed.png (devtools/breadcrumbs/rtl-start-selected-pressed.png)
skin/classic/browser/devtools/breadcrumbs/rtl-start.png (devtools/breadcrumbs/rtl-start.png)
skin/classic/browser/devtools/breadcrumbs/rtl-start-selected.png (devtools/breadcrumbs/rtl-start-selected.png)
skin/classic/browser/devtools/splitview.css (devtools/splitview.css)
skin/classic/browser/devtools/styleeditor.css (devtools/styleeditor.css)
skin/classic/browser/devtools/eye-toggle.png (devtools/eye-toggle.png)
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/sync-throbber.png
skin/classic/browser/sync-16.png
@ -287,7 +289,9 @@ browser.jar:
skin/classic/aero/browser/devtools/breadcrumbs/rtl-start-selected-pressed.png (devtools/breadcrumbs/rtl-start-selected-pressed.png)
skin/classic/aero/browser/devtools/breadcrumbs/rtl-start.png (devtools/breadcrumbs/rtl-start.png)
skin/classic/aero/browser/devtools/breadcrumbs/rtl-start-selected.png (devtools/breadcrumbs/rtl-start-selected.png)
skin/classic/aero/browser/devtools/splitview.css (devtools/splitview.css)
skin/classic/aero/browser/devtools/styleeditor.css (devtools/styleeditor.css)
skin/classic/aero/browser/devtools/eye-toggle.png (devtools/eye-toggle.png)
#ifdef MOZ_SERVICES_SYNC
skin/classic/aero/browser/sync-throbber.png
skin/classic/aero/browser/sync-16.png

View File

@ -809,8 +809,6 @@ function loadManifestFromRDF(aUri, aStream) {
addon.softDisabled = addon.blocklistState == Ci.nsIBlocklistService.STATE_SOFTBLOCKED;
}
addon.appDisabled = !isUsableAddon(addon);
addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;
return addon;
@ -865,6 +863,7 @@ function loadManifestFromDir(aDir) {
addon.hasBinaryComponents = ChromeManifestParser.hasType(chromeManifest,
"binary-component");
addon.appDisabled = !isUsableAddon(addon);
return addon;
}
finally {
@ -907,6 +906,7 @@ function loadManifestFromZipReader(aZipReader) {
addon.hasBinaryComponents = false;
}
addon.appDisabled = !isUsableAddon(addon);
return addon;
}
finally {

View File

@ -0,0 +1 @@
binary-component components/mycomponent.so

View File

@ -0,0 +1,26 @@
<?xml version="1.0"?>
<!-- An extension that is incompatible with the XPCShell test suite and
has binary components, so won't be compatible-by-default. -->
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>addon5@tests.mozilla.org</em:id>
<em:version>1.0</em:version>
<!-- Front End MetaData -->
<em:name>Real Test 5</em:name>
<em:description>Test Description</em:description>
<em:unpack>true</em:unpack>
<em:targetApplication>
<Description>
<em:id>xpcshell@tests.mozilla.org</em:id>
<em:minVersion>0</em:minVersion>
<em:maxVersion>0</em:maxVersion>
</Description>
</em:targetApplication>
</Description>
</RDF>

View File

@ -842,7 +842,11 @@ const InstallListener = {
},
onInstallCancelled: function(install) {
do_check_eq(install.state, AddonManager.STATE_CANCELLED);
// If the install was cancelled by a listener returning false from
// onInstallStarted, then the state will revert to STATE_DOWNLOADED.
let possibleStates = [AddonManager.STATE_CANCELLED,
AddonManager.STATE_DOWNLOADED];
do_check_true(possibleStates.indexOf(install.state) != -1);
do_check_eq(install.error, 0);
do_check_eq("onInstallCancelled", getExpectedInstall(install.addon));
return check_test_completed(arguments);

View File

@ -1618,5 +1618,58 @@ function finish_test_27(aInstall) {
ensure_test_completed();
do_test_finished();
run_test_28();
}
// Tests that an install that isn't strictly compatible and has
// binary components correctly has appDisabled set (see bug 702868).
function run_test_28() {
prepare_test({ }, [
"onNewInstall"
]);
let url = "http://localhost:4444/addons/test_install5.xpi";
AddonManager.getInstallForURL(url, function(install) {
ensure_test_completed();
do_check_neq(install, null);
do_check_eq(install.version, "1.0");
do_check_eq(install.name, "Real Test 5");
do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
AddonManager.getInstallsByTypes(null, function(activeInstalls) {
do_check_eq(activeInstalls.length, 1);
do_check_eq(activeInstalls[0], install);
prepare_test({}, [
"onDownloadStarted",
"onDownloadEnded",
"onInstallStarted"
], check_test_28);
install.install();
});
}, "application/x-xpinstall", null, "Real Test 5", null, "1.0");
}
function check_test_28(install) {
ensure_test_completed();
do_check_eq(install.version, "1.0");
do_check_eq(install.name, "Real Test 5");
do_check_eq(install.state, AddonManager.STATE_INSTALLING);
do_check_eq(install.existingAddon, null);
do_check_false(install.addon.isCompatible);
do_check_true(install.addon.appDisabled);
prepare_test({}, [
"onInstallCancelled"
], finish_test_28);
return false;
}
function finish_test_28(install) {
prepare_test({}, [
"onDownloadCancelled"
], do_test_finished);
install.cancel();
}

View File

@ -155,6 +155,10 @@
display: none;
}
.webconsole-filter-button {
margin: 0 3px;
}
.webconsole-filter-button > .toolbarbutton-menubutton-button,
.webconsole-filter-button:not([type="menu-button"]) {
-moz-box-orient: horizontal;