Merge mozilla-central to mozilla-inbound

This commit is contained in:
Ed Morley 2011-11-07 22:59:01 +00:00
commit 3ea000b341
28 changed files with 1517 additions and 448 deletions

View File

@ -1004,6 +1004,7 @@ pref("devtools.errorconsole.enabled", false);
// Enable the Inspector
pref("devtools.inspector.enabled", true);
pref("devtools.inspector.htmlHeight", 112);
// Enable the style inspector
pref("devtools.styleinspector.enabled", true);

View File

@ -1063,6 +1063,10 @@
<svg:rect x="0" y="0" width="1" height="1" fill="white"/>
<svg:circle cx="-0.41" cy="0.5" r="0.65"/>
</svg:mask>
<svg:mask id="pinstripe-urlbar-back-button-mask" maskContentUnits="userSpaceOnUse">
<svg:rect x="0" y="-5" width="10000" height="55" fill="white"/>
<svg:circle cx="-9" cy="11" r="15"/>
</svg:mask>
<svg:mask id="pinstripe-tab-ontop-left-curve-mask" maskContentUnits="userSpaceOnUse">
<svg:circle cx="9" cy="3" r="3" fill="white"/>
<svg:rect x="9" y="0" width="3" height="3" fill="white"/>

View File

@ -227,7 +227,14 @@ TreePanel.prototype = {
treeBox = this.document.createElement("vbox");
treeBox.id = "inspector-tree-box";
treeBox.state = "open"; // for the registerTools API.
treeBox.minHeight = 10;
try {
treeBox.height =
Services.prefs.getIntPref("devtools.inspector.htmlHeight");
} catch(e) {
treeBox.height = 112;
}
treeBox.minHeight = 64;
treeBox.flex = 1;
toolbarParent.insertBefore(treeBox, toolbar);
@ -262,6 +269,7 @@ TreePanel.prototype = {
this.IUI.toolbar.removeAttribute("treepanel-open");
let treeBox = this.container;
Services.prefs.setIntPref("devtools.inspector.htmlHeight", treeBox.height);
let treeBoxParent = treeBox.parentNode;
treeBoxParent.removeChild(treeBox);
} else {
@ -577,6 +585,7 @@ TreePanel.prototype = {
this.editingContext.attrObj.innerHTML = editorInput.value;
this.IUI.isDirty = true;
this.IUI.nodeChanged(this.registrationObject);
// event notification
Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.EDITOR_SAVED,

View File

@ -1158,6 +1158,19 @@ InspectorUI.prototype = {
this.toolsSelect(aScroll);
},
/**
* Called when the highlighted node is changed by a tool.
*
* @param object aUpdater
* The tool that triggered the update (if any), that tool's
* onChanged will not be called.
*/
nodeChanged: function IUI_nodeChanged(aUpdater)
{
this.highlighter.highlight();
this.toolsOnChanged(aUpdater);
},
/////////////////////////////////////////////////////////////////////////
//// Event Handling
@ -1336,10 +1349,28 @@ InspectorUI.prototype = {
openRuleView: function IUI_openRuleView()
{
let iframe = this.getToolIframe(this.ruleViewObject);
if (iframe.getAttribute("src")) {
// We're already loading this tool, let it finish.
return;
}
let boundLoadListener = function() {
iframe.removeEventListener("load", boundLoadListener, true);
let doc = iframe.contentDocument;
this.ruleView = new CssRuleView(doc);
let winID = this.winID;
let ruleViewStore = this.store.getValue(winID, "ruleView");
if (!ruleViewStore) {
ruleViewStore = {};
this.store.setValue(winID, "ruleView", ruleViewStore);
}
this.ruleView = new CssRuleView(doc, ruleViewStore);
this.boundRuleViewChanged = this.ruleViewChanged.bind(this);
this.ruleView.element.addEventListener("CssRuleViewChanged",
this.boundRuleViewChanged);
doc.documentElement.appendChild(this.ruleView.element);
this.ruleView.highlight(this.selection);
Services.obs.notifyObservers(null,
@ -1370,6 +1401,12 @@ InspectorUI.prototype = {
this.ruleView.highlight(aNode);
},
ruleViewChanged: function IUI_ruleViewChanged()
{
this.isDirty = true;
this.nodeChanged(this.ruleViewObject);
},
/**
* Destroy the rule view.
*/
@ -1379,6 +1416,9 @@ InspectorUI.prototype = {
iframe.parentNode.removeChild(iframe);
if (this.ruleView) {
this.ruleView.element.removeEventListener("CssRuleViewChanged",
this.boundRuleViewChanged);
delete boundRuleViewChanged;
this.ruleView.clear();
delete this.ruleView;
}
@ -1685,8 +1725,6 @@ InspectorUI.prototype = {
*/
toolShow: function IUI_toolShow(aTool)
{
aTool.show.call(aTool.context, this.selection);
let btn = this.chromeDoc.getElementById(this.getToolbarButtonId(aTool.id));
btn.setAttribute("checked", "true");
if (aTool.sidebar) {
@ -1697,6 +1735,8 @@ InspectorUI.prototype = {
this.getToolbarButtonId(other.id)).removeAttribute("checked");
}.bind(this));
}
aTool.show.call(aTool.context, this.selection);
},
/**
@ -1848,13 +1888,29 @@ InspectorUI.prototype = {
*/
toolsDim: function IUI_toolsDim(aState)
{
this.toolsDo(function IUI_toolsOnSelect(aTool) {
this.toolsDo(function IUI_toolsDim(aTool) {
if (aTool.isOpen && "dim" in aTool) {
aTool.dim.call(aTool.context, aState);
}
});
},
/**
* Notify registered tools of changes to the highlighted element.
*
* @param object aUpdater
* The tool that triggered the update (if any), that tool's
* onChanged will not be called.
*/
toolsOnChanged: function IUI_toolsChanged(aUpdater)
{
this.toolsDo(function IUI_toolsOnChanged(aTool) {
if (aTool.isOpen && ("onChanged" in aTool) && aTool != aUpdater) {
aTool.onChanged.call(aTool.context);
}
});
},
/**
* Loop through all registered tools and pass each into the provided function
* @param aFunction The function to which each tool is to be passed
@ -2062,8 +2118,13 @@ InspectorProgressListener.prototype = {
return;
}
// Skip non-start states.
if (!(aFlag & Ci.nsIWebProgressListener.STATE_START)) {
let isStart = aFlag & Ci.nsIWebProgressListener.STATE_START;
let isDocument = aFlag & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
let isNetwork = aFlag & Ci.nsIWebProgressListener.STATE_IS_NETWORK;
let isRequest = aFlag & Ci.nsIWebProgressListener.STATE_IS_REQUEST;
// Skip non-interesting states.
if (!isStart || !isDocument || !isRequest || !isNetwork) {
return;
}

View File

@ -65,6 +65,10 @@ _BROWSER_FILES = \
browser_inspector_keybindings.js \
browser_inspector_breadcrumbs.html \
browser_inspector_breadcrumbs.js \
browser_inspector_bug_699308_iframe_navigation.js \
browser_inspector_changes.js \
browser_inspector_ruleviewstore.js \
browser_inspector_duplicate_ruleview.js \
$(NULL)
# Disabled due to constant failures

View File

@ -0,0 +1,77 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let iframe;
let iframeLoads = 0;
let checksAfterLoads = false;
function startTest() {
ok(window.InspectorUI, "InspectorUI variable exists");
Services.obs.addObserver(runInspectorTests,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, null);
InspectorUI.toggleInspectorUI();
}
function runInspectorTests() {
Services.obs.removeObserver(runInspectorTests,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, null);
iframe = content.document.querySelector("iframe");
ok(iframe, "found the iframe element");
ok(InspectorUI.inspecting, "Inspector is highlighting");
ok(InspectorUI.isInspectorOpen, "Inspector is open");
Services.obs.addObserver(finishTest,
InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
iframe.addEventListener("load", onIframeLoad, false);
executeSoon(function() {
iframe.contentWindow.location = "javascript:location.reload()";
});
}
function onIframeLoad() {
if (++iframeLoads != 2) {
executeSoon(function() {
iframe.contentWindow.location = "javascript:location.reload()";
});
return;
}
iframe.removeEventListener("load", onIframeLoad, false);
ok(InspectorUI.inspecting, "Inspector is highlighting after iframe nav");
ok(InspectorUI.isInspectorOpen, "Inspector Panel is open after iframe nav");
checksAfterLoads = true;
InspectorUI.closeInspectorUI();
}
function finishTest() {
Services.obs.removeObserver(finishTest,
InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED);
is(iframeLoads, 2, "iframe loads");
ok(checksAfterLoads, "the Inspector tests got the chance to run after iframe reloads");
ok(!InspectorUI.isInspectorOpen, "Inspector Panel is not open");
iframe = null;
gBrowser.removeCurrentTab();
executeSoon(finish);
}
function test() {
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onBrowserLoad() {
gBrowser.selectedBrowser.removeEventListener("load", onBrowserLoad, true);
waitForFocus(startTest, content);
}, true);
content.location = "data:text/html,<p>bug 699308 - test iframe navigation" +
"<iframe src='data:text/html,hello world'></iframe>";
}

View File

@ -0,0 +1,158 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* ***** 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 Inspect Tests.
*
* 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):
* Dave Camp <dcamp@mozilla.com>
* Rob Campbell <rcampbell@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 ***** */
let doc;
let testDiv;
function createDocument()
{
doc.body.innerHTML = '<div id="testdiv">Test div!</div>';
doc.title = "Inspector Change Test";
startInspectorTests();
}
function getInspectorProp(aName)
{
for each (let view in InspectorUI.stylePanel.cssHtmlTree.propertyViews) {
if (view.name == aName) {
return view;
}
}
return null;
}
function startInspectorTests()
{
ok(InspectorUI, "InspectorUI variable exists");
Services.obs.addObserver(runInspectorTests,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
InspectorUI.toggleInspectorUI();
}
function runInspectorTests()
{
Services.obs.removeObserver(runInspectorTests, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
testDiv = doc.getElementById("testdiv");
testDiv.style.fontSize = "10px";
InspectorUI.inspectNode(testDiv);
InspectorUI.stopInspecting();
// Start up the style inspector panel...
Services.obs.addObserver(stylePanelTests, "StyleInspector-populated", false);
executeSoon(function() {
InspectorUI.showSidebar();
document.getElementById(InspectorUI.getToolbarButtonId("styleinspector")).click();
});
}
function stylePanelTests()
{
Services.obs.removeObserver(stylePanelTests, "StyleInspector-populated");
ok(InspectorUI.isSidebarOpen, "Inspector Sidebar is open");
ok(InspectorUI.stylePanel.cssHtmlTree, "Style Panel has a cssHtmlTree");
let propView = getInspectorProp("font-size");
is(propView.value, "10px", "Style inspector should be showing the correct font size.");
Services.obs.addObserver(stylePanelAfterChange, "StyleInspector-populated", false);
testDiv.style.fontSize = "15px";
InspectorUI.nodeChanged();
}
function stylePanelAfterChange()
{
Services.obs.removeObserver(stylePanelAfterChange, "StyleInspector-populated");
let propView = getInspectorProp("font-size");
is(propView.value, "15px", "Style inspector should be showing the new font size.");
stylePanelNotActive();
}
function stylePanelNotActive()
{
// Tests changes made while the style panel is not active.
InspectorUI.ruleButton.click();
executeSoon(function() {
testDiv.style.fontSize = "20px";
Services.obs.addObserver(stylePanelAfterSwitch, "StyleInspector-populated", false);
document.getElementById(InspectorUI.getToolbarButtonId("styleinspector")).click();
});
}
function stylePanelAfterSwitch()
{
Services.obs.removeObserver(stylePanelAfterSwitch, "StyleInspector-populated");
let propView = getInspectorProp("font-size");
is(propView.value, "20px", "Style inspector should be showing the newest font size.");
Services.obs.addObserver(finishTest, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
executeSoon(function() {
InspectorUI.closeInspectorUI(true);
});
}
function finishTest()
{
Services.obs.removeObserver(finishTest,
InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED);
gBrowser.removeCurrentTab();
finish();
}
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
doc = content.document;
waitForFocus(createDocument, content);
}, true);
content.location = "data:text/html,basic tests for inspector";
}

View File

@ -0,0 +1,126 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let div;
let tab1;
let tab2;
let tab1window;
function inspectorTabOpen1()
{
ok(window.InspectorUI, "InspectorUI variable exists");
ok(!InspectorUI.inspecting, "Inspector is not highlighting");
ok(InspectorUI.store.isEmpty(), "Inspector.store is empty");
Services.obs.addObserver(inspectorUIOpen1,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
InspectorUI.openInspectorUI();
}
function inspectorUIOpen1()
{
Services.obs.removeObserver(inspectorUIOpen1,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
// Make sure the inspector is open.
ok(InspectorUI.inspecting, "Inspector is highlighting");
ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
ok(!InspectorUI.isSidebarOpen, "Inspector Sidebar is not open");
ok(!InspectorUI.store.isEmpty(), "InspectorUI.store is not empty");
is(InspectorUI.store.length, 1, "Inspector.store.length = 1");
// Highlight a node.
div = content.document.getElementsByTagName("div")[0];
InspectorUI.inspectNode(div);
is(InspectorUI.selection, div, "selection matches the div element");
Services.obs.addObserver(inspectorRuleViewOpened,
InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false);
InspectorUI.showSidebar();
InspectorUI.openRuleView();
}
function inspectorRuleViewOpened() {
Services.obs.removeObserver(inspectorRuleViewOpened,
InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY);
// Open the second tab.
tab2 = gBrowser.addTab();
gBrowser.selectedTab = tab2;
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee,
true);
waitForFocus(inspectorTabOpen2, content);
}, true);
content.location = "data:text/html,<p>tab 2: the inspector should close now";
}
function inspectorTabOpen2()
{
// Make sure the inspector is closed.
ok(!InspectorUI.inspecting, "Inspector is not highlighting");
ok(!InspectorUI.treePanel, "Inspector Tree Panel is closed");
ok(!InspectorUI.isSidebarOpen, "Inspector Sidebar is not open");
is(InspectorUI.store.length, 1, "Inspector.store.length = 1");
Services.obs.addObserver(inspectorFocusTab1,
InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false);
// Switch back to tab 1.
executeSoon(function() {
gBrowser.selectedTab = tab1;
});
}
function inspectorFocusTab1()
{
Services.obs.removeObserver(inspectorFocusTab1,
InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false);
Services.obs.addObserver(inspectorRuleTrap,
InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false);
// Make sure the inspector is open.
ok(InspectorUI.inspecting, "Inspector is highlighting");
ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
is(InspectorUI.store.length, 1, "Inspector.store.length = 1");
is(InspectorUI.selection, div, "selection matches the div element");
ok(InspectorUI.isSidebarOpen, "sidebar is open");
ok(InspectorUI.isRuleViewOpen(), "rule view is open");
is(InspectorUI.ruleView.doc.documentElement.children.length, 1, "RuleView elements.length == 1");
requestLongerTimeout(4);
executeSoon(function() {
InspectorUI.closeInspectorUI();
gBrowser.removeCurrentTab(); // tab 1
gBrowser.removeCurrentTab(); // tab 2
finish();
});
}
function inspectorRuleTrap()
{
Services.obs.removeObserver(inspectorRuleTrap,
InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false);
is(InspectorUI.ruleView.doc.documentElement.children.length, 1, "RuleView elements.length == 1");
}
function test()
{
waitForExplicitFinish();
tab1 = gBrowser.addTab();
gBrowser.selectedTab = tab1;
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee,
true);
waitForFocus(inspectorTabOpen1, content);
}, true);
content.location = "data:text/html,<p>tab switching tests for inspector" +
"<div>tab 1</div>";
}

View File

@ -0,0 +1,152 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* ***** 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 Inspector Tab Switch Tests.
*
* 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):
* Rob Campbell <rcampbell@mozilla.com>
* Mihai Șucan <mihai.sucan@gmail.com>
* Dave Camp <dcamp@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 properties disabled in the rule view survive a tab switch.
*/
let div;
let tab1;
function waitForRuleView(aCallback)
{
if (InspectorUI.ruleView) {
aCallback();
return;
}
let ruleViewFrame = InspectorUI.getToolIframe(InspectorUI.ruleViewObject);
ruleViewFrame.addEventListener("load", function(evt) {
ruleViewFrame.removeEventListener(evt.type, arguments.callee, true);
executeSoon(function() {
aCallback();
});
}, true);
}
function inspectorTabOpen1()
{
Services.obs.addObserver(inspectorUIOpen1,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
InspectorUI.openInspectorUI();
}
function inspectorUIOpen1()
{
Services.obs.removeObserver(inspectorUIOpen1,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
// Highlight a node.
div = content.document.getElementsByTagName("div")[0];
InspectorUI.inspectNode(div);
// Open the rule view sidebar.
waitForRuleView(ruleViewOpened1);
InspectorUI.showSidebar();
InspectorUI.ruleButton.click();
}
function ruleViewOpened1()
{
let prop = InspectorUI.ruleView._elementStyle.rules[0].textProps[0];
is(prop.name, "background-color", "First prop is the background color prop.");
prop.setEnabled(false);
// Open second tab and switch to it
tab2 = gBrowser.addTab();
gBrowser.selectedTab = tab2;
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee,
true);
waitForFocus(inspectorTabOpen2, content);
}, true);
content.location = "data:text/html,<p>tab 2: the inspector should close now";
}
function inspectorTabOpen2()
{
// Switch back to tab 1.
executeSoon(function() {
Services.obs.addObserver(inspectorFocusTab1,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
gBrowser.removeCurrentTab();
gBrowser.selectedTab = tab1;
});
}
function inspectorFocusTab1()
{
Services.obs.removeObserver(inspectorFocusTab1,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
// Now wait for the rule view to load again...
waitForRuleView(ruleViewOpened2);
}
function ruleViewOpened2()
{
let prop = InspectorUI.ruleView._elementStyle.rules[0].textProps[0];
is(prop.name, "background-color", "First prop is the background color prop.");
ok(!prop.enabled, "First prop should be disabled.");
gBrowser.removeCurrentTab();
InspectorUI.closeInspectorUI();
finish();
}
function test()
{
waitForExplicitFinish();
tab1 = gBrowser.addTab();
gBrowser.selectedTab = tab1;
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee,
true);
waitForFocus(inspectorTabOpen1, content);
}, true);
content.location = "data:text/html,<p>tab switching tests for inspector" +
'<div style="background-color: green;">tab 1</div>';
}

View File

@ -0,0 +1,144 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* ***** 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 Inspector Tests.
*
* 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):
* Rob Campbell <rcampbell@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 ***** */
let doc;
let salutation;
let closing;
const NEWHEIGHT = 226;
function createDocument()
{
doc.body.innerHTML = '<div id="first" style="{ margin: 10em; ' +
'font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA}">\n' +
'<h1>Some header text</h1>\n' +
'<p id="salutation" style="{font-size: 12pt}">hi.</p>\n' +
'<p id="body" style="{font-size: 12pt}">I am a test-case. This text exists ' +
'solely to provide some things to test the inspector initialization.</p>\n' +
'If you are reading this, you should go do something else instead. Maybe ' +
'read a book. Or better yet, write some test-cases for another bit of code. ' +
'<span style="{font-style: italic}">Maybe more inspector test-cases!</span></p>\n' +
'<p id="closing">end transmission</p>\n' +
'</div>';
doc.title = "Inspector Initialization Test";
startInspectorTests();
}
function startInspectorTests()
{
ok(InspectorUI, "InspectorUI variable exists");
Services.obs.addObserver(runInspectorTests,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
InspectorUI.toggleInspectorUI();
}
function runInspectorTests()
{
Services.obs.removeObserver(runInspectorTests,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
if (InspectorUI.treePanelEnabled) {
Services.obs.addObserver(treePanelTests,
InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
InspectorUI.stopInspecting();
InspectorUI.treePanel.open();
} else
finishInspectorTests();
}
function treePanelTests()
{
Services.obs.removeObserver(treePanelTests,
InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
Services.obs.addObserver(treePanelTests2,
InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open");
let height = Services.prefs.getIntPref("devtools.inspector.htmlHeight");
is(InspectorUI.treePanel.container.height, height,
"Container height is " + height);
InspectorUI.treePanel.container.height = NEWHEIGHT;
executeSoon(function() {
InspectorUI.treePanel.close();
InspectorUI.treePanel.open();
});
}
function treePanelTests2()
{
Services.obs.removeObserver(treePanelTests2,
InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open");
let height = Services.prefs.getIntPref("devtools.inspector.htmlHeight");
is(InspectorUI.treePanel.container.height, NEWHEIGHT,
"Container height is now " + height);
InspectorUI.treePanel.close();
executeSoon(function() {
finishInspectorTests()
});
}
function finishInspectorTests()
{
gBrowser.removeCurrentTab();
finish();
}
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
doc = content.document;
waitForFocus(createDocument, content);
}, true);
content.location = "data:text/html,basic tests for inspector";
}

View File

@ -43,6 +43,9 @@
const Cu = Components.utils;
const FILTER_CHANGED_TIMEOUT = 300;
const HTML_NS = "http://www.w3.org/1999/xhtml";
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/PluralForm.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -51,6 +54,94 @@ Cu.import("resource:///modules/devtools/Templater.jsm");
var EXPORTED_SYMBOLS = ["CssHtmlTree", "PropertyView"];
/**
* Helper for long-running processes that should yield occasionally to
* the mainloop.
*
* @param {Window} aWin
* Timeouts will be set on this window when appropriate.
* @param {Generator} aGenerator
* Will iterate this generator.
* @param {object} aOptions
* Options for the update process:
* onItem {function} Will be called with the value of each iteration.
* onBatch {function} Will be called after each batch of iterations,
* before yielding to the main loop.
* onDone {function} Will be called when iteration is complete.
* onCancel {function} Will be called if the process is canceled.
* threshold {int} How long to process before yielding, in ms.
*
* @constructor
*/
function UpdateProcess(aWin, aGenerator, aOptions)
{
this.win = aWin;
this.iter = Iterator(aGenerator);
this.onItem = aOptions.onItem || function() {};
this.onBatch = aOptions.onBatch || function () {};
this.onDone = aOptions.onDone || function() {};
this.onCancel = aOptions.onCancel || function() {};
this.threshold = aOptions.threshold || 45;
this.canceled = false;
}
UpdateProcess.prototype = {
/**
* Schedule a new batch on the main loop.
*/
schedule: function UP_schedule()
{
if (this.cancelled) {
return;
}
this._timeout = this.win.setTimeout(this._timeoutHandler.bind(this), 0);
},
/**
* Cancel the running process. onItem will not be called again,
* and onCancel will be called.
*/
cancel: function UP_cancel()
{
if (this._timeout) {
this.win.clearTimeout(this._timeout);
this._timeout = 0;
}
this.canceled = true;
this.onCancel();
},
_timeoutHandler: function UP_timeoutHandler() {
this._timeout = null;
try {
this._runBatch();
this.schedule();
} catch(e) {
if (e instanceof StopIteration) {
this.onBatch();
this.onDone();
return;
}
throw e;
}
},
_runBatch: function Y_runBatch()
{
let time = Date.now();
while(!this.cancelled) {
// Continue until iter.next() throws...
let next = this.iter.next();
this.onItem(next[1]);
if ((Date.now() - time) > this.threshold) {
this.onBatch();
return;
}
}
}
};
/**
* CssHtmlTree is a panel that manages the display of a table sorted by style.
* There should be one instance of CssHtmlTree per style display (of which there
@ -74,13 +165,13 @@ function CssHtmlTree(aStyleInspector)
// Nodes used in templating
this.root = this.styleDocument.getElementById("root");
this.path = this.styleDocument.getElementById("path");
this.templateRoot = this.styleDocument.getElementById("templateRoot");
this.templatePath = this.styleDocument.getElementById("templatePath");
this.propertyContainer = this.styleDocument.getElementById("propertyContainer");
this.templateProperty = this.styleDocument.getElementById("templateProperty");
this.panel = aStyleInspector.panel;
// No results text.
this.noResults = this.styleDocument.getElementById("noResults");
// The element that we're inspecting, and the document that it comes from.
this.viewedElement = null;
this.createStyleViews();
@ -132,6 +223,10 @@ CssHtmlTree.processTemplate = function CssHtmlTree_processTemplate(aTemplate,
XPCOMUtils.defineLazyGetter(CssHtmlTree, "_strings", function() Services.strings
.createBundle("chrome://browser/locale/devtools/styleinspector.properties"));
XPCOMUtils.defineLazyGetter(CssHtmlTree, "HELP_LINK_TITLE", function() {
return CssHtmlTree.HELP_LINK_TITLE = CssHtmlTree.l10n("helpLinkTitle");
});
CssHtmlTree.prototype = {
// Cache the list of properties that have matched and unmatched properties.
_matchedProperties: null,
@ -154,6 +249,9 @@ CssHtmlTree.prototype = {
// Toggle for zebra striping
_darkStripe: true,
// Number of visible properties
numVisibleProperties: 0,
get showOnlyUserStyles()
{
return this.onlyUserStylesCheckbox.checked;
@ -166,55 +264,45 @@ CssHtmlTree.prototype = {
*/
highlight: function CssHtmlTree_highlight(aElement)
{
if (this.viewedElement == aElement) {
return;
}
this.viewedElement = aElement;
this._unmatchedProperties = null;
this._matchedProperties = null;
CssHtmlTree.processTemplate(this.templatePath, this.path, this);
if (this.htmlComplete) {
this.refreshPanel();
} else {
if (this._panelRefreshTimeout) {
this.win.clearTimeout(this._panelRefreshTimeout);
if (this._refreshProcess) {
this._refreshProcess.cancel();
}
CssHtmlTree.processTemplate(this.templateRoot, this.root, this);
// We use a setTimeout loop to display the properties in batches of 15 at a
// time. This results in a perceptibly more responsive UI.
let i = 0;
let batchSize = 15;
let max = CssHtmlTree.propertyNames.length - 1;
function displayProperties() {
if (this.viewedElement == aElement && this.styleInspector.isOpen()) {
// Display the next 15 properties
for (let step = i + batchSize; i < step && i <= max; i++) {
let name = CssHtmlTree.propertyNames[i];
let propView = new PropertyView(this, name);
CssHtmlTree.processTemplate(this.templateProperty,
this.propertyContainer, propView, true);
propView.refreshAllSelectors();
this.propertyViews.push(propView);
this.numVisibleProperties = 0;
let fragment = this.doc.createDocumentFragment();
this._refreshProcess = new UpdateProcess(this.win, CssHtmlTree.propertyNames, {
onItem: function(aPropertyName) {
// Per-item callback.
if (this.viewedElement != aElement || !this.styleInspector.isOpen()) {
return false;
}
if (i < max) {
// There are still some properties to display. We loop here to display
// the next batch of 15.
this._panelRefreshTimeout =
this.win.setTimeout(displayProperties.bind(this), 15);
} else {
this.htmlComplete = true;
this._panelRefreshTimeout = null;
Services.obs.notifyObservers(null, "StyleInspector-populated", null);
let propView = new PropertyView(this, aPropertyName);
fragment.appendChild(propView.build());
if (propView.visible) {
this.numVisibleProperties++;
}
}
}
this._panelRefreshTimeout =
this.win.setTimeout(displayProperties.bind(this), 15);
propView.refreshAllSelectors();
this.propertyViews.push(propView);
}.bind(this),
onDone: function() {
// Completed callback.
this.htmlComplete = true;
this.propertyContainer.appendChild(fragment);
this.noResults.hidden = this.numVisibleProperties > 0;
this._refreshProcess = null;
Services.obs.notifyObservers(null, "StyleInspector-populated", null);
}.bind(this)});
this._refreshProcess.schedule();
}
},
@ -223,47 +311,30 @@ CssHtmlTree.prototype = {
*/
refreshPanel: function CssHtmlTree_refreshPanel()
{
if (this._panelRefreshTimeout) {
this.win.clearTimeout(this._panelRefreshTimeout);
if (this._refreshProcess) {
this._refreshProcess.cancel();
}
this.noResults.hidden = true;
// Reset visible property count
this.numVisibleProperties = 0;
// Reset zebra striping.
this._darkStripe = true;
// We use a setTimeout loop to display the properties in batches of 15 at a
// time. This results in a perceptibly more responsive UI.
let i = 0;
let batchSize = 15;
let max = this.propertyViews.length - 1;
function refreshView() {
// Refresh the next 15 property views
for (let step = i + batchSize; i < step && i <= max; i++) {
this.propertyViews[i].refresh();
}
if (i < max) {
// There are still some property views to refresh. We loop here to
// display the next batch of 15.
this._panelRefreshTimeout = this.win.setTimeout(refreshView.bind(this), 15);
} else {
this._panelRefreshTimeout = null;
let display = this.propertyContainer.style.display;
this._refreshProcess = new UpdateProcess(this.win, this.propertyViews, {
onItem: function(aPropView) {
aPropView.refresh();
}.bind(this),
onDone: function() {
this._refreshProcess = null;
this.noResults.hidden = this.numVisibleProperties > 0
Services.obs.notifyObservers(null, "StyleInspector-populated", null);
}
}
this._panelRefreshTimeout = this.win.setTimeout(refreshView.bind(this), 15);
},
/**
* Called when the user clicks on a parent element in the "current element"
* path.
*
* @param {Event} aEvent the DOM Event object.
*/
pathClick: function CssHtmlTree_pathClick(aEvent)
{
aEvent.preventDefault();
if (aEvent.target && this.viewedElement != aEvent.target.pathElement) {
this.styleInspector.selectFromPath(aEvent.target.pathElement);
}
}.bind(this)
});
this._refreshProcess.schedule();
},
/**
@ -303,18 +374,6 @@ CssHtmlTree.prototype = {
this.refreshPanel();
},
/**
* Provide access to the path to get from document.body to the selected
* element.
*
* @return {array} the array holding the path from document.body to the
* selected element.
*/
get pathElements()
{
return CssLogic.getShortNamePath(this.viewedElement);
},
/**
* The CSS as displayed by the UI.
*/
@ -408,10 +467,7 @@ CssHtmlTree.prototype = {
// Nodes used in templating
delete this.root;
delete this.path;
delete this.templatePath;
delete this.propertyContainer;
delete this.templateProperty;
delete this.panel;
// The document in which we display the results (csshtmltree.xul).
@ -444,7 +500,6 @@ function PropertyView(aTree, aName)
this.link = "https://developer.mozilla.org/en/CSS/" + aName;
this.templateMatchedSelectors = aTree.styleDocument.getElementById("templateMatchedSelectors");
this.templateUnmatchedSelectors = aTree.styleDocument.getElementById("templateUnmatchedSelectors");
}
PropertyView.prototype = {
@ -514,7 +569,7 @@ PropertyView.prototype = {
*/
get hasMatchedSelectors()
{
return this.tree.matchedProperties[this.name];
return this.name in this.tree.matchedProperties;
},
/**
@ -522,7 +577,7 @@ PropertyView.prototype = {
*/
get hasUnmatchedSelectors()
{
return this.tree.hasUnmatchedSelectors(this.name);
return this.name in this.tree.hasUnmatchedSelectors;
},
/**
@ -559,6 +614,50 @@ PropertyView.prototype = {
return "property-view-hidden";
},
build: function PropertyView_build()
{
let doc = this.tree.doc;
this.element = doc.createElementNS(HTML_NS, "div");
this.element.setAttribute("class", this.className);
this.propertyHeader = doc.createElementNS(XUL_NS, "hbox");
this.element.appendChild(this.propertyHeader);
this.propertyHeader.setAttribute("class", "property-header");
this.propertyHeader.addEventListener("click", this.propertyHeaderClick.bind(this), false);
this.matchedExpander = doc.createElementNS(HTML_NS, "div");
this.propertyHeader.appendChild(this.matchedExpander);
this.matchedExpander.setAttribute("class", "match expander");
let name = doc.createElementNS(HTML_NS, "div");
this.propertyHeader.appendChild(name);
name.setAttribute("class", "property-name");
name.textContent = this.name;
let helpcontainer = doc.createElementNS(HTML_NS, "div");
this.propertyHeader.appendChild(helpcontainer);
helpcontainer.setAttribute("class", "helplink-container");
let helplink = doc.createElementNS(HTML_NS, "a");
helpcontainer.appendChild(helplink);
helplink.setAttribute("class", "helplink");
helplink.setAttribute("title", CssHtmlTree.HELP_LINK_TITLE);
helplink.textContent = CssHtmlTree.HELP_LINK_TITLE;
helplink.addEventListener("click", this.mdnLinkClick.bind(this), false);
this.valueNode = doc.createElementNS(HTML_NS, "div");
this.propertyHeader.appendChild(this.valueNode);
this.valueNode.setAttribute("class", "property-value");
this.valueNode.setAttribute("dir", "ltr");
this.valueNode.textContent = this.value;
this.matchedSelectorsContainer = doc.createElementNS(HTML_NS, "div");
this.element.appendChild(this.matchedSelectorsContainer);
this.matchedSelectorsContainer.setAttribute("class", "rulelink");
return this.element;
},
/**
* Refresh the panel's CSS property value.
*/
@ -575,14 +674,12 @@ PropertyView.prototype = {
if (!this.tree.viewedElement || !this.visible) {
this.valueNode.innerHTML = "";
this.matchedSelectorsContainer.hidden = true;
this.unmatchedSelectorsContainer.hidden = true;
this.unmatchedSelectorTable.innerHTML = "";
this.matchedSelectorsContainer.innerHTML = "";
this.matchedExpander.removeAttribute("open");
this.unmatchedExpander.removeAttribute("open");
return;
}
this.tree.numVisibleProperties++;
this.valueNode.innerHTML = this.propertyInfo.value;
this.refreshAllSelectors();
},
@ -595,7 +692,7 @@ PropertyView.prototype = {
let hasMatchedSelectors = this.hasMatchedSelectors;
this.matchedSelectorsContainer.hidden = !hasMatchedSelectors;
if (hasMatchedSelectors || this.hasUnmatchedSelectors) {
if (hasMatchedSelectors) {
this.propertyHeader.classList.add("expandable");
} else {
this.propertyHeader.classList.remove("expandable");
@ -654,7 +751,6 @@ PropertyView.prototype = {
refreshAllSelectors: function PropertyView_refreshAllSelectors()
{
this.refreshMatchedSelectors();
this.refreshUnmatchedSelectors();
},
/**
@ -702,9 +798,6 @@ PropertyView.prototype = {
{
if (aEvent.target.className != "helplink") {
this.matchedExpanded = !this.matchedExpanded;
if (!this.hasMatchedSelectors && this.hasUnmatchedSelectors) {
this.unmatchedExpanded = !this.unmatchedExpanded;
}
this.refreshAllSelectors();
aEvent.preventDefault();
}

View File

@ -604,15 +604,19 @@ CssLogic.prototype = {
this._matchedRules.some(function(aValue) {
let rule = aValue[0];
let status = aValue[1];
aProperties = aProperties.filter(function(aProperty) {
if (rule.getPropertyValue(aProperty)) {
// We just need to find if a rule has this property while it matches
// the viewedElement (or its parents).
// We just need to find if a rule has this property while it matches
// the viewedElement (or its parents).
if (rule.getPropertyValue(aProperty) &&
(status == CssLogic.STATUS.MATCHED ||
(status == CssLogic.STATUS.PARENT_MATCH &&
this.domUtils.isInheritedProperty(aProperty)))) {
result[aProperty] = true;
return false;
}
return true; // Keep the property for the next rule.
});
}.bind(this));
return aProperties.length == 0;
}, this);
@ -1660,7 +1664,10 @@ CssPropertyInfo.prototype = {
{
let cssRule = aSelector._cssRule;
let value = cssRule.getPropertyValue(this.property);
if (value) {
if (value &&
(aStatus == CssLogic.STATUS.MATCHED ||
(aStatus == CssLogic.STATUS.PARENT_MATCH &&
this._cssLogic.domUtils.isInheritedProperty(this.property)))) {
let selectorInfo = new CssSelectorInfo(aSelector, this.property, value,
aStatus);
this._matchedSelectors.push(selectorInfo);

View File

@ -89,11 +89,25 @@ var EXPORTED_SYMBOLS = ["CssRuleView",
/**
* ElementStyle maintains a list of Rule objects for a given element.
*
* @param Element aElement
* The element whose style we are viewing.
* @param object aStore
* The ElementStyle can use this object to store metadata
* that might outlast the rule view, particularly the current
* set of disabled properties.
*
* @constructor
*/
function ElementStyle(aElement)
function ElementStyle(aElement, aStore)
{
this.element = aElement;
this.store = aStore || {};
if (this.store.disabled) {
this.store.disabled = aStore.disabled;
} else {
this.store.disabled = WeakMap();
}
let doc = aElement.ownerDocument;
// To figure out how shorthand properties are interpreted by the
@ -117,6 +131,17 @@ ElementStyle.prototype = {
domUtils: Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils),
/**
* Called by the Rule object when it has been changed through the
* setProperty* methods.
*/
_changed: function ElementStyle_changed()
{
if (this.onChanged) {
this.onChanged();
}
},
/**
* Refresh the list of rules to be displayed for the active element.
* Upon completion, this.rules[] will hold a list of Rule objects.
@ -372,12 +397,20 @@ Rule.prototype = {
/**
* Reapply all the properties in this rule, and update their
* computed styles. Will re-mark overridden properties.
* computed styles. Store disabled properties in the element
* style's store. Will re-mark overridden properties.
*/
applyProperties: function Rule_applyProperties()
{
let disabledProps = [];
for each (let prop in this.textProps) {
if (!prop.enabled) {
disabledProps.push({
name: prop.name,
value: prop.value,
priority: prop.priority
});
continue;
}
@ -388,6 +421,11 @@ Rule.prototype = {
prop.priority = this.style.getPropertyPriority(prop.name);
prop.updateComputed();
}
this.elementStyle._changed();
// Store disabled properties in the disabled store.
let disabled = this.elementStyle.store.disabled;
disabled.set(this.style, disabledProps);
this.elementStyle.markOverridden();
},
@ -477,6 +515,19 @@ Rule.prototype = {
let prop = new TextProperty(this, name, matches[2], matches[3] || "");
this.textProps.push(prop);
}
// Include properties from the disabled property store, if any.
let disabledProps = this.elementStyle.store.disabled.get(this.style);
if (!disabledProps) {
return;
}
for each (let prop in disabledProps) {
let textProp = new TextProperty(this, prop.name,
prop.value, prop.priority);
textProp.enabled = false;
this.textProps.push(textProp);
}
},
}
@ -593,15 +644,24 @@ TextProperty.prototype = {
*
* @param Document aDocument
* The document that will contain the rule view.
* @param object aStore
* The CSS rule view can use this object to store metadata
* that might outlast the rule view, particularly the current
* set of disabled properties.
* @constructor
*/
function CssRuleView(aDoc)
function CssRuleView(aDoc, aStore)
{
this.doc = aDoc;
this.store = aStore;
this.element = this.doc.createElementNS(HTML_NS, "div");
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";
}
CssRuleView.prototype = {
@ -627,7 +687,15 @@ CssRuleView.prototype = {
return;
}
this._elementStyle = new ElementStyle(aElement);
if (this._elementStyle) {
delete this._elementStyle.onChanged;
}
this._elementStyle = new ElementStyle(aElement, this.store);
this._elementStyle.onChanged = function() {
this._changed();
}.bind(this);
this._createEditors();
},
@ -643,6 +711,17 @@ CssRuleView.prototype = {
this._elementStyle = null;
},
/**
* Called when the user has made changes to the ElementStyle.
* Emits an event that clients can listen to.
*/
_changed: function CssRuleView_changed()
{
var evt = this.doc.createEvent("Events");
evt.initEvent("CssRuleViewChanged", true, false);
this.element.dispatchEvent(evt);
},
/**
* Creates editor UI for each of the rules in _elementStyle.
*/
@ -1003,10 +1082,12 @@ TextPropertyEditor.prototype = {
*/
_parseValue: function TextPropertyEditor_parseValue(aValue)
{
let [value, priority] = aValue.split("!", 2);
let pieces = aValue.split("!", 2);
let value = pieces[0];
let priority = pieces.length > 1 ? pieces[1] : "";
return {
value: value.trim(),
priority: (priority ? priority.trim() : "")
value: pieces[0].trim(),
priority: (pieces.length > 1 ? pieces[1].trim() : "")
};
},

View File

@ -87,6 +87,7 @@ StyleInspector.prototype = {
context: this,
get isOpen() isOpen(),
onSelect: this.selectNode,
onChanged: this.updateNode,
show: this.open,
hide: this.close,
dim: this.dimTool,
@ -247,6 +248,17 @@ StyleInspector.prototype = {
}
},
/**
* Update the display for the currently-selected node.
*/
updateNode: function SI_updateNode()
{
if (this.isOpen() && !this.dimmed) {
this.cssLogic.highlight(this.selectedNode);
this.cssHtmlTree.refreshPanel();
}
},
/**
* Dim or undim a panel by setting or removing a dimmed attribute.
* @param aState

View File

@ -19,9 +19,10 @@
- the Initial Developer. All Rights Reserved.
-
- Contributor(s):
- Joe Walker (jwalker@mozilla.com) (original author)
- Joe Walker <jwalker@mozilla.com> (original author)
- Mihai Șucan <mihai.sucan@gmail.com>
- Michael Ratcliffe <mratcliffe@mozilla.com>
- Dão Gottwald <dao@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
@ -36,8 +37,10 @@
- the terms of any one of the MPL, the GPL or the LGPL.
-
- ***** END LICENSE BLOCK ***** -->
<?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="chrome://browser/skin/devtools/csshtmltree.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % inspectorDTD SYSTEM "chrome://browser/locale/devtools/styleinspector.dtd">
%inspectorDTD;
@ -53,14 +56,16 @@
<!ATTLIST loop if CDATA #IMPLIED>
<!ATTLIST tr if CDATA #IMPLIED>
]>
<xul:window xmlns="http://www.w3.org/1999/xhtml"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- The output from #templateRoot (below) is inserted here. -->
<div id="root"></div>
<!-- The output from #templatePath (below) is inserted here. -->
<div id="path">
<!-- When no properties are found the following block is displayed. -->
<div id="noResults" hidden="">
&noPropertiesFound;
</div>
<!-- The output from #templateProperty (below) is appended here. -->
@ -71,9 +76,6 @@
<xul:label class="legendKey bestmatch">&bestMatch;</xul:label>
<xul:label class="legendKey matched">&matched;</xul:label>
<xul:label class="legendKey parentmatch">&parentMatch;</xul:label>
<xul:label class="legendKey unmatched">&unmatched;</xul:label>
<xul:spacer flex="1"/>
<xul:resizer dir="bottomright"/>
</xul:hbox>
<!--
To visually debug the templates without running firefox, alter the display:none
@ -84,68 +86,16 @@ To visually debug the templates without running firefox, alter the display:none
styles" checkbox. For data it needs an instance of CssHtmlTree.
-->
<div id="templateRoot">
<xul:hbox class="header" flex="1">
<label class="userStylesLabel">
<input class="onlyuserstyles" save="${onlyUserStylesCheckbox}"
type="checkbox" onchange="${onlyUserStylesChanged}" checked=""/>
&userStylesLabel;
</label>
<xul:hbox class="headerControls" flex="1" align="center">
<xul:checkbox class="onlyuserstyles" save="${onlyUserStylesCheckbox}"
oncommand="${onlyUserStylesChanged}" checked="true"
label="&userStylesLabel;"/>
<xul:textbox class="searchfield" type="search" save="${searchField}"
placeholder="&userStylesSearch;"
oncommand="${filterChanged}"/>
placeholder="&userStylesSearch;" flex="1"
oncommand="${filterChanged}"/>
</xul:hbox>
</div>
<!--
templatePath sits just below the top of the window showing what we're looking
at. For data it needs an instance of CssHtmlTree.
-->
<div id="templatePath">
<span class="selectedElementLabel">
&selectedElementLabel;
</span>
<!-- following broken in RTL mode, see bug 699900 -->
<ol>
<li foreach="item in ${pathElements}">
<a href="#" onclick="${pathClick}" __pathElement="${item.element}">
${__element.pathElement = item.element; item.display}
</a>
</li>
</ol>
</div>
<!--
TemplateProperty lists the properties themselves. Each needs data like this:
{
property: ... // PropertyView from CssHtmlTree.jsm
}
-->
<div id="templateProperty">
<div class="${className}" save="${element}">
<div class="property-header" save="${propertyHeader}"
onclick="${propertyHeaderClick}">
<div save="${matchedExpander}" class="match expander"/>
<div class="property-name">${name}</div>
<div class="helplink-container">
<a href="${link}" class="helplink" title="&helpLinkTitle;" onclick="${mdnLinkClick}">
&helpLinkTitle;
</a>
</div>
<div save="${valueNode}" class="property-value" dir="ltr">${value}</div>
</div>
<div save="${matchedSelectorsContainer}" class="rulelink">
</div>
<div save="${unmatchedSelectorsContainer}" class="rulelink">
<div save="${unmatchedTitleBlock}" onclick="${unmatchedSelectorsClick}"
class="rule-unmatched">
<div save="${unmatchedExpander}" class="expander"/>
<div save="${unmatchedSelectorsTitleNode}">&unmatchedSelectors;</div>
</div>
<div save="${unmatchedSelectorTable}" class="unmatchedSelectorTable"/>
</div>
</div>
</div>
<!--
A templateMatchedSelectors sits inside each templateProperties showing the
@ -165,32 +115,7 @@ To visually debug the templates without running firefox, alter the display:none
</td>
<td class="rule-link">
<a target="_blank" href="view-source:${selector.selectorInfo.href}" class="link"
title="${selector.selectorInfo.href}">${selector.selectorInfo.source}</a>
</td>
</tr>
</loop>
</table>
</div>
<!--
A templateUnmatchedSelectors sits inside each templateProperties showing the
list of selectors that do not affect that property. Each needs data like this:
{
unmatchedSelectorViews: ..., // from cssHtmlTree.propertyViews[name].unmatchedSelectorViews
}
This is a template so the parent does not need to be a table, except that
using a div as the parent causes the DOM to muck with the tr elements
-->
<div id="templateUnmatchedSelectors">
<table>
<loop foreach="selector in ${unmatchedSelectorViews}">
<tr>
<td dir="ltr" class="rule-text ${selector.statusClass}">
${selector.humanReadableText(__element)}
</td>
<td class="rule-link">
<a target="_blank" href="view-source:${selector.selectorInfo.href}" class="link"
title="${selector.selectorInfo.href}">${selector.selectorInfo.source}</a>
title="${selector.selectorInfo.href}">${selector.selectorInfo.source}</a>
</td>
</tr>
</loop>

View File

@ -51,7 +51,9 @@ _BROWSER_TEST_FILES = \
browser_bug683672.js \
browser_styleinspector_bug_672746_default_styles.js \
browser_styleinspector_bug_672744_search_filter.js \
browser_styleinspector_bug_689759_no_results_placeholder.js \
browser_bug_692400_element_style.js \
browser_csslogic_inherited.js \
browser_ruleview_editor.js \
browser_ruleview_inherit.js \
browser_ruleview_manipulation.js \

View File

@ -38,7 +38,7 @@ function runTests()
ok(stylePanel.isOpen(), "style inspector is open");
testMatchedSelectors();
testUnmatchedSelectors();
//testUnmatchedSelectors();
info("finishing up");
Services.obs.addObserver(finishUp, "StyleInspector-closed", false);

View File

@ -0,0 +1,46 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that inherited properties are treated correctly.
Cu.import("resource:///modules/devtools/CssLogic.jsm");
let doc;
function createDocument()
{
doc.body.innerHTML = '<div style="margin-left:10px; font-size: 5px"><div id="innerdiv">Inner div</div></div>';
doc.title = "Style Inspector Inheritance Test";
let cssLogic = new CssLogic();
cssLogic.highlight(doc.getElementById("innerdiv"));
let marginProp = cssLogic.getPropertyInfo("margin-left");
is(marginProp.matchedRuleCount, 0, "margin-left should not be included in matched selectors.");
let fontSizeProp = cssLogic.getPropertyInfo("font-size");
is(fontSizeProp.matchedRuleCount, 1, "font-size should be included in matched selectors.");
finishUp();
}
function finishUp()
{
doc = null;
gBrowser.removeCurrentTab();
finish();
}
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
doc = content.document;
waitForFocus(createDocument, content);
}, true);
content.location = "data:text/html,selector text test, bug 692400";
}

View File

@ -32,6 +32,18 @@ function waitForEditorBlur(aEditor, aCallback)
}, false);
}
var gRuleViewChanged = false;
function ruleViewChanged()
{
gRuleViewChanged = true;
}
function expectChange()
{
ok(gRuleViewChanged, "Rule view should have fired a change event.");
gRuleViewChanged = false;
}
function startTest()
{
let style = '' +
@ -54,6 +66,7 @@ function startTest()
let doc = ruleDialog.document;
ruleView = new CssRuleView(doc);
doc.documentElement.appendChild(ruleView.element);
ruleView.element.addEventListener("CssRuleViewChanged", ruleViewChanged, false);
ruleView.highlight(testElement);
waitForFocus(testCancelNew, ruleDialog);
}, true);
@ -69,6 +82,7 @@ function testCancelNew()
is(elementRuleEditor.newPropSpan.inplaceEditor, aEditor, "Next focused editor should be the new property editor.");
let input = aEditor.input;
waitForEditorBlur(aEditor, function () {
ok(!gRuleViewChanged, "Shouldn't get a change event after a cancel.");
is(elementRuleEditor.rule.textProps.length, 0, "Should have canceled creating a new text property.");
ok(!elementRuleEditor.propertyList.hasChildNodes(), "Should not have any properties.");
testCreateNew();
@ -91,6 +105,7 @@ function testCreateNew()
input.value = "background-color";
waitForEditorFocus(elementRuleEditor.element, function onNewValue(aEditor) {
expectChange();
is(elementRuleEditor.rule.textProps.length, 1, "Should have created a new text property.");
is(elementRuleEditor.propertyList.children.length, 1, "Should have created a property editor.");
let textProp = elementRuleEditor.rule.textProps[0];
@ -98,6 +113,7 @@ function testCreateNew()
aEditor.input.value = "purple";
waitForEditorBlur(aEditor, function() {
expectChange();
is(textProp.value, "purple", "Text prop should have been changed.");
testEditProperty();
});
@ -120,10 +136,12 @@ function testEditProperty()
is(propEditor.nameSpan.inplaceEditor, aEditor, "Next focused editor should be the name editor.");
let input = aEditor.input;
waitForEditorFocus(propEditor.element, function onNewName(aEditor) {
expectChange();
input = aEditor.input;
is(propEditor.valueSpan.inplaceEditor, aEditor, "Focus should have moved to the value.");
waitForEditorBlur(aEditor, function() {
expectChange();
is(idRuleEditor.rule.style.getPropertyValue("border-color"), "red",
"border-color should have been set.");
testDisableProperty();
@ -150,14 +168,20 @@ function testDisableProperty()
propEditor.enable.click();
is(idRuleEditor.rule.style.getPropertyValue("border-color"), "", "Border-color should have been unset.");
expectChange();
propEditor.enable.click();
is(idRuleEditor.rule.style.getPropertyValue("border-color"), "red",
"Border-color should have been reset.");
expectChange();
finishTest();
}
function finishTest()
{
ruleView.element.removeEventListener("CssRuleViewChanged", ruleViewChanged, false);
ruleView.clear();
ruleDialog.close();
ruleDialog = ruleView = null;
doc = null;

View File

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

View File

@ -0,0 +1,118 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Tests that the no results placeholder works properly.
let doc;
let stylePanel;
function createDocument()
{
doc.body.innerHTML = '<style type="text/css"> ' +
'.matches {color: #F00;}</style>' +
'<span id="matches" class="matches">Some styled text</span>';
doc.title = "Tests that the no results placeholder works properly";
ok(window.StyleInspector, "StyleInspector exists");
stylePanel = new StyleInspector(window);
Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-opened", false);
stylePanel.createPanel(false, function() {
stylePanel.open(doc.body);
});
}
function runStyleInspectorTests()
{
Services.obs.removeObserver(runStyleInspectorTests, "StyleInspector-opened", false);
ok(stylePanel.isOpen(), "style inspector is open");
Services.obs.addObserver(SI_AddFilterText, "StyleInspector-populated", false);
let span = doc.querySelector("#matches");
ok(span, "captain, we have the matches span");
let htmlTree = stylePanel.cssHtmlTree;
stylePanel.selectNode(span);
is(span, htmlTree.viewedElement,
"style inspector node matches the selected node");
is(htmlTree.viewedElement, stylePanel.cssLogic.viewedElement,
"cssLogic node matches the cssHtmlTree node");
}
function SI_AddFilterText()
{
Services.obs.removeObserver(SI_AddFilterText, "StyleInspector-populated", false);
let iframe = stylePanel.iframe;
let searchbar = stylePanel.cssHtmlTree.searchField;
let searchTerm = "xxxxx";
Services.obs.addObserver(SI_checkPlaceholderVisible, "StyleInspector-populated", false);
info("setting filter text to \"" + searchTerm + "\"");
searchbar.focus();
for each (let c in searchTerm) {
EventUtils.synthesizeKey(c, {}, iframe.contentWindow);
}
}
function SI_checkPlaceholderVisible()
{
Services.obs.removeObserver(SI_checkPlaceholderVisible, "StyleInspector-populated", false);
info("SI_checkPlaceholderVisible called");
let placeholder = stylePanel.cssHtmlTree.noResults;
let iframe = stylePanel.iframe;
let display = iframe.contentWindow.getComputedStyle(placeholder).display;
is(display, "block", "placeholder is visible");
SI_ClearFilterText();
}
function SI_ClearFilterText()
{
let iframe = stylePanel.iframe;
let searchbar = stylePanel.cssHtmlTree.searchField;
Services.obs.addObserver(SI_checkPlaceholderHidden, "StyleInspector-populated", false);
info("clearing filter text");
searchbar.focus();
searchbar.value = "";
EventUtils.synthesizeKey("c", {}, iframe.contentWindow);
}
function SI_checkPlaceholderHidden()
{
Services.obs.removeObserver(SI_checkPlaceholderHidden, "StyleInspector-populated", false);
let placeholder = stylePanel.cssHtmlTree.noResults;
let iframe = stylePanel.iframe;
let display = iframe.contentWindow.getComputedStyle(placeholder).display;
is(display, "none", "placeholder is hidden");
Services.obs.addObserver(finishUp, "StyleInspector-closed", false);
stylePanel.close();
}
function finishUp()
{
Services.obs.removeObserver(finishUp, "StyleInspector-closed", false);
ok(!stylePanel.isOpen(), "style inspector is closed");
doc = stylePanel = null;
gBrowser.removeCurrentTab();
finish();
}
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
doc = content.document;
waitForFocus(createDocument, content);
}, true);
content.location = "data:text/html,no results placeholder test";
}

View File

@ -12,11 +12,10 @@
- tree. -->
<!ENTITY selectedElementLabel "Selected element:">
<!-- LOCALIZATION NOTE (helpLinkTitle): For each style property
- the user can hover it and get a help link button which allows one to
- quickly jump to the documentation from the Mozilla Developer Network site.
- This is the link title shown in the hover tooltip. -->
<!ENTITY helpLinkTitle "Read the documentation for this property">
<!-- LOCALIZATION NOTE (noPropertiesFound): In the case where there are no CSS
- properties to display e.g. due to search criteria this message is
- displayed. -->
<!ENTITY noPropertiesFound "No CSS properties found.">
<!-- LOCALIZATION NOTE (unmatchedSelectors): For each style property
- the panel shows whether there are any selectors that do not match the

View File

@ -41,3 +41,8 @@ style.highlighter.button.label1=Properties
style.highlighter.accesskey1=P
style.highlighter.button.tooltip=Inspect element styles
# LOCALIZATION NOTE (helpLinkTitle): For each style property
# the user can hover it and get a help link button which allows one to
# quickly jump to the documentation from the Mozilla Developer Network site.
# This is the link title shown in the hover tooltip.
helpLinkTitle=Read the documentation for this property

View File

@ -22,6 +22,7 @@
* Joe Walker <jwalker@mozilla.com> (original author)
* Mihai Șucan <mihai.sucan@gmail.com>
* Michael Ratcliffe <mratcliffe@mozilla.com>
* Dão Gottwald <dao@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
@ -47,35 +48,6 @@
display: -moz-box;
}
#path {
font-size: 11px;
word-spacing: -1px;
margin-bottom: 0;
color: -moz-dialogtext;
background-color: -moz-dialog;
padding: 4px 5px 0;
}
#path ol {
list-style: none outside none;
margin: 0;
padding: 0;
}
#path li {
border-radius: 3px;
padding: 2px 3px;
font-size: 11px;
display: inline-block;
}
#path li:after {
content: " > ";
}
#path li:last-child {
font-weight: bold;
color: #0091ff;
}
#path li:last-child:after {
content: "";
}
.property-header {
padding: 4px;
@ -83,13 +55,13 @@
-moz-padding-end: 5px;
}
.rule-unmatched {
cursor: pointer;
padding: 2px;
-moz-padding-start: 4px;
-moz-padding-end: 0;
white-space: nowrap;
}
.rule-unmatched {
cursor: pointer;
padding: 2px;
-moz-padding-start: 4px;
-moz-padding-end: 0;
white-space: nowrap;
}
/* Take away these two :visited rules to get a core dumper */
/* See https://bugzilla.mozilla.org/show_bug.cgi?id=575675#c30 */
@ -107,21 +79,20 @@
.helplink-container {
position: relative;
top: 2px;
display: inline-block;
visibility: hidden;
}
.helplink {
display: block;
height: 0;
width: 14px;
height: 14px;
width: 0;
overflow: hidden;
padding-top: 14px;
-moz-padding-start: 14px;
background-image: url("chrome://browser/skin/devtools/goto-mdn.png");
-moz-margin-end: 2px;
}
.property-header:hover > .helplink-container {
visibility: visible;
.property-header:not(:hover) > .helplink-container {
visibility: hidden;
}
.unmatchedSelectorTable {
@ -134,22 +105,13 @@
}
.expander {
width: 8px;
height: 8px;
float: left;
-moz-margin-start: 15px;
-moz-appearance: treetwisty;
-moz-margin-start: 10px;
-moz-margin-end: 5px;
margin-top: 3px;
background: url("chrome://browser/skin/devtools/arrows.png") 48px 0;
}
.expander:-moz-locale-dir(rtl) {
float: right;
background-position: 40px 0;
}
.expander[open] {
background-position: 32px 0;
-moz-appearance: treetwistyopen;
}
.expandable {
@ -161,7 +123,6 @@
}
.expandable > .match {
margin-top: 5px;
visibility: visible;
}
@ -170,13 +131,11 @@
}
.property-name {
display: inline-block;
font-size: 12px;
color: -moz-FieldText;
width: 220px;
}
.property-value {
display: inline-block;
font-size: 10px;
color: grey;
}
@ -216,41 +175,29 @@
overflow-y: auto;
}
.selectedElementLabel {
font-weight: bold;
}
.darkrow {
background-color: rgba(0,0,0,.022);
}
.header {
#noResults {
font-size: 18px;
margin-top: 5px;
text-align: center;
}
.headerControls {
color: -moz-dialogtext;
background-color: -moz-dialog;
padding: 5px 0 0;
}
.onlyuserstyles,
.userStylesLabel {
cursor: pointer;
}
.userStylesLabel {
display: -moz-box;
white-space: nowrap;
padding-top: 5px;
}
.onlyuserstyles {
position: relative;
top: 3px;
font-family: sans-serif;
cursor: pointer;
font-size: 11px;
}
.searchfield {
display: -moz-box;
-moz-box-flex: 1;
margin-left: 10px;
-moz-margin-start: 10px;
}
.styleinspector-legend {

View File

@ -47,6 +47,10 @@
@import url("chrome://global/skin/");
%include shared.inc
%filter substitution
%define forwardTransitionLength 150ms
%define conditionalForwardWithUrlbar window:not([chromehidden~=toolbar]) #navigator-toolbox[iconsize=large][mode=icons] > :-moz-any(#nav-bar[currentset*="unified-back-forward-button,urlbar-container"],#nav-bar:not([currentset])) > #unified-back-forward-button
%define conditionalForwardWithUrlbarWidth 27
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
@namespace html url("http://www.w3.org/1999/xhtml");
@ -531,6 +535,71 @@ toolbar[mode="icons"] #forward-button {
mask: url(chrome://browser/content/browser.xul#pinstripe-keyhole-forward-mask);
}
@conditionalForwardWithUrlbar@ > #forward-button:not(:-moz-lwtheme) {
-moz-appearance: none;
-moz-padding-start: 2px;
background: -moz-linear-gradient(hsl(0,0%,99%), hsl(0,0%,67%)) padding-box;
border: 1px solid;
border-color: hsl(0,0%,31%) hsla(0,0%,29%,.6) hsl(0,0%,27%);
box-shadow: inset 0 1px 0 hsla(0,0%,100%,.35),
0 1px 0 hsla(0,0%,100%,.2);
}
@conditionalForwardWithUrlbar@ > #forward-button {
border-radius: 0;
-moz-margin-end: 0;
}
@conditionalForwardWithUrlbar@ > #forward-button:-moz-lwtheme {
-moz-padding-start: 2px;
-moz-padding-end: 0;
}
@conditionalForwardWithUrlbar@:not([switchingtabs]) > #forward-button {
-moz-transition: opacity @forwardTransitionLength@ ease-out;
}
@conditionalForwardWithUrlbar@ > #forward-button:hover:active:not(:-moz-lwtheme) {
background-image: -moz-linear-gradient(hsl(0,0%,74%), hsl(0,0%,61%));
box-shadow: inset rgba(0,0,0,.3) 0 -6px 10px,
inset #000 0 1px 3px,
inset rgba(0,0,0,.2) 0 1px 3px,
0 1px 0 hsla(0,0%,100%,.2);
}
@conditionalForwardWithUrlbar@ > #forward-button:-moz-window-inactive:not(:-moz-lwtheme) {
border-color: hsl(0,0%,64%) hsl(0,0%,65%) hsl(0,0%,66%);
background-image: -moz-linear-gradient(hsl(0,0%,99%), hsl(0,0%,82%));
box-shadow: inset 0 1px 0 hsla(0,0%,100%,.35);
}
@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] {
opacity: 0;
}
@media (-moz-mac-lion-theme) {
@conditionalForwardWithUrlbar@ > #forward-button:not(:-moz-lwtheme) {
background-image: -moz-linear-gradient(hsla(0,0%,100%,.73), hsla(0,0%,100%,.05) 85%);
border-color: hsla(0,0%,0%,.35) hsla(0,0%,0%,.25) hsla(0,0%,0%,.2);
box-shadow: inset 0 1px 0 hsla(0,0%,100%,.2),
inset 0 0 1px hsla(0,0%,100%,.1),
0 1px 0 hsla(0,0%,100%,.2);
}
@conditionalForwardWithUrlbar@ > #forward-button:hover:active:not(:-moz-lwtheme) {
background-image: -moz-linear-gradient(hsla(0,0%,60%,.37), hsla(0,0%,100%,.35) 95%);
border-color: hsla(0,0%,0%,.43) hsla(0,0%,0%,.25) hsla(0,0%,0%,.37);
box-shadow: inset 0 1px 0 hsla(0,0%,0%,.02),
inset 0 1px 2px hsla(0,0%,0%,.2),
0 1px 0 hsla(0,0%,100%,.2);
}
@conditionalForwardWithUrlbar@ > #forward-button:-moz-window-inactive:not(:-moz-lwtheme) {
background-image: none;
border-color: hsla(0,0%,0%,.2);
}
}
#navigator-toolbox[iconsize="small"][mode="icons"] > #nav-bar #forward-button {
width: 27px;
}
@ -842,6 +911,57 @@ toolbar[mode="icons"] #zoom-in-button {
border-radius: @toolbarbuttonCornerRadius@;
}
@conditionalForwardWithUrlbar@ + #urlbar-container {
padding-left: @conditionalForwardWithUrlbarWidth@px;
-moz-margin-start: -@conditionalForwardWithUrlbarWidth@px;
position: relative;
pointer-events: none;
}
@conditionalForwardWithUrlbar@ + #urlbar-container > #urlbar {
-moz-border-start: none;
margin-left: 0;
pointer-events: all;
}
@conditionalForwardWithUrlbar@:not([switchingtabs]) + #urlbar-container > #urlbar {
-moz-transition: margin-left @forwardTransitionLength@ ease-out;
}
@conditionalForwardWithUrlbar@ + #urlbar-container > #urlbar:-moz-locale-dir(ltr) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
@conditionalForwardWithUrlbar@ + #urlbar-container > #urlbar:-moz-locale-dir(rtl) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
@conditionalForwardWithUrlbar@[forwarddisabled] + #urlbar-container {
mask: url("chrome://browser/content/browser.xul#pinstripe-urlbar-back-button-mask");
}
@conditionalForwardWithUrlbar@[forwarddisabled] + #urlbar-container > #urlbar {
margin-left: -@conditionalForwardWithUrlbarWidth@px;
}
@conditionalForwardWithUrlbar@[forwarddisabled]:hover:not([switchingtabs]) + #urlbar-container > #urlbar {
/* delay the hiding of the forward button when hovered to avoid accidental clicks on the url bar */
-moz-transition-delay: 100s;
}
@conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) + #urlbar-container > #urlbar {
/* when not hovered anymore, trigger a new transition to hide the forward button immediately */
margin-left: -@conditionalForwardWithUrlbarWidth@.01px;
}
@conditionalForwardWithUrlbar@ + #urlbar-container:-moz-locale-dir(rtl),
@conditionalForwardWithUrlbar@ + #urlbar-container > #urlbar:-moz-locale-dir(rtl) {
/* let pinstripe-urlbar-back-button-mask clip the urlbar's right side for RTL */
-moz-transform: scaleX(-1);
}
#identity-box {
background-image: -moz-linear-gradient(hsl(0,0%,98%), hsl(0,0%,92%));
box-shadow: 0 1px 0 hsla(0,0%,0%,.05) inset;
@ -860,6 +980,38 @@ toolbar[mode="icons"] #zoom-in-button {
border-bottom-right-radius: 2px;
}
#notification-popup-box:not([hidden]) + #identity-box {
-moz-padding-start: 10px;
border-radius: 0;
}
@conditionalForwardWithUrlbar@ + #urlbar-container > #urlbar > #identity-box {
border-radius: 0;
}
@conditionalForwardWithUrlbar@[forwarddisabled] + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
-moz-transition: 0s padding-left;
padding-left: 10px;
}
@conditionalForwardWithUrlbar@[forwarddisabled] + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
-moz-transition: 0s padding-right;
padding-right: 10px;
}
@conditionalForwardWithUrlbar@[forwarddisabled]:hover:not([switchingtabs]) + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box {
/* delay the hiding of the forward button when hovered to avoid accidental clicks on the url bar */
-moz-transition-delay: 100s;
}
@conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
padding-left: 10.01px;
}
@conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
padding-right: 10.01px;
}
#identity-box:active:hover,
#identity-box[open="true"] {
background-image: -moz-linear-gradient(hsl(0,0%,93%), hsl(0,0%,80%));
@ -2155,9 +2307,8 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
-moz-margin-end: -8px;
}
#notification-popup-box:not([hidden]) + #identity-box {
-moz-padding-start: 10px;
border-radius: 0;
@conditionalForwardWithUrlbar@[forwarddisabled] + #urlbar-container > #urlbar > #notification-popup-box {
padding-left: 7px;
}
#notification-popup-box:-moz-locale-dir(rtl),

View File

@ -22,6 +22,7 @@
* Joe Walker <jwalker@mozilla.com> (original author)
* Mihai Șucan <mihai.sucan@gmail.com>
* Michael Ratcliffe <mratcliffe@mozilla.com>
* Dão Gottwald <dao@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
@ -47,35 +48,6 @@
display: -moz-box;
}
#path {
font-size: 11px;
word-spacing: -1px;
margin-bottom: 0;
color: -moz-dialogtext;
background-color: -moz-dialog;
padding: 4px 5px 0;
}
#path ol {
list-style: none outside none;
margin: 0;
padding: 0;
}
#path li {
border-radius: 3px;
padding: 2px 3px;
font-size: 11px;
display: inline-block;
}
#path li:after {
content: " > ";
}
#path li:last-child {
font-weight: bold;
color: #0091ff;
}
#path li:last-child:after {
content: "";
}
.property-header {
padding: 4px;
@ -83,13 +55,13 @@
-moz-padding-end: 5px;
}
.rule-unmatched {
cursor: pointer;
padding: 2px;
-moz-padding-start: 4px;
-moz-padding-end: 0;
white-space: nowrap;
}
.rule-unmatched {
cursor: pointer;
padding: 2px;
-moz-padding-start: 4px;
-moz-padding-end: 0;
white-space: nowrap;
}
/* Take away these two :visited rules to get a core dumper */
/* See https://bugzilla.mozilla.org/show_bug.cgi?id=575675#c30 */
@ -107,21 +79,20 @@
.helplink-container {
position: relative;
top: 2px;
display: inline-block;
visibility: hidden;
}
.helplink {
display: block;
height: 0;
width: 14px;
height: 14px;
width: 0;
overflow: hidden;
padding-top: 14px;
-moz-padding-start: 14px;
background-image: url("chrome://browser/skin/devtools/goto-mdn.png");
-moz-margin-end: 2px;
}
.property-header:hover > .helplink-container {
visibility: visible;
.property-header:not(:hover) > .helplink-container {
visibility: hidden;
}
.unmatchedSelectorTable {
@ -137,15 +108,10 @@
-moz-appearance: treetwisty;
width: 12px;
height: 12px;
float: left;
-moz-margin-start: 5px;
-moz-margin-end: 5px;
}
.expander:-moz-locale-dir(rtl) {
float: right;
}
.expander[open] {
-moz-appearance: treetwistyopen;
}
@ -159,7 +125,6 @@
}
.expandable > .match {
margin-top: 5px;
visibility: visible;
}
@ -168,13 +133,11 @@
}
.property-name {
display: inline-block;
font-size: 12px;
color: -moz-FieldText;
width: 220px;
}
.property-value {
display: inline-block;
font-size: 10px;
color: grey;
}
@ -214,41 +177,29 @@
overflow-y: auto;
}
.selectedElementLabel {
font-weight: bold;
}
.darkrow {
background-color: rgba(0,0,0,.022);
}
.header {
#noResults {
font-size: 18px;
margin-top: 5px;
text-align: center;
}
.headerControls {
color: -moz-dialogtext;
background-color: -moz-dialog;
padding: 5px 0 0;
}
.onlyuserstyles,
.userStylesLabel {
cursor: pointer;
}
.userStylesLabel {
display: -moz-box;
white-space: nowrap;
padding-top: 5px;
}
.onlyuserstyles {
position: relative;
top: 3px;
font-family: sans-serif;
cursor: pointer;
font-size: 11px;
}
.searchfield {
display: -moz-box;
-moz-box-flex: 1;
margin-left: 10px;
-moz-margin-start: 10px;
}
.styleinspector-legend {

View File

@ -22,6 +22,7 @@
* Joe Walker <jwalker@mozilla.com> (original author)
* Mihai Șucan <mihai.sucan@gmail.com>
* Michael Ratcliffe <mratcliffe@mozilla.com>
* Dão Gottwald <dao@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
@ -47,49 +48,19 @@
display: -moz-box;
}
#path {
font-size: 11px;
word-spacing: -1px;
margin-bottom: 0;
color: -moz-dialogtext;
background-color: -moz-dialog;
padding: 4px 5px 0;
}
#path ol {
list-style: none outside none;
margin: 0;
padding: 0;
}
#path li {
border-radius: 3px;
padding: 2px 3px;
font-size: 11px;
display: inline-block;
}
#path li:after {
content: " > ";
}
#path li:last-child {
font-weight: bold;
color: #0091ff;
}
#path li:last-child:after {
content: "";
}
.property-header {
padding: 4px;
-moz-padding-start: 0;
-moz-padding-end: 5px;
}
.rule-unmatched {
cursor: pointer;
padding: 2px;
-moz-padding-start: 4px;
-moz-padding-end: 0;
white-space: nowrap;
}
.rule-unmatched {
cursor: pointer;
padding: 2px;
-moz-padding-start: 4px;
-moz-padding-end: 0;
white-space: nowrap;
}
/* Take away these two :visited rules to get a core dumper */
/* See https://bugzilla.mozilla.org/show_bug.cgi?id=575675#c30 */
@ -107,21 +78,20 @@
.helplink-container {
position: relative;
top: 2px;
display: inline-block;
visibility: hidden;
}
.helplink {
display: block;
height: 0;
width: 14px;
height: 14px;
width: 0;
overflow: hidden;
padding-top: 14px;
-moz-padding-start: 14px;
background-image: url("chrome://browser/skin/devtools/goto-mdn.png");
-moz-margin-end: 2px;
}
.property-header:hover > .helplink-container {
visibility: visible;
.property-header:not(:hover) > .helplink-container {
visibility: hidden;
}
.unmatchedSelectorTable {
@ -136,15 +106,9 @@
.expander {
width: 9px;
height: 9px;
float: left;
margin-top: 1px;
-moz-margin-start: 5px;
-moz-margin-end: 5px;
background-image: url("chrome://global/skin/tree/twisty-clsd.png");
}
.expander:-moz-locale-dir(rtl) {
float: right;
background: url("chrome://global/skin/tree/twisty-clsd.png") center center no-repeat;
}
.expander[open] {
@ -160,7 +124,6 @@
}
.expandable > .match {
margin-top: 5px;
visibility: visible;
}
@ -169,13 +132,11 @@
}
.property-name {
display: inline-block;
font-size: 12px;
color: -moz-FieldText;
width: 220px;
}
.property-value {
display: inline-block;
font-size: 10px;
color: grey;
}
@ -215,41 +176,29 @@
overflow-y: auto;
}
.selectedElementLabel {
font-weight: bold;
}
.darkrow {
background-color: rgba(0,0,0,.022);
}
.header {
#noResults {
font-size: 18px;
margin-top: 5px;
text-align: center;
}
.headerControls {
color: -moz-dialogtext;
background-color: -moz-dialog;
padding: 5px 0 0;
}
.onlyuserstyles,
.userStylesLabel {
cursor: pointer;
}
.userStylesLabel {
display: -moz-box;
white-space: nowrap;
padding-top: 5px;
}
.onlyuserstyles {
position: relative;
top: 3px;
font-family: sans-serif;
cursor: pointer;
font-size: 11px;
}
.searchfield {
display: -moz-box;
-moz-box-flex: 1;
margin-left: 10px;
-moz-margin-start: 10px;
}
.styleinspector-legend {

View File

@ -62,6 +62,7 @@
#include "nsWidgetsCID.h"
#include "nsPIDOMWindow.h"
#include "nsAppDirectoryServiceDefs.h"
#include "mozilla/Preferences.h"
#include <io.h>
#include <propvarutil.h>
#include <propkey.h>
@ -274,6 +275,28 @@ WinTaskbar::~WinTaskbar() {
// static
bool
WinTaskbar::GetAppUserModelID(nsAString & aDefaultGroupId) {
// If marked as such in prefs, use a hash of the profile path for the id
// instead of the install path hash setup by the installer.
bool useProfile =
Preferences::GetBool("taskbar.grouping.useprofile", false);
if (useProfile) {
nsCOMPtr<nsIFile> profileDir;
NS_GetSpecialDirectory(NS_APP_PROFILE_DEFAULTS_50_DIR,
getter_AddRefs(profileDir));
bool exists = false;
if (profileDir && NS_SUCCEEDED(profileDir->Exists(&exists)) && exists) {
nsCAutoString path;
if (NS_SUCCEEDED(profileDir->GetNativePath(path))) {
nsAutoString id;
id.AppendInt(HashString(path));
if (!id.IsEmpty()) {
aDefaultGroupId.Assign(id);
return true;
}
}
}
}
// The default value is set by the installer and is stored in the registry
// under (HKLM||HKCU)/Software/Mozilla/Firefox/TaskBarIDs. If for any reason
// hash generation operation fails, the installer will not store a value in