mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
Merge mozilla-central to mozilla-inbound. a=merge CLOSED TREE
This commit is contained in:
commit
8dd2a49ca4
29
.eslintrc.js
29
.eslintrc.js
@ -54,5 +54,34 @@ module.exports = {
|
||||
"varsIgnorePattern": "^Cc|Ci|Cu|Cr|EXPORTED_SYMBOLS"
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
// XXX Bug 1433175. These directories are still being fixed, so turn off
|
||||
// mozilla/use-cc-etc for now.
|
||||
"files": [
|
||||
"accessible/**",
|
||||
"browser/**",
|
||||
"devtools/**",
|
||||
"dom/**",
|
||||
"extensions/pref/**",
|
||||
"mobile/android/**",
|
||||
"security/manager/**",
|
||||
"services/**",
|
||||
"storage/test/**",
|
||||
"testing/**",
|
||||
"toolkit/**",
|
||||
"xpcom/**",
|
||||
],
|
||||
"rules": {
|
||||
"mozilla/use-cc-etc": "off",
|
||||
}
|
||||
}, {
|
||||
// XXX Bug 1436303. These directories are still being fixed, so turn off
|
||||
// mozilla/no-cc-etc for now.
|
||||
"files": [
|
||||
"devtools/**"
|
||||
],
|
||||
"rules": {
|
||||
"mozilla/no-define-cc-etc": "off",
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
@ -584,20 +584,20 @@ var PlacesCommandHook = {
|
||||
|
||||
/**
|
||||
* Opens the Places Organizer.
|
||||
* @param aLeftPaneRoot
|
||||
* The query to select in the organizer window - options
|
||||
* are: History, AllBookmarks, BookmarksMenu, BookmarksToolbar,
|
||||
* UnfiledBookmarks, Tags and Downloads.
|
||||
* @param {String} item The item to select in the organizer window,
|
||||
* options are (case sensitive):
|
||||
* BookmarksMenu, BookmarksToolbar, UnfiledBookmarks,
|
||||
* AllBookmarks, History, Downloads.
|
||||
*/
|
||||
showPlacesOrganizer: function PCH_showPlacesOrganizer(aLeftPaneRoot) {
|
||||
showPlacesOrganizer(item) {
|
||||
var organizer = Services.wm.getMostRecentWindow("Places:Organizer");
|
||||
// Due to bug 528706, getMostRecentWindow can return closed windows.
|
||||
if (!organizer || organizer.closed) {
|
||||
// No currently open places window, so open one with the specified mode.
|
||||
openDialog("chrome://browser/content/places/places.xul",
|
||||
"", "chrome,toolbar=yes,dialog=no,resizable", aLeftPaneRoot);
|
||||
"", "chrome,toolbar=yes,dialog=no,resizable", item);
|
||||
} else {
|
||||
organizer.PlacesOrganizer.selectLeftPaneContainerByHierarchy(aLeftPaneRoot);
|
||||
organizer.PlacesOrganizer.selectLeftPaneContainerByHierarchy(item);
|
||||
organizer.focus();
|
||||
}
|
||||
},
|
||||
|
@ -2,8 +2,6 @@
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
var {Ci: interfaces, Cc: classes} = Components;
|
||||
|
||||
var HasFindClipboard = Services.clipboard.supportsFindClipboard();
|
||||
|
||||
add_task(async function() {
|
||||
|
@ -50,8 +50,6 @@ const startupPhases = {
|
||||
// We are at this phase after creating the first browser window (ie. after final-ui-startup).
|
||||
"before opening first browser window": {blacklist: {
|
||||
modules: new Set([
|
||||
"resource://gre/modules/PlacesBackups.jsm",
|
||||
"resource://gre/modules/PlacesUtils.jsm",
|
||||
])
|
||||
}},
|
||||
|
||||
@ -74,6 +72,7 @@ const startupPhases = {
|
||||
"resource:///modules/DirectoryLinksProvider.jsm",
|
||||
"resource://gre/modules/NewTabUtils.jsm",
|
||||
"resource://gre/modules/PageThumbs.jsm",
|
||||
"resource://gre/modules/PlacesUtils.jsm",
|
||||
"resource://gre/modules/Promise.jsm", // imported by devtools during _delayedStartup
|
||||
"resource://gre/modules/Preferences.jsm",
|
||||
]),
|
||||
@ -100,6 +99,7 @@ const startupPhases = {
|
||||
"resource://gre/modules/CrashSubmit.jsm",
|
||||
"resource://gre/modules/FxAccounts.jsm",
|
||||
"resource://gre/modules/FxAccountsStorage.jsm",
|
||||
"resource://gre/modules/PlacesBackups.jsm",
|
||||
"resource://gre/modules/PlacesSyncUtils.jsm",
|
||||
"resource://gre/modules/Sqlite.jsm",
|
||||
]),
|
||||
|
@ -205,7 +205,7 @@ let InternalFaviconLoader = {
|
||||
};
|
||||
|
||||
this.PlacesUIUtils = {
|
||||
ORGANIZER_LEFTPANE_VERSION: 7,
|
||||
ORGANIZER_LEFTPANE_VERSION: 8,
|
||||
ORGANIZER_FOLDER_ANNO: "PlacesOrganizer/OrganizerFolder",
|
||||
ORGANIZER_QUERY_ANNO: "PlacesOrganizer/OrganizerQuery",
|
||||
|
||||
@ -513,6 +513,16 @@ this.PlacesUIUtils = {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is it a query pointing to one of the special root folders?
|
||||
if (PlacesUtils.nodeIsQuery(parentNode) && PlacesUtils.nodeIsFolder(aNode)) {
|
||||
let guid = PlacesUtils.getConcreteItemGuid(aNode);
|
||||
// If the parent folder is not a folder, it must be a query, and so this node
|
||||
// cannot be removed.
|
||||
if (PlacesUtils.isRootItem(guid)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If it's not a bookmark, we can remove it unless it's a child of a
|
||||
// livemark.
|
||||
if (aNode.itemId == -1) {
|
||||
@ -563,7 +573,7 @@ this.PlacesUIUtils = {
|
||||
view.controller.hasCachedLivemarkInfo(placesNode))
|
||||
return true;
|
||||
|
||||
// leftPaneFolderId, and as a result, allBookmarksFolderId, is a lazy getter
|
||||
// leftPaneFolderId is a lazy getter
|
||||
// performing at least a synchronous DB query (and on its very first call
|
||||
// in a fresh profile, it also creates the entire structure).
|
||||
// Therefore we don't want to this function, which is called very often by
|
||||
@ -577,8 +587,7 @@ this.PlacesUIUtils = {
|
||||
if (typeof Object.getOwnPropertyDescriptor(this, "leftPaneFolderId").get == "function") {
|
||||
return false;
|
||||
}
|
||||
return itemId == this.leftPaneFolderId ||
|
||||
itemId == this.allBookmarksFolderId;
|
||||
return itemId == this.leftPaneFolderId;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -839,7 +848,6 @@ this.PlacesUIUtils = {
|
||||
// Get the folder id for the organizer left-pane folder.
|
||||
maybeRebuildLeftPane() {
|
||||
let leftPaneRoot = -1;
|
||||
let allBookmarksId;
|
||||
|
||||
// Shortcuts to services.
|
||||
let bs = PlacesUtils.bookmarks;
|
||||
@ -852,21 +860,9 @@ this.PlacesUIUtils = {
|
||||
"Downloads": { title: this.getString("OrganizerQueryDownloads") },
|
||||
"Tags": { title: this.getString("OrganizerQueryTags") },
|
||||
"AllBookmarks": { title: this.getString("OrganizerQueryAllBookmarks") },
|
||||
"BookmarksToolbar":
|
||||
{ title: "",
|
||||
concreteTitle: PlacesUtils.getString("BookmarksToolbarFolderTitle"),
|
||||
concreteId: PlacesUtils.toolbarFolderId },
|
||||
"BookmarksMenu":
|
||||
{ title: "",
|
||||
concreteTitle: PlacesUtils.getString("BookmarksMenuFolderTitle"),
|
||||
concreteId: PlacesUtils.bookmarksMenuFolderId },
|
||||
"UnfiledBookmarks":
|
||||
{ title: "",
|
||||
concreteTitle: PlacesUtils.getString("OtherBookmarksFolderTitle"),
|
||||
concreteId: PlacesUtils.unfiledBookmarksFolderId },
|
||||
};
|
||||
// All queries but PlacesRoot.
|
||||
const EXPECTED_QUERY_COUNT = 7;
|
||||
const EXPECTED_QUERY_COUNT = 4;
|
||||
|
||||
// Removes an item and associated annotations, ignoring eventual errors.
|
||||
function safeRemoveItem(aItemId) {
|
||||
@ -1053,19 +1049,9 @@ this.PlacesUIUtils = {
|
||||
Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_ASCENDING);
|
||||
|
||||
// All Bookmarks Folder.
|
||||
allBookmarksId = this.create_folder("AllBookmarks", leftPaneRoot, false);
|
||||
|
||||
// All Bookmarks->Bookmarks Toolbar Query.
|
||||
this.create_query("BookmarksToolbar", allBookmarksId,
|
||||
"place:folder=TOOLBAR");
|
||||
|
||||
// All Bookmarks->Bookmarks Menu Query.
|
||||
this.create_query("BookmarksMenu", allBookmarksId,
|
||||
"place:folder=BOOKMARKS_MENU");
|
||||
|
||||
// All Bookmarks->Unfiled Bookmarks Query.
|
||||
this.create_query("UnfiledBookmarks", allBookmarksId,
|
||||
"place:folder=UNFILED_BOOKMARKS");
|
||||
this.create_query("AllBookmarks", leftPaneRoot,
|
||||
"place:type=" +
|
||||
Ci.nsINavHistoryQueryOptions.RESULTS_AS_ROOTS_QUERY);
|
||||
}
|
||||
};
|
||||
bs.runInBatchMode(callback, null);
|
||||
@ -1073,16 +1059,6 @@ this.PlacesUIUtils = {
|
||||
return leftPaneRoot;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the folder id for the organizer left-pane folder.
|
||||
*/
|
||||
get allBookmarksFolderId() {
|
||||
// ensure the left-pane root is initialized;
|
||||
this.leftPaneFolderId;
|
||||
delete this.allBookmarksFolderId;
|
||||
return this.allBookmarksFolderId = this.leftPaneQueries.AllBookmarks;
|
||||
},
|
||||
|
||||
/**
|
||||
* If an item is a left-pane query, returns the name of the query
|
||||
* or an empty string if not.
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
function init() {
|
||||
document.getElementById("bookmarks-view").place =
|
||||
"place:queryType=1&folder=" + window.top.PlacesUIUtils.allBookmarksFolderId;
|
||||
"place:type=" + Ci.nsINavHistoryQueryOptions.RESULTS_AS_ROOTS_QUERY;
|
||||
}
|
||||
|
||||
function searchBookmarks(aSearchString) {
|
||||
|
@ -1377,7 +1377,8 @@ var PlacesControllerDragHelper = {
|
||||
* @return True if the node can be moved, false otherwise.
|
||||
*/
|
||||
canMoveUnwrappedNode(unwrappedNode) {
|
||||
if (unwrappedNode.id <= 0 || PlacesUtils.isRootItem(unwrappedNode.id)) {
|
||||
if ((unwrappedNode.concreteGuid && PlacesUtils.isRootItem(unwrappedNode.concreteGuid)) ||
|
||||
unwrappedNode.id <= 0 || PlacesUtils.isRootItem(unwrappedNode.id)) {
|
||||
return false;
|
||||
}
|
||||
let parentId = unwrappedNode.parent;
|
||||
@ -1392,8 +1393,7 @@ var PlacesControllerDragHelper = {
|
||||
// them first, especially because isCommandEnabled may be called way
|
||||
// before the left pane folder is even necessary.
|
||||
if (typeof Object.getOwnPropertyDescriptor(PlacesUIUtils, "leftPaneFolderId").get != "function" &&
|
||||
(parentId == PlacesUIUtils.leftPaneFolderId ||
|
||||
parentId == PlacesUIUtils.allBookmarksFolderId)) {
|
||||
(parentId == PlacesUIUtils.leftPaneFolderId)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -60,8 +60,7 @@ var gEditItemOverlay = {
|
||||
let folderId = PlacesUtils.getConcreteItemId(parent);
|
||||
isParentReadOnly = folderId == PlacesUtils.placesRootId ||
|
||||
(!("get" in Object.getOwnPropertyDescriptor(PlacesUIUtils, "leftPaneFolderId")) &&
|
||||
(folderId == PlacesUIUtils.leftPaneFolderId ||
|
||||
folderId == PlacesUIUtils.allBookmarksFolderId));
|
||||
(folderId == PlacesUIUtils.leftPaneFolderId));
|
||||
}
|
||||
parentId = parent.itemId;
|
||||
parentGuid = parent.bookmarkGuid;
|
||||
@ -704,13 +703,13 @@ var gEditItemOverlay = {
|
||||
// the editable mode set on this tree, together with its collapsed state
|
||||
// breaks the view.
|
||||
const FOLDER_TREE_PLACE_URI =
|
||||
"place:excludeItems=1&excludeQueries=1&excludeReadOnlyFolders=1&folder=" +
|
||||
PlacesUIUtils.allBookmarksFolderId;
|
||||
"place:excludeItems=1&excludeQueries=1&excludeReadOnlyFolders=1&type=" +
|
||||
Ci.nsINavHistoryQueryOptions.RESULTS_AS_ROOTS_QUERY;
|
||||
this._folderTree.place = FOLDER_TREE_PLACE_URI;
|
||||
|
||||
this._element("chooseFolderSeparator").hidden =
|
||||
this._element("chooseFolderMenuItem").hidden = true;
|
||||
this._folderTree.selectItems([this._paneInfo.parentId]);
|
||||
this._folderTree.selectItems([this._paneInfo.parentGuid]);
|
||||
this._folderTree.focus();
|
||||
}
|
||||
},
|
||||
@ -929,7 +928,7 @@ var gEditItemOverlay = {
|
||||
let ip = this._folderTree.insertionPoint;
|
||||
|
||||
// default to the bookmarks menu folder
|
||||
if (!ip || ip.itemId == PlacesUIUtils.allBookmarksFolderId) {
|
||||
if (!ip) {
|
||||
ip = new InsertionPoint({
|
||||
parentId: PlacesUtils.bookmarksMenuFolderId,
|
||||
parentGuid: PlacesUtils.bookmarks.menuGuid
|
||||
|
@ -41,28 +41,63 @@ var PlacesOrganizer = {
|
||||
this._places.place = "place:excludeItems=1&expandQueries=0&folder=" + leftPaneRoot;
|
||||
},
|
||||
|
||||
selectLeftPaneQuery: function PO_selectLeftPaneQuery(aQueryName) {
|
||||
var itemId = PlacesUIUtils.leftPaneQueries[aQueryName];
|
||||
this._places.selectItems([itemId]);
|
||||
// Forcefully expand all-bookmarks
|
||||
if (aQueryName == "AllBookmarks" || aQueryName == "History")
|
||||
PlacesUtils.asContainer(this._places.selectedNode).containerOpen = true;
|
||||
/**
|
||||
* Selects a left pane built-in item.
|
||||
*
|
||||
* @param {String} item The built-in item to select, may be one of (case sensitive):
|
||||
* AllBookmarks, BookmarksMenu, BookmarksToolbar,
|
||||
* History, Downloads, Tags, UnfiledBookmarks.
|
||||
*/
|
||||
selectLeftPaneBuiltIn(item) {
|
||||
switch (item) {
|
||||
case "AllBookmarks":
|
||||
case "History":
|
||||
case "Downloads":
|
||||
case "Tags": {
|
||||
var itemId = PlacesUIUtils.leftPaneQueries[item];
|
||||
this._places.selectItems([itemId]);
|
||||
// Forcefully expand all-bookmarks
|
||||
if (item == "AllBookmarks" || item == "History")
|
||||
PlacesUtils.asContainer(this._places.selectedNode).containerOpen = true;
|
||||
break;
|
||||
}
|
||||
case "BookmarksMenu":
|
||||
this.selectLeftPaneContainerByHierarchy([
|
||||
PlacesUIUtils.leftPaneQueries.AllBookmarks,
|
||||
PlacesUtils.bookmarks.virtualMenuGuid
|
||||
]);
|
||||
break;
|
||||
case "BookmarksToolbar":
|
||||
this.selectLeftPaneContainerByHierarchy([
|
||||
PlacesUIUtils.leftPaneQueries.AllBookmarks,
|
||||
PlacesUtils.bookmarks.virtualToolbarGuid
|
||||
]);
|
||||
break;
|
||||
case "UnfiledBookmarks":
|
||||
this.selectLeftPaneContainerByHierarchy([
|
||||
PlacesUIUtils.leftPaneQueries.AllBookmarks,
|
||||
PlacesUtils.bookmarks.virtualUnfiledGuid
|
||||
]);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unrecognized item ${item} passed to selectLeftPaneRootItem`);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens a given hierarchy in the left pane, stopping at the last reachable
|
||||
* container.
|
||||
* container. Note: item ids should be considered deprecated.
|
||||
*
|
||||
* @param aHierarchy A single container or an array of containers, sorted from
|
||||
* the outmost to the innermost in the hierarchy. Each
|
||||
* container may be either an item id, a Places URI string,
|
||||
* or a named query.
|
||||
* or a named query, like:
|
||||
* "BookmarksMenu", "BookmarksToolbar", "UnfiledBookmarks", "AllBookmarks".
|
||||
* @see PlacesUIUtils.leftPaneQueries for supported named queries.
|
||||
*/
|
||||
selectLeftPaneContainerByHierarchy:
|
||||
function PO_selectLeftPaneContainerByHierarchy(aHierarchy) {
|
||||
selectLeftPaneContainerByHierarchy(aHierarchy) {
|
||||
if (!aHierarchy)
|
||||
throw new Error("Invalid containers hierarchy");
|
||||
throw new Error("Containers hierarchy not specified");
|
||||
let hierarchy = [].concat(aHierarchy);
|
||||
let selectWasSuppressed = this._places.view.selection.selectEventsSuppressed;
|
||||
if (!selectWasSuppressed)
|
||||
@ -74,12 +109,16 @@ var PlacesOrganizer = {
|
||||
this._places.selectItems([container], false);
|
||||
break;
|
||||
case "string":
|
||||
if (container.substr(0, 6) == "place:")
|
||||
this._places.selectPlaceURI(container);
|
||||
else if (container in PlacesUIUtils.leftPaneQueries)
|
||||
this.selectLeftPaneQuery(container);
|
||||
else
|
||||
throw new Error("Invalid container found: " + container);
|
||||
try {
|
||||
this.selectLeftPaneBuiltIn(container);
|
||||
} catch (ex) {
|
||||
if (container.substr(0, 6) == "place:") {
|
||||
this._places.selectPlaceURI(container);
|
||||
} else {
|
||||
// May be a guid.
|
||||
this._places.selectItems([container], false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error("Invalid container type found: " + container);
|
||||
|
@ -76,7 +76,8 @@
|
||||
// tag containers, so we must fall to the default case.
|
||||
if (PlacesUtils.nodeIsHistoryContainer(queryNode) ||
|
||||
options.resultType == options.RESULTS_AS_TAG_QUERY ||
|
||||
options.resultType == options.RESULTS_AS_TAG_CONTENTS)
|
||||
options.resultType == options.RESULTS_AS_TAG_CONTENTS ||
|
||||
options.resultType == options.RESULTS_AS_ROOTS_QUERY)
|
||||
options.resultType = options.RESULTS_AS_URI;
|
||||
|
||||
var query = PlacesUtils.history.getNewQuery();
|
||||
@ -621,8 +622,12 @@
|
||||
checkedGuidsSet.has(concreteGuid))
|
||||
return foundOne;
|
||||
|
||||
// Only follow a query if it has been been explicitly opened by the caller.
|
||||
let shouldOpen = aOpenContainers && PlacesUtils.nodeIsFolder(node);
|
||||
// Only follow a query if it has been been explicitly opened by the
|
||||
// caller. We support the "AllBookmarks" case to allow callers to
|
||||
// specify just the top-level bookmark folders.
|
||||
let shouldOpen = aOpenContainers && (PlacesUtils.nodeIsFolder(node) ||
|
||||
(PlacesUtils.nodeIsQuery(node) && node.itemId == PlacesUIUtils.leftPaneQueries.AllBookmarks));
|
||||
|
||||
PlacesUtils.asContainer(node);
|
||||
if (!node.containerOpen && !shouldOpen)
|
||||
return foundOne;
|
||||
|
@ -137,6 +137,7 @@ PlacesTreeView.prototype = {
|
||||
case Ci.nsINavHistoryQueryOptions.RESULTS_AS_SITE_QUERY:
|
||||
case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_SITE_QUERY:
|
||||
case Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY:
|
||||
case Ci.nsINavHistoryQueryOptions.RESULTS_AS_ROOTS_QUERY:
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -195,8 +196,9 @@ PlacesTreeView.prototype = {
|
||||
let parent = aNode.parent;
|
||||
let parentIsPlain = this._isPlainContainer(parent);
|
||||
if (!parentIsPlain) {
|
||||
if (parent == this._rootNode)
|
||||
if (parent == this._rootNode) {
|
||||
return this._rows.indexOf(aNode);
|
||||
}
|
||||
|
||||
return this._rows.indexOf(aNode, aParentRow);
|
||||
}
|
||||
@ -1276,21 +1278,35 @@ PlacesTreeView.prototype = {
|
||||
properties += " hostContainer";
|
||||
} else if (nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER ||
|
||||
nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT) {
|
||||
if (this._controller.hasCachedLivemarkInfo(node)) {
|
||||
properties += " livemark";
|
||||
} else {
|
||||
PlacesUtils.livemarks.getLivemark({ id: node.itemId })
|
||||
.then(aLivemark => {
|
||||
this._controller.cacheLivemarkInfo(node, aLivemark);
|
||||
let livemarkProps = this._cellProperties.get(node);
|
||||
this._cellProperties.set(node, livemarkProps += " livemark");
|
||||
// The livemark attribute is set as a cell property on the title cell.
|
||||
this._invalidateCellValue(node, this.COLUMN_TYPE_TITLE);
|
||||
}, () => undefined);
|
||||
if (itemId != -1) {
|
||||
if (this._controller.hasCachedLivemarkInfo(node)) {
|
||||
properties += " livemark";
|
||||
} else {
|
||||
PlacesUtils.livemarks.getLivemark({ id: itemId })
|
||||
.then(aLivemark => {
|
||||
this._controller.cacheLivemarkInfo(node, aLivemark);
|
||||
let livemarkProps = this._cellProperties.get(node);
|
||||
this._cellProperties.set(node, livemarkProps += " livemark");
|
||||
// The livemark attribute is set as a cell property on the title cell.
|
||||
this._invalidateCellValue(node, this.COLUMN_TYPE_TITLE);
|
||||
}, () => undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (itemId != -1) {
|
||||
if (itemId == -1) {
|
||||
switch (node.bookmarkGuid) {
|
||||
case PlacesUtils.bookmarks.virtualToolbarGuid:
|
||||
properties += ` queryFolder_${PlacesUtils.bookmarks.toolbarGuid}`;
|
||||
break;
|
||||
case PlacesUtils.bookmarks.virtualMenuGuid:
|
||||
properties += ` queryFolder_${PlacesUtils.bookmarks.menuGuid}`;
|
||||
break;
|
||||
case PlacesUtils.bookmarks.virtualUnfiledGuid:
|
||||
properties += ` queryFolder_${PlacesUtils.bookmarks.unfiledGuid}`;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
let queryName = PlacesUIUtils.getLeftPaneQueryNameFromId(itemId);
|
||||
if (queryName)
|
||||
properties += " OrganizerQuery_" + queryName;
|
||||
@ -1764,12 +1780,12 @@ PlacesTreeView.prototype = {
|
||||
// Note that concrete itemIds aren't used intentionally. For example, we
|
||||
// have no reason to disallow renaming a shortcut to the Bookmarks Toolbar,
|
||||
// except for the one under All Bookmarks.
|
||||
if (PlacesUtils.nodeIsSeparator(node) || PlacesUtils.isRootItem(itemGuid))
|
||||
if (PlacesUtils.nodeIsSeparator(node) || PlacesUtils.isRootItem(itemGuid) ||
|
||||
PlacesUtils.isQueryGeneratedFolder(itemGuid))
|
||||
return false;
|
||||
|
||||
let parentId = PlacesUtils.getConcreteItemId(node.parent);
|
||||
if (parentId == PlacesUIUtils.leftPaneFolderId ||
|
||||
parentId == PlacesUIUtils.allBookmarksFolderId) {
|
||||
if (parentId == PlacesUIUtils.leftPaneFolderId) {
|
||||
// Note that the for the time being this is the check that actually
|
||||
// blocks renaming places "roots", and not the isRootItem check above.
|
||||
// That's because places root are only exposed through folder shortcuts
|
||||
|
@ -78,7 +78,7 @@ add_task(async function() {
|
||||
"Left pane version has been correctly upgraded");
|
||||
|
||||
// Check left pane is populated.
|
||||
organizer.PlacesOrganizer.selectLeftPaneQuery("History");
|
||||
organizer.PlacesOrganizer.selectLeftPaneBuiltIn("History");
|
||||
is(organizer.PlacesOrganizer._places.selectedNode.itemId,
|
||||
PlacesUIUtils.leftPaneQueries.History,
|
||||
"Library left pane is populated and working");
|
||||
|
@ -19,7 +19,7 @@ add_task(async function() {
|
||||
await promiseLibraryClosed(library);
|
||||
});
|
||||
|
||||
PlacesOrganizer.selectLeftPaneQuery("Tags");
|
||||
PlacesOrganizer.selectLeftPaneBuiltIn("Tags");
|
||||
let tree = PlacesOrganizer._places;
|
||||
let tagsContainer = tree.selectedNode;
|
||||
tagsContainer.containerOpen = true;
|
||||
|
@ -29,10 +29,6 @@ add_task(async function() {
|
||||
namepicker.blur();
|
||||
bookmark = await PlacesUtils.bookmarks.fetch(PlacesUtils.bookmarks.unfiledGuid);
|
||||
Assert.equal(namepicker.value, bookmark.title, "Root title is correct");
|
||||
// Check the shortcut's title.
|
||||
info(tree.selectedNode.bookmarkGuid);
|
||||
bookmark = await PlacesUtils.bookmarks.fetch(tree.selectedNode.bookmarkGuid);
|
||||
Assert.equal(bookmark.title, "", "Shortcut title is null");
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -85,15 +85,16 @@ add_task(async function() {
|
||||
|
||||
info("Test that special folders and cannot be moved but other shortcuts can.");
|
||||
let roots = [
|
||||
PlacesUtils.bookmarksMenuFolderId,
|
||||
PlacesUtils.unfiledBookmarksFolderId,
|
||||
PlacesUtils.toolbarFolderId,
|
||||
PlacesUtils.bookmarks.menuGuid,
|
||||
PlacesUtils.bookmarks.unfiledGuid,
|
||||
PlacesUtils.bookmarks.toolbarGuid,
|
||||
];
|
||||
|
||||
for (let id of roots) {
|
||||
selectShortcutForRootId(tree, id);
|
||||
for (let guid of roots) {
|
||||
tree.selectItems([guid]);
|
||||
Assert.ok(!PlacesControllerDragHelper.canMoveNode(tree.selectedNode, tree),
|
||||
"shouldn't be able to move default shortcuts to roots");
|
||||
let id = await PlacesUtils.promiseItemId(guid);
|
||||
let s = await PlacesUtils.bookmarks.insert({
|
||||
parentGuid: root.guid,
|
||||
title: "bar",
|
||||
@ -107,14 +108,3 @@ add_task(async function() {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function selectShortcutForRootId(tree, id) {
|
||||
for (let i = 0; i < tree.result.root.childCount; ++i) {
|
||||
let child = tree.result.root.getChild(i);
|
||||
if (PlacesUtils.getConcreteItemId(child) == id) {
|
||||
tree.selectItems([child.itemId]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Assert.ok(false, "Cannot find shortcut to root");
|
||||
}
|
||||
|
@ -17,12 +17,12 @@ add_task(async function copy_toolbar_shortcut() {
|
||||
PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId);
|
||||
});
|
||||
|
||||
library.PlacesOrganizer.selectLeftPaneQuery("BookmarksToolbar");
|
||||
library.PlacesOrganizer.selectLeftPaneBuiltIn("BookmarksToolbar");
|
||||
|
||||
await promiseClipboard(function() { library.PlacesOrganizer._places.controller.copy(); },
|
||||
PlacesUtils.TYPE_X_MOZ_PLACE);
|
||||
|
||||
library.PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
|
||||
library.PlacesOrganizer.selectLeftPaneBuiltIn("UnfiledBookmarks");
|
||||
|
||||
await library.ContentTree.view.controller.paste();
|
||||
|
||||
@ -32,7 +32,7 @@ add_task(async function copy_toolbar_shortcut() {
|
||||
"copy is still a folder shortcut");
|
||||
|
||||
PlacesUtils.bookmarks.removeItem(toolbarCopyNode.itemId);
|
||||
library.PlacesOrganizer.selectLeftPaneQuery("BookmarksToolbar");
|
||||
library.PlacesOrganizer.selectLeftPaneBuiltIn("BookmarksToolbar");
|
||||
is(library.PlacesOrganizer._places.selectedNode.type,
|
||||
Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT,
|
||||
"original is still a folder shortcut");
|
||||
@ -41,12 +41,12 @@ add_task(async function copy_toolbar_shortcut() {
|
||||
add_task(async function copy_history_query() {
|
||||
let library = await promiseLibrary();
|
||||
|
||||
library.PlacesOrganizer.selectLeftPaneQuery("History");
|
||||
library.PlacesOrganizer.selectLeftPaneBuiltIn("History");
|
||||
|
||||
await promiseClipboard(function() { library.PlacesOrganizer._places.controller.copy(); },
|
||||
PlacesUtils.TYPE_X_MOZ_PLACE);
|
||||
|
||||
library.PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
|
||||
library.PlacesOrganizer.selectLeftPaneBuiltIn("UnfiledBookmarks");
|
||||
await library.ContentTree.view.controller.paste();
|
||||
|
||||
let historyCopyNode = library.ContentTree.view.view.nodeForTreeIndex(0);
|
||||
@ -55,7 +55,7 @@ add_task(async function copy_history_query() {
|
||||
"copy is still a query");
|
||||
|
||||
PlacesUtils.bookmarks.removeItem(historyCopyNode.itemId);
|
||||
library.PlacesOrganizer.selectLeftPaneQuery("History");
|
||||
library.PlacesOrganizer.selectLeftPaneBuiltIn("History");
|
||||
is(library.PlacesOrganizer._places.selectedNode.type,
|
||||
Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY,
|
||||
"original is still a query");
|
||||
|
@ -46,7 +46,7 @@ add_task(async function() {
|
||||
}, PlacesUtils.TYPE_X_MOZ_PLACE);
|
||||
|
||||
info("Selecting UnfiledBookmarks in the left pane");
|
||||
PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
|
||||
PlacesOrganizer.selectLeftPaneBuiltIn("UnfiledBookmarks");
|
||||
info("Pasting clipboard");
|
||||
await ContentTree.view.controller.paste();
|
||||
|
||||
@ -57,7 +57,7 @@ var selectBookmarksIn = async function(organizer, bookmarks, aLeftPaneQuery) {
|
||||
let PlacesOrganizer = organizer.PlacesOrganizer;
|
||||
let ContentTree = organizer.ContentTree;
|
||||
info("Selecting " + aLeftPaneQuery + " in the left pane");
|
||||
PlacesOrganizer.selectLeftPaneQuery(aLeftPaneQuery);
|
||||
PlacesOrganizer.selectLeftPaneBuiltIn(aLeftPaneQuery);
|
||||
|
||||
for (let {guid} of bookmarks) {
|
||||
let bookmark = await PlacesUtils.bookmarks.fetch(guid);
|
||||
|
@ -31,7 +31,7 @@ var testForgetThisSiteVisibility = async function(selectionCount) {
|
||||
let organizer = await promiseLibrary();
|
||||
|
||||
// Select History in the left pane.
|
||||
organizer.PlacesOrganizer.selectLeftPaneQuery("History");
|
||||
organizer.PlacesOrganizer.selectLeftPaneBuiltIn("History");
|
||||
let PO = organizer.PlacesOrganizer;
|
||||
let histContainer = PO._places.selectedNode.QueryInterface(Ci.nsINavHistoryContainerResultNode);
|
||||
histContainer.containerOpen = true;
|
||||
|
@ -44,7 +44,7 @@ add_task(async function test_create_and_batch_remove_bookmarks() {
|
||||
|
||||
// Select and open the left pane "History" query.
|
||||
let PO = gLibrary.PlacesOrganizer;
|
||||
PO.selectLeftPaneQuery("UnfiledBookmarks");
|
||||
PO.selectLeftPaneBuiltIn("UnfiledBookmarks");
|
||||
Assert.notEqual(PO._places.selectedNode, null, "Selected unsorted bookmarks");
|
||||
|
||||
let unsortedNode = PlacesUtils.asContainer(PO._places.selectedNode);
|
||||
@ -76,8 +76,8 @@ add_task(async function test_ensure_correct_selection_and_functionality() {
|
||||
let PO = gLibrary.PlacesOrganizer;
|
||||
let ContentTree = gLibrary.ContentTree;
|
||||
// Move selection forth and back.
|
||||
PO.selectLeftPaneQuery("History");
|
||||
PO.selectLeftPaneQuery("UnfiledBookmarks");
|
||||
PO.selectLeftPaneBuiltIn("History");
|
||||
PO.selectLeftPaneBuiltIn("UnfiledBookmarks");
|
||||
// Now select the "keepme" folder in the right pane and delete it.
|
||||
ContentTree.view.selectNode(ContentTree.view.result.root.getChild(0));
|
||||
Assert.equal(ContentTree.view.selectedNode.title, "keepme",
|
||||
|
@ -24,7 +24,7 @@ add_task(async function test_date_container() {
|
||||
// Select and open the left pane "History" query.
|
||||
let PO = library.PlacesOrganizer;
|
||||
|
||||
PO.selectLeftPaneQuery("History");
|
||||
PO.selectLeftPaneBuiltIn("History");
|
||||
isnot(PO._places.selectedNode, null, "We correctly selected History");
|
||||
|
||||
// Check that both delete and cut commands are disabled, cause this is
|
||||
@ -81,14 +81,14 @@ add_task(async function test_query_on_toolbar() {
|
||||
// Select and open the left pane "Bookmarks Toolbar" folder.
|
||||
let PO = library.PlacesOrganizer;
|
||||
|
||||
PO.selectLeftPaneQuery("BookmarksToolbar");
|
||||
PO.selectLeftPaneBuiltIn("BookmarksToolbar");
|
||||
isnot(PO._places.selectedNode, null, "We have a valid selection");
|
||||
is(PlacesUtils.getConcreteItemId(PO._places.selectedNode),
|
||||
PlacesUtils.toolbarFolderId,
|
||||
"We have correctly selected bookmarks toolbar node.");
|
||||
|
||||
// Check that both cut and delete commands are disabled, cause this is a child
|
||||
// of AllBookmarksFolderId.
|
||||
// of the All Bookmarks special query.
|
||||
ok(PO._places.controller.isCommandEnabled("cmd_copy"),
|
||||
"Copy command is enabled");
|
||||
ok(!PO._places.controller.isCommandEnabled("cmd_cut"),
|
||||
@ -150,7 +150,7 @@ add_task(async function test_search_contents() {
|
||||
// Select and open the left pane "Bookmarks Toolbar" folder.
|
||||
let PO = library.PlacesOrganizer;
|
||||
|
||||
PO.selectLeftPaneQuery("BookmarksToolbar");
|
||||
PO.selectLeftPaneBuiltIn("BookmarksToolbar");
|
||||
isnot(PO._places.selectedNode, null, "We have a valid selection");
|
||||
is(PlacesUtils.getConcreteItemId(PO._places.selectedNode),
|
||||
PlacesUtils.toolbarFolderId,
|
||||
@ -188,7 +188,7 @@ add_task(async function test_tags() {
|
||||
// Select and open the left pane "Bookmarks Toolbar" folder.
|
||||
let PO = library.PlacesOrganizer;
|
||||
|
||||
PO.selectLeftPaneQuery("Tags");
|
||||
PO.selectLeftPaneBuiltIn("Tags");
|
||||
let tagsNode = PO._places.selectedNode;
|
||||
isnot(tagsNode, null, "We have a valid selection");
|
||||
let tagsTitle = PlacesUtils.getString("TagsFolderTitle");
|
||||
|
@ -45,7 +45,7 @@ add_task(async function test_tags() {
|
||||
// Select and open the left pane "Bookmarks Toolbar" folder.
|
||||
let PO = library.PlacesOrganizer;
|
||||
|
||||
PO.selectLeftPaneQuery("Tags");
|
||||
PO.selectLeftPaneBuiltIn("Tags");
|
||||
let tagsNode = PO._places.selectedNode;
|
||||
Assert.notEqual(tagsNode, null, "Should have a valid selection");
|
||||
let tagsTitle = PlacesUtils.getString("TagsFolderTitle");
|
||||
|
@ -28,7 +28,7 @@ add_task(async function test_tags() {
|
||||
// Select and open the left pane "Bookmarks Toolbar" folder.
|
||||
let PO = library.PlacesOrganizer;
|
||||
|
||||
PO.selectLeftPaneQuery("Tags");
|
||||
PO.selectLeftPaneBuiltIn("Tags");
|
||||
let tagsNode = PO._places.selectedNode;
|
||||
Assert.notEqual(tagsNode, null, "Should have a valid selection");
|
||||
let tagsTitle = PlacesUtils.getString("TagsFolderTitle");
|
||||
|
@ -25,7 +25,7 @@ add_task(async function() {
|
||||
await PlacesTestUtils.addVisits("http://www.mozilla.org/");
|
||||
|
||||
// open all bookmarks node
|
||||
PO.selectLeftPaneQuery("AllBookmarks");
|
||||
PO.selectLeftPaneBuiltIn("AllBookmarks");
|
||||
isnot(PO._places.selectedNode, null,
|
||||
"Correctly selected all bookmarks node.");
|
||||
checkInfoBoxSelected();
|
||||
@ -34,7 +34,7 @@ add_task(async function() {
|
||||
checkAddInfoFieldsCollapsed(PO);
|
||||
|
||||
// open history node
|
||||
PO.selectLeftPaneQuery("History");
|
||||
PO.selectLeftPaneBuiltIn("History");
|
||||
isnot(PO._places.selectedNode, null, "Correctly selected history node.");
|
||||
checkInfoBoxSelected();
|
||||
ok(infoBoxExpanderWrapper.hidden,
|
||||
@ -64,7 +64,7 @@ add_task(async function() {
|
||||
historyNode.containerOpen = false;
|
||||
|
||||
// open bookmarks menu node
|
||||
PO.selectLeftPaneQuery("BookmarksMenu");
|
||||
PO.selectLeftPaneBuiltIn("BookmarksMenu");
|
||||
isnot(PO._places.selectedNode, null,
|
||||
"Correctly selected bookmarks menu node.");
|
||||
checkInfoBoxSelected();
|
||||
@ -157,4 +157,3 @@ function getAndCheckElmtById(id) {
|
||||
isnot(elmt, null, "Correctly got element: #" + id);
|
||||
return elmt;
|
||||
}
|
||||
|
||||
|
@ -60,25 +60,10 @@ function test() {
|
||||
var query = { name: queryName,
|
||||
itemId,
|
||||
correctTitle: PlacesUtils.bookmarks.getItemTitle(itemId) };
|
||||
switch (queryName) {
|
||||
case "BookmarksToolbar":
|
||||
query.concreteId = PlacesUtils.toolbarFolderId;
|
||||
query.concreteTitle = PlacesUtils.bookmarks.getItemTitle(query.concreteId);
|
||||
break;
|
||||
case "BookmarksMenu":
|
||||
query.concreteId = PlacesUtils.bookmarksMenuFolderId;
|
||||
query.concreteTitle = PlacesUtils.bookmarks.getItemTitle(query.concreteId);
|
||||
break;
|
||||
case "UnfiledBookmarks":
|
||||
query.concreteId = PlacesUtils.unfiledBookmarksFolderId;
|
||||
query.concreteTitle = PlacesUtils.bookmarks.getItemTitle(query.concreteId);
|
||||
break;
|
||||
}
|
||||
|
||||
leftPaneQueries.push(query);
|
||||
// Rename to a bad title.
|
||||
PlacesUtils.bookmarks.setItemTitle(query.itemId, "badName");
|
||||
if ("concreteId" in query)
|
||||
PlacesUtils.bookmarks.setItemTitle(query.concreteId, "badName");
|
||||
}
|
||||
|
||||
restoreLeftPaneGetters();
|
||||
|
@ -22,7 +22,7 @@ add_task(async function test_setup() {
|
||||
|
||||
registerCleanupFunction(async () => {
|
||||
// We must close "Other Bookmarks" ready for other tests.
|
||||
gLibrary.PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
|
||||
gLibrary.PlacesOrganizer.selectLeftPaneBuiltIn("UnfiledBookmarks");
|
||||
gLibrary.PlacesOrganizer._places.selectedNode.containerOpen = false;
|
||||
|
||||
await PlacesUtils.bookmarks.eraseEverything();
|
||||
@ -51,7 +51,7 @@ add_task(async function test_open_folder_in_tabs() {
|
||||
});
|
||||
|
||||
// Select unsorted bookmarks root in the left pane.
|
||||
gLibrary.PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
|
||||
gLibrary.PlacesOrganizer.selectLeftPaneBuiltIn("UnfiledBookmarks");
|
||||
Assert.notEqual(gLibrary.PlacesOrganizer._places.selectedNode, null,
|
||||
"We correctly have selection in the Library left pane");
|
||||
|
||||
|
@ -29,7 +29,7 @@ add_task(async function test_setup() {
|
||||
|
||||
registerCleanupFunction(async () => {
|
||||
// We must close "Other Bookmarks" ready for other tests.
|
||||
gLibrary.PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
|
||||
gLibrary.PlacesOrganizer.selectLeftPaneBuiltIn("UnfiledBookmarks");
|
||||
gLibrary.PlacesOrganizer._places.selectedNode.containerOpen = false;
|
||||
|
||||
await PlacesUtils.bookmarks.eraseEverything();
|
||||
@ -53,7 +53,7 @@ gTests.push({
|
||||
});
|
||||
|
||||
// Select unsorted bookmarks root in the left pane.
|
||||
gLibrary.PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
|
||||
gLibrary.PlacesOrganizer.selectLeftPaneBuiltIn("UnfiledBookmarks");
|
||||
Assert.notEqual(gLibrary.PlacesOrganizer._places.selectedNode, null,
|
||||
"We correctly have selection in the Library left pane");
|
||||
|
||||
@ -94,7 +94,7 @@ gTests.push({
|
||||
});
|
||||
|
||||
// Select unsorted bookmarks root in the left pane.
|
||||
gLibrary.PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
|
||||
gLibrary.PlacesOrganizer.selectLeftPaneBuiltIn("UnfiledBookmarks");
|
||||
isnot(gLibrary.PlacesOrganizer._places.selectedNode, null,
|
||||
"We correctly have selection in the Library left pane");
|
||||
// Get our bookmark in the right pane.
|
||||
@ -149,10 +149,8 @@ gTests.push({
|
||||
url: queryString,
|
||||
});
|
||||
|
||||
gLibrary.PlacesOrganizer.selectLeftPaneQuery("Query");
|
||||
|
||||
// Select unsorted bookmarks root in the left pane.
|
||||
gLibrary.PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
|
||||
gLibrary.PlacesOrganizer.selectLeftPaneBuiltIn("UnfiledBookmarks");
|
||||
isnot(gLibrary.PlacesOrganizer._places.selectedNode, null,
|
||||
"We correctly have selection in the Library left pane");
|
||||
// Get our bookmark in the right pane.
|
||||
|
@ -20,16 +20,14 @@ add_task(async function() {
|
||||
}],
|
||||
});
|
||||
|
||||
let library = await promiseLibrary("AllBookmarks");
|
||||
let library = await promiseLibrary("UnfiledBookmarks");
|
||||
registerCleanupFunction(async function() {
|
||||
await promiseLibraryClosed(library);
|
||||
await PlacesUtils.bookmarks.eraseEverything();
|
||||
});
|
||||
|
||||
// Select unfiled later, to ensure it's closed.
|
||||
library.PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
|
||||
ok(!library.PlacesOrganizer._places.selectedNode.containerOpen,
|
||||
"Unfiled container is closed");
|
||||
// Ensure the container is closed.
|
||||
library.PlacesOrganizer._places.selectedNode.containerOpen = false;
|
||||
|
||||
let folderNode = library.ContentTree.view.view.nodeForTreeIndex(0);
|
||||
is(folderNode.bookmarkGuid, bookmarks[0].guid,
|
||||
|
@ -12,43 +12,37 @@
|
||||
* STRs: Open Library, select an history entry in History, close Library.
|
||||
* ISSUE: We were adding a bookmarks observer when editing a bookmark, when
|
||||
* selecting an history entry the panel was not un-initialized, and
|
||||
* since an histroy entry does not have an itemId, the observer was
|
||||
* since an history entry does not have an itemId, the observer was
|
||||
* never removed.
|
||||
*/
|
||||
|
||||
const TEST_URI = "http://www.mozilla.org/";
|
||||
|
||||
function test() {
|
||||
function onLibraryReady(organizer) {
|
||||
let contentTree = organizer.document.getElementById("placeContent");
|
||||
isnot(contentTree, null, "Sanity check: placeContent tree should exist");
|
||||
isnot(organizer.PlacesOrganizer, null, "Sanity check: PlacesOrganizer should exist");
|
||||
isnot(organizer.gEditItemOverlay, null, "Sanity check: gEditItemOverlay should exist");
|
||||
|
||||
ok(organizer.gEditItemOverlay.initialized, "gEditItemOverlay is initialized");
|
||||
isnot(organizer.gEditItemOverlay.itemId, -1, "Editing a bookmark");
|
||||
|
||||
// Select History in the left pane.
|
||||
organizer.PlacesOrganizer.selectLeftPaneQuery("History");
|
||||
// Select the first history entry.
|
||||
let selection = contentTree.view.selection;
|
||||
selection.clearSelection();
|
||||
selection.rangedSelect(0, 0, true);
|
||||
// Check the panel is editing the history entry.
|
||||
is(organizer.gEditItemOverlay.itemId, -1, "Editing an history entry");
|
||||
// Close Library window.
|
||||
organizer.close();
|
||||
// Clean up history.
|
||||
PlacesTestUtils.clearHistory().then(finish);
|
||||
}
|
||||
|
||||
waitForExplicitFinish();
|
||||
add_task(async function test_no_leak_closing_library_with_history_selected() {
|
||||
// Add an history entry.
|
||||
ok(PlacesUtils, "checking PlacesUtils, running in chrome context?");
|
||||
PlacesTestUtils.addVisits(
|
||||
{uri: Services.io.newURI(TEST_URI), visitDate: Date.now() * 1000,
|
||||
transition: PlacesUtils.history.TRANSITION_TYPED}
|
||||
).then(() => {
|
||||
openLibrary(onLibraryReady);
|
||||
});
|
||||
}
|
||||
await PlacesTestUtils.addVisits(TEST_URI);
|
||||
|
||||
let organizer = await promiseLibrary();
|
||||
|
||||
let contentTree = organizer.document.getElementById("placeContent");
|
||||
Assert.notEqual(contentTree, null, "Sanity check: placeContent tree should exist");
|
||||
Assert.notEqual(organizer.PlacesOrganizer, null, "Sanity check: PlacesOrganizer should exist");
|
||||
Assert.notEqual(organizer.gEditItemOverlay, null, "Sanity check: gEditItemOverlay should exist");
|
||||
|
||||
Assert.ok(organizer.gEditItemOverlay.initialized, "gEditItemOverlay is initialized");
|
||||
Assert.notEqual(organizer.gEditItemOverlay._paneInfo.itemGuid, "", "Editing a bookmark");
|
||||
|
||||
// Select History in the left pane.
|
||||
organizer.PlacesOrganizer.selectLeftPaneBuiltIn("History");
|
||||
// Select the first history entry.
|
||||
let selection = contentTree.view.selection;
|
||||
selection.clearSelection();
|
||||
selection.rangedSelect(0, 0, true);
|
||||
// Check the panel is editing the history entry.
|
||||
Assert.equal(organizer.gEditItemOverlay._paneInfo.itemGuid, "", "Editing an history entry");
|
||||
// Close Library window.
|
||||
organizer.close();
|
||||
|
||||
// Clean up history.
|
||||
await PlacesUtils.history.clear();
|
||||
});
|
||||
|
@ -32,7 +32,7 @@ var gLibrary;
|
||||
|
||||
var testCases = [
|
||||
function allBookmarksScope() {
|
||||
let defScope = getDefaultScope(PlacesUIUtils.allBookmarksFolderId);
|
||||
let defScope = getDefaultScope(PlacesUIUtils.leftPaneQueries.AllBookmarks);
|
||||
search(PlacesUIUtils.allBookmarksFolderId, "dummy", defScope);
|
||||
},
|
||||
|
||||
|
@ -24,7 +24,7 @@ add_task(async function setup() {
|
||||
|
||||
add_task(async function paste() {
|
||||
info("Selecting BookmarksToolbar in the left pane");
|
||||
PlacesOrganizer.selectLeftPaneQuery("BookmarksToolbar");
|
||||
PlacesOrganizer.selectLeftPaneBuiltIn("BookmarksToolbar");
|
||||
|
||||
let bookmark = await PlacesUtils.bookmarks.insert({
|
||||
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
@ -40,7 +40,7 @@ add_task(async function paste() {
|
||||
}, PlacesUtils.TYPE_X_MOZ_PLACE);
|
||||
|
||||
info("Selecting UnfiledBookmarks in the left pane");
|
||||
PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
|
||||
PlacesOrganizer.selectLeftPaneBuiltIn("UnfiledBookmarks");
|
||||
|
||||
info("Pasting clipboard");
|
||||
await ContentTree.view.controller.paste();
|
||||
@ -60,7 +60,7 @@ add_task(async function paste() {
|
||||
|
||||
add_task(async function paste_check_indexes() {
|
||||
info("Selecting BookmarksToolbar in the left pane");
|
||||
PlacesOrganizer.selectLeftPaneQuery("BookmarksToolbar");
|
||||
PlacesOrganizer.selectLeftPaneBuiltIn("BookmarksToolbar");
|
||||
|
||||
let copyChildren = [];
|
||||
let targetChildren = [];
|
||||
@ -98,7 +98,7 @@ add_task(async function paste_check_indexes() {
|
||||
}, PlacesUtils.TYPE_X_MOZ_PLACE);
|
||||
|
||||
info("Selecting UnfiledBookmarks in the left pane");
|
||||
PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
|
||||
PlacesOrganizer.selectLeftPaneBuiltIn("UnfiledBookmarks");
|
||||
|
||||
ContentTree.view.selectItems([targetBookmarks[4].guid]);
|
||||
|
||||
@ -137,7 +137,7 @@ add_task(async function paste_check_indexes() {
|
||||
|
||||
add_task(async function paste_check_indexes_same_folder() {
|
||||
info("Selecting BookmarksToolbar in the left pane");
|
||||
PlacesOrganizer.selectLeftPaneQuery("BookmarksToolbar");
|
||||
PlacesOrganizer.selectLeftPaneBuiltIn("BookmarksToolbar");
|
||||
|
||||
let copyChildren = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
@ -224,7 +224,7 @@ add_task(async function paste_from_different_instance() {
|
||||
Services.clipboard.setData(xferable, null, Ci.nsIClipboard.kGlobalClipboard);
|
||||
|
||||
info("Selecting UnfiledBookmarks in the left pane");
|
||||
PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
|
||||
PlacesOrganizer.selectLeftPaneBuiltIn("UnfiledBookmarks");
|
||||
|
||||
info("Pasting clipboard");
|
||||
await ContentTree.view.controller.paste();
|
||||
|
@ -46,7 +46,7 @@ add_task(async function() {
|
||||
await PlacesOrganizer._places.controller.paste();
|
||||
|
||||
// re-focus the history again
|
||||
PlacesOrganizer.selectLeftPaneQuery("History");
|
||||
PlacesOrganizer.selectLeftPaneBuiltIn("History");
|
||||
let histContainer = PlacesOrganizer._places.selectedNode;
|
||||
PlacesUtils.asContainer(histContainer);
|
||||
histContainer.containerOpen = true;
|
||||
@ -67,7 +67,7 @@ add_task(async function() {
|
||||
|
||||
// is the bookmark visible in the UI?
|
||||
// get the Unsorted Bookmarks node
|
||||
PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
|
||||
PlacesOrganizer.selectLeftPaneBuiltIn("UnfiledBookmarks");
|
||||
|
||||
// now we can see what is in the ContentTree tree
|
||||
let unsortedNode = ContentTree.view.view.nodeForTreeIndex(1);
|
||||
@ -87,7 +87,7 @@ add_task(async function() {
|
||||
});
|
||||
|
||||
function focusTag(PlacesOrganizer) {
|
||||
PlacesOrganizer.selectLeftPaneQuery("Tags");
|
||||
PlacesOrganizer.selectLeftPaneBuiltIn("Tags");
|
||||
let tags = PlacesOrganizer._places.selectedNode;
|
||||
tags.containerOpen = true;
|
||||
let fooTag = tags.getChild(0);
|
||||
@ -100,7 +100,7 @@ function focusTag(PlacesOrganizer) {
|
||||
|
||||
function copyHistNode(PlacesOrganizer, ContentTree) {
|
||||
// focus the history object
|
||||
PlacesOrganizer.selectLeftPaneQuery("History");
|
||||
PlacesOrganizer.selectLeftPaneBuiltIn("History");
|
||||
let histContainer = PlacesOrganizer._places.selectedNode;
|
||||
PlacesUtils.asContainer(histContainer);
|
||||
histContainer.containerOpen = true;
|
||||
|
@ -6,8 +6,7 @@ ChromeUtils.defineModuleGetter(this, "TestUtils",
|
||||
"resource://testing-common/TestUtils.jsm");
|
||||
|
||||
// We need to cache these before test runs...
|
||||
let leftPaneGetters = new Map([["leftPaneFolderId", null],
|
||||
["allBookmarksFolderId", null]]);
|
||||
let leftPaneGetters = new Map([["leftPaneFolderId", null]]);
|
||||
for (let [key, val] of leftPaneGetters) {
|
||||
if (!val) {
|
||||
let getter = Object.getOwnPropertyDescriptor(PlacesUIUtils, key).get;
|
||||
|
@ -46,8 +46,7 @@
|
||||
function runTest() {
|
||||
// We need to cache and restore the getters in order to simulate
|
||||
// Bug 510634.
|
||||
let leftPaneGetters = new Map([["leftPaneFolderId", null],
|
||||
["allBookmarksFolderId", null]]);
|
||||
let leftPaneGetters = new Map([["leftPaneFolderId", null]]);
|
||||
for (let [key, val] of leftPaneGetters) {
|
||||
if (!val) {
|
||||
let getter = Object.getOwnPropertyDescriptor(PlacesUIUtils, key).get;
|
||||
@ -78,20 +77,34 @@
|
||||
// Open All Bookmarks
|
||||
tree.selectItems([PlacesUIUtils.leftPaneQueries["AllBookmarks"]]);
|
||||
PlacesUtils.asContainer(tree.selectedNode).containerOpen = true;
|
||||
is(PlacesUIUtils.allBookmarksFolderId, tree.selectedNode.itemId,
|
||||
is(tree.selectedNode.uri,
|
||||
"place:type=" + Ci.nsINavHistoryQueryOptions.RESULTS_AS_ROOTS_QUERY +
|
||||
"&queryType=" + Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS,
|
||||
"Opened All Bookmarks");
|
||||
|
||||
["History", "Downloads", "Tags", "AllBookmarks", "BookmarksToolbar",
|
||||
"BookmarksMenu", "UnfiledBookmarks"].forEach(
|
||||
function(aQueryName, aRow) {
|
||||
let found = false;
|
||||
for (let i = 0; i < tree.view.rowCount && !found; i++) {
|
||||
rowProperties = tree.view.getCellProperties(i, titleColumn).split(" ");
|
||||
found = rowProperties.includes("OrganizerQuery_" + aQueryName);
|
||||
}
|
||||
ok(found, "OrganizerQuery_" + aQueryName + " is set");
|
||||
for (let queryName of ["History", "Downloads", "Tags", "AllBookmarks"]) {
|
||||
let found = false;
|
||||
for (let i = 0; i < tree.view.rowCount && !found; i++) {
|
||||
rowProperties = tree.view.getCellProperties(i, titleColumn).split(" ");
|
||||
found = rowProperties.includes("OrganizerQuery_" + queryName);
|
||||
}
|
||||
);
|
||||
ok(found, "OrganizerQuery_" + queryName + " is set");
|
||||
}
|
||||
|
||||
const folderGuids = [
|
||||
PlacesUtils.bookmarks.toolbarGuid,
|
||||
PlacesUtils.bookmarks.menuGuid,
|
||||
PlacesUtils.bookmarks.unfiledGuid,
|
||||
];
|
||||
|
||||
for (let guid of folderGuids) {
|
||||
let found = false;
|
||||
for (let i = 0; i < tree.view.rowCount && !found; i++) {
|
||||
rowProperties = tree.view.getCellProperties(i, titleColumn).split(" ");
|
||||
found = rowProperties.includes("queryFolder_" + guid);
|
||||
}
|
||||
ok(found, "queryFolder_" + guid + " is set");
|
||||
}
|
||||
|
||||
// Close the root node
|
||||
tree.result.root.containerOpen = false;
|
||||
|
@ -10,7 +10,6 @@
|
||||
|
||||
// Used to store the original leftPaneFolderId getter.
|
||||
var gLeftPaneFolderIdGetter;
|
||||
var gAllBookmarksFolderIdGetter;
|
||||
// Used to store the original left Pane status as a JSON string.
|
||||
var gReferenceHierarchy;
|
||||
var gLeftPaneFolderId;
|
||||
@ -25,8 +24,6 @@ add_task(async function() {
|
||||
// Check getters.
|
||||
gLeftPaneFolderIdGetter = Object.getOwnPropertyDescriptor(PlacesUIUtils, "leftPaneFolderId");
|
||||
Assert.equal(typeof(gLeftPaneFolderIdGetter.get), "function");
|
||||
gAllBookmarksFolderIdGetter = Object.getOwnPropertyDescriptor(PlacesUIUtils, "allBookmarksFolderId");
|
||||
Assert.equal(typeof(gAllBookmarksFolderIdGetter.get), "function");
|
||||
|
||||
registerCleanupFunction(() => PlacesUtils.bookmarks.eraseEverything());
|
||||
});
|
||||
@ -52,12 +49,11 @@ add_task(async function() {
|
||||
|
||||
while (gTests.length) {
|
||||
// Run current test.
|
||||
await gTests.shift();
|
||||
await gTests.shift()();
|
||||
|
||||
// Regenerate getters.
|
||||
Object.defineProperty(PlacesUIUtils, "leftPaneFolderId", gLeftPaneFolderIdGetter);
|
||||
gLeftPaneFolderId = PlacesUIUtils.leftPaneFolderId;
|
||||
Object.defineProperty(PlacesUIUtils, "allBookmarksFolderId", gAllBookmarksFolderIdGetter);
|
||||
|
||||
// Check the new left pane folder.
|
||||
let leftPaneHierarchy = folderIdToHierarchy(gLeftPaneFolderId);
|
||||
@ -89,13 +85,7 @@ var gTests = [
|
||||
},
|
||||
|
||||
async function test4() {
|
||||
print("4. Delete AllBookmarks.");
|
||||
let guid = await PlacesUtils.promiseItemGuid(PlacesUIUtils.allBookmarksFolderId);
|
||||
await PlacesUtils.bookmarks.remove(guid);
|
||||
},
|
||||
|
||||
async function test5() {
|
||||
print("5. Create a duplicated left pane folder.");
|
||||
print("4. Create a duplicated left pane folder.");
|
||||
let folder = await PlacesUtils.bookmarks.insert({
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
title: "PlacesRoot",
|
||||
@ -109,8 +99,8 @@ var gTests = [
|
||||
PlacesUtils.annotations.EXPIRE_NEVER);
|
||||
},
|
||||
|
||||
async function test6() {
|
||||
print("6. Create a duplicated left pane query.");
|
||||
async function test5() {
|
||||
print("5. Create a duplicated left pane query.");
|
||||
let folder = await PlacesUtils.bookmarks.insert({
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
title: "AllBookmarks",
|
||||
@ -124,25 +114,11 @@ var gTests = [
|
||||
PlacesUtils.annotations.EXPIRE_NEVER);
|
||||
},
|
||||
|
||||
function test7() {
|
||||
print("7. Remove the left pane folder annotation.");
|
||||
function test6() {
|
||||
print("6. Remove the left pane folder annotation.");
|
||||
PlacesUtils.annotations.removeItemAnnotation(gLeftPaneFolderId,
|
||||
ORGANIZER_FOLDER_ANNO);
|
||||
},
|
||||
|
||||
function test8() {
|
||||
print("8. Remove a left pane query annotation.");
|
||||
PlacesUtils.annotations.removeItemAnnotation(PlacesUIUtils.allBookmarksFolderId,
|
||||
ORGANIZER_QUERY_ANNO);
|
||||
},
|
||||
|
||||
async function test9() {
|
||||
print("9. Remove a child of AllBookmarks.");
|
||||
let guid = await PlacesUtils.promiseItemGuid(PlacesUIUtils.allBookmarksFolderId);
|
||||
let bm = await PlacesUtils.bookmarks.fetch({parentGuid: guid, index: 0});
|
||||
await PlacesUtils.bookmarks.remove(bm.guid);
|
||||
}
|
||||
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -683,7 +683,6 @@ var gMainPane = {
|
||||
revertCheckbox();
|
||||
return;
|
||||
case CONFIRM_RESTART_PROMPT_RESTART_NOW:
|
||||
const Cc = Components.classes, Ci = Components.interfaces;
|
||||
let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
|
||||
.createInstance(Ci.nsISupportsPRBool);
|
||||
Services.obs.notifyObservers(cancelQuit, "quit-application-requested",
|
||||
@ -886,11 +885,8 @@ var gMainPane = {
|
||||
},
|
||||
|
||||
_getTabsForHomePage() {
|
||||
var win;
|
||||
var tabs = [];
|
||||
|
||||
const Cc = Components.classes, Ci = Components.interfaces;
|
||||
win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
var win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
|
||||
if (win && win.document.documentElement
|
||||
.getAttribute("windowtype") == "navigator:browser") {
|
||||
|
@ -19,7 +19,7 @@
|
||||
var SelectBookmarkDialog = {
|
||||
init: function SBD_init() {
|
||||
document.getElementById("bookmarks").place =
|
||||
"place:queryType=1&folder=" + PlacesUIUtils.allBookmarksFolderId;
|
||||
"place:type=" + Ci.nsINavHistoryQueryOptions.RESULTS_AS_ROOTS_QUERY;
|
||||
|
||||
// Initial update of the OK button.
|
||||
this.selectionChanged();
|
||||
|
@ -37,23 +37,19 @@ treechildren::-moz-tree-image(title, separator) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(container, OrganizerQuery_AllBookmarks) {
|
||||
list-style-image: url("chrome://browser/skin/places/allBookmarks.png");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(container, livemark) {
|
||||
list-style-image: url("chrome://browser/skin/places/folder-live.svg");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(container, OrganizerQuery_BookmarksToolbar) {
|
||||
treechildren::-moz-tree-image(container, queryFolder_toolbar_____) {
|
||||
list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.svg");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(container, OrganizerQuery_BookmarksMenu) {
|
||||
treechildren::-moz-tree-image(container, queryFolder_menu________) {
|
||||
list-style-image: url("chrome://browser/skin/places/bookmarksMenu.svg");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(container, OrganizerQuery_UnfiledBookmarks) {
|
||||
treechildren::-moz-tree-image(container, queryFolder_unfiled_____) {
|
||||
list-style-image: url("chrome://browser/skin/places/unfiledBookmarks.svg");
|
||||
}
|
||||
|
||||
@ -62,6 +58,10 @@ treechildren::-moz-tree-image(query) {
|
||||
list-style-image: url("chrome://browser/skin/places/folder-smart.svg");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(query, OrganizerQuery_AllBookmarks) {
|
||||
list-style-image: url("chrome://browser/skin/places/allBookmarks.png");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(query, OrganizerQuery_Downloads) {
|
||||
list-style-image: url("chrome://browser/skin/places/downloads.png");
|
||||
}
|
||||
|
@ -8,8 +8,8 @@ updateAppInfo({
|
||||
|
||||
registerManifests([do_get_file("data/test_abi.manifest")]);
|
||||
|
||||
const catman = Components.classes["@mozilla.org/categorymanager;1"].
|
||||
getService(Components.interfaces.nsICategoryManager);
|
||||
const catman = Cc["@mozilla.org/categorymanager;1"].
|
||||
getService(Ci.nsICategoryManager);
|
||||
|
||||
function is_registered(name) {
|
||||
try {
|
||||
|
@ -1,65 +0,0 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
/* global __dirname */
|
||||
|
||||
"use strict";
|
||||
|
||||
const toolbox = require("../node_modules/devtools-launchpad/index");
|
||||
const feature = require("devtools-config");
|
||||
const envConfig = require("../configs/development.json");
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
feature.setConfig(envConfig);
|
||||
const webpackConfig = require("../webpack.config")(envConfig);
|
||||
|
||||
let {app} = toolbox.startDevServer(envConfig, webpackConfig);
|
||||
|
||||
function sendFile(res, src, encoding) {
|
||||
const filePath = path.join(__dirname, src);
|
||||
const file = encoding ? fs.readFileSync(filePath, encoding) : fs.readFileSync(filePath);
|
||||
res.send(file);
|
||||
}
|
||||
|
||||
function addFileRoute(from, to) {
|
||||
app.get(from, function (req, res) {
|
||||
sendFile(res, to, "utf-8");
|
||||
});
|
||||
}
|
||||
|
||||
// Routes
|
||||
addFileRoute("/", "../inspector.xhtml");
|
||||
addFileRoute("/markup/markup.xhtml", "../markup/markup.xhtml");
|
||||
|
||||
app.get("/devtools/skin/images/:file.png", function (req, res) {
|
||||
res.contentType("image/png");
|
||||
sendFile(res, "../../themes/images/" + req.params.file + ".png");
|
||||
});
|
||||
|
||||
app.get("/devtools/skin/images/:file.svg", function (req, res) {
|
||||
res.contentType("image/svg+xml");
|
||||
sendFile(res, "../../themes/images/" + req.params.file + ".svg", "utf-8");
|
||||
});
|
||||
|
||||
app.get("/images/:file.svg", function (req, res) {
|
||||
res.contentType("image/svg+xml");
|
||||
sendFile(res, "../../themes/images/" + req.params.file + ".svg", "utf-8");
|
||||
});
|
||||
|
||||
// Redirect chrome:devtools/skin/file.css to ../../themes/file.css
|
||||
app.get("/devtools/skin/:file.css", function (req, res) {
|
||||
res.contentType("text/css; charset=utf-8");
|
||||
sendFile(res, "../../themes/" + req.params.file + ".css", "utf-8");
|
||||
});
|
||||
|
||||
// Redirect chrome:devtools/client/path/to/file.css to ../../path/to/file.css
|
||||
// and chrome:devtools/content/path/to/file.css to ../../path/to/file.css
|
||||
app.get(/^\/devtools\/(?:client|content)\/(.*)\.css$/, function (req, res) {
|
||||
res.contentType("text/css; charset=utf-8");
|
||||
sendFile(res, "../../" + req.params[0] + ".css");
|
||||
});
|
@ -1,119 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
/* global window, document */
|
||||
|
||||
"use strict";
|
||||
|
||||
const React = require("devtools/client/shared/vendor/react");
|
||||
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
|
||||
const { appinfo } = require("Services");
|
||||
|
||||
const { buildFakeToolbox, Inspector } = require("./inspector");
|
||||
|
||||
function onConnect(arg) {
|
||||
if (!arg || !arg.client) {
|
||||
return;
|
||||
}
|
||||
|
||||
let client = arg.client;
|
||||
|
||||
const tabTarget = client.getTabTarget();
|
||||
let threadClient = { paused: false };
|
||||
buildFakeToolbox(
|
||||
tabTarget,
|
||||
() => threadClient,
|
||||
{ React, ReactDOM, browserRequire: () => {} }
|
||||
).then(function (fakeToolbox) {
|
||||
let inspector = new Inspector(fakeToolbox);
|
||||
inspector.init();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Stylesheet links in devtools xhtml files are using chrome or resource URLs.
|
||||
* Rewrite the href attribute to remove the protocol. web-server.js contains redirects
|
||||
* to map CSS urls to the proper file. Supports urls using:
|
||||
* - devtools/client/
|
||||
* - devtools/content/
|
||||
* - skin/
|
||||
* The css for the light-theme will additionally be loaded.
|
||||
* Will also add mandatory classnames and attributes to be compatible with devtools theme
|
||||
* stylesheet.
|
||||
*
|
||||
*/
|
||||
function fixStylesheets(doc) {
|
||||
let links = doc.head.querySelectorAll("link");
|
||||
for (let link of links) {
|
||||
link.href = link.href.replace(/(resource|chrome)\:\/\//, "/");
|
||||
}
|
||||
|
||||
// Add the light theme stylesheet to compensate for the missing theme-switching.js
|
||||
let themeLink = doc.createElement("link");
|
||||
themeLink.setAttribute("rel", "stylesheet");
|
||||
themeLink.setAttribute("href", "/devtools/skin/light-theme.css");
|
||||
|
||||
doc.head.appendChild(themeLink);
|
||||
doc.documentElement.classList.add("theme-light");
|
||||
doc.body.classList.add("theme-light");
|
||||
|
||||
if (appinfo.OS === "Darwin") {
|
||||
doc.documentElement.setAttribute("platform", "mac");
|
||||
} else if (appinfo.OS === "Linux") {
|
||||
doc.documentElement.setAttribute("platform", "linux");
|
||||
} else {
|
||||
doc.documentElement.setAttribute("platform", "win");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called each time a childList mutation is received on the main document.
|
||||
* Check the iframes currently loaded in the document and call fixStylesheets if needed.
|
||||
*/
|
||||
function fixStylesheetsOnMutation() {
|
||||
let frames = document.body.querySelectorAll("iframe");
|
||||
for (let frame of frames) {
|
||||
let doc = frame.contentDocument || frame.contentWindow.document;
|
||||
if (doc.__fixStylesheetsFlag) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Mark the document as processed to avoid extra changes.
|
||||
doc.__fixStylesheetsFlag = true;
|
||||
if (doc.readyState !== "complete") {
|
||||
// If the document is not loaded yet, wait for DOMContentLoaded.
|
||||
frame.contentWindow.addEventListener("DOMContentLoaded", () => {
|
||||
fixStylesheets(doc);
|
||||
}, { once: true });
|
||||
} else {
|
||||
fixStylesheets(doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("DOMContentLoaded", function () {
|
||||
// Add styling for the main document.
|
||||
fixStylesheets(document);
|
||||
|
||||
// Add a mutation observer to check if new iframes have been loaded and need to have
|
||||
// their stylesheet links updated.
|
||||
new window.MutationObserver(mutations => {
|
||||
fixStylesheetsOnMutation();
|
||||
}).observe(document.body, { childList: true, subtree: true });
|
||||
|
||||
const hasFirefoxTabParam = window.location.href.includes("firefox-tab");
|
||||
if (!hasFirefoxTabParam) {
|
||||
const inspectorRoot = document.querySelector(".inspector");
|
||||
// Remove the inspector specific markup and add the landing page root element.
|
||||
inspectorRoot.remove();
|
||||
let mount = document.createElement("div");
|
||||
mount.setAttribute("id", "mount");
|
||||
document.body.appendChild(mount);
|
||||
}
|
||||
|
||||
// Toolbox tries to add a theme classname on the documentElement and should only be
|
||||
// required after DOMContentLoaded.
|
||||
const { bootstrap } = require("devtools-launchpad");
|
||||
bootstrap(React, ReactDOM).then(onConnect);
|
||||
}, {once: true});
|
@ -1,14 +0,0 @@
|
||||
{
|
||||
"name": "inspector.html",
|
||||
"version": "0.0.1",
|
||||
"description": "The Firefox Inspector",
|
||||
"scripts": {
|
||||
"start": "node bin/dev-server"
|
||||
},
|
||||
"author": "",
|
||||
"dependencies": {
|
||||
"devtools-launchpad": "=0.0.43",
|
||||
"raw-loader": "^0.5.1",
|
||||
"json-loader": "^0.5.4"
|
||||
}
|
||||
}
|
@ -1,169 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
/* global __dirname */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {toolboxConfig} = require("devtools-launchpad/index");
|
||||
|
||||
const path = require("path");
|
||||
const webpack = require("webpack");
|
||||
|
||||
module.exports = envConfig => {
|
||||
let webpackConfig = {
|
||||
bail: false,
|
||||
entry: [
|
||||
path.join(__dirname, "local-toolbox.js")
|
||||
],
|
||||
output: {
|
||||
path: path.join(__dirname, "assets/build"),
|
||||
filename: "inspector.js",
|
||||
publicPath: "/"
|
||||
},
|
||||
module: {
|
||||
noParse: [
|
||||
/netmonitor\/panel\.js/i,
|
||||
/debugger\/new\/panel\.js/i
|
||||
],
|
||||
|
||||
// Disable handling of unknown requires
|
||||
unknownContextRegExp: /$^/,
|
||||
unknownContextCritical: false,
|
||||
|
||||
// Disable handling of requires with a single expression
|
||||
exprContextRegExp: /$^/,
|
||||
exprContextCritical: false,
|
||||
|
||||
// Warn for every expression in require
|
||||
wrappedContextCritical: true,
|
||||
|
||||
loaders: [
|
||||
{
|
||||
test: /event-emitter/,
|
||||
exclude: /node_modules/,
|
||||
loaders: ["rewrite-event-emitter"],
|
||||
}, {
|
||||
test: /client(\/|\\)inspector(\/|\\).*\.js$/,
|
||||
loaders: [
|
||||
// Replace all references to this.browserRequire() by require()
|
||||
"rewrite-browser-require",
|
||||
// Replace all references to loader.lazyRequire() by require()
|
||||
"rewrite-lazy-require",
|
||||
],
|
||||
}, {
|
||||
test: /shared(\/|\\)inspector(\/|\\)css-logic\.js$/,
|
||||
loaders: [
|
||||
// Replace a very specific lazy importer, which should really be moved to
|
||||
// /server ...
|
||||
"rewrite-css-logic-importer",
|
||||
],
|
||||
}, {
|
||||
test: /react-redux\.js$/,
|
||||
loaders: [
|
||||
// Replace dynamic paths in react-redux file
|
||||
"rewrite-react-redux",
|
||||
],
|
||||
}, {
|
||||
// Replace all references sdk's lazyRequire by require()
|
||||
test: /sdk(\/|\\).*\.js$/,
|
||||
loaders: ["rewrite-sdk-lazy-require"],
|
||||
}
|
||||
]
|
||||
},
|
||||
resolveLoader: {
|
||||
root: [
|
||||
path.resolve("./node_modules"),
|
||||
path.resolve("../shared/webpack"),
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"react": "devtools/client/shared/vendor/react",
|
||||
"redux": "devtools/client/shared/vendor/redux",
|
||||
"react-dom": "devtools/client/shared/vendor/react-dom",
|
||||
"acorn/util/walk": path.join(__dirname, "../../shared/acorn/walk"),
|
||||
"acorn": path.join(__dirname, "../../shared/acorn"),
|
||||
"devtools/client/framework/about-devtools-toolbox":
|
||||
path.join(__dirname, "./webpack/about-devtools-sham.js"),
|
||||
"devtools/client/framework/attach-thread":
|
||||
path.join(__dirname, "./webpack/attach-thread-sham.js"),
|
||||
"devtools/client/framework/target-from-url":
|
||||
path.join(__dirname, "./webpack/target-from-url-sham.js"),
|
||||
"devtools/client/jsonview/main":
|
||||
path.join(__dirname, "./webpack/jsonview-sham.js"),
|
||||
"devtools/client/sourceeditor/editor":
|
||||
path.join(__dirname, "./webpack/editor-sham.js"),
|
||||
"devtools/client/locales": path.join(__dirname, "../locales/en-US"),
|
||||
"devtools/shared/DevToolsUtils":
|
||||
path.join(__dirname, "./webpack/devtools-utils-sham.js"),
|
||||
"devtools/shared/locales": path.join(__dirname, "../../shared/locales/en-US"),
|
||||
"devtools/shared/platform/clipboard": path.join(__dirname,
|
||||
"../../client/shared/webpack/shims/platform-clipboard-stub"),
|
||||
"devtools/shared/platform/stack": path.join(__dirname,
|
||||
"../../client/shared/webpack/shims/platform-stack-stub"),
|
||||
"devtools": path.join(__dirname, "../../"),
|
||||
"gcli": path.join(__dirname, "../../shared/gcli/source/lib/gcli"),
|
||||
"method": path.join(__dirname, "../../../addon-sdk/source/lib/method"),
|
||||
"modules/libpref/init/all":
|
||||
path.join(__dirname, "../../../modules/libpref/init/all.js"),
|
||||
"devtools/shared/generate-uuid":
|
||||
path.join(__dirname, "./webpack/uuid-sham.js"),
|
||||
"sdk": path.join(__dirname, "../../../addon-sdk/source/lib/sdk"),
|
||||
"Services": path.join(__dirname, "../shared/shim/Services.js"),
|
||||
"toolkit/locales":
|
||||
path.join(__dirname, "../../../toolkit/locales/en-US/chrome/global"),
|
||||
},
|
||||
},
|
||||
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
"isWorker": JSON.stringify(false),
|
||||
"reportError": "console.error",
|
||||
"AppConstants": "{ DEBUG: true, DEBUG_JS_MODULES: true }",
|
||||
"loader": `{
|
||||
lazyRequireGetter: () => {},
|
||||
lazyGetter: () => {},
|
||||
lazyImporter: () => {}
|
||||
}`,
|
||||
"dump": "console.log",
|
||||
})
|
||||
]
|
||||
};
|
||||
|
||||
webpackConfig.externals = [
|
||||
/codemirror\//,
|
||||
{
|
||||
"promise": "var Promise",
|
||||
"devtools/server/main": "{}",
|
||||
|
||||
// Just trying to get build to work. These should be removed eventually:
|
||||
"chrome": "{}",
|
||||
|
||||
// In case you end up in chrome-land you can use this to help track down issues.
|
||||
// SDK for instance does a bunch of this so if you somehow end up importing an SDK
|
||||
// dependency this might help for debugging:
|
||||
// "chrome": `{
|
||||
// Cc: {
|
||||
// "@mozilla.org/uuid-generator;1": { getService: () => { return {} } },
|
||||
// "@mozilla.org/observer-service;1": { getService: () => { return {} } },
|
||||
// },
|
||||
// Ci: {},
|
||||
// Cr: {},
|
||||
// Cm: {},
|
||||
// components: { classesByID: () => {} , ID: () => {} }
|
||||
// }`,
|
||||
|
||||
"resource://gre/modules/XPCOMUtils.jsm": "{}",
|
||||
"resource://devtools/client/styleeditor/StyleEditorUI.jsm": "{}",
|
||||
"resource://devtools/client/styleeditor/StyleEditorUtil.jsm": "{}",
|
||||
"devtools/client/shared/developer-toolbar": "{}",
|
||||
},
|
||||
];
|
||||
|
||||
// Exclude all files from devtools/ or addon-sdk/ or modules/ .
|
||||
webpackConfig.babelExcludes = /(devtools(\/|\\)|addon-sdk(\/|\\)|modules(\/|\\))/;
|
||||
|
||||
return toolboxConfig(webpackConfig, envConfig);
|
||||
};
|
@ -1,12 +0,0 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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";
|
||||
|
||||
module.exports = {
|
||||
register: () => {},
|
||||
unregister: () => {},
|
||||
};
|
@ -1,11 +0,0 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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";
|
||||
|
||||
module.exports = {
|
||||
attachThread: () => {},
|
||||
};
|
@ -1,27 +0,0 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
/* global setImmediate */
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
executeSoon: setImmediate,
|
||||
isGenerator: function (fn) {
|
||||
if (typeof fn !== "function") {
|
||||
return false;
|
||||
}
|
||||
let proto = Object.getPrototypeOf(fn);
|
||||
if (!proto) {
|
||||
return false;
|
||||
}
|
||||
let ctor = proto.constructor;
|
||||
if (!ctor) {
|
||||
return false;
|
||||
}
|
||||
return ctor.name == "GeneratorFunction";
|
||||
}
|
||||
};
|
@ -1,13 +0,0 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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";
|
||||
|
||||
function Editor() {}
|
||||
Editor.modes = {};
|
||||
Editor.prototype.appendToLocalElement = () => {};
|
||||
|
||||
module.exports = Editor;
|
@ -1,14 +0,0 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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";
|
||||
|
||||
module.exports = {
|
||||
JsonView: {
|
||||
initialize: () => {},
|
||||
destroy: () => {},
|
||||
}
|
||||
};
|
@ -1,72 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
// Rewrite devtools.js or all.js, leaving just the relevant pref() calls.
|
||||
|
||||
"use strict";
|
||||
|
||||
const PREF_WHITELIST = [
|
||||
"devtools",
|
||||
];
|
||||
|
||||
const acceptLine = function (line) {
|
||||
let matches = line.match(/^ *pref\("([^"]+)"/);
|
||||
if (!matches || !matches[1]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let [, prefName] = matches;
|
||||
return PREF_WHITELIST.some(filter => prefName.startsWith(filter));
|
||||
};
|
||||
|
||||
module.exports = function (content) {
|
||||
this.cacheable && this.cacheable();
|
||||
|
||||
// If we're reading devtools.js we have to do some preprocessing.
|
||||
// If we're reading all.js we just assume we can dump all the
|
||||
// conditionals.
|
||||
let isDevtools = this.request.endsWith("/devtools.js");
|
||||
|
||||
// This maps the text of a "#if" to its truth value. This has to
|
||||
// cover all uses of #if in devtools.js.
|
||||
const ifMap = {
|
||||
"#if MOZ_UPDATE_CHANNEL == beta": false,
|
||||
"#if defined(NIGHTLY_BUILD)": false,
|
||||
"#ifdef NIGHTLY_BUILD": false,
|
||||
"#ifdef MOZ_DEV_EDITION": false,
|
||||
"#ifdef RELEASE_OR_BETA": true,
|
||||
"#ifdef RELEASE_BUILD": true,
|
||||
};
|
||||
|
||||
let lines = content.split("\n");
|
||||
let ignoring = false;
|
||||
let newLines = [];
|
||||
let continuation = false;
|
||||
for (let line of lines) {
|
||||
if (line.startsWith("sticky_pref")) {
|
||||
line = line.slice(7);
|
||||
}
|
||||
|
||||
if (isDevtools) {
|
||||
if (line.startsWith("#if")) {
|
||||
if (!(line in ifMap)) {
|
||||
throw new Error("missing line in ifMap: " + line);
|
||||
}
|
||||
ignoring = !ifMap[line];
|
||||
} else if (line.startsWith("#else")) {
|
||||
ignoring = !ignoring;
|
||||
} else if (line.startsWith("#endif")) {
|
||||
ignoring = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (continuation || (!ignoring && acceptLine(line))) {
|
||||
newLines.push(line);
|
||||
|
||||
// The call to pref(...); might span more than one line.
|
||||
continuation = !/\);/.test(line);
|
||||
}
|
||||
}
|
||||
return newLines.join("\n");
|
||||
};
|
@ -1,11 +0,0 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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";
|
||||
|
||||
module.exports = {
|
||||
targetFromURL: () => {},
|
||||
};
|
@ -1,19 +0,0 @@
|
||||
/* 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";
|
||||
|
||||
const s4 = function () {
|
||||
return Math.floor((1 + Math.random()) * 0x10000)
|
||||
.toString(16)
|
||||
.substring(1);
|
||||
};
|
||||
|
||||
let generateUUID = function () {
|
||||
return "ss-s-s-s-sss".replace(/s/g, function () {
|
||||
return s4();
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = { generateUUID };
|
File diff suppressed because it is too large
Load Diff
@ -36,8 +36,6 @@
|
||||
<!ENTITY runtimeMenu_takeScreenshot_accesskey "S">
|
||||
<!ENTITY runtimeMenu_showDetails_label "Runtime Info">
|
||||
<!ENTITY runtimeMenu_showDetails_accesskey "E">
|
||||
<!ENTITY runtimeMenu_showMonitor_label "Monitor">
|
||||
<!ENTITY runtimeMenu_showMonitor_accesskey "M">
|
||||
<!ENTITY runtimeMenu_showDevicePrefs_label "Device Preferences">
|
||||
<!ENTITY runtimeMenu_showDevicePrefs_accesskey "D">
|
||||
<!ENTITY runtimeMenu_showSettings_label "Device Settings">
|
||||
@ -147,10 +145,6 @@
|
||||
<!ENTITY devicesetting_newtext "Setting value">
|
||||
<!ENTITY devicesetting_addnew "Add new setting">
|
||||
|
||||
<!-- Monitor -->
|
||||
<!ENTITY monitor_title "Monitor">
|
||||
<!ENTITY monitor_help "Help">
|
||||
|
||||
<!-- WiFi Authentication -->
|
||||
<!-- LOCALIZATION NOTE (wifi_auth_header): The header displayed on the dialog
|
||||
that instructs the user to transfer an authentication token to the
|
||||
|
@ -16,8 +16,6 @@ webide.jar:
|
||||
content/runtimedetails.xhtml (runtimedetails.xhtml)
|
||||
content/prefs.js (prefs.js)
|
||||
content/prefs.xhtml (prefs.xhtml)
|
||||
content/monitor.xhtml (monitor.xhtml)
|
||||
content/monitor.js (monitor.js)
|
||||
content/devicepreferences.js (devicepreferences.js)
|
||||
content/devicepreferences.xhtml (devicepreferences.xhtml)
|
||||
content/wifi-auth.js (wifi-auth.js)
|
||||
|
@ -1,739 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const Services = require("Services");
|
||||
const {AppManager} = require("devtools/client/webide/modules/app-manager");
|
||||
const {AppActorFront} = require("devtools/shared/apps/app-actor-front");
|
||||
const {Connection} = require("devtools/shared/client/connection-manager");
|
||||
const EventEmitter = require("devtools/shared/old-event-emitter");
|
||||
|
||||
window.addEventListener("load", function () {
|
||||
window.addEventListener("resize", Monitor.resize);
|
||||
window.addEventListener("unload", Monitor.unload);
|
||||
|
||||
document.querySelector("#close").onclick = () => {
|
||||
window.parent.UI.openProject();
|
||||
};
|
||||
|
||||
Monitor.load();
|
||||
}, {once: true});
|
||||
|
||||
|
||||
/**
|
||||
* The Monitor is a WebIDE tool used to display any kind of time-based data in
|
||||
* the form of graphs.
|
||||
*
|
||||
* The data can come from a Firefox OS device, or from a WebSockets
|
||||
* server running locally.
|
||||
*
|
||||
* The format of a data update is typically an object like:
|
||||
*
|
||||
* { graph: 'mygraph', curve: 'mycurve', value: 42, time: 1234 }
|
||||
*
|
||||
* or an array of such objects. For more details on the data format, see the
|
||||
* `Graph.update(data)` method.
|
||||
*/
|
||||
var Monitor = {
|
||||
|
||||
apps: new Map(),
|
||||
graphs: new Map(),
|
||||
front: null,
|
||||
socket: null,
|
||||
wstimeout: null,
|
||||
b2ginfo: false,
|
||||
b2gtimeout: null,
|
||||
|
||||
/**
|
||||
* Add new data to the graphs, create a new graph if necessary.
|
||||
*/
|
||||
update: function (data, fallback) {
|
||||
if (Array.isArray(data)) {
|
||||
data.forEach(d => Monitor.update(d, fallback));
|
||||
return;
|
||||
}
|
||||
|
||||
if (Monitor.b2ginfo && data.graph === "USS") {
|
||||
// If we're polling b2g-info, ignore USS updates from the device's
|
||||
// USSAgents (see Monitor.pollB2GInfo()).
|
||||
return;
|
||||
}
|
||||
|
||||
if (fallback) {
|
||||
for (let key in fallback) {
|
||||
if (!data[key]) {
|
||||
data[key] = fallback[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let graph = Monitor.graphs.get(data.graph);
|
||||
if (!graph) {
|
||||
let element = document.createElement("div");
|
||||
element.classList.add("graph");
|
||||
document.body.appendChild(element);
|
||||
|
||||
graph = new Graph(data.graph, element);
|
||||
Monitor.resize(); // a scrollbar might have dis/reappeared
|
||||
Monitor.graphs.set(data.graph, graph);
|
||||
}
|
||||
graph.update(data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize the Monitor.
|
||||
*/
|
||||
load: function () {
|
||||
AppManager.on("app-manager-update", Monitor.onAppManagerUpdate);
|
||||
Monitor.connectToRuntime();
|
||||
Monitor.connectToWebSocket();
|
||||
},
|
||||
|
||||
/**
|
||||
* Clean up the Monitor.
|
||||
*/
|
||||
unload: function () {
|
||||
AppManager.off("app-manager-update", Monitor.onAppManagerUpdate);
|
||||
Monitor.disconnectFromRuntime();
|
||||
Monitor.disconnectFromWebSocket();
|
||||
},
|
||||
|
||||
/**
|
||||
* Resize all the graphs.
|
||||
*/
|
||||
resize: function () {
|
||||
for (let graph of Monitor.graphs.values()) {
|
||||
graph.resize();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* When WebIDE connects to a new runtime, start its data forwarders.
|
||||
*/
|
||||
onAppManagerUpdate: function (event, what, details) {
|
||||
switch (what) {
|
||||
case "runtime-global-actors":
|
||||
Monitor.connectToRuntime();
|
||||
break;
|
||||
case "connection":
|
||||
if (AppManager.connection.status == Connection.Status.DISCONNECTED) {
|
||||
Monitor.disconnectFromRuntime();
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Use an AppActorFront on a runtime to watch track its apps.
|
||||
*/
|
||||
connectToRuntime: function () {
|
||||
Monitor.pollB2GInfo();
|
||||
let client = AppManager.connection && AppManager.connection.client;
|
||||
let resp = AppManager._listTabsResponse;
|
||||
if (client && resp && !Monitor.front) {
|
||||
Monitor.front = new AppActorFront(client, resp);
|
||||
Monitor.front.watchApps(Monitor.onRuntimeAppEvent);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy our AppActorFront.
|
||||
*/
|
||||
disconnectFromRuntime: function () {
|
||||
Monitor.unpollB2GInfo();
|
||||
if (Monitor.front) {
|
||||
Monitor.front.unwatchApps(Monitor.onRuntimeAppEvent);
|
||||
Monitor.front = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Try connecting to a local websockets server and accept updates from it.
|
||||
*/
|
||||
connectToWebSocket: function () {
|
||||
let webSocketURL = Services.prefs.getCharPref("devtools.webide.monitorWebSocketURL");
|
||||
try {
|
||||
Monitor.socket = new WebSocket(webSocketURL);
|
||||
Monitor.socket.onmessage = function (event) {
|
||||
Monitor.update(JSON.parse(event.data));
|
||||
};
|
||||
Monitor.socket.onclose = function () {
|
||||
Monitor.wstimeout = setTimeout(Monitor.connectToWebsocket, 1000);
|
||||
};
|
||||
} catch (e) {
|
||||
Monitor.wstimeout = setTimeout(Monitor.connectToWebsocket, 1000);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Used when cleaning up.
|
||||
*/
|
||||
disconnectFromWebSocket: function () {
|
||||
clearTimeout(Monitor.wstimeout);
|
||||
if (Monitor.socket) {
|
||||
Monitor.socket.onclose = () => {};
|
||||
Monitor.socket.close();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* When an app starts on the runtime, start a monitor actor for its process.
|
||||
*/
|
||||
onRuntimeAppEvent: function (type, app) {
|
||||
if (type !== "appOpen" && type !== "appClose") {
|
||||
return;
|
||||
}
|
||||
|
||||
let client = AppManager.connection.client;
|
||||
app.getForm().then(form => {
|
||||
if (type === "appOpen") {
|
||||
app.monitorClient = new MonitorClient(client, form);
|
||||
app.monitorClient.start();
|
||||
app.monitorClient.on("update", Monitor.onRuntimeUpdate);
|
||||
Monitor.apps.set(form.monitorActor, app);
|
||||
} else {
|
||||
let app = Monitor.apps.get(form.monitorActor);
|
||||
if (app) {
|
||||
app.monitorClient.stop(() => app.monitorClient.destroy());
|
||||
Monitor.apps.delete(form.monitorActor);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Accept data updates from the monitor actors of a runtime.
|
||||
*/
|
||||
onRuntimeUpdate: function (type, packet) {
|
||||
let fallback = {}, app = Monitor.apps.get(packet.from);
|
||||
if (app) {
|
||||
fallback.curve = app.manifest.name;
|
||||
}
|
||||
Monitor.update(packet.data, fallback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Bug 1047355: If possible, parsing the output of `b2g-info` has several
|
||||
* benefits over bug 1037465's multi-process USSAgent approach, notably:
|
||||
* - Works for older Firefox OS devices (pre-2.1),
|
||||
* - Doesn't need certified-apps debugging,
|
||||
* - Polling time is synchronized for all processes.
|
||||
* TODO: After bug 1043324 lands, consider removing this hack.
|
||||
*/
|
||||
pollB2GInfo: function () {
|
||||
if (AppManager.selectedRuntime) {
|
||||
let device = AppManager.selectedRuntime.device;
|
||||
if (device && device.shell) {
|
||||
device.shell("b2g-info").then(s => {
|
||||
let lines = s.split("\n");
|
||||
let line = "";
|
||||
|
||||
// Find the header row to locate NAME and USS, looks like:
|
||||
// ' NAME PID NICE USS PSS RSS VSIZE OOM_ADJ USER '.
|
||||
while (!line.includes("NAME")) {
|
||||
if (lines.length < 1) {
|
||||
// Something is wrong with this output, don't trust b2g-info.
|
||||
Monitor.unpollB2GInfo();
|
||||
return;
|
||||
}
|
||||
line = lines.shift();
|
||||
}
|
||||
let namelength = line.indexOf("NAME") + "NAME".length;
|
||||
let ussindex = line.slice(namelength).split(/\s+/).indexOf("USS");
|
||||
|
||||
// Get the NAME and USS in each following line, looks like:
|
||||
// 'Homescreen 375 18 12.6 16.3 27.1 67.8 4 app_375'.
|
||||
while (lines.length > 0 && lines[0].length > namelength) {
|
||||
line = lines.shift();
|
||||
let name = line.slice(0, namelength);
|
||||
let uss = line.slice(namelength).split(/\s+/)[ussindex];
|
||||
Monitor.update({
|
||||
curve: name.trim(),
|
||||
value: 1024 * 1024 * parseFloat(uss) // Convert MB to bytes.
|
||||
}, {
|
||||
// Note: We use the fallback object to set the graph name to 'USS'
|
||||
// so that Monitor.update() can ignore USSAgent updates.
|
||||
graph: "USS"
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Monitor.b2ginfo = true;
|
||||
Monitor.b2gtimeout = setTimeout(Monitor.pollB2GInfo, 350);
|
||||
},
|
||||
|
||||
/**
|
||||
* Polling b2g-info doesn't work or is no longer needed.
|
||||
*/
|
||||
unpollB2GInfo: function () {
|
||||
clearTimeout(Monitor.b2gtimeout);
|
||||
Monitor.b2ginfo = false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A MonitorClient is used as an actor client of a runtime's monitor actors,
|
||||
* receiving its updates.
|
||||
*/
|
||||
function MonitorClient(client, form) {
|
||||
this.client = client;
|
||||
this.actor = form.monitorActor;
|
||||
this.events = ["update"];
|
||||
|
||||
EventEmitter.decorate(this);
|
||||
this.client.registerClient(this);
|
||||
}
|
||||
MonitorClient.prototype.destroy = function () {
|
||||
this.client.unregisterClient(this);
|
||||
};
|
||||
MonitorClient.prototype.start = function () {
|
||||
this.client.request({
|
||||
to: this.actor,
|
||||
type: "start"
|
||||
});
|
||||
};
|
||||
MonitorClient.prototype.stop = function (callback) {
|
||||
this.client.request({
|
||||
to: this.actor,
|
||||
type: "stop"
|
||||
}, callback);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A Graph populates a container DOM element with an SVG graph and a legend.
|
||||
*/
|
||||
function Graph(name, element) {
|
||||
this.name = name;
|
||||
this.element = element;
|
||||
this.curves = new Map();
|
||||
this.events = new Map();
|
||||
this.ignored = new Set();
|
||||
this.enabled = true;
|
||||
this.request = null;
|
||||
|
||||
this.x = d3.time.scale();
|
||||
this.y = d3.scale.linear();
|
||||
|
||||
this.xaxis = d3.svg.axis().scale(this.x).orient("bottom");
|
||||
this.yaxis = d3.svg.axis().scale(this.y).orient("left");
|
||||
|
||||
this.xformat = d3.time.format("%I:%M:%S");
|
||||
this.yformat = this.formatter(1);
|
||||
this.yaxis.tickFormat(this.formatter(0));
|
||||
|
||||
this.line = d3.svg.line().interpolate("linear")
|
||||
.x(function (d) { return this.x(d.time); })
|
||||
.y(function (d) { return this.y(d.value); });
|
||||
|
||||
this.color = d3.scale.category10();
|
||||
|
||||
this.svg = d3.select(element).append("svg").append("g")
|
||||
.attr("transform", "translate(" + this.margin.left + "," + this.margin.top + ")");
|
||||
|
||||
this.xelement = this.svg.append("g").attr("class", "x axis").call(this.xaxis);
|
||||
this.yelement = this.svg.append("g").attr("class", "y axis").call(this.yaxis);
|
||||
|
||||
// RULERS on axes
|
||||
let xruler = this.xruler = this.svg.select(".x.axis").append("g").attr("class", "x ruler");
|
||||
xruler.append("line").attr("y2", 6);
|
||||
xruler.append("line").attr("stroke-dasharray", "1,1");
|
||||
xruler.append("text").attr("y", 9).attr("dy", ".71em");
|
||||
|
||||
let yruler = this.yruler = this.svg.select(".y.axis").append("g").attr("class", "y ruler");
|
||||
yruler.append("line").attr("x2", -6);
|
||||
yruler.append("line").attr("stroke-dasharray", "1,1");
|
||||
yruler.append("text").attr("x", -9).attr("dy", ".32em");
|
||||
|
||||
let self = this;
|
||||
|
||||
d3.select(element).select("svg")
|
||||
.on("mousemove", function () {
|
||||
let mouse = d3.mouse(this);
|
||||
self.mousex = mouse[0] - self.margin.left,
|
||||
self.mousey = mouse[1] - self.margin.top;
|
||||
|
||||
xruler.attr("transform", "translate(" + self.mousex + ",0)");
|
||||
yruler.attr("transform", "translate(0," + self.mousey + ")");
|
||||
});
|
||||
/* .on('mouseout', function() {
|
||||
self.xruler.attr('transform', 'translate(-500,0)');
|
||||
self.yruler.attr('transform', 'translate(0,-500)');
|
||||
});*/
|
||||
this.mousex = this.mousey = -500;
|
||||
|
||||
let sidebar = d3.select(this.element).append("div").attr("class", "sidebar");
|
||||
let title = sidebar.append("label").attr("class", "graph-title");
|
||||
|
||||
title.append("input")
|
||||
.attr("type", "checkbox")
|
||||
.attr("checked", "true")
|
||||
.on("click", function () { self.toggle(); });
|
||||
title.append("span").text(this.name);
|
||||
|
||||
this.legend = sidebar.append("div").attr("class", "legend");
|
||||
|
||||
this.resize = this.resize.bind(this);
|
||||
this.render = this.render.bind(this);
|
||||
this.averages = this.averages.bind(this);
|
||||
|
||||
setInterval(this.averages, 1000);
|
||||
|
||||
this.resize();
|
||||
}
|
||||
|
||||
Graph.prototype = {
|
||||
|
||||
/**
|
||||
* These margin are used to properly position the SVG graph items inside the
|
||||
* container element.
|
||||
*/
|
||||
margin: {
|
||||
top: 10,
|
||||
right: 150,
|
||||
bottom: 20,
|
||||
left: 50
|
||||
},
|
||||
|
||||
/**
|
||||
* A Graph can be collapsed by the user.
|
||||
*/
|
||||
toggle: function () {
|
||||
if (this.enabled) {
|
||||
this.element.classList.add("disabled");
|
||||
this.enabled = false;
|
||||
} else {
|
||||
this.element.classList.remove("disabled");
|
||||
this.enabled = true;
|
||||
}
|
||||
Monitor.resize();
|
||||
},
|
||||
|
||||
/**
|
||||
* If the container element is resized (e.g. because the window was resized or
|
||||
* a scrollbar dis/appeared), the graph needs to be resized as well.
|
||||
*/
|
||||
resize: function () {
|
||||
let style = getComputedStyle(this.element),
|
||||
height = parseFloat(style.height) - this.margin.top - this.margin.bottom,
|
||||
width = parseFloat(style.width) - this.margin.left - this.margin.right;
|
||||
|
||||
d3.select(this.element).select("svg")
|
||||
.attr("width", width + this.margin.left)
|
||||
.attr("height", height + this.margin.top + this.margin.bottom);
|
||||
|
||||
this.x.range([0, width]);
|
||||
this.y.range([height, 0]);
|
||||
|
||||
this.xelement.attr("transform", "translate(0," + height + ")");
|
||||
this.xruler.select("line[stroke-dasharray]").attr("y2", -height);
|
||||
this.yruler.select("line[stroke-dasharray]").attr("x2", width);
|
||||
},
|
||||
|
||||
/**
|
||||
* If the domain of the Graph's data changes (on the time axis and/or on the
|
||||
* value axis), the axes' domains need to be updated and the graph items need
|
||||
* to be rescaled in order to represent all the data.
|
||||
*/
|
||||
rescale: function () {
|
||||
let gettime = v => { return v.time; },
|
||||
getvalue = v => { return v.value; },
|
||||
ignored = c => { return this.ignored.has(c.id); };
|
||||
|
||||
let xmin = null, xmax = null, ymin = null, ymax = null;
|
||||
for (let curve of this.curves.values()) {
|
||||
if (ignored(curve)) {
|
||||
continue;
|
||||
}
|
||||
if (xmax == null || curve.xmax > xmax) {
|
||||
xmax = curve.xmax;
|
||||
}
|
||||
if (xmin == null || curve.xmin < xmin) {
|
||||
xmin = curve.xmin;
|
||||
}
|
||||
if (ymax == null || curve.ymax > ymax) {
|
||||
ymax = curve.ymax;
|
||||
}
|
||||
if (ymin == null || curve.ymin < ymin) {
|
||||
ymin = curve.ymin;
|
||||
}
|
||||
}
|
||||
for (let event of this.events.values()) {
|
||||
if (ignored(event)) {
|
||||
continue;
|
||||
}
|
||||
if (xmax == null || event.xmax > xmax) {
|
||||
xmax = event.xmax;
|
||||
}
|
||||
if (xmin == null || event.xmin < xmin) {
|
||||
xmin = event.xmin;
|
||||
}
|
||||
}
|
||||
|
||||
let oldxdomain = this.x.domain();
|
||||
if (xmin != null && xmax != null) {
|
||||
this.x.domain([xmin, xmax]);
|
||||
let newxdomain = this.x.domain();
|
||||
if (newxdomain[0] !== oldxdomain[0] || newxdomain[1] !== oldxdomain[1]) {
|
||||
this.xelement.call(this.xaxis);
|
||||
}
|
||||
}
|
||||
|
||||
let oldydomain = this.y.domain();
|
||||
if (ymin != null && ymax != null) {
|
||||
this.y.domain([ymin, ymax]).nice();
|
||||
let newydomain = this.y.domain();
|
||||
if (newydomain[0] !== oldydomain[0] || newydomain[1] !== oldydomain[1]) {
|
||||
this.yelement.call(this.yaxis);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Add new values to the graph.
|
||||
*/
|
||||
update: function (data) {
|
||||
delete data.graph;
|
||||
|
||||
let time = data.time || Date.now();
|
||||
delete data.time;
|
||||
|
||||
let curve = data.curve;
|
||||
delete data.curve;
|
||||
|
||||
// Single curve value, e.g. { curve: 'memory', value: 42, time: 1234 }.
|
||||
if ("value" in data) {
|
||||
this.push(this.curves, curve, [{time: time, value: data.value}]);
|
||||
delete data.value;
|
||||
}
|
||||
|
||||
// Several curve values, e.g. { curve: 'memory', values: [{value: 42, time: 1234}] }.
|
||||
if ("values" in data) {
|
||||
this.push(this.curves, curve, data.values);
|
||||
delete data.values;
|
||||
}
|
||||
|
||||
// Punctual event, e.g. { event: 'gc', time: 1234 },
|
||||
// event with duration, e.g. { event: 'jank', duration: 425, time: 1234 }.
|
||||
if ("event" in data) {
|
||||
this.push(this.events, data.event, [{time: time, value: data.duration}]);
|
||||
delete data.event;
|
||||
delete data.duration;
|
||||
}
|
||||
|
||||
// Remaining keys are curves, e.g. { time: 1234, memory: 42, battery: 13, temperature: 45 }.
|
||||
for (let key in data) {
|
||||
this.push(this.curves, key, [{time: time, value: data[key]}]);
|
||||
}
|
||||
|
||||
// If no render is currently pending, request one.
|
||||
if (this.enabled && !this.request) {
|
||||
this.request = requestAnimationFrame(this.render);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Insert new data into the graph's data structures.
|
||||
*/
|
||||
push: function (collection, id, values) {
|
||||
|
||||
// Note: collection is either `this.curves` or `this.events`.
|
||||
let item = collection.get(id);
|
||||
if (!item) {
|
||||
item = { id: id, values: [], xmin: null, xmax: null, ymin: 0, ymax: null, average: 0 };
|
||||
collection.set(id, item);
|
||||
}
|
||||
|
||||
for (let v of values) {
|
||||
let time = new Date(v.time), value = +v.value;
|
||||
// Update the curve/event's domain values.
|
||||
if (item.xmax == null || time > item.xmax) {
|
||||
item.xmax = time;
|
||||
}
|
||||
if (item.xmin == null || time < item.xmin) {
|
||||
item.xmin = time;
|
||||
}
|
||||
if (item.ymax == null || value > item.ymax) {
|
||||
item.ymax = value;
|
||||
}
|
||||
if (item.ymin == null || value < item.ymin) {
|
||||
item.ymin = value;
|
||||
}
|
||||
// Note: A curve's average is not computed here. Call `graph.averages()`.
|
||||
item.values.push({ time: time, value: value });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Render the SVG graph with curves, events, crosshair and legend.
|
||||
*/
|
||||
render: function () {
|
||||
this.request = null;
|
||||
this.rescale();
|
||||
|
||||
|
||||
// DATA
|
||||
|
||||
let self = this,
|
||||
getid = d => { return d.id; },
|
||||
gettime = d => { return d.time.getTime(); },
|
||||
getline = d => { return self.line(d.values); },
|
||||
getcolor = d => { return self.color(d.id); },
|
||||
getvalues = d => { return d.values; },
|
||||
ignored = d => { return self.ignored.has(d.id); };
|
||||
|
||||
// Convert our maps to arrays for d3.
|
||||
let curvedata = [...this.curves.values()],
|
||||
eventdata = [...this.events.values()],
|
||||
data = curvedata.concat(eventdata);
|
||||
|
||||
|
||||
// CURVES
|
||||
|
||||
// Map curve data to curve elements.
|
||||
let curves = this.svg.selectAll(".curve").data(curvedata, getid);
|
||||
|
||||
// Create new curves (no element corresponding to the data).
|
||||
curves.enter().append("g").attr("class", "curve").append("path")
|
||||
.style("stroke", getcolor);
|
||||
|
||||
// Delete old curves (elements corresponding to data not present anymore).
|
||||
curves.exit().remove();
|
||||
|
||||
// Update all curves from data.
|
||||
this.svg.selectAll(".curve").select("path")
|
||||
.attr("d", d => { return ignored(d) ? "" : getline(d); });
|
||||
|
||||
let height = parseFloat(getComputedStyle(this.element).height) - this.margin.top - this.margin.bottom;
|
||||
|
||||
|
||||
// EVENTS
|
||||
|
||||
// Map event data to event elements.
|
||||
let events = this.svg.selectAll(".event-slot").data(eventdata, getid);
|
||||
|
||||
// Create new events.
|
||||
events.enter().append("g").attr("class", "event-slot");
|
||||
|
||||
// Remove old events.
|
||||
events.exit().remove();
|
||||
|
||||
// Get all occurences of an event, and map its data to them.
|
||||
let lines = this.svg.selectAll(".event-slot")
|
||||
.style("stroke", d => { return ignored(d) ? "none" : getcolor(d); })
|
||||
.selectAll(".event")
|
||||
.data(getvalues, gettime);
|
||||
|
||||
// Create new event occurrence.
|
||||
lines.enter().append("line").attr("class", "event").attr("y2", height);
|
||||
|
||||
// Delete old event occurrence.
|
||||
lines.exit().remove();
|
||||
|
||||
// Update all event occurrences from data.
|
||||
this.svg.selectAll(".event")
|
||||
.attr("transform", d => { return "translate(" + self.x(d.time) + ",0)"; });
|
||||
|
||||
|
||||
// CROSSHAIR
|
||||
|
||||
// TODO select curves and events, intersect with curves and show values/hovers
|
||||
// e.g. look like http://code.shutterstock.com/rickshaw/examples/lines.html
|
||||
|
||||
// Update crosshair labels on each axis.
|
||||
this.xruler.select("text").text(self.xformat(self.x.invert(self.mousex)));
|
||||
this.yruler.select("text").text(self.yformat(self.y.invert(self.mousey)));
|
||||
|
||||
|
||||
// LEGEND
|
||||
|
||||
// Map data to legend elements.
|
||||
let legends = this.legend.selectAll("label").data(data, getid);
|
||||
|
||||
// Update averages.
|
||||
legends.attr("title", c => { return "Average: " + self.yformat(c.average); });
|
||||
|
||||
// Create new legends.
|
||||
let newlegend = legends.enter().append("label");
|
||||
newlegend.append("input").attr("type", "checkbox").attr("checked", "true").on("click", function (c) {
|
||||
if (ignored(c)) {
|
||||
this.parentElement.classList.remove("disabled");
|
||||
self.ignored.delete(c.id);
|
||||
} else {
|
||||
this.parentElement.classList.add("disabled");
|
||||
self.ignored.add(c.id);
|
||||
}
|
||||
self.update({}); // if no re-render is pending, request one.
|
||||
});
|
||||
newlegend.append("span").attr("class", "legend-color").style("background-color", getcolor);
|
||||
newlegend.append("span").attr("class", "legend-id").text(getid);
|
||||
|
||||
// Delete old legends.
|
||||
legends.exit().remove();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a SI value formatter with a given precision.
|
||||
*/
|
||||
formatter: function (decimals) {
|
||||
return value => {
|
||||
// Don't use sub-unit SI prefixes (milli, micro, etc.).
|
||||
if (Math.abs(value) < 1) return value.toFixed(decimals);
|
||||
// SI prefix, e.g. 1234567 will give '1.2M' at precision 1.
|
||||
let prefix = d3.formatPrefix(value);
|
||||
return prefix.scale(value).toFixed(decimals) + prefix.symbol;
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Compute the average of each time series.
|
||||
*/
|
||||
averages: function () {
|
||||
for (let c of this.curves.values()) {
|
||||
let length = c.values.length;
|
||||
if (length > 0) {
|
||||
let total = 0;
|
||||
c.values.forEach(v => total += v.value);
|
||||
c.average = (total / length);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Bisect a time serie to find the data point immediately left of `time`.
|
||||
*/
|
||||
bisectTime: d3.bisector(d => d.time).left,
|
||||
|
||||
/**
|
||||
* Get all curve values at a given time.
|
||||
*/
|
||||
valuesAt: function (time) {
|
||||
let values = { time: time };
|
||||
|
||||
for (let id of this.curves.keys()) {
|
||||
let curve = this.curves.get(id);
|
||||
|
||||
// Find the closest value just before `time`.
|
||||
let i = this.bisectTime(curve.values, time);
|
||||
if (i < 0) {
|
||||
// Curve starts after `time`, use first value.
|
||||
values[id] = curve.values[0].value;
|
||||
} else if (i > curve.values.length - 2) {
|
||||
// Curve ends before `time`, use last value.
|
||||
values[id] = curve.values[curve.values.length - 1].value;
|
||||
} else {
|
||||
// Curve has two values around `time`, interpolate.
|
||||
let v1 = curve.values[i],
|
||||
v2 = curve.values[i + 1],
|
||||
delta = (time - v1.time) / (v2.time - v1.time);
|
||||
values[id] = v1.value + (v2.value - v1.time) * delta;
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
};
|
@ -1,31 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- 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/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://devtools/locale/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf8"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/deck.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/monitor.css" type="text/css"/>
|
||||
<script src="chrome://devtools/content/shared/vendor/d3.js"></script>
|
||||
<script type="application/javascript" src="monitor.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="controls">
|
||||
<a href="https://developer.mozilla.org/docs/Tools/WebIDE/Monitor" target="_blank">&monitor_help;</a>
|
||||
<a id="close">&deck_close;</a>
|
||||
</div>
|
||||
|
||||
<h1>&monitor_title;</h1>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -177,9 +177,6 @@ var UI = {
|
||||
this.updateTitle();
|
||||
this.updateCommands();
|
||||
break;
|
||||
case "install-progress":
|
||||
this.updateProgress(Math.round(100 * details.bytesSent / details.totalBytes));
|
||||
break;
|
||||
case "runtime-targets":
|
||||
this.autoSelectProject();
|
||||
break;
|
||||
@ -213,13 +210,6 @@ var UI = {
|
||||
_busyOperationDescription: null,
|
||||
_busyPromise: null,
|
||||
|
||||
updateProgress: function (percent) {
|
||||
let progress = document.querySelector("#action-busy-determined");
|
||||
progress.mode = "determined";
|
||||
progress.value = percent;
|
||||
this.setupBusyTimeout();
|
||||
},
|
||||
|
||||
busy: function () {
|
||||
let win = document.querySelector("window");
|
||||
win.classList.add("busy");
|
||||
@ -389,7 +379,6 @@ var UI = {
|
||||
}
|
||||
|
||||
// Runtime commands
|
||||
let monitorCmd = document.querySelector("#cmd_showMonitor");
|
||||
let screenshotCmd = document.querySelector("#cmd_takeScreenshot");
|
||||
let detailsCmd = document.querySelector("#cmd_showRuntimeDetails");
|
||||
let disconnectCmd = document.querySelector("#cmd_disconnectRuntime");
|
||||
@ -398,7 +387,6 @@ var UI = {
|
||||
|
||||
if (AppManager.connected) {
|
||||
if (AppManager.deviceFront) {
|
||||
monitorCmd.removeAttribute("disabled");
|
||||
detailsCmd.removeAttribute("disabled");
|
||||
screenshotCmd.removeAttribute("disabled");
|
||||
}
|
||||
@ -407,7 +395,6 @@ var UI = {
|
||||
}
|
||||
disconnectCmd.removeAttribute("disabled");
|
||||
} else {
|
||||
monitorCmd.setAttribute("disabled", "true");
|
||||
detailsCmd.setAttribute("disabled", "true");
|
||||
screenshotCmd.setAttribute("disabled", "true");
|
||||
disconnectCmd.setAttribute("disabled", "true");
|
||||
@ -891,10 +878,6 @@ var Cmds = {
|
||||
UI.selectDeckPanel("devicepreferences");
|
||||
},
|
||||
|
||||
showMonitor: function () {
|
||||
UI.selectDeckPanel("monitor");
|
||||
},
|
||||
|
||||
play: Task.async(function* () {
|
||||
let busy;
|
||||
switch (AppManager.selectedProject.type) {
|
||||
|
@ -44,7 +44,6 @@
|
||||
<command id="cmd_showProjectPanel" oncommand="Cmds.showProjectPanel()"/>
|
||||
<command id="cmd_showRuntimePanel" oncommand="Cmds.showRuntimePanel()"/>
|
||||
<command id="cmd_disconnectRuntime" oncommand="Cmds.disconnectRuntime()" label="&runtimeMenu_disconnect_label;"/>
|
||||
<command id="cmd_showMonitor" oncommand="Cmds.showMonitor()" label="&runtimeMenu_showMonitor_label;"/>
|
||||
<command id="cmd_showRuntimeDetails" oncommand="Cmds.showRuntimeDetails()" label="&runtimeMenu_showDetails_label;"/>
|
||||
<command id="cmd_takeScreenshot" oncommand="Cmds.takeScreenshot()" label="&runtimeMenu_takeScreenshot_label;"/>
|
||||
<command id="cmd_showAddons" oncommand="Cmds.showAddons()"/>
|
||||
@ -80,7 +79,6 @@
|
||||
|
||||
<menu id="menu-runtime" label="&runtimeMenu_label;" accesskey="&runtimeMenu_accesskey;">
|
||||
<menupopup id="menu-runtime-popup">
|
||||
<menuitem command="cmd_showMonitor" accesskey="&runtimeMenu_showMonitor_accesskey;"/>
|
||||
<menuitem command="cmd_takeScreenshot" accesskey="&runtimeMenu_takeScreenshot_accesskey;"/>
|
||||
<menuitem command="cmd_showRuntimeDetails" accesskey="&runtimeMenu_showDetails_accesskey;"/>
|
||||
<menuitem command="cmd_showDevicePrefs" accesskey="&runtimeMenu_showDevicePrefs_accesskey;"/>
|
||||
@ -151,7 +149,6 @@
|
||||
<iframe id="deck-panel-addons" flex="1" src="addons.xhtml"/>
|
||||
<iframe id="deck-panel-prefs" flex="1" src="prefs.xhtml"/>
|
||||
<iframe id="deck-panel-runtimedetails" flex="1" lazysrc="runtimedetails.xhtml"/>
|
||||
<iframe id="deck-panel-monitor" flex="1" lazysrc="monitor.xhtml"/>
|
||||
<iframe id="deck-panel-devicepreferences" flex="1" lazysrc="devicepreferences.xhtml"/>
|
||||
</deck>
|
||||
<splitter class="devtools-side-splitter" id="runtime-listing-splitter"/>
|
||||
|
@ -13,7 +13,6 @@ const {AppProjects} = require("devtools/client/webide/modules/app-projects");
|
||||
const TabStore = require("devtools/client/webide/modules/tab-store");
|
||||
const {AppValidator} = require("devtools/client/webide/modules/app-validator");
|
||||
const {ConnectionManager, Connection} = require("devtools/shared/client/connection-manager");
|
||||
const {AppActorFront} = require("devtools/shared/apps/app-actor-front");
|
||||
const {getDeviceFront} = require("devtools/shared/fronts/device");
|
||||
const {getPreferenceFront} = require("devtools/shared/fronts/preference");
|
||||
const {Task} = require("devtools/shared/task");
|
||||
@ -55,8 +54,6 @@ var AppManager = exports.AppManager = {
|
||||
RuntimeScanners.enable();
|
||||
this._rebuildRuntimeList();
|
||||
|
||||
this.onInstallProgress = this.onInstallProgress.bind(this);
|
||||
|
||||
this._telemetry = new Telemetry();
|
||||
},
|
||||
|
||||
@ -93,10 +90,6 @@ var AppManager = exports.AppManager = {
|
||||
* |cancel| callback that will abort the project change if desired.
|
||||
* connection:
|
||||
* The connection status has changed (connected, disconnected, etc.)
|
||||
* install-progress:
|
||||
* A project being installed to a runtime has made further progress. This
|
||||
* event contains additional details about exactly how far the process is
|
||||
* when such information is available.
|
||||
* project:
|
||||
* The selected project has changed.
|
||||
* project-started:
|
||||
@ -111,8 +104,6 @@ var AppManager = exports.AppManager = {
|
||||
* name, manifest details, etc.
|
||||
* runtime:
|
||||
* The selected runtime has changed.
|
||||
* runtime-apps-icons:
|
||||
* The list of URLs for the runtime app icons are available.
|
||||
* runtime-global-actors:
|
||||
* The list of global actors for the entire runtime (but not actors for a
|
||||
* specific tab or app) are now available, so we can test for features
|
||||
@ -160,38 +151,12 @@ var AppManager = exports.AppManager = {
|
||||
}
|
||||
|
||||
if (!this.connected) {
|
||||
if (this._appsFront) {
|
||||
this._appsFront.off("install-progress", this.onInstallProgress);
|
||||
this._appsFront.unwatchApps();
|
||||
this._appsFront = null;
|
||||
}
|
||||
this._listTabsResponse = null;
|
||||
} else {
|
||||
this.connection.client.listTabs().then((response) => {
|
||||
if (response.webappsActor) {
|
||||
let front = new AppActorFront(this.connection.client,
|
||||
response);
|
||||
front.on("install-progress", this.onInstallProgress);
|
||||
front.watchApps(() => this.checkIfProjectIsRunning())
|
||||
.then(() => {
|
||||
// This can't be done earlier as many operations
|
||||
// in the apps actor require watchApps to be called
|
||||
// first.
|
||||
this._appsFront = front;
|
||||
this._listTabsResponse = response;
|
||||
this._recordRuntimeInfo();
|
||||
this.update("runtime-global-actors");
|
||||
})
|
||||
.then(() => {
|
||||
this.checkIfProjectIsRunning();
|
||||
this.update("runtime-targets", { type: "apps" });
|
||||
front.fetchIcons().then(() => this.update("runtime-apps-icons"));
|
||||
});
|
||||
} else {
|
||||
this._listTabsResponse = response;
|
||||
this._recordRuntimeInfo();
|
||||
this.update("runtime-global-actors");
|
||||
}
|
||||
this._listTabsResponse = response;
|
||||
this._recordRuntimeInfo();
|
||||
this.update("runtime-global-actors");
|
||||
});
|
||||
}
|
||||
|
||||
@ -211,10 +176,6 @@ var AppManager = exports.AppManager = {
|
||||
}
|
||||
},
|
||||
|
||||
onInstallProgress: function (event, details) {
|
||||
this.update("install-progress", details);
|
||||
},
|
||||
|
||||
isProjectRunning: function () {
|
||||
if (this.selectedProject.type == "mainProcess" ||
|
||||
this.selectedProject.type == "tab") {
|
||||
|
@ -42,7 +42,6 @@ ProjectList.prototype = {
|
||||
// See AppManager.update() for descriptions of what these events mean.
|
||||
switch (what) {
|
||||
case "project-removed":
|
||||
case "runtime-apps-icons":
|
||||
case "runtime-targets":
|
||||
case "connection":
|
||||
this.update(details);
|
||||
|
@ -12,7 +12,6 @@ webide.jar:
|
||||
skin/deck.css (deck.css)
|
||||
skin/addons.css (addons.css)
|
||||
skin/runtimedetails.css (runtimedetails.css)
|
||||
skin/monitor.css (monitor.css)
|
||||
skin/config-view.css (config-view.css)
|
||||
skin/wifi-auth.css (wifi-auth.css)
|
||||
skin/panel-listing.css (panel-listing.css)
|
||||
|
@ -1,86 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
/* Graph */
|
||||
.graph {
|
||||
height: 500px;
|
||||
width: 100%;
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
margin-bottom: 30px;
|
||||
background-color: white;
|
||||
}
|
||||
.graph > svg, .sidebar {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
.disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.graph.disabled {
|
||||
height: 30px;
|
||||
}
|
||||
.graph.disabled > svg {
|
||||
visibility: hidden;
|
||||
}
|
||||
.curve path, .event-slot line {
|
||||
fill: none;
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
.axis line {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
.axis path {
|
||||
fill: none;
|
||||
stroke: black;
|
||||
stroke-width: 1px;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
.tick text, .x.ruler text, .y.ruler text {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.x.ruler text {
|
||||
text-anchor: middle;
|
||||
}
|
||||
.y.ruler text {
|
||||
text-anchor: end;
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar {
|
||||
width: 150px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.sidebar label {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
}
|
||||
.sidebar span:not(.color) {
|
||||
vertical-align: 13%;
|
||||
}
|
||||
.sidebar input {
|
||||
visibility: hidden;
|
||||
}
|
||||
.sidebar input:hover {
|
||||
visibility: visible;
|
||||
}
|
||||
.graph-title {
|
||||
margin-top: 5px;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.legend-color {
|
||||
display: inline-block;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
margin-left: 1px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
.legend-id {
|
||||
font-size: .9em;
|
||||
}
|
||||
.graph.disabled > .sidebar > .legend {
|
||||
display: none;
|
||||
}
|
@ -9,7 +9,6 @@ pref("devtools.webide.restoreLastProject", true);
|
||||
pref("devtools.webide.enableLocalRuntime", false);
|
||||
pref("devtools.webide.adbAddonURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/adb-helper/#OS#/adbhelper-#OS#-latest.xpi");
|
||||
pref("devtools.webide.adbAddonID", "adbhelper@mozilla.org");
|
||||
pref("devtools.webide.monitorWebSocketURL", "ws://localhost:9000");
|
||||
pref("devtools.webide.lastConnectedRuntime", "");
|
||||
pref("devtools.webide.lastSelectedProject", "");
|
||||
pref("devtools.webide.zoom", "1");
|
||||
|
@ -1,147 +0,0 @@
|
||||
/* 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";
|
||||
|
||||
const {Ci, Cc} = require("chrome");
|
||||
const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
|
||||
const Services = require("Services");
|
||||
|
||||
function MonitorActor(connection) {
|
||||
this.conn = connection;
|
||||
this._updates = [];
|
||||
this._started = false;
|
||||
}
|
||||
|
||||
MonitorActor.prototype = {
|
||||
actorPrefix: "monitor",
|
||||
|
||||
// Updates.
|
||||
|
||||
_sendUpdate: function () {
|
||||
if (this._started) {
|
||||
this.conn.sendActorEvent(this.actorID, "update", { data: this._updates });
|
||||
this._updates = [];
|
||||
}
|
||||
},
|
||||
|
||||
// Methods available from the front.
|
||||
|
||||
start: function () {
|
||||
if (!this._started) {
|
||||
this._started = true;
|
||||
Services.obs.addObserver(this, "devtools-monitor-update");
|
||||
Services.obs.notifyObservers(null, "devtools-monitor-start");
|
||||
this._agents.forEach(agent => this._startAgent(agent));
|
||||
}
|
||||
return {};
|
||||
},
|
||||
|
||||
stop: function () {
|
||||
if (this._started) {
|
||||
this._agents.forEach(agent => agent.stop());
|
||||
Services.obs.notifyObservers(null, "devtools-monitor-stop");
|
||||
Services.obs.removeObserver(this, "devtools-monitor-update");
|
||||
this._started = false;
|
||||
}
|
||||
return {};
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.stop();
|
||||
},
|
||||
|
||||
// nsIObserver.
|
||||
|
||||
observe: function (subject, topic, data) {
|
||||
if (topic == "devtools-monitor-update") {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch (e) {
|
||||
console.error("Observer notification data is not a valid JSON-string:",
|
||||
data, e.message);
|
||||
return;
|
||||
}
|
||||
if (!Array.isArray(data)) {
|
||||
this._updates.push(data);
|
||||
} else {
|
||||
this._updates = this._updates.concat(data);
|
||||
}
|
||||
this._sendUpdate();
|
||||
}
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
|
||||
|
||||
// Update agents (see USSAgent for an example).
|
||||
|
||||
_agents: [],
|
||||
|
||||
_startAgent: function (agent) {
|
||||
try {
|
||||
agent.start();
|
||||
} catch (e) {
|
||||
this._removeAgent(agent);
|
||||
}
|
||||
},
|
||||
|
||||
_addAgent: function (agent) {
|
||||
this._agents.push(agent);
|
||||
if (this._started) {
|
||||
this._startAgent(agent);
|
||||
}
|
||||
},
|
||||
|
||||
_removeAgent: function (agent) {
|
||||
let index = this._agents.indexOf(agent);
|
||||
if (index > -1) {
|
||||
this._agents.splice(index, 1);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
MonitorActor.prototype.requestTypes = {
|
||||
"start": MonitorActor.prototype.start,
|
||||
"stop": MonitorActor.prototype.stop,
|
||||
};
|
||||
|
||||
exports.MonitorActor = MonitorActor;
|
||||
|
||||
var USSAgent = {
|
||||
_mgr: null,
|
||||
_timeout: null,
|
||||
_packet: {
|
||||
graph: "USS",
|
||||
time: null,
|
||||
value: null
|
||||
},
|
||||
|
||||
start: function () {
|
||||
USSAgent._mgr = Cc["@mozilla.org/memory-reporter-manager;1"].getService(
|
||||
Ci.nsIMemoryReporterManager);
|
||||
if (!USSAgent._mgr.residentUnique) {
|
||||
throw new Error("Couldn't get USS.");
|
||||
}
|
||||
USSAgent.update();
|
||||
},
|
||||
|
||||
update: function () {
|
||||
if (!USSAgent._mgr) {
|
||||
USSAgent.stop();
|
||||
return;
|
||||
}
|
||||
USSAgent._packet.time = Date.now();
|
||||
USSAgent._packet.value = USSAgent._mgr.residentUnique;
|
||||
Services.obs.notifyObservers(null, "devtools-monitor-update",
|
||||
JSON.stringify(USSAgent._packet));
|
||||
USSAgent._timeout = setTimeout(USSAgent.update, 300);
|
||||
},
|
||||
|
||||
stop: function () {
|
||||
clearTimeout(USSAgent._timeout);
|
||||
USSAgent._mgr = null;
|
||||
}
|
||||
};
|
||||
|
||||
MonitorActor.prototype._addAgent(USSAgent);
|
@ -40,7 +40,6 @@ DevToolsModules(
|
||||
'highlighters.js',
|
||||
'layout.js',
|
||||
'memory.js',
|
||||
'monitor.js',
|
||||
'object.js',
|
||||
'pause-scoped.js',
|
||||
'perf.js',
|
||||
@ -87,9 +86,6 @@ with Files('csscoverage.js'):
|
||||
with Files('memory.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Memory')
|
||||
|
||||
with Files('monitor.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools')
|
||||
|
||||
with Files('performance*'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Performance Tools (Profiler/Timeline)')
|
||||
|
||||
|
@ -519,11 +519,6 @@ var DebuggerServer = {
|
||||
constructor: "CSSUsageActor",
|
||||
type: { tab: true }
|
||||
});
|
||||
this.registerModule("devtools/server/actors/monitor", {
|
||||
prefix: "monitor",
|
||||
constructor: "MonitorActor",
|
||||
type: { tab: true }
|
||||
});
|
||||
this.registerModule("devtools/server/actors/timeline", {
|
||||
prefix: "timeline",
|
||||
constructor: "TimelineActor",
|
||||
|
@ -1,75 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test the monitor actor.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
function run_test() {
|
||||
let EventEmitter = require("devtools/shared/old-event-emitter");
|
||||
|
||||
function MonitorClient(client, form) {
|
||||
this.client = client;
|
||||
this.actor = form.monitorActor;
|
||||
this.events = ["update"];
|
||||
|
||||
EventEmitter.decorate(this);
|
||||
client.registerClient(this);
|
||||
}
|
||||
MonitorClient.prototype.destroy = function () {
|
||||
this.client.unregisterClient(this);
|
||||
};
|
||||
MonitorClient.prototype.start = function (callback) {
|
||||
this.client.request({
|
||||
to: this.actor,
|
||||
type: "start"
|
||||
}, callback);
|
||||
};
|
||||
MonitorClient.prototype.stop = function (callback) {
|
||||
this.client.request({
|
||||
to: this.actor,
|
||||
type: "stop"
|
||||
}, callback);
|
||||
};
|
||||
|
||||
let monitor, client;
|
||||
|
||||
// Start the monitor actor.
|
||||
get_chrome_actors((c, form) => {
|
||||
client = c;
|
||||
monitor = new MonitorClient(client, form);
|
||||
monitor.on("update", gotUpdate);
|
||||
monitor.start(update);
|
||||
});
|
||||
|
||||
let time = Date.now();
|
||||
|
||||
function update() {
|
||||
let event = {
|
||||
graph: "Test",
|
||||
curve: "test",
|
||||
value: 42,
|
||||
time: time,
|
||||
};
|
||||
Services.obs.notifyObservers(null, "devtools-monitor-update", JSON.stringify(event));
|
||||
}
|
||||
|
||||
function gotUpdate(type, packet) {
|
||||
packet.data.forEach(function (event) {
|
||||
// Ignore updates that were not sent by this test.
|
||||
if (event.graph === "Test") {
|
||||
Assert.equal(event.curve, "test");
|
||||
Assert.equal(event.value, 42);
|
||||
Assert.equal(event.time, time);
|
||||
monitor.stop(function (response) {
|
||||
monitor.destroy();
|
||||
finishClient(client);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
do_test_pending();
|
||||
}
|
@ -216,7 +216,6 @@ reason = bug 937197
|
||||
[test_protocolSpec.js]
|
||||
[test_registerClient.js]
|
||||
[test_client_request.js]
|
||||
[test_monitor_actor.js]
|
||||
[test_symbols-01.js]
|
||||
[test_symbols-02.js]
|
||||
[test_get-executable-lines.js]
|
||||
|
@ -1,817 +0,0 @@
|
||||
/* 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";
|
||||
|
||||
const {Ci, Cc, Cr} = require("chrome");
|
||||
const {OS} = require("resource://gre/modules/osfile.jsm");
|
||||
const {FileUtils} = require("resource://gre/modules/FileUtils.jsm");
|
||||
const {NetUtil} = require("resource://gre/modules/NetUtil.jsm");
|
||||
const promise = require("promise");
|
||||
const defer = require("devtools/shared/defer");
|
||||
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
const EventEmitter = require("devtools/shared/old-event-emitter");
|
||||
|
||||
// Bug 1188401: When loaded from xpcshell tests, we do not have browser/ files
|
||||
// and can't load target.js. Should be fixed by bug 912121.
|
||||
loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
|
||||
|
||||
// XXX: bug 912476 make this module a real protocol.js front
|
||||
// by converting webapps actor to protocol.js
|
||||
|
||||
const PR_USEC_PER_MSEC = 1000;
|
||||
const PR_RDWR = 0x04;
|
||||
const PR_CREATE_FILE = 0x08;
|
||||
const PR_TRUNCATE = 0x20;
|
||||
|
||||
const CHUNK_SIZE = 10000;
|
||||
|
||||
const appTargets = new Map();
|
||||
|
||||
function addDirToZip(writer, dir, basePath) {
|
||||
let files = dir.directoryEntries;
|
||||
|
||||
while (files.hasMoreElements()) {
|
||||
let file = files.getNext().QueryInterface(Ci.nsIFile);
|
||||
|
||||
if (file.isHidden() ||
|
||||
file.isSpecial() ||
|
||||
file.equals(writer.file)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (file.isDirectory()) {
|
||||
writer.addEntryDirectory(basePath + file.leafName + "/",
|
||||
file.lastModifiedTime * PR_USEC_PER_MSEC,
|
||||
true);
|
||||
addDirToZip(writer, file, basePath + file.leafName + "/");
|
||||
} else {
|
||||
writer.addEntryFile(basePath + file.leafName,
|
||||
Ci.nsIZipWriter.COMPRESSION_DEFAULT,
|
||||
file,
|
||||
true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getResultText(code) {
|
||||
/*
|
||||
* If it ever becomes necessary to convert the nsresult to a useful
|
||||
* string here, we'll need an API for that.
|
||||
*/
|
||||
return { name: "Error code", message: code + "" };
|
||||
}
|
||||
|
||||
function zipDirectory(zipFile, dirToArchive) {
|
||||
let deferred = defer();
|
||||
let writer = Cc["@mozilla.org/zipwriter;1"].createInstance(Ci.nsIZipWriter);
|
||||
writer.open(zipFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
|
||||
|
||||
addDirToZip(writer, dirToArchive, "");
|
||||
|
||||
writer.processQueue({
|
||||
onStartRequest: function onStartRequest(request, context) {},
|
||||
onStopRequest: (request, context, status) => {
|
||||
if (status == Cr.NS_OK) {
|
||||
writer.close();
|
||||
deferred.resolve(zipFile);
|
||||
} else {
|
||||
let { name, message } = getResultText(status);
|
||||
deferred.reject(name + ": " + message);
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function uploadPackage(client, webappsActor, packageFile, progressCallback) {
|
||||
if (client.traits.bulk) {
|
||||
return uploadPackageBulk(client, webappsActor, packageFile, progressCallback);
|
||||
}
|
||||
return uploadPackageJSON(client, webappsActor, packageFile, progressCallback);
|
||||
}
|
||||
|
||||
function uploadPackageJSON(client, webappsActor, packageFile, progressCallback) {
|
||||
let deferred = defer();
|
||||
|
||||
let request = {
|
||||
to: webappsActor,
|
||||
type: "uploadPackage"
|
||||
};
|
||||
client.request(request, (res) => {
|
||||
openFile(res.actor);
|
||||
});
|
||||
|
||||
let fileSize;
|
||||
let bytesRead = 0;
|
||||
|
||||
function emitProgress() {
|
||||
progressCallback({
|
||||
bytesSent: bytesRead,
|
||||
totalBytes: fileSize
|
||||
});
|
||||
}
|
||||
|
||||
function openFile(actor) {
|
||||
let openedFile;
|
||||
OS.File.open(packageFile.path).then(file => {
|
||||
openedFile = file;
|
||||
return openedFile.stat();
|
||||
}).then(fileInfo => {
|
||||
fileSize = fileInfo.size;
|
||||
emitProgress();
|
||||
uploadChunk(actor, openedFile);
|
||||
});
|
||||
}
|
||||
function uploadChunk(actor, file) {
|
||||
file.read(CHUNK_SIZE).then(function (bytes) {
|
||||
bytesRead += bytes.length;
|
||||
emitProgress();
|
||||
// To work around the fact that JSON.stringify translates the typed
|
||||
// array to object, we are encoding the typed array here into a string
|
||||
let chunk = String.fromCharCode.apply(null, bytes);
|
||||
|
||||
let chunkRequest = {
|
||||
to: actor,
|
||||
type: "chunk",
|
||||
chunk,
|
||||
};
|
||||
client.request(chunkRequest, (res) => {
|
||||
if (bytes.length == CHUNK_SIZE) {
|
||||
uploadChunk(actor, file);
|
||||
} else {
|
||||
file.close().then(function () {
|
||||
endsUpload(actor);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
function endsUpload(actor) {
|
||||
let doneRequest = {
|
||||
to: actor,
|
||||
type: "done"
|
||||
};
|
||||
client.request(doneRequest, (res) => {
|
||||
deferred.resolve(actor);
|
||||
});
|
||||
}
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function uploadPackageBulk(client, webappsActor, packageFile, progressCallback) {
|
||||
let deferred = defer();
|
||||
|
||||
let request = {
|
||||
to: webappsActor,
|
||||
type: "uploadPackage",
|
||||
bulk: true
|
||||
};
|
||||
client.request(request, (res) => {
|
||||
startBulkUpload(res.actor);
|
||||
});
|
||||
|
||||
function startBulkUpload(actor) {
|
||||
console.log("Starting bulk upload");
|
||||
let fileSize = packageFile.fileSize;
|
||||
console.log("File size: " + fileSize);
|
||||
|
||||
let streamRequest = client.startBulkRequest({
|
||||
actor: actor,
|
||||
type: "stream",
|
||||
length: fileSize
|
||||
});
|
||||
|
||||
streamRequest.on("bulk-send-ready", ({copyFrom}) => {
|
||||
NetUtil.asyncFetch({
|
||||
uri: NetUtil.newURI(packageFile),
|
||||
loadUsingSystemPrincipal: true
|
||||
}, function (inputStream) {
|
||||
let copying = copyFrom(inputStream);
|
||||
copying.on("progress", (e, progress) => {
|
||||
progressCallback(progress);
|
||||
});
|
||||
copying.then(() => {
|
||||
console.log("Bulk upload done");
|
||||
inputStream.close();
|
||||
deferred.resolve(actor);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function removeServerTemporaryFile(client, fileActor) {
|
||||
let request = {
|
||||
to: fileActor,
|
||||
type: "remove"
|
||||
};
|
||||
client.request(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* progressCallback argument:
|
||||
* Function called as packaged app installation proceeds.
|
||||
* The progress object passed to this function contains:
|
||||
* * bytesSent: The number of bytes sent so far
|
||||
* * totalBytes: The total number of bytes to send
|
||||
*/
|
||||
function installPackaged(client, webappsActor, packagePath, appId, progressCallback) {
|
||||
let deferred = defer();
|
||||
let file = FileUtils.File(packagePath);
|
||||
let packagePromise;
|
||||
if (file.isDirectory()) {
|
||||
let tmpZipFile = FileUtils.getDir("TmpD", [], true);
|
||||
tmpZipFile.append("application.zip");
|
||||
tmpZipFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
|
||||
packagePromise = zipDirectory(tmpZipFile, file);
|
||||
} else {
|
||||
packagePromise = promise.resolve(file);
|
||||
}
|
||||
packagePromise.then((zipFile) => {
|
||||
uploadPackage(client, webappsActor, zipFile, progressCallback).then((fileActor) => {
|
||||
let request = {
|
||||
to: webappsActor,
|
||||
type: "install",
|
||||
appId: appId,
|
||||
upload: fileActor
|
||||
};
|
||||
client.request(request, (res) => {
|
||||
// If the install method immediatly fails,
|
||||
// reject immediatly the installPackaged promise.
|
||||
// Otherwise, wait for webappsEvent for completion
|
||||
if (res.error) {
|
||||
deferred.reject(res);
|
||||
}
|
||||
if ("error" in res) {
|
||||
deferred.reject({error: res.error, message: res.message});
|
||||
} else {
|
||||
deferred.resolve({appId: res.appId});
|
||||
}
|
||||
});
|
||||
// Ensure deleting the temporary package file, but only if that a temporary
|
||||
// package created when we pass a directory as `packagePath`
|
||||
if (zipFile != file) {
|
||||
zipFile.remove(false);
|
||||
}
|
||||
// In case of success or error, ensure deleting the temporary package file
|
||||
// also created on the device, but only once install request is done
|
||||
deferred.promise.then(
|
||||
() => removeServerTemporaryFile(client, fileActor),
|
||||
() => removeServerTemporaryFile(client, fileActor));
|
||||
});
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
exports.installPackaged = installPackaged;
|
||||
|
||||
function installHosted(client, webappsActor, appId, metadata, manifest) {
|
||||
let deferred = defer();
|
||||
let request = {
|
||||
to: webappsActor,
|
||||
type: "install",
|
||||
appId: appId,
|
||||
metadata: metadata,
|
||||
manifest: manifest
|
||||
};
|
||||
client.request(request, (res) => {
|
||||
if (res.error) {
|
||||
deferred.reject(res);
|
||||
}
|
||||
if ("error" in res) {
|
||||
deferred.reject({error: res.error, message: res.message});
|
||||
} else {
|
||||
deferred.resolve({appId: res.appId});
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
exports.installHosted = installHosted;
|
||||
|
||||
function getTargetForApp(client, webappsActor, manifestURL) {
|
||||
// Ensure always returning the exact same JS object for a target
|
||||
// of the same app in order to show only one toolbox per app and
|
||||
// avoid re-creating lot of objects twice.
|
||||
let existingTarget = appTargets.get(manifestURL);
|
||||
if (existingTarget) {
|
||||
return promise.resolve(existingTarget);
|
||||
}
|
||||
|
||||
let deferred = defer();
|
||||
let request = {
|
||||
to: webappsActor,
|
||||
type: "getAppActor",
|
||||
manifestURL: manifestURL,
|
||||
};
|
||||
client.request(request, (res) => {
|
||||
if (res.error) {
|
||||
deferred.reject(res.error);
|
||||
} else {
|
||||
let options = {
|
||||
form: res.actor,
|
||||
client: client,
|
||||
chrome: false
|
||||
};
|
||||
|
||||
TargetFactory.forRemoteTab(options).then((target) => {
|
||||
target.isApp = true;
|
||||
appTargets.set(manifestURL, target);
|
||||
target.on("close", () => {
|
||||
appTargets.delete(manifestURL);
|
||||
});
|
||||
deferred.resolve(target);
|
||||
}, (error) => {
|
||||
deferred.reject(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
exports.getTargetForApp = getTargetForApp;
|
||||
|
||||
function reloadApp(client, webappsActor, manifestURL) {
|
||||
return getTargetForApp(
|
||||
client, webappsActor, manifestURL
|
||||
).then((target) => {
|
||||
// Request the ContentActor to reload the app
|
||||
let request = {
|
||||
to: target.form.actor,
|
||||
type: "reload",
|
||||
options: {
|
||||
force: true
|
||||
},
|
||||
manifestURL,
|
||||
};
|
||||
return client.request(request);
|
||||
}, () => {
|
||||
throw new Error("Not running");
|
||||
});
|
||||
}
|
||||
exports.reloadApp = reloadApp;
|
||||
|
||||
function launchApp(client, webappsActor, manifestURL) {
|
||||
return client.request({
|
||||
to: webappsActor,
|
||||
type: "launch",
|
||||
manifestURL: manifestURL
|
||||
});
|
||||
}
|
||||
exports.launchApp = launchApp;
|
||||
|
||||
function closeApp(client, webappsActor, manifestURL) {
|
||||
return client.request({
|
||||
to: webappsActor,
|
||||
type: "close",
|
||||
manifestURL: manifestURL
|
||||
});
|
||||
}
|
||||
exports.closeApp = closeApp;
|
||||
|
||||
function getTarget(client, form) {
|
||||
let deferred = defer();
|
||||
let options = {
|
||||
form: form,
|
||||
client: client,
|
||||
chrome: false
|
||||
};
|
||||
|
||||
TargetFactory.forRemoteTab(options).then((target) => {
|
||||
target.isApp = true;
|
||||
deferred.resolve(target);
|
||||
}, (error) => {
|
||||
deferred.reject(error);
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* `App` instances are client helpers to manage a given app
|
||||
* and its the tab actors
|
||||
*/
|
||||
function App(client, webappsActor, manifest) {
|
||||
this.client = client;
|
||||
this.webappsActor = webappsActor;
|
||||
this.manifest = manifest;
|
||||
|
||||
// This attribute is managed by the AppActorFront
|
||||
this.running = false;
|
||||
|
||||
this.iconURL = null;
|
||||
}
|
||||
|
||||
App.prototype = {
|
||||
getForm: function () {
|
||||
if (this._form) {
|
||||
return promise.resolve(this._form);
|
||||
}
|
||||
let request = {
|
||||
to: this.webappsActor,
|
||||
type: "getAppActor",
|
||||
manifestURL: this.manifest.manifestURL
|
||||
};
|
||||
return this.client.request(request).then(res => {
|
||||
this._form = res.actor;
|
||||
return this._form;
|
||||
});
|
||||
},
|
||||
|
||||
getTarget: function () {
|
||||
if (this._target) {
|
||||
return promise.resolve(this._target);
|
||||
}
|
||||
return this.getForm().then(
|
||||
(form) => getTarget(this.client, form)
|
||||
).then((target) => {
|
||||
target.on("close", () => {
|
||||
delete this._form;
|
||||
delete this._target;
|
||||
});
|
||||
this._target = target;
|
||||
return this._target;
|
||||
});
|
||||
},
|
||||
|
||||
launch: function () {
|
||||
return launchApp(this.client, this.webappsActor,
|
||||
this.manifest.manifestURL);
|
||||
},
|
||||
|
||||
reload: function () {
|
||||
return reloadApp(this.client, this.webappsActor,
|
||||
this.manifest.manifestURL);
|
||||
},
|
||||
|
||||
close: function () {
|
||||
return closeApp(this.client, this.webappsActor,
|
||||
this.manifest.manifestURL);
|
||||
},
|
||||
|
||||
getIcon: function () {
|
||||
if (this.iconURL) {
|
||||
return promise.resolve(this.iconURL);
|
||||
}
|
||||
|
||||
let deferred = defer();
|
||||
|
||||
let request = {
|
||||
to: this.webappsActor,
|
||||
type: "getIconAsDataURL",
|
||||
manifestURL: this.manifest.manifestURL
|
||||
};
|
||||
|
||||
this.client.request(request, res => {
|
||||
if (res.error) {
|
||||
deferred.reject(res.message || res.error);
|
||||
} else if (res.url) {
|
||||
this.iconURL = res.url;
|
||||
deferred.resolve(res.url);
|
||||
} else {
|
||||
deferred.reject("Unable to fetch app icon");
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* `AppActorFront` is a client for the webapps actor.
|
||||
*/
|
||||
function AppActorFront(client, form) {
|
||||
this.client = client;
|
||||
this.actor = form.webappsActor;
|
||||
|
||||
this._clientListener = this._clientListener.bind(this);
|
||||
this._onInstallProgress = this._onInstallProgress.bind(this);
|
||||
|
||||
this._listeners = [];
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
AppActorFront.prototype = {
|
||||
/**
|
||||
* List `App` instances for all currently running apps.
|
||||
*/
|
||||
get runningApps() {
|
||||
if (!this._apps) {
|
||||
throw new Error("Can't get running apps before calling watchApps.");
|
||||
}
|
||||
let r = new Map();
|
||||
for (let [manifestURL, app] of this._apps) {
|
||||
if (app.running) {
|
||||
r.set(manifestURL, app);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
},
|
||||
|
||||
/**
|
||||
* List `App` instances for all installed apps.
|
||||
*/
|
||||
get apps() {
|
||||
if (!this._apps) {
|
||||
throw new Error("Can't get apps before calling watchApps.");
|
||||
}
|
||||
return this._apps;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a `App` object instance for the given manifest URL
|
||||
* (and cache it per AppActorFront object)
|
||||
*/
|
||||
_getApp: function (manifestURL) {
|
||||
let app = this._apps ? this._apps.get(manifestURL) : null;
|
||||
if (app) {
|
||||
return promise.resolve(app);
|
||||
}
|
||||
let request = {
|
||||
to: this.actor,
|
||||
type: "getApp",
|
||||
manifestURL,
|
||||
};
|
||||
return this.client.request(request).then(res => {
|
||||
app = new App(this.client, this.actor, res.app);
|
||||
if (this._apps) {
|
||||
this._apps.set(manifestURL, app);
|
||||
}
|
||||
return app;
|
||||
}, e => {
|
||||
console.error("Unable to retrieve app", manifestURL, e);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts watching for app opening/closing installing/uninstalling.
|
||||
* Needs to be called before using `apps` or `runningApps` attributes.
|
||||
*/
|
||||
watchApps: function (listener) {
|
||||
// Fixes race between two references to the same front
|
||||
// calling watchApps at the same time
|
||||
if (this._loadingPromise) {
|
||||
return this._loadingPromise;
|
||||
}
|
||||
|
||||
// Only call watchApps for the first listener being register,
|
||||
// for all next ones, just send fake appOpen events for already
|
||||
// opened apps
|
||||
if (this._apps) {
|
||||
this.runningApps.forEach((app, manifestURL) => {
|
||||
listener("appOpen", app);
|
||||
});
|
||||
return promise.resolve();
|
||||
}
|
||||
|
||||
// First retrieve all installed apps and create
|
||||
// related `App` object for each
|
||||
let request = {
|
||||
to: this.actor,
|
||||
type: "getAll"
|
||||
};
|
||||
this._loadingPromise = this.client.request(request).then(res => {
|
||||
delete this._loadingPromise;
|
||||
this._apps = new Map();
|
||||
for (let a of res.apps) {
|
||||
let app = new App(this.client, this.actor, a);
|
||||
this._apps.set(a.manifestURL, app);
|
||||
}
|
||||
}).then(() => {
|
||||
// Then retrieve all running apps in order to flag them as running
|
||||
let listRequest = {
|
||||
to: this.actor,
|
||||
type: "listRunningApps"
|
||||
};
|
||||
return this.client.request(listRequest).then(res => res.apps);
|
||||
}).then(apps => {
|
||||
let promises = apps.map(manifestURL => {
|
||||
// _getApp creates `App` instance and register it to AppActorFront
|
||||
return this._getApp(manifestURL).then(app => {
|
||||
app.running = true;
|
||||
// Fake appOpen event for all already opened
|
||||
this._notifyListeners("appOpen", app);
|
||||
});
|
||||
});
|
||||
return promise.all(promises);
|
||||
}).then(() => {
|
||||
// Finally ask to receive all app events
|
||||
return this._listenAppEvents(listener);
|
||||
});
|
||||
return this._loadingPromise;
|
||||
},
|
||||
|
||||
fetchIcons: function () {
|
||||
// On demand, retrieve apps icons in order to be able
|
||||
// to synchronously retrieve it on `App` objects
|
||||
let promises = [];
|
||||
for (let [, app] of this._apps) {
|
||||
promises.push(app.getIcon());
|
||||
}
|
||||
|
||||
return DevToolsUtils.settleAll(promises)
|
||||
.catch(() => {});
|
||||
},
|
||||
|
||||
_listenAppEvents: function (listener) {
|
||||
this._listeners.push(listener);
|
||||
|
||||
if (this._listeners.length > 1) {
|
||||
return promise.resolve();
|
||||
}
|
||||
|
||||
let client = this.client;
|
||||
let f = this._clientListener;
|
||||
client.addListener("appOpen", f);
|
||||
client.addListener("appClose", f);
|
||||
client.addListener("appInstall", f);
|
||||
client.addListener("appUninstall", f);
|
||||
|
||||
let request = {
|
||||
to: this.actor,
|
||||
type: "watchApps"
|
||||
};
|
||||
return this.client.request(request);
|
||||
},
|
||||
|
||||
_unlistenAppEvents: function (listener) {
|
||||
let idx = this._listeners.indexOf(listener);
|
||||
if (idx != -1) {
|
||||
this._listeners.splice(idx, 1);
|
||||
}
|
||||
|
||||
// Until we released all listener, we don't ask to stop sending events
|
||||
if (this._listeners.length != 0) {
|
||||
return promise.resolve();
|
||||
}
|
||||
|
||||
let client = this.client;
|
||||
let f = this._clientListener;
|
||||
client.removeListener("appOpen", f);
|
||||
client.removeListener("appClose", f);
|
||||
client.removeListener("appInstall", f);
|
||||
client.removeListener("appUninstall", f);
|
||||
|
||||
// Remove `_apps` in order to allow calling watchApps again
|
||||
// and repopulate the apps Map.
|
||||
delete this._apps;
|
||||
|
||||
let request = {
|
||||
to: this.actor,
|
||||
type: "unwatchApps"
|
||||
};
|
||||
return this.client.request(request);
|
||||
},
|
||||
|
||||
_clientListener: function (type, message) {
|
||||
let { manifestURL } = message;
|
||||
|
||||
// Reset the app object to get a fresh copy when we (re)install the app.
|
||||
if (type == "appInstall" && this._apps && this._apps.has(manifestURL)) {
|
||||
this._apps.delete(manifestURL);
|
||||
}
|
||||
|
||||
this._getApp(manifestURL).then((app) => {
|
||||
switch (type) {
|
||||
case "appOpen":
|
||||
app.running = true;
|
||||
this._notifyListeners("appOpen", app);
|
||||
break;
|
||||
case "appClose":
|
||||
app.running = false;
|
||||
this._notifyListeners("appClose", app);
|
||||
break;
|
||||
case "appInstall":
|
||||
// The call to _getApp is going to create App object
|
||||
|
||||
// This app may have been running while being installed, so check the list
|
||||
// of running apps again to get the right answer.
|
||||
let request = {
|
||||
to: this.actor,
|
||||
type: "listRunningApps"
|
||||
};
|
||||
this.client.request(request).then(res => {
|
||||
if (res.apps.includes(manifestURL)) {
|
||||
app.running = true;
|
||||
this._notifyListeners("appInstall", app);
|
||||
this._notifyListeners("appOpen", app);
|
||||
} else {
|
||||
this._notifyListeners("appInstall", app);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "appUninstall":
|
||||
// Fake a appClose event if we didn't got one before uninstall
|
||||
if (app.running) {
|
||||
app.running = false;
|
||||
this._notifyListeners("appClose", app);
|
||||
}
|
||||
this._apps.delete(manifestURL);
|
||||
this._notifyListeners("appUninstall", app);
|
||||
break;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_notifyListeners: function (type, app) {
|
||||
this._listeners.forEach(f => {
|
||||
f(type, app);
|
||||
});
|
||||
},
|
||||
|
||||
unwatchApps: function (listener) {
|
||||
return this._unlistenAppEvents(listener);
|
||||
},
|
||||
|
||||
/*
|
||||
* Install a packaged app.
|
||||
*
|
||||
* Events are going to be emitted on the front
|
||||
* as install progresses. Events will have the following fields:
|
||||
* * bytesSent: The number of bytes sent so far
|
||||
* * totalBytes: The total number of bytes to send
|
||||
*/
|
||||
installPackaged: function (packagePath, appId) {
|
||||
let request = () => {
|
||||
return installPackaged(this.client, this.actor, packagePath, appId,
|
||||
this._onInstallProgress)
|
||||
.then(response => ({
|
||||
appId: response.appId,
|
||||
manifestURL: "app://" + response.appId + "/manifest.webapp"
|
||||
}));
|
||||
};
|
||||
return this._install(request);
|
||||
},
|
||||
|
||||
_onInstallProgress: function (progress) {
|
||||
this.emit("install-progress", progress);
|
||||
},
|
||||
|
||||
_install: function (request) {
|
||||
let deferred = defer();
|
||||
let finalAppId = null, manifestURL = null;
|
||||
let installs = {};
|
||||
|
||||
// We need to resolve only once the request is done *AND*
|
||||
// once we receive the related appInstall message for
|
||||
// the same manifestURL
|
||||
let resolve = app => {
|
||||
this._unlistenAppEvents(listener);
|
||||
installs = null;
|
||||
deferred.resolve({ app: app, appId: finalAppId });
|
||||
};
|
||||
|
||||
// Listen for appInstall event, in order to resolve with
|
||||
// the matching app object.
|
||||
let listener = (type, app) => {
|
||||
if (type == "appInstall") {
|
||||
// Resolves immediately if the request has already resolved
|
||||
// or just flag the installed app to eventually resolve
|
||||
// when the request gets its response.
|
||||
if (app.manifest.manifestURL === manifestURL) {
|
||||
resolve(app);
|
||||
} else {
|
||||
installs[app.manifest.manifestURL] = app;
|
||||
}
|
||||
}
|
||||
};
|
||||
// Execute the request
|
||||
this._listenAppEvents(listener).then(request).then(response => {
|
||||
finalAppId = response.appId;
|
||||
manifestURL = response.manifestURL;
|
||||
|
||||
// Resolves immediately if the appInstall event
|
||||
// was dispatched during the request.
|
||||
if (manifestURL in installs) {
|
||||
resolve(installs[manifestURL]);
|
||||
}
|
||||
}, deferred.reject);
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/*
|
||||
* Install a hosted app.
|
||||
*
|
||||
* Events are going to be emitted on the front
|
||||
* as install progresses. Events will have the following fields:
|
||||
* * bytesSent: The number of bytes sent so far
|
||||
* * totalBytes: The total number of bytes to send
|
||||
*/
|
||||
installHosted: function (appId, metadata, manifest) {
|
||||
let manifestURL = metadata.manifestURL ||
|
||||
metadata.origin + "/manifest.webapp";
|
||||
let request = () => {
|
||||
return installHosted(
|
||||
this.client, this.actor, appId, metadata, manifest
|
||||
).then(response => ({
|
||||
appId: response.appId,
|
||||
manifestURL: manifestURL
|
||||
}));
|
||||
};
|
||||
return this._install(request);
|
||||
}
|
||||
};
|
||||
|
||||
exports.AppActorFront = AppActorFront;
|
@ -4,6 +4,5 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DevToolsModules(
|
||||
'app-actor-front.js',
|
||||
'Devices.jsm'
|
||||
)
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "mozilla/HTMLEditor.h"
|
||||
#include "mozilla/LoadInfo.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/MediaFeatureChange.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/ResultExtensions.h"
|
||||
#include "mozilla/Services.h"
|
||||
@ -4179,7 +4180,8 @@ nsDocShell::SetDeviceSizeIsPageSize(bool aValue)
|
||||
RefPtr<nsPresContext> presContext;
|
||||
GetPresContext(getter_AddRefs(presContext));
|
||||
if (presContext) {
|
||||
presContext->MediaFeatureValuesChanged(nsRestyleHint(0));
|
||||
presContext->MediaFeatureValuesChanged({
|
||||
MediaFeatureChangeReason::DeviceSizeIsPageSizeChange });
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
@ -14423,7 +14425,8 @@ nsDocShell::SetDisplayMode(uint32_t aDisplayMode)
|
||||
|
||||
RefPtr<nsPresContext> presContext;
|
||||
if (NS_SUCCEEDED(GetPresContext(getter_AddRefs(presContext)))) {
|
||||
presContext->MediaFeatureValuesChangedAllDocuments(nsRestyleHint(0));
|
||||
presContext->MediaFeatureValuesChangedAllDocuments({
|
||||
MediaFeatureChangeReason::DisplayModeChange });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "nsAttrValueInlines.h"
|
||||
#include "nsAtom.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "mozilla/CORSMode.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/ServoBindingTypes.h"
|
||||
#include "mozilla/ServoUtils.h"
|
||||
@ -1692,7 +1693,9 @@ nsAttrValue::LoadImage(nsIDocument* aDocument)
|
||||
"How did we end up with an empty string for eURL");
|
||||
|
||||
mozilla::css::ImageValue* image =
|
||||
mozilla::css::ImageValue::CreateFromURLValue(url, aDocument);
|
||||
mozilla::css::ImageValue::CreateFromURLValue(url,
|
||||
aDocument,
|
||||
mozilla::CORSMode::CORS_NONE);
|
||||
|
||||
NS_ADDREF(image);
|
||||
cont->mValue.mImage = image;
|
||||
|
@ -3701,6 +3701,20 @@ nsContentUtils::IsImageInCache(nsIURI* aURI, nsIDocument* aDocument)
|
||||
return (NS_SUCCEEDED(rv) && props);
|
||||
}
|
||||
|
||||
// static
|
||||
int32_t
|
||||
nsContentUtils::CORSModeToLoadImageFlags(mozilla::CORSMode aMode)
|
||||
{
|
||||
switch (aMode) {
|
||||
case CORS_ANONYMOUS:
|
||||
return imgILoader::LOAD_CORS_ANONYMOUS;
|
||||
case CORS_USE_CREDENTIALS:
|
||||
return imgILoader::LOAD_CORS_USE_CREDENTIALS;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
nsContentUtils::LoadImage(nsIURI* aURI, nsINode* aContext,
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "js/RootingAPI.h"
|
||||
#include "mozilla/dom/FromParser.h"
|
||||
#include "mozilla/BasicEvents.h"
|
||||
#include "mozilla/CORSMode.h"
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/GuardObjects.h"
|
||||
#include "mozilla/TaskCategory.h"
|
||||
@ -825,6 +826,14 @@ public:
|
||||
*/
|
||||
static bool DocumentInactiveForImageLoads(nsIDocument* aDocument);
|
||||
|
||||
/**
|
||||
* Convert a CORSMode into the corresponding imgILoader flags for
|
||||
* passing to LoadImage.
|
||||
* @param aMode CORS mode to convert
|
||||
* @return a bitfield suitable to bitwise OR with other nsIRequest flags
|
||||
*/
|
||||
static int32_t CORSModeToLoadImageFlags(mozilla::CORSMode aMode);
|
||||
|
||||
/**
|
||||
* Method to start an image load. This does not do any security checks.
|
||||
* This method will attempt to make aURI immutable; a caller that wants to
|
||||
|
@ -9254,20 +9254,9 @@ nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr,
|
||||
return;
|
||||
}
|
||||
|
||||
nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
|
||||
switch (Element::StringToCORSMode(aCrossOriginAttr)) {
|
||||
case CORS_NONE:
|
||||
// Nothing to do
|
||||
break;
|
||||
case CORS_ANONYMOUS:
|
||||
loadFlags |= imgILoader::LOAD_CORS_ANONYMOUS;
|
||||
break;
|
||||
case CORS_USE_CREDENTIALS:
|
||||
loadFlags |= imgILoader::LOAD_CORS_USE_CREDENTIALS;
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unknown CORS mode!");
|
||||
}
|
||||
nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL |
|
||||
nsContentUtils::CORSModeToLoadImageFlags(
|
||||
Element::StringToCORSMode(aCrossOriginAttr));
|
||||
|
||||
nsContentPolicyType policyType =
|
||||
aIsImgSet ? nsIContentPolicy::TYPE_IMAGESET :
|
||||
|
@ -963,13 +963,9 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
|
||||
"Principal mismatch?");
|
||||
#endif
|
||||
|
||||
nsLoadFlags loadFlags = aLoadFlags;
|
||||
int32_t corsmode = GetCORSMode();
|
||||
if (corsmode == CORS_ANONYMOUS) {
|
||||
loadFlags |= imgILoader::LOAD_CORS_ANONYMOUS;
|
||||
} else if (corsmode == CORS_USE_CREDENTIALS) {
|
||||
loadFlags |= imgILoader::LOAD_CORS_USE_CREDENTIALS;
|
||||
}
|
||||
nsLoadFlags loadFlags = aLoadFlags |
|
||||
nsContentUtils::CORSModeToLoadImageFlags(
|
||||
GetCORSMode());
|
||||
|
||||
// get document wide referrer policy
|
||||
// if referrer attributes are enabled in preferences, load img referrer attribute
|
||||
|
@ -46,12 +46,17 @@ add_task(async function() {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
info("Waiting selectionchange event...");
|
||||
gURLBar.addEventListener("selectionchange", () => {
|
||||
ok(isAllTextSelected(), "All text of the URL bar should be selected");
|
||||
info("Waiting selection changes...");
|
||||
function tryToCheckItLater() {
|
||||
if (!isAllTextSelected()) {
|
||||
SimpleTest.executeSoon(tryToCheckItLater);
|
||||
return;
|
||||
}
|
||||
ok(true, "All text of the URL bar should be selected");
|
||||
isnot(gURLBar.inputField.value, "", "The URL bar should have non-empty text");
|
||||
resolve();
|
||||
}, {once: true});
|
||||
}
|
||||
SimpleTest.executeSoon(tryToCheckItLater);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -847,6 +847,7 @@ class RTCPeerConnection {
|
||||
|
||||
async _createOffer(options) {
|
||||
this._checkClosed();
|
||||
this._syncTransceivers();
|
||||
let origin = Cu.getWebIDLCallerPrincipal().origin;
|
||||
return this._chain(async () => {
|
||||
let haveAssertion;
|
||||
@ -878,6 +879,7 @@ class RTCPeerConnection {
|
||||
|
||||
async _createAnswer(options) {
|
||||
this._checkClosed();
|
||||
this._syncTransceivers();
|
||||
let origin = Cu.getWebIDLCallerPrincipal().origin;
|
||||
return this._chain(async () => {
|
||||
// We give up line-numbers in errors by doing this here, but do all
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include "nsIPrefService.h"
|
||||
#include "VideoFrameUtils.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "webrtc/api/video/i420_buffer.h"
|
||||
#include "webrtc/common_video/include/video_frame_buffer.h"
|
||||
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
|
||||
|
||||
mozilla::LogModule* GetMediaManagerLog();
|
||||
@ -39,9 +39,14 @@ MediaEngineRemoteVideoSource::MediaEngineRemoteVideoSource(
|
||||
, mCapEngine(aCapEngine)
|
||||
, mScary(aScary)
|
||||
, mMutex("MediaEngineRemoteVideoSource::mMutex")
|
||||
, mRescalingBufferPool(/* zero_initialize */ false,
|
||||
/* max_number_of_buffers */ 1)
|
||||
, mSettings(MakeAndAddRef<media::Refcountable<MediaTrackSettings>>())
|
||||
{
|
||||
MOZ_ASSERT(aMediaSource != MediaSourceEnum::Other);
|
||||
mSettings->mWidth.Construct(0);
|
||||
mSettings->mHeight.Construct(0);
|
||||
mSettings->mFrameRate.Construct(0);
|
||||
Init();
|
||||
}
|
||||
|
||||
@ -183,13 +188,14 @@ MediaEngineRemoteVideoSource::Allocate(
|
||||
MOZ_ASSERT(mState == kReleased);
|
||||
|
||||
NormalizedConstraints constraints(aConstraints);
|
||||
LOG(("ChooseCapability(kFitness) for mTargetCapability and mCapability (Allocate) ++"));
|
||||
if (!ChooseCapability(constraints, aPrefs, aDeviceId, mCapability, kFitness)) {
|
||||
webrtc::CaptureCapability newCapability;
|
||||
LOG(("ChooseCapability(kFitness) for mCapability (Allocate) ++"));
|
||||
if (!ChooseCapability(constraints, aPrefs, aDeviceId, newCapability, kFitness)) {
|
||||
*aOutBadConstraint =
|
||||
MediaConstraintsHelper::FindBadConstraint(constraints, this, aDeviceId);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
LOG(("ChooseCapability(kFitness) for mTargetCapability and mCapability (Allocate) --"));
|
||||
LOG(("ChooseCapability(kFitness) for mCapability (Allocate) --"));
|
||||
|
||||
if (camera::GetChildAndCall(&camera::CamerasChild::AllocateCaptureDevice,
|
||||
mCapEngine, mUniqueId.get(),
|
||||
@ -203,6 +209,7 @@ MediaEngineRemoteVideoSource::Allocate(
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mState = kAllocated;
|
||||
mCapability = newCapability;
|
||||
}
|
||||
|
||||
LOG(("Video device %d allocated", mCaptureIndex));
|
||||
@ -232,8 +239,9 @@ MediaEngineRemoteVideoSource::Deallocate(const RefPtr<const AllocationHandle>& a
|
||||
|
||||
// Stop() has stopped capture synchronously on the media thread before we get
|
||||
// here, so there are no longer any callbacks on an IPC thread accessing
|
||||
// mImageContainer.
|
||||
// mImageContainer or mRescalingBufferPool.
|
||||
mImageContainer = nullptr;
|
||||
mRescalingBufferPool.Release();
|
||||
|
||||
LOG(("Video device %d deallocated", mCaptureIndex));
|
||||
|
||||
@ -299,6 +307,13 @@ MediaEngineRemoteVideoSource::Start(const RefPtr<const AllocationHandle>& aHandl
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||
"MediaEngineRemoteVideoSource::SetLastCapability",
|
||||
[settings = mSettings, cap = mCapability]() mutable {
|
||||
settings->mWidth.Value() = cap.width;
|
||||
settings->mHeight.Value() = cap.height;
|
||||
settings->mFrameRate.Value() = cap.maxFPS;
|
||||
}));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -355,9 +370,11 @@ MediaEngineRemoteVideoSource::Reconfigure(const RefPtr<AllocationHandle>& aHandl
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Start() applies mCapability on the device.
|
||||
mCapability = newCapability;
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
// Start() applies mCapability on the device.
|
||||
mCapability = newCapability;
|
||||
}
|
||||
|
||||
if (mState == kStarted) {
|
||||
// Allocate always returns a null AllocationHandle.
|
||||
@ -465,93 +482,61 @@ MediaEngineRemoteVideoSource::DeliverFrame(uint8_t* aBuffer,
|
||||
req_ideal_height = (mCapability.height >> 16) & 0xffff;
|
||||
}
|
||||
|
||||
int32_t dest_max_width = std::min(req_max_width, aProps.width());
|
||||
int32_t dest_max_height = std::min(req_max_height, aProps.height());
|
||||
int32_t dst_max_width = std::min(req_max_width, aProps.width());
|
||||
int32_t dst_max_height = std::min(req_max_height, aProps.height());
|
||||
// This logic works for both camera and screen sharing case.
|
||||
// for camera case, req_ideal_width and req_ideal_height is 0.
|
||||
// The following snippet will set dst_width to dest_max_width and dst_height to dest_max_height
|
||||
int32_t dst_width = std::min(req_ideal_width > 0 ? req_ideal_width : aProps.width(), dest_max_width);
|
||||
int32_t dst_height = std::min(req_ideal_height > 0 ? req_ideal_height : aProps.height(), dest_max_height);
|
||||
// The following snippet will set dst_width to dst_max_width and dst_height to dst_max_height
|
||||
int32_t dst_width = std::min(req_ideal_width > 0 ? req_ideal_width : aProps.width(), dst_max_width);
|
||||
int32_t dst_height = std::min(req_ideal_height > 0 ? req_ideal_height : aProps.height(), dst_max_height);
|
||||
|
||||
int dst_stride_y = dst_width;
|
||||
int dst_stride_uv = (dst_width + 1) / 2;
|
||||
rtc::Callback0<void> callback_unused;
|
||||
rtc::scoped_refptr<webrtc::VideoFrameBuffer> buffer =
|
||||
new rtc::RefCountedObject<webrtc::WrappedI420Buffer>(
|
||||
aProps.width(),
|
||||
aProps.height(),
|
||||
aBuffer,
|
||||
aProps.yStride(),
|
||||
aBuffer + aProps.yAllocatedSize(),
|
||||
aProps.uStride(),
|
||||
aBuffer + aProps.yAllocatedSize() + aProps.uAllocatedSize(),
|
||||
aProps.vStride(),
|
||||
callback_unused);
|
||||
|
||||
camera::VideoFrameProperties properties;
|
||||
UniquePtr<uint8_t []> frameBuf;
|
||||
uint8_t* frame;
|
||||
bool needReScale = (dst_width != aProps.width() ||
|
||||
dst_height != aProps.height()) &&
|
||||
dst_width <= aProps.width() &&
|
||||
dst_height <= aProps.height();
|
||||
|
||||
if (!needReScale) {
|
||||
dst_width = aProps.width();
|
||||
dst_height = aProps.height();
|
||||
frame = aBuffer;
|
||||
} else {
|
||||
rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer;
|
||||
i420Buffer = webrtc::I420Buffer::Create(aProps.width(),
|
||||
aProps.height(),
|
||||
aProps.width(),
|
||||
(aProps.width() + 1) / 2,
|
||||
(aProps.width() + 1) / 2);
|
||||
|
||||
const int conversionResult = webrtc::ConvertToI420(webrtc::kI420,
|
||||
aBuffer,
|
||||
0, 0, // No cropping
|
||||
aProps.width(), aProps.height(),
|
||||
aProps.width() * aProps.height() * 3 / 2,
|
||||
webrtc::kVideoRotation_0,
|
||||
i420Buffer.get());
|
||||
|
||||
webrtc::VideoFrame captureFrame(i420Buffer, 0, 0, webrtc::kVideoRotation_0);
|
||||
if (conversionResult < 0) {
|
||||
if ((dst_width != aProps.width() || dst_height != aProps.height()) &&
|
||||
dst_width <= aProps.width() &&
|
||||
dst_height <= aProps.height()) {
|
||||
// Destination resolution is smaller than source buffer. We'll rescale.
|
||||
rtc::scoped_refptr<webrtc::I420Buffer> scaledBuffer =
|
||||
mRescalingBufferPool.CreateBuffer(dst_width, dst_height);
|
||||
if (!scaledBuffer) {
|
||||
MOZ_ASSERT_UNREACHABLE("We might fail to allocate a buffer, but with this "
|
||||
"being a recycling pool that shouldn't happen");
|
||||
return 0;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<webrtc::I420Buffer> scaledBuffer;
|
||||
scaledBuffer = webrtc::I420Buffer::Create(dst_width, dst_height, dst_stride_y,
|
||||
dst_stride_uv, dst_stride_uv);
|
||||
|
||||
scaledBuffer->CropAndScaleFrom(*captureFrame.video_frame_buffer().get());
|
||||
webrtc::VideoFrame scaledFrame(scaledBuffer, 0, 0, webrtc::kVideoRotation_0);
|
||||
|
||||
VideoFrameUtils::InitFrameBufferProperties(scaledFrame, properties);
|
||||
frameBuf.reset(new (fallible) uint8_t[properties.bufferSize()]);
|
||||
frame = frameBuf.get();
|
||||
|
||||
if (!frame) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
VideoFrameUtils::CopyVideoFrameBuffers(frame,
|
||||
properties.bufferSize(), scaledFrame);
|
||||
scaledBuffer->CropAndScaleFrom(*buffer);
|
||||
buffer = scaledBuffer;
|
||||
}
|
||||
|
||||
// Create a video frame and append it to the track.
|
||||
RefPtr<layers::PlanarYCbCrImage> image =
|
||||
mImageContainer->CreatePlanarYCbCrImage();
|
||||
|
||||
const uint8_t lumaBpp = 8;
|
||||
const uint8_t chromaBpp = 4;
|
||||
|
||||
layers::PlanarYCbCrData data;
|
||||
|
||||
// Take lots of care to round up!
|
||||
data.mYChannel = frame;
|
||||
data.mYSize = IntSize(dst_width, dst_height);
|
||||
data.mYStride = (dst_width * lumaBpp + 7) / 8;
|
||||
data.mCbCrStride = (dst_width * chromaBpp + 7) / 8;
|
||||
data.mCbChannel = frame + dst_height * data.mYStride;
|
||||
data.mCrChannel = data.mCbChannel + ((dst_height + 1) / 2) * data.mCbCrStride;
|
||||
data.mCbCrSize = IntSize((dst_width + 1) / 2, (dst_height + 1) / 2);
|
||||
data.mYChannel = const_cast<uint8_t*>(buffer->DataY());
|
||||
data.mYSize = IntSize(buffer->width(), buffer->height());
|
||||
data.mYStride = buffer->StrideY();
|
||||
MOZ_ASSERT(buffer->StrideU() == buffer->StrideV());
|
||||
data.mCbCrStride = buffer->StrideU();
|
||||
data.mCbChannel = const_cast<uint8_t*>(buffer->DataU());
|
||||
data.mCrChannel = const_cast<uint8_t*>(buffer->DataV());
|
||||
data.mCbCrSize = IntSize((buffer->width() + 1) / 2,
|
||||
(buffer->height() + 1) / 2);
|
||||
data.mPicX = 0;
|
||||
data.mPicY = 0;
|
||||
data.mPicSize = IntSize(dst_width, dst_height);
|
||||
data.mStereoMode = StereoMode::MONO;
|
||||
data.mPicSize = IntSize(buffer->width(), buffer->height());
|
||||
|
||||
RefPtr<layers::PlanarYCbCrImage> image =
|
||||
mImageContainer->CreatePlanarYCbCrImage();
|
||||
if (!image->CopyData(data)) {
|
||||
MOZ_ASSERT(false);
|
||||
MOZ_ASSERT_UNREACHABLE("We might fail to allocate a buffer, but with this "
|
||||
"being a recycling container that shouldn't happen");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -566,7 +551,8 @@ MediaEngineRemoteVideoSource::DeliverFrame(uint8_t* aBuffer,
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
// implicitly releases last image
|
||||
sizeChanged = mImage && image && mImage->GetSize() != image->GetSize();
|
||||
sizeChanged = (!mImage && image) ||
|
||||
(mImage && image && mImage->GetSize() != image->GetSize());
|
||||
mImage = image.forget();
|
||||
mImageSize = mImage->GetSize();
|
||||
}
|
||||
@ -616,7 +602,7 @@ MediaEngineRemoteVideoSource::GetFitnessDistance(
|
||||
uint64_t(H::FitnessDistance(aDeviceId, aConstraints.mDeviceId)) +
|
||||
uint64_t(H::FitnessDistance(mFacingMode, aConstraints.mFacingMode)) +
|
||||
uint64_t(aCandidate.width ? H::FitnessDistance(int32_t(aCandidate.width),
|
||||
aConstraints.mWidth) : 0) +
|
||||
aConstraints.mWidth) : 0) +
|
||||
uint64_t(aCandidate.height ? H::FitnessDistance(int32_t(aCandidate.height),
|
||||
aConstraints.mHeight) : 0) +
|
||||
uint64_t(aCandidate.maxFPS ? H::FitnessDistance(double(aCandidate.maxFPS),
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "NullTransport.h"
|
||||
|
||||
// WebRTC includes
|
||||
#include "webrtc/common_video/include/i420_buffer_pool.h"
|
||||
#include "webrtc/modules/video_capture/video_capture_defines.h"
|
||||
|
||||
namespace webrtc {
|
||||
@ -208,6 +209,10 @@ private:
|
||||
// Protected by mMutex.
|
||||
RefPtr<layers::Image> mImage;
|
||||
|
||||
// A buffer pool used to manage the temporary buffer used when rescaling
|
||||
// incoming images. Cameras IPC thread only.
|
||||
webrtc::I420BufferPool mRescalingBufferPool;
|
||||
|
||||
// The intrinsic size of the latest captured image, so we can feed black
|
||||
// images of the same size while stopped.
|
||||
// Set under mMutex on the owning thread. Accessed under one of the two.
|
||||
|
@ -764,49 +764,27 @@ nsBindingManager::WalkAllRules(nsIStyleRuleProcessor::EnumFunc aFunc,
|
||||
#endif
|
||||
|
||||
bool
|
||||
nsBindingManager::MediumFeaturesChanged(nsPresContext* aPresContext)
|
||||
nsBindingManager::MediumFeaturesChanged(nsPresContext* aPresContext,
|
||||
mozilla::MediaFeatureChangeReason aReason)
|
||||
{
|
||||
MOZ_ASSERT(!mDocument->IsStyledByServo());
|
||||
#ifdef MOZ_OLD_STYLE
|
||||
bool rulesChanged = false;
|
||||
RefPtr<nsPresContext> presContext = aPresContext;
|
||||
bool isStyledByServo = mDocument->IsStyledByServo();
|
||||
|
||||
EnumerateBoundContentBindings([=, &rulesChanged](nsXBLBinding* aBinding) {
|
||||
if (isStyledByServo) {
|
||||
ServoStyleSet* styleSet = aBinding->PrototypeBinding()->GetServoStyleSet();
|
||||
if (styleSet) {
|
||||
bool styleSetChanged = false;
|
||||
|
||||
if (styleSet->IsPresContextChanged(presContext)) {
|
||||
styleSetChanged = styleSet->SetPresContext(presContext);
|
||||
} else {
|
||||
// PresContext is not changed. This means aPresContext is still
|
||||
// alive since the last time it initialized this XBL styleset.
|
||||
// It's safe to check whether medium features changed.
|
||||
bool viewportUnitsUsed = false;
|
||||
styleSetChanged =
|
||||
styleSet->MediumFeaturesChangedRules(&viewportUnitsUsed);
|
||||
MOZ_ASSERT(!viewportUnitsUsed,
|
||||
"Non-master stylesets shouldn't get flagged as using "
|
||||
"viewport units!");
|
||||
}
|
||||
rulesChanged = rulesChanged || styleSetChanged;
|
||||
}
|
||||
} else {
|
||||
#ifdef MOZ_OLD_STYLE
|
||||
nsIStyleRuleProcessor* ruleProcessor =
|
||||
aBinding->PrototypeBinding()->GetRuleProcessor();
|
||||
if (ruleProcessor) {
|
||||
bool thisChanged = ruleProcessor->MediumFeaturesChanged(presContext);
|
||||
rulesChanged = rulesChanged || thisChanged;
|
||||
}
|
||||
#else
|
||||
MOZ_CRASH("old style system disabled");
|
||||
#endif
|
||||
nsIStyleRuleProcessor* ruleProcessor =
|
||||
aBinding->PrototypeBinding()->GetRuleProcessor();
|
||||
if (ruleProcessor) {
|
||||
bool thisChanged = ruleProcessor->MediumFeaturesChanged(presContext);
|
||||
rulesChanged = rulesChanged || thisChanged;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
return rulesChanged;
|
||||
#else
|
||||
MOZ_CRASH("old style system disabled");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "nsXBLBinding.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/MediaFeatureChange.h"
|
||||
#include "mozilla/StyleSheet.h"
|
||||
#include "mozilla/EventStates.h"
|
||||
|
||||
@ -138,7 +139,8 @@ public:
|
||||
// characteristics of the medium, and return whether this rule processor's
|
||||
// rules or the servo style set have changed (e.g., because of media
|
||||
// queries).
|
||||
bool MediumFeaturesChanged(nsPresContext* aPresContext);
|
||||
bool MediumFeaturesChanged(nsPresContext* aPresContext,
|
||||
mozilla::MediaFeatureChangeReason);
|
||||
|
||||
// Update the content bindings in mBoundContentSet due to medium features
|
||||
// changed.
|
||||
|
@ -501,7 +501,7 @@ ClientLayerManager::FlushAsyncPaints()
|
||||
void
|
||||
ClientLayerManager::ScheduleComposite()
|
||||
{
|
||||
mForwarder->Composite();
|
||||
mForwarder->ScheduleComposite();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -324,7 +324,6 @@ CompositorBridgeParent::CompositorBridgeParent(CompositorManagerParent* aManager
|
||||
, mWidget(nullptr)
|
||||
, mScale(aScale)
|
||||
, mVsyncRate(aVsyncRate)
|
||||
, mIsTesting(false)
|
||||
, mPendingTransaction(0)
|
||||
, mPaused(false)
|
||||
, mUseExternalSurfaceSize(aUseExternalSurfaceSize)
|
||||
@ -1015,7 +1014,7 @@ CompositorBridgeParent::CompositeToTarget(DrawTarget* aTarget, const gfx::IntRec
|
||||
|
||||
mCompositionManager->ComputeRotation();
|
||||
|
||||
TimeStamp time = mIsTesting ? mTestTime : mCompositorScheduler->GetLastComposeTime();
|
||||
TimeStamp time = mTestTime.valueOr(mCompositorScheduler->GetLastComposeTime());
|
||||
bool requestNextFrame = mCompositionManager->TransformShadowTree(time, mVsyncRate);
|
||||
if (requestNextFrame) {
|
||||
ScheduleComposition();
|
||||
@ -1270,7 +1269,7 @@ CompositorBridgeParent::ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
|
||||
}
|
||||
|
||||
void
|
||||
CompositorBridgeParent::ForceComposite(LayerTransactionParent* aLayerTree)
|
||||
CompositorBridgeParent::ScheduleComposite(LayerTransactionParent* aLayerTree)
|
||||
{
|
||||
ScheduleComposition();
|
||||
}
|
||||
@ -1285,8 +1284,7 @@ CompositorBridgeParent::SetTestSampleTime(const uint64_t& aId,
|
||||
return false;
|
||||
}
|
||||
|
||||
mIsTesting = true;
|
||||
mTestTime = aTime;
|
||||
mTestTime = Some(aTime);
|
||||
|
||||
if (mWrBridge) {
|
||||
mWrBridge->FlushRendering();
|
||||
@ -1314,14 +1312,14 @@ CompositorBridgeParent::SetTestSampleTime(const uint64_t& aId,
|
||||
void
|
||||
CompositorBridgeParent::LeaveTestMode(const uint64_t& aId)
|
||||
{
|
||||
mIsTesting = false;
|
||||
mTestTime = Nothing();
|
||||
}
|
||||
|
||||
void
|
||||
CompositorBridgeParent::ApplyAsyncProperties(LayerTransactionParent* aLayerTree)
|
||||
{
|
||||
// NOTE: This should only be used for testing. For example, when mIsTesting is
|
||||
// true or when called from test-only methods like
|
||||
// NOTE: This should only be used for testing. For example, when mTestTime is
|
||||
// non-empty, or when called from test-only methods like
|
||||
// LayerTransactionParent::RecvGetAnimationTransform.
|
||||
|
||||
// Synchronously update the layer tree
|
||||
@ -1329,7 +1327,7 @@ CompositorBridgeParent::ApplyAsyncProperties(LayerTransactionParent* aLayerTree)
|
||||
AutoResolveRefLayers resolve(mCompositionManager);
|
||||
SetShadowProperties(mLayerManager->GetRoot());
|
||||
|
||||
TimeStamp time = mIsTesting ? mTestTime : mCompositorScheduler->GetLastComposeTime();
|
||||
TimeStamp time = mTestTime.valueOr(mCompositorScheduler->GetLastComposeTime());
|
||||
bool requestNextFrame =
|
||||
mCompositionManager->TransformShadowTree(time, mVsyncRate,
|
||||
AsyncCompositionManager::TransformsToSkip::APZ);
|
||||
@ -1773,7 +1771,7 @@ CompositorBridgeParent::GetWebRenderBridgeParent() const
|
||||
Maybe<TimeStamp>
|
||||
CompositorBridgeParent::GetTestingTimeStamp() const
|
||||
{
|
||||
return mIsTesting ? Some(mTestTime) : Nothing();
|
||||
return mTestTime;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -107,7 +107,7 @@ public:
|
||||
|
||||
virtual void NotifyClearCachedResources(LayerTransactionParent* aLayerTree) { }
|
||||
|
||||
virtual void ForceComposite(LayerTransactionParent* aLayerTree) { }
|
||||
virtual void ScheduleComposite(LayerTransactionParent* aLayerTree) { }
|
||||
virtual bool SetTestSampleTime(const uint64_t& aId,
|
||||
const TimeStamp& aTime) { return true; }
|
||||
virtual void LeaveTestMode(const uint64_t& aId) { }
|
||||
@ -236,7 +236,7 @@ public:
|
||||
void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
|
||||
const TransactionInfo& aInfo,
|
||||
bool aHitTestUpdate) override;
|
||||
void ForceComposite(LayerTransactionParent* aLayerTree) override;
|
||||
void ScheduleComposite(LayerTransactionParent* aLayerTree) override;
|
||||
bool SetTestSampleTime(const uint64_t& aId,
|
||||
const TimeStamp& aTime) override;
|
||||
void LeaveTestMode(const uint64_t& aId) override;
|
||||
@ -293,7 +293,7 @@ public:
|
||||
bool ScheduleResumeOnCompositorThread();
|
||||
bool ScheduleResumeOnCompositorThread(int width, int height);
|
||||
|
||||
virtual void ScheduleComposition();
|
||||
void ScheduleComposition();
|
||||
void NotifyShadowTreeTransaction(uint64_t aId, bool aIsFirstPaint,
|
||||
const FocusTarget& aFocusTarget,
|
||||
bool aScheduleComposite, uint32_t aPaintSequenceNumber,
|
||||
@ -577,10 +577,9 @@ protected:
|
||||
RefPtr<AsyncImagePipelineManager> mAsyncImageManager;
|
||||
RefPtr<WebRenderBridgeParent> mWrBridge;
|
||||
widget::CompositorWidget* mWidget;
|
||||
TimeStamp mTestTime;
|
||||
Maybe<TimeStamp> mTestTime;
|
||||
CSSToLayoutDeviceScale mScale;
|
||||
TimeDuration mVsyncRate;
|
||||
bool mIsTesting;
|
||||
|
||||
uint64_t mPendingTransaction;
|
||||
TimeStamp mTxnStartTime;
|
||||
|
@ -392,7 +392,7 @@ CrossProcessCompositorBridgeParent::DidCompositeLocked(
|
||||
}
|
||||
|
||||
void
|
||||
CrossProcessCompositorBridgeParent::ForceComposite(LayerTransactionParent* aLayerTree)
|
||||
CrossProcessCompositorBridgeParent::ScheduleComposite(LayerTransactionParent* aLayerTree)
|
||||
{
|
||||
uint64_t id = aLayerTree->GetId();
|
||||
MOZ_ASSERT(id != 0);
|
||||
@ -402,7 +402,7 @@ CrossProcessCompositorBridgeParent::ForceComposite(LayerTransactionParent* aLaye
|
||||
parent = sIndirectLayerTrees[id].mParent;
|
||||
}
|
||||
if (parent) {
|
||||
parent->ForceComposite(aLayerTree);
|
||||
parent->ScheduleComposite(aLayerTree);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ public:
|
||||
void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
|
||||
const TransactionInfo& aInfo,
|
||||
bool aHitTestUpdate) override;
|
||||
void ForceComposite(LayerTransactionParent* aLayerTree) override;
|
||||
void ScheduleComposite(LayerTransactionParent* aLayerTree) override;
|
||||
void NotifyClearCachedResources(LayerTransactionParent* aLayerTree) override;
|
||||
bool SetTestSampleTime(const uint64_t& aId,
|
||||
const TimeStamp& aTime) override;
|
||||
|
@ -879,9 +879,9 @@ LayerTransactionParent::RecvClearCachedResources()
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
LayerTransactionParent::RecvForceComposite()
|
||||
LayerTransactionParent::RecvScheduleComposite()
|
||||
{
|
||||
mCompositorBridge->ForceComposite(this);
|
||||
mCompositorBridge->ScheduleComposite(this);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
@ -119,7 +119,7 @@ protected:
|
||||
mozilla::ipc::IPCResult RecvReleaseCompositable(const CompositableHandle& aHandle) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvClearCachedResources() override;
|
||||
mozilla::ipc::IPCResult RecvForceComposite() override;
|
||||
mozilla::ipc::IPCResult RecvScheduleComposite() override;
|
||||
mozilla::ipc::IPCResult RecvSetTestSampleTime(const TimeStamp& aTime) override;
|
||||
mozilla::ipc::IPCResult RecvLeaveTestMode() override;
|
||||
mozilla::ipc::IPCResult RecvGetAnimationOpacity(const uint64_t& aCompositorAnimationsId,
|
||||
|
@ -110,7 +110,7 @@ parent:
|
||||
async ClearCachedResources();
|
||||
|
||||
// Schedule a composite if one isn't already scheduled.
|
||||
async ForceComposite();
|
||||
async ScheduleComposite();
|
||||
|
||||
// Get a copy of the compositor-side APZ test data instance for this
|
||||
// layers id.
|
||||
|
@ -68,7 +68,7 @@ parent:
|
||||
async SetLayerObserverEpoch(uint64_t layerObserverEpoch);
|
||||
async ClearCachedResources();
|
||||
// Schedule a composite if one isn't already scheduled.
|
||||
async ForceComposite();
|
||||
async ScheduleComposite();
|
||||
// Save the frame capture to disk
|
||||
async Capture();
|
||||
|
||||
|
@ -941,12 +941,12 @@ void ShadowLayerForwarder::ClearCachedResources()
|
||||
mShadowManager->SendClearCachedResources();
|
||||
}
|
||||
|
||||
void ShadowLayerForwarder::Composite()
|
||||
void ShadowLayerForwarder::ScheduleComposite()
|
||||
{
|
||||
if (!IPCOpen()) {
|
||||
return;
|
||||
}
|
||||
mShadowManager->SendForceComposite();
|
||||
mShadowManager->SendScheduleComposite();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -278,7 +278,7 @@ public:
|
||||
|
||||
void ClearCachedResources();
|
||||
|
||||
void Composite();
|
||||
void ScheduleComposite();
|
||||
|
||||
/**
|
||||
* True if this is forwarding to a LayerManagerComposite.
|
||||
|
@ -998,7 +998,7 @@ WebRenderBridgeParent::UpdateWebRender(CompositorVsyncScheduler* aScheduler,
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
WebRenderBridgeParent::RecvForceComposite()
|
||||
WebRenderBridgeParent::RecvScheduleComposite()
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return IPC_OK();
|
||||
|
@ -114,7 +114,7 @@ public:
|
||||
mozilla::ipc::IPCResult RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvClearCachedResources() override;
|
||||
mozilla::ipc::IPCResult RecvForceComposite() override;
|
||||
mozilla::ipc::IPCResult RecvScheduleComposite() override;
|
||||
mozilla::ipc::IPCResult RecvCapture() override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvSetConfirmedTargetAPZC(const uint64_t& aBlockId,
|
||||
|
@ -610,7 +610,7 @@ WebRenderLayerManager::SendInvalidRegion(const nsIntRegion& aRegion)
|
||||
void
|
||||
WebRenderLayerManager::ScheduleComposite()
|
||||
{
|
||||
WrBridge()->SendForceComposite();
|
||||
WrBridge()->SendScheduleComposite();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -198,6 +198,12 @@ JS_GetScriptPrincipals(JSScript* script)
|
||||
return script->principals();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSCompartment*)
|
||||
js::GetScriptCompartment(JSScript* script)
|
||||
{
|
||||
return script->compartment();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
JS_ScriptHasMutedErrors(JSScript* script)
|
||||
{
|
||||
|
@ -194,6 +194,11 @@ JS_SetCompartmentPrincipals(JSCompartment* compartment, JSPrincipals* principals
|
||||
extern JS_FRIEND_API(JSPrincipals*)
|
||||
JS_GetScriptPrincipals(JSScript* script);
|
||||
|
||||
namespace js {
|
||||
extern JS_FRIEND_API(JSCompartment*)
|
||||
GetScriptCompartment(JSScript* script);
|
||||
} /* namespace js */
|
||||
|
||||
extern JS_FRIEND_API(bool)
|
||||
JS_ScriptHasMutedErrors(JSScript* script);
|
||||
|
||||
|
@ -306,13 +306,11 @@ nsPresContext::nsPresContext(nsIDocument* aDocument, nsPresContextType aType)
|
||||
mPendingSysColorChanged(false),
|
||||
mPendingThemeChanged(false),
|
||||
mPendingUIResolutionChanged(false),
|
||||
mPendingMediaFeatureValuesChanged(false),
|
||||
mPrefChangePendingNeedsReflow(false),
|
||||
mIsEmulatingMedia(false),
|
||||
mIsGlyph(false),
|
||||
mUsesRootEMUnits(false),
|
||||
mUsesExChUnits(false),
|
||||
mPendingViewportChange(false),
|
||||
mCounterStylesDirty(true),
|
||||
mFontFeatureValuesDirty(true),
|
||||
mSuppressResizeReflow(false),
|
||||
@ -741,7 +739,11 @@ nsPresContext::AppUnitsPerDevPixelChanged()
|
||||
|
||||
if (HasCachedStyleData()) {
|
||||
// All cached style data must be recomputed.
|
||||
MediaFeatureValuesChanged(eRestyle_ForceDescendants, NS_STYLE_HINT_REFLOW);
|
||||
MediaFeatureValuesChanged({
|
||||
eRestyle_ForceDescendants,
|
||||
NS_STYLE_HINT_REFLOW,
|
||||
MediaFeatureChangeReason::ResolutionChange
|
||||
});
|
||||
}
|
||||
|
||||
mCurAppUnitsPerDevPixel = AppUnitsPerDevPixel();
|
||||
@ -1412,8 +1414,11 @@ nsPresContext::UpdateEffectiveTextZoom()
|
||||
if (mDocument->IsStyledByServo() || HasCachedStyleData()) {
|
||||
// Media queries could have changed, since we changed the meaning
|
||||
// of 'em' units in them.
|
||||
MediaFeatureValuesChanged(eRestyle_ForceDescendants,
|
||||
NS_STYLE_HINT_REFLOW);
|
||||
MediaFeatureValuesChanged({
|
||||
eRestyle_ForceDescendants,
|
||||
NS_STYLE_HINT_REFLOW,
|
||||
MediaFeatureChangeReason::ZoomChange
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1461,7 +1466,7 @@ nsPresContext::SetOverrideDPPX(float aDPPX)
|
||||
mOverrideDPPX = aDPPX;
|
||||
|
||||
if (HasCachedStyleData()) {
|
||||
MediaFeatureValuesChanged(nsRestyleHint(0), nsChangeHint(0));
|
||||
MediaFeatureValuesChanged({ MediaFeatureChangeReason::ResolutionChange });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1920,7 +1925,11 @@ nsPresContext::RefreshSystemMetrics()
|
||||
// properly reflected in computed style data), system fonts (whose
|
||||
// changes are not), and -moz-appearance (whose changes likewise are
|
||||
// not), so we need to recascade for the first, and reflow for the rest.
|
||||
MediaFeatureValuesChanged(eRestyle_ForceDescendants, NS_STYLE_HINT_REFLOW);
|
||||
MediaFeatureValuesChanged({
|
||||
eRestyle_ForceDescendants,
|
||||
NS_STYLE_HINT_REFLOW,
|
||||
MediaFeatureChangeReason::SystemMetricsChange,
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
@ -2020,7 +2029,7 @@ nsPresContext::EmulateMedium(const nsAString& aMediaType)
|
||||
|
||||
mMediaEmulated = NS_Atomize(mediaType);
|
||||
if (mMediaEmulated != previousMedium && mShell) {
|
||||
MediaFeatureValuesChanged(nsRestyleHint(0), nsChangeHint(0));
|
||||
MediaFeatureValuesChanged({ MediaFeatureChangeReason::MediumChange });
|
||||
}
|
||||
}
|
||||
|
||||
@ -2029,7 +2038,7 @@ void nsPresContext::StopEmulatingMedium()
|
||||
nsAtom* previousMedium = Medium();
|
||||
mIsEmulatingMedia = false;
|
||||
if (Medium() != previousMedium) {
|
||||
MediaFeatureValuesChanged(nsRestyleHint(0), nsChangeHint(0));
|
||||
MediaFeatureValuesChanged({ MediaFeatureChangeReason::MediumChange });
|
||||
}
|
||||
}
|
||||
|
||||
@ -2067,6 +2076,8 @@ nsPresContext::RebuildAllStyleData(nsChangeHint aExtraHint,
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME(emilio): Why is it safe to reset mUsesRootEMUnits / mUsesEXChUnits
|
||||
// here if there's no restyle hint? That looks pretty bogus.
|
||||
mUsesRootEMUnits = false;
|
||||
mUsesExChUnits = false;
|
||||
if (mShell->StyleSet()->IsGecko()) {
|
||||
@ -2108,50 +2119,47 @@ struct MediaFeatureHints
|
||||
};
|
||||
|
||||
static bool
|
||||
MediaFeatureValuesChangedAllDocumentsCallback(nsIDocument* aDocument, void* aHints)
|
||||
MediaFeatureValuesChangedAllDocumentsCallback(nsIDocument* aDocument, void* aChange)
|
||||
{
|
||||
MediaFeatureHints* hints = static_cast<MediaFeatureHints*>(aHints);
|
||||
auto* change = static_cast<const MediaFeatureChange*>(aChange);
|
||||
if (nsIPresShell* shell = aDocument->GetShell()) {
|
||||
if (nsPresContext* pc = shell->GetPresContext()) {
|
||||
pc->MediaFeatureValuesChangedAllDocuments(hints->restyleHint,
|
||||
hints->changeHint);
|
||||
pc->MediaFeatureValuesChangedAllDocuments(*change);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
nsPresContext::MediaFeatureValuesChangedAllDocuments(nsRestyleHint aRestyleHint,
|
||||
nsChangeHint aChangeHint)
|
||||
nsPresContext::MediaFeatureValuesChangedAllDocuments(
|
||||
const MediaFeatureChange& aChange)
|
||||
{
|
||||
MediaFeatureValuesChanged(aRestyleHint, aChangeHint);
|
||||
MediaFeatureHints hints = {
|
||||
aRestyleHint,
|
||||
aChangeHint
|
||||
};
|
||||
|
||||
mDocument->EnumerateSubDocuments(MediaFeatureValuesChangedAllDocumentsCallback,
|
||||
&hints);
|
||||
MediaFeatureValuesChanged(aChange);
|
||||
mDocument->EnumerateSubDocuments(
|
||||
MediaFeatureValuesChangedAllDocumentsCallback,
|
||||
const_cast<MediaFeatureChange*>(&aChange));
|
||||
}
|
||||
|
||||
void
|
||||
nsPresContext::MediaFeatureValuesChanged(nsRestyleHint aRestyleHint,
|
||||
nsChangeHint aChangeHint)
|
||||
nsPresContext::FlushPendingMediaFeatureValuesChanged()
|
||||
{
|
||||
mPendingMediaFeatureValuesChanged = false;
|
||||
if (!mPendingMediaFeatureValuesChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
MediaFeatureChange change = *mPendingMediaFeatureValuesChange;
|
||||
mPendingMediaFeatureValuesChange.reset();
|
||||
|
||||
// MediumFeaturesChanged updates the applied rules, so it always gets called.
|
||||
if (mShell) {
|
||||
aRestyleHint |= mShell->
|
||||
StyleSet()->MediumFeaturesChanged(mPendingViewportChange);
|
||||
change.mRestyleHint |=
|
||||
mShell->StyleSet()->MediumFeaturesChanged(change.mReason);
|
||||
}
|
||||
|
||||
if (aRestyleHint || aChangeHint) {
|
||||
RebuildAllStyleData(aChangeHint, aRestyleHint);
|
||||
if (change.mRestyleHint || change.mChangeHint) {
|
||||
RebuildAllStyleData(change.mChangeHint, change.mRestyleHint);
|
||||
}
|
||||
|
||||
mPendingViewportChange = false;
|
||||
|
||||
if (!mShell || !mShell->DidInitialize()) {
|
||||
return;
|
||||
}
|
||||
@ -2172,51 +2180,24 @@ nsPresContext::MediaFeatureValuesChanged(nsRestyleHint aRestyleHint,
|
||||
// Note that we do this after the new style from media queries in
|
||||
// style sheets has been computed.
|
||||
|
||||
if (!mDocument->MediaQueryLists().isEmpty()) {
|
||||
// We build a list of all the notifications we're going to send
|
||||
// before we send any of them.
|
||||
|
||||
// Copy pointers to all the lists into a new array, in case one of our
|
||||
// notifications modifies the list.
|
||||
nsTArray<RefPtr<mozilla::dom::MediaQueryList>> localMediaQueryLists;
|
||||
for (auto* mql : mDocument->MediaQueryLists()) {
|
||||
localMediaQueryLists.AppendElement(mql);
|
||||
}
|
||||
|
||||
// Now iterate our local array of the lists.
|
||||
for (const auto& mql : localMediaQueryLists) {
|
||||
nsAutoMicroTask mt;
|
||||
mql->MaybeNotify();
|
||||
}
|
||||
if (mDocument->MediaQueryLists().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsPresContext::PostMediaFeatureValuesChangedEvent()
|
||||
{
|
||||
// FIXME: We should probably replace this event with use of
|
||||
// nsRefreshDriver::AddStyleFlushObserver (except the pres shell would
|
||||
// need to track whether it's been added).
|
||||
if (!mPendingMediaFeatureValuesChanged && mShell) {
|
||||
nsCOMPtr<nsIRunnable> ev =
|
||||
NewRunnableMethod("nsPresContext::HandleMediaFeatureValuesChangedEvent",
|
||||
this, &nsPresContext::HandleMediaFeatureValuesChangedEvent);
|
||||
nsresult rv =
|
||||
Document()->Dispatch(TaskCategory::Other, ev.forget());
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mPendingMediaFeatureValuesChanged = true;
|
||||
mShell->SetNeedStyleFlush();
|
||||
}
|
||||
// We build a list of all the notifications we're going to send
|
||||
// before we send any of them.
|
||||
|
||||
// Copy pointers to all the lists into a new array, in case one of our
|
||||
// notifications modifies the list.
|
||||
nsTArray<RefPtr<mozilla::dom::MediaQueryList>> localMediaQueryLists;
|
||||
for (auto* mql : mDocument->MediaQueryLists()) {
|
||||
localMediaQueryLists.AppendElement(mql);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsPresContext::HandleMediaFeatureValuesChangedEvent()
|
||||
{
|
||||
// Null-check mShell in case the shell has been destroyed (and the
|
||||
// event is the only thing holding the pres context alive).
|
||||
if (mPendingMediaFeatureValuesChanged && mShell) {
|
||||
MediaFeatureValuesChanged(nsRestyleHint(0));
|
||||
// Now iterate our local array of the lists.
|
||||
for (const auto& mql : localMediaQueryLists) {
|
||||
nsAutoMicroTask mt;
|
||||
mql->MaybeNotify();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2231,12 +2212,14 @@ NotifyTabSizeModeChanged(TabParent* aTab, void* aArg)
|
||||
void
|
||||
nsPresContext::SizeModeChanged(nsSizeMode aSizeMode)
|
||||
{
|
||||
if (HasCachedStyleData()) {
|
||||
nsContentUtils::CallOnAllRemoteChildren(mDocument->GetWindow(),
|
||||
NotifyTabSizeModeChanged,
|
||||
&aSizeMode);
|
||||
MediaFeatureValuesChangedAllDocuments(nsRestyleHint(0));
|
||||
if (!HasCachedStyleData()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsContentUtils::CallOnAllRemoteChildren(mDocument->GetWindow(),
|
||||
NotifyTabSizeModeChanged,
|
||||
&aSizeMode);
|
||||
MediaFeatureValuesChangedAllDocuments({ MediaFeatureChangeReason::SizeModeChange });
|
||||
}
|
||||
|
||||
nsCompatibility
|
||||
|
@ -10,6 +10,7 @@
|
||||
#define nsPresContext_h___
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/MediaFeatureChange.h"
|
||||
#include "mozilla/NotNull.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
@ -17,6 +18,7 @@
|
||||
#include "nsCoord.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsIPresShellInlines.h"
|
||||
#include "nsRect.h"
|
||||
#include "nsStringFwd.h"
|
||||
#include "nsFont.h"
|
||||
@ -224,12 +226,10 @@ public:
|
||||
|
||||
mozilla::StyleSetHandle StyleSet() const { return GetPresShell()->StyleSet(); }
|
||||
|
||||
#ifdef DEBUG
|
||||
bool HasPendingMediaQueryUpdates() const
|
||||
{
|
||||
return mPendingMediaFeatureValuesChanged;
|
||||
return !!mPendingMediaFeatureValuesChange;
|
||||
}
|
||||
#endif
|
||||
|
||||
nsFrameManager* FrameManager()
|
||||
{ return PresShell()->FrameManager(); }
|
||||
@ -273,6 +273,7 @@ public:
|
||||
void PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
|
||||
nsRestyleHint aRestyleHint);
|
||||
|
||||
|
||||
/**
|
||||
* Handle changes in the values of media features (used in media
|
||||
* queries).
|
||||
@ -293,22 +294,28 @@ public:
|
||||
* a nonzero aChangeHint forces rebuilding style data even if
|
||||
* nsRestyleHint(0) is passed.)
|
||||
*/
|
||||
void MediaFeatureValuesChanged(nsRestyleHint aRestyleHint,
|
||||
nsChangeHint aChangeHint = nsChangeHint(0));
|
||||
void MediaFeatureValuesChanged(const mozilla::MediaFeatureChange& aChange)
|
||||
{
|
||||
if (mShell) {
|
||||
mShell->EnsureStyleFlush();
|
||||
}
|
||||
|
||||
if (!mPendingMediaFeatureValuesChange) {
|
||||
mPendingMediaFeatureValuesChange.emplace(aChange);
|
||||
return;
|
||||
}
|
||||
|
||||
*mPendingMediaFeatureValuesChange |= aChange;
|
||||
}
|
||||
|
||||
void FlushPendingMediaFeatureValuesChanged();
|
||||
|
||||
/**
|
||||
* Calls MediaFeatureValuesChanged for this pres context and all descendant
|
||||
* subdocuments that have a pres context. This should be used for media
|
||||
* features that must be updated in all subdocuments e.g. display-mode.
|
||||
*/
|
||||
void MediaFeatureValuesChangedAllDocuments(nsRestyleHint aRestyleHint,
|
||||
nsChangeHint aChangeHint = nsChangeHint(0));
|
||||
|
||||
void PostMediaFeatureValuesChangedEvent();
|
||||
void HandleMediaFeatureValuesChangedEvent();
|
||||
void FlushPendingMediaFeatureValuesChanged() {
|
||||
if (mPendingMediaFeatureValuesChanged)
|
||||
MediaFeatureValuesChanged(nsRestyleHint(0));
|
||||
}
|
||||
void MediaFeatureValuesChangedAllDocuments(const mozilla::MediaFeatureChange&);
|
||||
|
||||
/**
|
||||
* Updates the size mode on all remote children and recursively notifies this
|
||||
@ -466,8 +473,9 @@ public:
|
||||
mVisibleArea = r;
|
||||
// Visible area does not affect media queries when paginated.
|
||||
if (!IsPaginated() && HasCachedStyleData()) {
|
||||
mPendingViewportChange = true;
|
||||
PostMediaFeatureValuesChangedEvent();
|
||||
MediaFeatureValuesChanged({
|
||||
mozilla::MediaFeatureChangeReason::ViewportChange
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -618,8 +626,11 @@ public:
|
||||
if (HasCachedStyleData()) {
|
||||
// Media queries could have changed, since we changed the meaning
|
||||
// of 'em' units in them.
|
||||
MediaFeatureValuesChanged(eRestyle_ForceDescendants,
|
||||
NS_STYLE_HINT_REFLOW);
|
||||
MediaFeatureValuesChanged({
|
||||
eRestyle_ForceDescendants,
|
||||
NS_STYLE_HINT_REFLOW,
|
||||
mozilla::MediaFeatureChangeReason::MinFontSizeChange
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1455,7 +1466,6 @@ protected:
|
||||
unsigned mPendingSysColorChanged : 1;
|
||||
unsigned mPendingThemeChanged : 1;
|
||||
unsigned mPendingUIResolutionChanged : 1;
|
||||
unsigned mPendingMediaFeatureValuesChanged : 1;
|
||||
unsigned mPrefChangePendingNeedsReflow : 1;
|
||||
unsigned mIsEmulatingMedia : 1;
|
||||
|
||||
@ -1467,9 +1477,6 @@ protected:
|
||||
// Does the associated document use ex or ch units?
|
||||
unsigned mUsesExChUnits : 1;
|
||||
|
||||
// Has there been a change to the viewport's dimensions?
|
||||
unsigned mPendingViewportChange : 1;
|
||||
|
||||
// Is the current mCounterStyleManager valid?
|
||||
unsigned mCounterStylesDirty : 1;
|
||||
|
||||
@ -1514,6 +1521,7 @@ protected:
|
||||
unsigned mInitialized : 1;
|
||||
#endif
|
||||
|
||||
mozilla::Maybe<mozilla::MediaFeatureChange> mPendingMediaFeatureValuesChange;
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -244,7 +244,8 @@ ImageLoader::ClearFrames(nsPresContext* aPresContext)
|
||||
|
||||
void
|
||||
ImageLoader::LoadImage(nsIURI* aURI, nsIPrincipal* aOriginPrincipal,
|
||||
nsIURI* aReferrer, ImageLoader::Image* aImage)
|
||||
nsIURI* aReferrer, ImageLoader::Image* aImage,
|
||||
CORSMode aCorsMode)
|
||||
{
|
||||
NS_ASSERTION(aImage->mRequests.Count() == 0, "Huh?");
|
||||
|
||||
@ -254,11 +255,14 @@ ImageLoader::LoadImage(nsIURI* aURI, nsIPrincipal* aOriginPrincipal,
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t loadFlags = nsIRequest::LOAD_NORMAL |
|
||||
nsContentUtils::CORSModeToLoadImageFlags(aCorsMode);
|
||||
|
||||
RefPtr<imgRequestProxy> request;
|
||||
nsresult rv = nsContentUtils::LoadImage(aURI, mDocument, mDocument,
|
||||
aOriginPrincipal, 0, aReferrer,
|
||||
mDocument->GetReferrerPolicy(),
|
||||
nullptr, nsIRequest::LOAD_NORMAL,
|
||||
nullptr, loadFlags,
|
||||
NS_LITERAL_STRING("css"),
|
||||
getter_AddRefs(request));
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user