merge fx-team to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2016-02-09 12:00:16 +01:00
Родитель 69f1cff2e1 610f3f5dbb
Коммит 403c4339fa
117 изменённых файлов: 1799 добавлений и 866 удалений

Просмотреть файл

@ -92,7 +92,6 @@ devtools/client/framework/**
# included in the ignore list.
devtools/client/inspector/computed/**
devtools/client/inspector/fonts/**
devtools/client/inspector/layout/**
devtools/client/inspector/markup/test/**
devtools/client/inspector/rules/**
devtools/client/inspector/shared/test/**

Просмотреть файл

@ -426,9 +426,12 @@ var gGestureSupport = {
try {
// Determine what type of data to load based on default value's type
let type = typeof aDef;
let getFunc = "get" + (type == "boolean" ? "Bool" :
type == "number" ? "Int" : "Char") + "Pref";
return gPrefService[getFunc](branch + aPref);
let getFunc = "Char";
if (type == "boolean")
getFunc = "Bool";
else if (type == "number")
getFunc = "Int";
return gPrefService["get" + getFunc + "Pref"](branch + aPref);
}
catch (e) {
return aDef;

Просмотреть файл

@ -0,0 +1,84 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
* 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/. */
/**
* If the user has opted into blocking refresh and redirect attempts by
* default, this handles showing the notification to the user which
* gives them the option to let the refresh or redirect proceed.
*/
var RefreshBlocker = {
init() {
gBrowser.addEventListener("RefreshBlocked", this);
},
uninit() {
gBrowser.removeEventListener("RefreshBlocked", this);
},
handleEvent: function(event) {
if (event.type == "RefreshBlocked") {
this.block(event.originalTarget, event.detail);
}
},
/**
* Shows the blocked refresh / redirect notification for some browser.
*
* @param browser (<xul:browser>)
* The browser that had the refresh blocked. This will be the browser
* for which we'll show the notification on.
* @param data (object)
* An object with the following properties:
*
* URI (string)
* The URI that a page is attempting to refresh or redirect to.
*
* delay (int)
* The delay (in milliseconds) before the page was going to reload
* or redirect.
*
* sameURI (bool)
* true if we're refreshing the page. false if we're redirecting.
*
* outerWindowID (int)
* The outerWindowID of the frame that requested the refresh or
* redirect.
*/
block(browser, data) {
let brandBundle = document.getElementById("bundle_brand");
let brandShortName = brandBundle.getString("brandShortName");
let message =
gNavigatorBundle.getFormattedString(data.sameURI ? "refreshBlocked.refreshLabel"
: "refreshBlocked.redirectLabel",
[brandShortName]);
let notificationBox = gBrowser.getNotificationBox(browser);
let notification = notificationBox.getNotificationWithValue("refresh-blocked");
if (notification) {
notification.label = message;
} else {
let refreshButtonText =
gNavigatorBundle.getString("refreshBlocked.goButton");
let refreshButtonAccesskey =
gNavigatorBundle.getString("refreshBlocked.goButton.accesskey");
let buttons = [{
label: refreshButtonText,
accessKey: refreshButtonAccesskey,
callback: function (notification, button) {
if (browser.messageManager) {
browser.messageManager.sendAsyncMessage("RefreshBlocker:Refresh", data);
}
}
}];
notificationBox.appendNotification(message, "refresh-blocked",
"chrome://browser/skin/Info.png",
notificationBox.PRIORITY_INFO_MEDIUM,
buttons);
}
}
};

Просмотреть файл

@ -923,6 +923,7 @@ var gBrowserInit = {
DevEdition.init();
AboutPrivateBrowsingListener.init();
TrackingProtection.init();
RefreshBlocker.init();
let mm = window.getGroupMessageManager("browsers");
mm.loadFrameScript("chrome://browser/content/tab-content.js", true);
@ -1446,6 +1447,8 @@ var gBrowserInit = {
TrackingProtection.uninit();
RefreshBlocker.uninit();
gMenuButtonUpdateBadge.uninit();
gMenuButtonBadgeManager.uninit();
@ -4696,54 +4699,6 @@ var TabsProgressListener = {
FullZoom.onLocationChange(aLocationURI, false, aBrowser);
},
onRefreshAttempted: function (aBrowser, aWebProgress, aURI, aDelay, aSameURI) {
if (gPrefService.getBoolPref("accessibility.blockautorefresh")) {
let brandBundle = document.getElementById("bundle_brand");
let brandShortName = brandBundle.getString("brandShortName");
let refreshButtonText =
gNavigatorBundle.getString("refreshBlocked.goButton");
let refreshButtonAccesskey =
gNavigatorBundle.getString("refreshBlocked.goButton.accesskey");
let message =
gNavigatorBundle.getFormattedString(aSameURI ? "refreshBlocked.refreshLabel"
: "refreshBlocked.redirectLabel",
[brandShortName]);
let docShell = aWebProgress.DOMWindow
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
let notificationBox = gBrowser.getNotificationBox(aBrowser);
let notification = notificationBox.getNotificationWithValue("refresh-blocked");
if (notification) {
notification.label = message;
notification.refreshURI = aURI;
notification.delay = aDelay;
notification.docShell = docShell;
} else {
let buttons = [{
label: refreshButtonText,
accessKey: refreshButtonAccesskey,
callback: function (aNotification, aButton) {
var refreshURI = aNotification.docShell
.QueryInterface(Ci.nsIRefreshURI);
refreshURI.forceRefreshURI(aNotification.refreshURI,
aNotification.delay, true);
}
}];
notification =
notificationBox.appendNotification(message, "refresh-blocked",
"chrome://browser/skin/Info.png",
notificationBox.PRIORITY_INFO_MEDIUM,
buttons);
notification.refreshURI = aURI;
notification.delay = aDelay;
notification.docShell = docShell;
}
return false;
}
return true;
}
}
function nsBrowserAccess() { }

Просмотреть файл

@ -277,9 +277,13 @@ ContentSearchUIController.prototype = {
if (this.suggestionAtIndex(this.selectedIndex)) {
eventData.selection = {
index: this.selectedIndex,
kind: aEvent instanceof MouseEvent ? "mouse" :
aEvent instanceof KeyboardEvent ? "key" : undefined,
kind: undefined,
};
if (aEvent instanceof MouseEvent) {
eventData.selection.kind = "mouse";
} else if (aEvent instanceof KeyboardEvent) {
eventData.selection.kind = "key";
}
}
this._sendMsg("Search", eventData);

Просмотреть файл

@ -24,6 +24,7 @@
<script type="application/javascript" src="chrome://browser/content/browser-gestureSupport.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-places.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-plugins.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-refreshblocker.js"/>
#ifdef MOZ_SAFE_BROWSING
<script type="application/javascript" src="chrome://browser/content/browser-safebrowsing.js"/>
#endif

Просмотреть файл

@ -300,7 +300,7 @@ function initPluginsRow() {
let entries = Array.from(permissionMap, item => ({ name: item[1], permission: item[0] }));
entries.sort(function(a, b) {
return a.name < b.name ? -1 : (a.name == b.name ? 0 : 1);
return a.name.localeCompare(b.name);
});
let permissionEntries = entries.map(p => fillInPluginPermissionTemplate(p.name, p.permission));

Просмотреть файл

@ -688,7 +688,76 @@ var DOMFullscreenHandler = {
};
DOMFullscreenHandler.init();
var RefreshBlocker = {
init() {
this._filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
.createInstance(Ci.nsIWebProgress);
this._filter.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_REFRESH);
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
webProgress.addProgressListener(this._filter, Ci.nsIWebProgress.NOTIFY_REFRESH);
addMessageListener("RefreshBlocker:Refresh", this);
},
uninit() {
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
webProgress.removeProgressListener(this._filter);
this._filter.removeProgressListener(this);
this._filter = null;
removeMessageListener("RefreshBlocker:Refresh", this);
},
onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
if (Services.prefs.getBoolPref("accessibility.blockautorefresh")) {
let win = aWebProgress.DOMWindow;
let outerWindowID = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.outerWindowID;
sendAsyncMessage("RefreshBlocker:Blocked", {
URI: aURI.spec,
originCharset: aURI.originCharset,
delay: aDelay,
sameURI: aSameURI,
outerWindowID,
});
return false;
}
return true;
},
receiveMessage(message) {
let data = message.data;
if (message.name == "RefreshBlocker:Refresh") {
let win = Services.wm.getOuterWindowWithId(data.outerWindowID);
let refreshURI = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell)
.QueryInterface(Ci.nsIRefreshURI);
let URI = BrowserUtils.makeURI(data.URI, data.originCharset, null);
refreshURI.forceRefreshURI(URI, data.delay, true);
}
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener2,
Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference,
Ci.nsISupports]),
};
RefreshBlocker.init();
ExtensionContent.init(this);
addEventListener("unload", () => {
ExtensionContent.uninit(this);
RefreshBlocker.uninit();
});

Просмотреть файл

@ -4030,7 +4030,7 @@
<method name="receiveMessage">
<parameter name="aMessage"/>
<body><![CDATA[
let json = aMessage.json;
let data = aMessage.data;
let browser = aMessage.target;
switch (aMessage.name) {
@ -4062,32 +4062,32 @@
break;
}
case "contextmenu": {
let spellInfo = aMessage.data.spellInfo;
let spellInfo = data.spellInfo;
if (spellInfo)
spellInfo.target = aMessage.target.messageManager;
let documentURIObject = makeURI(aMessage.data.docLocation,
aMessage.data.charSet,
makeURI(aMessage.data.baseURI));
let documentURIObject = makeURI(data.docLocation,
data.charSet,
makeURI(data.baseURI));
gContextMenuContentData = { isRemote: true,
event: aMessage.objects.event,
popupNode: aMessage.objects.popupNode,
browser: browser,
editFlags: aMessage.data.editFlags,
editFlags: data.editFlags,
spellInfo: spellInfo,
principal: aMessage.data.principal,
customMenuItems: aMessage.data.customMenuItems,
addonInfo: aMessage.data.addonInfo,
principal: data.principal,
customMenuItems: data.customMenuItems,
addonInfo: data.addonInfo,
documentURIObject: documentURIObject,
docLocation: aMessage.data.docLocation,
charSet: aMessage.data.charSet,
referrer: aMessage.data.referrer,
referrerPolicy: aMessage.data.referrerPolicy,
contentType: aMessage.data.contentType,
contentDisposition: aMessage.data.contentDisposition,
frameOuterWindowID: aMessage.data.frameOuterWindowID,
selectionInfo: aMessage.data.selectionInfo,
disableSetDesktopBackground: aMessage.data.disableSetDesktopBg,
loginFillInfo: aMessage.data.loginFillInfo,
docLocation: data.docLocation,
charSet: data.charSet,
referrer: data.referrer,
referrerPolicy: data.referrerPolicy,
contentType: data.contentType,
contentDisposition: data.contentDisposition,
frameOuterWindowID: data.frameOuterWindowID,
selectionInfo: data.selectionInfo,
disableSetDesktopBackground: data.disableSetDesktopBg,
loginFillInfo: data.loginFillInfo,
};
let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
let event = gContextMenuContentData.event;
@ -4118,13 +4118,13 @@
// it if there's a possibility FindAsYouType will be used.
// There's no point in doing it for most random keypresses.
if (!this.isFindBarInitialized(tab) &&
aMessage.data.shouldFastFind) {
data.shouldFastFind) {
let shouldFastFind = this._findAsYouType;
if (!shouldFastFind) {
// Please keep in sync with toolkit/content/widgets/findbar.xml
const FAYT_LINKS_KEY = "'";
const FAYT_TEXT_KEY = "/";
let charCode = aMessage.data.fakeEvent.charCode;
let charCode = data.fakeEvent.charCode;
let key = charCode ? String.fromCharCode(charCode) : null;
shouldFastFind = key == FAYT_LINKS_KEY || key == FAYT_TEXT_KEY;
}
@ -4135,6 +4135,17 @@
}
break;
}
case "RefreshBlocker:Blocked": {
let event = new CustomEvent("RefreshBlocked", {
bubbles: true,
cancelable: false,
detail: data,
});
browser.dispatchEvent(event);
break;
}
}
]]></body>
@ -4244,6 +4255,7 @@
}
messageManager.addMessageListener("DOMWebNotificationClicked", this);
messageManager.addMessageListener("DOMServiceWorkerFocusClient", this);
messageManager.addMessageListener("RefreshBlocker:Blocked", this);
// To correctly handle keypresses for potential FindAsYouType, while
// the tab's find bar is not yet initialized.

Просмотреть файл

@ -368,6 +368,7 @@ skip-if = buildapp == 'mulet'
skip-if = buildapp == 'mulet'
[browser_purgehistory_clears_sh.js]
[browser_PageMetaData_pushstate.js]
[browser_refreshBlocker.js]
[browser_relatedTabs.js]
[browser_remoteTroubleshoot.js]
support-files =

Просмотреть файл

@ -125,9 +125,9 @@ function crashTabTestHelper(fieldValues, expectedExtra) {
*/
add_task(function* test_default() {
yield crashTabTestHelper({}, {
"Comments": "",
"Comments": null,
"URL": "",
"Email": "",
"Email": null,
});
});
@ -140,7 +140,7 @@ add_task(function* test_just_a_comment() {
}, {
"Comments": COMMENTS,
"URL": "",
"Email": "",
"Email": null,
});
});
@ -152,9 +152,9 @@ add_task(function* test_no_email() {
email: EMAIL,
emailMe: false,
}, {
"Comments": "",
"Comments": null,
"URL": "",
"Email": "",
"Email": null,
});
});
@ -166,7 +166,7 @@ add_task(function* test_yes_email() {
email: EMAIL,
emailMe: true,
}, {
"Comments": "",
"Comments": null,
"URL": "",
"Email": EMAIL,
});
@ -179,9 +179,9 @@ add_task(function* test_send_URL() {
yield crashTabTestHelper({
includeURL: true,
}, {
"Comments": "",
"Comments": null,
"URL": PAGE,
"Email": "",
"Email": null,
});
});
@ -200,3 +200,4 @@ add_task(function* test_send_all() {
"Email": EMAIL,
});
});

Просмотреть файл

@ -0,0 +1,92 @@
"use strict";
const PAGE = "http://example.org/browser/browser/base/content/test/general/dummy_page.html";
const PREF = "accessibility.blockautorefresh";
/**
* Goes into the content, and simulates a meta-refresh header at a very
* low level, and checks to see if it was blocked. This will always cancel
* the refresh, regardless of whether or not the refresh was blocked.
*
* @param browser (<xul:browser>)
* The browser to test for refreshing.
* @param expectRefresh (bool)
* Whether or not we expect the refresh attempt to succeed.
* @returns Promise
*/
function* attemptRefresh(browser, expectRefresh) {
yield ContentTask.spawn(browser, expectRefresh, function*(expectRefresh) {
let URI = docShell.QueryInterface(Ci.nsIWebNavigation).currentURI;
let refresher = docShell.QueryInterface(Ci.nsIRefreshURI);
refresher.refreshURI(URI, 0, false, true);
is(refresher.refreshPending, expectRefresh,
"Got the right refreshPending state");
if (refresher.refreshPending) {
// Cancel the pending refresh
refresher.cancelRefreshURITimers();
}
});
}
/**
* Tests that we can enable the blocking pref and block a refresh
* from occurring while showing a notification bar. Also tests that
* when we disable the pref, that refreshes can go through again.
*/
add_task(function* test_can_enable_and_block() {
yield BrowserTestUtils.withNewTab({
gBrowser,
url: PAGE,
}, function*(browser) {
// By default, we should be able to reload the page.
yield attemptRefresh(browser, true);
yield pushPrefs(["accessibility.blockautorefresh", true]);
let notificationPromise =
BrowserTestUtils.waitForNotificationBar(gBrowser, browser,
"refresh-blocked");
yield attemptRefresh(browser, false);
yield notificationPromise;
yield pushPrefs(["accessibility.blockautorefresh", false]);
// Page reloads should go through again.
yield attemptRefresh(browser, true);
});
});
/**
* Tests that when a refresh is blocked that we show a notification
* bar, and that when we click on the "Allow" button, the refresh
* can then go through.
*/
add_task(function* test_can_allow_refresh() {
yield BrowserTestUtils.withNewTab({
gBrowser,
url: PAGE,
}, function*(browser) {
yield pushPrefs(["accessibility.blockautorefresh", true]);
let notificationPromise =
BrowserTestUtils.waitForNotificationBar(gBrowser, browser,
"refresh-blocked");
yield attemptRefresh(browser, false);
let notification = yield notificationPromise;
// Then click the button to submit the crash report.
let buttons = notification.querySelectorAll(".notification-button");
is(buttons.length, 1, "Should have one button.");
// Prepare a Promise that should resolve when the refresh goes through
let refreshPromise = BrowserTestUtils.browserLoaded(browser);
buttons[0].click();
yield refreshPromise;
});
});

Просмотреть файл

@ -1149,19 +1149,24 @@ function getPropertyBagValue(bag, key) {
* been submitted. This function will also test the crash
* reports extra data to see if it matches expectedExtra.
*
* @param expectedExtra
* @param expectedExtra (object)
* An Object whose key-value pairs will be compared
* against the key-value pairs in the extra data of the
* crash report. A test failure will occur if there is
* a mismatch.
*
* Note that this will only check the values that exist
* If the value of the key-value pair is "null", this will
* be interpreted as "this key should not be included in the
* extra data", and will cause a test failure if it is detected
* in the crash report.
*
* Note that this will ignore any keys that are not included
* in expectedExtra. It's possible that the crash report
* will contain other extra information that is not
* compared against.
* @returns Promise
*/
function promiseCrashReport(expectedExtra) {
function promiseCrashReport(expectedExtra={}) {
return Task.spawn(function*() {
info("Starting wait on crash-report-status");
let [subject, data] =
@ -1200,9 +1205,13 @@ function promiseCrashReport(expectedExtra) {
let key = enumerator.getNext().QueryInterface(Ci.nsIProperty).name;
let value = extra.getPropertyAsAString(key);
if (key in expectedExtra) {
if (expectedExtra[key] == null) {
ok(false, `Got unexpected key ${key} with value ${value}`);
} else {
is(value, expectedExtra[key],
`Crash report had the right extra value for ${key}`);
}
}
}
});
}

Просмотреть файл

@ -89,6 +89,7 @@ browser.jar:
content/browser/browser-gestureSupport.js (content/browser-gestureSupport.js)
content/browser/browser-places.js (content/browser-places.js)
content/browser/browser-plugins.js (content/browser-plugins.js)
content/browser/browser-refreshblocker.js (content/browser-refreshblocker.js)
#ifdef MOZ_SAFE_BROWSING
content/browser/browser-safebrowsing.js (content/browser-safebrowsing.js)
#endif

Просмотреть файл

@ -916,8 +916,12 @@ CustomizeMode.prototype = {
let removable = aPlace == "palette" || CustomizableUI.isWidgetRemovable(aNode);
wrapper.setAttribute("removable", removable);
let contextMenuAttrName = aNode.getAttribute("context") ? "context" :
aNode.getAttribute("contextmenu") ? "contextmenu" : "";
let contextMenuAttrName = "";
if (aNode.getAttribute("context")) {
contextMenuAttrName = "context";
} else if (aNode.getAttribute("contextmenu")) {
contextMenuAttrName = "contextmenu";
}
let currentContextMenu = aNode.getAttribute(contextMenuAttrName);
let contextMenuForPlace = aPlace == "panel" ?
kPanelItemContextMenu :

Просмотреть файл

@ -61,6 +61,7 @@ var COLUMN_TYPES = {
JET_coltypUnsignedLong: 14, /* 4-byte unsigned integer */
JET_coltypLongLong: 15, /* 8-byte signed integer */
JET_coltypGUID: 16, /* 16-byte globally unique identifier */
};
// Not very efficient, but only used for error messages
@ -234,7 +235,7 @@ function loadLibraries() {
}
function ESEDB(rootPath, dbPath, logPath) {
log.error("Created db");
log.info("Created db");
this.rootPath = rootPath;
this.dbPath = dbPath;
this.logPath = logPath;
@ -311,7 +312,7 @@ ESEDB.prototype = {
checkForColumn(tableName, columnName) {
if (!this._opened) {
throw "The database was closed!";
throw new Error("The database was closed!");
}
let columnInfo;
@ -323,9 +324,33 @@ ESEDB.prototype = {
return columnInfo[0];
},
tableExists(tableName) {
if (!this._opened) {
throw new Error("The database was closed!");
}
let tableId = new ESE.JET_TABLEID();
let rv = ESE.ManualOpenTableW(this._sessionId, this._dbId, tableName, null,
0, 4 /* JET_bitTableReadOnly */,
tableId.address());
if (rv == -1305 /* JET_errObjectNotFound */) {
return false;
}
if (rv < 0) {
log.error("Got error " + rv + " calling OpenTableW");
throw new Error(convertESEError(rv));
}
if (rv > 0) {
log.error("Got warning " + rv + " calling OpenTableW");
}
ESE.FailSafeCloseTable(this._sessionId, tableId);
return true;
},
tableItems: function*(tableName, columns) {
if (!this._opened) {
throw "The database was closed!";
throw new Error("The database was closed!");
}
let tableOpened = false;
@ -386,8 +411,11 @@ ESEDB.prototype = {
buffer = new ctypes.uint8_t();
} else if (column.type == "date") {
buffer = new KERNEL.FILETIME();
} else if (column.type == "guid") {
let byteArray = ctypes.ArrayType(ctypes.uint8_t);
buffer = new byteArray(column.dbSize);
} else {
throw "Unknown type " + column.type;
throw new Error("Unknown type " + column.type);
}
return [buffer, buffer.constructor.size];
},
@ -399,7 +427,7 @@ ESEDB.prototype = {
buffer = null;
} else {
Cu.reportError("Unexpected JET error: " + err + ";" + " retrieving value for column " + column.name);
throw this.convertError(err);
throw new Error(convertESEError(err));
}
}
if (column.type == "string") {
@ -408,6 +436,22 @@ ESEDB.prototype = {
if (column.type == "boolean") {
return buffer ? (255 == buffer.value) : false;
}
if (column.type == "guid") {
if (buffer.length != 16) {
Cu.reportError("Buffer size for guid field " + column.id + " should have been 16!");
return "";
}
let rv = "{";
for (let i = 0; i < 16; i++) {
if (i == 4 || i == 6 || i == 8 || i == 10) {
rv += "-";
}
let byteValue = buffer.addressOfElement(i).contents;
// Ensure there's a leading 0
rv += ("0" + byteValue.toString(16)).substr(-2);
}
return rv + "}";
}
if (column.type == "date") {
if (!buffer) {
return null;
@ -457,6 +501,11 @@ ESEDB.prototype = {
throw new Error("Invalid column type for column " + column.name +
"; expected long long type, got type " + getColTypeName(dbType));
}
} else if (column.type == "guid") {
if (dbType != COLUMN_TYPES.JET_coltypGUID) {
throw new Error("Invalid column type for column " + column.name +
"; expected guid type, got type " + getColTypeName(dbType));
}
} else if (column.type) {
throw new Error("Unknown column type " + column.type + " requested for column " +
column.name + ", don't know what to do.");

Просмотреть файл

@ -18,7 +18,72 @@ XPCOMUtils.defineLazyModuleGetter(this, "ESEDBReader",
const kEdgeRegistryRoot = "SOFTWARE\\Classes\\Local Settings\\Software\\" +
"Microsoft\\Windows\\CurrentVersion\\AppContainer\\Storage\\" +
"microsoft.microsoftedge_8wekyb3d8bbwe\\MicrosoftEdge";
const kEdgeReadingListPath = "AC\\MicrosoftEdge\\User\\Default\\DataStore\\Data\\";
const kEdgeDatabasePath = "AC\\MicrosoftEdge\\User\\Default\\DataStore\\Data\\";
XPCOMUtils.defineLazyGetter(this, "gEdgeDatabase", function() {
let edgeDir = MSMigrationUtils.getEdgeLocalDataFolder();
if (!edgeDir) {
return null;
}
edgeDir.appendRelativePath(kEdgeDatabasePath);
if (!edgeDir.exists() || !edgeDir.isReadable() || !edgeDir.isDirectory()) {
return null;
}
let expectedLocation = edgeDir.clone();
expectedLocation.appendRelativePath("nouser1\\120712-0049\\DBStore\\spartan.edb");
if (expectedLocation.exists() && expectedLocation.isReadable() && expectedLocation.isFile()) {
return expectedLocation;
}
// We used to recurse into arbitrary subdirectories here, but that code
// went unused, so it likely isn't necessary, even if we don't understand
// where the magic folders above come from, they seem to be the same for
// everyone. Just return null if they're not there:
return null;
});
/**
* Get rows from a table in the Edge DB as an array of JS objects.
*
* @param {String} tableName the name of the table to read.
* @param {String[]|function} columns a list of column specifiers
* (see ESEDBReader.jsm) or a function that
* generates them based on the database
* reference once opened.
* @param {function} filterFn a function that is called for each row.
* Only rows for which it returns a truthy
* value are included in the result.
* @returns {Array} An array of row objects.
*/
function readTableFromEdgeDB(tableName, columns, filterFn) {
let database;
let rows = [];
try {
let logFile = gEdgeDatabase.parent;
logFile.append("LogFiles");
database = ESEDBReader.openDB(gEdgeDatabase.parent, gEdgeDatabase, logFile);
if (typeof columns == "function") {
columns = columns(database);
}
let tableReader = database.tableItems(tableName, columns);
for (let row of tableReader) {
if (filterFn(row)) {
rows.push(row);
}
}
} catch (ex) {
Cu.reportError("Failed to extract items from table " + tableName + " in Edge database at " +
gEdgeDatabase.path + " due to the following error: " + ex);
// Deliberately make this fail so we expose failure in the UI:
throw ex;
} finally {
if (database) {
ESEDBReader.closeDB(database);
}
}
return rows;
}
function EdgeTypedURLMigrator() {
}
@ -88,32 +153,9 @@ function EdgeReadingListMigrator() {
EdgeReadingListMigrator.prototype = {
type: MigrationUtils.resourceTypes.BOOKMARKS,
_dbFile: null,
get exists() {
this._dbFile = this._getDBFile();
return !!this._dbFile;
},
_getDBFile() {
let edgeDir = MSMigrationUtils.getEdgeLocalDataFolder();
if (!edgeDir) {
return null;
}
edgeDir.appendRelativePath(kEdgeReadingListPath);
if (!edgeDir.exists() || !edgeDir.isReadable() || !edgeDir.isDirectory()) {
return null;
}
let expectedLocation = edgeDir.clone();
expectedLocation.appendRelativePath("nouser1\\120712-0049\\DBStore\\spartan.edb");
if (expectedLocation.exists() && expectedLocation.isReadable() && expectedLocation.isFile()) {
return expectedLocation;
}
// We used to recurse into arbitrary subdirectories here, but that code
// went unused, so it likely isn't necessary, even if we don't understand
// where the magic folders above come from, they seem to be the same for
// everyone. Just return null if they're not there:
return null;
return !!gEdgeDatabase;
},
migrate(callback) {
@ -127,43 +169,30 @@ EdgeReadingListMigrator.prototype = {
},
_migrateReadingList: Task.async(function*(parentGuid) {
let readingListItems = [];
let database;
try {
let logFile = this._dbFile.parent;
logFile.append("LogFiles");
database = ESEDBReader.openDB(this._dbFile.parent, this._dbFile, logFile);
let columnFn = db => {
let columns = [
{name: "URL", type: "string"},
{name: "Title", type: "string"},
{name: "AddedDate", type: "date"}
];
// Later versions have an isDeleted column:
let isDeletedColumn = database.checkForColumn("ReadingList", "isDeleted");
// Later versions have an IsDeleted column:
let isDeletedColumn = db.checkForColumn("ReadingList", "IsDeleted");
if (isDeletedColumn && isDeletedColumn.dbType == ESEDBReader.COLUMN_TYPES.JET_coltypBit) {
columns.push({name: "isDeleted", type: "boolean"});
columns.push({name: "IsDeleted", type: "boolean"});
}
return columns;
};
let tableReader = database.tableItems("ReadingList", columns);
for (let row of tableReader) {
if (!row.isDeleted) {
readingListItems.push(row);
}
}
} catch (ex) {
Cu.reportError("Failed to extract Edge reading list information from " +
"the database at " + this._dbFile.path + " due to the following error: " + ex);
// Deliberately make this fail so we expose failure in the UI:
throw ex;
} finally {
if (database) {
ESEDBReader.closeDB(database);
}
}
let filterFn = row => {
return !row.IsDeleted;
};
let readingListItems = readTableFromEdgeDB("ReadingList", columnFn, filterFn);
if (!readingListItems.length) {
return;
}
let destFolderGuid = yield this._ensureReadingListFolder(parentGuid);
let exceptionThrown;
for (let item of readingListItems) {
@ -192,14 +221,184 @@ EdgeReadingListMigrator.prototype = {
}),
};
function EdgeBookmarksMigrator() {
}
EdgeBookmarksMigrator.prototype = {
type: MigrationUtils.resourceTypes.BOOKMARKS,
get TABLE_NAME() { return "Favorites" },
get exists() {
if ("_exists" in this) {
return this._exists;
}
return this._exists = (!!gEdgeDatabase && this._checkTableExists());
},
_checkTableExists() {
let database;
let rv;
try {
let logFile = gEdgeDatabase.parent;
logFile.append("LogFiles");
database = ESEDBReader.openDB(gEdgeDatabase.parent, gEdgeDatabase, logFile);
rv = database.tableExists(this.TABLE_NAME);
} catch (ex) {
Cu.reportError("Failed to check for table " + tableName + " in Edge database at " +
gEdgeDatabase.path + " due to the following error: " + ex);
return false;
} finally {
if (database) {
ESEDBReader.closeDB(database);
}
}
return rv;
},
migrate(callback) {
this._migrateBookmarks(PlacesUtils.bookmarks.menuGuid).then(
() => callback(true),
ex => {
Cu.reportError(ex);
callback(false);
}
);
},
_migrateBookmarks: Task.async(function*(rootGuid) {
let {bookmarks, folderMap} = this._fetchBookmarksFromDB();
if (!bookmarks.length) {
return;
}
yield this._importBookmarks(bookmarks, folderMap, rootGuid);
}),
_importBookmarks: Task.async(function*(bookmarks, folderMap, rootGuid) {
if (!MigrationUtils.isStartupMigration) {
rootGuid =
yield MigrationUtils.createImportedBookmarksFolder("Edge", rootGuid);
}
let exceptionThrown;
for (let bookmark of bookmarks) {
// If this is a folder, we might have created it already to put other bookmarks in.
if (bookmark.IsFolder && bookmark._guid) {
continue;
}
// If this is a folder, just create folders up to and including that folder.
// Otherwise, create folders until we have a parent for this bookmark.
// This avoids duplicating logic for the bookmarks bar.
let folderId = bookmark.IsFolder ? bookmark.ItemId : bookmark.ParentId;
let parentGuid = yield this._getGuidForFolder(folderId, folderMap, rootGuid).catch(ex => {
if (!exceptionThrown) {
exceptionThrown = ex;
}
Cu.reportError(ex);
});
// If this was a folder, we're done with this item
if (bookmark.IsFolder) {
continue;
}
if (!parentGuid) {
// If we couldn't sort out a parent, fall back to importing on the root:
parentGuid = rootGuid;
}
let placesInfo = {
parentGuid,
url: bookmark.URL,
dateAdded: bookmark.DateUpdated || new Date(),
title: bookmark.Title,
}
yield PlacesUtils.bookmarks.insert(placesInfo).catch(ex => {
if (!exceptionThrown) {
exceptionThrown = ex;
}
Cu.reportError(ex);
});
}
if (exceptionThrown) {
throw exceptionThrown;
}
}),
_fetchBookmarksFromDB() {
let folderMap = new Map();
let columns = [
{name: "URL", type: "string"},
{name: "Title", type: "string"},
{name: "DateUpdated", type: "date"},
{name: "IsFolder", type: "boolean"},
{name: "IsDeleted", type: "boolean"},
{name: "ParentId", type: "guid"},
{name: "ItemId", type: "guid"}
];
let filterFn = row => {
if (row.IsDeleted) {
return false;
}
if (row.IsFolder) {
folderMap.set(row.ItemId, row);
}
return true;
}
let bookmarks = readTableFromEdgeDB(this.TABLE_NAME, columns, filterFn);
return {bookmarks, folderMap};
},
_getGuidForFolder: Task.async(function*(folderId, folderMap, rootGuid) {
// If the folderId is not known as a folder in the folder map, we assume
// we just need the root
if (!folderMap.has(folderId)) {
return rootGuid;
}
let folder = folderMap.get(folderId);
// If the folder already has a places guid, just return that.
if (folder._guid) {
return folder._guid;
}
// Hacks! The bookmarks bar is special:
if (folder.Title == "_Favorites_Bar_") {
let toolbarGuid = PlacesUtils.bookmarks.toolbarGuid;
if (!MigrationUtils.isStartupMigration) {
toolbarGuid =
yield MigrationUtils.createImportedBookmarksFolder("Edge", toolbarGuid);
}
return folder._guid = toolbarGuid;
}
// Otherwise, get the right parent guid recursively:
let parentGuid = yield this._getGuidForFolder(folder.ParentId, folderMap, rootGuid);
let folderInfo = {
title: folder.Title,
type: PlacesUtils.bookmarks.TYPE_FOLDER,
dateAdded: folder.DateUpdated || new Date(),
parentGuid,
};
// and add ourselves as a kid, and return the guid we got.
let parentBM = yield PlacesUtils.bookmarks.insert(folderInfo);
return folder._guid = parentBM.guid;
}),
}
function EdgeProfileMigrator() {
}
EdgeProfileMigrator.prototype = Object.create(MigratorPrototype);
EdgeProfileMigrator.prototype.getResources = function() {
let bookmarksMigrator = new EdgeBookmarksMigrator();
if (!bookmarksMigrator.exists) {
bookmarksMigrator = MSMigrationUtils.getBookmarksMigrator(MSMigrationUtils.MIGRATION_TYPE_EDGE);
}
let resources = [
MSMigrationUtils.getBookmarksMigrator(MSMigrationUtils.MIGRATION_TYPE_EDGE),
bookmarksMigrator,
MSMigrationUtils.getCookiesMigrator(MSMigrationUtils.MIGRATION_TYPE_EDGE),
new EdgeTypedURLMigrator(),
new EdgeReadingListMigrator(),

Просмотреть файл

@ -11,7 +11,7 @@ Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "EventEmitter", function() {
const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js", {});
const {EventEmitter} = Cu.import("resource://devtools/shared/event-emitter.js", {});
return EventEmitter;
});

Просмотреть файл

@ -417,8 +417,12 @@
// Autoscroll the popup strip if we drag over the scroll buttons.
let anonid = event.originalTarget.getAttribute('anonid');
let scrollDir = anonid == "scrollbutton-up" ? -1 :
anonid == "scrollbutton-down" ? 1 : 0;
let scrollDir = 0;
if (anonid == "scrollbutton-up") {
scrollDir = -1;
} else if (anonid == "scrollbutton-down") {
scrollDir = 1;
}
if (scrollDir != 0) {
this._scrollBox.scrollByIndex(scrollDir, false);
}

Просмотреть файл

@ -542,7 +542,7 @@ var pktApi = (function() {
usedTagsObjectArray.sort(function(usedTagA, usedTagB) {
var a = usedTagA.timestamp;
var b = usedTagB.timestamp;
return a < b ? -1 : a > b ? 1 : 0;
return a - b;
});
// Get all keys tags

Просмотреть файл

@ -163,13 +163,31 @@ this.TabCrashHandler = {
URL,
} = message.data;
let extraExtraKeyVals = {
"Comments": comments,
"Email": email,
"URL": URL,
};
// For the entries in extraExtraKeyVals, we only want to submit the
// extra data values where they are not the empty string.
for (let key in extraExtraKeyVals) {
let val = extraExtraKeyVals[key].trim();
if (!val) {
delete extraExtraKeyVals[key];
}
}
// URL is special, since it's already been written to extra data by
// default. In order to make sure we don't send it, we overwrite it
// with the empty string.
if (!includeURL) {
extraExtraKeyVals["URL"] = "";
}
CrashSubmit.submit(dumpID, {
recordSubmission: true,
extraExtraKeyVals: {
Comments: comments,
Email: email,
URL: URL,
},
extraExtraKeyVals,
}).then(null, Cu.reportError);
this.prefs.setBoolPref("sendReport", true);

Просмотреть файл

@ -8,7 +8,7 @@ const {AddonWatcher} = Cu.import("resource://gre/modules/AddonWatcher.jsm", {});
const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
function setup() {
requestLongerTimeout(10);
requestLongerTimeout(20);
info("Checking for mozscreenshots extension");
return new Promise((resolve) => {

Просмотреть файл

@ -13,8 +13,10 @@ const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironmen
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("chrome://mozscreenshots/content/Screenshot.jsm");
// Create a new instance of the ConsoleAPI so we can control the maxLogLevel with a pref.
@ -146,9 +148,15 @@ this.TestRunner = {
function changeConfig(config) {
log.debug("calling " + config.name);
let promise = config.applyConfig();
let promise = Promise.resolve(config.applyConfig());
log.debug("called " + config.name);
return promise;
// Add a default timeout of 500ms to avoid conflicts when configurations
// try to apply at the same time. e.g WindowSize and TabsInTitlebar
return promise.then(() => {
return new Promise((resolve) => {
setTimeout(resolve, 500);
});
});
}
try {

Просмотреть файл

@ -10,7 +10,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://devtools/client/framework/gDevTools.jsm");
Cu.import("resource://gre/modules/Services.jsm");
let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
let { devtools } = Cu.import("resource://devtools/shared/Loader.jsm", {});
let TargetFactory = devtools.TargetFactory;
function getTargetForSelectedTab() {

Просмотреть файл

@ -15,12 +15,12 @@ Cu.import("resource://gre/modules/Timer.jsm");
this.LightweightThemes = {
init(libDir) {
// convert -size 3000x200 canvas:black black_theme.png
// convert -size 3000x200 canvas:#333 black_theme.png
let blackImage = libDir.clone();
blackImage.append("black_theme.png");
this._blackImageURL = Services.io.newFileURI(blackImage).spec;
// convert -size 3000x200 canvas:white white_theme.png
// convert -size 3000x200 canvas:#eee white_theme.png
let whiteImage = libDir.clone();
whiteImage.append("white_theme.png");
this._whiteImageURL = Services.io.newFileURI(whiteImage).spec;
@ -40,7 +40,7 @@ this.LightweightThemes = {
name: "black",
headerURL: LightweightThemes._blackImageURL,
footerURL: LightweightThemes._blackImageURL,
textcolor: "#ffffff",
textcolor: "#eeeeee",
accentcolor: "#111111",
});
@ -62,7 +62,7 @@ this.LightweightThemes = {
name: "white",
headerURL: LightweightThemes._whiteImageURL,
footerURL: LightweightThemes._whiteImageURL,
textcolor: "#000000",
textcolor: "#111111",
accentcolor: "#eeeeee",
});
// Wait for LWT listener

Просмотреть файл

@ -64,7 +64,8 @@ this.Tabs = {
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
browserWindow.gBrowser.loadTabs([
"about:addons",
PREFS_TAB,
CUST_TAB,
"about:home",
DEFAULT_FAVICON_TAB,
"about:newtab",
@ -93,12 +94,10 @@ this.Tabs = {
DEFAULT_FAVICON_TAB,
"about:newtab",
], true, true);
let tab = browserWindow.gBrowser.addTab(PREFS_TAB);
browserWindow.gBrowser.pinTab(tab);
tab = browserWindow.gBrowser.addTab(CUST_TAB);
browserWindow.gBrowser.pinTab(tab);
browserWindow.gBrowser.selectTabAtIndex(4);
hoverTab(browserWindow.gBrowser.tabs[6]);
browserWindow.gBrowser.pinTab(browserWindow.gBrowser.tabs[1]);
browserWindow.gBrowser.pinTab(browserWindow.gBrowser.tabs[2]);
browserWindow.gBrowser.selectTabAtIndex(3);
hoverTab(browserWindow.gBrowser.tabs[5]);
yield new Promise((resolve, reject) => {
setTimeout(resolve, 3000);
});

Просмотреть файл

@ -11,6 +11,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://testing-common/BrowserTestUtils.jsm");
this.WindowSize = {
@ -22,7 +23,7 @@ this.WindowSize = {
maximized: {
applyConfig: Task.async(function*() {
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
browserWindow.fullScreen = false;
yield toggleFullScreen(browserWindow, false);
// Wait for the Lion fullscreen transition to end as there doesn't seem to be an event
// and trying to maximize while still leaving fullscreen doesn't work.
@ -38,7 +39,7 @@ this.WindowSize = {
normal: {
applyConfig: Task.async(function*() {
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
browserWindow.fullScreen = false;
yield toggleFullScreen(browserWindow, false);
browserWindow.restore();
yield new Promise((resolve, reject) => {
setTimeout(resolve, 5000);
@ -49,13 +50,19 @@ this.WindowSize = {
fullScreen: {
applyConfig: Task.async(function*() {
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
browserWindow.fullScreen = true;
yield toggleFullScreen(browserWindow, true);
// OS X Lion fullscreen transition takes a while
yield new Promise((resolve, reject) => {
setTimeout(resolve, 5000);
});
}),
},
},
};
function toggleFullScreen(browserWindow, wantsFS) {
browserWindow.fullScreen = wantsFS;
return BrowserTestUtils.waitForCondition(() => {
return wantsFS == browserWindow.document.documentElement.hasAttribute("inFullscreen");
}, "waiting for @inFullscreen change");
}

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 343 B

После

Ширина:  |  Высота:  |  Размер: 977 B

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 522 B

После

Ширина:  |  Высота:  |  Размер: 977 B

Просмотреть файл

@ -42,6 +42,9 @@ var AnimationsPanel = {
this.timelineCurrentTimeEl = $("#timeline-current-time");
this.rateSelectorEl = $("#timeline-rate");
this.rewindTimelineButtonEl.setAttribute("title",
L10N.getStr("timeline.rewindButtonTooltip"));
// If the server doesn't support toggling all animations at once, hide the
// whole global toolbar.
if (!AnimationsController.traits.hasToggleAll) {
@ -213,6 +216,12 @@ var AnimationsPanel = {
this.playTimelineButtonEl.classList.toggle("paused", !isMoving);
let l10nPlayProperty = isMoving ? "timeline.resumedButtonTooltip" :
"timeline.pausedButtonTooltip";
this.playTimelineButtonEl.setAttribute("title",
L10N.getStr(l10nPlayProperty));
// If the timeline data changed as a result of the user dragging the
// scrubber, then pause all animations and set their currentTimes.
// (Note that we want server-side requests to be sequenced, so we only do

Просмотреть файл

@ -28,7 +28,10 @@ RateSelector.prototype = {
this.selectEl = createNode({
parent: containerEl,
nodeType: "select",
attributes: {"class": "devtools-button"}
attributes: {
"class": "devtools-button",
"title": L10N.getStr("timeline.rateSelectorTooltip")
}
});
this.selectEl.addEventListener("change", this.onRateChanged);

Просмотреть файл

@ -495,7 +495,4 @@
<textbox id="conditional-breakpoint-panel-textbox"/>
</vbox>
</panel>
<script type="text/javascript" src="resource://gre/browser/modules/devtools/debugger/mocks.js"/>
<script type="text/javascript" src="resource://gre/browser/modules/devtools/debugger/mock-init.js"/>
</window>

Просмотреть файл

@ -12,4 +12,3 @@ support-files =
[browser_fontinspector_edit-previews.js]
[browser_fontinspector_edit-previews-show-all.js]
[browser_fontinspector_theme-change.js]
skip-if = e10s && debug && os == 'win'

Просмотреть файл

@ -307,8 +307,8 @@ LayoutView.prototype = {
element: element,
initial: initialValue,
start: editor => {
editor.elt.parentNode.classList.add("layout-editing");
start: self => {
self.elt.parentNode.classList.add("layout-editing");
},
change: value => {
@ -457,7 +457,7 @@ LayoutView.prototype = {
update: function() {
let lastRequest = Task.spawn((function*() {
if (!this.isViewVisibleAndNodeValid()) {
return;
return null;
}
let node = this.inspector.selection.nodeFront;
@ -546,7 +546,8 @@ LayoutView.prototype = {
this.inspector.emit("layoutview-updated");
}).bind(this)).then(null, console.error);
return this._lastRequest = lastRequest;
this._lastRequest = lastRequest;
return this._lastRequest;
},
/**
@ -587,7 +588,7 @@ LayoutView.prototype = {
* Show the box-model highlighter on the currently selected element
* @param {Object} options Options passed to the highlighter actor
*/
showBoxModel: function(options={}) {
showBoxModel: function(options = {}) {
let toolbox = this.inspector.toolbox;
let nodeFront = this.inspector.selection.nodeFront;

Просмотреть файл

@ -1,6 +1,5 @@
[DEFAULT]
tags = devtools
skip-if = e10s # Bug ?????? - devtools tests disabled with e10s
subsuite = devtools
support-files =
doc_layout_iframe1.html

Просмотреть файл

@ -127,37 +127,42 @@ var res2 = [
];
add_task(function*() {
let style = "div { position: absolute; top: 42px; left: 42px; height: 100.111px; width: 100px; border: 10px solid black; padding: 20px; margin: 30px auto;}";
let style = "div { position: absolute; top: 42px; left: 42px; " +
"height: 100.111px; width: 100px; border: 10px solid black; " +
"padding: 20px; margin: 30px auto;}";
let html = "<style>" + style + "</style><div></div>";
yield addTab("data:text/html," + encodeURIComponent(html));
let {toolbox, inspector, view} = yield openLayoutView();
let {inspector, view, testActor} = yield openLayoutView();
yield selectNode("div", inspector);
yield runTests(inspector, view);
yield testInitialValues(inspector, view);
yield testChangingValues(inspector, view, testActor);
});
addTest("Test that the initial values of the box model are correct",
function*(inspector, view) {
function* testInitialValues(inspector, view) {
info("Test that the initial values of the box model are correct");
let viewdoc = view.doc;
for (let i = 0; i < res1.length; i++) {
let elt = viewdoc.querySelector(res1[i].selector);
is(elt.textContent, res1[i].value, res1[i].selector + " has the right value.");
is(elt.textContent, res1[i].value,
res1[i].selector + " has the right value.");
}
});
}
addTest("Test that changing the document updates the box model",
function*(inspector, view) {
function* testChangingValues(inspector, view, testActor) {
info("Test that changing the document updates the box model");
let viewdoc = view.doc;
let onUpdated = waitForUpdate(inspector);
inspector.selection.node.style.height = "150px";
inspector.selection.node.style.paddingRight = "50px";
yield testActor.setAttribute("div", "style",
"height:150px;padding-right:50px;");
yield onUpdated;
for (let i = 0; i < res2.length; i++) {
let elt = viewdoc.querySelector(res2[i].selector);
is(elt.textContent, res2[i].value, res2[i].selector + " has the right value after style update.");
is(elt.textContent, res2[i].value,
res2[i].selector + " has the right value after style update.");
}
});
}

Просмотреть файл

@ -19,21 +19,24 @@ const TEST_URI = "<style>" +
"<div id='div3'></div><div id='div4'></div>" +
"<div id='div5'></div>";
function getStyle(node, property) {
return node.style.getPropertyValue(property);
}
add_task(function*() {
yield addTab("data:text/html," + encodeURIComponent(TEST_URI));
let {toolbox, inspector, view} = yield openLayoutView();
let {inspector, view, testActor} = yield openLayoutView();
yield runTests(inspector, view);
yield testEditingMargins(inspector, view, testActor);
yield testKeyBindings(inspector, view, testActor);
yield testEscapeToUndo(inspector, view, testActor);
yield testDeletingValue(inspector, view, testActor);
yield testRefocusingOnClick(inspector, view, testActor);
yield testBluringOnClick(inspector, view);
});
addTest("Test that editing margin dynamically updates the document, pressing escape cancels the changes",
function*(inspector, view) {
let node = content.document.getElementById("div1");
is(getStyle(node, "margin-top"), "", "Should be no margin-top on the element.")
function* testEditingMargins(inspector, view, testActor) {
info("Test that editing margin dynamically updates the document, pressing " +
"escape cancels the changes");
is((yield getStyle(testActor, "#div1", "margin-top")), "",
"Should be no margin-top on the element.");
yield selectNode("#div1", inspector);
let span = view.doc.querySelector(".layout-margin.layout-top > span");
@ -47,19 +50,23 @@ function*(inspector, view) {
EventUtils.synthesizeKey("3", {}, view.doc.defaultView);
yield waitForUpdate(inspector);
is(getStyle(node, "margin-top"), "3px", "Should have updated the margin.");
is((yield getStyle(testActor, "#div1", "margin-top")), "3px",
"Should have updated the margin.");
EventUtils.synthesizeKey("VK_ESCAPE", {}, view.doc.defaultView);
yield waitForUpdate(inspector);
is(getStyle(node, "margin-top"), "", "Should be no margin-top on the element.")
is((yield getStyle(testActor, "#div1", "margin-top")), "",
"Should be no margin-top on the element.");
is(span.textContent, 5, "Should have the right value in the box model.");
});
}
addTest("Test that arrow keys work correctly and pressing enter commits the changes",
function*(inspector, view) {
let node = content.document.getElementById("div1");
is(getStyle(node, "margin-left"), "", "Should be no margin-top on the element.")
function* testKeyBindings(inspector, view, testActor) {
info("Test that arrow keys work correctly and pressing enter commits the " +
"changes");
is((yield getStyle(testActor, "#div1", "margin-left")), "",
"Should be no margin-top on the element.");
yield selectNode("#div1", inspector);
let span = view.doc.querySelector(".layout-margin.layout-left > span");
@ -74,29 +81,35 @@ function*(inspector, view) {
yield waitForUpdate(inspector);
is(editor.value, "11px", "Should have the right value in the editor.");
is(getStyle(node, "margin-left"), "11px", "Should have updated the margin.");
is((yield getStyle(testActor, "#div1", "margin-left")), "11px",
"Should have updated the margin.");
EventUtils.synthesizeKey("VK_DOWN", {}, view.doc.defaultView);
yield waitForUpdate(inspector);
is(editor.value, "10px", "Should have the right value in the editor.");
is(getStyle(node, "margin-left"), "10px", "Should have updated the margin.");
is((yield getStyle(testActor, "#div1", "margin-left")), "10px",
"Should have updated the margin.");
EventUtils.synthesizeKey("VK_UP", { shiftKey: true }, view.doc.defaultView);
yield waitForUpdate(inspector);
is(editor.value, "20px", "Should have the right value in the editor.");
is(getStyle(node, "margin-left"), "20px", "Should have updated the margin.");
is((yield getStyle(testActor, "#div1", "margin-left")), "20px",
"Should have updated the margin.");
EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView);
is(getStyle(node, "margin-left"), "20px", "Should be the right margin-top on the element.")
is((yield getStyle(testActor, "#div1", "margin-left")), "20px",
"Should be the right margin-top on the element.");
is(span.textContent, 20, "Should have the right value in the box model.");
});
}
addTest("Test that deleting the value removes the property but escape undoes that",
function*(inspector, view) {
let node = content.document.getElementById("div1");
is(getStyle(node, "margin-left"), "20px", "Should be the right margin-top on the element.")
function* testEscapeToUndo(inspector, view, testActor) {
info("Test that deleting the value removes the property but escape undoes " +
"that");
is((yield getStyle(testActor, "#div1", "margin-left")), "20px",
"Should be the right margin-top on the element.");
yield selectNode("#div1", inspector);
let span = view.doc.querySelector(".layout-margin.layout-left > span");
@ -111,20 +124,21 @@ function*(inspector, view) {
yield waitForUpdate(inspector);
is(editor.value, "", "Should have the right value in the editor.");
is(getStyle(node, "margin-left"), "", "Should have updated the margin.");
is((yield getStyle(testActor, "#div1", "margin-left")), "",
"Should have updated the margin.");
EventUtils.synthesizeKey("VK_ESCAPE", {}, view.doc.defaultView);
yield waitForUpdate(inspector);
is(getStyle(node, "margin-left"), "20px", "Should be the right margin-top on the element.")
is((yield getStyle(testActor, "#div1", "margin-left")), "20px",
"Should be the right margin-top on the element.");
is(span.textContent, 20, "Should have the right value in the box model.");
});
}
addTest("Test that deleting the value removes the property",
function*(inspector, view) {
let node = content.document.getElementById("div1");
function* testDeletingValue(inspector, view, testActor) {
info("Test that deleting the value removes the property");
node.style.marginRight = "15px";
yield setStyle(testActor, "#div1", "marginRight", "15px");
yield waitForUpdate(inspector);
yield selectNode("#div1", inspector);
@ -141,17 +155,18 @@ function*(inspector, view) {
yield waitForUpdate(inspector);
is(editor.value, "", "Should have the right value in the editor.");
is(getStyle(node, "margin-right"), "", "Should have updated the margin.");
is((yield getStyle(testActor, "#div1", "margin-right")), "",
"Should have updated the margin.");
EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView);
is(getStyle(node, "margin-right"), "", "Should be the right margin-top on the element.")
is((yield getStyle(testActor, "#div1", "margin-right")), "",
"Should be the right margin-top on the element.");
is(span.textContent, 10, "Should have the right value in the box model.");
});
}
addTest("Test that clicking in the editor input does not remove focus",
function*(inspector, view) {
let node = content.document.getElementById("div4");
function* testRefocusingOnClick(inspector, view, testActor) {
info("Test that clicking in the editor input does not remove focus");
yield selectNode("#div4", inspector);
@ -172,17 +187,17 @@ function*(inspector, view) {
yield waitForUpdate(inspector);
is(editor.value, "2px", "Should have the right value in the editor.");
is(getStyle(node, "margin-top"), "2px", "Should have updated the margin.");
is((yield getStyle(testActor, "#div4", "margin-top")), "2px",
"Should have updated the margin.");
EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView);
is(getStyle(node, "margin-top"), "2px",
is((yield getStyle(testActor, "#div4", "margin-top")), "2px",
"Should be the right margin-top on the element.");
is(span.textContent, 2, "Should have the right value in the box model.");
});
}
addTest("Test that clicking outside the editor blurs it",
function*(inspector, view) {
let node = content.document.getElementById("div5");
function* testBluringOnClick(inspector, view) {
info("Test that clicking outside the editor blurs it");
yield selectNode("#div5", inspector);
@ -200,4 +215,4 @@ function*(inspector, view) {
is(view.doc.querySelector(".styleinspector-propertyeditor"), null,
"Inplace editor has been removed.");
});
}

Просмотреть файл

@ -14,22 +14,20 @@ const TEST_URI = "<style>" +
"</style>" +
"<div id='div1'></div><div id='div2'></div><div id='div3'></div>";
function getStyle(node, property) {
return node.style.getPropertyValue(property);
}
add_task(function*() {
yield addTab("data:text/html," + encodeURIComponent(TEST_URI));
let {toolbox, inspector, view} = yield openLayoutView();
let {inspector, view, testActor} = yield openLayoutView();
yield runTests(inspector, view);
yield testEditing(inspector, view, testActor);
yield testEditingAndCanceling(inspector, view, testActor);
yield testDeleting(inspector, view, testActor);
yield testDeletingAndCanceling(inspector, view, testActor);
});
addTest("When all properties are set on the node editing one should work",
function*(inspector, view) {
let node = content.document.getElementById("div1");
function* testEditing(inspector, view, testActor) {
info("When all properties are set on the node editing one should work");
node.style.padding = "5px";
yield setStyle(testActor, "#div1", "padding", "5px");
yield waitForUpdate(inspector);
yield selectNode("#div1", inspector);
@ -46,19 +44,21 @@ function*(inspector, view) {
yield waitForUpdate(inspector);
is(editor.value, "7", "Should have the right value in the editor.");
is(getStyle(node, "padding-bottom"), "7px", "Should have updated the padding");
is((yield getStyle(testActor, "#div1", "padding-bottom")), "7px",
"Should have updated the padding");
EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView);
is(getStyle(node, "padding-bottom"), "7px", "Should be the right padding.")
is((yield getStyle(testActor, "#div1", "padding-bottom")), "7px",
"Should be the right padding.");
is(span.textContent, 7, "Should have the right value in the box model.");
});
}
addTest("When all properties are set on the node editing one should work",
function*(inspector, view) {
let node = content.document.getElementById("div1");
function* testEditingAndCanceling(inspector, view, testActor) {
info("When all properties are set on the node editing one and then " +
"cancelling with ESCAPE should work");
node.style.padding = "5px";
yield setStyle(testActor, "#div1", "padding", "5px");
yield waitForUpdate(inspector);
yield selectNode("#div1", inspector);
@ -75,20 +75,19 @@ function*(inspector, view) {
yield waitForUpdate(inspector);
is(editor.value, "8", "Should have the right value in the editor.");
is(getStyle(node, "padding-left"), "8px", "Should have updated the padding");
is((yield getStyle(testActor, "#div1", "padding-left")), "8px",
"Should have updated the padding");
EventUtils.synthesizeKey("VK_ESCAPE", {}, view.doc.defaultView);
yield waitForUpdate(inspector);
is(getStyle(node, "padding-left"), "5px", "Should be the right padding.")
is((yield getStyle(testActor, "#div1", "padding-left")), "5px",
"Should be the right padding.");
is(span.textContent, 5, "Should have the right value in the box model.");
});
}
addTest("When all properties are set on the node deleting one should work",
function*(inspector, view) {
let node = content.document.getElementById("div1");
node.style.padding = "5px";
function* testDeleting(inspector, view, testActor) {
info("When all properties are set on the node deleting one should work");
yield selectNode("#div1", inspector);
@ -104,19 +103,21 @@ function*(inspector, view) {
yield waitForUpdate(inspector);
is(editor.value, "", "Should have the right value in the editor.");
is(getStyle(node, "padding-left"), "", "Should have updated the padding");
is((yield getStyle(testActor, "#div1", "padding-left")), "",
"Should have updated the padding");
EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView);
is(getStyle(node, "padding-left"), "", "Should be the right padding.")
is((yield getStyle(testActor, "#div1", "padding-left")), "",
"Should be the right padding.");
is(span.textContent, 3, "Should have the right value in the box model.");
});
}
addTest("When all properties are set on the node deleting one then cancelling should work",
function*(inspector, view) {
let node = content.document.getElementById("div1");
function* testDeletingAndCanceling(inspector, view, testActor) {
info("When all properties are set on the node deleting one then cancelling " +
"should work");
node.style.padding = "5px";
yield setStyle(testActor, "#div1", "padding", "5px");
yield waitForUpdate(inspector);
yield selectNode("#div1", inspector);
@ -133,11 +134,13 @@ function*(inspector, view) {
yield waitForUpdate(inspector);
is(editor.value, "", "Should have the right value in the editor.");
is(getStyle(node, "padding-left"), "", "Should have updated the padding");
is((yield getStyle(testActor, "#div1", "padding-left")), "",
"Should have updated the padding");
EventUtils.synthesizeKey("VK_ESCAPE", {}, view.doc.defaultView);
yield waitForUpdate(inspector);
is(getStyle(node, "padding-left"), "5px", "Should be the right padding.")
is((yield getStyle(testActor, "#div1", "padding-left")), "5px",
"Should be the right padding.");
is(span.textContent, 5, "Should have the right value in the box model.");
});
}

Просмотреть файл

@ -14,17 +14,14 @@ const TEST_URI = "<style>" +
"</style>" +
"<div id='div1'></div><div id='div2'></div><div id='div3'></div>";
function getStyle(node, property) {
return node.style.getPropertyValue(property);
}
add_task(function*() {
yield addTab("data:text/html," + encodeURIComponent(TEST_URI));
let {toolbox, inspector, view} = yield openLayoutView();
let {inspector, view, testActor} = yield openLayoutView();
let node = content.document.getElementById("div1");
is(getStyle(node, "border-top-width"), "", "Should have the right border");
is(getStyle(node, "border-top-style"), "", "Should have the right border");
is((yield getStyle(testActor, "#div1", "border-top-width")), "",
"Should have the right border");
is((yield getStyle(testActor, "#div1", "border-top-style")), "",
"Should have the right border");
yield selectNode("#div1", inspector);
let span = view.doc.querySelector(".layout-border.layout-top > span");
@ -39,13 +36,17 @@ add_task(function*() {
yield waitForUpdate(inspector);
is(editor.value, "1", "Should have the right value in the editor.");
is(getStyle(node, "border-top-width"), "1px", "Should have the right border");
is(getStyle(node, "border-top-style"), "solid", "Should have the right border");
is((yield getStyle(testActor, "#div1", "border-top-width")), "1px",
"Should have the right border");
is((yield getStyle(testActor, "#div1", "border-top-style")), "solid",
"Should have the right border");
EventUtils.synthesizeKey("VK_ESCAPE", {}, view.doc.defaultView);
yield waitForUpdate(inspector);
is(getStyle(node, "border-top-width"), "", "Should be the right padding.")
is(getStyle(node, "border-top-style"), "", "Should have the right border");
is((yield getStyle(testActor, "#div1", "border-top-width")), "",
"Should be the right padding.");
is((yield getStyle(testActor, "#div1", "border-top-style")), "",
"Should have the right border");
is(span.textContent, 0, "Should have the right value in the box model.");
});

Просмотреть файл

@ -15,21 +15,20 @@ const TEST_URI = "<style>" +
"</style>" +
"<div id='div1'></div><div id='div2'></div><div id='div3'></div>";
function getStyle(node, property) {
return node.style.getPropertyValue(property);
}
add_task(function*() {
yield addTab("data:text/html," + encodeURIComponent(TEST_URI));
let {toolbox, inspector, view} = yield openLayoutView();
let {inspector, view, testActor} = yield openLayoutView();
yield runTests(inspector, view);
yield testUnits(inspector, view, testActor);
yield testValueComesFromStyleRule(inspector, view, testActor);
yield testShorthandsAreParsed(inspector, view, testActor);
});
addTest("Test that entering units works",
function*(inspector, view) {
let node = content.document.getElementById("div1");
is(getStyle(node, "padding-top"), "", "Should have the right padding");
function* testUnits(inspector, view, testActor) {
info("Test that entering units works");
is((yield getStyle(testActor, "#div1", "padding-top")), "",
"Should have the right padding");
yield selectNode("#div1", inspector);
let span = view.doc.querySelector(".layout-padding.layout-top > span");
@ -45,24 +44,28 @@ function*(inspector, view) {
EventUtils.synthesizeKey("e", {}, view.doc.defaultView);
yield waitForUpdate(inspector);
is(getStyle(node, "padding-top"), "", "An invalid value is handled cleanly");
is((yield getStyle(testActor, "#div1", "padding-top")), "",
"An invalid value is handled cleanly");
EventUtils.synthesizeKey("m", {}, view.doc.defaultView);
yield waitForUpdate(inspector);
is(editor.value, "1em", "Should have the right value in the editor.");
is(getStyle(node, "padding-top"), "1em", "Should have updated the padding.");
is((yield getStyle(testActor, "#div1", "padding-top")),
"1em", "Should have updated the padding.");
EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView);
is(getStyle(node, "padding-top"), "1em", "Should be the right padding.")
is((yield getStyle(testActor, "#div1", "padding-top")), "1em",
"Should be the right padding.");
is(span.textContent, 16, "Should have the right value in the box model.");
});
}
addTest("Test that we pick up the value from a higher style rule",
function*(inspector, view) {
let node = content.document.getElementById("div2");
is(getStyle(node, "border-bottom-width"), "", "Should have the right border-bottom-width");
function* testValueComesFromStyleRule(inspector, view, testActor) {
info("Test that we pick up the value from a higher style rule");
is((yield getStyle(testActor, "#div2", "border-bottom-width")), "",
"Should have the right border-bottom-width");
yield selectNode("#div2", inspector);
let span = view.doc.querySelector(".layout-border.layout-bottom > span");
@ -77,18 +80,21 @@ function*(inspector, view) {
yield waitForUpdate(inspector);
is(editor.value, "0", "Should have the right value in the editor.");
is(getStyle(node, "border-bottom-width"), "0px", "Should have updated the border.");
is((yield getStyle(testActor, "#div2", "border-bottom-width")), "0px",
"Should have updated the border.");
EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView);
is(getStyle(node, "border-bottom-width"), "0px", "Should be the right border-bottom-width.")
is((yield getStyle(testActor, "#div2", "border-bottom-width")), "0px",
"Should be the right border-bottom-width.");
is(span.textContent, 0, "Should have the right value in the box model.");
});
}
addTest("Test that shorthand properties are parsed correctly",
function*(inspector, view) {
let node = content.document.getElementById("div3");
is(getStyle(node, "padding-right"), "", "Should have the right padding");
function* testShorthandsAreParsed(inspector, view, testActor) {
info("Test that shorthand properties are parsed correctly");
is((yield getStyle(testActor, "#div3", "padding-right")), "",
"Should have the right padding");
yield selectNode("#div3", inspector);
let span = view.doc.querySelector(".layout-padding.layout-right > span");
@ -101,6 +107,7 @@ function*(inspector, view) {
EventUtils.synthesizeKey("VK_RETURN", {}, view.doc.defaultView);
is(getStyle(node, "padding-right"), "", "Should be the right padding.")
is((yield getStyle(testActor, "#div3", "padding-right")), "",
"Should be the right padding.");
is(span.textContent, 32, "Should have the right value in the box model.");
});
}

Просмотреть файл

@ -9,8 +9,9 @@
// devtools/inspector/test folder. This test only cares about checking that the
// layout-view does call the highlighter, and it does so by mocking it.
const STYLE = "div { position: absolute; top: 50px; left: 50px; height: 10px; " +
"width: 10px; border: 10px solid black; padding: 10px; margin: 10px;}";
const STYLE = "div { position: absolute; top: 50px; left: 50px; " +
"height: 10px; width: 10px; border: 10px solid black; " +
"padding: 10px; margin: 10px;}";
const HTML = "<style>" + STYLE + "</style><div></div>";
const TEST_URL = "data:text/html;charset=utf-8," + encodeURIComponent(HTML);
@ -25,7 +26,7 @@ add_task(function*() {
toolbox.highlighter.showBoxModel = function(nodeFront, options) {
highlightedNodeFront = nodeFront;
highlighterOptions = options;
}
};
let elt = view.doc.getElementById("layout-margins");
yield testGuideOnLayoutHover(elt, "margin", inspector, view);
@ -40,9 +41,9 @@ add_task(function*() {
yield testGuideOnLayoutHover(elt, "content", inspector, view);
});
function* testGuideOnLayoutHover(elt, expectedRegion, inspector, view) {
function* testGuideOnLayoutHover(elt, expectedRegion, inspector) {
info("Synthesizing mouseover on the layout-view");
EventUtils.synthesizeMouse(elt, 2, 2, {type:'mouseover'},
EventUtils.synthesizeMouse(elt, 2, 2, {type: "mouseover"},
elt.ownerDocument.defaultView);
info("Waiting for the node-highlight event from the toolbox");

Просмотреть файл

@ -23,7 +23,8 @@ const res1 = [
const TEST_URI = encodeURIComponent([
"<style>",
"div{border:10px solid black; padding: 20px 20px 20px 2000000px; margin: 30px auto;}",
"div { border:10px solid black; padding: 20px 20px 20px 2000000px; " +
"margin: 30px auto; }",
"</style>",
"<div></div>"
].join(""));
@ -31,7 +32,7 @@ const LONG_TEXT_ROTATE_LIMIT = 3;
add_task(function*() {
yield addTab("data:text/html," + TEST_URI);
let {toolbox, inspector, view} = yield openLayoutView();
let {inspector, view} = yield openLayoutView();
yield selectNode("div", inspector);
for (let i = 0; i < res1.length; i++) {

Просмотреть файл

@ -42,14 +42,14 @@ const VALUES_TEST_DATA = [{
ruleSelector: "#div1",
styleSheetLocation: "null:1"
}]
},{
}, {
selector: "#div2",
values: [{
name: "border-bottom-width",
ruleSelector: "#div2",
styleSheetLocation: "null:2"
}]
},{
}, {
selector: "#div3",
values: [{
name: "padding-top",
@ -72,7 +72,7 @@ const VALUES_TEST_DATA = [{
add_task(function*() {
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
let {toolbox, inspector, view} = yield openLayoutView();
let {inspector, view} = yield openLayoutView();
info("Checking the regions tooltips");

Просмотреть файл

@ -7,14 +7,31 @@
// Test that the layout-view continues to work after a page navigation and that
// it also works after going back
const IFRAME1 = URL_ROOT + "doc_layout_iframe1.html";
const IFRAME2 = URL_ROOT + "doc_layout_iframe2.html";
add_task(function*() {
yield addTab(URL_ROOT + "doc_layout_iframe1.html");
let {toolbox, inspector, view} = yield openLayoutView();
yield runTests(inspector, view);
yield addTab(IFRAME1);
let {inspector, view, testActor} = yield openLayoutView();
yield testFirstPage(inspector, view, testActor);
info("Navigate to the second page");
yield testActor.eval(`content.location.href="${IFRAME2}"`);
yield inspector.once("markuploaded");
yield testSecondPage(inspector, view, testActor);
info("Go back to the first page");
yield testActor.eval("content.history.back();");
yield inspector.once("markuploaded");
yield testBackToFirstPage(inspector, view, testActor);
});
addTest("Test that the layout-view works on the first page",
function*(inspector, view) {
function* testFirstPage(inspector, view, testActor) {
info("Test that the layout-view works on the first page");
info("Selecting the test node");
yield selectNode("p", inspector);
@ -24,22 +41,17 @@ function*(inspector, view) {
info("Listening for layout-view changes and modifying the padding");
let onUpdated = waitForUpdate(inspector);
getNode("p").style.padding = "20px";
yield setStyle(testActor, "p", "padding", "20px");
yield onUpdated;
ok(true, "Layout-view got updated");
info("Checking that the layout-view shows the right value after update");
is(paddingElt.textContent, "20");
});
}
addTest("Navigate to the second page",
function*(inspector, view) {
yield navigateTo(URL_ROOT + "doc_layout_iframe2.html");
yield inspector.once("markuploaded");
});
function* testSecondPage(inspector, view, testActor) {
info("Test that the layout-view works on the second page");
addTest("Test that the layout-view works on the second page",
function*(inspector, view) {
info("Selecting the test node");
yield selectNode("p", inspector);
@ -49,22 +61,17 @@ function*(inspector, view) {
info("Listening for layout-view changes and modifying the size");
let onUpdated = waitForUpdate(inspector);
getNode("p").style.width = "200px";
yield setStyle(testActor, "p", "width", "200px");
yield onUpdated;
ok(true, "Layout-view got updated");
info("Checking that the layout-view shows the right value after update");
is(sizeElt.textContent, "200" + "\u00D7" + "100");
});
}
addTest("Go back to the first page",
function*(inspector, view) {
content.history.back();
yield inspector.once("markuploaded");
});
function* testBackToFirstPage(inspector, view, testActor) {
info("Test that the layout-view works on the first page after going back");
addTest("Test that the layout-view works on the first page after going back",
function*(inspector, view) {
info("Selecting the test node");
yield selectNode("p", inspector);
@ -75,24 +82,10 @@ function*(inspector, view) {
info("Listening for layout-view changes and modifying the padding");
let onUpdated = waitForUpdate(inspector);
getNode("p").style.padding = "100px";
yield setStyle(testActor, "p", "padding", "100px");
yield onUpdated;
ok(true, "Layout-view got updated");
info("Checking that the layout-view shows the right value after update");
is(paddingElt.textContent, "100");
});
function navigateTo(url) {
info("Navigating to " + url);
let def = promise.defer();
gBrowser.selectedBrowser.addEventListener("load", function onload() {
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
info("URL " + url + " loading complete");
waitForFocus(def.resolve, content);
}, true);
content.location = url;
return def.promise;
}

Просмотреть файл

@ -8,20 +8,20 @@
add_task(function*() {
yield addTab(URL_ROOT + "doc_layout_iframe1.html");
let {toolbox, inspector, view} = yield openLayoutView();
let {inspector, view, testActor} = yield openLayoutView();
info("Test that the layout-view works on the first page");
yield assertLayoutView(inspector, view);
yield assertLayoutView(inspector, view, testActor);
info("Reload the page");
content.location.reload();
yield testActor.eval("content.location.reload();");
yield inspector.once("markuploaded");
info("Test that the layout-view works on the reloaded page");
yield assertLayoutView(inspector, view);
yield assertLayoutView(inspector, view, testActor);
});
function* assertLayoutView(inspector, view) {
function* assertLayoutView(inspector, view, testActor) {
info("Selecting the test node");
yield selectNode("p", inspector);
@ -31,7 +31,7 @@ function* assertLayoutView(inspector, view) {
info("Listening for layout-view changes and modifying the padding");
let onUpdated = waitForUpdate(inspector);
getNode("p").style.padding = "20px";
yield setStyle(testActor, "p", "padding", "20px");
yield onUpdated;
ok(true, "Layout-view got updated");

Просмотреть файл

@ -9,16 +9,16 @@
add_task(function*() {
yield addTab(URL_ROOT + "doc_layout_iframe1.html");
let iframe2 = getNode("iframe").contentDocument.querySelector("iframe");
let {inspector, view, testActor} = yield openLayoutView();
let {toolbox, inspector, view} = yield openLayoutView();
yield runTests(inspector, view, iframe2);
yield testResizingInIframe(inspector, view, testActor);
yield testReflowsAfterIframeDeletion(inspector, view, testActor);
});
addTest("Test that resizing an element in an iframe updates its box model",
function*(inspector, view, iframe2) {
function* testResizingInIframe(inspector, view, testActor) {
info("Test that resizing an element in an iframe updates its box model");
info("Selecting the nested test node");
let node = iframe2.contentDocument.querySelector("div");
yield selectNodeInIframe2("div", inspector);
info("Checking that the layout-view shows the right value");
@ -27,22 +27,23 @@ function*(inspector, view, iframe2) {
info("Listening for layout-view changes and modifying its size");
let onUpdated = waitForUpdate(inspector);
node.style.width = "200px";
yield setStyleInIframe2(testActor, "div", "width", "200px");
yield onUpdated;
ok(true, "Layout-view got updated");
info("Checking that the layout-view shows the right value after update");
is(sizeElt.textContent, "200\u00D7200");
});
}
function* testReflowsAfterIframeDeletion(inspector, view, testActor) {
info("Test reflows are still sent to the layout-view after deleting an " +
"iframe");
addTest("Test reflows are still sent to the layout-view after deleting an iframe",
function*(inspector, view, iframe2) {
info("Deleting the iframe2");
iframe2.remove();
yield removeIframe2(testActor);
yield inspector.once("inspector-updated");
info("Selecting the test node in iframe1");
let node = getNode("iframe").contentDocument.querySelector("p");
yield selectNodeInIframe1("p", inspector);
info("Checking that the layout-view shows the right value");
@ -51,13 +52,13 @@ function*(inspector, view, iframe2) {
info("Listening for layout-view changes and modifying its size");
let onUpdated = waitForUpdate(inspector);
node.style.width = "200px";
yield setStyleInIframe1(testActor, "p", "width", "200px");
yield onUpdated;
ok(true, "Layout-view got updated");
info("Checking that the layout-view shows the right value after update");
is(sizeElt.textContent, "200\u00D7100");
});
}
function* selectNodeInIframe1(selector, inspector) {
let iframe1 = yield getNodeFront("iframe", inspector);
@ -71,3 +72,30 @@ function* selectNodeInIframe2(selector, inspector) {
let node = yield getNodeFrontInFrame(selector, iframe2, inspector);
yield selectNode(node, inspector);
}
function* setStyleInIframe1(testActor, selector, propertyName, value) {
yield testActor.eval(`
content.document.querySelector("iframe")
.contentDocument.querySelector("${selector}")
.style.${propertyName} = "${value}";
`);
}
function* setStyleInIframe2(testActor, selector, propertyName, value) {
yield testActor.eval(`
content.document.querySelector("iframe")
.contentDocument
.querySelector("iframe")
.contentDocument.querySelector("${selector}")
.style.${propertyName} = "${value}";
`);
}
function* removeIframe2(testActor) {
yield testActor.eval(`
content.document.querySelector("iframe")
.contentDocument
.querySelector("iframe")
.remove();
`);
}

Просмотреть файл

@ -1,6 +1,9 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint no-unused-vars: [2, {"vars": "local"}] */
/* import-globals-from ../../../framework/test/shared-head.js */
/* import-globals-from ../../test/head.js */
"use strict";
// Import the inspector's head.js first (which itself imports shared-head.js).
@ -52,23 +55,24 @@ function selectAndHighlightNode(nodeOrSelector, inspector) {
* view is visible and ready
*/
function openLayoutView() {
return openInspectorSidebarTab("layoutview").then(({toolbox, inspector}) => {
return openInspectorSidebarTab("layoutview").then(data => {
// The actual highligher show/hide methods are mocked in layoutview tests.
// The highlighter is tested in devtools/inspector/test.
function mockHighlighter({highlighter}) {
highlighter.showBoxModel = function(nodeFront, options) {
highlighter.showBoxModel = function() {
return promise.resolve();
};
highlighter.hideBoxModel = function() {
return promise.resolve();
};
}
mockHighlighter(toolbox);
mockHighlighter(data.toolbox);
return {
toolbox,
inspector,
view: inspector.layoutview
toolbox: data.toolbox,
inspector: data.inspector,
view: data.inspector.layoutview,
testActor: data.testActor
};
});
}
@ -81,27 +85,16 @@ function waitForUpdate(inspector) {
return inspector.once("layoutview-updated");
}
/**
* The addTest/runTests function couple provides a simple way to define
* subsequent test cases in a test file. Example:
*
* addTest("what this test does", function*() {
* ... actual code for the test ...
* });
* addTest("what this second test does", function*() {
* ... actual code for the second test ...
* });
* runTests().then(...);
*/
var TESTS = [];
function addTest(message, func) {
TESTS.push([message, Task.async(func)]);
function getStyle(testActor, selector, propertyName) {
return testActor.eval(`
content.document.querySelector("${selector}")
.style.getPropertyValue("${propertyName}");
`);
}
var runTests = Task.async(function*(...args) {
for (let [message, test] of TESTS) {
info("Running new test case: " + message);
yield test.apply(null, args);
}
});
function setStyle(testActor, selector, propertyName, value) {
return testActor.eval(`
content.document.querySelector("${selector}")
.style.${propertyName} = "${value}";
`);
}

Просмотреть файл

@ -170,14 +170,15 @@ function getActiveInspector() {
* visible and ready
*/
var openInspectorSidebarTab = Task.async(function*(id, hostType) {
let {toolbox, inspector} = yield openInspector();
let {toolbox, inspector, testActor} = yield openInspector();
info("Selecting the " + id + " sidebar");
inspector.sidebar.select(id);
return {
toolbox,
inspector
inspector,
testActor
};
});

Просмотреть файл

@ -61,14 +61,35 @@ player.timeLabel=%Ss
# LOCALIZATION NOTE (player.playbackRateLabel):
# This string is displayed in each animation player widget, as the label of
# drop-down list items that can be used to change the rate at which the
# animation runs (1x being the default, 2x being twice as fast).
player.playbackRateLabel=%Sx
# animation runs (1× being the default, 2× being twice as fast).
player.playbackRateLabel=%S×
# LOCALIZATION NOTE (player.runningOnCompositorTooltip):
# This string is displayed as a tooltip for the icon that indicates that the
# animation is running on the compositor thread.
player.runningOnCompositorTooltip=This animation is running on compositor thread
# LOCALIZATION NOTE (timeline.rateSelectorTooltip):
# This string is displayed in the timeline toolbar, as the tooltip of the
# drop-down list that can be used to change the rate at which the animations
# run.
timeline.rateSelectorTooltip=Set the animations playback rates
# LOCALIZATION NOTE (timeline.pauseResumeButtonTooltip):
# This string is displayed in the timeline toolbar, as the tooltip of the
# pause/resume button that can be used to pause or resume the animations
timeline.pausedButtonTooltip=Resume the animations
# LOCALIZATION NOTE (timeline.pauseResumeButtonTooltip):
# This string is displayed in the timeline toolbar, as the tooltip of the
# pause/resume button that can be used to pause or resume the animations
timeline.resumedButtonTooltip=Pause the animations
# LOCALIZATION NOTE (timeline.rewindButtonTooltip):
# This string is displayed in the timeline toolbar, as the tooltip of the
# rewind button that can be used to rewind the animations
timeline.rewindButtonTooltip=Rewind the animations
# LOCALIZATION NOTE (timeline.timeGraduationLabel):
# This string is displayed at the top of the animation panel, next to each time
# graduation, to indicate what duration (in milliseconds) this graduation

Просмотреть файл

@ -9,8 +9,8 @@ Cu.import("resource://gre/modules/Task.jsm");
var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
var { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
var { require } = Cu.import("resource://gre/modules/devtools/shared/Loader.jsm", {});
var { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
var { gDevTools } = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {});
var { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
var { DebuggerServer } = require("devtools/server/main");
var { DebuggerClient } = require("devtools/shared/client/main");

Просмотреть файл

@ -178,11 +178,15 @@ body {
left: 0;
width: 100%;
height: 100%;
background-image: url("chrome://devtools/skin/images/dropmarker.svg");
background-repeat: no-repeat;
background-position: calc(100% - 4px) center;
padding-right: 1em;
}
#timeline-rate {
position: relative;
width: 4em;
width: 4.5em;
}
/* Animation timeline component */

Просмотреть файл

@ -1158,7 +1158,7 @@ var Cmds = {
reloadDevtools: function(event) {
if (Services.prefs.prefHasUserValue("devtools.loader.srcdir")) {
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
let {devtools} = Cu.import("resource://devtools/shared/Loader.jsm", {});
devtools.reload();
}
}

Просмотреть файл

@ -41,6 +41,7 @@ const ANIMATION_TYPES = {
CSS_TRANSITION: "csstransition",
UNKNOWN: "unknown"
};
exports.ANIMATION_TYPES = ANIMATION_TYPES;
/**
* The AnimationPlayerActor provides information about a given animation: its
@ -71,16 +72,12 @@ var AnimationPlayerActor = ActorClass({
this.onAnimationMutation = this.onAnimationMutation.bind(this);
this.walker = animationsActor.walker;
this.tabActor = animationsActor.tabActor;
this.player = player;
this.node = player.effect.target;
let win = this.node.ownerDocument.defaultView;
this.styles = win.getComputedStyle(this.node);
// Listen to animation mutations on the node to alert the front when the
// current animation changes.
this.observer = new win.MutationObserver(this.onAnimationMutation);
this.observer = new this.window.MutationObserver(this.onAnimationMutation);
this.observer.observe(this.node, {animations: true});
},
@ -90,12 +87,15 @@ var AnimationPlayerActor = ActorClass({
if (this.observer && !Cu.isDeadWrapper(this.observer)) {
this.observer.disconnect();
}
this.tabActor = this.player = this.node = this.styles = null;
this.observer = this.walker = null;
this.player = this.node = this.observer = this.walker = null;
Actor.prototype.destroy.call(this);
},
get window() {
return this.node.ownerDocument.defaultView;
},
/**
* Release the actor, when it isn't needed anymore.
* Protocol.js uses this release method to call the destroy method.
@ -119,12 +119,12 @@ var AnimationPlayerActor = ActorClass({
return data;
},
isAnimation: function(player=this.player) {
return player instanceof this.tabActor.window.CSSAnimation;
isAnimation: function(player = this.player) {
return player instanceof this.window.CSSAnimation;
},
isTransition: function(player=this.player) {
return player instanceof this.tabActor.window.CSSTransition;
isTransition: function(player = this.player) {
return player instanceof this.window.CSSTransition;
},
getType: function() {
@ -372,6 +372,8 @@ var AnimationPlayerActor = ActorClass({
})
});
exports.AnimationPlayerActor = AnimationPlayerActor;
var AnimationPlayerFront = FrontClass(AnimationPlayerActor, {
initialize: function(conn, form, detail, ctx) {
Front.prototype.initialize.call(this, conn, form, detail, ctx);

Просмотреть файл

@ -145,19 +145,6 @@
width: 100px;
}
}
.script-generated {
display: inline-block;
width: 50px;
height: 50px;
border-radius: 50%;
background-color: black;
background-image:
repeating-linear-gradient(45deg, transparent 0, transparent 5px, #f06 5px, #f06 10px);
border: 5px solid #f06;
box-sizing: border-box;
}
</style>
<div class="not-animated"></div>
<div class="simple-animation"></div>
@ -170,26 +157,11 @@
<div class="delayed-multiple-animations"></div>
<div class="multiple-animations-2"></div>
<div class="all-transitions"></div>
<div class="script-generated"></div>
<script type="text/javascript">
// Get the transitions started when the page loads
var players;
addEventListener("load", function() {
document.querySelector(".transition").classList.add("get-round");
document.querySelector(".delayed-transition").classList.add("get-round");
// Create a script-generated animation.
var animation = document.querySelector(".script-generated").animate({
backgroundColor: ["black", "gold"]
}, {
duration: 500,
iterations: Infinity,
direction: "alternate"
});
animation.id = "custom-animation-name";
// Add a custom animation id to an existing css animation.
document.querySelector(".delayed-animation")
.getAnimations()[0].id = "cssanimation-custom-name";
});
</script>

Просмотреть файл

@ -28,7 +28,6 @@ support-files =
[browser_animation_getStateAfterFinished.js]
[browser_animation_getSubTreeAnimations.js]
[browser_animation_keepFinished.js]
[browser_animation_name.js]
[browser_animation_playerState.js]
[browser_animation_playPauseIframe.js]
[browser_animation_playPauseSeveral.js]

Просмотреть файл

@ -1,51 +0,0 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the AnimationPlayerActor provides the correct name for an
// animation. Whether this animation is a CSSAnimation, CSSTransition or a
// script-based animation that has been given an id, or even a CSSAnimation that
// has been given an id.
const TEST_DATA = [{
selector: ".simple-animation",
animationIndex: 0,
expectedName: "move"
}, {
selector: ".transition",
animationIndex: 0,
expectedName: "width"
}, {
selector: ".script-generated",
animationIndex: 0,
expectedName: "custom-animation-name"
}, {
selector: ".delayed-animation",
animationIndex: 0,
expectedName: "cssanimation-custom-name"
}];
add_task(function*() {
let {client, walker, animations} =
yield initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
for (let {selector, animationIndex, expectedName} of TEST_DATA) {
let {name} = yield getAnimationStateForNode(walker, animations, selector,
animationIndex);
is(name, expectedName, "The animation has the expected name");
}
yield closeDebuggerClient(client);
gBrowser.removeCurrentTab();
});
function* getAnimationStateForNode(walker, animations, nodeSelector, index) {
let node = yield walker.querySelector(walker.rootNode, nodeSelector);
let players = yield animations.getAnimationPlayersForNode(node);
let player = players[index];
yield player.ready();
let state = yield player.getCurrentState();
return state;
}

Просмотреть файл

@ -14,7 +14,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1157469
window.onload = function() {
const Cu = Components.utils;
Cu.import("resource://gre/modules/devtools/Loader.jsm");
Cu.import("resource://devtools/shared/Loader.jsm");
const {InspectorFront} =
devtools.require("devtools/server/actors/inspector");

Просмотреть файл

@ -13,13 +13,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1222409
<script type="application/javascript;version=1.8">
window.onload = function() {
const Cu = Components.utils;
Cu.import("resource://gre/modules/devtools/Loader.jsm");
Cu.import("resource://devtools/shared/Loader.jsm");
const {Promise: promise} =
Cu.import("resource://gre/modules/Promise.jsm", {});
const {InspectorFront} =
devtools.require("devtools/server/actors/inspector");
const {console} =
Cu.import("resource://gre/modules/devtools/shared/Console.jsm", {});
const {console} = Cu.import("resource://gre/modules/Console.jsm", {});
SimpleTest.waitForExplicitFinish();

Просмотреть файл

@ -12,13 +12,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=835896
<script type="application/javascript;version=1.8">
window.onload = function() {
const Cu = Components.utils;
Cu.import("resource://gre/modules/devtools/Loader.jsm");
Cu.import("resource://devtools/shared/Loader.jsm");
const {Promise: promise} =
Cu.import("resource://gre/modules/Promise.jsm", {});
const {InspectorFront} =
devtools.require("devtools/server/actors/inspector");
const {console} =
Cu.import("resource://gre/modules/devtools/shared/Console.jsm", {});
const {console} = Cu.import("resource://gre/modules/Console.jsm", {});
SimpleTest.waitForExplicitFinish();

Просмотреть файл

@ -12,15 +12,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=835896
<script type="application/javascript;version=1.8">
window.onload = function() {
const Cu = Components.utils;
Cu.import("resource://gre/modules/devtools/Loader.jsm");
Cu.import("resource://devtools/shared/Loader.jsm");
const {Promise: promise} =
Cu.import("resource://gre/modules/Promise.jsm", {});
const {InspectorFront} =
devtools.require("devtools/server/actors/inspector");
const {WalkerSearch, WalkerIndex} =
devtools.require("devtools/server/actors/utils/walker-search");
const {console} =
Cu.import("resource://gre/modules/devtools/shared/Console.jsm", {});
const {console} = Cu.import("resource://gre/modules/Console.jsm", {});
SimpleTest.waitForExplicitFinish();

Просмотреть файл

@ -0,0 +1,79 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that AnimationPlayerActor.getName returns the right name depending on
// the type of an animation and the various properties available on it.
const { AnimationPlayerActor } = require("devtools/server/actors/animation");
function run_test() {
// Mock a window with just the properties the AnimationPlayerActor uses.
let window = {
MutationObserver: function() {
this.observe = () => {};
},
Animation: function() {
this.effect = {target: getMockNode()};
},
CSSAnimation: function() {
this.effect = {target: getMockNode()};
},
CSSTransition: function() {
this.effect = {target: getMockNode()};
}
};
// Helper to get a mock DOM node.
function getMockNode() {
return {
ownerDocument: {
defaultView: window
}
};
}
// Objects in this array should contain the following properties:
// - desc {String} For logging
// - animation {Object} An animation object instantiated from one of the mock
// window animation constructors.
// - props {Objet} Properties of this object will be added to the animation
// object.
// - expectedName {String} The expected name returned by
// AnimationPlayerActor.getName.
const TEST_DATA = [{
desc: "Animation with an id",
animation: new window.Animation(),
props: { id: "animation-id" },
expectedName: "animation-id"
}, {
desc: "CSSTransition with an id",
animation: new window.CSSTransition(),
props: { id: "transition-with-id", transitionProperty: "width" },
expectedName: "transition-with-id"
}, {
desc: "CSSAnimation with an id",
animation: new window.CSSAnimation(),
props: { id: "animation-with-id", animationName: "move" },
expectedName: "animation-with-id"
}, {
desc: "CSSTransition without an id",
animation: new window.CSSTransition(),
props: { transitionProperty: "width" },
expectedName: "width"
}, {
desc: "CSSAnimation without an id",
animation: new window.CSSAnimation(),
props: { animationName: "move" },
expectedName: "move"
}];
for (let { desc, animation, props, expectedName } of TEST_DATA) {
do_print(desc);
for (let key in props) {
animation[key] = props[key];
}
let actor = AnimationPlayerActor({}, animation);
do_check_eq(actor.getName(), expectedName);
}
}

Просмотреть файл

@ -0,0 +1,61 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test the output of AnimationPlayerActor.getType().
const { ANIMATION_TYPES, AnimationPlayerActor } =
require("devtools/server/actors/animation");
function run_test() {
// Mock a window with just the properties the AnimationPlayerActor uses.
let window = {
MutationObserver: function() {
this.observe = () => {};
},
Animation: function() {
this.effect = {target: getMockNode()};
},
CSSAnimation: function() {
this.effect = {target: getMockNode()};
},
CSSTransition: function() {
this.effect = {target: getMockNode()};
}
};
// Helper to get a mock DOM node.
function getMockNode() {
return {
ownerDocument: {
defaultView: window
}
};
}
// Objects in this array should contain the following properties:
// - desc {String} For logging
// - animation {Object} An animation object instantiated from one of the mock
// window animation constructors.
// - expectedType {String} The expected type returned by
// AnimationPlayerActor.getType.
const TEST_DATA = [{
desc: "Test CSSAnimation type",
animation: new window.CSSAnimation(),
expectedType: ANIMATION_TYPES.CSS_ANIMATION
}, {
desc: "Test CSSTransition type",
animation: new window.CSSTransition(),
expectedType: ANIMATION_TYPES.CSS_TRANSITION
}, {
desc: "Test unknown type",
animation: {effect: {target: getMockNode()}},
expectedType: ANIMATION_TYPES.UNKNOWN
}];
for (let { desc, animation, expectedType } of TEST_DATA) {
do_print(desc);
let actor = AnimationPlayerActor({}, animation);
do_check_eq(actor.getType(), expectedType);
}
}

Просмотреть файл

@ -33,6 +33,8 @@ support-files =
setBreakpoint-on-line-with-no-offsets-at-end-of-script.js
setBreakpoint-on-line-with-no-offsets-in-gcd-script.js
[test_animation_name.js]
[test_animation_type.js]
[test_ScriptStore.js]
[test_actor-registry-actor.js]
[test_nesting-01.js]

Просмотреть файл

@ -191,7 +191,7 @@ pref("xpinstall.whitelist.fileRequest", false);
pref("xpinstall.whitelist.add", "https://addons.mozilla.org");
pref("xpinstall.whitelist.add.180", "https://marketplace.firefox.com");
pref("xpinstall.signatures.required", false);
pref("xpinstall.signatures.required", true);
pref("extensions.enabledScopes", 1);
pref("extensions.autoupdate.enabled", true);

Просмотреть файл

@ -6,7 +6,6 @@
package org.mozilla.gecko;
import android.Manifest;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import org.json.JSONArray;
import org.mozilla.gecko.adjust.AdjustHelperInterface;
@ -34,7 +33,9 @@ import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.home.BrowserSearch;
import org.mozilla.gecko.home.HomeBanner;
import org.mozilla.gecko.home.HomeConfig;
import org.mozilla.gecko.home.HomeConfig.PanelType;
import org.mozilla.gecko.home.HomeConfigPrefsBackend;
import org.mozilla.gecko.home.HomePager;
import org.mozilla.gecko.home.HomePager.OnUrlOpenInBackgroundListener;
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
@ -180,6 +181,7 @@ public class BrowserApp extends GeckoApp
private static final String ADD_SHORTCUT_TOAST = "add_shortcut_toast";
public static final String GUEST_BROWSING_ARG = "--guest";
public static final String INTENT_KEY_SWITCHBOARD_UUID = "switchboard-uuid";
private static final String STATE_ABOUT_HOME_TOP_PADDING = "abouthome_top_padding";
@ -202,7 +204,7 @@ public class BrowserApp extends GeckoApp
private BrowserToolbar mBrowserToolbar;
private View mDoorhangerOverlay;
// We can't name the TabStrip class because it's not included on API 9.
private Refreshable mTabStrip;
private TabStripInterface mTabStrip;
private ToolbarProgressView mProgressView;
private FirstrunAnimationContainer mFirstrunAnimationContainer;
private HomePager mHomePager;
@ -589,12 +591,15 @@ public class BrowserApp extends GeckoApp
// Initializes the default URLs the first time.
SwitchBoard.initDefaultServerUrls("https://switchboard-server.dev.mozaws.net/urls", "https://switchboard-server.dev.mozaws.net/v1", true);
final String switchboardUUID = ContextUtils.getStringExtra(intent, INTENT_KEY_SWITCHBOARD_UUID);
SwitchBoard.setUUIDFromExtra(switchboardUUID);
// Looks at the server if there are changes in the server URL that should be used in the future
new AsyncConfigLoader(this, AsyncConfigLoader.UPDATE_SERVER).execute();
new AsyncConfigLoader(this, AsyncConfigLoader.UPDATE_SERVER, switchboardUUID).execute();
// Loads the actual config. This can be done on app start or on app onResume() depending
// how often you want to update the config.
new AsyncConfigLoader(this, AsyncConfigLoader.CONFIG_SERVER).execute();
new AsyncConfigLoader(this, AsyncConfigLoader.CONFIG_SERVER, switchboardUUID).execute();
}
mBrowserChrome = (ViewGroup) findViewById(R.id.browser_chrome);
@ -635,7 +640,7 @@ public class BrowserApp extends GeckoApp
}
if (HardwareUtils.isTablet()) {
mTabStrip = (Refreshable) (((ViewStub) findViewById(R.id.tablet_tab_strip)).inflate());
mTabStrip = (TabStripInterface) (((ViewStub) findViewById(R.id.tablet_tab_strip)).inflate());
}
((GeckoApp.MainLayout) mMainLayout).setTouchEventInterceptor(new HideOnTouchListener());
@ -853,6 +858,16 @@ public class BrowserApp extends GeckoApp
if (prefs.getBoolean(FirstrunAnimationContainer.PREF_FIRSTRUN_ENABLED, false)) {
if (!Intent.ACTION_VIEW.equals(intent.getAction())) {
showFirstrunPager();
if (HardwareUtils.isTablet()) {
mTabStrip.setOnTabChangedListener(new TabStripInterface.OnTabAddedOrRemovedListener() {
@Override
public void onTabChanged() {
hideFirstrunPager(TelemetryContract.Method.BUTTON);
mTabStrip.setOnTabChangedListener(null);
}
});
}
}
// Don't bother trying again to show the v1 minimal first run.
prefs.edit().putBoolean(FirstrunAnimationContainer.PREF_FIRSTRUN_ENABLED, false).apply();
@ -891,8 +906,7 @@ public class BrowserApp extends GeckoApp
return;
}
if (hideFirstrunPager()) {
Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, TelemetryContract.Method.BACK, "firstrun-pane");
if (hideFirstrunPager(TelemetryContract.Method.BACK)) {
return;
}
@ -1224,6 +1238,8 @@ public class BrowserApp extends GeckoApp
public boolean onContextItemSelected(MenuItem item) {
final int itemId = item.getItemId();
if (itemId == R.id.pasteandgo) {
hideFirstrunPager(TelemetryContract.Method.CONTEXT_MENU);
String text = Clipboard.getText();
if (!TextUtils.isEmpty(text)) {
loadUrlOrKeywordSearch(text);
@ -1745,6 +1761,9 @@ public class BrowserApp extends GeckoApp
Telemetry.addToHistogram("BROWSER_IS_USER_DEFAULT", (isDefaultBrowser(Intent.ACTION_VIEW) ? 1 : 0));
Telemetry.addToHistogram("FENNEC_TABQUEUE_ENABLED", (TabQueueHelper.isTabQueueEnabled(BrowserApp.this) ? 1 : 0));
Telemetry.addToHistogram("FENNEC_CUSTOM_HOMEPAGE", (TextUtils.isEmpty(getHomepage()) ? 0 : 1));
final SharedPreferences prefs = GeckoSharedPrefs.forProfile(getContext());
final boolean hasCustomHomepanels = prefs.contains(HomeConfigPrefsBackend.PREFS_CONFIG_KEY) || prefs.contains(HomeConfigPrefsBackend.PREFS_CONFIG_KEY_OLD);
Telemetry.addToHistogram("FENNEC_HOMEPANELS_CUSTOM", hasCustomHomepanels ? 1 : 0);
if (Versions.feature16Plus) {
Telemetry.addToHistogram("BROWSER_IS_ASSIST_DEFAULT", (isDefaultBrowser(Intent.ACTION_ASSIST) ? 1 : 0));
@ -1997,6 +2016,8 @@ public class BrowserApp extends GeckoApp
if (Tabs.getInstance().getDisplayCount() == 0)
return;
hideFirstrunPager(TelemetryContract.Method.TABSTRAY);
if (ensureTabsPanelExists()) {
// If we've just inflated the tabs panel, only show it once the current
// layout pass is done to avoid displayed temporary UI states during
@ -2228,10 +2249,6 @@ public class BrowserApp extends GeckoApp
* the app starts. In this case, we simply fallback to an empty URL.
*/
private void enterEditingMode() {
if (hideFirstrunPager()) {
Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, TelemetryContract.Method.ACTIONBAR, "firstrun-pane");
}
String url = "";
String telemetryMsg = "urlbar-empty";
@ -2260,6 +2277,8 @@ public class BrowserApp extends GeckoApp
* url is given, the empty String will be used instead.
*/
private void enterEditingMode(@NonNull String url) {
hideFirstrunPager(TelemetryContract.Method.ACTIONBAR);
if (mBrowserToolbar.isEditing() || mBrowserToolbar.isAnimating()) {
return;
}
@ -2652,11 +2671,20 @@ public class BrowserApp extends GeckoApp
mLayerView.setVisibility(View.INVISIBLE);
}
public boolean hideFirstrunPager() {
/**
* Hide the Onboarding pager on user action, and don't show any onFinish hints.
* @param method TelemetryContract method by which action was taken
* @return boolean of whether pager was visible
*/
private boolean hideFirstrunPager(TelemetryContract.Method method) {
if (!isFirstrunVisible()) {
return false;
}
Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, method, "firstrun-pane");
// Don't show any onFinish actions when hiding from this Activity.
mFirstrunAnimationContainer.registerOnFinishListener(null);
mFirstrunAnimationContainer.hide();
return true;
}
@ -3014,6 +3042,8 @@ public class BrowserApp extends GeckoApp
@Override
public void openOptionsMenu() {
hideFirstrunPager(TelemetryContract.Method.MENU);
// Disable menu access (for hardware buttons) when the software menu button is inaccessible.
// Note that the software button is always accessible on new tablet.
if (mBrowserToolbar.isEditing() && !HardwareUtils.isTablet()) {
@ -3657,7 +3687,7 @@ public class BrowserApp extends GeckoApp
mBrowserToolbar.cancelEdit();
// Hide firstrun-pane if the user is loading a URL from an external app.
hideFirstrunPager();
hideFirstrunPager(TelemetryContract.Method.NONE);
if (isBookmarkAction) {
// GeckoApp.ACTION_HOMESCREEN_SHORTCUT means we're opening a bookmark that
@ -3955,8 +3985,12 @@ public class BrowserApp extends GeckoApp
sharedPrefs.edit().putInt(TelemetryConstants.PREF_SEQ_COUNT, seq + 1).apply();
}
public static interface Refreshable {
public static interface TabStripInterface {
public void refresh();
void setOnTabChangedListener(OnTabAddedOrRemovedListener listener);
interface OnTabAddedOrRemovedListener {
void onTabChanged();
}
}
@Override

Просмотреть файл

@ -4,13 +4,11 @@
package org.mozilla.gecko;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.util.GeckoEventListener;
import org.mozilla.gecko.util.GeckoRequest;
import org.mozilla.gecko.util.NativeJSObject;
import org.mozilla.gecko.util.ThreadUtils;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
@ -23,7 +21,6 @@ import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.CheckedTextView;
import android.widget.LinearLayout;
import android.widget.TextView;
@ -31,12 +28,8 @@ public class FindInPageBar extends LinearLayout implements TextWatcher, View.OnC
private static final String LOGTAG = "GeckoFindInPageBar";
private static final String REQUEST_ID = "FindInPageBar";
// Will be removed by Bug 1113297.
private static final boolean MATCH_CASE_ENABLED = AppConstants.NIGHTLY_BUILD;
private final Context mContext;
private CustomEditText mFindText;
private CheckedTextView mMatchCase;
private TextView mStatusText;
private boolean mInflated;
@ -71,13 +64,6 @@ public class FindInPageBar extends LinearLayout implements TextWatcher, View.OnC
}
});
mMatchCase = (CheckedTextView) content.findViewById(R.id.find_matchcase);
if (MATCH_CASE_ENABLED) {
mMatchCase.setOnClickListener(this);
} else {
mMatchCase.setVisibility(View.GONE);
}
mStatusText = (TextView) content.findViewById(R.id.find_status);
mInflated = true;
@ -153,15 +139,6 @@ public class FindInPageBar extends LinearLayout implements TextWatcher, View.OnC
String extras = getResources().getResourceEntryName(viewId);
Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.BUTTON, extras);
if (viewId == R.id.find_matchcase) {
// Toggle matchcase state (color).
mMatchCase.toggle();
// Repeat the find after a matchcase change.
sendRequestToFinderHelper("FindInPage:Find", mFindText.getText().toString());
return;
}
if (viewId == R.id.find_prev) {
sendRequestToFinderHelper("FindInPage:Prev", mFindText.getText().toString());
getInputMethodManager(mFindText).hideSoftInputFromWindow(mFindText.getWindowToken(), 0);
@ -223,16 +200,7 @@ public class FindInPageBar extends LinearLayout implements TextWatcher, View.OnC
* Request find operation, and update matchCount results (current count and total).
*/
private void sendRequestToFinderHelper(final String request, final String searchString) {
final JSONObject json = new JSONObject();
try {
json.put("searchString", searchString);
json.put("matchCase", mMatchCase.isChecked());
} catch (JSONException e) {
Log.e(LOGTAG, "JSON error - Error creating JSONObject", e);
return;
}
GeckoAppShell.sendRequestToGecko(new GeckoRequest(request, json) {
GeckoAppShell.sendRequestToGecko(new GeckoRequest(request, searchString) {
@Override
public void onResponse(NativeJSObject nativeJSObject) {
final int total = nativeJSObject.optInt("total", 0);

Просмотреть файл

@ -17,10 +17,9 @@ import org.mozilla.gecko.annotation.RobocopTarget;
import org.mozilla.gecko.util.HardwareUtils;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.text.TextUtils;
import android.util.Log;
import org.mozilla.gecko.util.NetworkUtils;
/**
* Use network-based search suggestions.
@ -70,7 +69,7 @@ public class SuggestClient {
return suggestions;
}
if (!isNetworkConnected() && mCheckNetwork) {
if (!NetworkUtils.isConnected(mContext) && mCheckNetwork) {
Log.i(LOGTAG, "Not connected to network");
return suggestions;
}
@ -129,19 +128,6 @@ public class SuggestClient {
return suggestions;
}
private boolean isNetworkConnected() {
NetworkInfo networkInfo = getActiveNetworkInfo();
return networkInfo != null && networkInfo.isConnected();
}
private NetworkInfo getActiveNetworkInfo() {
ConnectivityManager connectivity = (ConnectivityManager) mContext
.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivity == null)
return null;
return connectivity.getActiveNetworkInfo();
}
private String convertStreamToString(java.io.InputStream is) {
try {
return new java.util.Scanner(is).useDelimiter("\\A").next();

Просмотреть файл

@ -188,6 +188,9 @@ public interface TelemetryContract {
// Action triggered from a suggestion provided to the user.
SUGGESTION("suggestion"),
// Action triggered from the Tabs tray.
TABSTRAY("tabstray"),
// Action triggered from a SuperToast.
// Note: Only used in JavaScript for now, but here for completeness.
TOAST("toast"),

Просмотреть файл

@ -39,7 +39,9 @@ import android.util.Log;
final class BrowserDatabaseHelper extends SQLiteOpenHelper {
private static final String LOGTAG = "GeckoBrowserDBHelper";
public static final int DATABASE_VERSION = 27; // Bug 1128675
// Replace the Bug number below with your Bug that is conducting a DB upgrade, as to force a merge conflict with any
// other patches that require a DB upgrade.
public static final int DATABASE_VERSION = 27; // Bug 826400
public static final String DATABASE_NAME = "browser.db";
final protected Context mContext;

Просмотреть файл

@ -7,6 +7,7 @@ package org.mozilla.gecko.home;
import java.util.EnumSet;
import android.util.Log;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
@ -21,6 +22,7 @@ import android.view.View;
import android.widget.AdapterView;
import android.widget.HeaderViewListAdapter;
import android.widget.ListAdapter;
import org.mozilla.gecko.util.NetworkUtils;
/**
* A ListView of bookmarks.
@ -93,6 +95,7 @@ public class BookmarksListView extends HomeListView
final String url = cursor.getString(cursor.getColumnIndexOrThrow(Bookmarks.URL));
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.LIST_ITEM, "bookmarks");
Telemetry.addToHistogram("FENNEC_LOAD_SAVED_PAGE", NetworkUtils.isConnected(getContext()) ? 2 : 3);
// This item is a TwoLinePageRow, so we allow switch-to-tab.
getOnUrlOpenListener().onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));

Просмотреть файл

@ -33,17 +33,17 @@ import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
class HomeConfigPrefsBackend implements HomeConfigBackend {
public class HomeConfigPrefsBackend implements HomeConfigBackend {
private static final String LOGTAG = "GeckoHomeConfigBackend";
// Increment this to trigger a migration.
private static final int VERSION = 3;
// This key was originally used to store only an array of panel configs.
private static final String PREFS_CONFIG_KEY_OLD = "home_panels";
public static final String PREFS_CONFIG_KEY_OLD = "home_panels";
// This key is now used to store a version number with the array of panel configs.
private static final String PREFS_CONFIG_KEY = "home_panels_with_version";
public static final String PREFS_CONFIG_KEY = "home_panels_with_version";
// Keys used with JSON object stored in prefs.
private static final String JSON_KEY_PANELS = "panels";

Просмотреть файл

@ -7,6 +7,7 @@ package org.mozilla.gecko.home;
import java.util.EnumSet;
import android.util.Log;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.R;
import org.mozilla.gecko.ReaderModeUtils;
@ -34,6 +35,7 @@ import android.view.ViewStub;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.TextView;
import org.mozilla.gecko.util.NetworkUtils;
/**
* Fragment that displays reading list contents in a ListView.
@ -92,6 +94,7 @@ public class ReadingListPanel extends HomeFragment {
url = ReaderModeUtils.getAboutReaderForUrl(url);
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.LIST_ITEM, "reading_list");
Telemetry.addToHistogram("FENNEC_LOAD_SAVED_PAGE", NetworkUtils.isConnected(context) ? 0 : 1);
// This item is a TwoLinePageRow, so we allow switch-to-tab.
mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));

Просмотреть файл

@ -15,7 +15,8 @@ import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewTreeObserver;
import org.mozilla.gecko.BrowserApp.Refreshable;
import org.mozilla.gecko.BrowserApp;
import org.mozilla.gecko.BrowserApp.TabStripInterface;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs;
@ -24,13 +25,14 @@ import org.mozilla.gecko.widget.themed.ThemedImageButton;
import org.mozilla.gecko.widget.themed.ThemedLinearLayout;
public class TabStrip extends ThemedLinearLayout
implements Refreshable {
implements TabStripInterface {
private static final String LOGTAG = "GeckoTabStrip";
private final TabStripView tabStripView;
private final ThemedImageButton addTabButton;
private final TabsListener tabsListener;
private OnTabAddedOrRemovedListener tabChangedListener;
public TabStrip(Context context) {
this(context, null);
@ -100,6 +102,10 @@ public class TabStrip extends ThemedLinearLayout
addTabButton.setPrivateMode(isPrivate);
}
public void setOnTabChangedListener(OnTabAddedOrRemovedListener listener) {
tabChangedListener = listener;
}
private class TabsListener implements Tabs.OnTabsChangedListener {
@Override
public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) {
@ -110,10 +116,16 @@ public class TabStrip extends ThemedLinearLayout
case ADDED:
tabStripView.addTab(tab);
if (tabChangedListener != null) {
tabChangedListener.onTabChanged();
}
break;
case CLOSED:
tabStripView.removeTab(tab);
if (tabChangedListener != null) {
tabChangedListener.onTabChanged();
}
break;
case SELECTED:

Просмотреть файл

@ -0,0 +1,28 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.util;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
public class NetworkUtils {
/**
* Indicates whether network connectivity exists and it is possible to establish connections and pass data.
*/
public static boolean isConnected(Context context) {
final ConnectivityManager connectivity = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivity == null) {
return false;
}
final NetworkInfo networkInfo = connectivity.getActiveNetworkInfo();
if (networkInfo == null) {
return false;
}
return networkInfo.isConnected();
}
}

Просмотреть файл

@ -718,11 +718,6 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!-- Localization note: Used when the sync has not happend yet, showed in place of a date -->
<!ENTITY remote_tabs_never_synced "Last synced: never">
<!-- Find-In-Page strings -->
<!-- LOCALIZATION NOTE (find_matchcase): This is meant to appear as an icon that changes color
if match-case is activated. i.e. No more than two letters, one uppercase, one lowercase. -->
<!ENTITY find_matchcase "Aa">
<!ENTITY intent_uri_cannot_open "Cannot open link">
<!-- LOCALIZATION NOTE (intent_uri_private_browsing_prompt): This string will
appear in an alert when a user, who is currently in private browsing,

Просмотреть файл

@ -116,6 +116,7 @@ gujar.sources += ['java/org/mozilla/gecko/' + x for x in [
'util/NativeEventListener.java',
'util/NativeJSContainer.java',
'util/NativeJSObject.java',
'util/NetworkUtils.java',
'util/NonEvictingLruCache.java',
'util/PrefUtils.java',
'util/ProxySelector.java',

Просмотреть файл

@ -1,14 +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/. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true"
android:color="@color/tabs_tray_icon_grey"/>
<item android:state_checked="false"
android:color="@color/find_matchcase_off"/>
</selector>

Просмотреть файл

@ -28,14 +28,6 @@
android:textColor="@color/tabs_tray_icon_grey"
android:visibility="gone"/>
<CheckedTextView android:id="@+id/find_matchcase"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/find_in_page_matchcase_padding"
android:checked="false"
android:text="@string/find_matchcase"
android:textColor="@drawable/find_matchcase_selector"/>
<ImageButton android:id="@+id/find_prev"
style="@style/FindBar.ImageButton"
android:contentDescription="@string/find_prev"

Просмотреть файл

@ -141,9 +141,6 @@
<color name="tab_history_favicon_background">#FFFFFF</color>
<color name="tab_history_border_color">#DADADF</color>
<!-- Colour used for Find-In-Page dialog -->
<color name="find_matchcase_off">#D02626</color>
<!-- Canvas delegate paint color -->
<color name="canvas_delegate_paint">#FFFF0000</color>

Просмотреть файл

@ -206,7 +206,6 @@
<dimen name="find_in_page_text_padding_left">10dip</dimen>
<dimen name="find_in_page_text_padding_right">10dip</dimen>
<dimen name="find_in_page_status_margin_right">10dip</dimen>
<dimen name="find_in_page_matchcase_padding">10dip</dimen>
<dimen name="find_in_page_control_margin_top">2dip</dimen>
<!-- The share icon asset has no padding while the other action bar items do

Просмотреть файл

@ -116,7 +116,6 @@
<string name="save_as_pdf">&save_as_pdf;</string>
<string name="print">&print;</string>
<string name="find_in_page">&find_in_page;</string>
<string name="find_matchcase">&find_matchcase;</string>
<string name="desktop_mode">&desktop_mode;</string>
<string name="page">&page;</string>
<string name="tools">&tools;</string>

Просмотреть файл

@ -40,18 +40,18 @@ var FindHelper = {
}
Messaging.addListener((data) => {
this.doFind(data.searchString, data.matchCase);
return this._getMatchesCountResult(data.searchString);
this.doFind(data);
return this._getMatchesCountResult(data);
}, "FindInPage:Find");
Messaging.addListener((data) => {
this.findAgain(data.searchString, false, data.matchCase);
return this._getMatchesCountResult(data.searchString);
this.findAgain(data, false);
return this._getMatchesCountResult(data);
}, "FindInPage:Next");
Messaging.addListener((data) => {
this.findAgain(data.searchString, true, data.matchCase);
return this._getMatchesCountResult(data.searchString);
this.findAgain(data, true);
return this._getMatchesCountResult(data);
}, "FindInPage:Prev");
},
@ -116,25 +116,23 @@ var FindHelper = {
this._result.limit = this._limit;
},
doFind: function(searchString, matchCase) {
doFind: function(searchString) {
if (!this._finder) {
this._init();
}
this._finder.caseSensitive = matchCase;
this._finder.fastFind(searchString, false);
},
findAgain: function(searchString, findBackwards, matchCase) {
findAgain: function(searchString, findBackwards) {
// This always happens if the user taps next/previous after re-opening the
// search bar, and not only forces _init() but also an initial fastFind(STRING)
// before any findAgain(DIRECTION).
if (!this._finder) {
this.doFind(searchString, matchCase);
this.doFind(searchString);
return;
}
this._finder.caseSensitive = matchCase;
this._finder.findAgain(findBackwards, false, false);
},

Просмотреть файл

@ -40,11 +40,6 @@ function copyStringShowSnackbar(string, notifyString) {
// Delay filtering while typing in MS
const FILTER_DELAY = 500;
/* Constants for usage telemetry */
const LOGINS_LIST_VIEWED = 0;
const LOGIN_VIEWED = 1;
const LOGIN_EDITED = 2;
const LOGIN_PW_TOGGLED = 3;
var Logins = {
_logins: [],
@ -71,9 +66,7 @@ var Logins = {
let getAllLogins = () => {
let logins = [];
try {
TelemetryStopwatch.start("PWMGR_ABOUT_LOGINS_GET_ALL_LOGINS_MS");
logins = Services.logins.getAllLogins();
TelemetryStopwatch.finish("PWMGR_ABOUT_LOGINS_GET_ALL_LOGINS_MS");
} catch(e) {
// It's likely that the Master Password was not entered; give
// a hint to the next person.
@ -226,7 +219,6 @@ var Logins = {
},
_showList: function () {
Services.telemetry.getHistogramById("PWMGR_ABOUT_LOGINS_USAGE").add(LOGINS_LIST_VIEWED);
let loginsListPage = document.getElementById("logins-list-page");
loginsListPage.classList.remove("hidden");
@ -249,7 +241,6 @@ var Logins = {
}
},
_showEditLoginDialog: function (login) {
Services.telemetry.getHistogramById("PWMGR_ABOUT_LOGINS_USAGE").add(LOGIN_VIEWED);
let listPage = document.getElementById("logins-list-page");
listPage.classList.add("hidden");
@ -289,7 +280,6 @@ var Logins = {
},
_onSaveEditLogin: function() {
Services.telemetry.getHistogramById("PWMGR_ABOUT_LOGINS_USAGE").add(LOGIN_EDITED);
let newUsername = document.getElementById("username").value;
let newPassword = document.getElementById("password").value;
let newDomain = document.getElementById("hostname").value;
@ -328,7 +318,6 @@ var Logins = {
},
_onPasswordBtn: function () {
Services.telemetry.getHistogramById("PWMGR_ABOUT_LOGINS_USAGE").add(LOGIN_PW_TOGGLED);
this._updatePasswordBtn(this._isPasswordBtnInHideMode());
},

Просмотреть файл

@ -27,7 +27,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.UUID;
import java.util.zip.CRC32;
import org.json.JSONException;
@ -78,6 +77,8 @@ public class SwitchBoard {
private static final String IS_EXPERIMENT_ACTIVE = "isActive";
private static final String EXPERIMENT_VALUES = "values";
private static String uuidExtra = null;
/**
* Basic initialization with one server.
@ -93,6 +94,9 @@ public class SwitchBoard {
DEBUG = isDebug;
}
public static void setUUIDFromExtra(String uuid) {
uuidExtra = uuid;
}
/**
* Advanced initialization that supports a production and staging environment without changing the server URLs manually.
* SwitchBoard will connect to the staging environment in debug mode. This makes it very simple to test new experiements
@ -435,8 +439,11 @@ public class SwitchBoard {
*/
private static int getUserBucket(Context c) {
//get uuid
String uuid = uuidExtra;
if (uuid == null) {
DeviceUuidFactory df = new DeviceUuidFactory(c);
String uuid = df.getDeviceUuid().toString();
uuid = df.getDeviceUuid().toString();
}
CRC32 crc = new CRC32();
crc.update(uuid.getBytes());

Просмотреть файл

@ -920,9 +920,8 @@ pref("devtools.commands.dir", "");
// Allows setting the performance marks for which telemetry metrics will be recorded.
pref("devtools.telemetry.supported_performance_marks", "contentInteractive,navigationInteractive,navigationLoaded,visuallyLoaded,fullyLoaded,mediaEnumerated,scanEnd");
// Deprecation warnings after DevTools file migration. Bug 1204127 tracks
// enabling this.
pref("devtools.migration.warnings", false);
// Deprecation warnings after DevTools file migration.
pref("devtools.migration.warnings", true);
// view source
pref("view_source.syntax_highlight", true);
@ -5196,4 +5195,3 @@ pref("dom.input.fallbackUploadDir", "");
// Turn rewriting of youtube embeds on/off
pref("plugins.rewrite_youtube_embeds", true);

Просмотреть файл

@ -37,6 +37,7 @@ const SCRIPTS = [
"browser/base/content/browser-gestureSupport.js",
"browser/base/content/browser-places.js",
"browser/base/content/browser-plugins.js",
"browser/base/content/browser-refreshblocker.js",
"browser/base/content/browser-safebrowsing.js",
"browser/base/content/browser-sidebar.js",
"browser/base/content/browser-social.js",

Просмотреть файл

@ -794,4 +794,36 @@ this.BrowserTestUtils = {
}, interval);
});
},
/**
* Waits for a <xul:notification> with a particular value to appear
* for the <xul:notificationbox> of the passed in browser.
*
* @param tabbrowser (<xul:tabbrowser>)
* The gBrowser that hosts the browser that should show
* the notification. For most tests, this will probably be
* gBrowser.
* @param browser (<xul:browser>)
* The browser that should be showing the notification.
* @param notificationValue (string)
* The "value" of the notification, which is often used as
* a unique identifier. Example: "plugin-crashed".
* @return Promise
* Resolves to the <xul:notification> that is being shown.
*/
waitForNotificationBar(tabbrowser, browser, notificationValue) {
let notificationBox = tabbrowser.getNotificationBox(browser);
return new Promise((resolve) => {
let check = (event) => {
return event.target.value == notificationValue;
};
BrowserTestUtils.waitForEvent(notificationBox, "AlertActive",
false, check).then((event) => {
// The originalTarget of the AlertActive on a notificationbox
// will be the notification itself.
resolve(event.originalTarget);
});
});
},
};

Просмотреть файл

@ -110,7 +110,7 @@
"no-negated-in-lhs": 2,
// Nested ternary statements are confusing
// "no-nested-ternary": 2,
"no-nested-ternary": 2,
// Use {} instead of new Object()
// "no-new-object": 2,

Просмотреть файл

@ -1193,9 +1193,7 @@ TreeNode.compareAmounts = function(aA, aB) {
};
TreeNode.compareUnsafeNames = function(aA, aB) {
return aA._unsafeName < aB._unsafeName ? -1 :
aA._unsafeName > aB._unsafeName ? 1 :
0;
return aA._unsafeName.localeCompare(aB._unsafeName);
};

Просмотреть файл

@ -1593,12 +1593,13 @@ module.exports = function (chai, _) {
}
}
var actuallyGot = ''
, expectedThrown = name !== null
? name
: desiredError
? '#{exp}' //_.inspect(desiredError)
: 'an error';
var actuallyGot = '';
var expectedThrown = 'an error';
if (name !== null) {
expectedThrown = name;
} else if (desiredError) {
expectedThrown = '#{exp}'; //_.inspect(desiredError)
}
if (thrown) {
actuallyGot = ' but #{act} was thrown'

Просмотреть файл

@ -415,7 +415,13 @@ var JsDiff = (function() {
var ret = [], change;
for ( var i = 0; i < changes.length; i++) {
change = changes[i];
ret.push([(change.added ? 1 : change.removed ? -1 : 0), change.value]);
var order = 0;
if (change.added) {
order = 1;
} else if (change.removed) {
order = -1;
}
ret.push([order, change.value]);
}
return ret;
}
@ -2097,12 +2103,13 @@ var color = exports.color = function(type, str) {
*/
exports.window = {
width: isatty
? process.stdout.getWindowSize
? process.stdout.getWindowSize(1)[0]
: tty.getWindowSize()[1]
: 75
width: 75
};
if (isatty) {
exports.window.width = process.stdout.getWindowSize
? process.stdout.getWindowSize(1)[0]
: tty.getWindowSize()[1];
}
/**
* Expose some basic cursor interactions
@ -2240,11 +2247,13 @@ function Base(runner) {
stats.passes = stats.passes || 0;
var medium = test.slow() / 2;
test.speed = test.duration > test.slow()
? 'slow'
: test.duration > medium
? 'medium'
: 'fast';
if (test.duration > test.slow()) {
test.speed = 'slow';
} else if (test.duration > medium) {
test.speed = 'medium';
} else {
test.speed = 'fast';
}
stats.passes++;
});

Просмотреть файл

@ -581,11 +581,11 @@ var REGEXP_PRECEDER_PATTERN = '(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&
var wrapper = undefined;
for (var c = element.firstChild; c; c = c.nextSibling) {
var type = c.nodeType;
wrapper = (type === 1) // Element Node
? (wrapper ? element : c)
: (type === 3) // Text Node
? (notWs.test(c.nodeValue) ? element : wrapper)
: wrapper;
if (type === 1) {
wrapper = wrapper ? element : c;
} else if (type === 3) {
wrapper = notWs.test(c.nodeValue) ? element : wrapper;
}
}
return wrapper === element ? undefined : wrapper;
}
@ -1411,9 +1411,11 @@ var REGEXP_PRECEDER_PATTERN = '(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&
// Look for a class like linenums or linenums:<n> where <n> is the
// 1-indexed number of the first line.
var lineNums = cs.className.match(/\blinenums\b(?::(\d+))?/);
lineNums = lineNums
? lineNums[1] && lineNums[1].length ? +lineNums[1] : true
: false;
if (lineNums) {
lineNums = lineNums[1] && lineNums[1].length ? +lineNums[1] : true;
} else {
lineNums = false;
}
if (lineNums) { numberLines(cs, lineNums); }
// do the pretty printing

Просмотреть файл

@ -16,7 +16,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "console",
"resource://gre/modules/devtools/Console.jsm");
"resource://gre/modules/Console.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PerformanceWatcher",
"resource://gre/modules/PerformanceWatcher.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "Telemetry",

Просмотреть файл

@ -256,8 +256,11 @@ BookmarkImporter.prototype = {
} else {
// Ensure tag folder gets processed last
nodes[0].children.sort(function sortRoots(aNode, bNode) {
return (aNode.root && aNode.root == "tagsFolder") ? 1 :
(bNode.root && bNode.root == "tagsFolder") ? -1 : 0;
if (aNode.root && aNode.root == "tagsFolder")
return 1;
if (bNode.root && bNode.root == "tagsFolder")
return -1;
return 0;
});
let batch = {

Просмотреть файл

@ -977,8 +977,9 @@ function reorderChildren(parent, orderedChildrenGuids) {
let i = orderedChildrenGuids.indexOf(a.guid);
let j = orderedChildrenGuids.indexOf(b.guid);
// This works provided fetchBookmarksByParent returns sorted children.
return (i == -1 && j == -1) ? 0 :
(i != -1 && j != -1 && i < j) || (i != -1 && j == -1) ? -1 : 1;
if (i == -1 && j == -1)
return 0;
return (i != -1 && j != -1 && i < j) || (i != -1 && j == -1) ? -1 : 1;
});
// Update the bookmarks position now. If any unknown guid have been

Просмотреть файл

@ -173,7 +173,7 @@ this.PlacesBackups = {
this._entries.sort((a, b) => {
let aDate = this.getDateForFile(a);
let bDate = this.getDateForFile(b);
return aDate < bDate ? 1 : aDate > bDate ? -1 : 0;
return bDate - aDate;
});
return this._entries;
},
@ -215,7 +215,7 @@ this.PlacesBackups = {
this._backupFiles.sort((a, b) => {
let aDate = this.getDateForFile(a);
let bDate = this.getDateForFile(b);
return aDate < bDate ? 1 : aDate > bDate ? -1 : 0;
return bDate - aDate;
});
return this._backupFiles;

Просмотреть файл

@ -1751,16 +1751,21 @@ Search.prototype = {
let typed = Prefs.autofillTyped || this.hasBehavior("typed");
let bookmarked = this.hasBehavior("bookmark") && !this.hasBehavior("history");
return [
bookmarked ? typed ? SQL_BOOKMARKED_TYPED_HOST_QUERY
: SQL_BOOKMARKED_HOST_QUERY
: typed ? SQL_TYPED_HOST_QUERY
: SQL_HOST_QUERY,
{
let query = [];
if (bookmarked) {
query.push(typed ? SQL_BOOKMARKED_TYPED_HOST_QUERY
: SQL_BOOKMARKED_HOST_QUERY);
} else {
query.push(typed ? SQL_TYPED_HOST_QUERY
: SQL_HOST_QUERY);
}
query.push({
query_type: QUERYTYPE_AUTOFILL_HOST,
searchString: this._searchString.toLowerCase()
}
];
});
return query;
},
/**
@ -1780,17 +1785,22 @@ Search.prototype = {
let typed = Prefs.autofillTyped || this.hasBehavior("typed");
let bookmarked = this.hasBehavior("bookmark") && !this.hasBehavior("history");
return [
bookmarked ? typed ? SQL_BOOKMARKED_TYPED_URL_QUERY
: SQL_BOOKMARKED_URL_QUERY
: typed ? SQL_TYPED_URL_QUERY
: SQL_URL_QUERY,
{
let query = [];
if (bookmarked) {
query.push(typed ? SQL_BOOKMARKED_TYPED_URL_QUERY
: SQL_BOOKMARKED_URL_QUERY);
} else {
query.push(typed ? SQL_TYPED_URL_QUERY
: SQL_URL_QUERY);
}
query.push({
query_type: QUERYTYPE_AUTOFILL_URL,
searchString: this._autofillUrlSearchString,
revHost
}
];
});
return query;
},
/**

Просмотреть файл

@ -534,9 +534,12 @@ function check_JSON_backup(aIsAutomaticBackup) {
*/
function frecencyForUrl(aURI)
{
let url = aURI instanceof Ci.nsIURI ? aURI.spec
: aURI instanceof URL ? aURI.href
: aURI;
let url = aURI;
if (aURI instanceof Ci.nsIURI) {
url = aURI.spec;
} else if (aURI instanceof URL) {
url = aURI.href;
}
let stmt = DBConn().createStatement(
"SELECT frecency FROM moz_places WHERE url = ?1"
);

Просмотреть файл

@ -72,8 +72,12 @@ var Readability = function(uri, doc, options) {
return rv + '("' + e.textContent + '")';
}
var classDesc = e.className && ("." + e.className.replace(/ /g, "."));
var elDesc = e.id ? "(#" + e.id + classDesc + ")" :
(classDesc ? "(" + classDesc + ")" : "");
var elDesc = "";
if (e.id) {
elDesc = "(#" + e.id + classDesc + ")";
} else if (classDesc) {
elDesc = "(" + classDesc + ")";
}
return rv + elDesc;
}
this.log = function () {
@ -743,7 +747,7 @@ Readability.prototype = {
// - parent: 1 (no division)
// - grandparent: 2
// - great grandparent+: ancestor level * 3
var scoreDivider = level === 0 ? 1 : level === 1 ? 2 : level * 3;
var scoreDivider = level < 2 ? level + 1 : level * 3;
ancestor.readability.contentScore += contentScore / scoreDivider;
});
});

Просмотреть файл

@ -5156,6 +5156,13 @@
"description": "Time for the home screen Top Sites query to return with no filter set (ms)",
"cpp_guard": "ANDROID"
},
"FENNEC_HOMEPANELS_CUSTOM": {
"expires_in_version": "54",
"kind": "boolean",
"bug_numbers": [1245368],
"description": "Whether the user has customized their homepanels",
"cpp_guard": "ANDROID"
},
"FENNEC_WAS_KILLED": {
"expires_in_version": "never",
"kind": "flag",
@ -8719,19 +8726,6 @@
"n_buckets": 20,
"description": "Sanitize: Time it takes to sanitize the open windows list (ms)"
},
"PWMGR_ABOUT_LOGINS_GET_ALL_LOGINS_MS": {
"expires_in_version": "55",
"kind": "exponential",
"high": 60000,
"n_buckets": 30,
"description": "How long getAllLogins() on about:logins takes for mobile users"
},
"PWMGR_ABOUT_LOGINS_USAGE": {
"expires_in_version": "55",
"kind": "enumerated",
"n_values": 12,
"description": "Usage of about:logins 0= list of logins viewed, 1=a login's specifics page was viewed, 2=user edited login credentials 3=user toggled the show/hide button"
},
"PWMGR_BLOCKLIST_NUM_SITES": {
"expires_in_version": "never",
"kind": "exponential",
@ -9031,11 +9025,19 @@
},
"FENNEC_READER_VIEW_BUTTON" : {
"expires_in_version": "50",
"alert_emails": ["mleibovic@mozilla.com"],
"alert_emails": ["mobile-frontend@mozilla.com"],
"kind": "enumerated",
"n_values": 10,
"description": "Bug 1219240: Measures user interaction with the reader view button (0=Button hidden, 1=Button shown, 2=Tap to enter reader view, 3=Tap to exit reader view, 4=Long tap)"
},
"FENNEC_LOAD_SAVED_PAGE": {
"expires_in_version": "50",
"alert_emails": ["mobile-frontend@mozilla.com"],
"kind": "enumerated",
"n_values": 10,
"description": "How often users load saved items when online/offline (0=RL online, 1=RL offline, 2=BM online, 3=BM offline)",
"bug_numbers": [1243387]
},
"PERMISSIONS_SQL_CORRUPTED": {
"expires_in_version": "never",
"kind": "count",

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше