зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound
This commit is contained in:
Коммит
302e99ae6a
|
@ -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,8 +1205,12 @@ function promiseCrashReport(expectedExtra) {
|
|||
let key = enumerator.getNext().QueryInterface(Ci.nsIProperty).name;
|
||||
let value = extra.getPropertyAsAString(key);
|
||||
if (key in expectedExtra) {
|
||||
is(value, expectedExtra[key],
|
||||
`Crash report had the right extra value for ${key}`);
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
#filter substitution
|
||||
# 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/.
|
||||
|
||||
# These are used for the big if statement, as the preprocessor can't handle
|
||||
# dashes.
|
||||
#define bn_BD bn-BD
|
||||
#define bn_IN bn-IN
|
||||
#define en_GB en-GB
|
||||
#define en_US en-US
|
||||
#define es_CL es-CL
|
||||
#define es_ES es-ES
|
||||
#define es_MX es-MX
|
||||
#define fy_NL fy-NL
|
||||
#define gu_IN gu-IN
|
||||
#define hi_IN hi-IN
|
||||
#define hy_AM hy-AM
|
||||
#define nb_NO nb-NO
|
||||
#define ne_NP ne-NP
|
||||
#define pa_IN pa-IN
|
||||
#define pt_BR pt-BR
|
||||
#define pt_PT pt-PT
|
||||
#define sv_SE sv-SE
|
||||
#define zh_CN zh-CN
|
||||
#define zh_TW zh-TW
|
||||
|
||||
[features/loop@mozilla.org] @AB_CD@.jar:
|
||||
% locale loop @AB_CD@ %locale/@AB_CD@/
|
||||
# For locales we support, include the file from the locale's directory in the
|
||||
# source tree.
|
||||
# For other locales (and en-US) fallback to the en-US directory.
|
||||
#if AB_CD == af || AB_CD == ar || AB_CD == as || AB_CD == ast || AB_CD == az || AB_CD == be || AB_CD == bg || AB_CD == bn_BD || AB_CD == bn_IN || AB_CD == bs || AB_CD == ca || AB_CD == cs || AB_CD == cy || AB_CD == da || AB_CD == de || AB_CD == dsb || AB_CD == el || AB_CD == en_GB || AB_CD == en_US || AB_CD == eo || AB_CD == es_CL || AB_CD == es_ES || AB_CD == es_MX || AB_CD == et || AB_CD == eu || AB_CD == fa || AB_CD == ff || AB_CD == fi || AB_CD == fr || AB_CD == fy || AB_CD == fy_NL || AB_CD == ga || AB_CD == gd || AB_CD == gl || AB_CD == gu_IN || AB_CD == he || AB_CD == hi_IN || AB_CD == hr || AB_CD == hsb || AB_CD == ht || AB_CD == hu || AB_CD == hy_AM || AB_CD == id || AB_CD == it || AB_CD == ja || AB_CD == kk || AB_CD == km || AB_CD == kn || AB_CD == ko || AB_CD == ku || AB_CD == lij || AB_CD == lt || AB_CD == lv || AB_CD == mk || AB_CD == ml || AB_CD == mn || AB_CD == ms || AB_CD == my || AB_CD == nb_NO || AB_CD == ne_NP || AB_CD == nl || AB_CD == or || AB_CD == pa || AB_CD == pa_IN || AB_CD == pl || AB_CD == pt || AB_CD == pt_BR || AB_CD == pt_PT || AB_CD == rm || AB_CD == ro || AB_CD == ru || AB_CD == si || AB_CD == sk || AB_CD == sl || AB_CD == son || AB_CD == sq || AB_CD == sr || AB_CD == sv_SE || AB_CD == ta || AB_CD == te || AB_CD == th || AB_CD == tr || AB_CD == uk || AB_CD == ur || AB_CD == vi || AB_CD == xh || AB_CD == zh_CN || AB_CD == zh_TW || AB_CD == zu
|
||||
locale/@AB_CD@/ (@AB_CD@/*)
|
||||
#else
|
||||
locale/@AB_CD@/ (en-US/*)
|
||||
#endif
|
|
@ -0,0 +1,7 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
|
@ -3,32 +3,9 @@
|
|||
# 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/.
|
||||
|
||||
# These are used for the big if statement, as the preprocessor can't handle
|
||||
# dashes.
|
||||
#define bn_BD bn-BD
|
||||
#define bn_IN bn-IN
|
||||
#define en_GB en-GB
|
||||
#define en_US en-US
|
||||
#define es_CL es-CL
|
||||
#define es_ES es-ES
|
||||
#define es_MX es-MX
|
||||
#define fy_NL fy-NL
|
||||
#define gu_IN gu-IN
|
||||
#define hi_IN hi-IN
|
||||
#define hy_AM hy-AM
|
||||
#define nb_NO nb-NO
|
||||
#define ne_NP ne-NP
|
||||
#define pa_IN pa-IN
|
||||
#define pt_BR pt-BR
|
||||
#define pt_PT pt-PT
|
||||
#define sv_SE sv-SE
|
||||
#define zh_CN zh-CN
|
||||
#define zh_TW zh-TW
|
||||
|
||||
[features/loop@mozilla.org] chrome.jar:
|
||||
% content loop %content/ contentaccessible=yes
|
||||
% content loop-locale-fallback %content/locale-fallback/en-US/
|
||||
% locale loop @AB_CD@ %locale/@AB_CD@/
|
||||
% skin loop classic/1.0 %skin/linux/ os=Linux
|
||||
% skin loop classic/1.0 %skin/osx/ os=Darwin
|
||||
% skin loop classic/1.0 %skin/windows/ os=WINNT
|
||||
|
@ -54,14 +31,6 @@
|
|||
% override chrome://loop/skin/toolbar@2x.png chrome://loop/skin/toolbar-win8@2x.png os=WINNT osversion=6.3
|
||||
# Due to the way Loop's L10n works, we include a fallback to en-US.
|
||||
content/locale-fallback/en-US/ (chrome/locale/en-US/*)
|
||||
# For locales we support, include the file from the locale's directory in the
|
||||
# source tree.
|
||||
# For other locales (and en-US) fallback to the en-US directory.
|
||||
#if AB_CD == af || AB_CD == ar || AB_CD == as || AB_CD == ast || AB_CD == az || AB_CD == be || AB_CD == bg || AB_CD == bn_BD || AB_CD == bn_IN || AB_CD == bs || AB_CD == ca || AB_CD == cs || AB_CD == cy || AB_CD == da || AB_CD == de || AB_CD == dsb || AB_CD == el || AB_CD == en_GB || AB_CD == en_US || AB_CD == eo || AB_CD == es_CL || AB_CD == es_ES || AB_CD == es_MX || AB_CD == et || AB_CD == eu || AB_CD == fa || AB_CD == ff || AB_CD == fi || AB_CD == fr || AB_CD == fy || AB_CD == fy_NL || AB_CD == ga || AB_CD == gd || AB_CD == gl || AB_CD == gu_IN || AB_CD == he || AB_CD == hi_IN || AB_CD == hr || AB_CD == hsb || AB_CD == ht || AB_CD == hu || AB_CD == hy_AM || AB_CD == id || AB_CD == it || AB_CD == ja || AB_CD == kk || AB_CD == km || AB_CD == kn || AB_CD == ko || AB_CD == ku || AB_CD == lij || AB_CD == lt || AB_CD == lv || AB_CD == mk || AB_CD == ml || AB_CD == mn || AB_CD == ms || AB_CD == my || AB_CD == nb_NO || AB_CD == ne_NP || AB_CD == nl || AB_CD == or || AB_CD == pa || AB_CD == pa_IN || AB_CD == pl || AB_CD == pt || AB_CD == pt_BR || AB_CD == pt_PT || AB_CD == rm || AB_CD == ro || AB_CD == ru || AB_CD == si || AB_CD == sk || AB_CD == sl || AB_CD == son || AB_CD == sq || AB_CD == sr || AB_CD == sv_SE || AB_CD == ta || AB_CD == te || AB_CD == th || AB_CD == tr || AB_CD == uk || AB_CD == ur || AB_CD == vi || AB_CD == xh || AB_CD == zh_CN || AB_CD == zh_TW || AB_CD == zu
|
||||
locale/@AB_CD@/ (chrome/locale/@AB_CD@/*)
|
||||
#else
|
||||
locale/@AB_CD@/ (chrome/locale/en-US/*)
|
||||
#endif
|
||||
skin/ (chrome/skin/*)
|
||||
content/modules/ (chrome/content/modules/*)
|
||||
# We don't package the test/ directory for panels, so do these separately.
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
# 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/.
|
||||
|
||||
DIRS += ['chrome/locale']
|
||||
|
||||
FINAL_TARGET_FILES.features['loop@mozilla.org'] += [
|
||||
'bootstrap.js'
|
||||
]
|
||||
|
|
|
@ -15,7 +15,7 @@ import time
|
|||
def monitor():
|
||||
while 1:
|
||||
time.sleep(1.)
|
||||
_ = sys._current_frames()
|
||||
sys._current_frames()
|
||||
|
||||
|
||||
def start_monitoring():
|
||||
|
@ -23,5 +23,3 @@ def start_monitoring():
|
|||
thread.daemon = True
|
||||
thread.start()
|
||||
return thread
|
||||
|
||||
start_monitoring()
|
||||
|
|
|
@ -14,7 +14,10 @@ import sys
|
|||
import os
|
||||
sys.path.append(os.path.dirname(__file__))
|
||||
import hanging_threads
|
||||
from config import *
|
||||
from config import CONTENT_SERVER_PORT, LOOP_SERVER_PORT, LOOP_SERVER_URL, \
|
||||
FIREFOX_PREFERENCES
|
||||
|
||||
hanging_threads.start_monitoring()
|
||||
|
||||
CONTENT_SERVER_COMMAND = ["make", "runserver"]
|
||||
CONTENT_SERVER_ENV = os.environ.copy()
|
||||
|
@ -24,7 +27,7 @@ CONTENT_SERVER_ENV.update({"PORT": str(CONTENT_SERVER_PORT),
|
|||
"LOOP_SERVER_URL": LOOP_SERVER_URL})
|
||||
|
||||
ROOMS_WEB_APP_URL = "http://localhost:" + str(CONTENT_SERVER_PORT) + \
|
||||
"/content/{token}"
|
||||
"/content/{token}"
|
||||
|
||||
LOOP_SERVER_COMMAND = ["make", "runserver"]
|
||||
LOOP_SERVER_ENV = os.environ.copy()
|
||||
|
@ -62,8 +65,8 @@ class LoopTestServers:
|
|||
def start_content_server():
|
||||
content_server_location = os.environ.get('STANDALONE_SERVER')
|
||||
if content_server_location is None:
|
||||
content_server_location = os.path.join(os.path.dirname(__file__),
|
||||
"../../standalone")
|
||||
content_server_location = os.path.join(os.path.dirname(__file__),
|
||||
"../../standalone")
|
||||
os.chdir(content_server_location)
|
||||
|
||||
p = processhandler.ProcessHandler(CONTENT_SERVER_COMMAND,
|
||||
|
@ -84,4 +87,3 @@ class LoopTestServers:
|
|||
self.content_server.kill()
|
||||
if hasattr(self, "loop_server"):
|
||||
self.loop_server.kill()
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ sys.path.insert(1, os.path.dirname(os.path.abspath(__file__)))
|
|||
import pyperclip
|
||||
|
||||
from serversetup import LoopTestServers
|
||||
from config import *
|
||||
from config import FIREFOX_PREFERENCES
|
||||
|
||||
|
||||
class Test1BrowserCall(MarionetteTestCase):
|
||||
|
@ -81,7 +81,7 @@ class Test1BrowserCall(MarionetteTestCase):
|
|||
# for Marionette bug 1094246 to be fixed.
|
||||
chatbox = self.wait_for_element_exists(By.TAG_NAME, 'chatbox')
|
||||
script = ("return document.getAnonymousElementByAttribute("
|
||||
"arguments[0], 'class', 'chat-frame');")
|
||||
"arguments[0], 'anonid', 'content');")
|
||||
frame = self.marionette.execute_script(script, [chatbox])
|
||||
self.marionette.switch_to_frame(frame)
|
||||
|
||||
|
@ -130,7 +130,7 @@ class Test1BrowserCall(MarionetteTestCase):
|
|||
# Assumes the standalone or the conversation window is selected first.
|
||||
def check_video(self, selector):
|
||||
video = self.wait_for_element_displayed(By.CSS_SELECTOR,
|
||||
selector, 20)
|
||||
selector, 20)
|
||||
self.wait_for_element_attribute_to_be_false(video, "paused")
|
||||
self.assertEqual(video.get_attribute("ended"), "false")
|
||||
|
||||
|
@ -216,8 +216,8 @@ class Test1BrowserCall(MarionetteTestCase):
|
|||
chatbox = self.wait_for_element_exists(By.TAG_NAME, 'chatbox')
|
||||
script = '''
|
||||
let chatBrowser = document.getAnonymousElementByAttribute(
|
||||
arguments[0], 'class',
|
||||
'chat-frame')
|
||||
arguments[0], 'anonid',
|
||||
'content')
|
||||
|
||||
// note that using wrappedJSObject waives X-ray vision, which
|
||||
// has security implications, but because we trust the code
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -105,7 +105,7 @@ ifdef MOZ_WEBAPP_RUNTIME
|
|||
endif
|
||||
@$(MAKE) -C ../../extensions/spellcheck/locales AB_CD=$* XPI_NAME=locale-$*
|
||||
@$(MAKE) -C ../extensions/pocket/locales AB_CD=$* XPI_NAME=locale-$*
|
||||
@$(MAKE) -C ../extensions/loop/ AB_CD=$* XPI_NAME=locale-$*
|
||||
@$(MAKE) -C ../extensions/loop/chrome/locale AB_CD=$* XPI_NAME=locale-$*
|
||||
@$(MAKE) -C ../../intl/locales AB_CD=$* XPI_NAME=locale-$*
|
||||
@$(MAKE) -C ../../devtools/client/locales AB_CD=$* XPI_NAME=locale-$* XPI_ROOT_APPID='$(XPI_ROOT_APPID)'
|
||||
@$(MAKE) -B searchplugins AB_CD=$* XPI_NAME=locale-$*
|
||||
|
|
|
@ -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",
|
||||
"Should be the right margin-top on the element.");
|
||||
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");
|
||||
|
|
|
@ -7,23 +7,24 @@
|
|||
// Test that longer values are rotated on the side
|
||||
|
||||
const res1 = [
|
||||
{selector: ".layout-margin.layout-top > span", value: 30},
|
||||
{selector: ".layout-margin.layout-left > span", value: "auto"},
|
||||
{selector: ".layout-margin.layout-bottom > span", value: 30},
|
||||
{selector: ".layout-margin.layout-right > span", value: "auto"},
|
||||
{selector: ".layout-padding.layout-top > span", value: 20},
|
||||
{selector: ".layout-padding.layout-left > span", value: 2000000},
|
||||
{selector: ".layout-padding.layout-bottom > span", value: 20},
|
||||
{selector: ".layout-padding.layout-right > span", value: 20},
|
||||
{selector: ".layout-border.layout-top > span", value: 10},
|
||||
{selector: ".layout-border.layout-left > span", value: 10},
|
||||
{selector: ".layout-border.layout-bottom > span", value: 10},
|
||||
{selector: ".layout-border.layout-right > span", value: 10},
|
||||
{selector: ".layout-margin.layout-top > span", value: 30},
|
||||
{selector: ".layout-margin.layout-left > span", value: "auto"},
|
||||
{selector: ".layout-margin.layout-bottom > span", value: 30},
|
||||
{selector: ".layout-margin.layout-right > span", value: "auto"},
|
||||
{selector: ".layout-padding.layout-top > span", value: 20},
|
||||
{selector: ".layout-padding.layout-left > span", value: 2000000},
|
||||
{selector: ".layout-padding.layout-bottom > span", value: 20},
|
||||
{selector: ".layout-padding.layout-right > span", value: 20},
|
||||
{selector: ".layout-border.layout-top > span", value: 10},
|
||||
{selector: ".layout-border.layout-left > span", value: 10},
|
||||
{selector: ".layout-border.layout-bottom > span", value: 10},
|
||||
{selector: ".layout-border.layout-right > span", value: 10},
|
||||
];
|
||||
|
||||
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");
|
||||
|
||||
|
|
|
@ -1,80 +1,79 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test that the inspector actor emits "resize" events when the page is resized.
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1222409
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1222409</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
|
||||
<script type="application/javascript;version=1.8">
|
||||
window.onload = function() {
|
||||
const Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/devtools/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", {});
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let win = null;
|
||||
let inspector = null;
|
||||
|
||||
addAsyncTest(function* setup() {
|
||||
info ("Setting up inspector and walker actors.");
|
||||
|
||||
let url = document.getElementById("inspectorContent").href;
|
||||
|
||||
yield new Promise(resolve => {
|
||||
attachURL(url, function(err, client, tab, doc) {
|
||||
win = doc.defaultView;
|
||||
inspector = InspectorFront(client, tab);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
runNextTest();
|
||||
});
|
||||
|
||||
addAsyncTest(function*() {
|
||||
let walker = yield inspector.getWalker();
|
||||
|
||||
// We can't receive events from the walker if we haven't first executed a
|
||||
// method on the actor to initialize it.
|
||||
yield walker.querySelector(walker.rootNode, "img");
|
||||
|
||||
let {outerWidth, outerHeight} = win;
|
||||
let onResize = new Promise(resolve => {
|
||||
walker.once("resize", () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
win.resizeTo(800, 600);
|
||||
yield onResize;
|
||||
|
||||
ok(true, "The resize event was emitted");
|
||||
win.resizeTo(outerWidth, outerHeight);
|
||||
|
||||
runNextTest();
|
||||
});
|
||||
|
||||
runNextTest();
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a id="inspectorContent" target="_blank" href="inspector-search-data.html">Test Document</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test that the inspector actor emits "resize" events when the page is resized.
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1222409
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1222409</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
|
||||
<script type="application/javascript;version=1.8">
|
||||
window.onload = function() {
|
||||
const Cu = Components.utils;
|
||||
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/Console.jsm", {});
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let win = null;
|
||||
let inspector = null;
|
||||
|
||||
addAsyncTest(function* setup() {
|
||||
info ("Setting up inspector and walker actors.");
|
||||
|
||||
let url = document.getElementById("inspectorContent").href;
|
||||
|
||||
yield new Promise(resolve => {
|
||||
attachURL(url, function(err, client, tab, doc) {
|
||||
win = doc.defaultView;
|
||||
inspector = InspectorFront(client, tab);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
runNextTest();
|
||||
});
|
||||
|
||||
addAsyncTest(function*() {
|
||||
let walker = yield inspector.getWalker();
|
||||
|
||||
// We can't receive events from the walker if we haven't first executed a
|
||||
// method on the actor to initialize it.
|
||||
yield walker.querySelector(walker.rootNode, "img");
|
||||
|
||||
let {outerWidth, outerHeight} = win;
|
||||
let onResize = new Promise(resolve => {
|
||||
walker.once("resize", () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
win.resizeTo(800, 600);
|
||||
yield onResize;
|
||||
|
||||
ok(true, "The resize event was emitted");
|
||||
win.resizeTo(outerWidth, outerHeight);
|
||||
|
||||
runNextTest();
|
||||
});
|
||||
|
||||
runNextTest();
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a id="inspectorContent" target="_blank" href="inspector-search-data.html">Test Document</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -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;
|
||||
|
@ -77,6 +76,8 @@ public class SwitchBoard {
|
|||
|
||||
private static final String IS_EXPERIMENT_ACTIVE = "isActive";
|
||||
private static final String EXPERIMENT_VALUES = "values";
|
||||
|
||||
private static String uuidExtra = null;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
DeviceUuidFactory df = new DeviceUuidFactory(c);
|
||||
String uuid = df.getDeviceUuid().toString();
|
||||
String uuid = uuidExtra;
|
||||
if (uuid == null) {
|
||||
DeviceUuidFactory df = new DeviceUuidFactory(c);
|
||||
uuid = df.getDeviceUuid().toString();
|
||||
}
|
||||
|
||||
CRC32 crc = new CRC32();
|
||||
crc.update(uuid.getBytes());
|
||||
|
|
|
@ -929,9 +929,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);
|
||||
|
@ -5211,4 +5210,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
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче