mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 13:51:41 +00:00
Merge fx-team to m-c.
This commit is contained in:
commit
99581e1939
@ -477,7 +477,7 @@ let CustomizableUIInternal = {
|
||||
|
||||
this.insertWidgetBefore(node, currentNode, container, aArea);
|
||||
if (gResetting) {
|
||||
this.notifyListeners("onWidgetReset", id, aArea);
|
||||
this.notifyListeners("onWidgetReset", node, container);
|
||||
}
|
||||
}
|
||||
|
||||
@ -527,7 +527,7 @@ let CustomizableUIInternal = {
|
||||
}
|
||||
|
||||
if (gResetting) {
|
||||
this.notifyListeners("onAreaReset", aArea);
|
||||
this.notifyListeners("onAreaReset", aArea, container);
|
||||
}
|
||||
} finally {
|
||||
this.endBatchUpdate();
|
||||
@ -668,6 +668,8 @@ let CustomizableUIInternal = {
|
||||
}
|
||||
|
||||
let area = gAreas.get(aArea);
|
||||
let isToolbar = area.get("type") == CustomizableUI.TYPE_TOOLBAR;
|
||||
let isOverflowable = isToolbar && area.get("overflowable");
|
||||
let showInPrivateBrowsing = gPalette.has(aWidgetId)
|
||||
? gPalette.get(aWidgetId).showInPrivateBrowsing
|
||||
: true;
|
||||
@ -679,12 +681,15 @@ let CustomizableUIInternal = {
|
||||
continue;
|
||||
}
|
||||
|
||||
let container = areaNode.customizationTarget;
|
||||
let widgetNode = container.ownerDocument.getElementById(aWidgetId);
|
||||
let widgetNode = window.document.getElementById(aWidgetId);
|
||||
if (!widgetNode) {
|
||||
ERROR("Widget not found, unable to remove");
|
||||
continue;
|
||||
}
|
||||
let container = areaNode.customizationTarget;
|
||||
if (isOverflowable) {
|
||||
container = areaNode.overflowable.getContainerFor(widgetNode);
|
||||
}
|
||||
|
||||
this.notifyListeners("onWidgetBeforeDOMChange", widgetNode, null, container, true);
|
||||
|
||||
@ -702,7 +707,7 @@ let CustomizableUIInternal = {
|
||||
}
|
||||
this.notifyListeners("onWidgetAfterDOMChange", widgetNode, null, container, true);
|
||||
|
||||
if (area.get("type") == CustomizableUI.TYPE_TOOLBAR) {
|
||||
if (isToolbar) {
|
||||
areaNode.setAttribute("currentset", gPlacements.get(aArea).join(','));
|
||||
}
|
||||
|
||||
@ -2094,47 +2099,330 @@ this.CustomizableUI = {
|
||||
get WIDE_PANEL_CLASS() "panel-wide-item",
|
||||
get PANEL_COLUMN_COUNT() 3,
|
||||
|
||||
/**
|
||||
* Add a listener object that will get fired for various events regarding
|
||||
* customization.
|
||||
*
|
||||
* @param aListener the listener object to add
|
||||
*
|
||||
* Not all event handler methods need to be defined.
|
||||
* CustomizableUI will catch exceptions. Events are dispatched
|
||||
* synchronously on the UI thread, so if you can delay any/some of your
|
||||
* processing, that is advisable. The following event handlers are supported:
|
||||
* - onWidgetAdded(aWidgetId, aArea, aPosition)
|
||||
* Fired when a widget is added to an area. aWidgetId is the widget that
|
||||
* was added, aArea the area it was added to, and aPosition the position
|
||||
* in which it was added.
|
||||
* - onWidgetMoved(aWidgetId, aArea, aOldPosition, aNewPosition)
|
||||
* Fired when a widget is moved within its area. aWidgetId is the widget
|
||||
* that was moved, aArea the area it was moved in, aOldPosition its old
|
||||
* position, and aNewPosition its new position.
|
||||
* - onWidgetRemoved(aWidgetId, aArea)
|
||||
* Fired when a widget is removed from its area. aWidgetId is the widget
|
||||
* that was removed, aArea the area it was removed from.
|
||||
*
|
||||
* - onWidgetBeforeDOMChange(aNode, aNextNode, aContainer, aIsRemoval)
|
||||
* Fired *before* a widget's DOM node is acted upon by CustomizableUI
|
||||
* (to add, move or remove it). aNode is the DOM node changed, aNextNode
|
||||
* the DOM node (if any) before which a widget will be inserted,
|
||||
* aContainer the *actual* DOM container (could be an overflow panel in
|
||||
* case of an overflowable toolbar), and aWasRemoval is true iff the
|
||||
* action about to happen is the removal of the DOM node.
|
||||
* - onWidgetAfterDOMChange(aNode, aNextNode, aContainer, aWasRemoval)
|
||||
* Like onWidgetBeforeDOMChange, but fired after the change to the DOM
|
||||
* node of the widget.
|
||||
*
|
||||
* - onWidgetReset(aNode, aContainer)
|
||||
* Fired after a reset to default placements moves a widget's node to a
|
||||
* different location. aNode is the widget's node, aContainer is the
|
||||
* area it was moved into (NB: it might already have been there and been
|
||||
* moved to a different position!)
|
||||
* - onAreaReset(aArea, aContainer)
|
||||
* Fired after a reset to default placements is complete on an area's
|
||||
* DOM node. Note that this is fired for each DOM node. aArea is the area
|
||||
* that was reset, aContainer the DOM node that was reset.
|
||||
*
|
||||
* - onWidgetCreated(aWidgetId)
|
||||
* Fired when a widget with id aWidgetId has been created, but before it
|
||||
* is added to any placements or any DOM nodes have been constructed.
|
||||
* Only fired for API-based widgets.
|
||||
* - onWidgetAfterCreation(aWidgetId, aArea)
|
||||
* Fired after a widget with id aWidgetId has been created, and has been
|
||||
* added to either its default area or the area in which it was placed
|
||||
* previously. If the widget has no default area and/or it has never
|
||||
* been placed anywhere, aArea may be null. Only fired for API-based
|
||||
* widgets.
|
||||
* - onWidgetDestroyed(aWidgetId)
|
||||
* Fired when widgets are destroyed. aWidgetId is the widget that is
|
||||
* being destroyed. Only fired for API-based widgets.
|
||||
* - onWidgetInstanceRemoved(aWidgetId, aDocument)
|
||||
* Fired when a window is unloaded and a widget's instance is destroyed
|
||||
* because of this. Only fired for API-based widgets.
|
||||
*
|
||||
* - onWidgetDrag(aWidgetId, aArea)
|
||||
* Fired both when and after customize mode drag handling system tries
|
||||
* to determine the width and height of widget aWidgetId when dragged to a
|
||||
* different area. aArea will be the area the item is dragged to, or
|
||||
* undefined after the measurements have been done and the node has been
|
||||
* moved back to its 'regular' area.
|
||||
*
|
||||
* - onCustomizeStart(aWindow)
|
||||
* Fired when opening customize mode in aWindow.
|
||||
* - onCustomizeEnd(aWindow)
|
||||
* Fired when exiting customize mode in aWindow.
|
||||
*/
|
||||
addListener: function(aListener) {
|
||||
CustomizableUIInternal.addListener(aListener);
|
||||
},
|
||||
/**
|
||||
* Remove a listener added with addListener
|
||||
* @param aListener the listener object to remove
|
||||
*/
|
||||
removeListener: function(aListener) {
|
||||
CustomizableUIInternal.removeListener(aListener);
|
||||
},
|
||||
|
||||
/**
|
||||
* Register a customizable area with CustomizableUI.
|
||||
* @param aName the name of the area to register. Can only contain
|
||||
* alphanumeric characters, dashes (-) and underscores (_).
|
||||
* @param aProps the properties of the area. The following properties are
|
||||
* recognized:
|
||||
* - type: the type of area. Either TYPE_TOOLBAR (default) or
|
||||
* TYPE_MENU_PANEL;
|
||||
* - anchor: for a menu panel or overflowable toolbar, the
|
||||
* anchoring node for the panel.
|
||||
* - legacy: set to true if you want customizableui to
|
||||
* automatically migrate the currentset attribute
|
||||
* - overflowable: set to true if your toolbar is overflowable.
|
||||
* This requires an anchor, and only has an
|
||||
* effect for toolbars.
|
||||
* - defaultPlacements: an array of widget IDs making up the
|
||||
* default contents of the area
|
||||
*/
|
||||
registerArea: function(aName, aProperties) {
|
||||
CustomizableUIInternal.registerArea(aName, aProperties);
|
||||
},
|
||||
/**
|
||||
* Register a concrete node for a registered area. This method is automatically
|
||||
* called from any toolbar in the main browser window that has its
|
||||
* "customizable" attribute set to true. There should normally be no need to
|
||||
* call it yourself.
|
||||
*
|
||||
* Note that ideally, you should register your toolbar using registerArea
|
||||
* before any of the toolbars have their XBL bindings constructed (which
|
||||
* will happen when they're added to the DOM and are not hidden). If you
|
||||
* don't, and your toolbar has a defaultset attribute, CustomizableUI will
|
||||
* register it automatically. If your toolbar does not have a defaultset
|
||||
* attribute, the node will be saved for processing when you call
|
||||
* registerArea. Note that CustomizableUI won't restore state in the area,
|
||||
* allow the user to customize it in customize mode, or otherwise deal
|
||||
* with it, until the area has been registered.
|
||||
*/
|
||||
registerToolbarNode: function(aToolbar, aExistingChildren) {
|
||||
CustomizableUIInternal.registerToolbarNode(aToolbar, aExistingChildren);
|
||||
},
|
||||
/**
|
||||
* Register the menu panel node. This method should not be called by anyone
|
||||
* apart from the built-in PanelUI.
|
||||
* @param aPanel the panel DOM node being registered.
|
||||
*/
|
||||
registerMenuPanel: function(aPanel) {
|
||||
CustomizableUIInternal.registerMenuPanel(aPanel);
|
||||
},
|
||||
/**
|
||||
* Unregister a customizable area. The inverse of registerArea.
|
||||
*
|
||||
* Unregistering an area will remove all the (removable) widgets in the
|
||||
* area, which will return to the panel, and destroy all other traces
|
||||
* of the area within CustomizableUI. Note that this means the *contents*
|
||||
* of the area's DOM nodes will be moved to the panel or removed, but
|
||||
* the area's DOM nodes *themselves* will stay.
|
||||
*
|
||||
* Furthermore, by default the placements of the area will be kept in the
|
||||
* saved state (!) and restored if you re-register the area at a later
|
||||
* point. This is useful for e.g. add-ons that get disabled and then
|
||||
* re-enabled (e.g. when they update).
|
||||
*
|
||||
* You can override this last behaviour (and destroy the placements
|
||||
* information in the saved state) by passing true for aDestroyPlacements.
|
||||
*
|
||||
* @param aName the name of the area to unregister
|
||||
* @param aDestroyPlacements whether to destroy the placements information
|
||||
* for the area, too.
|
||||
*/
|
||||
unregisterArea: function(aName, aDestroyPlacements) {
|
||||
CustomizableUIInternal.unregisterArea(aName, aDestroyPlacements);
|
||||
},
|
||||
/**
|
||||
* Add a widget to an area.
|
||||
* If the area to which you try to add is not known to CustomizableUI,
|
||||
* this will throw.
|
||||
* If the area to which you try to add has not yet been restored from its
|
||||
* legacy state, this will postpone the addition.
|
||||
* If the area to which you try to add is the same as the area in which
|
||||
* the widget is currently placed, this will do the same as
|
||||
* moveWidgetWithinArea.
|
||||
* If the widget cannot be removed from its original location, this will
|
||||
* no-op.
|
||||
*
|
||||
* This will fire an onWidgetAdded notification,
|
||||
* and an onWidgetBeforeDOMChange and onWidgetAfterDOMChange notification
|
||||
* for each window CustomizableUI knows about.
|
||||
*
|
||||
* @param aWidgetId the widget to add
|
||||
* @param aArea the area to add the widget to
|
||||
* @param aPosition the position at which to add the widget. If you do not
|
||||
* pass a position, the widget will be added to the end
|
||||
* of the area.
|
||||
*/
|
||||
addWidgetToArea: function(aWidgetId, aArea, aPosition) {
|
||||
CustomizableUIInternal.addWidgetToArea(aWidgetId, aArea, aPosition);
|
||||
},
|
||||
/**
|
||||
* Remove a widget from its area. If the widget cannot be removed from its
|
||||
* area, or is not in any area, this will no-op. Otherwise, this will fire an
|
||||
* onWidgetRemoved notification, and an onWidgetBeforeDOMChange and
|
||||
* onWidgetAfterDOMChange notification for each window CustomizableUI knows
|
||||
* about.
|
||||
*
|
||||
* @param aWidgetId the widget to remove
|
||||
*/
|
||||
removeWidgetFromArea: function(aWidgetId) {
|
||||
CustomizableUIInternal.removeWidgetFromArea(aWidgetId);
|
||||
},
|
||||
/**
|
||||
* Move a widget within an area.
|
||||
* If the widget is not in any area, this will no-op.
|
||||
* If the widget is already at the indicated position, this will no-op.
|
||||
*
|
||||
* Otherwise, this will move the widget and fire an onWidgetMoved notification,
|
||||
* and an onWidgetBeforeDOMChange and onWidgetAfterDOMChange notification for
|
||||
* each window CustomizableUI knows about.
|
||||
*
|
||||
* @param aWidgetid the widget to move
|
||||
* @param aPosition the position to move the widget to.
|
||||
* Negative values or values greater than the number of
|
||||
* widgets will be interpreted to mean moving the widget to
|
||||
* respectively the first or last position.
|
||||
*/
|
||||
moveWidgetWithinArea: function(aWidgetId, aPosition) {
|
||||
CustomizableUIInternal.moveWidgetWithinArea(aWidgetId, aPosition);
|
||||
},
|
||||
/**
|
||||
* Ensure a XUL-based widget created in a window after areas were
|
||||
* initialized moves to its correct position.
|
||||
* This is roughly equivalent to manually looking up the position and using
|
||||
* insertItem in the old API, but a lot less work for consumers.
|
||||
* Always prefer this over using toolbar.insertItem (which might no-op
|
||||
* because it delegates to addWidgetToArea) or, worse, moving items in the
|
||||
* DOM yourself.
|
||||
*
|
||||
* @param aWidgetId the widget that was just created
|
||||
* @param aWindow the window in which you want to ensure it was added.
|
||||
*
|
||||
* NB: why is this API per-window, you wonder? Because if you need this,
|
||||
* presumably you yourself need to create the widget in all the windows
|
||||
* and need to loop through them anyway.
|
||||
*/
|
||||
ensureWidgetPlacedInWindow: function(aWidgetId, aWindow) {
|
||||
return CustomizableUIInternal.ensureWidgetPlacedInWindow(aWidgetId, aWindow);
|
||||
},
|
||||
/**
|
||||
* Start a batch update of items.
|
||||
* During a batch update, the customization state is not saved to the user's
|
||||
* preferences file, in order to reduce (possibly sync) IO.
|
||||
* Calls to begin/endBatchUpdate may be nested.
|
||||
*
|
||||
* Callers should ensure that NO MATTER WHAT they call endBatchUpdate once
|
||||
* for each call to endBatchUpdate, even if there are exceptions in the
|
||||
* code in the batch update. Otherwise, for the duration of the
|
||||
* Firefox session, customization state is never saved. Typically, you
|
||||
* would do this using a try...finally block.
|
||||
*/
|
||||
beginBatchUpdate: function() {
|
||||
CustomizableUIInternal.beginBatchUpdate();
|
||||
},
|
||||
/**
|
||||
* End a batch update. See the documentation for beginBatchUpdate above.
|
||||
*
|
||||
* State is not saved if we believe it is identical to the last known
|
||||
* saved state. State is only ever saved when all batch updates have
|
||||
* finished (ie there has been 1 endBatchUpdate call for each
|
||||
* beginBatchUpdate call). If any of the endBatchUpdate calls pass
|
||||
* aForceDirty=true, we will flush to the prefs file.
|
||||
*
|
||||
* @param aForceDirty force CustomizableUI to flush to the prefs file when
|
||||
* all batch updates have finished.
|
||||
*/
|
||||
endBatchUpdate: function(aForceDirty) {
|
||||
CustomizableUIInternal.endBatchUpdate(aForceDirty);
|
||||
},
|
||||
/**
|
||||
* Create a widget.
|
||||
*
|
||||
* To create a widget, you should pass an object with its desired
|
||||
* properties. The following properties are supported:
|
||||
*
|
||||
* - id: the ID of the widget (required).
|
||||
* - type: a string indicating the type of widget. Possible types
|
||||
* are:
|
||||
* 'button' - for simple button widgets (the default)
|
||||
* 'view' - for buttons that open a panel or subview,
|
||||
* depending on where they are placed.
|
||||
* 'custom' - for fine-grained control over the creation
|
||||
* of the widget.
|
||||
* - viewId: Only useful for views (and required there): the id of the
|
||||
* <panelview> that should be shown when clicking the widget.
|
||||
* - onBuild(aDoc): Only useful for custom widgets (and required there); a
|
||||
* function that will be invoked with the document in which
|
||||
* to build a widget. Should return the DOM node that has
|
||||
* been constructed.
|
||||
* - onCreated(aNode): Attached to all widgets; a function that will be invoked
|
||||
* whenever the widget has a DOM node constructed, passing the
|
||||
* constructed node as an argument.
|
||||
* - onCommand(aEvt): Only useful for button widgets; a function that will be
|
||||
* invoked when the user activates the button.
|
||||
* - onClick(aEvt): Attached to all widgets; a function that will be invoked
|
||||
* when the user clicks the widget.
|
||||
* - onViewShowing(aEvt): Only useful for views; a function that will be
|
||||
* invoked when a user shows your view.
|
||||
* - onViewHiding(aEvt): Only useful for views; a function that will be
|
||||
* invoked when a user hides your view.
|
||||
* - tooltiptext: string to use for the tooltip of the widget
|
||||
* - label: string to use for the label of the widget
|
||||
* - removable: whether the widget is removable (optional, default: false)
|
||||
* - overflows: whether widget can overflow when in an overflowable
|
||||
* toolbar (optional, default: true)
|
||||
* - defaultArea: default area to add the widget to
|
||||
* (optional, default: none)
|
||||
* - shortcutId: id of an element that has a shortcut for this widget
|
||||
* (optional, default: null). This is only used to display
|
||||
* the shortcut as part of the tooltip for builtin widgets
|
||||
* (which have strings inside
|
||||
* customizableWidgets.properties). If you're in an add-on,
|
||||
* you should not set this property.
|
||||
* - showInPrivateBrowsing: whether to show the widget in private browsing
|
||||
* mode (optional, default: true)
|
||||
*
|
||||
* @param aProperties the specifications for the widget.
|
||||
*/
|
||||
createWidget: function(aProperties) {
|
||||
return CustomizableUIInternal.wrapWidget(
|
||||
CustomizableUIInternal.createWidget(aProperties)
|
||||
);
|
||||
},
|
||||
/**
|
||||
* Destroy a widget
|
||||
*
|
||||
* If the widget is part of the default placements in an area, this will
|
||||
* remove it from there. It will also remove any DOM instances. However,
|
||||
* it will keep the widget in the placements for whatever area it was
|
||||
* in at the time. You can remove it from there yourself by calling
|
||||
* CustomizableUI.removeWidgetFromArea(aWidgetId).
|
||||
*
|
||||
* @param aWidgetId the widget to destroy
|
||||
*/
|
||||
destroyWidget: function(aWidgetId) {
|
||||
CustomizableUIInternal.destroyWidget(aWidgetId);
|
||||
},
|
||||
@ -2765,7 +3053,14 @@ OverflowableToolbar.prototype = {
|
||||
return [this._target, null];
|
||||
}
|
||||
return [this._list, nextNode];
|
||||
}
|
||||
},
|
||||
|
||||
getContainerFor: function(aNode) {
|
||||
if (aNode.classList.contains("overflowedItem")) {
|
||||
return this._list;
|
||||
}
|
||||
return this._target;
|
||||
},
|
||||
};
|
||||
|
||||
// When IDs contain special characters, we need to escape them for use with querySelector:
|
||||
|
@ -406,8 +406,8 @@ const CustomizableWidgets = [{
|
||||
updateZoomResetButton();
|
||||
}.bind(this),
|
||||
|
||||
onWidgetReset: function(aWidgetId) {
|
||||
if (aWidgetId != this.id)
|
||||
onWidgetReset: function(aWidgetNode) {
|
||||
if (aWidgetNode != node)
|
||||
return;
|
||||
updateCombinedWidgetStyle(node, this.currentArea, true);
|
||||
updateZoomResetButton();
|
||||
@ -506,8 +506,8 @@ const CustomizableWidgets = [{
|
||||
updateCombinedWidgetStyle(node);
|
||||
}.bind(this),
|
||||
|
||||
onWidgetReset: function(aWidgetId) {
|
||||
if (aWidgetId != this.id)
|
||||
onWidgetReset: function(aWidgetNode) {
|
||||
if (aWidgetNode != node)
|
||||
return;
|
||||
updateCombinedWidgetStyle(node, this.currentArea);
|
||||
}.bind(this),
|
||||
@ -814,4 +814,4 @@ if (isWin8OrHigher()) {
|
||||
});
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
@ -101,7 +101,7 @@ AreaPositionManager.prototype = {
|
||||
let doc = aContainer.ownerDocument;
|
||||
let draggedItem = doc.getElementById(aDraggedItemId);
|
||||
// If dragging a wide item, always pick the first item in a row:
|
||||
if (draggedItem &&
|
||||
if (this._inPanel && draggedItem &&
|
||||
draggedItem.classList.contains(CustomizableUI.WIDE_PANEL_CLASS)) {
|
||||
return this._firstInRow(closest);
|
||||
}
|
||||
|
@ -149,7 +149,8 @@ ToolbarView.prototype = {
|
||||
_onStepOverPressed: function() {
|
||||
if (DebuggerController.activeThread.paused) {
|
||||
DebuggerController.StackFrames.currentFrameDepth = -1;
|
||||
DebuggerController.activeThread.stepOver();
|
||||
let warn = DebuggerController._ensureResumptionOrder;
|
||||
DebuggerController.activeThread.stepOver(warn);
|
||||
}
|
||||
},
|
||||
|
||||
@ -159,7 +160,8 @@ ToolbarView.prototype = {
|
||||
_onStepInPressed: function() {
|
||||
if (DebuggerController.activeThread.paused) {
|
||||
DebuggerController.StackFrames.currentFrameDepth = -1;
|
||||
DebuggerController.activeThread.stepIn();
|
||||
let warn = DebuggerController._ensureResumptionOrder;
|
||||
DebuggerController.activeThread.stepIn(warn);
|
||||
}
|
||||
},
|
||||
|
||||
@ -169,7 +171,8 @@ ToolbarView.prototype = {
|
||||
_onStepOutPressed: function() {
|
||||
if (DebuggerController.activeThread.paused) {
|
||||
DebuggerController.StackFrames.currentFrameDepth = -1;
|
||||
DebuggerController.activeThread.stepOut();
|
||||
let warn = DebuggerController._ensureResumptionOrder;
|
||||
DebuggerController.activeThread.stepOut(warn);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -484,22 +484,22 @@ function testOriginalRawDataIntegrity(arr, obj) {
|
||||
function testAnonymousHeaders(fooScope, anonymousVar, anonymousScope, barVar, bazProperty) {
|
||||
is(fooScope.header, true,
|
||||
"A named scope should have a header visible.");
|
||||
is(fooScope.target.hasAttribute("non-header"), false,
|
||||
is(fooScope.target.hasAttribute("untitled"), false,
|
||||
"The non-header attribute should not be applied to scopes with headers.");
|
||||
|
||||
is(anonymousScope.header, false,
|
||||
"An anonymous scope should have a header visible.");
|
||||
is(anonymousScope.target.hasAttribute("non-header"), true,
|
||||
is(anonymousScope.target.hasAttribute("untitled"), true,
|
||||
"The non-header attribute should not be applied to scopes without headers.");
|
||||
|
||||
is(barVar.header, true,
|
||||
"A named variable should have a header visible.");
|
||||
is(barVar.target.hasAttribute("non-header"), false,
|
||||
is(barVar.target.hasAttribute("untitled"), false,
|
||||
"The non-header attribute should not be applied to variables with headers.");
|
||||
|
||||
is(anonymousVar.header, false,
|
||||
"An anonymous variable should have a header visible.");
|
||||
is(anonymousVar.target.hasAttribute("non-header"), true,
|
||||
is(anonymousVar.target.hasAttribute("untitled"), true,
|
||||
"The non-header attribute should not be applied to variables without headers.");
|
||||
}
|
||||
|
||||
|
@ -72,29 +72,29 @@ function testVariablesAndPropertiesFiltering() {
|
||||
is(constr2Var.expanded, true,
|
||||
"The constr2Var should be expanded.");
|
||||
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([non-match])").length, 1,
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([unmatched])").length, 1,
|
||||
"There should be 1 variable displayed in the local scope.");
|
||||
is(withScope.target.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,
|
||||
is(withScope.target.querySelectorAll(".variables-view-variable:not([unmatched])").length, 0,
|
||||
"There should be 0 variables displayed in the with scope.");
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([unmatched])").length, 0,
|
||||
"There should be 0 variables displayed in the function scope.");
|
||||
isnot(globalScope.target.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,
|
||||
isnot(globalScope.target.querySelectorAll(".variables-view-variable:not([unmatched])").length, 0,
|
||||
"There should be some variables displayed in the global scope.");
|
||||
|
||||
is(withScope.target.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
|
||||
is(withScope.target.querySelectorAll(".variables-view-property:not([unmatched])").length, 0,
|
||||
"There should be 0 properties displayed in the with scope.");
|
||||
is(functionScope.target.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
|
||||
is(functionScope.target.querySelectorAll(".variables-view-property:not([unmatched])").length, 0,
|
||||
"There should be 0 properties displayed in the function scope.");
|
||||
is(globalScope.target.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
|
||||
is(globalScope.target.querySelectorAll(".variables-view-property:not([unmatched])").length, 0,
|
||||
"There should be 0 properties displayed in the global scope.");
|
||||
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"),
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([unmatched]) > .title > .name")[0].getAttribute("value"),
|
||||
"__proto__", "The only inner variable displayed should be '__proto__'");
|
||||
is(localScope.target.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[0].getAttribute("value"),
|
||||
is(localScope.target.querySelectorAll(".variables-view-property:not([unmatched]) > .title > .name")[0].getAttribute("value"),
|
||||
"constructor", "The first inner property displayed should be 'constructor'");
|
||||
is(localScope.target.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[1].getAttribute("value"),
|
||||
is(localScope.target.querySelectorAll(".variables-view-property:not([unmatched]) > .title > .name")[1].getAttribute("value"),
|
||||
"__proto__", "The second inner property displayed should be '__proto__'");
|
||||
is(localScope.target.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[2].getAttribute("value"),
|
||||
is(localScope.target.querySelectorAll(".variables-view-property:not([unmatched]) > .title > .name")[2].getAttribute("value"),
|
||||
"constructor", "The third inner property displayed should be 'constructor'");
|
||||
}
|
||||
|
||||
|
@ -72,36 +72,36 @@ function testVariablesAndPropertiesFiltering() {
|
||||
is(constr2Var.expanded, true,
|
||||
"The constr2Var should be expanded.");
|
||||
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([non-match])").length, 1,
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([unmatched])").length, 1,
|
||||
"There should be 1 variable displayed in the local scope.");
|
||||
is(withScope.target.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,
|
||||
is(withScope.target.querySelectorAll(".variables-view-variable:not([unmatched])").length, 0,
|
||||
"There should be 0 variables displayed in the with scope.");
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([unmatched])").length, 0,
|
||||
"There should be 0 variables displayed in the function scope.");
|
||||
is(globalScope.target.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,
|
||||
is(globalScope.target.querySelectorAll(".variables-view-variable:not([unmatched])").length, 0,
|
||||
"There should be no variables displayed in the global scope.");
|
||||
|
||||
is(localScope.target.querySelectorAll(".variables-view-property:not([non-match])").length, 4,
|
||||
is(localScope.target.querySelectorAll(".variables-view-property:not([unmatched])").length, 4,
|
||||
"There should be 4 properties displayed in the local scope.");
|
||||
is(withScope.target.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
|
||||
is(withScope.target.querySelectorAll(".variables-view-property:not([unmatched])").length, 0,
|
||||
"There should be 0 properties displayed in the with scope.");
|
||||
is(functionScope.target.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
|
||||
is(functionScope.target.querySelectorAll(".variables-view-property:not([unmatched])").length, 0,
|
||||
"There should be 0 properties displayed in the function scope.");
|
||||
is(globalScope.target.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
|
||||
is(globalScope.target.querySelectorAll(".variables-view-property:not([unmatched])").length, 0,
|
||||
"There should be 0 properties displayed in the global scope.");
|
||||
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"),
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([unmatched]) > .title > .name")[0].getAttribute("value"),
|
||||
"__proto__", "The only inner variable displayed should be '__proto__'");
|
||||
is(localScope.target.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[0].getAttribute("value"),
|
||||
is(localScope.target.querySelectorAll(".variables-view-property:not([unmatched]) > .title > .name")[0].getAttribute("value"),
|
||||
"constructor", "The first inner property displayed should be 'constructor'");
|
||||
is(localScope.target.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[1].getAttribute("value"),
|
||||
is(localScope.target.querySelectorAll(".variables-view-property:not([unmatched]) > .title > .name")[1].getAttribute("value"),
|
||||
"__proto__", "The second inner property displayed should be '__proto__'");
|
||||
is(localScope.target.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[2].getAttribute("value"),
|
||||
is(localScope.target.querySelectorAll(".variables-view-property:not([unmatched]) > .title > .name")[2].getAttribute("value"),
|
||||
"constructor", "The third inner property displayed should be 'constructor'");
|
||||
|
||||
is(localScope.target.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[3].getAttribute("value"),
|
||||
is(localScope.target.querySelectorAll(".variables-view-property:not([unmatched]) > .title > .name")[3].getAttribute("value"),
|
||||
"name", "The fourth inner property displayed should be 'name'");
|
||||
is(localScope.target.querySelectorAll(".variables-view-property:not([non-match]) > .title > .value")[3].getAttribute("value"),
|
||||
is(localScope.target.querySelectorAll(".variables-view-property:not([unmatched]) > .title > .value")[3].getAttribute("value"),
|
||||
"\"Function\"", "The fourth inner property displayed should be '\"Function\"'");
|
||||
}
|
||||
|
||||
|
@ -58,20 +58,20 @@ function testVariablesAndPropertiesFiltering() {
|
||||
is(globalScope.expanded, true,
|
||||
"The globalScope should be expanded.");
|
||||
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([non-match])").length, 1,
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([unmatched])").length, 1,
|
||||
"There should be 1 variable displayed in the local scope.");
|
||||
is(withScope.target.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,
|
||||
is(withScope.target.querySelectorAll(".variables-view-variable:not([unmatched])").length, 0,
|
||||
"There should be 0 variables displayed in the with scope.");
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([unmatched])").length, 0,
|
||||
"There should be 0 variables displayed in the function scope.");
|
||||
|
||||
is(localScope.target.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
|
||||
is(localScope.target.querySelectorAll(".variables-view-property:not([unmatched])").length, 0,
|
||||
"There should be 0 properties displayed in the local scope.");
|
||||
is(withScope.target.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
|
||||
is(withScope.target.querySelectorAll(".variables-view-property:not([unmatched])").length, 0,
|
||||
"There should be 0 properties displayed in the with scope.");
|
||||
is(functionScope.target.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
|
||||
is(functionScope.target.querySelectorAll(".variables-view-property:not([unmatched])").length, 0,
|
||||
"There should be 0 properties displayed in the function scope.");
|
||||
is(globalScope.target.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
|
||||
is(globalScope.target.querySelectorAll(".variables-view-property:not([unmatched])").length, 0,
|
||||
"There should be 0 properties displayed in the global scope.");
|
||||
}
|
||||
|
||||
@ -79,9 +79,9 @@ function testVariablesAndPropertiesFiltering() {
|
||||
typeText(gSearchBox, "*one");
|
||||
testFiltered("one");
|
||||
|
||||
isnot(globalScope.target.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,
|
||||
isnot(globalScope.target.querySelectorAll(".variables-view-variable:not([unmatched])").length, 0,
|
||||
"There should be some variables displayed in the global scope.");
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"),
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([unmatched]) > .title > .name")[0].getAttribute("value"),
|
||||
"one", "The only inner variable displayed should be 'one'");
|
||||
}
|
||||
|
||||
@ -104,9 +104,9 @@ function testVariablesAndPropertiesFiltering() {
|
||||
typeText(gSearchBox, "*two");
|
||||
testFiltered("two");
|
||||
|
||||
is(globalScope.target.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,
|
||||
is(globalScope.target.querySelectorAll(".variables-view-variable:not([unmatched])").length, 0,
|
||||
"There should be no variables displayed in the global scope.");
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"),
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([unmatched]) > .title > .name")[0].getAttribute("value"),
|
||||
"two", "The only inner variable displayed should be 'two'");
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@ function testVariablesAndPropertiesFiltering() {
|
||||
assertScopeExpansion([true, true, true, true]);
|
||||
assertVariablesCountAtLeast([0, 0, 1, 0]);
|
||||
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"),
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([unmatched]) > .title > .name")[0].getAttribute("value"),
|
||||
"arguments", "The arguments pseudoarray should be visible.");
|
||||
is(functionScope.get("arguments").expanded, false,
|
||||
"The arguments pseudoarray in functionScope should not be expanded.");
|
||||
@ -69,12 +69,12 @@ function testVariablesAndPropertiesFiltering() {
|
||||
assertScopeExpansion([true, true, true, true]);
|
||||
assertVariablesCountAtLeast([0, 0, 1, 1]);
|
||||
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"),
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([unmatched]) > .title > .name")[0].getAttribute("value"),
|
||||
"arguments", "The arguments pseudoarray should be visible.");
|
||||
is(functionScope.get("arguments").expanded, false,
|
||||
"The arguments pseudoarray in functionScope should not be expanded.");
|
||||
|
||||
is(globalScope.target.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"),
|
||||
is(globalScope.target.querySelectorAll(".variables-view-variable:not([unmatched]) > .title > .name")[0].getAttribute("value"),
|
||||
"EventTarget", "The EventTarget object should be visible.");
|
||||
is(globalScope.get("EventTarget").expanded, false,
|
||||
"The EventTarget object in globalScope should not be expanded.");
|
||||
@ -85,17 +85,17 @@ function testVariablesAndPropertiesFiltering() {
|
||||
assertScopeExpansion([true, true, true, true]);
|
||||
assertVariablesCountAtLeast([0, 1, 3, 1]);
|
||||
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"),
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([unmatched]) > .title > .name")[0].getAttribute("value"),
|
||||
"aNumber", "The aNumber param should be visible.");
|
||||
is(functionScope.get("aNumber").expanded, false,
|
||||
"The aNumber param in functionScope should not be expanded.");
|
||||
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[1].getAttribute("value"),
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([unmatched]) > .title > .name")[1].getAttribute("value"),
|
||||
"a", "The a variable should be visible.");
|
||||
is(functionScope.get("a").expanded, false,
|
||||
"The a variable in functionScope should not be expanded.");
|
||||
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[2].getAttribute("value"),
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([unmatched]) > .title > .name")[2].getAttribute("value"),
|
||||
"arguments", "The arguments pseudoarray should be visible.");
|
||||
is(functionScope.get("arguments").expanded, false,
|
||||
"The arguments pseudoarray in functionScope should not be expanded.");
|
||||
@ -106,37 +106,37 @@ function testVariablesAndPropertiesFiltering() {
|
||||
assertScopeExpansion([true, true, true, true]);
|
||||
assertVariablesCountAtLeast([4, 1, 3, 1]);
|
||||
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"),
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([unmatched]) > .title > .name")[0].getAttribute("value"),
|
||||
"this", "The this reference should be visible.");
|
||||
is(localScope.get("this").expanded, false,
|
||||
"The this reference in localScope should not be expanded.");
|
||||
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[1].getAttribute("value"),
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([unmatched]) > .title > .name")[1].getAttribute("value"),
|
||||
"one", "The one variable should be visible.");
|
||||
is(localScope.get("one").expanded, false,
|
||||
"The one variable in localScope should not be expanded.");
|
||||
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[2].getAttribute("value"),
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([unmatched]) > .title > .name")[2].getAttribute("value"),
|
||||
"two", "The two variable should be visible.");
|
||||
is(localScope.get("two").expanded, false,
|
||||
"The two variable in localScope should not be expanded.");
|
||||
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[3].getAttribute("value"),
|
||||
is(localScope.target.querySelectorAll(".variables-view-variable:not([unmatched]) > .title > .name")[3].getAttribute("value"),
|
||||
"__proto__", "The __proto__ reference should be visible.");
|
||||
is(localScope.get("__proto__").expanded, false,
|
||||
"The __proto__ reference in localScope should not be expanded.");
|
||||
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"),
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([unmatched]) > .title > .name")[0].getAttribute("value"),
|
||||
"aNumber", "The aNumber param should be visible.");
|
||||
is(functionScope.get("aNumber").expanded, false,
|
||||
"The aNumber param in functionScope should not be expanded.");
|
||||
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[1].getAttribute("value"),
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([unmatched]) > .title > .name")[1].getAttribute("value"),
|
||||
"a", "The a variable should be visible.");
|
||||
is(functionScope.get("a").expanded, false,
|
||||
"The a variable in functionScope should not be expanded.");
|
||||
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[2].getAttribute("value"),
|
||||
is(functionScope.target.querySelectorAll(".variables-view-variable:not([unmatched]) > .title > .name")[2].getAttribute("value"),
|
||||
"arguments", "The arguments pseudoarray should be visible.");
|
||||
is(functionScope.get("arguments").expanded, false,
|
||||
"The arguments pseudoarray in functionScope should not be expanded.");
|
||||
@ -162,19 +162,19 @@ function testVariablesAndPropertiesFiltering() {
|
||||
}
|
||||
|
||||
function assertVariablesCountAtLeast(aCounts) {
|
||||
ok(localScope.target.querySelectorAll(".variables-view-variable:not([non-match])").length >= aCounts[0],
|
||||
ok(localScope.target.querySelectorAll(".variables-view-variable:not([unmatched])").length >= aCounts[0],
|
||||
"There should be " + aCounts[0] +
|
||||
" variable displayed in the local scope (" + step + ").");
|
||||
|
||||
ok(withScope.target.querySelectorAll(".variables-view-variable:not([non-match])").length >= aCounts[1],
|
||||
ok(withScope.target.querySelectorAll(".variables-view-variable:not([unmatched])").length >= aCounts[1],
|
||||
"There should be " + aCounts[1] +
|
||||
" variable displayed in the with scope (" + step + ").");
|
||||
|
||||
ok(functionScope.target.querySelectorAll(".variables-view-variable:not([non-match])").length >= aCounts[2],
|
||||
ok(functionScope.target.querySelectorAll(".variables-view-variable:not([unmatched])").length >= aCounts[2],
|
||||
"There should be " + aCounts[2] +
|
||||
" variable displayed in the function scope (" + step + ").");
|
||||
|
||||
ok(globalScope.target.querySelectorAll(".variables-view-variable:not([non-match])").length >= aCounts[3],
|
||||
ok(globalScope.target.querySelectorAll(".variables-view-variable:not([unmatched])").length >= aCounts[3],
|
||||
"There should be " + aCounts[3] +
|
||||
" variable displayed in the global scope (" + step + ").");
|
||||
|
||||
|
@ -19,9 +19,9 @@ function test() {
|
||||
is(tooltip.querySelectorAll(".variables-view-container").length, 1,
|
||||
"There should be one variables view container added to the tooltip.");
|
||||
|
||||
is(tooltip.querySelectorAll(".variables-view-scope[non-header]").length, 1,
|
||||
is(tooltip.querySelectorAll(".variables-view-scope[untitled]").length, 1,
|
||||
"There should be one scope with no header displayed.");
|
||||
is(tooltip.querySelectorAll(".variables-view-variable[non-header]").length, 1,
|
||||
is(tooltip.querySelectorAll(".variables-view-variable[untitled]").length, 1,
|
||||
"There should be one variable with no header displayed.");
|
||||
|
||||
is(tooltip.querySelectorAll(".variables-view-property").length, 2,
|
||||
|
@ -19,9 +19,9 @@ function test() {
|
||||
is(tooltip.querySelectorAll(".variables-view-container").length, 1,
|
||||
"There should be one variables view container added to the tooltip.");
|
||||
|
||||
is(tooltip.querySelectorAll(".variables-view-scope[non-header]").length, 1,
|
||||
is(tooltip.querySelectorAll(".variables-view-scope[untitled]").length, 1,
|
||||
"There should be one scope with no header displayed.");
|
||||
is(tooltip.querySelectorAll(".variables-view-variable[non-header]").length, 1,
|
||||
is(tooltip.querySelectorAll(".variables-view-variable[untitled]").length, 1,
|
||||
"There should be one variable with no header displayed.");
|
||||
|
||||
is(tooltip.querySelectorAll(".variables-view-property").length, 7,
|
||||
|
@ -33,9 +33,9 @@ function test() {
|
||||
is(tooltip.querySelectorAll(".devtools-tooltip-simple-text").length, 0,
|
||||
"There should be no simple text node added to the tooltip.");
|
||||
|
||||
is(tooltip.querySelectorAll(".variables-view-scope[non-header]").length, 1,
|
||||
is(tooltip.querySelectorAll(".variables-view-scope[untitled]").length, 1,
|
||||
"There should be one scope with no header displayed.");
|
||||
is(tooltip.querySelectorAll(".variables-view-variable[non-header]").length, 1,
|
||||
is(tooltip.querySelectorAll(".variables-view-variable[untitled]").length, 1,
|
||||
"There should be one variable with no header displayed.");
|
||||
|
||||
ok(tooltip.querySelectorAll(".variables-view-property").length >= propertyCount,
|
||||
|
@ -1476,7 +1476,7 @@ Scope.prototype = {
|
||||
if (this._isHeaderVisible || !this._nameString) {
|
||||
return;
|
||||
}
|
||||
this._target.removeAttribute("non-header");
|
||||
this._target.removeAttribute("untitled");
|
||||
this._isHeaderVisible = true;
|
||||
},
|
||||
|
||||
@ -1489,7 +1489,7 @@ Scope.prototype = {
|
||||
return;
|
||||
}
|
||||
this.expand();
|
||||
this._target.setAttribute("non-header", "");
|
||||
this._target.setAttribute("untitled", "");
|
||||
this._isHeaderVisible = false;
|
||||
},
|
||||
|
||||
@ -1928,10 +1928,10 @@ Scope.prototype = {
|
||||
}
|
||||
if (aStatus) {
|
||||
this._isMatch = true;
|
||||
this.target.removeAttribute("non-match");
|
||||
this.target.removeAttribute("unmatched");
|
||||
} else {
|
||||
this._isMatch = false;
|
||||
this.target.setAttribute("non-match", "");
|
||||
this.target.setAttribute("unmatched", "");
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -50,9 +50,9 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.variables-view-scope[non-header] > .title,
|
||||
.variable-or-property[non-header] > .title,
|
||||
.variable-or-property[non-match] > .title {
|
||||
.variables-view-scope[untitled] > .title,
|
||||
.variable-or-property[untitled] > .title,
|
||||
.variable-or-property[unmatched] > .title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -308,10 +308,11 @@ var BrowserUI = {
|
||||
|
||||
isStartURI: function isStartURI(aURI) {
|
||||
aURI = aURI || Browser.selectedBrowser.currentURI.spec;
|
||||
return aURI == kStartURI;
|
||||
return aURI == kStartURI || aURI == "about:home";
|
||||
},
|
||||
|
||||
updateStartURIAttributes: function (aURI) {
|
||||
let wasStart = Elements.windowState.hasAttribute("startpage");
|
||||
aURI = aURI || Browser.selectedBrowser.currentURI.spec;
|
||||
if (this.isStartURI(aURI)) {
|
||||
ContextUI.displayNavbar();
|
||||
@ -319,6 +320,13 @@ var BrowserUI = {
|
||||
} else if (aURI != "about:blank") { // about:blank is loaded briefly for new tabs; ignore it
|
||||
Elements.windowState.removeAttribute("startpage");
|
||||
}
|
||||
|
||||
let isStart = Elements.windowState.hasAttribute("startpage");
|
||||
if (wasStart != isStart) {
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("StartUIChange", true, true);
|
||||
Browser.selectedBrowser.dispatchEvent(event);
|
||||
}
|
||||
},
|
||||
|
||||
getDisplayURI: function(browser) {
|
||||
|
@ -1305,6 +1305,7 @@ Tab.prototype = {
|
||||
}
|
||||
browser.addEventListener("pageshow", onPageShowEvent, true);
|
||||
browser.addEventListener("DOMWindowCreated", this, false);
|
||||
browser.addEventListener("StartUIChange", this, false);
|
||||
Elements.browsers.addEventListener("SizeChanged", this, false);
|
||||
|
||||
browser.messageManager.addMessageListener("Content:StateChange", this);
|
||||
@ -1316,12 +1317,19 @@ Tab.prototype = {
|
||||
|
||||
updateViewport: function (aEvent) {
|
||||
// <meta name=viewport> is not yet supported; just use the browser size.
|
||||
this.browser.setWindowSize(this.browser.clientWidth, this.browser.clientHeight);
|
||||
let browser = this.browser;
|
||||
|
||||
// On the start page we add padding to keep the browser above the navbar.
|
||||
let paddingBottom = parseInt(getComputedStyle(browser).paddingBottom, 10);
|
||||
let height = browser.clientHeight - paddingBottom;
|
||||
|
||||
browser.setWindowSize(browser.clientWidth, height);
|
||||
},
|
||||
|
||||
handleEvent: function (aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "DOMWindowCreated":
|
||||
case "StartUIChange":
|
||||
this.updateViewport();
|
||||
break;
|
||||
case "SizeChanged":
|
||||
@ -1354,6 +1362,7 @@ Tab.prototype = {
|
||||
destroy: function destroy() {
|
||||
this._browser.messageManager.removeMessageListener("Content:StateChange", this);
|
||||
this._browser.removeEventListener("DOMWindowCreated", this, false);
|
||||
this._browser.removeEventListener("StartUIChange", this, false);
|
||||
Elements.browsers.removeEventListener("SizeChanged", this, false);
|
||||
clearTimeout(this._updateThumbnailTimeout);
|
||||
|
||||
|
@ -467,7 +467,7 @@
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.variable-or-property:not([non-header]) > .variables-view-element-details {
|
||||
.variable-or-property:not([untitled]) > .variables-view-element-details {
|
||||
-moz-margin-start: 10px;
|
||||
}
|
||||
|
||||
@ -632,7 +632,7 @@
|
||||
min-height: 24px;
|
||||
}
|
||||
|
||||
.variable-or-property[non-match] {
|
||||
.variable-or-property[unmatched] {
|
||||
border: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
@ -461,7 +461,7 @@
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.variable-or-property:not([non-header]) > .variables-view-element-details {
|
||||
.variable-or-property:not([untitled]) > .variables-view-element-details {
|
||||
-moz-margin-start: 10px;
|
||||
}
|
||||
|
||||
@ -626,7 +626,7 @@
|
||||
min-height: 24px;
|
||||
}
|
||||
|
||||
.variable-or-property[non-match] {
|
||||
.variable-or-property[unmatched] {
|
||||
border: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
@ -464,7 +464,7 @@
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.variable-or-property:not([non-header]) > .variables-view-element-details {
|
||||
.variable-or-property:not([untitled]) > .variables-view-element-details {
|
||||
-moz-margin-start: 10px;
|
||||
}
|
||||
|
||||
@ -629,7 +629,7 @@
|
||||
min-height: 24px;
|
||||
}
|
||||
|
||||
.variable-or-property[non-match] {
|
||||
.variable-or-property[unmatched] {
|
||||
border: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
@ -91,10 +91,14 @@ classes.dex: proguard-jars
|
||||
@echo 'DX classes.dex'
|
||||
$(DX) --dex --output=classes.dex jars-proguarded $(ANDROID_COMPAT_LIB)
|
||||
|
||||
ifdef MOZ_DEBUG
|
||||
PROGUARD_PASSES=1
|
||||
ifdef MOZ_DISABLE_PROGUARD
|
||||
PROGUARD_PASSES=0
|
||||
else
|
||||
PROGUARD_PASSES=6
|
||||
ifdef MOZ_DEBUG
|
||||
PROGUARD_PASSES=1
|
||||
else
|
||||
PROGUARD_PASSES=6
|
||||
endif
|
||||
endif
|
||||
|
||||
proguard-jars: $(ALL_JARS)
|
||||
|
@ -19,6 +19,7 @@ import org.mozilla.gecko.db.BrowserContract.Schema;
|
||||
import org.mozilla.gecko.db.BrowserContract.SyncColumns;
|
||||
import org.mozilla.gecko.db.BrowserContract.Thumbnails;
|
||||
import org.mozilla.gecko.db.BrowserContract.URLColumns;
|
||||
import org.mozilla.gecko.db.PerProfileDatabases.DatabaseHelperFactory;
|
||||
import org.mozilla.gecko.gfx.BitmapUtils;
|
||||
import org.mozilla.gecko.mozglue.RobocopTarget;
|
||||
import org.mozilla.gecko.sync.Utils;
|
||||
@ -67,6 +68,8 @@ public class BrowserProvider extends ContentProvider {
|
||||
private static final String LOGTAG = "GeckoBrowserProvider";
|
||||
private Context mContext;
|
||||
|
||||
private PerProfileDatabases<BrowserDatabaseHelper> mDatabases;
|
||||
|
||||
static final String DATABASE_NAME = "browser.db";
|
||||
|
||||
static final int DATABASE_VERSION = 17;
|
||||
@ -313,8 +316,6 @@ public class BrowserProvider extends ContentProvider {
|
||||
SEARCH_SUGGEST_PROJECTION_MAP = Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
private HashMap<String, DatabaseHelper> mDatabasePerProfile;
|
||||
|
||||
private interface BookmarkMigrator {
|
||||
public void updateForNewTable(ContentValues bookmark);
|
||||
}
|
||||
@ -364,8 +365,8 @@ public class BrowserProvider extends ContentProvider {
|
||||
}
|
||||
}
|
||||
|
||||
final class DatabaseHelper extends SQLiteOpenHelper {
|
||||
public DatabaseHelper(Context context, String databasePath) {
|
||||
final class BrowserDatabaseHelper extends SQLiteOpenHelper {
|
||||
public BrowserDatabaseHelper(Context context, String databasePath) {
|
||||
super(context, databasePath, null, DATABASE_VERSION);
|
||||
}
|
||||
|
||||
@ -1953,63 +1954,9 @@ public class BrowserProvider extends ContentProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private DatabaseHelper getDatabaseHelperForProfile(String profile, boolean isTest) {
|
||||
// Each profile has a separate browser.db database. The target
|
||||
// profile is provided using a URI query argument in each request
|
||||
// to our content provider.
|
||||
|
||||
// Always fallback to default profile if none has been provided.
|
||||
if (TextUtils.isEmpty(profile)) {
|
||||
profile = GeckoProfile.get(mContext).getName();
|
||||
}
|
||||
|
||||
DatabaseHelper dbHelper;
|
||||
synchronized (this) {
|
||||
dbHelper = mDatabasePerProfile.get(profile);
|
||||
if (dbHelper != null) {
|
||||
return dbHelper;
|
||||
}
|
||||
|
||||
String databasePath = getDatabasePath(profile, isTest);
|
||||
|
||||
// Before bug 768532, the database was located outside if the
|
||||
// profile on Android 2.2. Make sure it is moved inside the profile
|
||||
// directory.
|
||||
if (Build.VERSION.SDK_INT == 8) {
|
||||
File oldPath = mContext.getDatabasePath("browser-" + profile + ".db");
|
||||
if (oldPath.exists()) {
|
||||
oldPath.renameTo(new File(databasePath));
|
||||
}
|
||||
}
|
||||
|
||||
dbHelper = new DatabaseHelper(getContext(), databasePath);
|
||||
mDatabasePerProfile.put(profile, dbHelper);
|
||||
|
||||
DBUtils.ensureDatabaseIsNotLocked(dbHelper, databasePath);
|
||||
}
|
||||
|
||||
debug("Created database helper for profile: " + profile);
|
||||
return dbHelper;
|
||||
}
|
||||
|
||||
@RobocopTarget
|
||||
public String getDatabasePath(String profile, boolean isTest) {
|
||||
trace("Getting database path for profile: " + profile);
|
||||
|
||||
if (isTest) {
|
||||
return DATABASE_NAME;
|
||||
}
|
||||
|
||||
File profileDir = GeckoProfile.get(mContext, profile).getDir();
|
||||
if (profileDir == null) {
|
||||
debug("Couldn't find directory for profile: " + profile);
|
||||
return null;
|
||||
}
|
||||
|
||||
String databasePath = new File(profileDir, DATABASE_NAME).getAbsolutePath();
|
||||
debug("Successfully created database path for profile: " + databasePath);
|
||||
|
||||
return databasePath;
|
||||
return mDatabases.getDatabasePathForProfile(profile, isTest);
|
||||
}
|
||||
|
||||
private SQLiteDatabase getReadableDatabase(Uri uri) {
|
||||
@ -2020,7 +1967,7 @@ public class BrowserProvider extends ContentProvider {
|
||||
if (uri != null)
|
||||
profile = uri.getQueryParameter(BrowserContract.PARAM_PROFILE);
|
||||
|
||||
return getDatabaseHelperForProfile(profile, isTest(uri)).getReadableDatabase();
|
||||
return mDatabases.getDatabaseHelperForProfile(profile, isTest(uri)).getReadableDatabase();
|
||||
}
|
||||
|
||||
private SQLiteDatabase getWritableDatabase(Uri uri) {
|
||||
@ -2031,7 +1978,7 @@ public class BrowserProvider extends ContentProvider {
|
||||
if (uri != null)
|
||||
profile = uri.getQueryParameter(BrowserContract.PARAM_PROFILE);
|
||||
|
||||
return getDatabaseHelperForProfile(profile, isTest(uri)).getWritableDatabase();
|
||||
return mDatabases.getDatabaseHelperForProfile(profile, isTest(uri)).getWritableDatabase();
|
||||
}
|
||||
|
||||
private void cleanupSomeDeletedRecords(Uri fromUri, Uri targetUri, String tableName) {
|
||||
@ -2178,7 +2125,13 @@ public class BrowserProvider extends ContentProvider {
|
||||
|
||||
synchronized (this) {
|
||||
mContext = getContext();
|
||||
mDatabasePerProfile = new HashMap<String, DatabaseHelper>();
|
||||
mDatabases = new PerProfileDatabases<BrowserDatabaseHelper>(
|
||||
getContext(), DATABASE_NAME, new DatabaseHelperFactory<BrowserDatabaseHelper>() {
|
||||
@Override
|
||||
public BrowserDatabaseHelper makeDatabaseHelper(Context context, String databasePath) {
|
||||
return new BrowserDatabaseHelper(context, databasePath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
|
81
mobile/android/base/db/PerProfileDatabases.java
Normal file
81
mobile/android/base/db/PerProfileDatabases.java
Normal file
@ -0,0 +1,81 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
|
||||
package org.mozilla.gecko.db;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.mozilla.gecko.GeckoProfile;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.text.TextUtils;
|
||||
|
||||
/**
|
||||
* Manages a set of per-profile database storage helpers.
|
||||
*/
|
||||
public class PerProfileDatabases<T extends SQLiteOpenHelper> {
|
||||
|
||||
private final HashMap<String, T> mStorages = new HashMap<String, T>();
|
||||
|
||||
private final Context mContext;
|
||||
private final String mDatabaseName;
|
||||
private final DatabaseHelperFactory<T> mHelperFactory;
|
||||
|
||||
public interface DatabaseHelperFactory<T> {
|
||||
public T makeDatabaseHelper(Context context, String databasePath);
|
||||
}
|
||||
|
||||
public PerProfileDatabases(final Context context, final String databaseName, final DatabaseHelperFactory<T> helperFactory) {
|
||||
mContext = context;
|
||||
mDatabaseName = databaseName;
|
||||
mHelperFactory = helperFactory;
|
||||
}
|
||||
|
||||
public String getDatabasePathForProfile(String profile) {
|
||||
return getDatabasePathForProfile(profile, false);
|
||||
}
|
||||
|
||||
public String getDatabasePathForProfile(String profile, boolean isTest) {
|
||||
if (isTest) {
|
||||
return mDatabaseName;
|
||||
}
|
||||
|
||||
final File profileDir = GeckoProfile.get(mContext, profile).getDir();
|
||||
if (profileDir == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new File(profileDir, mDatabaseName).getAbsolutePath();
|
||||
}
|
||||
|
||||
public T getDatabaseHelperForProfile(String profile) {
|
||||
return getDatabaseHelperForProfile(profile, false);
|
||||
}
|
||||
|
||||
public T getDatabaseHelperForProfile(String profile, boolean isTest) {
|
||||
// Always fall back to default profile if none has been provided.
|
||||
if (TextUtils.isEmpty(profile)) {
|
||||
profile = GeckoProfile.get(mContext).getName();
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
if (mStorages.containsKey(profile)) {
|
||||
return mStorages.get(profile);
|
||||
}
|
||||
|
||||
final String databasePath = getDatabasePathForProfile(profile, isTest);
|
||||
if (databasePath == null) {
|
||||
throw new IllegalStateException("Database path is null for profile: " + profile);
|
||||
}
|
||||
|
||||
final T helper = mHelperFactory.makeDatabaseHelper(mContext, databasePath);
|
||||
DBUtils.ensureDatabaseIsNotLocked(helper, databasePath);
|
||||
|
||||
mStorages.put(profile, helper);
|
||||
return helper;
|
||||
}
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ import java.util.Map;
|
||||
import org.mozilla.gecko.GeckoProfile;
|
||||
import org.mozilla.gecko.db.BrowserContract.Clients;
|
||||
import org.mozilla.gecko.db.BrowserContract.Tabs;
|
||||
import org.mozilla.gecko.db.PerProfileDatabases.DatabaseHelperFactory;
|
||||
import org.mozilla.gecko.mozglue.RobocopTarget;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
@ -33,6 +34,8 @@ public class TabsProvider extends ContentProvider {
|
||||
private static final String LOGTAG = "GeckoTabsProvider";
|
||||
private Context mContext;
|
||||
|
||||
private PerProfileDatabases<TabsDatabaseHelper> mDatabases;
|
||||
|
||||
static final String DATABASE_NAME = "tabs.db";
|
||||
|
||||
static final int DATABASE_VERSION = 2;
|
||||
@ -85,8 +88,6 @@ public class TabsProvider extends ContentProvider {
|
||||
CLIENTS_PROJECTION_MAP = Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
private HashMap<String, DatabaseHelper> mDatabasePerProfile;
|
||||
|
||||
static final String selectColumn(String table, String column) {
|
||||
return table + "." + column + " = ?";
|
||||
}
|
||||
@ -107,8 +108,8 @@ public class TabsProvider extends ContentProvider {
|
||||
}
|
||||
}
|
||||
|
||||
final class DatabaseHelper extends SQLiteOpenHelper {
|
||||
public DatabaseHelper(Context context, String databasePath) {
|
||||
final class TabsDatabaseHelper extends SQLiteOpenHelper {
|
||||
public TabsDatabaseHelper(Context context, String databasePath) {
|
||||
super(context, databasePath, null, DATABASE_VERSION);
|
||||
}
|
||||
|
||||
@ -207,59 +208,9 @@ public class TabsProvider extends ContentProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private DatabaseHelper getDatabaseHelperForProfile(String profile) {
|
||||
// Each profile has a separate tabs.db database. The target
|
||||
// profile is provided using a URI query argument in each request
|
||||
// to our content provider.
|
||||
|
||||
// Always fallback to default profile if none has been provided.
|
||||
if (TextUtils.isEmpty(profile)) {
|
||||
profile = GeckoProfile.get(getContext()).getName();
|
||||
}
|
||||
|
||||
DatabaseHelper dbHelper;
|
||||
synchronized (this) {
|
||||
dbHelper = mDatabasePerProfile.get(profile);
|
||||
if (dbHelper != null) {
|
||||
return dbHelper;
|
||||
}
|
||||
|
||||
String databasePath = getDatabasePath(profile);
|
||||
|
||||
// Before bug 768532, the database was located outside if the
|
||||
// profile on Android 2.2. Make sure it is moved inside the profile
|
||||
// directory.
|
||||
if (Build.VERSION.SDK_INT == 8) {
|
||||
File oldPath = mContext.getDatabasePath("tabs-" + profile + ".db");
|
||||
if (oldPath.exists()) {
|
||||
oldPath.renameTo(new File(databasePath));
|
||||
}
|
||||
}
|
||||
|
||||
dbHelper = new DatabaseHelper(getContext(), databasePath);
|
||||
mDatabasePerProfile.put(profile, dbHelper);
|
||||
|
||||
DBUtils.ensureDatabaseIsNotLocked(dbHelper, databasePath);
|
||||
}
|
||||
|
||||
debug("Created database helper for profile: " + profile);
|
||||
return dbHelper;
|
||||
}
|
||||
|
||||
@RobocopTarget
|
||||
private String getDatabasePath(String profile) {
|
||||
trace("Getting database path for profile: " + profile);
|
||||
|
||||
File profileDir = GeckoProfile.get(mContext, profile).getDir();
|
||||
if (profileDir == null) {
|
||||
debug("Couldn't find directory for profile: " + profile);
|
||||
return null;
|
||||
}
|
||||
|
||||
String databasePath = new File(profileDir, DATABASE_NAME).getAbsolutePath();
|
||||
debug("Successfully created database path for profile: " + databasePath);
|
||||
|
||||
return databasePath;
|
||||
return mDatabases.getDatabasePathForProfile(profile);
|
||||
}
|
||||
|
||||
private SQLiteDatabase getReadableDatabase(Uri uri) {
|
||||
@ -270,7 +221,7 @@ public class TabsProvider extends ContentProvider {
|
||||
if (uri != null)
|
||||
profile = uri.getQueryParameter(BrowserContract.PARAM_PROFILE);
|
||||
|
||||
return getDatabaseHelperForProfile(profile).getReadableDatabase();
|
||||
return mDatabases.getDatabaseHelperForProfile(profile).getReadableDatabase();
|
||||
}
|
||||
|
||||
private SQLiteDatabase getWritableDatabase(Uri uri) {
|
||||
@ -281,7 +232,7 @@ public class TabsProvider extends ContentProvider {
|
||||
if (uri != null)
|
||||
profile = uri.getQueryParameter(BrowserContract.PARAM_PROFILE);
|
||||
|
||||
return getDatabaseHelperForProfile(profile).getWritableDatabase();
|
||||
return mDatabases.getDatabaseHelperForProfile(profile).getWritableDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -290,7 +241,13 @@ public class TabsProvider extends ContentProvider {
|
||||
|
||||
synchronized (this) {
|
||||
mContext = getContext();
|
||||
mDatabasePerProfile = new HashMap<String, DatabaseHelper>();
|
||||
mDatabases = new PerProfileDatabases<TabsDatabaseHelper>(
|
||||
getContext(), DATABASE_NAME, new DatabaseHelperFactory<TabsDatabaseHelper>() {
|
||||
@Override
|
||||
public TabsDatabaseHelper makeDatabaseHelper(Context context, String databasePath) {
|
||||
return new TabsDatabaseHelper(context, databasePath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -117,6 +117,7 @@ gbjar.sources += [
|
||||
'db/LocalBrowserDB.java',
|
||||
'db/PasswordsProvider.java',
|
||||
'db/PerProfileContentProvider.java',
|
||||
'db/PerProfileDatabases.java',
|
||||
'db/TabsProvider.java',
|
||||
'Distribution.java',
|
||||
'DoorHanger.java',
|
||||
|
@ -261,19 +261,22 @@ BreakpointStore.prototype = {
|
||||
* @param nsIJSInspector inspector
|
||||
* The underlying JS inspector we use to enter and exit nested event
|
||||
* loops.
|
||||
* @param ThreadActor thread
|
||||
* The thread actor instance that owns this EventLoopStack.
|
||||
* @param DebuggerServerConnection connection
|
||||
* The remote protocol connection associated with this event loop stack.
|
||||
* @param Object hooks
|
||||
* An object with the following properties:
|
||||
* - url: The URL string of the debuggee we are spinning an event loop
|
||||
* for.
|
||||
* - preNest: function called before entering a nested event loop
|
||||
* - postNest: function called after exiting a nested event loop
|
||||
* @param ThreadActor thread
|
||||
* The thread actor instance that owns this EventLoopStack.
|
||||
*/
|
||||
function EventLoopStack({ inspector, thread, hooks }) {
|
||||
function EventLoopStack({ inspector, thread, connection, hooks }) {
|
||||
this._inspector = inspector;
|
||||
this._hooks = hooks;
|
||||
this._thread = thread;
|
||||
this._connection = connection;
|
||||
}
|
||||
|
||||
EventLoopStack.prototype = {
|
||||
@ -301,6 +304,14 @@ EventLoopStack.prototype = {
|
||||
return url;
|
||||
},
|
||||
|
||||
/**
|
||||
* The DebuggerServerConnection of the debugger who pushed the event loop on
|
||||
* top of the stack
|
||||
*/
|
||||
get lastConnection() {
|
||||
return this._inspector.lastNestRequestor._connection;
|
||||
},
|
||||
|
||||
/**
|
||||
* Push a new nested event loop onto the stack.
|
||||
*
|
||||
@ -310,6 +321,7 @@ EventLoopStack.prototype = {
|
||||
return new EventLoop({
|
||||
inspector: this._inspector,
|
||||
thread: this._thread,
|
||||
connection: this._connection,
|
||||
hooks: this._hooks
|
||||
});
|
||||
}
|
||||
@ -323,14 +335,17 @@ EventLoopStack.prototype = {
|
||||
* The JS Inspector that runs nested event loops.
|
||||
* @param ThreadActor thread
|
||||
* The thread actor that is creating this nested event loop.
|
||||
* @param DebuggerServerConnection connection
|
||||
* The remote protocol connection associated with this event loop.
|
||||
* @param Object hooks
|
||||
* The same hooks object passed into EventLoopStack during its
|
||||
* initialization.
|
||||
*/
|
||||
function EventLoop({ inspector, thread, hooks }) {
|
||||
function EventLoop({ inspector, thread, connection, hooks }) {
|
||||
this._inspector = inspector;
|
||||
this._thread = thread;
|
||||
this._hooks = hooks;
|
||||
this._connection = connection;
|
||||
|
||||
this.enter = this.enter.bind(this);
|
||||
this.resolve = this.resolve.bind(this);
|
||||
@ -414,11 +429,6 @@ function ThreadActor(aHooks, aGlobal)
|
||||
this._frameActors = [];
|
||||
this._hooks = aHooks;
|
||||
this.global = aGlobal;
|
||||
this._nestedEventLoops = new EventLoopStack({
|
||||
inspector: DebuggerServer.xpcInspector,
|
||||
hooks: aHooks,
|
||||
thread: this
|
||||
});
|
||||
// A map of actorID -> actor for breakpoints created and managed by the server.
|
||||
this._hiddenBreakpoints = new Map();
|
||||
|
||||
@ -675,6 +685,15 @@ ThreadActor.prototype = {
|
||||
|
||||
update(this._options, aRequest.options || {});
|
||||
|
||||
// Initialize an event loop stack. This can't be done in the constructor,
|
||||
// because this.conn is not yet initialized by the actor pool at that time.
|
||||
this._nestedEventLoops = new EventLoopStack({
|
||||
inspector: DebuggerServer.xpcInspector,
|
||||
hooks: this._hooks,
|
||||
connection: this.conn,
|
||||
thread: this
|
||||
});
|
||||
|
||||
if (!this.dbg) {
|
||||
this._initDebugger();
|
||||
}
|
||||
@ -992,7 +1011,8 @@ ThreadActor.prototype = {
|
||||
// different tabs or multiple debugger clients connected to the same tab)
|
||||
// only allow resumption in a LIFO order.
|
||||
if (this._nestedEventLoops.size && this._nestedEventLoops.lastPausedUrl
|
||||
&& this._nestedEventLoops.lastPausedUrl !== this._hooks.url) {
|
||||
&& (this._nestedEventLoops.lastPausedUrl !== this._hooks.url
|
||||
|| this._nestedEventLoops.lastConnection !== this.conn)) {
|
||||
return {
|
||||
error: "wrongOrder",
|
||||
message: "trying to resume in the wrong order.",
|
||||
|
@ -72,7 +72,8 @@ PackageUploadActor.prototype = {
|
||||
* and also be locked.
|
||||
*/
|
||||
done: function (aRequest) {
|
||||
return this._file.close();
|
||||
this._file.close();
|
||||
return {};
|
||||
},
|
||||
|
||||
/**
|
||||
@ -81,7 +82,6 @@ PackageUploadActor.prototype = {
|
||||
*/
|
||||
remove: function (aRequest) {
|
||||
this._cleanupFile();
|
||||
|
||||
return {};
|
||||
},
|
||||
|
||||
|
@ -6,15 +6,26 @@
|
||||
// ThreadActor.prototype.synchronize.
|
||||
|
||||
const { defer } = devtools.require("sdk/core/promise");
|
||||
var gClient;
|
||||
var gThreadActor;
|
||||
|
||||
function run_test() {
|
||||
initTestDebuggerServer();
|
||||
let gDebuggee = addTestGlobal("test-nesting");
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect(function () {
|
||||
attachTestTabAndResume(gClient, "test-nesting", function (aResponse, aTabClient, aThreadClient) {
|
||||
// Reach over the protocol connection and get a reference to the thread actor.
|
||||
gThreadActor = aThreadClient._transport._serverConnection.getActor(aThreadClient._actor);
|
||||
|
||||
test_nesting();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
test_nesting();
|
||||
}
|
||||
|
||||
function test_nesting() {
|
||||
const thread = new DebuggerServer.ThreadActor(DebuggerServer);
|
||||
const thread = gThreadActor;
|
||||
const { resolve, reject, promise } = defer();
|
||||
|
||||
let currentStep = 0;
|
||||
@ -34,5 +45,5 @@ function test_nesting() {
|
||||
// There shouldn't be any nested event loops anymore
|
||||
do_check_eq(thread._nestedEventLoops.size, 0);
|
||||
|
||||
do_test_finished();
|
||||
finishClient(gClient);
|
||||
}
|
||||
|
@ -6,15 +6,26 @@
|
||||
// loops when requested.
|
||||
|
||||
const { defer } = devtools.require("sdk/core/promise");
|
||||
var gClient;
|
||||
var gThreadActor;
|
||||
|
||||
function run_test() {
|
||||
initTestDebuggerServer();
|
||||
let gDebuggee = addTestGlobal("test-nesting");
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect(function () {
|
||||
attachTestTabAndResume(gClient, "test-nesting", function (aResponse, aTabClient, aThreadClient) {
|
||||
// Reach over the protocol connection and get a reference to the thread actor.
|
||||
gThreadActor = aThreadClient._transport._serverConnection.getActor(aThreadClient._actor);
|
||||
|
||||
test_nesting();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
test_nesting();
|
||||
}
|
||||
|
||||
function test_nesting() {
|
||||
const thread = new DebuggerServer.ThreadActor(DebuggerServer);
|
||||
const thread = gThreadActor;
|
||||
const { resolve, reject, promise } = defer();
|
||||
|
||||
// The following things should happen (in order):
|
||||
@ -64,5 +75,5 @@ function test_nesting() {
|
||||
// There shouldn't be any nested event loops anymore
|
||||
do_check_eq(thread._nestedEventLoops.size, 0);
|
||||
|
||||
do_test_finished();
|
||||
finishClient(gClient);
|
||||
}
|
||||
|
50
toolkit/devtools/server/tests/unit/test_nesting-03.js
Normal file
50
toolkit/devtools/server/tests/unit/test_nesting-03.js
Normal file
@ -0,0 +1,50 @@
|
||||
/* -*- Mode: javascript; js-indent-level: 2; -*- */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that we can detect nested event loops in tabs with the same URL.
|
||||
|
||||
const { defer } = devtools.require("sdk/core/promise");
|
||||
var gClient1, gClient2;
|
||||
|
||||
function run_test() {
|
||||
initTestDebuggerServer();
|
||||
addTestGlobal("test-nesting1");
|
||||
addTestGlobal("test-nesting1");
|
||||
// Conect the first client to the first debuggee.
|
||||
gClient1 = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient1.connect(function () {
|
||||
attachTestThread(gClient1, "test-nesting1", function (aResponse, aTabClient, aThreadClient) {
|
||||
start_second_connection();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function start_second_connection() {
|
||||
gClient2 = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient2.connect(function () {
|
||||
attachTestThread(gClient2, "test-nesting1", function (aResponse, aTabClient, aThreadClient) {
|
||||
test_nesting();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function test_nesting() {
|
||||
const { resolve, reject, promise } = defer();
|
||||
|
||||
gClient1.activeThread.resume(aResponse => {
|
||||
do_check_eq(aResponse.error, "wrongOrder");
|
||||
gClient2.activeThread.resume(aResponse => {
|
||||
do_check_true(!aResponse.error);
|
||||
do_check_eq(aResponse.from, gClient2.activeThread.actor);
|
||||
|
||||
gClient1.activeThread.resume(aResponse => {
|
||||
do_check_true(!aResponse.error);
|
||||
do_check_eq(aResponse.from, gClient1.activeThread.actor);
|
||||
|
||||
gClient1.close(() => finishClient(gClient2));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
@ -71,6 +71,10 @@ TestTabActor.prototype = {
|
||||
return { wrappedJSObject: this._global };
|
||||
},
|
||||
|
||||
get url() {
|
||||
return this._global.__name;
|
||||
},
|
||||
|
||||
form: function() {
|
||||
let response = { actor: this.actorID, title: this._global.__name };
|
||||
|
||||
|
@ -18,6 +18,7 @@ support-files =
|
||||
|
||||
[test_nesting-01.js]
|
||||
[test_nesting-02.js]
|
||||
[test_nesting-03.js]
|
||||
[test_forwardingprefix.js]
|
||||
[test_getyoungestframe.js]
|
||||
[test_nsjsinspector.js]
|
||||
|
Loading…
Reference in New Issue
Block a user