mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge fx-team to m-c on a CLOSED TREE
This commit is contained in:
commit
d600490156
@ -9,6 +9,13 @@ support-files =
|
||||
blockPluginHard.xml
|
||||
blockPluginVulnerableNoUpdate.xml
|
||||
blockPluginVulnerableUpdatable.xml
|
||||
browser_bug479408_sample.html
|
||||
browser_bug678392-1.html
|
||||
browser_bug678392-2.html
|
||||
browser_clearplugindata.html
|
||||
browser_clearplugindata_noage.html
|
||||
browser_registerProtocolHandler_notification.html
|
||||
browser_tab_dragdrop2_frame1.xul
|
||||
bug564387.html
|
||||
bug564387_video1.ogv
|
||||
bug564387_video1.ogv^headers^
|
||||
@ -139,7 +146,6 @@ skip-if = toolkit == "cocoa"
|
||||
[browser_bug462673.js]
|
||||
[browser_bug477014.js]
|
||||
[browser_bug479408.js]
|
||||
[browser_bug479408_sample.html]
|
||||
[browser_bug481560.js]
|
||||
[browser_bug484315.js]
|
||||
[browser_bug491431.js]
|
||||
@ -192,8 +198,6 @@ run-if = toolkit == "cocoa"
|
||||
[browser_bug655584.js]
|
||||
[browser_bug664672.js]
|
||||
[browser_bug676619.js]
|
||||
[browser_bug678392-1.html]
|
||||
[browser_bug678392-2.html]
|
||||
[browser_bug678392.js]
|
||||
[browser_bug710878.js]
|
||||
[browser_bug719271.js]
|
||||
@ -225,9 +229,7 @@ run-if = toolkit == "cocoa"
|
||||
[browser_bug902156.js]
|
||||
[browser_bug906190.js]
|
||||
[browser_canonizeURL.js]
|
||||
[browser_clearplugindata.html]
|
||||
[browser_clearplugindata.js]
|
||||
[browser_clearplugindata_noage.html]
|
||||
[browser_contentAreaClick.js]
|
||||
[browser_contextSearchTabPosition.js]
|
||||
[browser_ctrlTab.js]
|
||||
@ -294,10 +296,8 @@ skip-if = true # disabled until the tree view is added
|
||||
[browser_tab_drag_drop_perwindow.js]
|
||||
[browser_tab_dragdrop.js]
|
||||
[browser_tab_dragdrop2.js]
|
||||
[browser_tab_dragdrop2_frame1.xul]
|
||||
[browser_tabbar_big_widgets.js]
|
||||
skip-if = os == "linux" # No tabs in titlebar on linux
|
||||
|
||||
[browser_tabfocus.js]
|
||||
[browser_tabopen_reflows.js]
|
||||
[browser_tabs_isActive.js]
|
||||
@ -323,5 +323,4 @@ skip-if = os == "linux" # No tabs in titlebar on linux
|
||||
[browser_wyciwyg_urlbarCopying.js]
|
||||
[browser_zbug569342.js]
|
||||
[browser_registerProtocolHandler_notification.js]
|
||||
[browser_registerProtocolHandler_notification.html]
|
||||
[browser_no_mcb_on_http_site.js]
|
||||
|
@ -1954,6 +1954,9 @@ this.CustomizableUI = {
|
||||
get TYPE_MENU_PANEL() "menu-panel",
|
||||
get TYPE_TOOLBAR() "toolbar",
|
||||
|
||||
get WIDE_PANEL_CLASS() "panel-wide-item",
|
||||
get PANEL_COLUMN_COUNT() 3,
|
||||
|
||||
addListener: function(aListener) {
|
||||
CustomizableUIInternal.addListener(aListener);
|
||||
},
|
||||
|
@ -14,10 +14,6 @@ const kPaletteId = "customization-palette";
|
||||
const kAboutURI = "about:customizing";
|
||||
const kDragDataTypePrefix = "text/toolbarwrapper-id/";
|
||||
const kPlaceholderClass = "panel-customization-placeholder";
|
||||
const kWidePanelItemClass = "panel-wide-item";
|
||||
// TODO(bug 885574): Merge this constant with the one in CustomizableWidgets.jsm,
|
||||
// maybe just use a pref for this.
|
||||
const kColumnsInMenuPanel = 3;
|
||||
const kSkipSourceNodePref = "browser.uiCustomization.skipSourceNodeCheck";
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
@ -25,6 +21,8 @@ Cu.import("resource:///modules/CustomizableUI.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DragPositionManager",
|
||||
"resource:///modules/DragPositionManager.jsm");
|
||||
|
||||
let gModuleName = "[CustomizeMode]";
|
||||
#include logging.js
|
||||
@ -218,6 +216,7 @@ CustomizeMode.prototype = {
|
||||
|
||||
window.gNavToolbox.removeEventListener("toolbarvisibilitychange", this);
|
||||
|
||||
DragPositionManager.stop();
|
||||
window.PanelUI.mainView.removeEventListener("contextmenu", this, true);
|
||||
this.visiblePalette.removeEventListener("dragstart", this, true);
|
||||
this.visiblePalette.removeEventListener("dragover", this, true);
|
||||
@ -797,22 +796,28 @@ CustomizeMode.prototype = {
|
||||
|
||||
// Hack needed so that the dragimage will still show the
|
||||
// item as it appeared before it was hidden.
|
||||
let win = aEvent.target.ownerDocument.defaultView;
|
||||
win.setTimeout(function() {
|
||||
this._initializeDragAfterMove = function() {
|
||||
// For automated tests, we sometimes start exiting customization mode
|
||||
// before this fires, which leaves us with placeholders inserted after
|
||||
// we've exited. So we need to check that we are indeed customizing.
|
||||
if (this._customizing && !this._transitioning) {
|
||||
item.hidden = true;
|
||||
this._showPanelCustomizationPlaceholders();
|
||||
DragPositionManager.start(this.window);
|
||||
}
|
||||
}.bind(this), 0);
|
||||
this._initializeDragAfterMove = null;
|
||||
this.window.clearTimeout(this._dragInitializeTimeout);
|
||||
}.bind(this);
|
||||
this._dragInitializeTimeout = this.window.setTimeout(this._initializeDragAfterMove, 0);
|
||||
},
|
||||
|
||||
_onDragOver: function(aEvent) {
|
||||
if (this._isUnwantedDragDrop(aEvent)) {
|
||||
return;
|
||||
}
|
||||
if (this._initializeDragAfterMove) {
|
||||
this._initializeDragAfterMove();
|
||||
}
|
||||
|
||||
__dumpDragData(aEvent);
|
||||
|
||||
@ -845,7 +850,8 @@ CustomizeMode.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
let targetNode = this._getDragOverNode(aEvent, targetArea);
|
||||
let targetIsToolbar = CustomizableUI.getAreaType(targetArea.id) == "toolbar";
|
||||
let targetNode = this._getDragOverNode(aEvent, targetArea, targetIsToolbar, draggedItemId);
|
||||
|
||||
// We need to determine the place that the widget is being dropped in
|
||||
// the target.
|
||||
@ -863,20 +869,15 @@ CustomizeMode.prototype = {
|
||||
dragValue = "after";
|
||||
} else {
|
||||
dragOverItem = targetParent.children[position];
|
||||
// Check if the aDraggedItem is hovered past the first half of dragOverItem
|
||||
let window = dragOverItem.ownerDocument.defaultView;
|
||||
let direction = window.getComputedStyle(dragOverItem, null).direction;
|
||||
let itemRect = dragOverItem.getBoundingClientRect();
|
||||
let dropTargetCenter = itemRect.left + (itemRect.width / 2);
|
||||
if (targetParent == window.PanelUI.contents) {
|
||||
dragValue = "true";
|
||||
if (direction == "ltr" && aEvent.clientX > dropTargetCenter) {
|
||||
position++;
|
||||
} else if (direction == "rtl" && aEvent.clientX < dropTargetCenter) {
|
||||
position--;
|
||||
}
|
||||
if (!targetIsToolbar) {
|
||||
dragValue = "before";
|
||||
dragOverItem = position == -1 ? targetParent.firstChild : targetParent.children[position];
|
||||
} else {
|
||||
// Check if the aDraggedItem is hovered past the first half of dragOverItem
|
||||
let window = dragOverItem.ownerDocument.defaultView;
|
||||
let direction = window.getComputedStyle(dragOverItem, null).direction;
|
||||
let itemRect = dragOverItem.getBoundingClientRect();
|
||||
let dropTargetCenter = itemRect.left + (itemRect.width / 2);
|
||||
let existingDir = dragOverItem.getAttribute("dragover");
|
||||
if ((existingDir == "before") == (direction == "ltr")) {
|
||||
dropTargetCenter += (parseInt(dragOverItem.style.borderLeftWidth) || 0) / 2;
|
||||
@ -890,12 +891,12 @@ CustomizeMode.prototype = {
|
||||
}
|
||||
|
||||
if (this._dragOverItem && dragOverItem != this._dragOverItem) {
|
||||
this._setDragActive(this._dragOverItem, false);
|
||||
this._cancelDragActive(this._dragOverItem, dragOverItem);
|
||||
}
|
||||
|
||||
if (dragOverItem != this._dragOverItem || dragValue != dragOverItem.getAttribute("dragover")) {
|
||||
if (dragOverItem != targetArea.customizationTarget) {
|
||||
this._setDragActive(dragOverItem, dragValue, draggedItemId);
|
||||
this._setDragActive(dragOverItem, dragValue, draggedItemId, targetIsToolbar);
|
||||
}
|
||||
this._dragOverItem = dragOverItem;
|
||||
}
|
||||
@ -910,6 +911,8 @@ CustomizeMode.prototype = {
|
||||
}
|
||||
|
||||
__dumpDragData(aEvent);
|
||||
this._initializeDragAfterMove = null;
|
||||
this.window.clearTimeout(this._dragInitializeTimeout);
|
||||
|
||||
let targetArea = this._getCustomizableParent(aEvent.currentTarget);
|
||||
let document = aEvent.target.ownerDocument;
|
||||
@ -918,8 +921,8 @@ CustomizeMode.prototype = {
|
||||
aEvent.dataTransfer.mozGetDataAt(kDragDataTypePrefix + documentId, 0);
|
||||
let draggedWrapper = document.getElementById("wrapper-" + draggedItemId);
|
||||
let originArea = this._getCustomizableParent(draggedWrapper);
|
||||
if (this._dragWidthMap) {
|
||||
this._dragWidthMap.clear();
|
||||
if (this._dragSizeMap) {
|
||||
this._dragSizeMap.clear();
|
||||
}
|
||||
// Do nothing if the target area or origin area are not customizable.
|
||||
if (!targetArea || !originArea) {
|
||||
@ -943,7 +946,7 @@ CustomizeMode.prototype = {
|
||||
targetNode = targetNode.firstChild;
|
||||
}
|
||||
|
||||
this._setDragActive(this._dragOverItem, false);
|
||||
this._cancelDragActive(this._dragOverItem, null, true);
|
||||
this._removePanelCustomizationPlaceholders();
|
||||
|
||||
try {
|
||||
@ -1067,8 +1070,14 @@ CustomizeMode.prototype = {
|
||||
|
||||
__dumpDragData(aEvent);
|
||||
|
||||
if (this._dragOverItem) {
|
||||
this._setDragActive(this._dragOverItem, false);
|
||||
// When leaving customization areas, cancel the drag on the last dragover item
|
||||
// We've attached the listener to areas, so aEvent.currentTarget will be the area.
|
||||
// We don't care about dragexit events fired on descendants of the area,
|
||||
// so we check that the event's target is the same as the area to which the listener
|
||||
// was attached.
|
||||
if (this._dragOverItem && aEvent.target == aEvent.currentTarget) {
|
||||
this._cancelDragActive(this._dragOverItem);
|
||||
this._dragOverItem = null;
|
||||
}
|
||||
},
|
||||
|
||||
@ -1076,6 +1085,8 @@ CustomizeMode.prototype = {
|
||||
if (this._isUnwantedDragDrop(aEvent)) {
|
||||
return;
|
||||
}
|
||||
this._initializeDragAfterMove = null;
|
||||
this.window.clearTimeout(this._dragInitializeTimeout);
|
||||
|
||||
__dumpDragData(aEvent);
|
||||
let document = aEvent.target.ownerDocument;
|
||||
@ -1114,176 +1125,131 @@ CustomizeMode.prototype = {
|
||||
mozSourceNode.ownerDocument.defaultView != this.window;
|
||||
},
|
||||
|
||||
_setDragActive: function(aItem, aValue, aDraggedItemId) {
|
||||
_setDragActive: function(aItem, aValue, aDraggedItemId, aInToolbar) {
|
||||
if (!aItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aValue) {
|
||||
if (aItem.hasAttribute("dragover") != aValue) {
|
||||
aItem.setAttribute("dragover", aValue);
|
||||
if (aItem.hasAttribute("dragover") != aValue) {
|
||||
aItem.setAttribute("dragover", aValue);
|
||||
|
||||
let window = aItem.ownerDocument.defaultView;
|
||||
let draggedItem = window.document.getElementById(aDraggedItemId);
|
||||
if (!aInToolbar) {
|
||||
this._setPanelDragActive(aItem, draggedItem, aValue);
|
||||
} else {
|
||||
// Calculate width of the item when it'd be dropped in this position
|
||||
let window = aItem.ownerDocument.defaultView;
|
||||
let draggedItem = window.document.getElementById(aDraggedItemId);
|
||||
let width = this._getDragItemWidth(aItem, draggedItem);
|
||||
if (width) {
|
||||
let panelContents = window.PanelUI.contents;
|
||||
if (aItem.parentNode == panelContents) {
|
||||
this._setPanelDragActive(aItem, draggedItem, width);
|
||||
} else {
|
||||
let direction = window.getComputedStyle(aItem).direction;
|
||||
let prop, otherProp;
|
||||
// If we're inserting before in ltr, or after in rtl:
|
||||
if ((aValue == "before") == (direction == "ltr")) {
|
||||
prop = "borderLeftWidth";
|
||||
otherProp = "border-right-width";
|
||||
} else {
|
||||
// otherwise:
|
||||
prop = "borderRightWidth";
|
||||
otherProp = "border-left-width";
|
||||
}
|
||||
aItem.style[prop] = width;
|
||||
aItem.style.removeProperty(otherProp);
|
||||
}
|
||||
let width = this._getDragItemSize(aItem, draggedItem).width;
|
||||
let direction = window.getComputedStyle(aItem).direction;
|
||||
let prop, otherProp;
|
||||
// If we're inserting before in ltr, or after in rtl:
|
||||
if ((aValue == "before") == (direction == "ltr")) {
|
||||
prop = "borderLeftWidth";
|
||||
otherProp = "border-right-width";
|
||||
} else {
|
||||
// otherwise:
|
||||
prop = "borderRightWidth";
|
||||
otherProp = "border-left-width";
|
||||
}
|
||||
aItem.style[prop] = width + 'px';
|
||||
aItem.style.removeProperty(otherProp);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
},
|
||||
_cancelDragActive: function(aItem, aNextItem, aNoTransition) {
|
||||
let currentArea = this._getCustomizableParent(aItem);
|
||||
if (!currentArea) {
|
||||
return;
|
||||
}
|
||||
let isToolbar = CustomizableUI.getAreaType(currentArea.id) == "toolbar";
|
||||
if (isToolbar) {
|
||||
aItem.removeAttribute("dragover");
|
||||
// Remove both property values in the case that the end padding
|
||||
// had been set.
|
||||
aItem.style.removeProperty("border-left-width");
|
||||
aItem.style.removeProperty("border-right-width");
|
||||
}
|
||||
},
|
||||
|
||||
_setPanelDragActive: function(aDragOverNode, aDraggedItem, aWidth) {
|
||||
let document = aDragOverNode.ownerDocument;
|
||||
let window = document.defaultView;
|
||||
let panelContents = window.PanelUI.contents;
|
||||
while (!aDragOverNode.id && aDragOverNode.parentNode != panelContents)
|
||||
aDragOverNode = aDragOverNode.parentNode;
|
||||
if (!aDragOverNode.id)
|
||||
return;
|
||||
|
||||
if (!aDragOverNode.previousSibling ||
|
||||
!aDragOverNode.previousSibling.classList.contains(kPlaceholderClass)) {
|
||||
let isPlaceholderAtEnd = function(aPlaceholder) {
|
||||
do {
|
||||
aPlaceholder = aPlaceholder.nextSibling;
|
||||
if (!aPlaceholder)
|
||||
return true;
|
||||
if (!aPlaceholder.classList.contains(kPlaceholderClass))
|
||||
return false;
|
||||
} while (aPlaceholder.nextSibling)
|
||||
return true;
|
||||
}
|
||||
|
||||
let resetAnimAttributes = function(aPlaceholder) {
|
||||
if (!aPlaceholder)
|
||||
} else {
|
||||
if (aNextItem) {
|
||||
let nextArea = this._getCustomizableParent(aNextItem);
|
||||
if (nextArea == currentArea) {
|
||||
// No need to do anything if we're still dragging in this area:
|
||||
return;
|
||||
aPlaceholder.removeAttribute("expand");
|
||||
aPlaceholder.removeAttribute("contract");
|
||||
aPlaceholder.removeAttribute("hidden");
|
||||
aPlaceholder.style.removeProperty("width");
|
||||
}
|
||||
|
||||
let placeholders = Array.slice(panelContents.getElementsByClassName(kPlaceholderClass));
|
||||
|
||||
let toExpand = placeholders.shift();
|
||||
let toContract = placeholders.shift();
|
||||
if (toContract && isPlaceholderAtEnd(toContract))
|
||||
toContract = null;
|
||||
// Seek to find hidden placeholders first to use for the expand transition.
|
||||
while (toExpand.getAttribute("hidden") != "true" && placeholders.length)
|
||||
toExpand = placeholders.shift();
|
||||
|
||||
if (toExpand.transitioning || (toContract && toContract.transitioning))
|
||||
return;
|
||||
|
||||
let wasHidden = (toContract && toContract.getAttribute("hidden") == "true") ||
|
||||
toExpand.getAttribute("hidden") == "true";
|
||||
resetAnimAttributes(toContract);
|
||||
resetAnimAttributes(toExpand);
|
||||
|
||||
aDragOverNode.parentNode.insertBefore(toExpand, aDragOverNode);
|
||||
toExpand.style.width = "0px";
|
||||
toExpand.setAttribute("expand", "true");
|
||||
toExpand.transitioning = true;
|
||||
if (toContract) {
|
||||
toContract.style.width = aWidth;
|
||||
toContract.setAttribute("contract", "true");
|
||||
toContract.transitioning = true;
|
||||
}
|
||||
|
||||
window.mozRequestAnimationFrame(() => {
|
||||
if (toContract)
|
||||
toContract.style.width = "0px";
|
||||
toExpand.style.width = aWidth;
|
||||
});
|
||||
toExpand.addEventListener("transitionend", function expandTransitionEnd() {
|
||||
toExpand.removeEventListener("transitionend", expandTransitionEnd, false);
|
||||
toExpand.transitioning = false;
|
||||
});
|
||||
if (toContract) {
|
||||
toContract.addEventListener("transitionend", function contractTransitionEnd() {
|
||||
toContract.removeEventListener("transitionend", contractTransitionEnd, false);
|
||||
panelContents.appendChild(toContract);
|
||||
if (wasHidden)
|
||||
toContract.setAttribute("hidden", "true");
|
||||
toContract.transitioning = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
// Otherwise, clear everything out:
|
||||
let positionManager = DragPositionManager.getManagerForArea(currentArea);
|
||||
positionManager.clearPlaceholders(currentArea, aNoTransition);
|
||||
}
|
||||
},
|
||||
|
||||
_getDragItemWidth: function(aDragOverNode, aDraggedItem) {
|
||||
// Cache it good, cache it real good.
|
||||
if (!this._dragWidthMap)
|
||||
this._dragWidthMap = new WeakMap();
|
||||
if (!this._dragWidthMap.has(aDraggedItem))
|
||||
this._dragWidthMap.set(aDraggedItem, new WeakMap());
|
||||
let itemMap = this._dragWidthMap.get(aDraggedItem);
|
||||
_setPanelDragActive: function(aDragOverNode, aDraggedItem, aValue) {
|
||||
let targetArea = this._getCustomizableParent(aDragOverNode);
|
||||
if (!targetArea)
|
||||
return;
|
||||
// Return the width for this target from cache, if it exists.
|
||||
let width = itemMap.get(targetArea);
|
||||
if (width)
|
||||
return width;
|
||||
let positionManager = DragPositionManager.getManagerForArea(targetArea);
|
||||
let draggedSize = this._getDragItemSize(aDragOverNode, aDraggedItem);
|
||||
let isWide = aDraggedItem.classList.contains(CustomizableUI.WIDE_PANEL_CLASS);
|
||||
positionManager.insertPlaceholder(targetArea, aDragOverNode, isWide, draggedSize);
|
||||
},
|
||||
|
||||
// Calculate width of the item when it'd be dropped in this position.
|
||||
_getDragItemSize: function(aDragOverNode, aDraggedItem) {
|
||||
// Cache it good, cache it real good.
|
||||
if (!this._dragSizeMap)
|
||||
this._dragSizeMap = new WeakMap();
|
||||
if (!this._dragSizeMap.has(aDraggedItem))
|
||||
this._dragSizeMap.set(aDraggedItem, new WeakMap());
|
||||
let itemMap = this._dragSizeMap.get(aDraggedItem);
|
||||
let targetArea = this._getCustomizableParent(aDragOverNode);
|
||||
let currentArea = this._getCustomizableParent(aDraggedItem);
|
||||
// Return the size for this target from cache, if it exists.
|
||||
let size = itemMap.get(targetArea);
|
||||
if (size)
|
||||
return size;
|
||||
|
||||
// Calculate size of the item when it'd be dropped in this position.
|
||||
let currentParent = aDraggedItem.parentNode;
|
||||
let currentSibling = aDraggedItem.nextSibling;
|
||||
|
||||
// Move the widget temporarily next to the placeholder.
|
||||
aDragOverNode.parentNode.insertBefore(aDraggedItem, aDragOverNode);
|
||||
// Update the node's areaType.
|
||||
let areaType = CustomizableUI.getAreaType(targetArea.id);
|
||||
const kAreaType = "cui-areatype";
|
||||
let currentType = aDraggedItem.hasAttribute(kAreaType) &&
|
||||
aDraggedItem.getAttribute(kAreaType);
|
||||
if (areaType)
|
||||
aDraggedItem.setAttribute(kAreaType, areaType);
|
||||
CustomizableUI.onWidgetDrag(aDraggedItem.id, targetArea.id);
|
||||
// Fetch the new width.
|
||||
width = Math.floor(aDraggedItem.getBoundingClientRect().width) + "px";
|
||||
// Put the item back into its previous position.
|
||||
if (currentSibling)
|
||||
currentParent.insertBefore(aDraggedItem, currentSibling);
|
||||
else
|
||||
currentParent.appendChild(aDraggedItem);
|
||||
// restore the areaType
|
||||
if (areaType) {
|
||||
if (currentType === false)
|
||||
aDraggedItem.removeAttribute(kAreaType);
|
||||
else
|
||||
aDraggedItem.setAttribute(kAreaType, currentType);
|
||||
let areaType, currentType;
|
||||
|
||||
if (targetArea != currentArea) {
|
||||
// Move the widget temporarily next to the placeholder.
|
||||
aDragOverNode.parentNode.insertBefore(aDraggedItem, aDragOverNode);
|
||||
// Update the node's areaType.
|
||||
areaType = CustomizableUI.getAreaType(targetArea.id);
|
||||
currentType = aDraggedItem.hasAttribute(kAreaType) &&
|
||||
aDraggedItem.getAttribute(kAreaType);
|
||||
if (areaType)
|
||||
aDraggedItem.setAttribute(kAreaType, areaType);
|
||||
this.wrapToolbarItem(aDraggedItem, areaType || "palette");
|
||||
CustomizableUI.onWidgetDrag(aDraggedItem.id, targetArea.id);
|
||||
} else {
|
||||
aDraggedItem.parentNode.hidden = false;
|
||||
}
|
||||
CustomizableUI.onWidgetDrag(aDraggedItem.id);
|
||||
// Cache the found value of width for this target.
|
||||
itemMap.set(targetArea, width);
|
||||
return width;
|
||||
|
||||
// Fetch the new size.
|
||||
let rect = aDraggedItem.parentNode.getBoundingClientRect();
|
||||
size = {width: rect.width, height: rect.height};
|
||||
// Cache the found value of size for this target.
|
||||
itemMap.set(targetArea, size);
|
||||
|
||||
if (targetArea != currentArea) {
|
||||
this.unwrapToolbarItem(aDraggedItem.parentNode);
|
||||
// Put the item back into its previous position.
|
||||
if (currentSibling)
|
||||
currentParent.insertBefore(aDraggedItem, currentSibling);
|
||||
else
|
||||
currentParent.appendChild(aDraggedItem);
|
||||
// restore the areaType
|
||||
if (areaType) {
|
||||
if (currentType === false)
|
||||
aDraggedItem.removeAttribute(kAreaType);
|
||||
else
|
||||
aDraggedItem.setAttribute(kAreaType, currentType);
|
||||
}
|
||||
CustomizableUI.onWidgetDrag(aDraggedItem.id);
|
||||
} else {
|
||||
aDraggedItem.parentNode.hidden = true;
|
||||
}
|
||||
return size;
|
||||
},
|
||||
|
||||
_getCustomizableParent: function(aElement) {
|
||||
@ -1298,7 +1264,7 @@ CustomizeMode.prototype = {
|
||||
return null;
|
||||
},
|
||||
|
||||
_getDragOverNode: function(aEvent, aAreaElement) {
|
||||
_getDragOverNode: function(aEvent, aAreaElement, aInToolbar, aDraggedItemId) {
|
||||
let expectedParent = aAreaElement.customizationTarget || aAreaElement;
|
||||
// Our tests are stupid. Cope:
|
||||
if (!aEvent.clientX && !aEvent.clientY) {
|
||||
@ -1314,9 +1280,16 @@ CustomizeMode.prototype = {
|
||||
dragX = Math.min(bounds.right, Math.max(dragX, bounds.left));
|
||||
dragY = Math.min(bounds.bottom, Math.max(dragY, bounds.top));
|
||||
|
||||
let targetNode = aAreaElement.ownerDocument.elementFromPoint(dragX, dragY);
|
||||
while (targetNode && targetNode.parentNode != expectedParent) {
|
||||
targetNode = targetNode.parentNode;
|
||||
let targetNode;
|
||||
if (aInToolbar) {
|
||||
targetNode = aAreaElement.ownerDocument.elementFromPoint(dragX, dragY);
|
||||
while (targetNode && targetNode.parentNode != expectedParent) {
|
||||
targetNode = targetNode.parentNode;
|
||||
}
|
||||
} else {
|
||||
let positionManager = DragPositionManager.getManagerForArea(aAreaElement);
|
||||
// Find the closest node:
|
||||
targetNode = positionManager.find(aAreaElement, dragX, dragY, aDraggedItemId);
|
||||
}
|
||||
return targetNode || aEvent.target;
|
||||
},
|
||||
@ -1326,7 +1299,7 @@ CustomizeMode.prototype = {
|
||||
let doc = aEvent.target.ownerDocument;
|
||||
doc.documentElement.setAttribute("customizing-movingItem", true);
|
||||
let item = this._getWrapper(aEvent.target);
|
||||
if (item) {
|
||||
if (item && !item.classList.contains(kPlaceholderClass)) {
|
||||
item.setAttribute("mousedown", "true");
|
||||
}
|
||||
},
|
||||
@ -1351,35 +1324,37 @@ CustomizeMode.prototype = {
|
||||
},
|
||||
|
||||
_showPanelCustomizationPlaceholders: function() {
|
||||
this._removePanelCustomizationPlaceholders();
|
||||
let doc = this.document;
|
||||
let contents = this.panelUIContents;
|
||||
let visibleWideItems = contents.querySelectorAll("toolbarpaletteitem:not([hidden]) > ." + kWidePanelItemClass);
|
||||
let visibleChildren = contents.querySelectorAll("toolbarpaletteitem:not([hidden])");
|
||||
// TODO(bug 885578): Still doesn't handle a hole when there is a wide
|
||||
// widget located at the bottom of the panel.
|
||||
let narrowItemsAfterWideItem = 0;
|
||||
let node = contents.lastChild;
|
||||
while (node && !node.classList.contains(kWidePanelItemClass) &&
|
||||
(!node.firstChild || !node.firstChild.classList.contains(kWidePanelItemClass))) {
|
||||
if (!node.hidden) {
|
||||
while (node && !node.classList.contains(CustomizableUI.WIDE_PANEL_CLASS) &&
|
||||
(!node.firstChild || !node.firstChild.classList.contains(CustomizableUI.WIDE_PANEL_CLASS))) {
|
||||
if (!node.hidden && !node.classList.contains(kPlaceholderClass)) {
|
||||
narrowItemsAfterWideItem++;
|
||||
}
|
||||
node = node.previousSibling;
|
||||
}
|
||||
|
||||
let orphanedItems = narrowItemsAfterWideItem % kColumnsInMenuPanel;
|
||||
let placeholders = kColumnsInMenuPanel - orphanedItems;
|
||||
let orphanedItems = narrowItemsAfterWideItem % CustomizableUI.PANEL_COLUMN_COUNT;
|
||||
let placeholders = CustomizableUI.PANEL_COLUMN_COUNT - orphanedItems;
|
||||
|
||||
while (placeholders--) {
|
||||
let placeholder = doc.createElement("toolbarpaletteitem");
|
||||
placeholder.classList.add(kPlaceholderClass);
|
||||
//XXXjaws The toolbarbutton child here is only necessary to get
|
||||
// the styling right here.
|
||||
let placeholderChild = doc.createElement("toolbarbutton");
|
||||
placeholderChild.classList.add(kPlaceholderClass + "-child");
|
||||
placeholder.appendChild(placeholderChild);
|
||||
contents.appendChild(placeholder);
|
||||
let currentPlaceholderCount = contents.querySelectorAll("." + kPlaceholderClass).length;
|
||||
if (placeholders > currentPlaceholderCount) {
|
||||
while (placeholders-- > currentPlaceholderCount) {
|
||||
let placeholder = doc.createElement("toolbarpaletteitem");
|
||||
placeholder.classList.add(kPlaceholderClass);
|
||||
//XXXjaws The toolbarbutton child here is only necessary to get
|
||||
// the styling right here.
|
||||
let placeholderChild = doc.createElement("toolbarbutton");
|
||||
placeholderChild.classList.add(kPlaceholderClass + "-child");
|
||||
placeholder.appendChild(placeholderChild);
|
||||
contents.appendChild(placeholder);
|
||||
}
|
||||
} else if (placeholders < currentPlaceholderCount) {
|
||||
while (placeholders++ < currentPlaceholderCount) {
|
||||
contents.querySelectorAll("." + kPlaceholderClass)[0].remove();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -1409,6 +1384,9 @@ function getPlaceForItem(aElement) {
|
||||
}
|
||||
|
||||
function __dumpDragData(aEvent, caller) {
|
||||
if (!gDebug) {
|
||||
return;
|
||||
}
|
||||
let str = "Dumping drag data (CustomizeMode.jsm) {\n";
|
||||
str += " type: " + aEvent["type"] + "\n";
|
||||
for (let el of ["target", "currentTarget", "relatedTarget"]) {
|
||||
|
364
browser/components/customizableui/src/DragPositionManager.jsm
Normal file
364
browser/components/customizableui/src/DragPositionManager.jsm
Normal file
@ -0,0 +1,364 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
Components.utils.import("resource:///modules/CustomizableUI.jsm");
|
||||
|
||||
let gManagers = new WeakMap();
|
||||
|
||||
const kPaletteId = "customization-palette";
|
||||
const kPlaceholderClass = "panel-customization-placeholder";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["DragPositionManager"];
|
||||
|
||||
function AreaPositionManager(aContainer) {
|
||||
// Caching the direction and bounds of the container for quick access later:
|
||||
let window = aContainer.ownerDocument.defaultView;
|
||||
this._dir = window.getComputedStyle(aContainer).direction;
|
||||
let containerRect = aContainer.getBoundingClientRect();
|
||||
this._containerInfo = {
|
||||
left: containerRect.left,
|
||||
right: containerRect.right,
|
||||
width: containerRect.width
|
||||
};
|
||||
this._inPanel = aContainer.id == CustomizableUI.AREA_PANEL;
|
||||
this._horizontalDistance = null;
|
||||
this.update(aContainer);
|
||||
}
|
||||
|
||||
AreaPositionManager.prototype = {
|
||||
_nodePositionStore: null,
|
||||
_wideCache: null,
|
||||
|
||||
update: function(aContainer) {
|
||||
let window = aContainer.ownerDocument.defaultView;
|
||||
this._nodePositionStore = new WeakMap();
|
||||
this._wideCache = new Set();
|
||||
let last = null;
|
||||
let singleItemHeight;
|
||||
for (let child of aContainer.children) {
|
||||
let isNodeWide = this._checkIfWide(child);
|
||||
if (isNodeWide) {
|
||||
this._wideCache.add(child.id);
|
||||
}
|
||||
let coordinates = this._lazyStoreGet(child);
|
||||
// We keep a baseline horizontal distance between non-wide nodes around
|
||||
// for use when we can't compare with previous/next nodes
|
||||
if (!this._horizontalDistance && last && !isNodeWide) {
|
||||
this._horizontalDistance = coordinates.left - last.left;
|
||||
}
|
||||
// We also keep the basic height of non-wide items for use below:
|
||||
if (!isNodeWide && !singleItemHeight) {
|
||||
singleItemHeight = coordinates.height;
|
||||
}
|
||||
last = !isNodeWide ? coordinates : null;
|
||||
}
|
||||
if (this._inPanel) {
|
||||
this._heightToWidthFactor = CustomizableUI.PANEL_COLUMN_COUNT;
|
||||
} else {
|
||||
this._heightToWidthFactor = this._containerInfo.width / singleItemHeight;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Find the closest node in the container given the coordinates.
|
||||
* "Closest" is defined in a somewhat strange manner: we prefer nodes
|
||||
* which are in the same row over nodes that are in a different row.
|
||||
* In order to implement this, we use a weighted cartesian distance
|
||||
* where dy is more heavily weighted by a factor corresponding to the
|
||||
* ratio between the container's width and the height of its elements.
|
||||
*/
|
||||
find: function(aContainer, aX, aY, aDraggedItemId) {
|
||||
let closest = null;
|
||||
let minCartesian = Number.MAX_VALUE;
|
||||
for (let node of aContainer.children) {
|
||||
let coordinates = this._lazyStoreGet(node);
|
||||
let hDiff = coordinates.x - aX;
|
||||
let vDiff = coordinates.y - aY;
|
||||
// For wide widgets, we're always going to be further from the center
|
||||
// horizontally. Compensate:
|
||||
if (this.isWide(node)) {
|
||||
hDiff /= CustomizableUI.PANEL_COLUMN_COUNT;
|
||||
}
|
||||
// Then compensate for the height/width ratio so that we prefer items
|
||||
// which are in the same row:
|
||||
hDiff /= this._heightToWidthFactor;
|
||||
|
||||
let cartesianDiff = hDiff * hDiff + vDiff * vDiff;
|
||||
if (cartesianDiff < minCartesian) {
|
||||
minCartesian = cartesianDiff;
|
||||
closest = node;
|
||||
}
|
||||
}
|
||||
|
||||
// Now correct this node based on what we're dragging
|
||||
if (closest) {
|
||||
let doc = aContainer.ownerDocument;
|
||||
let draggedItem = doc.getElementById(aDraggedItemId);
|
||||
// If dragging a wide item, always pick the first item in a row:
|
||||
if (draggedItem &&
|
||||
draggedItem.classList.contains(CustomizableUI.WIDE_PANEL_CLASS)) {
|
||||
return this._firstInRow(closest);
|
||||
}
|
||||
let targetBounds = this._lazyStoreGet(closest);
|
||||
let farSide = this._dir == "ltr" ? "right" : "left";
|
||||
let outsideX = targetBounds[farSide];
|
||||
// Check if we're closer to the next target than to this one:
|
||||
// Only move if we're not targeting a node in a different row:
|
||||
if (aY > targetBounds.top && aY < targetBounds.bottom) {
|
||||
if ((this._dir == "ltr" && aX > outsideX) ||
|
||||
(this._dir == "rtl" && aX < outsideX)) {
|
||||
return closest.nextSibling || aContainer;
|
||||
}
|
||||
}
|
||||
}
|
||||
return closest;
|
||||
},
|
||||
|
||||
/**
|
||||
* "Insert" a "placeholder" by shifting the subsequent children out of the
|
||||
* way. We go through all the children, and shift them based on the position
|
||||
* they would have if we had inserted something before aBefore. We use CSS
|
||||
* transforms for this, which are CSS transitioned.
|
||||
*/
|
||||
insertPlaceholder: function(aContainer, aBefore, aWide, aSize) {
|
||||
let isShifted = false;
|
||||
let shiftDown = aWide;
|
||||
for (let child of aContainer.children) {
|
||||
// Don't need to shift hidden nodes:
|
||||
if (child.getAttribute("hidden") == "true") {
|
||||
continue;
|
||||
}
|
||||
// If this is the node before which we're inserting, start shifting
|
||||
// everything that comes after. One exception is inserting at the end
|
||||
// of the menupanel, in which case we do not shift the placeholders:
|
||||
if (child == aBefore && !child.classList.contains(kPlaceholderClass)) {
|
||||
isShifted = true;
|
||||
// If the node before which we're inserting is wide, we should
|
||||
// shift everything one row down:
|
||||
if (!shiftDown && this.isWide(child)) {
|
||||
shiftDown = true;
|
||||
}
|
||||
}
|
||||
// If we're moving items before a wide node that were already there,
|
||||
// it's possible it's not necessary to shift nodes
|
||||
// including & after the wide node.
|
||||
if (this.__undoShift) {
|
||||
isShifted = false;
|
||||
}
|
||||
if (isShifted) {
|
||||
// Conversely, if we're adding something before a wide node, for
|
||||
// simplicity's sake we move everything including the wide node down:
|
||||
if (this.__moveDown) {
|
||||
shiftDown = true;
|
||||
}
|
||||
// Determine the CSS transform based on the next node:
|
||||
child.style.transform = this._getNextPos(child, shiftDown, aSize);
|
||||
} else {
|
||||
// If we're not shifting this node, reset the transform
|
||||
child.style.transform = "";
|
||||
}
|
||||
}
|
||||
delete this.__moveDown;
|
||||
delete this.__undoShift;
|
||||
},
|
||||
|
||||
isWide: function(aNode) {
|
||||
return this._wideCache.has(aNode.id);
|
||||
},
|
||||
|
||||
_checkIfWide: function(aNode) {
|
||||
return this._inPanel && aNode && aNode.firstChild &&
|
||||
aNode.firstChild.classList.contains(CustomizableUI.WIDE_PANEL_CLASS);
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset all the transforms in this container, optionally without
|
||||
* transitioning them.
|
||||
* @param aContainer the container in which to reset transforms
|
||||
* @param aNoTransition if truthy, adds a notransition attribute to the node
|
||||
* while resetting the transform.
|
||||
*/
|
||||
clearPlaceholders: function(aContainer, aNoTransition) {
|
||||
for (let child of aContainer.children) {
|
||||
if (aNoTransition) {
|
||||
child.setAttribute("notransition", true);
|
||||
}
|
||||
child.style.transform = "";
|
||||
if (aNoTransition) {
|
||||
// Need to force a reflow otherwise this won't work.
|
||||
child.getBoundingClientRect();
|
||||
child.removeAttribute("notransition");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_getNextPos: function(aNode, aShiftDown, aSize) {
|
||||
// Shifting down is easy:
|
||||
if (this._inPanel && aShiftDown) {
|
||||
return "translate(0, " + aSize.height + "px)";
|
||||
}
|
||||
return this._diffWithNext(aNode, aSize);
|
||||
},
|
||||
|
||||
_diffWithNext: function(aNode, aSize) {
|
||||
let xDiff;
|
||||
let yDiff = null;
|
||||
let nodeBounds = this._lazyStoreGet(aNode);
|
||||
let side = this._dir == "ltr" ? "left" : "right";
|
||||
let next = this._getVisibleSiblingForDirection(aNode, "next");
|
||||
// First we determine the transform along the x axis.
|
||||
// Usually, there will be a next node to base this on:
|
||||
if (next) {
|
||||
let otherBounds = this._lazyStoreGet(next);
|
||||
xDiff = otherBounds[side] - nodeBounds[side];
|
||||
// If the next node is a wide item in the panel, check if we could maybe
|
||||
// just move further out in the same row, without snapping to the next
|
||||
// one. This happens, for example, if moving an item that's before a wide
|
||||
// node within its own row of items. There will be space to drop this
|
||||
// item within the row, and the rest of the items do not need to shift.
|
||||
if (this.isWide(next)) {
|
||||
let otherXDiff = this._moveNextBasedOnPrevious(aNode, nodeBounds,
|
||||
this._firstInRow(aNode));
|
||||
// If this has the same sign as our original shift, we're still
|
||||
// snapping to the start of the row. In this case, we should move
|
||||
// everything after us a row down, so as not to display two nodes on
|
||||
// top of each other:
|
||||
// (we would be able to get away with checking for equality instead of
|
||||
// equal signs here, but one of these is based on the x coordinate of
|
||||
// the first item in row N and one on that for row N - 1, so this is
|
||||
// safer, as their margins might differ)
|
||||
if ((otherXDiff < 0) == (xDiff < 0)) {
|
||||
this.__moveDown = true;
|
||||
} else {
|
||||
// Otherwise, we succeeded and can move further out. This also means
|
||||
// we can stop shifting the rest of the content:
|
||||
xDiff = otherXDiff;
|
||||
this.__undoShift = true;
|
||||
}
|
||||
} else {
|
||||
// We set this explicitly because otherwise some strange difference
|
||||
// between the height and the actual difference between line creeps in
|
||||
// and messes with alignments
|
||||
yDiff = otherBounds.top - nodeBounds.top;
|
||||
}
|
||||
} else {
|
||||
// We don't have a sibling whose position we can use. First, let's see
|
||||
// if we're also the first item (which complicates things):
|
||||
let firstNode = this._firstInRow(aNode);
|
||||
if (aNode == firstNode) {
|
||||
// Maybe we stored the horizontal distance between non-wide nodes,
|
||||
// if not, we'll use the width of the incoming node as a proxy:
|
||||
xDiff = this._horizontalDistance || aSize.width;
|
||||
} else {
|
||||
// If not, we should be able to get the distance to the previous node
|
||||
// and use the inverse, unless there's no room for another node (ie we
|
||||
// are the last node and there's no room for another one)
|
||||
xDiff = this._moveNextBasedOnPrevious(aNode, nodeBounds, firstNode);
|
||||
}
|
||||
}
|
||||
|
||||
// If we've not determined the vertical difference yet, check it here
|
||||
if (yDiff === null) {
|
||||
// If the next node is behind rather than in front, we must have moved
|
||||
// vertically:
|
||||
if ((xDiff > 0 && this._dir == "rtl") || (xDiff < 0 && this._dir == "ltr")) {
|
||||
yDiff = aSize.height;
|
||||
} else {
|
||||
// Otherwise, we haven't
|
||||
yDiff = 0;
|
||||
}
|
||||
}
|
||||
return "translate(" + xDiff + "px, " + yDiff + "px)";
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper function to find the transform a node if there isn't a next node
|
||||
* to base that on.
|
||||
* @param aNode the node to transform
|
||||
* @param aNodeBounds the bounding rect info of this node
|
||||
* @param aFirstNodeInRow the first node in aNode's row
|
||||
*/
|
||||
_moveNextBasedOnPrevious: function(aNode, aNodeBounds, aFirstNodeInRow) {
|
||||
let next = this._getVisibleSiblingForDirection(aNode, "previous");
|
||||
let otherBounds = this._lazyStoreGet(next);
|
||||
let side = this._dir == "ltr" ? "left" : "right";
|
||||
let xDiff = aNodeBounds[side] - otherBounds[side];
|
||||
// If, however, this means we move outside the container's box
|
||||
// (i.e. the row in which this item is placed is full)
|
||||
// we should move it to align with the first item in the next row instead
|
||||
let bound = this._containerInfo[this._dir == "ltr" ? "right" : "left"];
|
||||
if ((this._dir == "ltr" && xDiff + aNodeBounds.right > bound) ||
|
||||
(this._dir == "rtl" && xDiff + aNodeBounds.left < bound)) {
|
||||
xDiff = this._lazyStoreGet(aFirstNodeInRow)[side] - aNodeBounds[side];
|
||||
}
|
||||
return xDiff;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get position details from our cache. If the node is not yet cached, get its position
|
||||
* information and cache it now.
|
||||
* @param aNode the node whose position info we want
|
||||
* @return the position info
|
||||
*/
|
||||
_lazyStoreGet: function(aNode) {
|
||||
let rect = this._nodePositionStore.get(aNode);
|
||||
if (!rect) {
|
||||
rect = aNode.getBoundingClientRect();
|
||||
rect.x = rect.left + rect.width / 2;
|
||||
rect.y = rect.top + rect.height / 2;
|
||||
this._nodePositionStore.set(aNode, rect);
|
||||
}
|
||||
return rect;
|
||||
},
|
||||
|
||||
_firstInRow: function(aNode) {
|
||||
let bound = this._lazyStoreGet(aNode).top;
|
||||
let rv = aNode;
|
||||
let prev;
|
||||
while (rv && (prev = this._getVisibleSiblingForDirection(rv, "previous"))) {
|
||||
if (this._lazyStoreGet(prev).bottom <= bound) {
|
||||
return rv;
|
||||
}
|
||||
rv = prev;
|
||||
}
|
||||
return rv;
|
||||
},
|
||||
|
||||
_getVisibleSiblingForDirection: function(aNode, aDirection) {
|
||||
let rv = aNode;
|
||||
do {
|
||||
rv = rv[aDirection + "Sibling"];
|
||||
} while (rv && rv.getAttribute("hidden") == "true")
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
let DragPositionManager = {
|
||||
start: function(aWindow) {
|
||||
let areas = CustomizableUI.areas.filter((area) => CustomizableUI.getAreaType(area) != "toolbar");
|
||||
areas = areas.map((area) => CustomizableUI.getCustomizeTargetForArea(area, aWindow));
|
||||
areas.push(aWindow.document.getElementById(kPaletteId));
|
||||
for (let areaNode of areas) {
|
||||
let positionManager = gManagers.get(areaNode);
|
||||
if (positionManager) {
|
||||
positionManager.update(areaNode);
|
||||
} else {
|
||||
gManagers.set(areaNode, new AreaPositionManager(areaNode));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
stop: function() {
|
||||
gManagers.clear();
|
||||
},
|
||||
|
||||
getManagerForArea: function(aArea) {
|
||||
return gManagers.get(aArea);
|
||||
}
|
||||
};
|
||||
|
||||
Object.freeze(DragPositionManager);
|
||||
|
@ -23,13 +23,6 @@ let gWideWidgets = new Set();
|
||||
// All the widgets we know of:
|
||||
let gSeenWidgets = new Set();
|
||||
|
||||
// The class by which we recognize wide widgets:
|
||||
const kWidePanelItemClass = "panel-wide-item";
|
||||
|
||||
// TODO(bug 885574): Merge this constant with the one in CustomizeMode.jsm,
|
||||
// maybe just use a pref for this.
|
||||
const kColumnsInMenuPanel = 3;
|
||||
|
||||
let PanelWideWidgetTracker = {
|
||||
// Listeners used to validate panel contents whenever they change:
|
||||
onWidgetAdded: function(aWidgetId, aArea, aPosition) {
|
||||
@ -61,7 +54,7 @@ let PanelWideWidgetTracker = {
|
||||
// Furthermore, onWidgetCreated only fires for API-based widgets, not for XUL ones.
|
||||
onWidgetAfterDOMChange: function(aNode, aNextNode, aContainer) {
|
||||
if (!gSeenWidgets.has(aNode.id)) {
|
||||
if (aNode.classList.contains(kWidePanelItemClass)) {
|
||||
if (aNode.classList.contains(CustomizableUI.WIDE_PANEL_CLASS)) {
|
||||
gWideWidgets.add(aNode.id);
|
||||
}
|
||||
gSeenWidgets.add(aNode.id);
|
||||
@ -137,12 +130,12 @@ let PanelWideWidgetTracker = {
|
||||
}
|
||||
}
|
||||
|
||||
if (fixedPos !== null || prevSiblingCount % kColumnsInMenuPanel) {
|
||||
if (fixedPos !== null || prevSiblingCount % CustomizableUI.PANEL_COLUMN_COUNT) {
|
||||
let desiredPos = (fixedPos !== null) ? fixedPos : gPanelPlacements.indexOf(aWidgetId);
|
||||
let desiredChange = -(prevSiblingCount % kColumnsInMenuPanel);
|
||||
let desiredChange = -(prevSiblingCount % CustomizableUI.PANEL_COLUMN_COUNT);
|
||||
if (aMoveForwards && fixedPos == null) {
|
||||
// +1 because otherwise we'd count ourselves:
|
||||
desiredChange = kColumnsInMenuPanel + desiredChange + 1;
|
||||
desiredChange = CustomizableUI.PANEL_COLUMN_COUNT + desiredChange + 1;
|
||||
}
|
||||
desiredPos += desiredChange;
|
||||
CustomizableUI.moveWidgetWithinArea(aWidgetId, desiredPos);
|
||||
|
@ -5,6 +5,7 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'DragPositionManager.jsm',
|
||||
'ScrollbarSampler.jsm',
|
||||
]
|
||||
|
||||
|
@ -1,5 +1,15 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
browser_privatebrowsing_concurrent_page.html
|
||||
browser_privatebrowsing_cookieacceptdialog.html
|
||||
browser_privatebrowsing_geoprompt_page.html
|
||||
browser_privatebrowsing_localStorage_before_after_page.html
|
||||
browser_privatebrowsing_localStorage_before_after_page2.html
|
||||
browser_privatebrowsing_localStorage_page1.html
|
||||
browser_privatebrowsing_localStorage_page2.html
|
||||
browser_privatebrowsing_placesTitleNoUpdate.html
|
||||
browser_privatebrowsing_protocolhandler_page.html
|
||||
browser_privatebrowsing_windowtitle_page.html
|
||||
head.js
|
||||
popup.html
|
||||
title.sjs
|
||||
@ -11,38 +21,28 @@ support-files =
|
||||
# Disabled for too many intermittent failures (bug 895390)
|
||||
[browser_privatebrowsing_certexceptionsui.js]
|
||||
[browser_privatebrowsing_concurrent.js]
|
||||
[browser_privatebrowsing_concurrent_page.html]
|
||||
[browser_privatebrowsing_cookieacceptdialog.html]
|
||||
[browser_privatebrowsing_cookieacceptdialog.js]
|
||||
[browser_privatebrowsing_crh.js]
|
||||
[browser_privatebrowsing_downloadLastDir.js]
|
||||
[browser_privatebrowsing_downloadLastDir_c.js]
|
||||
[browser_privatebrowsing_downloadLastDir_toggle.js]
|
||||
[browser_privatebrowsing_geoprompt.js]
|
||||
[browser_privatebrowsing_geoprompt_page.html]
|
||||
[browser_privatebrowsing_lastpbcontextexited.js]
|
||||
[browser_privatebrowsing_localStorage.js]
|
||||
[browser_privatebrowsing_localStorage_before_after.js]
|
||||
[browser_privatebrowsing_localStorage_before_after_page.html]
|
||||
[browser_privatebrowsing_localStorage_before_after_page2.html]
|
||||
[browser_privatebrowsing_localStorage_page1.html]
|
||||
[browser_privatebrowsing_localStorage_page2.html]
|
||||
[browser_privatebrowsing_noSessionRestoreMenuOption.js]
|
||||
[browser_privatebrowsing_nonbrowser.js]
|
||||
[browser_privatebrowsing_openLocationLastURL.js]
|
||||
[browser_privatebrowsing_opendir.js]
|
||||
[browser_privatebrowsing_openlocation.js]
|
||||
[browser_privatebrowsing_placesTitleNoUpdate.html]
|
||||
[browser_privatebrowsing_placesTitleNoUpdate.js]
|
||||
[browser_privatebrowsing_placestitle.js]
|
||||
[browser_privatebrowsing_popupblocker.js]
|
||||
[browser_privatebrowsing_protocolhandler.js]
|
||||
[browser_privatebrowsing_protocolhandler_page.html]
|
||||
[browser_privatebrowsing_sidebar.js]
|
||||
[browser_privatebrowsing_theming.js]
|
||||
[browser_privatebrowsing_ui.js]
|
||||
[browser_privatebrowsing_urlbarfocus.js]
|
||||
[browser_privatebrowsing_windowtitle.js]
|
||||
[browser_privatebrowsing_windowtitle_page.html]
|
||||
[browser_privatebrowsing_zoom.js]
|
||||
[browser_privatebrowsing_zoomrestore.js]
|
||||
|
@ -30,7 +30,7 @@ devtoolsCommandlineHandler.prototype = {
|
||||
if (!window) {
|
||||
let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
|
||||
// Load the browser devtools main module as the loader's main module.
|
||||
devtools.main("main");
|
||||
Cu.import("resource:///modules/devtools/gDevTools.jsm");
|
||||
let hudservice = devtools.require("devtools/webconsole/hudservice");
|
||||
let console = Cu.import("resource://gre/modules/devtools/Console.jsm", {}).console;
|
||||
hudservice.toggleBrowserConsole().then(null, console.error);
|
||||
|
@ -1,5 +1,6 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
browser_font.woff
|
||||
browser_fontinspector.html
|
||||
|
||||
[browser_font.woff]
|
||||
[browser_fontinspector.html]
|
||||
[browser_fontinspector.js]
|
||||
|
@ -52,6 +52,7 @@ DevTools.prototype = {
|
||||
* - id: Unique identifier for this tool (string|required)
|
||||
* - visibilityswitch: Property name to allow us to hide this tool from the
|
||||
* DevTools Toolbox.
|
||||
* A falsy value indicates that it cannot be hidden.
|
||||
* - icon: URL pointing to a graphic which will be used as the src for an
|
||||
* 16x16 img tag (string|required)
|
||||
* - url: URL pointing to a XUL/XHTML document containing the user interface
|
||||
@ -69,8 +70,13 @@ DevTools.prototype = {
|
||||
throw new Error("Invalid definition.id");
|
||||
}
|
||||
|
||||
toolDefinition.visibilityswitch = toolDefinition.visibilityswitch ||
|
||||
"devtools." + toolId + ".enabled";
|
||||
// Make sure that additional tools will always be able to be hidden.
|
||||
// When being called from main.js, defaultTools has not yet been exported.
|
||||
// But, we can assume that in this case, it is a default tool.
|
||||
if (devtools.defaultTools && devtools.defaultTools.indexOf(toolDefinition) == -1) {
|
||||
toolDefinition.visibilityswitch = "devtools." + toolId + ".enabled";
|
||||
}
|
||||
|
||||
this._tools.set(toolId, toolDefinition);
|
||||
|
||||
this.emit("tool-registered", toolId);
|
||||
@ -139,7 +145,7 @@ DevTools.prototype = {
|
||||
let tool = this._tools.get(toolId);
|
||||
if (!tool) {
|
||||
return null;
|
||||
} else if (tool.id == "options") {
|
||||
} else if (!tool.visibilityswitch) {
|
||||
return tool;
|
||||
}
|
||||
|
||||
@ -553,7 +559,8 @@ let gDevToolsBrowser = {
|
||||
|
||||
// Skip if the tool is disabled.
|
||||
try {
|
||||
if (!Services.prefs.getBoolPref(toolDefinition.visibilityswitch)) {
|
||||
if (toolDefinition.visibilityswitch &&
|
||||
!Services.prefs.getBoolPref(toolDefinition.visibilityswitch)) {
|
||||
return;
|
||||
}
|
||||
} catch(e) {}
|
||||
|
@ -1,5 +1,8 @@
|
||||
[DEFAULT]
|
||||
support-files = head.js
|
||||
support-files =
|
||||
browser_toolbox_options_disablejs.html
|
||||
browser_toolbox_options_disablejs_iframe.html
|
||||
head.js
|
||||
|
||||
[browser_devtools_api.js]
|
||||
[browser_dynamic_tool_enabling.js]
|
||||
@ -10,9 +13,7 @@ support-files = head.js
|
||||
[browser_toolbox_highlight.js]
|
||||
[browser_toolbox_hosts.js]
|
||||
[browser_toolbox_options.js]
|
||||
[browser_toolbox_options_disablejs.html]
|
||||
[browser_toolbox_options_disablejs.js]
|
||||
[browser_toolbox_options_disablejs_iframe.html]
|
||||
[browser_toolbox_raise.js]
|
||||
skip-if = os == "win"
|
||||
[browser_toolbox_ready.js]
|
||||
|
@ -105,8 +105,15 @@ function testToggleTools() {
|
||||
let toolNodes = panelWin.document.querySelectorAll("#default-tools-box > checkbox:not([unsupported])");
|
||||
let enabledTools = Array.prototype.filter.call(toolNodes, node => node.checked);
|
||||
|
||||
let toggleableTools = gDevTools.getDefaultTools().filter(tool=>tool.visibilityswitch);
|
||||
for (let node of toolNodes) {
|
||||
let id = node.getAttribute("id");
|
||||
ok (toggleableTools.some(tool=>tool.id === id),
|
||||
"There should be a toggle checkbox for: " + id);
|
||||
}
|
||||
|
||||
// Store modified pref names so that they can be cleared on error.
|
||||
for (let tool of gDevTools.getDefaultTools()) {
|
||||
for (let tool of toggleableTools) {
|
||||
let pref = tool.visibilityswitch;
|
||||
modifiedPrefs.push(pref);
|
||||
}
|
||||
|
@ -115,10 +115,10 @@ OptionsPanel.prototype = {
|
||||
};
|
||||
|
||||
// Populating the default tools lists
|
||||
for (let tool of gDevTools.getDefaultTools()) {
|
||||
if (tool.id == "options") {
|
||||
continue;
|
||||
}
|
||||
let toggleableTools = gDevTools.getDefaultTools().filter(tool => {
|
||||
return tool.visibilityswitch
|
||||
});
|
||||
for (let tool of toggleableTools) {
|
||||
defaultToolsBox.appendChild(createToolCheckbox(tool));
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,19 @@
|
||||
[DEFAULT]
|
||||
support-files = head.js
|
||||
support-files =
|
||||
browser_inspector_breadcrumbs.html
|
||||
browser_inspector_bug_650804_search.html
|
||||
browser_inspector_bug_831693_search_suggestions.html
|
||||
browser_inspector_cmd_inspect.html
|
||||
browser_inspector_dead_node_exception.html
|
||||
browser_inspector_destroyselection.html
|
||||
browser_inspector_menu.html
|
||||
browser_inspector_select_last_selected.html
|
||||
browser_inspector_select_last_selected2.html
|
||||
browser_inspector_bug_848731_reset_selection_on_delete.html
|
||||
head.js
|
||||
|
||||
[browser_inspector_basic_highlighter.js]
|
||||
[browser_inspector_breadcrumbs.html]
|
||||
[browser_inspector_breadcrumbs.js]
|
||||
[browser_inspector_bug_650804_search.html]
|
||||
[browser_inspector_bug_650804_search.js]
|
||||
[browser_inspector_bug_665880.js]
|
||||
[browser_inspector_bug_672902_keyboard_shortcuts.js]
|
||||
@ -13,17 +22,13 @@ support-files = head.js
|
||||
[browser_inspector_bug_817558_delete_node.js]
|
||||
[browser_inspector_bug_831693_combinator_suggestions.js]
|
||||
[browser_inspector_bug_831693_input_suggestion.js]
|
||||
[browser_inspector_bug_831693_search_suggestions.html]
|
||||
# [browser_inspector_bug_831693_searchbox_panel_navigation.js]
|
||||
# Disabled for too many intermittent failures (bug 851349)
|
||||
[browser_inspector_bug_835722_infobar_reappears.js]
|
||||
[browser_inspector_bug_840156_destroy_after_navigation.js]
|
||||
[browser_inspector_changes.js]
|
||||
[browser_inspector_cmd_inspect.html]
|
||||
[browser_inspector_cmd_inspect.js]
|
||||
[browser_inspector_dead_node_exception.html]
|
||||
[browser_inspector_dead_node_exception.js]
|
||||
[browser_inspector_destroyselection.html]
|
||||
[browser_inspector_destroyselection.js]
|
||||
[browser_inspector_highlighter.js]
|
||||
[browser_inspector_highlighter_autohide.js]
|
||||
@ -31,17 +36,13 @@ support-files = head.js
|
||||
[browser_inspector_infobar.js]
|
||||
[browser_inspector_initialization.js]
|
||||
[browser_inspector_invalidate.js]
|
||||
[browser_inspector_menu.html]
|
||||
[browser_inspector_menu.js]
|
||||
[browser_inspector_navigation.js]
|
||||
[browser_inspector_pseudoClass_menu.js]
|
||||
[browser_inspector_pseudoclass_lock.js]
|
||||
[browser_inspector_reload.js]
|
||||
[browser_inspector_scrolling.js]
|
||||
[browser_inspector_select_last_selected.html]
|
||||
[browser_inspector_select_last_selected.js]
|
||||
[browser_inspector_select_last_selected2.html]
|
||||
[browser_inspector_sidebarstate.js]
|
||||
[browser_inspector_bug_848731_reset_selection_on_delete.js]
|
||||
[browser_inspector_bug_848731_reset_selection_on_delete.html]
|
||||
[browser_inspector_bug_922125_destroy_on_navigate.js]
|
||||
|
@ -130,7 +130,6 @@ Tools.jsdebugger = {
|
||||
accesskey: l10n("debuggerMenu.accesskey", debuggerStrings),
|
||||
modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
|
||||
ordinal: 3,
|
||||
visibilityswitch: "devtools.debugger.enabled",
|
||||
icon: "chrome://browser/skin/devtools/tool-debugger.png",
|
||||
highlightedicon: "chrome://browser/skin/devtools/tool-debugger-paused.png",
|
||||
url: "chrome://browser/content/devtools/debugger.xul",
|
||||
|
@ -1,20 +1,21 @@
|
||||
[DEFAULT]
|
||||
support-files = head.js
|
||||
support-files =
|
||||
browser_inspector_markup_edit.html
|
||||
browser_inspector_markup_mutation.html
|
||||
browser_inspector_markup_mutation_flashing.html
|
||||
browser_inspector_markup_navigation.html
|
||||
browser_inspector_markup_subset.html
|
||||
browser_inspector_markup_765105_tooltip.png
|
||||
head.js
|
||||
|
||||
[browser_bug896181_css_mixed_completion_new_attribute.js]
|
||||
# Bug 916763 - too many intermittent failures
|
||||
skip-if = true
|
||||
[browser_inspector_markup_edit.html]
|
||||
[browser_inspector_markup_edit.js]
|
||||
[browser_inspector_markup_edit_outerhtml.js]
|
||||
[browser_inspector_markup_edit_outerhtml2.js]
|
||||
[browser_inspector_markup_mutation.html]
|
||||
[browser_inspector_markup_mutation.js]
|
||||
[browser_inspector_markup_mutation_flashing.html]
|
||||
[browser_inspector_markup_mutation_flashing.js]
|
||||
[browser_inspector_markup_navigation.html]
|
||||
[browser_inspector_markup_navigation.js]
|
||||
[browser_inspector_markup_subset.html]
|
||||
[browser_inspector_markup_subset.js]
|
||||
[browser_inspector_markup_765105_tooltip.js]
|
||||
[browser_inspector_markup_765105_tooltip.png]
|
||||
|
@ -500,12 +500,7 @@ var Scratchpad = {
|
||||
reject(aResponse);
|
||||
}
|
||||
else {
|
||||
let string = aResponse.displayString;
|
||||
if (string && string.type == "null") {
|
||||
string = "Exception: " +
|
||||
this.strings.GetStringFromName("stringConversionFailed");
|
||||
}
|
||||
this.writeAsComment(string);
|
||||
this.writeAsComment(aResponse.displayString);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
@ -28,6 +28,8 @@
|
||||
width="640" height="480"
|
||||
persist="screenX screenY width height sizemode">
|
||||
|
||||
<script type="application/javascript;version=1.8"
|
||||
src="chrome://browser/content/devtools/theme-switching.js"/>
|
||||
<script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/devtools/scratchpad.js"/>
|
||||
|
||||
|
@ -7,7 +7,6 @@ support-files = head.js
|
||||
[browser_scratchpad_goto_line_ui.js]
|
||||
[browser_scratchpad_reload_and_run.js]
|
||||
[browser_scratchpad_display_non_error_exceptions.js]
|
||||
[browser_scratchpad_cannot_convert_to_string.js]
|
||||
[browser_scratchpad_modeline.js]
|
||||
[browser_scratchpad_chrome_context_pref.js]
|
||||
[browser_scratchpad_help_key.js]
|
||||
|
@ -1,34 +0,0 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* Bug 807924 */
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
|
||||
openScratchpad(runTests);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html;charset=utf8,test display of values" +
|
||||
" which can't be converted to string in Scratchpad";
|
||||
}
|
||||
|
||||
|
||||
function runTests()
|
||||
{
|
||||
let sp = gScratchpadWindow.Scratchpad;
|
||||
sp.setText("Object.create(null);");
|
||||
|
||||
sp.display().then(([, , aResult]) => {
|
||||
is(sp.getText(),
|
||||
"Object.create(null);\n" +
|
||||
"/*\nException: Cannot convert value to string.\n*/",
|
||||
"'Cannot convert value to string' comment is shown");
|
||||
|
||||
finish();
|
||||
});
|
||||
}
|
@ -10,12 +10,30 @@ function ifWebGLSupported() {
|
||||
let [target, debuggee, front] = yield initBackend(SIMPLE_CANVAS_URL);
|
||||
front.setup({ reload: false });
|
||||
|
||||
// 0. Perform the initial reload.
|
||||
|
||||
reload(target);
|
||||
let firstProgram = yield once(front, "program-linked");
|
||||
let programs = yield front.getPrograms();
|
||||
is(programs.length, 1,
|
||||
"The first program should be returned by a call to getPrograms().");
|
||||
is(programs[0], firstProgram,
|
||||
"The first programs was correctly retrieved from the cache.");
|
||||
|
||||
// 1. Perform a simple navigation.
|
||||
|
||||
navigate(target, MULTIPLE_CONTEXTS_URL);
|
||||
let secondProgram = yield once(front, "program-linked");
|
||||
let thirdProgram = yield once(front, "program-linked");
|
||||
let programs = yield front.getPrograms();
|
||||
is(programs.length, 2,
|
||||
"The second and third programs should be returned by a call to getPrograms().");
|
||||
is(programs[0], secondProgram,
|
||||
"The second programs was correctly retrieved from the cache.");
|
||||
is(programs[1], thirdProgram,
|
||||
"The third programs was correctly retrieved from the cache.");
|
||||
|
||||
// 2. Perform a bfcache navigation.
|
||||
|
||||
yield navigateInHistory(target, "back");
|
||||
let globalDestroyed = observe("inner-window-destroyed");
|
||||
@ -35,6 +53,8 @@ function ifWebGLSupported() {
|
||||
yield checkHighlightingInTheFirstPage(programs[0]);
|
||||
ok(true, "The cached programs behave correctly after navigating back and reloading.");
|
||||
|
||||
// 3. Perform a bfcache navigation and a page reload.
|
||||
|
||||
yield navigateInHistory(target, "forward");
|
||||
let globalDestroyed = observe("inner-window-destroyed");
|
||||
let globalCreated = observe("content-document-global-created");
|
||||
|
@ -1,13 +1,16 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
browser_layoutHelpers.html
|
||||
browser_layoutHelpers_iframe.html
|
||||
browser_templater_basic.html
|
||||
browser_toolbar_basic.html
|
||||
browser_toolbar_webconsole_errors_count.html
|
||||
head.js
|
||||
leakhunt.js
|
||||
|
||||
[browser_css_color.js]
|
||||
[browser_eventemitter_basic.js]
|
||||
[browser_layoutHelpers.html]
|
||||
[browser_layoutHelpers.js]
|
||||
[browser_layoutHelpers_iframe.html]
|
||||
[browser_observableobject.js]
|
||||
[browser_outputparser.js]
|
||||
[browser_require_basic.js]
|
||||
@ -23,11 +26,8 @@ support-files =
|
||||
[browser_telemetry_toolboxtabs_options.js]
|
||||
[browser_telemetry_toolboxtabs_styleeditor.js]
|
||||
[browser_telemetry_toolboxtabs_webconsole.js]
|
||||
[browser_templater_basic.html]
|
||||
[browser_templater_basic.js]
|
||||
[browser_toolbar_basic.html]
|
||||
[browser_toolbar_basic.js]
|
||||
[browser_toolbar_tooltip.js]
|
||||
[browser_toolbar_webconsole_errors_count.html]
|
||||
[browser_toolbar_webconsole_errors_count.js]
|
||||
[browser_spectrum.js]
|
||||
|
@ -24,6 +24,7 @@ const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
const SPECTRUM_FRAME = "chrome://browser/content/devtools/spectrum-frame.xhtml";
|
||||
const ESCAPE_KEYCODE = Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE;
|
||||
const ENTER_KEYCODE = Ci.nsIDOMKeyEvent.DOM_VK_RETURN;
|
||||
const SHOW_TIMEOUT = 50;
|
||||
|
||||
/**
|
||||
* Tooltip widget.
|
||||
@ -191,8 +192,12 @@ Tooltip.prototype = {
|
||||
* @param {node} anchor
|
||||
* Which node should the tooltip be shown on
|
||||
* @param {string} position
|
||||
* Optional tooltip position. Defaults to before_start
|
||||
* https://developer.mozilla.org/en-US/docs/XUL/PopupGuide/Positioning
|
||||
* Defaults to before_start
|
||||
* @param {number} x
|
||||
* Optional x offset. Defaults to 0
|
||||
* @param {number} y
|
||||
* Optional y offset. Defaults to 0
|
||||
*/
|
||||
show: function(anchor,
|
||||
position = this.defaultPosition,
|
||||
@ -239,14 +244,14 @@ Tooltip.prototype = {
|
||||
|
||||
this.content = null;
|
||||
|
||||
if (this._basedNode) {
|
||||
this.stopTogglingOnHover();
|
||||
}
|
||||
|
||||
this.doc = null;
|
||||
|
||||
this.panel.parentNode.removeChild(this.panel);
|
||||
this.panel = null;
|
||||
|
||||
if (this._basedNode) {
|
||||
this.stopTogglingOnHover();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -279,17 +284,13 @@ Tooltip.prototype = {
|
||||
* tooltip if needed. If omitted, the tooltip will be shown everytime.
|
||||
* @param {Number} showDelay
|
||||
* An optional delay that will be observed before showing the tooltip.
|
||||
* Defaults to 750ms
|
||||
* Defaults to SHOW_TIMEOUT
|
||||
*/
|
||||
startTogglingOnHover: function(baseNode, targetNodeCb, showDelay = 750) {
|
||||
startTogglingOnHover: function(baseNode, targetNodeCb, showDelay=SHOW_TIMEOUT) {
|
||||
if (this._basedNode) {
|
||||
this.stopTogglingOnHover();
|
||||
}
|
||||
|
||||
// If no targetNodeCb callback is provided, then we need to hide the tooltip
|
||||
// on mouseleave since baseNode is the target node itself
|
||||
this._hideOnMouseLeave = !targetNodeCb;
|
||||
|
||||
this._basedNode = baseNode;
|
||||
this._showDelay = showDelay;
|
||||
this._targetNodeCb = targetNodeCb || (() => true);
|
||||
@ -322,7 +323,7 @@ Tooltip.prototype = {
|
||||
_onBaseNodeMouseMove: function(event) {
|
||||
if (event.target !== this._lastHovered) {
|
||||
this.hide();
|
||||
this._lastHovered = null;
|
||||
this._lastHovered = event.target;
|
||||
setNamedTimeout(this.uid, this._showDelay, () => {
|
||||
this._showOnHover(event.target);
|
||||
});
|
||||
@ -332,16 +333,13 @@ Tooltip.prototype = {
|
||||
_showOnHover: function(target) {
|
||||
if (this._targetNodeCb(target, this)) {
|
||||
this.show(target);
|
||||
this._lastHovered = target;
|
||||
}
|
||||
},
|
||||
|
||||
_onBaseNodeMouseLeave: function() {
|
||||
clearNamedTimeout(this.uid);
|
||||
this._lastHovered = null;
|
||||
if (this._hideOnMouseLeave) {
|
||||
this.hide();
|
||||
}
|
||||
this.hide();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -365,7 +363,7 @@ Tooltip.prototype = {
|
||||
/**
|
||||
* Sets some text as the content of this tooltip.
|
||||
*
|
||||
* @param string[] messages
|
||||
* @param {string[]} messages
|
||||
* A list of text messages.
|
||||
*/
|
||||
setTextContent: function(...messages) {
|
||||
|
@ -12,5 +12,8 @@
|
||||
]>
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
title="&PropertiesViewWindowTitle;">
|
||||
|
||||
<script type="application/javascript;version=1.8"
|
||||
src="chrome://browser/content/devtools/theme-switching.js"/>
|
||||
<vbox id="variables" flex="1"/>
|
||||
</window>
|
||||
|
0
browser/devtools/sourceeditor/orion/Makefile.dryice.js
Executable file → Normal file
0
browser/devtools/sourceeditor/orion/Makefile.dryice.js
Executable file → Normal file
@ -1,5 +1,6 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
browser_styleeditor_cmd_edit.html
|
||||
four.html
|
||||
head.js
|
||||
import.css
|
||||
@ -24,7 +25,6 @@ support-files =
|
||||
[browser_styleeditor_bug_740541_iframes.js]
|
||||
[browser_styleeditor_bug_851132_middle_click.js]
|
||||
[browser_styleeditor_bug_870339.js]
|
||||
[browser_styleeditor_cmd_edit.html]
|
||||
[browser_styleeditor_cmd_edit.js]
|
||||
[browser_styleeditor_enabled.js]
|
||||
[browser_styleeditor_filesave.js]
|
||||
|
@ -1125,23 +1125,17 @@ CssRuleView.prototype = {
|
||||
* prepare some content for the tooltip
|
||||
*/
|
||||
_buildTooltipContent: function(target) {
|
||||
let isValueWithImage = target.classList.contains("ruleview-propertyvalue") &&
|
||||
target.querySelector(".theme-link");
|
||||
|
||||
let isImageHref = target.classList.contains("theme-link") &&
|
||||
target.parentNode.classList.contains("ruleview-propertyvalue");
|
||||
if (isImageHref) {
|
||||
target = target.parentNode;
|
||||
}
|
||||
|
||||
// If the inplace-editor is visible or if this is not a background image
|
||||
// don't show the tooltip
|
||||
if (!isImageHref && !isValueWithImage) {
|
||||
if (!isImageHref) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retrieve the TextProperty for the hovered element
|
||||
let property = target.textProperty;
|
||||
let property = target.parentNode.textProperty;
|
||||
let href = property.rule.domRule.href;
|
||||
|
||||
// Fill some content
|
||||
|
@ -22,8 +22,7 @@ const PAGE_CONTENT = [
|
||||
' padding-left: 70px;',
|
||||
' }',
|
||||
'</style>',
|
||||
'<div class="test-element">test element</div>',
|
||||
'<div class="test-element-2">test element 2</div>'
|
||||
'<div class="test-element">test element</div>'
|
||||
].join("\n");
|
||||
|
||||
function test() {
|
||||
@ -50,8 +49,6 @@ function createDocument() {
|
||||
}
|
||||
|
||||
function startTests() {
|
||||
// let testElement = contentDoc.querySelector(".test-element");
|
||||
|
||||
inspector.selection.setNode(contentDoc.body);
|
||||
inspector.once("inspector-updated", testBodyRuleView);
|
||||
}
|
||||
@ -81,9 +78,10 @@ function testBodyRuleView() {
|
||||
ok(panel, "XUL panel exists");
|
||||
|
||||
// Get the background-image property inside the rule view
|
||||
let {nameSpan, valueSpan} = getRuleViewProperty("background-image");
|
||||
let {valueSpan} = getRuleViewProperty("background-image");
|
||||
let uriSpan = valueSpan.querySelector(".theme-link");
|
||||
// And verify that the tooltip gets shown on this property
|
||||
assertTooltipShownOn(ruleView.previewTooltip, valueSpan, () => {
|
||||
assertTooltipShownOn(ruleView.previewTooltip, uriSpan, () => {
|
||||
let images = panel.getElementsByTagName("image");
|
||||
is(images.length, 1, "Tooltip contains an image");
|
||||
ok(images[0].src.indexOf("iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHe") !== -1, "The image URL seems fine");
|
||||
@ -99,7 +97,7 @@ function testDivRuleView() {
|
||||
let panel = ruleView.previewTooltip.panel;
|
||||
|
||||
// Get the background property inside the rule view
|
||||
let {nameSpan, valueSpan} = getRuleViewProperty("background");
|
||||
let {valueSpan} = getRuleViewProperty("background");
|
||||
let uriSpan = valueSpan.querySelector(".theme-link");
|
||||
|
||||
// And verify that the tooltip gets shown on this property
|
||||
@ -110,10 +108,31 @@ function testDivRuleView() {
|
||||
|
||||
ruleView.previewTooltip.hide();
|
||||
|
||||
testComputedView();
|
||||
testTooltipAppearsEvenInEditMode();
|
||||
});
|
||||
}
|
||||
|
||||
function testTooltipAppearsEvenInEditMode() {
|
||||
let panel = ruleView.previewTooltip.panel;
|
||||
|
||||
// Switch one field to edit mode
|
||||
let brace = ruleView.doc.querySelector(".ruleview-ruleclose");
|
||||
waitForEditorFocus(brace.parentNode, editor => {
|
||||
// Now try to show the tooltip
|
||||
let {valueSpan} = getRuleViewProperty("background");
|
||||
let uriSpan = valueSpan.querySelector(".theme-link");
|
||||
assertTooltipShownOn(ruleView.previewTooltip, uriSpan, () => {
|
||||
is(ruleView.doc.activeElement, editor.input,
|
||||
"Tooltip was shown in edit mode, and inplace-editor still focused");
|
||||
|
||||
ruleView.previewTooltip.hide();
|
||||
|
||||
testComputedView();
|
||||
});
|
||||
});
|
||||
brace.click();
|
||||
}
|
||||
|
||||
function testComputedView() {
|
||||
info("Testing tooltips in the computed view");
|
||||
|
||||
@ -122,7 +141,7 @@ function testComputedView() {
|
||||
let doc = computedView.styleDocument;
|
||||
|
||||
let panel = computedView.tooltip.panel;
|
||||
let {nameSpan, valueSpan} = getComputedViewProperty("background-image");
|
||||
let {valueSpan} = getComputedViewProperty("background-image");
|
||||
|
||||
assertTooltipShownOn(computedView.tooltip, valueSpan, () => {
|
||||
let images = panel.getElementsByTagName("image");
|
||||
|
@ -86,10 +86,6 @@ fileNoLongerExists.notification=This file no longer exists.
|
||||
# appears in the filter text box for the properties view container.
|
||||
propertiesFilterPlaceholder=Filter properties
|
||||
|
||||
# LOCALIZATION NOTE (stringConversionFailed): Happens when a value cannot be
|
||||
# converted to a string for inspection.
|
||||
stringConversionFailed=Cannot convert value to string.
|
||||
|
||||
# LOCALIZATION NOTE (connectionTimeout): message displayed when the Remote Scratchpad
|
||||
# fails to connect to the server due to a timeout.
|
||||
connectionTimeout=Connection timeout. Check the Error Console on both ends for potential error messages. Reopen the Scratchpad to try again.
|
||||
|
@ -25,6 +25,9 @@ var ContextUI = {
|
||||
Elements.panelUI.addEventListener('ToolPanelShown', this, false);
|
||||
Elements.panelUI.addEventListener('ToolPanelHidden', this, false);
|
||||
|
||||
Elements.tray.addEventListener("mousemove", this, false);
|
||||
Elements.tray.addEventListener("mouseleave", this, false);
|
||||
|
||||
window.addEventListener("touchstart", this, true);
|
||||
window.addEventListener("mousedown", this, true);
|
||||
window.addEventListener("MozEdgeUIStarted", this, true);
|
||||
@ -163,6 +166,7 @@ var ContextUI = {
|
||||
dismissTabsWithDelay: function (aDelay) {
|
||||
aDelay = aDelay || kForegroundTabAnimationDelay;
|
||||
this._clearDelayedTimeout();
|
||||
this._lastTimeoutDelay = aDelay;
|
||||
this._hidingId = setTimeout(function () {
|
||||
ContextUI.dismissTabs();
|
||||
}, aDelay);
|
||||
@ -223,9 +227,16 @@ var ContextUI = {
|
||||
if (this._hidingId) {
|
||||
clearTimeout(this._hidingId);
|
||||
this._hidingId = 0;
|
||||
this._delayedHide = false;
|
||||
}
|
||||
},
|
||||
|
||||
_resetDelayedTimeout: function () {
|
||||
this._hidingId = setTimeout(function () {
|
||||
ContextUI.dismissTabs();
|
||||
}, this._lastTimeoutDelay);
|
||||
},
|
||||
|
||||
/*******************************************
|
||||
* Events
|
||||
*/
|
||||
@ -288,6 +299,20 @@ var ContextUI = {
|
||||
this.dismissContextAppbar();
|
||||
},
|
||||
|
||||
onMouseMove: function (aEvent) {
|
||||
if (this._hidingId) {
|
||||
this._clearDelayedTimeout();
|
||||
this._delayedHide = true;
|
||||
}
|
||||
},
|
||||
|
||||
onMouseLeave: function (aEvent) {
|
||||
if (this._delayedHide) {
|
||||
this._delayedHide = false;
|
||||
this._resetDelayedTimeout();
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent: function handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "URLChanged":
|
||||
@ -323,6 +348,12 @@ var ContextUI = {
|
||||
}
|
||||
this.onDownInput(aEvent);
|
||||
break;
|
||||
case "mousemove":
|
||||
this.onMouseMove(aEvent);
|
||||
break;
|
||||
case "mouseleave":
|
||||
this.onMouseLeave(aEvent);
|
||||
break;
|
||||
case "touchstart":
|
||||
this.onDownInput(aEvent);
|
||||
break;
|
||||
@ -334,11 +365,6 @@ var ContextUI = {
|
||||
case "AlertClose":
|
||||
ContentAreaObserver.updateContentArea();
|
||||
break;
|
||||
case "touchstart":
|
||||
if (!BrowserUI.isStartTabVisible) {
|
||||
this.dismiss();
|
||||
}
|
||||
break;
|
||||
case "MozFlyoutPanelShowing":
|
||||
if (BrowserUI.isStartTabVisible) {
|
||||
this.dismissTabs();
|
||||
|
@ -115,6 +115,21 @@ toolbarpaletteitem[mousedown] {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.panel-customization-placeholder,
|
||||
toolbarpaletteitem[place="palette"],
|
||||
toolbarpaletteitem[place="panel"] {
|
||||
transition: background-color, border-color, box-shadow, transform;
|
||||
transition-duration: 10ms, 10ms, 10ms, 250ms;
|
||||
transition-timing-function: linear, linear, linear, ease-in-out;
|
||||
}
|
||||
|
||||
toolbarpaletteitem[notransition][place="palette"],
|
||||
toolbarpaletteitem[notransition][place="panel"] {
|
||||
transition: background-color, border-color, box-shadow;
|
||||
transition-duration: 10ms, 10ms, 10ms;
|
||||
transition-timing-function: linear, linear, linear;
|
||||
}
|
||||
|
||||
toolbarpaletteitem > toolbarbutton > .toolbarbutton-icon {
|
||||
transition: transform 50ms ease-in-out;
|
||||
}
|
||||
|
@ -115,10 +115,19 @@
|
||||
|
||||
/* Tooltip widget (see browser/devtools/shared/widgets/Tooltip.js) */
|
||||
|
||||
.devtools-tooltip.devtools-tooltip-panel .panel-arrowcontent {
|
||||
.devtools-tooltip .panel-arrowcontent {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.devtools-tooltip .panel-arrowcontainer {
|
||||
/* Reseting the transition used when panels are shown */
|
||||
transition: none;
|
||||
/* Panels slide up/down/left/right when they appear using a transform.
|
||||
Since we want to remove the transition, we don't need to transform anymore
|
||||
plus it can interfeer by causing mouseleave events on the underlying nodes */
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.devtools-tooltip-simple-text {
|
||||
max-width: 400px;
|
||||
margin: 0 -4px; /* Compensate for the .panel-arrowcontent padding. */
|
||||
|
@ -247,6 +247,7 @@ public class Utils {
|
||||
while (name.endsWith("[]")) {
|
||||
sb.append('[');
|
||||
name = name.substring(0, len - 2);
|
||||
len = len - 2;
|
||||
}
|
||||
|
||||
// Look in the hashmap for the remainder...
|
||||
|
@ -84,3 +84,12 @@ include $(topsrcdir)/config/rules.mk
|
||||
tools:: $(ANDROID_APK_NAME).apk
|
||||
|
||||
GENERATED_DIRS += $(dir-tests)
|
||||
|
||||
# The test APK needs to know the contents of the target APK while not
|
||||
# being linked against them. This is a best effort to avoid getting
|
||||
# out of sync with base's build config.
|
||||
JARS_DIR := $(DEPTH)/mobile/android/base
|
||||
JAVA_BOOTCLASSPATH := $(JAVA_BOOTCLASSPATH):$(subst $(NULL) ,:,$(wildcard $(JARS_DIR)/*.jar))
|
||||
# We also want to re-compile classes.dex when the associated base
|
||||
# content changes.
|
||||
classes.dex: $(wildcard $(JARS_DIR)/*.jar)
|
||||
|
@ -13,6 +13,9 @@
|
||||
#include "unistd.h"
|
||||
#include "dirent.h"
|
||||
#include "sys/stat.h"
|
||||
#if !defined(ANDROID)
|
||||
#include <spawn.h>
|
||||
#endif // !defined(ANDROID)
|
||||
#endif // defined(XP_UNIX)
|
||||
|
||||
#if defined(XP_LINUX)
|
||||
@ -504,6 +507,11 @@ static const dom::ConstantSpec gLibcProperties[] =
|
||||
// The size of |time_t|.
|
||||
{ "OSFILE_SIZEOF_TIME_T", INT_TO_JSVAL(sizeof (time_t)) },
|
||||
|
||||
#if !defined(ANDROID)
|
||||
// The size of |posix_spawn_file_actions_t|.
|
||||
{ "OSFILE_SIZEOF_POSIX_SPAWN_FILE_ACTIONS_T", INT_TO_JSVAL(sizeof (posix_spawn_file_actions_t)) },
|
||||
#endif // !defined(ANDROID)
|
||||
|
||||
// Defining |dirent|.
|
||||
// Size
|
||||
{ "OSFILE_SIZEOF_DIRENT", INT_TO_JSVAL(sizeof (dirent)) },
|
||||
|
@ -1,5 +1,7 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
browser_frame_elements.html
|
||||
browser_geolocation_privatebrowsing_page.html
|
||||
network_geolocation.sjs
|
||||
page_privatestorageevent.html
|
||||
test-console-api.html
|
||||
@ -12,9 +14,7 @@ support-files =
|
||||
[browser_bug396843.js]
|
||||
[browser_focus_steal_from_chrome.js]
|
||||
[browser_focus_steal_from_chrome_during_mousedown.js]
|
||||
[browser_frame_elements.html]
|
||||
[browser_frame_elements.js]
|
||||
[browser_geolocation_privatebrowsing_page.html]
|
||||
[browser_geolocation_privatebrowsing_perwindowpb.js]
|
||||
[browser_localStorage_privatestorageevent.js]
|
||||
[browser_xhr_sandbox.js]
|
||||
|
@ -121,6 +121,14 @@ APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor,
|
||||
|
||||
apzc = container->GetAsyncPanZoomController();
|
||||
|
||||
// If the content represented by the container layer has changed (which may
|
||||
// be possible because of DLBI heuristics) then we don't want to keep using
|
||||
// the same old APZC for the new content. Null it out so we run through the
|
||||
// code to find another one or create one.
|
||||
if (apzc && !apzc->Matches(ScrollableLayerGuid(aLayersId, container->GetFrameMetrics()))) {
|
||||
apzc = nullptr;
|
||||
}
|
||||
|
||||
// If the container doesn't have an APZC already, try to find one of our
|
||||
// pre-existing ones that matches. In particular, if we find an APZC whose
|
||||
// ScrollableLayerGuid is the same, then we know what happened is that the
|
||||
@ -159,7 +167,7 @@ APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor,
|
||||
APZC_LOG("Using APZC %p for layer %p with identifiers %lld %lld\n", apzc, aLayer, aLayersId, container->GetFrameMetrics().mScrollId);
|
||||
|
||||
apzc->NotifyLayersUpdated(container->GetFrameMetrics(),
|
||||
aIsFirstPaint && (aLayersId == aFirstPaintLayersId));
|
||||
aIsFirstPaint && (aLayersId == aFirstPaintLayersId));
|
||||
|
||||
ScreenRect visible(container->GetFrameMetrics().mCompositionBounds);
|
||||
apzc->SetLayerHitTestData(visible, aTransform, aLayer->GetTransform());
|
||||
|
@ -493,8 +493,14 @@ abstract public class BrowserApp extends GeckoApp
|
||||
mBrowserToolbar.setOnStopEditingListener(new BrowserToolbar.OnStopEditingListener() {
|
||||
public void onStopEditing() {
|
||||
selectTargetTabForEditingMode();
|
||||
hideHomePager();
|
||||
|
||||
// Since the underlying LayerView is set visible in hideHomePager, we would
|
||||
// ordinarily want to call it first. However, hideBrowserSearch changes the
|
||||
// visibility of the HomePager and hideHomePager will take no action if the
|
||||
// HomePager is hidden, so we want to call hideBrowserSearch to restore the
|
||||
// HomePager visibility first.
|
||||
hideBrowserSearch();
|
||||
hideHomePager();
|
||||
|
||||
// Re-enable doorhanger notifications. They may trigger on the selected tab above.
|
||||
mDoorHangerPopup.enable();
|
||||
@ -920,6 +926,9 @@ abstract public class BrowserApp extends GeckoApp
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
public void run() {
|
||||
toolbarLayout.scrollTo(0, toolbarLayout.getHeight() - marginTop);
|
||||
if (mDoorHangerPopup.isShowing()) {
|
||||
mDoorHangerPopup.updatePopup();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -1454,6 +1463,19 @@ abstract public class BrowserApp extends GeckoApp
|
||||
|
||||
final String url = mBrowserToolbar.commitEdit();
|
||||
|
||||
// HACK: We don't know the url that will be loaded when hideHomePager is initially called
|
||||
// in BrowserToolbar's onStopEditing listener so on the awesomescreen, hideHomePager will
|
||||
// use the url "about:home" and return without taking any action. hideBrowserSearch is
|
||||
// then called, but since hideHomePager changes both HomePager and LayerView visibility
|
||||
// and exited without taking an action, no Views are displayed and graphical corruption is
|
||||
// visible instead.
|
||||
//
|
||||
// Here we call hideHomePager for the second time with the URL to be loaded so that
|
||||
// hideHomePager is called with the correct state for the upcoming page load.
|
||||
//
|
||||
// Expected to be fixed by bug 915825.
|
||||
hideHomePager(url);
|
||||
|
||||
// Don't do anything if the user entered an empty URL.
|
||||
if (TextUtils.isEmpty(url)) {
|
||||
return;
|
||||
@ -1529,12 +1551,6 @@ abstract public class BrowserApp extends GeckoApp
|
||||
return false;
|
||||
}
|
||||
|
||||
// cancelEdit will call hideHomePager. If we're on web content, this is fine. If we're on
|
||||
// about:home, the HomePager needs to be visible in the end (note that hideHomePager will
|
||||
// not hide the HomePager on about:home). However, filterEditingMode may have hidden the
|
||||
// HomePager so we set it visible here.
|
||||
mHomePager.setVisibility(View.VISIBLE);
|
||||
|
||||
mBrowserToolbar.cancelEdit();
|
||||
|
||||
return true;
|
||||
@ -1542,11 +1558,9 @@ abstract public class BrowserApp extends GeckoApp
|
||||
|
||||
void filterEditingMode(String searchTerm, AutocompleteHandler handler) {
|
||||
if (TextUtils.isEmpty(searchTerm)) {
|
||||
mHomePager.setVisibility(View.VISIBLE);
|
||||
hideBrowserSearch();
|
||||
} else {
|
||||
showBrowserSearch();
|
||||
mHomePager.setVisibility(View.INVISIBLE);
|
||||
mBrowserSearch.filter(searchTerm, handler);
|
||||
}
|
||||
}
|
||||
@ -1644,13 +1658,23 @@ abstract public class BrowserApp extends GeckoApp
|
||||
mLayerView.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the HomePager, using the url of the currently selected tab as the url to be
|
||||
* loaded.
|
||||
*/
|
||||
private void hideHomePager() {
|
||||
if (!isHomePagerVisible()) {
|
||||
return;
|
||||
}
|
||||
final Tab selectedTab = Tabs.getInstance().getSelectedTab();
|
||||
final String url = (selectedTab != null) ? selectedTab.getURL() : null;
|
||||
|
||||
final Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
if (tab != null && isAboutHome(tab)) {
|
||||
hideHomePager(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the HomePager. The given url should be the url of the page to be loaded, or null
|
||||
* if a new page is not being loaded.
|
||||
*/
|
||||
private void hideHomePager(final String url) {
|
||||
if (!isHomePagerVisible() || TextUtils.equals(url, ABOUT_HOME)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1677,6 +1701,9 @@ abstract public class BrowserApp extends GeckoApp
|
||||
|
||||
mBrowserSearchContainer.setVisibility(View.VISIBLE);
|
||||
|
||||
// Prevent overdraw by hiding the underlying HomePager View.
|
||||
mHomePager.setVisibility(View.INVISIBLE);
|
||||
|
||||
final FragmentManager fm = getSupportFragmentManager();
|
||||
|
||||
// In certain situations, showBrowserSearch() can be called immediately after hideBrowserSearch()
|
||||
@ -1695,6 +1722,10 @@ abstract public class BrowserApp extends GeckoApp
|
||||
return;
|
||||
}
|
||||
|
||||
// To prevent overdraw, the HomePager is hidden when BrowserSearch is displayed:
|
||||
// reverse that.
|
||||
mHomePager.setVisibility(View.VISIBLE);
|
||||
|
||||
mBrowserSearchContainer.setVisibility(View.INVISIBLE);
|
||||
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
|
@ -268,7 +268,7 @@ public class DoorHangerPopup extends ArrowPopup
|
||||
*
|
||||
* This method must be called on the UI thread.
|
||||
*/
|
||||
private void updatePopup() {
|
||||
void updatePopup() {
|
||||
// Bail if the selected tab is null, if there are no active doorhangers,
|
||||
// if we haven't inflated the layout yet (this can happen if updatePopup()
|
||||
// is called before the runnable from addDoorHanger() runs), or if the
|
||||
@ -299,7 +299,7 @@ public class DoorHangerPopup extends ArrowPopup
|
||||
|
||||
showDividers();
|
||||
if (isShowing()) {
|
||||
update();
|
||||
show();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -466,11 +466,13 @@ public class GeckoMenu extends ListView
|
||||
public static class DefaultActionItemBar extends LinearLayout
|
||||
implements ActionItemBarPresenter {
|
||||
public DefaultActionItemBar(Context context) {
|
||||
super(context);
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public DefaultActionItemBar(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
setWeightSum(3.0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -17,12 +17,17 @@ import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MenuItemActionView extends LinearLayout
|
||||
implements GeckoMenuItem.Layout {
|
||||
private static final String LOGTAG = "GeckoMenuItemActionView";
|
||||
|
||||
private MenuItemDefault mMenuItem;
|
||||
private ImageButton mActionButton;
|
||||
private ImageButton mMenuButton;
|
||||
private List<ImageButton> mActionButtons;
|
||||
private View.OnClickListener mActionButtonListener;
|
||||
|
||||
public MenuItemActionView(Context context) {
|
||||
this(context, null);
|
||||
@ -44,7 +49,8 @@ public class MenuItemActionView extends LinearLayout
|
||||
|
||||
LayoutInflater.from(context).inflate(R.layout.menu_item_action_view, this);
|
||||
mMenuItem = (MenuItemDefault) findViewById(R.id.menu_item);
|
||||
mActionButton = (ImageButton) findViewById(R.id.action_button);
|
||||
mMenuButton = (ImageButton) findViewById(R.id.menu_item_button);
|
||||
mActionButtons = new ArrayList<ImageButton>();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -59,10 +65,12 @@ public class MenuItemActionView extends LinearLayout
|
||||
|
||||
private void setIcon(Drawable icon) {
|
||||
mMenuItem.setIcon(icon);
|
||||
mMenuButton.setImageDrawable(icon);
|
||||
}
|
||||
|
||||
private void setIcon(int icon) {
|
||||
mMenuItem.setIcon(icon);
|
||||
mMenuButton.setImageResource(icon);
|
||||
}
|
||||
|
||||
private void setTitle(CharSequence title) {
|
||||
@ -73,27 +81,53 @@ public class MenuItemActionView extends LinearLayout
|
||||
public void setEnabled(boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
mMenuItem.setEnabled(enabled);
|
||||
mMenuButton.setEnabled(enabled);
|
||||
mMenuButton.setAlpha(enabled ? 255 : 99);
|
||||
|
||||
if (mActionButton != null) {
|
||||
mActionButton.setEnabled(enabled);
|
||||
mActionButton.setAlpha(enabled ? 255 : 99);
|
||||
for (ImageButton button : mActionButtons) {
|
||||
button.setEnabled(enabled);
|
||||
button.setAlpha(enabled ? 255 : 99);
|
||||
}
|
||||
}
|
||||
|
||||
public void setMenuItemClickListener(View.OnClickListener listener) {
|
||||
mMenuItem.setOnClickListener(listener);
|
||||
mMenuButton.setOnClickListener(listener);
|
||||
}
|
||||
|
||||
public void setActionButtonClickListener(View.OnClickListener listener) {
|
||||
mActionButton.setOnClickListener(listener);
|
||||
mActionButtonListener = listener;
|
||||
|
||||
for (ImageButton button : mActionButtons) {
|
||||
button.setOnClickListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
public void setActionButton(Drawable drawable) {
|
||||
if (drawable != null) {
|
||||
mActionButton.setImageDrawable(drawable);
|
||||
mActionButton.setVisibility(View.VISIBLE);
|
||||
public void addActionButton(Drawable drawable) {
|
||||
// If this is the first icon, retain the text.
|
||||
// If not, make the menu item an icon.
|
||||
final int count = mActionButtons.size();
|
||||
if (count == 0) {
|
||||
mMenuItem.setVisibility(View.VISIBLE);
|
||||
mMenuButton.setVisibility(View.GONE);
|
||||
} else {
|
||||
mActionButton.setVisibility(View.GONE);
|
||||
mMenuItem.setVisibility(View.GONE);
|
||||
mMenuButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
if (drawable != null) {
|
||||
ImageButton button = new ImageButton(getContext(), null, R.attr.menuItemShareActionButtonStyle);
|
||||
button.setImageDrawable(drawable);
|
||||
button.setOnClickListener(mActionButtonListener);
|
||||
button.setTag(count);
|
||||
|
||||
final int height = (int) (getResources().getDimension(R.dimen.menu_item_row_height));
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, height);
|
||||
params.weight = 1.0f;
|
||||
button.setLayoutParams(params);
|
||||
|
||||
mActionButtons.add(button);
|
||||
addView(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,16 +9,16 @@
|
||||
android:id="@+id/menu_item"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="1.0"
|
||||
android:layout_weight="2.0"
|
||||
android:background="@drawable/action_bar_button"
|
||||
android:clickable="true"
|
||||
android:focusable="true"/>
|
||||
|
||||
<ImageButton android:id="@+id/action_button"
|
||||
android:layout_width="@dimen/menu_item_action_icon"
|
||||
<ImageButton android:id="@+id/menu_item_button"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="@dimen/menu_item_row_height"
|
||||
android:paddingTop="8dip"
|
||||
android:paddingBottom="8dip"
|
||||
android:layout_weight="1.0"
|
||||
android:padding="10dip"
|
||||
android:scaleType="centerInside"
|
||||
android:background="@drawable/action_bar_button"
|
||||
android:layout_gravity="center_vertical"
|
||||
|
@ -19,6 +19,10 @@
|
||||
android:icon="@drawable/ic_menu_bookmark_add"
|
||||
android:title="@string/bookmark"/>
|
||||
|
||||
<item android:id="@+id/share"
|
||||
android:icon="@drawable/ic_menu_share"
|
||||
android:title="@string/share" />
|
||||
|
||||
<item android:id="@+id/new_tab"
|
||||
android:icon="@drawable/ic_menu_new_tab"
|
||||
android:title="@string/new_tab"/>
|
||||
@ -27,10 +31,6 @@
|
||||
android:icon="@drawable/ic_menu_new_private_tab"
|
||||
android:title="@string/new_private_tab"/>
|
||||
|
||||
<item android:id="@+id/share"
|
||||
android:icon="@drawable/ic_menu_share"
|
||||
android:title="@string/share" />
|
||||
|
||||
<item android:id="@+id/find_in_page"
|
||||
android:icon="@drawable/ic_menu_find_in_page"
|
||||
android:title="@string/find_in_page" />
|
||||
|
@ -20,6 +20,10 @@
|
||||
android:title="@string/bookmark"
|
||||
android:showAsAction="always"/>
|
||||
|
||||
<item android:id="@+id/share"
|
||||
android:icon="@drawable/ic_menu_share"
|
||||
android:title="@string/share" />
|
||||
|
||||
<item android:id="@+id/new_tab"
|
||||
android:icon="@drawable/ic_menu_new_tab"
|
||||
android:title="@string/new_tab"/>
|
||||
@ -28,10 +32,6 @@
|
||||
android:icon="@drawable/ic_menu_new_private_tab"
|
||||
android:title="@string/new_private_tab"/>
|
||||
|
||||
<item android:id="@+id/share"
|
||||
android:icon="@drawable/ic_menu_share"
|
||||
android:title="@string/share" />
|
||||
|
||||
<item android:id="@+id/find_in_page"
|
||||
android:icon="@drawable/ic_menu_find_in_page"
|
||||
android:title="@string/find_in_page" />
|
||||
|
@ -20,6 +20,10 @@
|
||||
android:title="@string/bookmark"
|
||||
android:showAsAction="always"/>
|
||||
|
||||
<item android:id="@+id/share"
|
||||
android:icon="@drawable/ic_menu_share"
|
||||
android:title="@string/share" />
|
||||
|
||||
<item android:id="@+id/new_tab"
|
||||
android:icon="@drawable/ic_menu_new_tab"
|
||||
android:title="@string/new_tab"/>
|
||||
@ -28,10 +32,6 @@
|
||||
android:icon="@drawable/ic_menu_new_private_tab"
|
||||
android:title="@string/new_private_tab"/>
|
||||
|
||||
<item android:id="@+id/share"
|
||||
android:icon="@drawable/ic_menu_share"
|
||||
android:title="@string/share" />
|
||||
|
||||
<item android:id="@+id/find_in_page"
|
||||
android:icon="@drawable/ic_menu_find_in_page"
|
||||
android:title="@string/find_in_page" />
|
||||
|
@ -42,6 +42,7 @@
|
||||
<item name="menuItemActionBarStyle">@style/Widget.MenuItemActionBar</item>
|
||||
<item name="menuItemActionViewStyle">@style/Widget.MenuItemActionView</item>
|
||||
<item name="menuItemDefaultStyle">@style/Widget.MenuItemDefault</item>
|
||||
<item name="menuItemShareActionButtonStyle">@style/Widget.MenuItemShareActionButton</item>
|
||||
<item name="bookmarksListViewStyle">@style/Widget.BookmarksListView</item>
|
||||
<item name="topSitesGridItemViewStyle">@style/Widget.TopSitesGridItemView</item>
|
||||
<item name="topSitesGridViewStyle">@style/Widget.TopSitesGridView</item>
|
||||
|
@ -17,6 +17,9 @@
|
||||
<!-- Style for MenuItemDefault -->
|
||||
<attr name="menuItemDefaultStyle" format="reference"/>
|
||||
|
||||
<!-- Style for MenuItemActionView's ShareActionButton -->
|
||||
<attr name="menuItemShareActionButtonStyle" format="reference"/>
|
||||
|
||||
<!-- Default style for the BookmarksListView -->
|
||||
<attr name="bookmarksListViewStyle" format="reference" />
|
||||
|
||||
|
@ -45,7 +45,6 @@
|
||||
<dimen name="doorhanger_textsize_small">8sp</dimen>
|
||||
|
||||
<dimen name="flow_layout_spacing">6dp</dimen>
|
||||
<dimen name="menu_item_action_icon">80dp</dimen>
|
||||
<dimen name="menu_item_icon">21dp</dimen>
|
||||
<dimen name="menu_item_state_icon">18dp</dimen>
|
||||
<dimen name="menu_item_row_height">44dp</dimen>
|
||||
|
@ -83,7 +83,14 @@
|
||||
<style name="Widget.MenuItemActionView">
|
||||
<item name="android:divider">@drawable/divider_vertical</item>
|
||||
<item name="android:showDividers">middle</item>
|
||||
<item name="android:dividerPadding">8dip</item>
|
||||
<item name="android:dividerPadding">12dip</item>
|
||||
<item name="android:gravity">left</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.MenuItemShareActionButton">
|
||||
<item name="android:padding">10dip</item>
|
||||
<item name="android:background">@drawable/action_bar_button</item>
|
||||
<item name="android:scaleType">centerInside</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.MenuItemDefault">
|
||||
|
@ -17,7 +17,6 @@ import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
|
@ -2,7 +2,9 @@ package org.mozilla.gecko.tests;
|
||||
|
||||
import com.jayway.android.robotium.solo.Condition;
|
||||
import com.jayway.android.robotium.solo.Solo;
|
||||
|
||||
import org.mozilla.gecko.*;
|
||||
import org.mozilla.gecko.GeckoThread.LaunchState;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Instrumentation;
|
||||
@ -37,7 +39,6 @@ import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
@ -79,13 +80,7 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
|
||||
protected void blockForGeckoReady() {
|
||||
try {
|
||||
Actions.EventExpecter geckoReadyExpector = mActions.expectGeckoEvent("Gecko:Ready");
|
||||
ClassLoader classLoader = getActivity().getClassLoader();
|
||||
Class appsCls = classLoader.loadClass("org.mozilla.gecko.GeckoThread");
|
||||
Class launchStateCls = classLoader.loadClass("org.mozilla.gecko.GeckoThread$LaunchState");
|
||||
Method checkLaunchState = appsCls.getMethod("checkLaunchState", launchStateCls);
|
||||
Object states[] = launchStateCls.getEnumConstants();
|
||||
Boolean ret = (Boolean)checkLaunchState.invoke(null, states[3]);
|
||||
if (!ret.booleanValue()) {
|
||||
if (!GeckoThread.checkLaunchState(LaunchState.GeckoRunning)) {
|
||||
geckoReadyExpector.blockForEvent();
|
||||
}
|
||||
geckoReadyExpector.unregisterListener();
|
||||
@ -256,12 +251,7 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
|
||||
*/
|
||||
protected final void loadUrl(final String url) {
|
||||
try {
|
||||
ClassLoader classLoader = getActivity().getClassLoader();
|
||||
Class tabsClass = classLoader.loadClass("org.mozilla.gecko.Tabs");
|
||||
Method getInstance = tabsClass.getMethod("getInstance");
|
||||
Method loadUrl = tabsClass.getMethod("loadUrl", String.class);
|
||||
Object tabs = getInstance.invoke(null);
|
||||
loadUrl.invoke(tabs, new Object[] { url });
|
||||
Tabs.getInstance().loadUrl(url);
|
||||
} catch (Exception e) {
|
||||
mAsserter.dumpLog("Exception in loadUrl", e);
|
||||
throw new RuntimeException(e);
|
||||
@ -728,11 +718,7 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
|
||||
// Determine device type
|
||||
type = "phone";
|
||||
try {
|
||||
ClassLoader classLoader = getActivity().getClassLoader();
|
||||
Class appsCls = classLoader.loadClass("org.mozilla.gecko.GeckoAppShell");
|
||||
Method isTabletMethod = appsCls.getMethod("isTablet", (Class[]) null);
|
||||
boolean isTablet = (Boolean)isTabletMethod.invoke(null);
|
||||
if (isTablet) {
|
||||
if (GeckoAppShell.isTablet()) {
|
||||
type = "tablet";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
@ -1,10 +1,11 @@
|
||||
package org.mozilla.gecko.tests;
|
||||
|
||||
import org.mozilla.gecko.*;
|
||||
import org.mozilla.gecko.util.Clipboard;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* This class covers interactions with the context menu opened from web content
|
||||
@ -53,18 +54,9 @@ abstract class ContentContextMenuTest extends PixelTest {
|
||||
boolean correctText = waitForTest(new BooleanTest() {
|
||||
@Override
|
||||
public boolean test() {
|
||||
try {
|
||||
ContentResolver resolver = getActivity().getContentResolver();
|
||||
ClassLoader classLoader = getActivity().getClassLoader();
|
||||
Class Clipboard = classLoader.loadClass("org.mozilla.gecko.util.Clipboard");
|
||||
Method getText = Clipboard.getMethod("getText");
|
||||
String clipboardText = (String)getText.invoke(null);
|
||||
mAsserter.dumpLog("Clipboard text = " + clipboardText + " , expected text = " + copiedText);
|
||||
return clipboardText.contains(copiedText);
|
||||
} catch (Exception e) {
|
||||
mAsserter.ok(false, "Exception getting the clipboard text ", e.toString()); // Fail before returning
|
||||
return false;
|
||||
}
|
||||
final String clipboardText = Clipboard.getText();
|
||||
mAsserter.dumpLog("Clipboard text = " + clipboardText + " , expected text = " + copiedText);
|
||||
return clipboardText.contains(copiedText);
|
||||
}
|
||||
}, MAX_TEST_TIMEOUT);
|
||||
mAsserter.ok(correctText, "Checking if the text is correctly copied", "The text was correctly copied");
|
||||
|
@ -231,6 +231,7 @@ abstract class ContentProviderTest extends BaseTest {
|
||||
throw new Exception("You should call setUp(providerClassName, authorityUriField) instead");
|
||||
}
|
||||
|
||||
// TODO: Take the actual class as an arg.
|
||||
public void setUp(String providerClassName, String authorityUriField) throws Exception {
|
||||
super.setUp();
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
package org.mozilla.gecko.tests;
|
||||
|
||||
import org.mozilla.gecko.*;
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
|
||||
class DatabaseHelper {
|
||||
@ -23,16 +23,8 @@ class DatabaseHelper {
|
||||
* This method can be used to check if an URL is present in the bookmarks database
|
||||
*/
|
||||
protected boolean isBookmark(String url) {
|
||||
try {
|
||||
ContentResolver resolver = mActivity.getContentResolver();
|
||||
ClassLoader classLoader = mActivity.getClassLoader();
|
||||
Class browserDB = classLoader.loadClass("org.mozilla.gecko.db.BrowserDB");
|
||||
Method isBookmarked = browserDB.getMethod("isBookmark", ContentResolver.class, String.class);
|
||||
return (Boolean)isBookmarked.invoke(null, resolver, url);
|
||||
} catch (Exception e) {
|
||||
mAsserter.ok(false, "Exception while checking if url is bookmarked", e.toString());
|
||||
return false;
|
||||
}
|
||||
final ContentResolver resolver = mActivity.getContentResolver();
|
||||
return BrowserDB.isBookmark(resolver, url);
|
||||
}
|
||||
|
||||
protected Uri buildUri(BrowserDataType dataType) {
|
||||
@ -53,16 +45,9 @@ class DatabaseHelper {
|
||||
* The LocalBrowserDB.addBookmark implementation handles updating existing bookmarks.
|
||||
*/
|
||||
protected void addOrUpdateMobileBookmark(String title, String url) {
|
||||
try {
|
||||
ContentResolver resolver = mActivity.getContentResolver();
|
||||
ClassLoader classLoader = mActivity.getClassLoader();
|
||||
Class browserDB = classLoader.loadClass("org.mozilla.gecko.db.BrowserDB");
|
||||
Method addBookmark = browserDB.getMethod("addBookmark", ContentResolver.class, String.class, String.class);
|
||||
addBookmark.invoke(null, resolver, title, url);
|
||||
mAsserter.ok(true, "Inserting/updating a new bookmark", "Inserting/updating the bookmark with the title = " + title + " and the url = " + url);
|
||||
} catch (Exception e) {
|
||||
mAsserter.ok(false, "Exception adding bookmark: ", e.toString());
|
||||
}
|
||||
final ContentResolver resolver = mActivity.getContentResolver();
|
||||
BrowserDB.addBookmark(resolver, title, url);
|
||||
mAsserter.ok(true, "Inserting/updating a new bookmark", "Inserting/updating the bookmark with the title = " + title + " and the url = " + url);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,58 +56,35 @@ class DatabaseHelper {
|
||||
* Warning: This method assumes that there's only one bookmark with the given URL.
|
||||
*/
|
||||
protected void updateBookmark(String url, String title, String keyword) {
|
||||
final ContentResolver resolver = mActivity.getContentResolver();
|
||||
// Get the id for the bookmark with the given URL.
|
||||
Cursor c = null;
|
||||
try {
|
||||
ContentResolver resolver = mActivity.getContentResolver();
|
||||
ClassLoader classLoader = mActivity.getClassLoader();
|
||||
Class browserDB = classLoader.loadClass("org.mozilla.gecko.db.BrowserDB");
|
||||
Method getBookmarkForUrl = browserDB.getMethod("getBookmarkForUrl", ContentResolver.class, String.class);
|
||||
|
||||
// Get the id for the bookmark with the given URL.
|
||||
Cursor c = null;
|
||||
try {
|
||||
c = (Cursor) getBookmarkForUrl.invoke(null, resolver, url);
|
||||
if (!c.moveToFirst()) {
|
||||
mAsserter.ok(false, "Getting bookmark with url", "Couldn't find bookmark with url = " + url);
|
||||
return;
|
||||
}
|
||||
|
||||
int id = c.getInt(c.getColumnIndexOrThrow("_id"));
|
||||
Method updateBookmark = browserDB.getMethod("updateBookmark", ContentResolver.class, int.class, String.class, String.class, String.class);
|
||||
updateBookmark.invoke(null, resolver, id, url, title, keyword);
|
||||
|
||||
mAsserter.ok(true, "Updating bookmark", "Updating bookmark with url = " + url);
|
||||
} finally {
|
||||
if (c != null) {
|
||||
c.close();
|
||||
}
|
||||
c = BrowserDB.getBookmarkForUrl(resolver, url);
|
||||
if (!c.moveToFirst()) {
|
||||
mAsserter.ok(false, "Getting bookmark with url", "Couldn't find bookmark with url = " + url);
|
||||
return;
|
||||
}
|
||||
|
||||
int id = c.getInt(c.getColumnIndexOrThrow("_id"));
|
||||
BrowserDB.updateBookmark(resolver, id, url, title, keyword);
|
||||
|
||||
mAsserter.ok(true, "Updating bookmark", "Updating bookmark with url = " + url);
|
||||
} finally {
|
||||
if (c != null) {
|
||||
c.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
mAsserter.ok(false, "Exception updating bookmark: ", e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
protected void deleteBookmark(String url) {
|
||||
try {
|
||||
ContentResolver resolver = mActivity.getContentResolver();
|
||||
ClassLoader classLoader = mActivity.getClassLoader();
|
||||
Class browserDB = classLoader.loadClass("org.mozilla.gecko.db.BrowserDB");
|
||||
Method removeBookmark = browserDB.getMethod("removeBookmarksWithURL", ContentResolver.class, String.class);
|
||||
removeBookmark.invoke(null, resolver, url);
|
||||
} catch (Exception e) {
|
||||
mAsserter.ok(false, "Exception deleting bookmark", e.toString());
|
||||
}
|
||||
final ContentResolver resolver = mActivity.getContentResolver();
|
||||
BrowserDB.removeBookmarksWithURL(resolver, url);
|
||||
}
|
||||
|
||||
protected void deleteHistoryItem(String url) {
|
||||
try {
|
||||
ContentResolver resolver = mActivity.getContentResolver();
|
||||
ClassLoader classLoader = mActivity.getClassLoader();
|
||||
Class browserDB = classLoader.loadClass("org.mozilla.gecko.db.BrowserDB");
|
||||
Method removeHistory = browserDB.getMethod("removeHistoryEntry", ContentResolver.class, String.class);
|
||||
removeHistory.invoke(null, resolver, url);
|
||||
} catch (Exception e) {
|
||||
mAsserter.ok(false, "Exception deleting history item", e.toString());
|
||||
}
|
||||
final ContentResolver resolver = mActivity.getContentResolver();
|
||||
BrowserDB.removeHistoryEntry(resolver, url);
|
||||
}
|
||||
|
||||
// About the same implementation as getFolderIdFromGuid from LocalBrowserDB because it is declared private and we can't use reflections to access it
|
||||
@ -158,25 +120,12 @@ class DatabaseHelper {
|
||||
protected ArrayList<String> getBrowserDBUrls(BrowserDataType dataType) {
|
||||
ArrayList<String> browserData = new ArrayList<String>();
|
||||
ContentResolver resolver = mActivity.getContentResolver();
|
||||
ClassLoader classLoader = mActivity.getClassLoader();
|
||||
Cursor cursor = null;
|
||||
Uri uri = buildUri(dataType);
|
||||
if (dataType == BrowserDataType.HISTORY) {
|
||||
try {
|
||||
Class browserDBClass = classLoader.loadClass("org.mozilla.gecko.db.BrowserDB");
|
||||
Method getAllVisitedHistory = browserDBClass.getMethod("getAllVisitedHistory", ContentResolver.class);
|
||||
cursor = (Cursor)getAllVisitedHistory.invoke(null, resolver);
|
||||
} catch (Exception e) {
|
||||
mAsserter.ok(false, "Exception while getting history", e.toString());
|
||||
}
|
||||
cursor = BrowserDB.getAllVisitedHistory(resolver);
|
||||
} else if (dataType == BrowserDataType.BOOKMARKS) {
|
||||
try {
|
||||
Class browserDBClass = classLoader.loadClass("org.mozilla.gecko.db.BrowserDB");
|
||||
Method getBookmarks = browserDBClass.getMethod("getBookmarksInFolder", ContentResolver.class, Long.TYPE);
|
||||
cursor = (Cursor)getBookmarks.invoke(null, resolver, getFolderIdFromGuid("mobile"));
|
||||
} catch (Exception e) {
|
||||
mAsserter.ok(false, "Exception while getting bookmarks", e.toString());
|
||||
}
|
||||
cursor = BrowserDB.getBookmarksInFolder(resolver, getFolderIdFromGuid("mobile"));
|
||||
}
|
||||
if (cursor != null) {
|
||||
cursor.moveToFirst();
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.mozilla.gecko.tests;
|
||||
|
||||
import org.mozilla.gecko.*;
|
||||
import org.mozilla.gecko.sync.Utils;
|
||||
|
||||
import com.jayway.android.robotium.solo.Condition;
|
||||
|
||||
@ -14,7 +15,6 @@ import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
@ -140,15 +140,7 @@ public class testBookmarkFolders extends AboutHomeTest {
|
||||
Long desktopFolderId = mDatabaseHelper.getFolderIdFromGuid("toolbar");
|
||||
|
||||
// Generate a Guid for the bookmark
|
||||
String generatedGuid = null;
|
||||
try {
|
||||
ClassLoader classLoader = getActivity().getClassLoader();
|
||||
Class syncUtilityClass = classLoader.loadClass("org.mozilla.gecko.sync.Utils");
|
||||
Method generateGuid = syncUtilityClass.getMethod("generateGuid", (Class[]) null);
|
||||
generatedGuid = (String)generateGuid.invoke(null);
|
||||
} catch (Exception e) {
|
||||
mAsserter.dumpLog("Exception in setUpDesktopBookmarks" + e);
|
||||
}
|
||||
final String generatedGuid = Utils.generateGuid();
|
||||
mAsserter.ok((generatedGuid != null), "Generating a random Guid for the bookmark", "We could not generate a Guid for the bookmark");
|
||||
|
||||
// Insert the bookmark
|
||||
|
@ -5,10 +5,11 @@ import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.SystemClock;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.UUID;
|
||||
import java.util.Random;
|
||||
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
|
||||
/*
|
||||
* This test is meant to exercise the performance of Fennec's
|
||||
* history and bookmarks content provider.
|
||||
@ -25,7 +26,6 @@ public class testBrowserProviderPerf extends ContentProviderTest {
|
||||
// multiple constraint words
|
||||
private final String KNOWN_PREFIX = "my mozilla test ";
|
||||
|
||||
private Method mFilterMethod;
|
||||
private Random mGenerator;
|
||||
|
||||
private final String MOBILE_FOLDER_GUID = "mobile";
|
||||
@ -61,22 +61,6 @@ public class testBrowserProviderPerf extends ContentProviderTest {
|
||||
return TEST_TALOS;
|
||||
}
|
||||
|
||||
private void loadFilterMethod() throws Exception {
|
||||
Class browserDBClass = mClassLoader.loadClass("org.mozilla.gecko.db.BrowserDB");
|
||||
|
||||
mFilterMethod =
|
||||
browserDBClass.getDeclaredMethod("filter", ContentResolver.class,
|
||||
CharSequence.class, int.class);
|
||||
}
|
||||
|
||||
private void initializeBrowserProvider() throws Exception {
|
||||
Class browserDBClass = mClassLoader.loadClass("org.mozilla.gecko.db.BrowserDB");
|
||||
|
||||
Method initializeMethod =
|
||||
browserDBClass.getDeclaredMethod("initialize", String.class);
|
||||
initializeMethod.invoke(null, "default");
|
||||
}
|
||||
|
||||
private void loadContractInfo() throws Exception {
|
||||
mBookmarksUri = getContentUri("Bookmarks");
|
||||
mHistoryUri = getContentUri("History");
|
||||
@ -254,18 +238,17 @@ public class testBrowserProviderPerf extends ContentProviderTest {
|
||||
mGenerator = new Random(19580427);
|
||||
|
||||
loadContractInfo();
|
||||
loadFilterMethod();
|
||||
}
|
||||
|
||||
public void testBrowserProviderPerf() throws Exception {
|
||||
initializeBrowserProvider();
|
||||
BrowserDB.initialize("default");
|
||||
|
||||
loadMobileFolderId();
|
||||
addTonsOfUrls();
|
||||
|
||||
long start = SystemClock.uptimeMillis();
|
||||
|
||||
Cursor c = (Cursor) mFilterMethod.invoke(null, mResolver, KNOWN_PREFIX, 100);
|
||||
final Cursor c = BrowserDB.filter(mResolver, KNOWN_PREFIX, 100);
|
||||
c.getCount(); // ensure query is not lazy loaded
|
||||
|
||||
long end = SystemClock.uptimeMillis();
|
||||
|
@ -8,9 +8,6 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.KeyEvent;
|
||||
import android.widget.TextView;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.RuntimeException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.mozilla.gecko.tests;
|
||||
|
||||
import org.mozilla.gecko.*;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
@ -9,8 +10,6 @@ import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.ClassLoader;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
@ -50,25 +49,22 @@ public class testDistribution extends ContentProviderTest {
|
||||
* writes prefs -- to finish before we begin the test.
|
||||
*/
|
||||
private void waitForBackgroundHappiness() {
|
||||
try {
|
||||
ClassLoader classLoader = mActivity.getClassLoader();
|
||||
Class threadUtilsClass = classLoader.loadClass("org.mozilla.gecko.util.ThreadUtils");
|
||||
Method postToBackgroundThread = threadUtilsClass.getMethod("postToBackgroundThread", Runnable.class);
|
||||
final Object signal = new Object();
|
||||
final Runnable done = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (signal) {
|
||||
signal.notify();
|
||||
}
|
||||
final Object signal = new Object();
|
||||
final Runnable done = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (signal) {
|
||||
signal.notify();
|
||||
}
|
||||
};
|
||||
synchronized (signal) {
|
||||
postToBackgroundThread.invoke(null, done);
|
||||
signal.wait();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
mAsserter.ok(false, "Exception waiting on background thread.", e.toString());
|
||||
};
|
||||
synchronized (signal) {
|
||||
ThreadUtils.postToBackgroundThread(done);
|
||||
try {
|
||||
signal.wait();
|
||||
} catch (InterruptedException e) {
|
||||
mAsserter.ok(false, "InterruptedException waiting on background thread.", e.toString());
|
||||
}
|
||||
}
|
||||
mAsserter.dumpLog("Background task completed. Proceeding.");
|
||||
}
|
||||
@ -99,19 +95,11 @@ public class testDistribution extends ContentProviderTest {
|
||||
|
||||
// Initialize the distribution from the mock package.
|
||||
private void initDistribution(String aPackagePath) {
|
||||
try {
|
||||
// Call Distribution.init with the mock package.
|
||||
ClassLoader classLoader = mActivity.getClassLoader();
|
||||
Class distributionClass = classLoader.loadClass("org.mozilla.gecko.Distribution");
|
||||
Method init = distributionClass.getMethod("init", Context.class, String.class, String.class);
|
||||
|
||||
Actions.EventExpecter distributionSetExpecter = mActions.expectGeckoEvent("Distribution:Set:OK");
|
||||
init.invoke(null, mActivity, aPackagePath, "prefs-" + System.currentTimeMillis());
|
||||
distributionSetExpecter.blockForEvent();
|
||||
distributionSetExpecter.unregisterListener();
|
||||
} catch (Exception e) {
|
||||
mAsserter.ok(false, "exception initializing distribution", e.toString());
|
||||
}
|
||||
// Call Distribution.init with the mock package.
|
||||
Actions.EventExpecter distributionSetExpecter = mActions.expectGeckoEvent("Distribution:Set:OK");
|
||||
Distribution.init(mActivity, aPackagePath, "prefs-" + System.currentTimeMillis());
|
||||
distributionSetExpecter.blockForEvent();
|
||||
distributionSetExpecter.unregisterListener();
|
||||
}
|
||||
|
||||
// Test distribution and preferences values stored in preferences.json
|
||||
|
@ -4,7 +4,6 @@ import org.mozilla.gecko.*;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.SharedPreferences;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
@ -1,11 +1,12 @@
|
||||
package org.mozilla.gecko.tests;
|
||||
|
||||
import org.mozilla.gecko.db.BrowserContract.FormHistory;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import java.io.File;
|
||||
import java.lang.ClassLoader;
|
||||
|
||||
/**
|
||||
* A basic form history contentprovider test.
|
||||
@ -36,29 +37,15 @@ public class testFormHistory extends BaseTest {
|
||||
int numUpdated;
|
||||
int numDeleted;
|
||||
|
||||
try {
|
||||
ClassLoader classLoader = getActivity().getClassLoader();
|
||||
Class fh = classLoader.loadClass("org.mozilla.gecko.db.BrowserContract$FormHistory");
|
||||
|
||||
cvs[0].put("fieldname", "fieldname");
|
||||
cvs[0].put("value", "value");
|
||||
cvs[0].put("timesUsed", "0");
|
||||
cvs[0].put("guid", "guid");
|
||||
|
||||
// Attempt to insert into the db
|
||||
formHistoryUri = (Uri)fh.getField("CONTENT_URI").get(null);
|
||||
Uri.Builder builder = formHistoryUri.buildUpon();
|
||||
formHistoryUri = builder.appendQueryParameter("profilePath", mProfile).build();
|
||||
} catch(ClassNotFoundException ex) {
|
||||
mAsserter.is(false, true, "Error getting class");
|
||||
return;
|
||||
} catch(NoSuchFieldException ex) {
|
||||
mAsserter.is(false, true, "Error getting field");
|
||||
return;
|
||||
} catch(IllegalAccessException ex) {
|
||||
mAsserter.is(false, true, "Error using field");
|
||||
return;
|
||||
}
|
||||
cvs[0].put("fieldname", "fieldname");
|
||||
cvs[0].put("value", "value");
|
||||
cvs[0].put("timesUsed", "0");
|
||||
cvs[0].put("guid", "guid");
|
||||
|
||||
// Attempt to insert into the db
|
||||
formHistoryUri = FormHistory.CONTENT_URI;
|
||||
Uri.Builder builder = formHistoryUri.buildUpon();
|
||||
formHistoryUri = builder.appendQueryParameter("profilePath", mProfile).build();
|
||||
|
||||
insertUri = cr.insert(formHistoryUri, cvs[0]);
|
||||
expectedUri = formHistoryUri.buildUpon().appendPath("1").build();
|
||||
|
@ -5,7 +5,6 @@ import org.mozilla.gecko.*;
|
||||
import android.app.Activity;
|
||||
import android.hardware.Camera;
|
||||
import android.os.Build;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class testGetUserMedia extends BaseTest {
|
||||
@Override
|
||||
|
@ -1,9 +1,9 @@
|
||||
package org.mozilla.gecko.tests;
|
||||
|
||||
import java.lang.ClassLoader;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.util.GeckoJarReader;
|
||||
|
||||
/**
|
||||
* A basic jar reader test. Tests reading a png from fennec's apk, as well
|
||||
@ -16,54 +16,34 @@ public class testJarReader extends BaseTest {
|
||||
}
|
||||
|
||||
public void testJarReader() {
|
||||
try {
|
||||
ClassLoader classLoader = getActivity().getClassLoader();
|
||||
String appPath = getActivity().getApplication().getPackageResourcePath();
|
||||
mAsserter.isnot(appPath, null, "getPackageResourcePath is non-null");
|
||||
|
||||
Class appConstantsClass = classLoader.loadClass("org.mozilla.gecko.AppConstants");
|
||||
String omniJarName = (String) appConstantsClass.getField("OMNIJAR_NAME").get(null);
|
||||
// Test reading a file from a jar url that looks correct.
|
||||
String url = "jar:file://" + appPath + "!/" + AppConstants.OMNIJAR_NAME;
|
||||
InputStream stream = GeckoJarReader.getStream("jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
|
||||
mAsserter.isnot(stream, null, "JarReader returned non-null for valid file in valid jar");
|
||||
|
||||
Class gjrClass = classLoader.loadClass("org.mozilla.gecko.util.GeckoJarReader");
|
||||
Method getStreamMethod = gjrClass.getMethod("getStream", String.class);
|
||||
String appPath = getActivity().getApplication().getPackageResourcePath();
|
||||
mAsserter.isnot(appPath, null, "getPackageResourcePath is non-null");
|
||||
// Test looking for an non-existent file in a jar.
|
||||
url = "jar:file://" + appPath + "!/" + AppConstants.OMNIJAR_NAME;
|
||||
stream = GeckoJarReader.getStream("jar:" + url + "!/chrome/chrome/content/branding/nonexistent_file.png");
|
||||
mAsserter.is(stream, null, "JarReader returned null for non-existent file in valid jar");
|
||||
|
||||
// Test reading a file from a jar url that looks correct.
|
||||
String url = "jar:file://" + appPath + "!/" + omniJarName;
|
||||
InputStream stream = (InputStream) getStreamMethod.invoke(null, "jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
|
||||
mAsserter.isnot(stream, null, "JarReader returned non-null for valid file in valid jar");
|
||||
// Test looking for a file that doesn't exist in the APK.
|
||||
url = "jar:file://" + appPath + "!/" + "BAD" + AppConstants.OMNIJAR_NAME;
|
||||
stream = GeckoJarReader.getStream("jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
|
||||
mAsserter.is(stream, null, "JarReader returned null for valid file in invalid jar file");
|
||||
|
||||
// Test looking for an non-existent file in a jar.
|
||||
url = "jar:file://" + appPath + "!/" + omniJarName;
|
||||
stream = (InputStream) getStreamMethod.invoke(null, "jar:" + url + "!/chrome/chrome/content/branding/nonexistent_file.png");
|
||||
mAsserter.is(stream, null, "JarReader returned null for non-existent file in valid jar");
|
||||
// Test looking for an jar with an invalid url.
|
||||
url = "jar:file://" + appPath + "!" + "!/" + AppConstants.OMNIJAR_NAME;
|
||||
stream = GeckoJarReader.getStream("jar:" + url + "!/chrome/chrome/content/branding/nonexistent_file.png");
|
||||
mAsserter.is(stream, null, "JarReader returned null for bad jar url");
|
||||
|
||||
// Test looking for a file that doesn't exist in the APK.
|
||||
url = "jar:file://" + appPath + "!/" + "BAD" + omniJarName;
|
||||
stream = (InputStream) getStreamMethod.invoke(null, "jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
|
||||
mAsserter.is(stream, null, "JarReader returned null for valid file in invalid jar file");
|
||||
// Test looking for a file that doesn't exist on disk.
|
||||
url = "jar:file://" + appPath + "BAD" + "!/" + AppConstants.OMNIJAR_NAME;
|
||||
stream = GeckoJarReader.getStream("jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
|
||||
mAsserter.is(stream, null, "JarReader returned null for a non-existent APK");
|
||||
|
||||
// Test looking for an jar with an invalid url.
|
||||
url = "jar:file://" + appPath + "!" + "!/" + omniJarName;
|
||||
stream = (InputStream) getStreamMethod.invoke(null, "jar:" + url + "!/chrome/chrome/content/branding/nonexistent_file.png");
|
||||
mAsserter.is(stream, null, "JarReader returned null for bad jar url");
|
||||
|
||||
// Test looking for a file that doesn't exist on disk.
|
||||
url = "jar:file://" + appPath + "BAD" + "!/" + omniJarName;
|
||||
stream = (InputStream) getStreamMethod.invoke(null, "jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
|
||||
mAsserter.is(stream, null, "JarReader returned null for a non-existent APK");
|
||||
} catch (java.lang.ClassCastException ex) {
|
||||
mAsserter.is(false, true, "Error getting OMNIJAR_NAME");
|
||||
} catch (java.lang.NoSuchFieldException ex) {
|
||||
mAsserter.is(false, true, "Error getting field");
|
||||
} catch (java.lang.ClassNotFoundException ex) {
|
||||
mAsserter.is(false, true, "Error getting class");
|
||||
} catch (java.lang.NoSuchMethodException ex) {
|
||||
mAsserter.is(false, true, "Error getting method");
|
||||
} catch (java.lang.IllegalAccessException ex) {
|
||||
mAsserter.is(false, true, "Error calling method");
|
||||
} catch (java.lang.reflect.InvocationTargetException ex) {
|
||||
mAsserter.is(false, true, "Invocation target exception " + ex.getTargetException());
|
||||
}
|
||||
// This test completes very quickly. If it completes too soon, the
|
||||
// minidumps directory may not be created before the process is
|
||||
// taken down, causing bug 722166.
|
||||
|
@ -1,13 +1,14 @@
|
||||
package org.mozilla.gecko.tests;
|
||||
|
||||
import org.mozilla.gecko.*;
|
||||
import org.mozilla.gecko.db.BrowserContract;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.ContentResolver;
|
||||
import android.database.Cursor;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
@ -19,101 +20,91 @@ public class testPasswordEncrypt extends BaseTest {
|
||||
}
|
||||
|
||||
public void testPasswordEncrypt() {
|
||||
Context context = (Context)getActivity();
|
||||
ContentResolver cr = context.getContentResolver();
|
||||
mAsserter.isnot(cr, null, "Found a content resolver");
|
||||
ContentValues cvs = new ContentValues();
|
||||
Context context = (Context)getActivity();
|
||||
ContentResolver cr = context.getContentResolver();
|
||||
mAsserter.isnot(cr, null, "Found a content resolver");
|
||||
ContentValues cvs = new ContentValues();
|
||||
|
||||
blockForGeckoReady();
|
||||
blockForGeckoReady();
|
||||
|
||||
File db = new File(mProfile, "signons.sqlite");
|
||||
String dbPath = db.getPath();
|
||||
File db = new File(mProfile, "signons.sqlite");
|
||||
String dbPath = db.getPath();
|
||||
|
||||
Uri passwordUri;
|
||||
try {
|
||||
ClassLoader classLoader = getActivity().getClassLoader();
|
||||
Class pwds = classLoader.loadClass("org.mozilla.gecko.db.BrowserContract$Passwords");
|
||||
Class nss = classLoader.loadClass("org.mozilla.gecko.NSSBridge");
|
||||
Class contextClass = classLoader.loadClass("android.content.Context");
|
||||
Class stringClass = classLoader.loadClass("java.lang.String");
|
||||
Class appshell = classLoader.loadClass("org.mozilla.gecko.GeckoAppShell");
|
||||
Uri passwordUri;
|
||||
cvs.put("hostname", "http://www.example.com");
|
||||
cvs.put("encryptedUsername", "username");
|
||||
cvs.put("encryptedPassword", "password");
|
||||
|
||||
Method decrypt = nss.getMethod("decrypt", contextClass, stringClass, stringClass);
|
||||
Method encrypt = nss.getMethod("encrypt", contextClass, stringClass, stringClass);
|
||||
|
||||
cvs.put("hostname", "http://www.example.com");
|
||||
cvs.put("encryptedUsername", "username");
|
||||
cvs.put("encryptedPassword", "password");
|
||||
// Attempt to insert into the db
|
||||
passwordUri = BrowserContract.Passwords.CONTENT_URI;
|
||||
Uri.Builder builder = passwordUri.buildUpon();
|
||||
passwordUri = builder.appendQueryParameter("profilePath", mProfile).build();
|
||||
|
||||
// Attempt to insert into the db
|
||||
passwordUri = (Uri)pwds.getField("CONTENT_URI").get(null);
|
||||
Uri.Builder builder = passwordUri.buildUpon();
|
||||
passwordUri = builder.appendQueryParameter("profilePath", mProfile).build();
|
||||
Uri uri = cr.insert(passwordUri, cvs);
|
||||
Uri expectedUri = passwordUri.buildUpon().appendPath("1").build();
|
||||
mAsserter.is(uri.toString(), expectedUri.toString(), "Insert returned correct uri");
|
||||
|
||||
Uri uri = cr.insert(passwordUri, cvs);
|
||||
Uri expectedUri = passwordUri.buildUpon().appendPath("1").build();
|
||||
mAsserter.is(uri.toString(), expectedUri.toString(), "Insert returned correct uri");
|
||||
Cursor list = mActions.querySql(dbPath, "SELECT encryptedUsername FROM moz_logins");
|
||||
list.moveToFirst();
|
||||
String decryptedU = null;
|
||||
try {
|
||||
decryptedU = NSSBridge.decrypt(context, mProfile, list.getString(0));
|
||||
} catch (Exception e) {
|
||||
mAsserter.ok(false, "NSSBridge.decrypt through Exception " + e, ""); // TODO: What is diag?
|
||||
}
|
||||
mAsserter.is(decryptedU, "username", "Username was encrypted correctly when inserting");
|
||||
|
||||
Cursor list = mActions.querySql(dbPath, "SELECT encryptedUsername FROM moz_logins");
|
||||
list.moveToFirst();
|
||||
String decryptedU = (String)decrypt.invoke(null, context, mProfile, list.getString(0));
|
||||
mAsserter.is(decryptedU, "username", "Username was encrypted correctly when inserting");
|
||||
list = mActions.querySql(dbPath, "SELECT encryptedPassword, encType FROM moz_logins");
|
||||
list.moveToFirst();
|
||||
String decryptedP = null;
|
||||
try {
|
||||
decryptedP = NSSBridge.decrypt(context, mProfile, list.getString(0));
|
||||
} catch (Exception e) {
|
||||
mAsserter.ok(false, "NSSBridge.decrypt through Exception " + e, ""); // TODO: What is diag?
|
||||
}
|
||||
mAsserter.is(decryptedP, "password", "Password was encrypted correctly when inserting");
|
||||
mAsserter.is(list.getInt(1), 1, "Password has correct encryption type");
|
||||
|
||||
list = mActions.querySql(dbPath, "SELECT encryptedPassword, encType FROM moz_logins");
|
||||
list.moveToFirst();
|
||||
String decryptedP = (String)decrypt.invoke(null, context, mProfile, list.getString(0));
|
||||
mAsserter.is(decryptedP, "password", "Password was encrypted correctly when inserting");
|
||||
mAsserter.is(list.getInt(1), 1, "Password has correct encryption type");
|
||||
|
||||
cvs.put("encryptedUsername", "username2");
|
||||
cvs.put("encryptedPassword", "password2");
|
||||
cr.update(passwordUri, cvs, null, null);
|
||||
cvs.put("encryptedUsername", "username2");
|
||||
cvs.put("encryptedPassword", "password2");
|
||||
cr.update(passwordUri, cvs, null, null);
|
||||
|
||||
list = mActions.querySql(dbPath, "SELECT encryptedUsername FROM moz_logins");
|
||||
list.moveToFirst();
|
||||
decryptedU = (String)decrypt.invoke(null, context, mProfile, list.getString(0));
|
||||
mAsserter.is(decryptedU, "username2", "Username was encrypted when updating");
|
||||
list = mActions.querySql(dbPath, "SELECT encryptedUsername FROM moz_logins");
|
||||
list.moveToFirst();
|
||||
try {
|
||||
decryptedU = NSSBridge.decrypt(context, mProfile, list.getString(0));
|
||||
} catch (Exception e) {
|
||||
mAsserter.ok(false, "NSSBridge.decrypt through Exception " + e, ""); // TODO: What is diag?
|
||||
}
|
||||
mAsserter.is(decryptedU, "username2", "Username was encrypted when updating");
|
||||
|
||||
list = mActions.querySql(dbPath, "SELECT encryptedPassword FROM moz_logins");
|
||||
list.moveToFirst();
|
||||
decryptedP = (String)decrypt.invoke(null, context, mProfile, list.getString(0));
|
||||
mAsserter.is(decryptedP, "password2", "Password was encrypted when updating");
|
||||
list = mActions.querySql(dbPath, "SELECT encryptedPassword FROM moz_logins");
|
||||
list.moveToFirst();
|
||||
try {
|
||||
decryptedP = NSSBridge.decrypt(context, mProfile, list.getString(0));
|
||||
} catch (Exception e) {
|
||||
mAsserter.ok(false, "NSSBridge.decrypt through Exception " + e, ""); // TODO: What is diag?
|
||||
}
|
||||
mAsserter.is(decryptedP, "password2", "Password was encrypted when updating");
|
||||
|
||||
// Trying to store a password while master password is enabled should throw,
|
||||
// but because Android can't send Exceptions across processes
|
||||
// it just results in a null uri/cursor being returned.
|
||||
toggleMasterPassword("password");
|
||||
try {
|
||||
uri = cr.insert(passwordUri, cvs);
|
||||
// TODO: restore this assertion -- see bug 764901
|
||||
// mAsserter.is(uri, null, "Storing a password while MP was set should fail");
|
||||
// Trying to store a password while master password is enabled should throw,
|
||||
// but because Android can't send Exceptions across processes
|
||||
// it just results in a null uri/cursor being returned.
|
||||
toggleMasterPassword("password");
|
||||
try {
|
||||
uri = cr.insert(passwordUri, cvs);
|
||||
// TODO: restore this assertion -- see bug 764901
|
||||
// mAsserter.is(uri, null, "Storing a password while MP was set should fail");
|
||||
|
||||
Cursor c = cr.query(passwordUri, null, null, null, null);
|
||||
// TODO: restore this assertion -- see bug 764901
|
||||
// mAsserter.is(c, null, "Querying passwords while MP was set should fail");
|
||||
} catch (Exception ex) {
|
||||
// Password provider currently can not throw across process
|
||||
// so we should not catch this exception here
|
||||
mAsserter.ok(false, "Caught exception", ex.toString());
|
||||
}
|
||||
toggleMasterPassword("password");
|
||||
|
||||
} catch(ClassNotFoundException ex) {
|
||||
mAsserter.ok(false, "Error getting class", ex.toString());
|
||||
return;
|
||||
} catch(NoSuchFieldException ex) {
|
||||
mAsserter.ok(false, "Error getting field", ex.toString());
|
||||
return;
|
||||
} catch(IllegalAccessException ex) {
|
||||
mAsserter.ok(false, "Error using field", ex.toString());
|
||||
return;
|
||||
} catch(java.lang.NoSuchMethodException ex) {
|
||||
mAsserter.ok(false, "Error getting method", ex.toString());
|
||||
return;
|
||||
} catch(java.lang.reflect.InvocationTargetException ex) {
|
||||
mAsserter.ok(false, "Error invoking method", ex.toString());
|
||||
return;
|
||||
}
|
||||
Cursor c = cr.query(passwordUri, null, null, null, null);
|
||||
// TODO: restore this assertion -- see bug 764901
|
||||
// mAsserter.is(c, null, "Querying passwords while MP was set should fail");
|
||||
} catch (Exception ex) {
|
||||
// Password provider currently can not throw across process
|
||||
// so we should not catch this exception here
|
||||
mAsserter.ok(false, "Caught exception", ex.toString());
|
||||
}
|
||||
toggleMasterPassword("password");
|
||||
}
|
||||
|
||||
private void toggleMasterPassword(String passwd) {
|
||||
|
@ -1,5 +1,7 @@
|
||||
package org.mozilla.gecko.tests;
|
||||
|
||||
import org.mozilla.gecko.db.BrowserContract.Passwords;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.ContentResolver;
|
||||
import android.database.Cursor;
|
||||
@ -30,34 +32,19 @@ public class testPasswordProvider extends BaseTest {
|
||||
|
||||
blockForGeckoReady();
|
||||
|
||||
Uri passwordUri;
|
||||
try {
|
||||
ClassLoader classLoader = getActivity().getClassLoader();
|
||||
Class pwds = classLoader.loadClass("org.mozilla.gecko.db.BrowserContract$Passwords");
|
||||
|
||||
cvs[0].put("hostname", "http://www.example.com");
|
||||
cvs[0].put("httpRealm", "http://www.example.com");
|
||||
cvs[0].put("formSubmitURL", "http://www.example.com");
|
||||
cvs[0].put("usernameField", "usernameField");
|
||||
cvs[0].put("passwordField", "passwordField");
|
||||
cvs[0].put("encryptedUsername", "username");
|
||||
cvs[0].put("encryptedPassword", "password");
|
||||
cvs[0].put("encType", "1");
|
||||
|
||||
// Attempt to insert into the db
|
||||
passwordUri = (Uri)pwds.getField("CONTENT_URI").get(null);
|
||||
Uri.Builder builder = passwordUri.buildUpon();
|
||||
passwordUri = builder.appendQueryParameter("profilePath", mProfile).build();
|
||||
} catch(ClassNotFoundException ex) {
|
||||
mAsserter.is(false, true, "Error getting class");
|
||||
return;
|
||||
} catch(NoSuchFieldException ex) {
|
||||
mAsserter.is(false, true, "Error getting field");
|
||||
return;
|
||||
} catch(IllegalAccessException ex) {
|
||||
mAsserter.is(false, true, "Error using field");
|
||||
return;
|
||||
}
|
||||
cvs[0].put("hostname", "http://www.example.com");
|
||||
cvs[0].put("httpRealm", "http://www.example.com");
|
||||
cvs[0].put("formSubmitURL", "http://www.example.com");
|
||||
cvs[0].put("usernameField", "usernameField");
|
||||
cvs[0].put("passwordField", "passwordField");
|
||||
cvs[0].put("encryptedUsername", "username");
|
||||
cvs[0].put("encryptedPassword", "password");
|
||||
cvs[0].put("encType", "1");
|
||||
|
||||
// Attempt to insert into the db
|
||||
Uri passwordUri = Passwords.CONTENT_URI;
|
||||
Uri.Builder builder = passwordUri.buildUpon();
|
||||
passwordUri = builder.appendQueryParameter("profilePath", mProfile).build();
|
||||
|
||||
Uri uri = cr.insert(passwordUri, cvs[0]);
|
||||
Uri expectedUri = passwordUri.buildUpon().appendPath("1").build();
|
||||
|
@ -2,7 +2,6 @@ package org.mozilla.gecko.tests;
|
||||
|
||||
import org.mozilla.gecko.*;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -138,56 +137,34 @@ public class testSettingsMenuItems extends PixelTest {
|
||||
* if they are present.
|
||||
*/
|
||||
public void addConditionalSettings(Map<String, List<String[]>> settingsMap) {
|
||||
try {
|
||||
ClassLoader classLoader = getActivity().getClassLoader();
|
||||
Class appConstants = classLoader.loadClass("org.mozilla.gecko.AppConstants");
|
||||
// Preferences dependent on RELEASE_BUILD
|
||||
if (!AppConstants.RELEASE_BUILD) {
|
||||
// Text reflow - only built if *not* release build
|
||||
String[] textReflowUi = { "Text reflow" };
|
||||
settingsMap.get("Display").add(textReflowUi);
|
||||
|
||||
// Preferences dependent on RELEASE_BUILD
|
||||
Field releaseBuildField = appConstants.getField("RELEASE_BUILD");
|
||||
boolean releaseBuild = releaseBuildField.getBoolean(appConstants);
|
||||
if (!releaseBuild) {
|
||||
// Text reflow - only built if *not* release build
|
||||
String[] textReflowUi = { "Text reflow" };
|
||||
settingsMap.get("Display").add(textReflowUi);
|
||||
// Anonymous cell tower/wifi collection - only built if *not* release build
|
||||
String[] networkReportingUi = { "Mozilla location services", "Help improve geolocation services for the Open Web by letting " + BRAND_NAME + " collect and send anonymous cellular tower data" };
|
||||
settingsMap.get("Mozilla").add(networkReportingUi);
|
||||
|
||||
// Anonymous cell tower/wifi collection - only built if *not* release build
|
||||
String[] networkReportingUi = { "Mozilla location services", "Help improve geolocation services for the Open Web by letting " + BRAND_NAME + " collect and send anonymous cellular tower data" };
|
||||
settingsMap.get("Mozilla").add(networkReportingUi);
|
||||
}
|
||||
|
||||
}
|
||||
// Automatic updates
|
||||
if (AppConstants.MOZ_UPDATER) {
|
||||
String[] autoUpdateUi = { "Automatic updates", "Only over Wi-Fi", "Enabled", "Only over Wi-Fi", "Disabled" };
|
||||
settingsMap.get("Customize").add(autoUpdateUi);
|
||||
}
|
||||
|
||||
// Automatic updates
|
||||
Field autoUpdateField = appConstants.getField("MOZ_UPDATER");
|
||||
boolean autoUpdate = autoUpdateField.getBoolean(appConstants);
|
||||
if (autoUpdate) {
|
||||
String[] autoUpdateUi = { "Automatic updates", "Only over Wi-Fi", "Enabled", "Only over Wi-Fi", "Disabled" };
|
||||
settingsMap.get("Customize").add(autoUpdateUi);
|
||||
}
|
||||
|
||||
// Crash reporter
|
||||
Field crashReportingField = appConstants.getField("MOZ_CRASHREPORTER");
|
||||
boolean crashReporter = crashReportingField.getBoolean(appConstants);
|
||||
if (crashReporter) {
|
||||
String[] crashReporterUi = { "Crash Reporter", BRAND_NAME + " submits crash reports to help Mozilla make your browser more stable and secure" };
|
||||
settingsMap.get("Mozilla").add(crashReporterUi);
|
||||
}
|
||||
|
||||
// Telemetry
|
||||
Field telemetryField = appConstants.getField("MOZ_TELEMETRY_REPORTING");
|
||||
boolean telemetry = telemetryField.getBoolean(appConstants);
|
||||
if (telemetry) {
|
||||
String[] telemetryUi = { "Telemetry", "Shares performance, usage, hardware and customization data about your browser with Mozilla to help us make " + BRAND_NAME + " better" };
|
||||
settingsMap.get("Mozilla").add(telemetryUi);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
mAsserter.ok(false, "Class not found in setting conditional settings", e.toString());
|
||||
|
||||
} catch (NoSuchFieldException e) {
|
||||
mAsserter.ok(false, "Field not found in setting conditional settings", e.toString());
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
mAsserter.ok(false, "Field cannot be accessed in setting conditional settings", e.toString());
|
||||
// Crash reporter
|
||||
if (AppConstants.MOZ_CRASHREPORTER) {
|
||||
String[] crashReporterUi = { "Crash Reporter", BRAND_NAME + " submits crash reports to help Mozilla make your browser more stable and secure" };
|
||||
settingsMap.get("Mozilla").add(crashReporterUi);
|
||||
}
|
||||
|
||||
// Telemetry
|
||||
if (AppConstants.MOZ_TELEMETRY_REPORTING) {
|
||||
String[] telemetryUi = { "Telemetry", "Shares performance, usage, hardware and customization data about your browser with Mozilla to help us make " + BRAND_NAME + " better" };
|
||||
settingsMap.get("Mozilla").add(telemetryUi);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
package org.mozilla.gecko.tests;
|
||||
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
@ -67,26 +69,15 @@ public class testThumbnails extends BaseTest {
|
||||
mAsserter.is(getTopSiteThumbnailColor(site2Title), Color.GREEN, "Top site thumbnail not updated for HTTP 404");
|
||||
|
||||
// test dropping thumbnails
|
||||
try {
|
||||
ClassLoader cl = getActivity().getApplicationContext().getClassLoader();
|
||||
Class browserDB = cl.loadClass("org.mozilla.gecko.db.BrowserDB");
|
||||
// check that the thumbnail is non-null
|
||||
byte[] thumbnailData = (byte[])browserDB
|
||||
.getMethod("getThumbnailForUrl", ContentResolver.class, String.class)
|
||||
.invoke(null, getActivity().getContentResolver(), site1Url);
|
||||
mAsserter.ok(thumbnailData != null && thumbnailData.length > 0, "Checking for thumbnail data", "No thumbnail data found");
|
||||
// drop thumbnails
|
||||
browserDB.getMethod("removeThumbnails", ContentResolver.class)
|
||||
.invoke(null, getActivity().getContentResolver());
|
||||
// check that the thumbnail is now null
|
||||
thumbnailData = (byte[])browserDB
|
||||
.getMethod("getThumbnailForUrl", ContentResolver.class, String.class)
|
||||
.invoke(null, getActivity().getContentResolver(), site1Url);
|
||||
mAsserter.ok(thumbnailData == null || thumbnailData.length == 0, "Checking for thumbnail data", "Thumbnail data found");
|
||||
} catch (Exception e) {
|
||||
mAsserter.ok(false, "Testing removing thumbnails", e.toString());
|
||||
mAsserter.dumpLog(e.toString(), e);
|
||||
}
|
||||
final ContentResolver resolver = getActivity().getContentResolver();
|
||||
// check that the thumbnail is non-null
|
||||
byte[] thumbnailData = BrowserDB.getThumbnailForUrl(resolver, site1Url);
|
||||
mAsserter.ok(thumbnailData != null && thumbnailData.length > 0, "Checking for thumbnail data", "No thumbnail data found");
|
||||
// drop thumbnails
|
||||
BrowserDB.removeThumbnails(resolver);
|
||||
// check that the thumbnail is now null
|
||||
thumbnailData = BrowserDB.getThumbnailForUrl(resolver, site1Url);
|
||||
mAsserter.ok(thumbnailData == null || thumbnailData.length == 0, "Checking for thumbnail data", "Thumbnail data found");
|
||||
}
|
||||
|
||||
private class ThumbnailTest implements BooleanTest {
|
||||
|
@ -114,6 +114,10 @@ public class ArrowPopup extends PopupWindow {
|
||||
arrowLayoutParams.setMargins(leftMargin, 0, 0, 0);
|
||||
}
|
||||
|
||||
showAsDropDown(mAnchor, offset, -mYOffset);
|
||||
if (isShowing()) {
|
||||
update(mAnchor, offset, -mYOffset, -1, -1);
|
||||
} else {
|
||||
showAsDropDown(mAnchor, offset, -mYOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,10 +53,14 @@ public class GeckoActionProvider extends ActionProvider {
|
||||
MenuItemActionView view = new MenuItemActionView(mContext, null);
|
||||
view.setActionButtonClickListener(mCallbacks);
|
||||
|
||||
if (dataModel.getHistorySize() > 0) {
|
||||
PackageManager packageManager = mContext.getPackageManager();
|
||||
ResolveInfo defaultActivity = dataModel.getDefaultActivity();
|
||||
view.setActionButton(defaultActivity == null ? null : defaultActivity.loadIcon(packageManager));
|
||||
final PackageManager packageManager = mContext.getPackageManager();
|
||||
int historySize = dataModel.getHistorySize();
|
||||
if (historySize > 2) {
|
||||
historySize = 2;
|
||||
}
|
||||
|
||||
for (int i = 0; i < historySize; i++) {
|
||||
view.addActionButton(dataModel.getActivity(i).loadIcon(packageManager));
|
||||
}
|
||||
|
||||
return view;
|
||||
@ -132,8 +136,9 @@ public class GeckoActionProvider extends ActionProvider {
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Integer index = (Integer) view.getTag();
|
||||
ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mHistoryFileName);
|
||||
chooseActivity(dataModel.getActivityIndex(dataModel.getDefaultActivity()));
|
||||
chooseActivity(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6799,6 +6799,20 @@ var SearchEngines = {
|
||||
onSuccess: function() {
|
||||
// Display a toast confirming addition of new search engine.
|
||||
NativeWindow.toast.show(Strings.browser.formatStringFromName("alertSearchEngineAddedToast", [engine.title], 1), "long");
|
||||
},
|
||||
|
||||
onError: function(aCode) {
|
||||
let errorMessage;
|
||||
if (aCode == 2) {
|
||||
// Engine is a duplicate.
|
||||
errorMessage = "alertSearchEngineDuplicateToast";
|
||||
|
||||
} else {
|
||||
// Unknown failure. Display general error message.
|
||||
errorMessage = "alertSearchEngineErrorToast";
|
||||
}
|
||||
|
||||
NativeWindow.toast.show(Strings.browser.formatStringFromName(errorMessage, [engine.title], 1), "long");
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -14,17 +14,18 @@ XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"nsIMessageSender");
|
||||
|
||||
function paymentSuccess(aRequestId) {
|
||||
return paymentCallback(aRequestId, "Payment:Success");
|
||||
return function(aResult) {
|
||||
closePaymentTab(aRequestId, function() {
|
||||
cpmm.sendAsyncMessage("Payment:Success", { result: aResult,
|
||||
requestId: aRequestId });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function paymentFailed(aRequestId) {
|
||||
return paymentCallback(aRequestId, "Payment:Failed");
|
||||
}
|
||||
|
||||
function paymentCallback(aRequestId, aMsg) {
|
||||
return function(aResult) {
|
||||
return function(aErrorMsg) {
|
||||
closePaymentTab(aRequestId, function() {
|
||||
cpmm.sendAsyncMessage(aMsg, { result: aResult,
|
||||
cpmm.sendAsyncMessage("Payment:Failed", { errorMsg: aErrorMsg,
|
||||
requestId: aRequestId });
|
||||
});
|
||||
}
|
||||
|
@ -22,10 +22,12 @@ alertDownloadsCancel=Cancel
|
||||
|
||||
alertFullScreenToast=Press BACK to leave full-screen mode
|
||||
|
||||
# LOCALIZATION NOTE (alertSearchEngineAddedToast)
|
||||
# LOCALIZATION NOTE (alertSearchEngineAddedToast, alertSearchEngineErrorToast, alertSearchEngineDuplicateToast)
|
||||
# %S will be replaced by the name of the search engine (exposed by the current page)
|
||||
# that has been added; for example, 'Google'.
|
||||
alertSearchEngineAddedToast='%S' has been added as a search engine
|
||||
alertSearchEngineErrorToast=Couldn't add '%S' as a search engine
|
||||
alertSearchEngineDuplicateToast='%S' is already one of your search engines
|
||||
|
||||
downloadCancelPromptTitle=Cancel Download
|
||||
downloadCancelPromptMessage=Do you want to cancel this download?
|
||||
|
@ -46,6 +46,9 @@ relativesrcdir toolkit/locales:
|
||||
locale/@AB_CD@/browser/overrides/passwordmgr.properties (%chrome/passwordmgr/passwordmgr.properties)
|
||||
locale/@AB_CD@/browser/overrides/search/search.properties (%chrome/search/search.properties)
|
||||
locale/@AB_CD@/browser/overrides/update/updates.properties (%chrome/mozapps/update/updates.properties)
|
||||
# plugins
|
||||
locale/@AB_CD@/browser/overrides/plugins/plugins.dtd (%chrome/mozapps/plugins/plugins.dtd)
|
||||
locale/@AB_CD@/browser/overrides/plugins/plugins.properties (%chrome/mozapps/plugins/plugins.properties)
|
||||
# about:support
|
||||
locale/@AB_CD@/browser/overrides/global/aboutSupport.dtd (%chrome/global/aboutSupport.dtd)
|
||||
locale/@AB_CD@/browser/overrides/global/aboutSupport.properties (%chrome/global/aboutSupport.properties)
|
||||
@ -68,6 +71,8 @@ relativesrcdir toolkit/locales:
|
||||
% override chrome://passwordmgr/locale/passwordmgr.properties chrome://browser/locale/overrides/passwordmgr/passwordmgr.properties
|
||||
% override chrome://global/locale/search/search.properties chrome://browser/locale/overrides/search/search.properties
|
||||
% override chrome://mozapps/locale/update/updates.properties chrome://browser/locale/overrides/update/updates.properties
|
||||
% override chrome://mozapps/locale/plugins/plugins.dtd chrome://browser/locale/overrides/plugins/plugins.dtd
|
||||
% override chrome://mozapps/locale/plugins/plugins.properties chrome://browser/locale/overrides/plugins/plugins.properties
|
||||
% override chrome://global/locale/aboutSupport.dtd chrome://browser/locale/overrides/global/aboutSupport.dtd
|
||||
% override chrome://global/locale/aboutSupport.properties chrome://browser/locale/overrides/global/aboutSupport.properties
|
||||
% override chrome://global/locale/crashes.dtd chrome://browser/locale/overrides/crashreporter/crashes.dtd
|
||||
|
@ -12,7 +12,9 @@ const Cr = Components.results;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Sqlite.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
|
||||
|
||||
@ -557,16 +559,244 @@ BookmarkExporter.prototype = {
|
||||
// Get list of itemIds that must be excluded from the backup.
|
||||
let excludeItems = PlacesUtils.annotations.getItemsWithAnnotation(
|
||||
PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO);
|
||||
let root = PlacesUtils.getFolderContents(PlacesUtils.placesRootId, false,
|
||||
false).root;
|
||||
// Serialize to JSON and write to stream.
|
||||
let nodeCount = yield BookmarkNode.serializeAsJSONToOutputStream(root,
|
||||
streamProxy,
|
||||
false,
|
||||
false,
|
||||
excludeItems);
|
||||
root.containerOpen = false;
|
||||
let nodeCount = yield BookmarkRow.serializeJSONToOutputStream(streamProxy,
|
||||
excludeItems);
|
||||
throw new Task.Result(nodeCount);
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
let BookmarkRow = {
|
||||
/**
|
||||
* Serializes the SQL results as JSON with async SQL call and writes the
|
||||
* serialization to the given output stream.
|
||||
*
|
||||
* @param aStream
|
||||
* An nsIOutputStream. NOTE: it only uses the write(str, len)
|
||||
* method of nsIOutputStream. The caller is responsible for
|
||||
* closing the stream.
|
||||
* @param aExcludeItems
|
||||
* An array of item ids that should not be written to the backup.
|
||||
* @return {Promise}
|
||||
* @resolves the number of serialized uri nodes.
|
||||
*/
|
||||
serializeJSONToOutputStream: function(aStream, aExcludeItems) {
|
||||
return Task.spawn(function() {
|
||||
let nodes = [];
|
||||
let nodeCount = 0;
|
||||
|
||||
let dbFilePath = OS.Path.join(OS.Constants.Path.profileDir,
|
||||
"places.sqlite");
|
||||
let conn = yield Sqlite.openConnection({ path: dbFilePath,
|
||||
sharedMemoryCache: false });
|
||||
try {
|
||||
let rows = yield conn.execute(
|
||||
"SELECT b.id, h.url, b.position, b.title, b.parent, " +
|
||||
"b.type, b.dateAdded, b.lastModified, b.guid, t.parent AS grandParent " +
|
||||
"FROM moz_bookmarks b " +
|
||||
"LEFT JOIN moz_bookmarks t ON t.id = b.parent " +
|
||||
"LEFT JOIN moz_places h ON h.id = b.fk " +
|
||||
"ORDER BY b.parent, b.position, b.id");
|
||||
|
||||
// Create a Map for lookup.
|
||||
let rowMap = new Map();
|
||||
for (let row of rows) {
|
||||
let parent = row.getResultByName("parent");
|
||||
if (rowMap.has(parent)) {
|
||||
let data = rowMap.get(parent);
|
||||
data.children.push(row);
|
||||
} else {
|
||||
rowMap.set(parent, { children: [row] });
|
||||
}
|
||||
}
|
||||
|
||||
let root = rowMap.get(0);
|
||||
if (!root) {
|
||||
throw new Error("Root does not exist.");
|
||||
}
|
||||
let result = yield BookmarkRow._appendConvertedNode(root.children[0],
|
||||
rowMap,
|
||||
nodes,
|
||||
aExcludeItems);
|
||||
if (result.appendedNode) {
|
||||
nodeCount = result.nodeCount;
|
||||
let json = JSON.stringify(nodes[0]);
|
||||
aStream.write(json, json.length);
|
||||
}
|
||||
} catch(e) {
|
||||
Cu.reportError("serializeJSONToOutputStream error " + e);
|
||||
} finally {
|
||||
yield conn.close();
|
||||
}
|
||||
throw new Task.Result(nodeCount);
|
||||
});
|
||||
},
|
||||
|
||||
_appendConvertedNode: function BR__appendConvertedNode(
|
||||
aRow, aRowMap, aNodes, aExcludeItems) {
|
||||
return Task.spawn(function() {
|
||||
let node = {};
|
||||
let nodeCount = 0;
|
||||
|
||||
this._addGenericProperties(aRow, node);
|
||||
|
||||
let parent = aRow.getResultByName("parent");
|
||||
let grandParent = parent ? aRow.getResultByName("grandParent") : null;
|
||||
let type = aRow.getResultByName("type");
|
||||
|
||||
if (type == Ci.nsINavBookmarksService.TYPE_BOOKMARK) {
|
||||
// Tag root accept only folder nodes
|
||||
if (parent == PlacesUtils.tagsFolderId)
|
||||
throw new Task.Result({ appendedNode: false, nodeCount: nodeCount });
|
||||
|
||||
// Check for url validity, since we can't halt while writing a backup.
|
||||
// This will throw if we try to serialize an invalid url and it does
|
||||
// not make sense saving a wrong or corrupt uri node.
|
||||
try {
|
||||
NetUtil.newURI(aRow.getResultByName("url"));
|
||||
} catch (ex) {
|
||||
throw new Task.Result({ appendedNode: false, nodeCount: nodeCount });
|
||||
}
|
||||
yield this._addURIProperties(aRow, node);
|
||||
nodeCount++;
|
||||
} else if (type == Ci.nsINavBookmarksService.TYPE_FOLDER) {
|
||||
// Tag containers accept only uri nodes
|
||||
if (grandParent && grandParent == PlacesUtils.tagsFolderId) {
|
||||
throw new Task.Result({ appendedNode: false, nodeCount: nodeCount });
|
||||
}
|
||||
this._addContainerProperties(aRow, node);
|
||||
} else if (type == Ci.nsINavBookmarksService.TYPE_SEPARATOR) {
|
||||
// Tag root accept only folder nodes
|
||||
// Tag containers accept only uri nodes
|
||||
if ((parent == PlacesUtils.tagsFolderId) ||
|
||||
(grandParent == PlacesUtils.tagsFolderId)) {
|
||||
throw new Task.Result({ appendedNode: false, nodeCount: nodeCount });
|
||||
}
|
||||
this._addSeparatorProperties(aRow, node);
|
||||
}
|
||||
|
||||
if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER) {
|
||||
nodeCount += yield this._appendConvertedComplexNode(node,
|
||||
aNodes,
|
||||
aRowMap,
|
||||
aExcludeItems);
|
||||
throw new Task.Result({ appendedNode: true, nodeCount: nodeCount });
|
||||
}
|
||||
|
||||
aNodes.push(node);
|
||||
throw new Task.Result({ appendedNode: true, nodeCount: nodeCount });
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
_addGenericProperties: function BR__addGenericProperties(aRow, aJSNode) {
|
||||
let title = aRow.getResultByName("title")
|
||||
aJSNode.title = title ? title : "";
|
||||
aJSNode.guid = aRow.getResultByName("guid");
|
||||
aJSNode.id = aRow.getResultByName("id");
|
||||
aJSNode.index = aRow.getResultByName("position");
|
||||
if (aJSNode.id != -1) {
|
||||
let parent = aRow.getResultByName("parent");
|
||||
if (parent)
|
||||
aJSNode.parent = parent;
|
||||
let dateAdded = aRow.getResultByName("dateAdded");;
|
||||
if (dateAdded)
|
||||
aJSNode.dateAdded = dateAdded;
|
||||
let lastModified = aRow.getResultByName("lastModified");
|
||||
if (lastModified)
|
||||
aJSNode.lastModified = lastModified;
|
||||
|
||||
// XXX need a hasAnnos api
|
||||
let annos = [];
|
||||
try {
|
||||
annos =
|
||||
PlacesUtils.getAnnotationsForItem(aJSNode.id).filter(function(anno) {
|
||||
// XXX should whitelist this instead, w/ a pref for
|
||||
// backup/restore of non-whitelisted annos
|
||||
// XXX causes JSON encoding errors, so utf-8 encode
|
||||
// anno.value = unescape(encodeURIComponent(anno.value));
|
||||
if (anno.name == PlacesUtils.LMANNO_FEEDURI)
|
||||
aJSNode.livemark = 1;
|
||||
return true;
|
||||
});
|
||||
} catch(ex) {}
|
||||
if (annos.length != 0)
|
||||
aJSNode.annos = annos;
|
||||
}
|
||||
// XXXdietrich - store annos for non-bookmark items
|
||||
},
|
||||
|
||||
_addURIProperties: function BR__addURIProperties(aRow, aJSNode) {
|
||||
return Task.spawn(function() {
|
||||
aJSNode.type = PlacesUtils.TYPE_X_MOZ_PLACE;
|
||||
aJSNode.uri = aRow.getResultByName("url");
|
||||
if (aJSNode.id && aJSNode.id != -1) {
|
||||
// Harvest bookmark-specific properties
|
||||
let keyword = PlacesUtils.bookmarks.getKeywordForBookmark(aJSNode.id);
|
||||
if (keyword)
|
||||
aJSNode.keyword = keyword;
|
||||
}
|
||||
|
||||
// Last character-set
|
||||
let uri = NetUtil.newURI(aRow.getResultByName("url"));
|
||||
let lastCharset = yield PlacesUtils.getCharsetForURI(uri)
|
||||
if (lastCharset)
|
||||
aJSNode.charset = lastCharset;
|
||||
});
|
||||
},
|
||||
|
||||
_addSeparatorProperties: function BR__addSeparatorProperties(aRow, aJSNode) {
|
||||
aJSNode.type = PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR;
|
||||
},
|
||||
|
||||
_addContainerProperties: function BR__addContainerProperties(aRow, aJSNode) {
|
||||
// This is a bookmark or a tag container.
|
||||
// Bookmark folder or a shortcut we should convert to folder.
|
||||
aJSNode.type = PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER;
|
||||
|
||||
// Mark root folders
|
||||
let itemId = aRow.getResultByName("id");
|
||||
if (itemId == PlacesUtils.placesRootId)
|
||||
aJSNode.root = "placesRoot";
|
||||
else if (itemId == PlacesUtils.bookmarksMenuFolderId)
|
||||
aJSNode.root = "bookmarksMenuFolder";
|
||||
else if (itemId == PlacesUtils.tagsFolderId)
|
||||
aJSNode.root = "tagsFolder";
|
||||
else if (itemId == PlacesUtils.unfiledBookmarksFolderId)
|
||||
aJSNode.root = "unfiledBookmarksFolder";
|
||||
else if (itemId == PlacesUtils.toolbarFolderId)
|
||||
aJSNode.root = "toolbarFolder";
|
||||
},
|
||||
|
||||
_appendConvertedComplexNode: function BR__appendConvertedComplexNode(
|
||||
aNode, aNodes, aRowMap, aExcludeItems) {
|
||||
return Task.spawn(function() {
|
||||
let repr = {};
|
||||
let nodeCount = 0;
|
||||
|
||||
for (let [name, value] in Iterator(aNode))
|
||||
repr[name] = value;
|
||||
repr.children = [];
|
||||
|
||||
let data = aRowMap.get(aNode.id);
|
||||
if (data) {
|
||||
for (let row of data.children) {
|
||||
let id = row.getResultByName("id");
|
||||
// ignore exclude items
|
||||
if (aExcludeItems && aExcludeItems.indexOf(id) != -1) {
|
||||
continue;
|
||||
}
|
||||
let result = yield this._appendConvertedNode(row,
|
||||
aRowMap,
|
||||
repr.children,
|
||||
aExcludeItems);
|
||||
nodeCount += result.nodeCount;
|
||||
}
|
||||
} else {
|
||||
Cu.reportError("_appendConvertedComplexNode error: Unable to find node");
|
||||
}
|
||||
|
||||
aNodes.push(repr);
|
||||
throw new Task.Result(nodeCount);
|
||||
}.bind(this));
|
||||
}
|
||||
|
@ -2647,6 +2647,189 @@ SourceActor.prototype.requestTypes = {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Determine if a given value is non-primitive.
|
||||
*
|
||||
* @param Any aValue
|
||||
* The value to test.
|
||||
* @return Boolean
|
||||
* Whether the value is non-primitive.
|
||||
*/
|
||||
function isObject(aValue) {
|
||||
const type = typeof aValue;
|
||||
return type == "object" ? aValue !== null : type == "function";
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a descriptor has a getter which doesn't call into JavaScript.
|
||||
*
|
||||
* @param Object aDesc
|
||||
* The descriptor to check for a safe getter.
|
||||
* @return Boolean
|
||||
* Whether a safe getter was found.
|
||||
*/
|
||||
function hasSafeGetter(aDesc) {
|
||||
let fn = aDesc.get;
|
||||
return fn && fn.callable && fn.class == "Function" && fn.script === undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely get the property value from a Debugger.Object for a given key. Walks
|
||||
* the prototype chain until the property is found.
|
||||
*
|
||||
* @param Debugger.Object aObject
|
||||
* The Debugger.Object to get the value from.
|
||||
* @param String aKey
|
||||
* The key to look for.
|
||||
* @return Any
|
||||
*/
|
||||
function getProperty(aObj, aKey) {
|
||||
try {
|
||||
do {
|
||||
const desc = aObj.getOwnPropertyDescriptor(aKey);
|
||||
if (desc) {
|
||||
if ("value" in desc) {
|
||||
return desc.value
|
||||
}
|
||||
// Call the getter if it's safe.
|
||||
return hasSafeGetter(desc) ? desc.get.call(aObj) : undefined;
|
||||
}
|
||||
aObj = aObj.proto;
|
||||
} while (aObj);
|
||||
} catch (e) {
|
||||
// If anything goes wrong report the error and return undefined.
|
||||
DevToolsUtils.reportException("getProperty", e);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a function that can safely stringify Debugger.Objects of a given
|
||||
* builtin type.
|
||||
*
|
||||
* @param Function aCtor
|
||||
* The builtin class constructor.
|
||||
* @return Function
|
||||
* The stringifier for the class.
|
||||
*/
|
||||
function createBuiltinStringifier(aCtor) {
|
||||
return aObj => aCtor.prototype.toString.call(aObj.unsafeDereference());
|
||||
}
|
||||
|
||||
/**
|
||||
* Stringify a Debugger.Object-wrapped Error instance.
|
||||
*
|
||||
* @param Debugger.Object aObj
|
||||
* The object to stringify.
|
||||
* @return String
|
||||
* The stringification of the object.
|
||||
*/
|
||||
function errorStringify(aObj) {
|
||||
let name = getProperty(aObj, "name");
|
||||
if (name === "" || name === undefined) {
|
||||
name = aObj.class;
|
||||
} else if (isObject(name)) {
|
||||
name = stringify(name);
|
||||
}
|
||||
|
||||
let message = getProperty(aObj, "message");
|
||||
if (isObject(message)) {
|
||||
message = stringify(message);
|
||||
}
|
||||
|
||||
if (message === "" || message === undefined) {
|
||||
return name;
|
||||
}
|
||||
return name + ": " + message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stringify a Debugger.Object based on its class.
|
||||
*
|
||||
* @param Debugger.Object aObj
|
||||
* The object to stringify.
|
||||
* @return String
|
||||
* The stringification for the object.
|
||||
*/
|
||||
function stringify(aObj) {
|
||||
if (Cu.isDeadWrapper(aObj)) {
|
||||
const error = new Error("Dead object encountered.");
|
||||
DevToolsUtils.reportException("stringify", error);
|
||||
return "<dead object>";
|
||||
}
|
||||
const stringifier = stringifiers[aObj.class] || stringifiers.Object;
|
||||
return stringifier(aObj);
|
||||
}
|
||||
|
||||
// Used to prevent infinite recursion when an array is found inside itself.
|
||||
let seen = null;
|
||||
|
||||
let stringifiers = {
|
||||
Error: errorStringify,
|
||||
EvalError: errorStringify,
|
||||
RangeError: errorStringify,
|
||||
ReferenceError: errorStringify,
|
||||
SyntaxError: errorStringify,
|
||||
TypeError: errorStringify,
|
||||
URIError: errorStringify,
|
||||
Boolean: createBuiltinStringifier(Boolean),
|
||||
Function: createBuiltinStringifier(Function),
|
||||
Number: createBuiltinStringifier(Number),
|
||||
RegExp: createBuiltinStringifier(RegExp),
|
||||
String: createBuiltinStringifier(String),
|
||||
Object: obj => "[object " + obj.class + "]",
|
||||
Array: obj => {
|
||||
// If we're at the top level then we need to create the Set for tracking
|
||||
// previously stringified arrays.
|
||||
const topLevel = !seen;
|
||||
if (topLevel) {
|
||||
seen = new Set();
|
||||
} else if (seen.has(obj)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
seen.add(obj);
|
||||
|
||||
const len = getProperty(obj, "length");
|
||||
let string = "";
|
||||
|
||||
// The following check is only required because the debuggee could possibly
|
||||
// be a Proxy and return any value. For normal objects, array.length is
|
||||
// always a non-negative integer.
|
||||
if (typeof len == "number" && len > 0) {
|
||||
for (let i = 0; i < len; i++) {
|
||||
const desc = obj.getOwnPropertyDescriptor(i);
|
||||
if (desc) {
|
||||
const { value } = desc;
|
||||
if (value != null) {
|
||||
string += isObject(value) ? stringify(value) : value;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < len - 1) {
|
||||
string += ",";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (topLevel) {
|
||||
seen = null;
|
||||
}
|
||||
|
||||
return string;
|
||||
},
|
||||
DOMException: obj => {
|
||||
const message = getProperty(obj, "message") || "<no message>";
|
||||
const result = (+getProperty(obj, "result")).toString(16);
|
||||
const code = getProperty(obj, "code");
|
||||
const name = getProperty(obj, "name") || "<unknown>";
|
||||
|
||||
return '[Exception... "' + message + '" ' +
|
||||
'code: "' + code +'" ' +
|
||||
'nsresult: "0x' + result + ' (' + name + ')"]';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an actor for the specified object.
|
||||
*
|
||||
@ -2883,9 +3066,7 @@ ObjectActor.prototype = {
|
||||
continue;
|
||||
}
|
||||
|
||||
let fn = desc.get;
|
||||
if (fn && fn.callable && fn.class == "Function" &&
|
||||
fn.script === undefined) {
|
||||
if (hasSafeGetter(desc)) {
|
||||
getters.add(name);
|
||||
}
|
||||
}
|
||||
@ -2929,34 +3110,9 @@ ObjectActor.prototype = {
|
||||
* The protocol request object.
|
||||
*/
|
||||
onDisplayString: function (aRequest) {
|
||||
let toString;
|
||||
try {
|
||||
// Attempt to locate the object's "toString" method.
|
||||
let obj = this.obj;
|
||||
do {
|
||||
let desc = obj.getOwnPropertyDescriptor("toString");
|
||||
if (desc) {
|
||||
toString = desc.value;
|
||||
break;
|
||||
}
|
||||
obj = obj.proto;
|
||||
} while ((obj));
|
||||
} catch (e) {
|
||||
dumpn(e);
|
||||
}
|
||||
|
||||
let result = null;
|
||||
if (toString && toString.callable) {
|
||||
// If a toString method was found then call it on the object.
|
||||
let ret = toString.call(this.obj).return;
|
||||
if (typeof ret == "string") {
|
||||
// Only use the result if it was a returned string.
|
||||
result = ret;
|
||||
}
|
||||
}
|
||||
|
||||
const string = stringify(this.obj);
|
||||
return { from: this.actorID,
|
||||
displayString: this.threadActor.createValueGrip(result) };
|
||||
displayString: this.threadActor.createValueGrip(string) };
|
||||
},
|
||||
|
||||
/**
|
||||
|
File diff suppressed because it is too large
Load Diff
145
toolkit/devtools/server/tests/unit/test_objectgrips-12.js
Normal file
145
toolkit/devtools/server/tests/unit/test_objectgrips-12.js
Normal file
@ -0,0 +1,145 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test getDisplayString.
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
|
||||
function run_test()
|
||||
{
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-grips");
|
||||
gDebuggee.eval(function stopMe(arg1) {
|
||||
debugger;
|
||||
}.toString());
|
||||
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect(function() {
|
||||
attachTestTabAndResume(gClient, "test-grips", function(aResponse, aTabClient, aThreadClient) {
|
||||
gThreadClient = aThreadClient;
|
||||
test_display_string();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function test_display_string()
|
||||
{
|
||||
const testCases = [
|
||||
{
|
||||
input: "new Boolean(true)",
|
||||
output: "true"
|
||||
},
|
||||
{
|
||||
input: "new Number(5)",
|
||||
output: "5"
|
||||
},
|
||||
{
|
||||
input: "new String('foo')",
|
||||
output: "foo"
|
||||
},
|
||||
{
|
||||
input: "new Map()",
|
||||
output: "[object Map]"
|
||||
},
|
||||
{
|
||||
input: "[,,,,,,,]",
|
||||
output: ",,,,,,"
|
||||
},
|
||||
{
|
||||
input: "[1, 2, 3]",
|
||||
output: "1,2,3"
|
||||
},
|
||||
{
|
||||
input: "[undefined, null, true, 'foo', 5]",
|
||||
output: ",,true,foo,5"
|
||||
},
|
||||
{
|
||||
input: "[{},{}]",
|
||||
output: "[object Object],[object Object]"
|
||||
},
|
||||
{
|
||||
input: "(" + function() {
|
||||
const arr = [1];
|
||||
arr.push(arr);
|
||||
return arr;
|
||||
} + ")()",
|
||||
output: "1,"
|
||||
},
|
||||
{
|
||||
input: "{}",
|
||||
output: "[object Object]"
|
||||
},
|
||||
{
|
||||
input: "Object.create(null)",
|
||||
output: "[object Object]"
|
||||
},
|
||||
{
|
||||
input: "new Error('foo')",
|
||||
output: "Error: foo"
|
||||
},
|
||||
{
|
||||
input: "new SyntaxError()",
|
||||
output: "SyntaxError"
|
||||
},
|
||||
{
|
||||
input: "new ReferenceError('')",
|
||||
output: "ReferenceError"
|
||||
},
|
||||
{
|
||||
input: "(" + function() {
|
||||
const err = new Error("bar");
|
||||
err.name = "foo";
|
||||
return err;
|
||||
} + ")()",
|
||||
output: "foo: bar"
|
||||
},
|
||||
{
|
||||
input: "() => {}",
|
||||
output: "() => {}"
|
||||
},
|
||||
{
|
||||
input: "function (foo, bar) {}",
|
||||
output: "function (foo, bar) {}"
|
||||
},
|
||||
{
|
||||
input: "function foo(bar) {}",
|
||||
output: "function foo(bar) {}"
|
||||
},
|
||||
{
|
||||
input: "Array",
|
||||
output: Array + ""
|
||||
},
|
||||
{
|
||||
input: "/foo[bar]/g",
|
||||
output: "/foo[bar]/g"
|
||||
},
|
||||
{
|
||||
input: "new Proxy({}, {})",
|
||||
output: "[object Object]"
|
||||
}
|
||||
];
|
||||
|
||||
gThreadClient.addOneTimeListener("paused", function(aEvent, aPacket) {
|
||||
const args = aPacket.frame.arguments;
|
||||
|
||||
(function loop() {
|
||||
const objClient = gThreadClient.pauseGrip(args.pop());
|
||||
objClient.getDisplayString(function({ displayString }) {
|
||||
do_check_eq(displayString, testCases.pop().output);
|
||||
if (args.length) {
|
||||
loop();
|
||||
} else {
|
||||
gThreadClient.resume(function() {
|
||||
finishClient(gClient);
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
||||
});
|
||||
|
||||
const inputs = testCases.map(({ input }) => input).join(",");
|
||||
gDebuggee.eval("stopMe(" + inputs + ")");
|
||||
}
|
@ -147,6 +147,7 @@ reason = bug 820380
|
||||
[test_objectgrips-09.js]
|
||||
[test_objectgrips-10.js]
|
||||
[test_objectgrips-11.js]
|
||||
[test_objectgrips-12.js]
|
||||
[test_interrupt.js]
|
||||
[test_stepping-01.js]
|
||||
[test_stepping-02.js]
|
||||
|
@ -1,4 +1,4 @@
|
||||
[DEFAULT]
|
||||
support-files = browser_clearplugindata.html
|
||||
|
||||
[browser_clearplugindata.html]
|
||||
[browser_clearplugindata.js]
|
||||
|
@ -83,19 +83,22 @@ GetDOMTargets(uint64_t aScrollId,
|
||||
class RequestContentRepaintEvent : public nsRunnable
|
||||
{
|
||||
typedef mozilla::layers::FrameMetrics FrameMetrics;
|
||||
typedef mozilla::layers::ScrollableLayerGuid ScrollableLayerGuid;
|
||||
|
||||
public:
|
||||
RequestContentRepaintEvent(const FrameMetrics& aFrameMetrics,
|
||||
nsIWidgetListener* aListener,
|
||||
CSSIntPoint* aLastOffsetOut) :
|
||||
CSSIntPoint* aLastOffsetOut,
|
||||
ScrollableLayerGuid* aLastScrollId) :
|
||||
mFrameMetrics(aFrameMetrics),
|
||||
mWidgetListener(aListener),
|
||||
mLastOffsetOut(aLastOffsetOut)
|
||||
mLastOffsetOut(aLastOffsetOut),
|
||||
mLastScrollIdOut(aLastScrollId)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
// This event shuts down the worker thread and so must be main thread.
|
||||
// This must be on the gecko thread since we access the dom
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
#ifdef DEBUG_CONTROLLER
|
||||
@ -139,6 +142,10 @@ public:
|
||||
if (mLastOffsetOut) {
|
||||
*mLastOffsetOut = actualScrollOffset;
|
||||
}
|
||||
if (mLastScrollIdOut) {
|
||||
mLastScrollIdOut->mScrollId = mFrameMetrics.mScrollId;
|
||||
mLastScrollIdOut->mPresShellId = mFrameMetrics.mPresShellId;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CONTROLLER
|
||||
WinUtils::Log("APZController: %I64d mDisplayPort: %0.2f %0.2f %0.2f %0.2f",
|
||||
@ -156,6 +163,7 @@ protected:
|
||||
FrameMetrics mFrameMetrics;
|
||||
nsIWidgetListener* mWidgetListener;
|
||||
CSSIntPoint* mLastOffsetOut;
|
||||
ScrollableLayerGuid* mLastScrollIdOut;
|
||||
};
|
||||
|
||||
void
|
||||
@ -225,19 +233,19 @@ APZController::ReceiveInputEvent(WidgetInputEvent* aInEvent,
|
||||
void
|
||||
APZController::RequestContentRepaint(const FrameMetrics& aFrameMetrics)
|
||||
{
|
||||
// Send the result back to the main thread so that it can shutdown
|
||||
if (!mWidgetListener) {
|
||||
NS_WARNING("Can't update display port, !mWidgetListener");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CONTROLLER
|
||||
WinUtils::Log("APZController::RequestContentRepaint scroll id = %I64d",
|
||||
WinUtils::Log("APZController::RequestContentRepaint scrollid=%I64d",
|
||||
aFrameMetrics.mScrollId);
|
||||
#endif
|
||||
nsCOMPtr<nsIRunnable> r1 = new RequestContentRepaintEvent(aFrameMetrics,
|
||||
mWidgetListener,
|
||||
&mLastScrollOffset);
|
||||
&mLastScrollOffset,
|
||||
&mLastScrollLayerGuid);
|
||||
if (!NS_IsMainThread()) {
|
||||
NS_DispatchToMainThread(r1);
|
||||
} else {
|
||||
@ -253,12 +261,20 @@ APZController::UpdateScrollOffset(const mozilla::layers::ScrollableLayerGuid& aS
|
||||
CSSIntPoint& aScrollOffset)
|
||||
{
|
||||
#ifdef DEBUG_CONTROLLER
|
||||
WinUtils::Log("APZController::UpdateScrollOffset: %d %d == %d %d",
|
||||
WinUtils::Log("APZController::UpdateScrollOffset: scrollid:%I64d == %I64d offsets: %d,%d == %d,%d",
|
||||
aScrollLayerId.mScrollId, aScrollLayerId.mScrollId,
|
||||
aScrollOffset.x, aScrollOffset.y,
|
||||
mLastScrollOffset.x, mLastScrollOffset.y);
|
||||
#endif
|
||||
|
||||
if (!sAPZC || mLastScrollOffset == aScrollOffset) {
|
||||
|
||||
// Bail if this the same scroll guid the apzc just scrolled and the offsets
|
||||
// equal the offset the apzc set.
|
||||
if (!sAPZC || (mLastScrollLayerGuid.mScrollId == aScrollLayerId.mScrollId &&
|
||||
mLastScrollLayerGuid.mPresShellId == aScrollLayerId.mPresShellId &&
|
||||
mLastScrollOffset == aScrollOffset)) {
|
||||
#ifdef DEBUG_CONTROLLER
|
||||
WinUtils::Log("Skipping UpdateScrollOffset");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
sAPZC->UpdateScrollOffset(aScrollLayerId, aScrollOffset);
|
||||
|
@ -25,6 +25,11 @@ class APZController :
|
||||
typedef mozilla::layers::ScrollableLayerGuid ScrollableLayerGuid;
|
||||
|
||||
public:
|
||||
APZController() :
|
||||
mWidgetListener(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
// GeckoContentController interface
|
||||
virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics);
|
||||
virtual void HandleDoubleTap(const mozilla::CSSIntPoint& aPoint);
|
||||
@ -54,6 +59,7 @@ public:
|
||||
|
||||
private:
|
||||
nsIWidgetListener* mWidgetListener;
|
||||
ScrollableLayerGuid mLastScrollLayerGuid;
|
||||
CSSIntPoint mLastScrollOffset;
|
||||
};
|
||||
|
||||
|
@ -1364,8 +1364,7 @@ MetroWidget::Move(double aX, double aY)
|
||||
NS_IMETHODIMP
|
||||
MetroWidget::Resize(double aWidth, double aHeight, bool aRepaint)
|
||||
{
|
||||
Resize(0, 0, aHeight, aHeight, aRepaint);
|
||||
return NS_OK;
|
||||
return Resize(0, 0, aWidth, aHeight, aRepaint);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
Loading…
Reference in New Issue
Block a user