зеркало из https://github.com/mozilla/gecko-dev.git
Bug 740543 - Rule view does not update when window is resized. r=robcee
This commit is contained in:
Родитель
7985fc62a3
Коммит
8f60bcb608
|
@ -44,6 +44,10 @@ const INSPECTOR_NOTIFICATIONS = {
|
|||
|
||||
const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
|
||||
|
||||
// Timer, in milliseconds, between change events fired by
|
||||
// things like resize events.
|
||||
const LAYOUT_CHANGE_TIMER = 250;
|
||||
|
||||
/**
|
||||
* Represents an open instance of the Inspector for a tab.
|
||||
* This is the object handed out to sidebars and other API consumers.
|
||||
|
@ -61,7 +65,10 @@ function Inspector(aIUI)
|
|||
{
|
||||
this._IUI = aIUI;
|
||||
this._winID = aIUI.winID;
|
||||
this._browser = aIUI.browser;
|
||||
this._listeners = {};
|
||||
|
||||
this._browser.addEventListener("resize", this, true);
|
||||
}
|
||||
|
||||
Inspector.prototype = {
|
||||
|
@ -106,18 +113,96 @@ Inspector.prototype = {
|
|||
*/
|
||||
change: function Inspector_change(aContext)
|
||||
{
|
||||
this._cancelLayoutChange();
|
||||
this._IUI.nodeChanged(aContext);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if a given sidebar panel is currently visible.
|
||||
* @param string aPanelName
|
||||
* The panel name as registered with registerSidebar
|
||||
*/
|
||||
isPanelVisible: function Inspector_isPanelVisible(aPanelName)
|
||||
{
|
||||
return this._IUI.sidebar.visible &&
|
||||
this._IUI.sidebar.activePanel === aPanelName;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called by the InspectorUI when the inspector is being destroyed.
|
||||
*/
|
||||
_destroy: function Inspector__destroy()
|
||||
{
|
||||
this._cancelLayoutChange();
|
||||
this._browser.removeEventListener("resize", this, true);
|
||||
delete this._IUI;
|
||||
delete this._listeners;
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for DOM events.
|
||||
*
|
||||
* @param DOMEvent aEvent
|
||||
*/
|
||||
handleEvent: function Inspector_handleEvent(aEvent)
|
||||
{
|
||||
switch(aEvent.type) {
|
||||
case "resize":
|
||||
this._scheduleLayoutChange();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Schedule a low-priority change event for things like paint
|
||||
* and resize.
|
||||
*/
|
||||
_scheduleLayoutChange: function Inspector_scheduleLayoutChange()
|
||||
{
|
||||
if (this._timer) {
|
||||
return null;
|
||||
}
|
||||
this._timer = this._IUI.win.setTimeout(function() {
|
||||
this.change("layout");
|
||||
}.bind(this), LAYOUT_CHANGE_TIMER);
|
||||
},
|
||||
|
||||
/**
|
||||
* Cancel a pending low-priority change event if any is
|
||||
* scheduled.
|
||||
*/
|
||||
_cancelLayoutChange: function Inspector_cancelLayoutChange()
|
||||
{
|
||||
if (this._timer) {
|
||||
this._IUI.win.clearTimeout(this._timer);
|
||||
delete this._timer;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called by InspectorUI after a tab switch, when the
|
||||
* inspector is no longer the active tab.
|
||||
*/
|
||||
_freeze: function Inspector__freeze()
|
||||
{
|
||||
this._cancelLayoutChange();
|
||||
this._browser.removeEventListener("resize", this, true);
|
||||
this._frozen = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called by InspectorUI after a tab switch when the
|
||||
* inspector is back to being the active tab.
|
||||
*/
|
||||
_thaw: function Inspector__thaw()
|
||||
{
|
||||
if (!this._frozen) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._browser.addEventListener("resize", this, true);
|
||||
delete this._frozen;
|
||||
},
|
||||
|
||||
/// Event stuff. Would like to refactor this eventually.
|
||||
/// Emulates the jetpack event source, which has a nice API.
|
||||
|
||||
|
@ -176,10 +261,23 @@ Inspector.prototype = {
|
|||
{
|
||||
if (!(aEvent in this._listeners))
|
||||
return;
|
||||
for each (let listener in this._listeners[aEvent]) {
|
||||
|
||||
let originalListeners = this._listeners[aEvent];
|
||||
for (let listener of this._listeners[aEvent]) {
|
||||
// If the inspector was destroyed during event emission, stop
|
||||
// emitting.
|
||||
if (!this._listeners) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If listeners were removed during emission, make sure the
|
||||
// event handler we're going to fire wasn't removed.
|
||||
if (originalListeners === this._listeners[aEvent] ||
|
||||
this._listeners[aEvent].some(function(l) l === listener)) {
|
||||
listener.apply(null, arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -513,6 +611,7 @@ InspectorUI.prototype = {
|
|||
// Has this windowID been inspected before?
|
||||
if (this.store.hasID(this.winID)) {
|
||||
this._currentInspector = this.store.getInspector(this.winID);
|
||||
this._currentInspector._thaw();
|
||||
let selectedNode = this.currentInspector._selectedNode;
|
||||
if (selectedNode) {
|
||||
this.inspectNode(selectedNode);
|
||||
|
@ -646,9 +745,12 @@ InspectorUI.prototype = {
|
|||
this.breadcrumbs = null;
|
||||
}
|
||||
|
||||
delete this._currentInspector;
|
||||
if (!aKeepInspector)
|
||||
if (aKeepInspector) {
|
||||
this._currentInspector._freeze();
|
||||
} else {
|
||||
this.store.deleteInspector(this.winID);
|
||||
}
|
||||
delete this._currentInspector;
|
||||
|
||||
this.inspectorUICommand.setAttribute("checked", "false");
|
||||
|
||||
|
@ -691,6 +793,7 @@ InspectorUI.prototype = {
|
|||
|
||||
_notifySelected: function IUI__notifySelected(aFrom)
|
||||
{
|
||||
this._currentInspector._cancelLayoutChange();
|
||||
this._currentInspector._emit("select", aFrom);
|
||||
},
|
||||
|
||||
|
@ -1477,9 +1580,6 @@ InspectorStyleSidebar.prototype = {
|
|||
// wire up button to show the iframe
|
||||
let onClick = function() {
|
||||
this.activatePanel(aRegObj.id);
|
||||
// Cheat a little bit and trigger a refresh
|
||||
// when switching panels.
|
||||
this._inspector.change("activatepanel-" + aRegObj.id);
|
||||
}.bind(this);
|
||||
btn.addEventListener("click", onClick, true);
|
||||
|
||||
|
@ -1636,7 +1736,13 @@ InspectorStyleSidebar.prototype = {
|
|||
aTool.context = aTool.registration.load(this._inspector, aTool.frame);
|
||||
|
||||
this._inspector._emit("sidebaractivated", aTool.id);
|
||||
this._inspector._emit("sidebaractivated-" + aTool.id);
|
||||
|
||||
// Send an event specific to the activation of this panel. For
|
||||
// this initial event, include a "createpanel" argument
|
||||
// to let panels watch sidebaractivated to refresh themselves
|
||||
// but ignore the one immediately after their load.
|
||||
// I don't really like this, we should find a better solution.
|
||||
this._inspector._emit("sidebaractivated-" + aTool.id, "createpanel");
|
||||
}.bind(this);
|
||||
aTool.frame.addEventListener("load", aTool.onLoad, true);
|
||||
aTool.frame.setAttribute("src", aTool.registration.contentURL);
|
||||
|
|
|
@ -46,6 +46,8 @@ include $(topsrcdir)/config/rules.mk
|
|||
|
||||
_BROWSER_FILES = \
|
||||
browser_responsiveui.js \
|
||||
browser_responsiveruleview.js \
|
||||
browser_responsivecomputedview.js \
|
||||
$(NULL)
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
let instance;
|
||||
|
||||
let computedView;
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onload() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
|
||||
waitForFocus(startTest, content);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,<html><style>" +
|
||||
"div {" +
|
||||
" width: 500px;" +
|
||||
" height: 10px;" +
|
||||
" background: purple;" +
|
||||
"} " +
|
||||
"@media screen and (max-width: 200px) {" +
|
||||
" div { " +
|
||||
" width: 100px;" +
|
||||
" }" +
|
||||
"};" +
|
||||
"</style><div></div></html>"
|
||||
|
||||
function computedWidth() {
|
||||
for (let prop of computedView.propertyViews) {
|
||||
if (prop.name === "width") {
|
||||
return prop.valueNode.textContent;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
document.getElementById("Tools:ResponsiveUI").doCommand();
|
||||
executeSoon(onUIOpen);
|
||||
}
|
||||
|
||||
function onUIOpen() {
|
||||
instance = gBrowser.selectedTab.responsiveUI;
|
||||
ok(instance, "instance of the module is attached to the tab.");
|
||||
|
||||
instance.stack.setAttribute("notransition", "true");
|
||||
registerCleanupFunction(function() {
|
||||
instance.stack.removeAttribute("notransition");
|
||||
});
|
||||
|
||||
instance.setSize(500, 500);
|
||||
|
||||
Services.obs.addObserver(onInspectorUIOpen,
|
||||
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
|
||||
InspectorUI.openInspectorUI();
|
||||
}
|
||||
|
||||
function onInspectorUIOpen() {
|
||||
Services.obs.removeObserver(onInspectorUIOpen,
|
||||
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
|
||||
|
||||
let div = content.document.getElementsByTagName("div")[0];
|
||||
InspectorUI.inspectNode(div);
|
||||
InspectorUI.stopInspecting();
|
||||
|
||||
Services.obs.addObserver(testShrink, "StyleInspector-populated", false);
|
||||
|
||||
InspectorUI.sidebar.show();
|
||||
InspectorUI.sidebar.activatePanel("computedview");
|
||||
}
|
||||
|
||||
function testShrink() {
|
||||
Services.obs.removeObserver(testShrink, "StyleInspector-populated", false);
|
||||
|
||||
computedView = InspectorUI.sidebar._toolContext("computedview").view;
|
||||
|
||||
is(computedWidth(), "500px", "Should show 500px initially.");
|
||||
|
||||
Services.obs.addObserver(function onShrink() {
|
||||
Services.obs.removeObserver(onShrink, "StyleInspector-populated");
|
||||
is(computedWidth(), "100px", "div should be 100px after shrinking.");
|
||||
testGrow();
|
||||
}, "StyleInspector-populated", false);
|
||||
|
||||
instance.setSize(100, 100);
|
||||
}
|
||||
|
||||
function testGrow() {
|
||||
Services.obs.addObserver(function onGrow() {
|
||||
Services.obs.removeObserver(onGrow, "StyleInspector-populated");
|
||||
is(computedWidth(), "500px", "Should be 500px after growing.");
|
||||
finishUp();
|
||||
}, "StyleInspector-populated", false);
|
||||
|
||||
instance.setSize(500, 500);
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
document.getElementById("Tools:ResponsiveUI").doCommand();
|
||||
|
||||
// Menus are correctly updated?
|
||||
is(document.getElementById("Tools:ResponsiveUI").getAttribute("checked"), "false", "menu unchecked");
|
||||
|
||||
InspectorUI.closeInspectorUI();
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
let instance;
|
||||
|
||||
let ruleView;
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onload() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
|
||||
waitForFocus(startTest, content);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,<html><style>" +
|
||||
"div {" +
|
||||
" width: 500px;" +
|
||||
" height: 10px;" +
|
||||
" background: purple;" +
|
||||
"} " +
|
||||
"@media screen and (max-width: 200px) {" +
|
||||
" div { " +
|
||||
" width: 100px;" +
|
||||
" }" +
|
||||
"};" +
|
||||
"</style><div></div></html>"
|
||||
|
||||
function numberOfRules() {
|
||||
return ruleView.element.querySelectorAll(".ruleview-code").length;
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
document.getElementById("Tools:ResponsiveUI").doCommand();
|
||||
executeSoon(onUIOpen);
|
||||
}
|
||||
|
||||
function onUIOpen() {
|
||||
instance = gBrowser.selectedTab.responsiveUI;
|
||||
ok(instance, "instance of the module is attached to the tab.");
|
||||
|
||||
instance.stack.setAttribute("notransition", "true");
|
||||
registerCleanupFunction(function() {
|
||||
instance.stack.removeAttribute("notransition");
|
||||
});
|
||||
|
||||
instance.setSize(500, 500);
|
||||
|
||||
Services.obs.addObserver(onInspectorUIOpen,
|
||||
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
|
||||
InspectorUI.openInspectorUI();
|
||||
}
|
||||
|
||||
function onInspectorUIOpen() {
|
||||
Services.obs.removeObserver(onInspectorUIOpen,
|
||||
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
|
||||
|
||||
let div = content.document.getElementsByTagName("div")[0];
|
||||
InspectorUI.inspectNode(div);
|
||||
InspectorUI.stopInspecting();
|
||||
|
||||
InspectorUI.currentInspector.once("sidebaractivated-ruleview", testShrink);
|
||||
|
||||
InspectorUI.sidebar.show();
|
||||
InspectorUI.sidebar.activatePanel("ruleview");
|
||||
}
|
||||
|
||||
function testShrink() {
|
||||
ruleView = InspectorUI.sidebar._toolContext("ruleview").view;
|
||||
|
||||
is(numberOfRules(), 2, "Should have two rules initially.");
|
||||
|
||||
ruleView.element.addEventListener("CssRuleViewRefreshed", function refresh() {
|
||||
ruleView.element.removeEventListener("CssRuleViewRefreshed", refresh, false);
|
||||
is(numberOfRules(), 3, "Should have three rules after shrinking.");
|
||||
testGrow();
|
||||
}, false);
|
||||
|
||||
instance.setSize(100, 100);
|
||||
}
|
||||
|
||||
function testGrow() {
|
||||
ruleView.element.addEventListener("CssRuleViewRefreshed", function refresh() {
|
||||
ruleView.element.removeEventListener("CssRuleViewRefreshed", refresh, false);
|
||||
is(numberOfRules(), 2, "Should have two rules after growing.");
|
||||
finishUp();
|
||||
}, false);
|
||||
|
||||
instance.setSize(500, 500);
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
document.getElementById("Tools:ResponsiveUI").doCommand();
|
||||
|
||||
// Menus are correctly updated?
|
||||
is(document.getElementById("Tools:ResponsiveUI").getAttribute("checked"), "false", "menu unchecked");
|
||||
|
||||
InspectorUI.closeInspectorUI();
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
}
|
|
@ -880,6 +880,13 @@ CssRuleView.prototype = {
|
|||
// The element that we're inspecting.
|
||||
_viewedElement: null,
|
||||
|
||||
/**
|
||||
* Returns true if the rule view currently has an input editor visible.
|
||||
*/
|
||||
get isEditing() {
|
||||
return this.element.querySelectorAll(".styleinspector-propertyeditor").length > 0;
|
||||
},
|
||||
|
||||
destroy: function CssRuleView_destroy()
|
||||
{
|
||||
this.clear();
|
||||
|
@ -946,11 +953,21 @@ CssRuleView.prototype = {
|
|||
*/
|
||||
nodeChanged: function CssRuleView_nodeChanged()
|
||||
{
|
||||
// Ignore refreshes during editing.
|
||||
if (this.isEditing) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Repopulate the element style.
|
||||
this._elementStyle.populate();
|
||||
|
||||
// Refresh the rule editors.
|
||||
this._createEditors();
|
||||
|
||||
// Notify anyone that cares that we refreshed.
|
||||
var evt = this.doc.createEvent("Events");
|
||||
evt.initEvent("CssRuleViewRefreshed", true, false);
|
||||
this.element.dispatchEvent(evt);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Cu = Components.utils;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
|
@ -116,6 +117,7 @@ function RuleViewTool(aInspector, aFrame)
|
|||
|
||||
this._onChange = this.onChange.bind(this);
|
||||
this.inspector.on("change", this._onChange);
|
||||
this.inspector.on("sidebaractivated-ruleview", this._onChange);
|
||||
|
||||
this.onSelect();
|
||||
}
|
||||
|
@ -134,11 +136,11 @@ RuleViewTool.prototype = {
|
|||
},
|
||||
|
||||
onChange: function RVT_onChange(aEvent, aFrom) {
|
||||
if (aFrom == "ruleview") {
|
||||
if (aFrom == "ruleview" || aFrom == "createpanel") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.inspector.locked) {
|
||||
if (this.inspector.locked && this.inspector.isPanelVisible("ruleview")) {
|
||||
this.view.nodeChanged();
|
||||
}
|
||||
},
|
||||
|
@ -146,6 +148,7 @@ RuleViewTool.prototype = {
|
|||
destroy: function RVT_destroy() {
|
||||
this.inspector.removeListener("select", this._onSelect);
|
||||
this.inspector.removeListener("change", this._onChange);
|
||||
this.inspector.removeListener("sidebaractivated-ruleview", this._onChange);
|
||||
this.view.element.removeEventListener("CssRuleViewChanged",
|
||||
this._changeHandler);
|
||||
this.view.element.removeEventListener("CssRuleViewCSSLinkClicked",
|
||||
|
@ -172,14 +175,13 @@ function ComputedViewTool(aInspector, aFrame)
|
|||
|
||||
this._onSelect = this.onSelect.bind(this);
|
||||
this.inspector.on("select", this._onSelect);
|
||||
|
||||
this._onChange = this.onChange.bind(this);
|
||||
this.inspector.on("change", this._onChange);
|
||||
|
||||
// Since refreshes of the computed view are non-destructive,
|
||||
// refresh when the tab is changed so we can notice script-driven
|
||||
// changes.
|
||||
this.inspector.on("sidebaractivated", this._onChange);
|
||||
this.inspector.on("sidebaractivated-computedview", this._onChange);
|
||||
|
||||
this.cssLogic.highlight(null);
|
||||
this.view.highlight(null);
|
||||
|
@ -199,19 +201,22 @@ ComputedViewTool.prototype = {
|
|||
onChange: function CVT_change(aEvent, aFrom)
|
||||
{
|
||||
if (aFrom == "computedview" ||
|
||||
aFrom == "createpanel" ||
|
||||
this.inspector.selection != this.cssLogic.viewedElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.inspector.locked && this.inspector.isPanelVisible("computedview")) {
|
||||
this.cssLogic.highlight(this.inspector.selection);
|
||||
this.view.refreshPanel();
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function CVT_destroy(aContext)
|
||||
{
|
||||
this.inspector.removeListener("select", this._onSelect);
|
||||
this.inspector.removeListener("change", this._onChange);
|
||||
this.inspector.removeListener("sidebaractivated", this._onChange);
|
||||
this.inspector.removeListener("sidebaractivated-computedview", this._onChange);
|
||||
this.view.destroy();
|
||||
delete this.view;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче