Merge fx-team to m-c on a CLOSED TREE

This commit is contained in:
Wes Kocher 2013-11-18 18:25:57 -08:00
commit d600490156
81 changed files with 2161 additions and 1155 deletions

View File

@ -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]

View File

@ -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);
},

View File

@ -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"]) {

View 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);

View File

@ -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);

View File

@ -5,6 +5,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXTRA_JS_MODULES += [
'DragPositionManager.jsm',
'ScrollbarSampler.jsm',
]

View File

@ -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]

View File

@ -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);

View File

@ -1,5 +1,6 @@
[DEFAULT]
support-files =
browser_font.woff
browser_fontinspector.html
[browser_font.woff]
[browser_fontinspector.html]
[browser_fontinspector.js]

View File

@ -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) {}

View File

@ -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]

View File

@ -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);
}

View File

@ -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));
}

View File

@ -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]

View File

@ -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",

View File

@ -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]

View File

@ -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();
}
});

View File

@ -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"/>

View File

@ -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]

View File

@ -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();
});
}

View File

@ -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");

View File

@ -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]

View File

@ -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) {

View File

@ -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
View File

View 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]

View File

@ -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

View File

@ -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");

View File

@ -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.

View File

@ -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();

View File

@ -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;
}

View File

@ -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. */

View File

@ -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...

View File

@ -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)

View File

@ -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)) },

View File

@ -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]

View File

@ -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());

View File

@ -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()

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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"

View File

@ -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" />

View File

@ -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" />

View File

@ -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" />

View File

@ -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>

View File

@ -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" />

View File

@ -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>

View File

@ -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">

View File

@ -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;

View File

@ -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) {

View File

@ -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");

View File

@ -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();

View File

@ -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();

View File

@ -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

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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();

View File

@ -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

View File

@ -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.

View File

@ -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) {

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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 {

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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");
}
});
},

View File

@ -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 });
});
}

View File

@ -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?

View File

@ -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

View File

@ -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));
}

View File

@ -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

View 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 + ")");
}

View File

@ -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]

View File

@ -1,4 +1,4 @@
[DEFAULT]
support-files = browser_clearplugindata.html
[browser_clearplugindata.html]
[browser_clearplugindata.js]

View File

@ -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);

View File

@ -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;
};

View File

@ -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