mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
Merge m-c to graphics
MozReview-Commit-ID: Ejcj1CD87t3
This commit is contained in:
commit
61bc491994
2
CLOBBER
2
CLOBBER
@ -22,4 +22,4 @@
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Touch clobber because of bug 1336456
|
||||
Touch clobber again because of bug 1336456
|
||||
|
@ -569,25 +569,12 @@ var Output = {
|
||||
}
|
||||
},
|
||||
|
||||
get androidBridge() {
|
||||
delete this.androidBridge;
|
||||
if (Utils.MozBuildApp === 'mobile/android') {
|
||||
this.androidBridge = Services.androidBridge;
|
||||
} else {
|
||||
this.androidBridge = null;
|
||||
}
|
||||
return this.androidBridge;
|
||||
},
|
||||
|
||||
Android: function Android(aDetails, aBrowser) {
|
||||
const ANDROID_VIEW_TEXT_CHANGED = 0x10;
|
||||
const ANDROID_VIEW_TEXT_SELECTION_CHANGED = 0x2000;
|
||||
|
||||
if (!this.androidBridge) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let androidEvent of aDetails) {
|
||||
androidEvent.type = 'Accessibility:Event';
|
||||
if (androidEvent.bounds) {
|
||||
androidEvent.bounds = AccessFu.adjustContentBounds(
|
||||
androidEvent.bounds, aBrowser);
|
||||
@ -607,9 +594,8 @@ var Output = {
|
||||
androidEvent.brailleOutput);
|
||||
break;
|
||||
}
|
||||
let win = Utils.win;
|
||||
let view = win && win.QueryInterface(Ci.nsIAndroidView);
|
||||
view.dispatch('Accessibility:Event', androidEvent);
|
||||
|
||||
Utils.win.WindowEventDispatcher.sendRequest(androidEvent);
|
||||
}
|
||||
},
|
||||
|
||||
@ -805,9 +791,7 @@ var Input = {
|
||||
|
||||
if (Utils.MozBuildApp == 'mobile/android') {
|
||||
// Return focus to native Android browser chrome.
|
||||
let win = Utils.win;
|
||||
let view = win && win.QueryInterface(Ci.nsIAndroidView);
|
||||
view.dispatch('ToggleChrome:Focus');
|
||||
Utils.win.WindowEventDispatcher.dispatch('ToggleChrome:Focus');
|
||||
}
|
||||
break;
|
||||
case aEvent.DOM_VK_RETURN:
|
||||
|
@ -315,7 +315,7 @@ AndroidPresenter.prototype.actionInvoked =
|
||||
|
||||
// Checkable objects use TalkBack's text derived from the event state,
|
||||
// so we don't populate the text here.
|
||||
let text = '';
|
||||
let text = null;
|
||||
if (!state.contains(States.CHECKABLE)) {
|
||||
text = Utils.localize(UtteranceGenerator.genForAction(aObject,
|
||||
aActionName));
|
||||
|
@ -1,5 +1,4 @@
|
||||
[DEFAULT]
|
||||
skip-if = (e10s && os == 'win') # Bug 1269369: Document loaded event does not fire in Windows
|
||||
support-files =
|
||||
events.js
|
||||
head.js
|
||||
@ -16,36 +15,62 @@ support-files =
|
||||
|
||||
# Caching tests
|
||||
[browser_caching_attributes.js]
|
||||
skip-if = e10s && os == 'win' # Bug 1288839
|
||||
[browser_caching_description.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_caching_name.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_caching_relations.js]
|
||||
skip-if = e10s && os == 'win' # Bug 1288839
|
||||
[browser_caching_states.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_caching_value.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
|
||||
# Events tests
|
||||
[browser_events_caretmove.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_events_hide.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_events_show.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_events_statechange.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_events_textchange.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
|
||||
# Tree update tests
|
||||
[browser_treeupdate_ariadialog.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_treeupdate_ariaowns.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_treeupdate_canvas.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_treeupdate_cssoverflow.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_treeupdate_doc.js]
|
||||
skip-if = e10s && os == 'win' # Bug 1288839
|
||||
[browser_treeupdate_gencontent.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_treeupdate_hidden.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_treeupdate_imagemap.js]
|
||||
skip-if = e10s # Bug 1318569
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_treeupdate_list.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_treeupdate_list_editabledoc.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_treeupdate_listener.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_treeupdate_optgroup.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_treeupdate_removal.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_treeupdate_table.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_treeupdate_textleaf.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_treeupdate_visibility.js]
|
||||
skip-if = e10s && os == 'win' && os_version == '5.1'
|
||||
[browser_treeupdate_whitespace.js]
|
||||
skip-if = true # Failing due to incorrect index of test container children on document load.
|
||||
|
@ -170,7 +170,22 @@ function* testContainer(browser) {
|
||||
testAccessibleTree(acc, tree);
|
||||
}
|
||||
|
||||
function* waitForImageMap(browser, accDoc) {
|
||||
const id = 'imgmap';
|
||||
const acc = findAccessibleChildByID(accDoc, id);
|
||||
if (acc.firstChild) {
|
||||
return;
|
||||
}
|
||||
|
||||
const onReorder = waitForEvent(EVENT_REORDER, id);
|
||||
// Wave over image map
|
||||
yield BrowserTestUtils.synthesizeMouse(`#${id}`, 10, 10,
|
||||
{ type: 'mousemove' }, browser);
|
||||
yield onReorder;
|
||||
}
|
||||
|
||||
addAccessibleTask('doc_treeupdate_imagemap.html', function*(browser, accDoc) {
|
||||
yield waitForImageMap(browser, accDoc);
|
||||
yield testImageMap(browser, accDoc);
|
||||
yield testContainer(browser);
|
||||
});
|
||||
|
@ -492,7 +492,6 @@
|
||||
@RESPATH@/components/nsWebHandlerApp.js
|
||||
@RESPATH@/components/satchel.manifest
|
||||
@RESPATH@/components/nsFormAutoComplete.js
|
||||
@RESPATH@/components/nsFormHistory.js
|
||||
@RESPATH@/components/FormHistoryStartup.js
|
||||
@RESPATH@/components/nsInputListAutoComplete.js
|
||||
@RESPATH@/components/formautofill.manifest
|
||||
|
@ -2878,24 +2878,6 @@ var BrowserOnClick = {
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent(event) {
|
||||
if (!event.isTrusted || // Don't trust synthetic events
|
||||
event.button == 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
let originalTarget = event.originalTarget;
|
||||
let ownerDoc = originalTarget.ownerDocument;
|
||||
if (!ownerDoc) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gMultiProcessBrowser &&
|
||||
ownerDoc.documentURI.toLowerCase() == "about:newtab") {
|
||||
this.onE10sAboutNewTab(event, ownerDoc);
|
||||
}
|
||||
},
|
||||
|
||||
receiveMessage(msg) {
|
||||
switch (msg.name) {
|
||||
case "Browser:CertExceptionError":
|
||||
@ -3082,28 +3064,6 @@ var BrowserOnClick = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* This functions prevents navigation from happening directly through the <a>
|
||||
* link in about:newtab (which is loaded in the parent and therefore would load
|
||||
* the next page also in the parent) and instructs the browser to open the url
|
||||
* in the current tab which will make it update the remoteness of the tab.
|
||||
*/
|
||||
onE10sAboutNewTab(event, ownerDoc) {
|
||||
let isTopFrame = (ownerDoc.defaultView.parent === ownerDoc.defaultView);
|
||||
if (!isTopFrame) {
|
||||
return;
|
||||
}
|
||||
|
||||
let anchorTarget = event.originalTarget.parentNode;
|
||||
|
||||
if (anchorTarget instanceof HTMLAnchorElement &&
|
||||
anchorTarget.classList.contains("newtab-link")) {
|
||||
event.preventDefault();
|
||||
let where = whereToOpenLink(event, false, false);
|
||||
openLinkIn(anchorTarget.href, where, { charset: ownerDoc.characterSet, referrerURI: ownerDoc.documentURIObject });
|
||||
}
|
||||
},
|
||||
|
||||
ignoreWarningButton(reason) {
|
||||
// Allow users to override and continue through to the site,
|
||||
// but add a notify bar as a reminder, so that they don't lose
|
||||
@ -4924,13 +4884,9 @@ var TabsProgressListener = {
|
||||
}
|
||||
}
|
||||
|
||||
// Attach a listener to watch for "click" events bubbling up from error
|
||||
// pages and other similar pages (like about:newtab). This lets us fix bugs
|
||||
// like 401575 which require error page UI to do privileged things, without
|
||||
// letting error pages have any privilege themselves.
|
||||
// We can't look for this during onLocationChange since at that point the
|
||||
// document URI is not yet the about:-uri of the error page.
|
||||
|
||||
// We used to listen for clicks in the browser here, but when that
|
||||
// became unnecessary, removing the code below caused focus issues.
|
||||
// This code should be removed. Tracked in bug 1337794.
|
||||
let isRemoteBrowser = aBrowser.isRemoteBrowser;
|
||||
// We check isRemoteBrowser here to avoid requesting the doc CPOW
|
||||
let doc = isRemoteBrowser ? null : aWebProgress.DOMWindow.document;
|
||||
@ -4945,11 +4901,9 @@ var TabsProgressListener = {
|
||||
// STATE_STOP may be received twice for documents, thus store an
|
||||
// attribute to ensure handling it just once.
|
||||
doc.documentElement.setAttribute("hasBrowserHandlers", "true");
|
||||
aBrowser.addEventListener("click", BrowserOnClick, true);
|
||||
aBrowser.addEventListener("pagehide", function onPageHide(event) {
|
||||
if (event.target.defaultView.frameElement)
|
||||
return;
|
||||
aBrowser.removeEventListener("click", BrowserOnClick, true);
|
||||
aBrowser.removeEventListener("pagehide", onPageHide, true);
|
||||
if (event.target.documentElement)
|
||||
event.target.documentElement.removeAttribute("hasBrowserHandlers");
|
||||
|
@ -81,6 +81,8 @@ support-files =
|
||||
browser_webext_update2.xpi
|
||||
browser_webext_update_icon1.xpi
|
||||
browser_webext_update_icon2.xpi
|
||||
browser_webext_update_perms1.xpi
|
||||
browser_webext_update_perms2.xpi
|
||||
browser_webext_update.json
|
||||
!/image/test/mochitest/blue.png
|
||||
!/toolkit/content/tests/browser/common/mockTransfer.js
|
||||
@ -252,7 +254,8 @@ skip-if = os == "mac" # decoder doctor isn't implemented on osx
|
||||
skip-if = true # browser_drag.js is disabled, as it needs to be updated for the new behavior from bug 320638.
|
||||
[browser_extension_permissions.js]
|
||||
[browser_extension_sideloading.js]
|
||||
[browser_extension_update.js]
|
||||
[browser_extension_update_background.js]
|
||||
[browser_extension_update_interactive.js]
|
||||
[browser_favicon_change.js]
|
||||
[browser_favicon_change_not_in_document.js]
|
||||
[browser_findbarClose.js]
|
||||
@ -349,7 +352,7 @@ skip-if = os == 'linux' # Bug 1304272
|
||||
[browser_tab_detach_restore.js]
|
||||
[browser_tab_drag_drop_perwindow.js]
|
||||
[browser_tab_dragdrop.js]
|
||||
skip-if = buildapp == 'mulet' || (e10s && (debug || asan)) # Bug 1312436
|
||||
skip-if = buildapp == 'mulet' || (e10s && (debug || os == 'linux')) # Bug 1312436
|
||||
[browser_tab_dragdrop2.js]
|
||||
[browser_tabbar_big_widgets.js]
|
||||
skip-if = os == "linux" || os == "mac" # No tabs in titlebar on linux
|
||||
|
@ -27,6 +27,11 @@ function* test_bookmarks_popup({isNewBookmark, popupShowFn, popupEditFn,
|
||||
});
|
||||
}
|
||||
|
||||
info(`BookmarkingUI.status is ${BookmarkingUI.status}`);
|
||||
yield BrowserTestUtils.waitForCondition(
|
||||
() => BookmarkingUI.status != BookmarkingUI.STATUS_UPDATING,
|
||||
"BookmarkingUI should not be updating");
|
||||
|
||||
is(bookmarkStar.hasAttribute("starred"), !isNewBookmark,
|
||||
"Page should only be starred prior to popupshown if editing bookmark");
|
||||
is(bookmarkPanel.state, "closed", "Panel should be 'closed' to start test");
|
||||
|
@ -71,25 +71,6 @@ class MockProvider {
|
||||
}
|
||||
}
|
||||
|
||||
function promiseViewLoaded(tab, viewid) {
|
||||
let win = tab.linkedBrowser.contentWindow;
|
||||
if (win.gViewController && !win.gViewController.isLoading &&
|
||||
win.gViewController.currentViewId == viewid) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
function listener() {
|
||||
if (win.gViewController.currentViewId != viewid) {
|
||||
return;
|
||||
}
|
||||
win.document.removeEventListener("ViewChanged", listener);
|
||||
resolve();
|
||||
}
|
||||
win.document.addEventListener("ViewChanged", listener);
|
||||
});
|
||||
}
|
||||
|
||||
function promisePopupNotificationShown(name) {
|
||||
return new Promise(resolve => {
|
||||
function popupshown() {
|
||||
@ -169,6 +150,17 @@ add_task(function* () {
|
||||
ExtensionsUI.emit("change");
|
||||
});
|
||||
|
||||
// Navigate away from the starting page to force about:addons to load
|
||||
// in a new tab during the tests below.
|
||||
gBrowser.selectedBrowser.loadURI("about:robots");
|
||||
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
|
||||
registerCleanupFunction(function*() {
|
||||
// Return to about:blank when we're done
|
||||
gBrowser.selectedBrowser.loadURI("about:blank");
|
||||
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
});
|
||||
|
||||
let changePromise = new Promise(resolve => {
|
||||
ExtensionsUI.on("change", function listener() {
|
||||
ExtensionsUI.off("change", listener);
|
||||
@ -189,22 +181,20 @@ add_task(function* () {
|
||||
is(addons.children.length, 2, "Have 2 menu entries for sideloaded extensions");
|
||||
|
||||
// Click the first sideloaded extension
|
||||
let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "about:addons");
|
||||
let popupPromise = promisePopupNotificationShown("addon-webext-permissions");
|
||||
addons.children[0].click();
|
||||
|
||||
// about:addons should load and go to the list of extensions
|
||||
let tab = yield tabPromise;
|
||||
is(tab.linkedBrowser.currentURI.spec, "about:addons", "Newly opened tab is at about:addons");
|
||||
// When we get the permissions prompt, we should be at the extensions
|
||||
// list in about:addons
|
||||
let panel = yield popupPromise;
|
||||
is(gBrowser.currentURI.spec, "about:addons", "Foreground tab is at about:addons");
|
||||
|
||||
const VIEW = "addons://list/extension";
|
||||
yield promiseViewLoaded(tab, VIEW);
|
||||
let win = tab.linkedBrowser.contentWindow;
|
||||
let win = gBrowser.selectedBrowser.contentWindow;
|
||||
ok(!win.gViewController.isLoading, "about:addons view is fully loaded");
|
||||
is(win.gViewController.currentViewId, VIEW, "about:addons is at extensions list");
|
||||
|
||||
// Wait for the permission prompt and cancel it
|
||||
let panel = yield popupPromise;
|
||||
// Check the contents of the notification, then choose "Cancel"
|
||||
let icon = panel.getAttribute("icon");
|
||||
is(icon, ICON_URL, "Permissions notification has the addon icon");
|
||||
|
||||
@ -219,7 +209,7 @@ add_task(function* () {
|
||||
is(addon1.userDisabled, true, "Addon 1 should still be disabled");
|
||||
is(addon2.userDisabled, true, "Addon 2 should still be disabled");
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
|
||||
// Should still have 1 entry in the hamburger menu
|
||||
yield PanelUI.show();
|
||||
@ -227,23 +217,21 @@ add_task(function* () {
|
||||
addons = document.getElementById("PanelUI-footer-addons");
|
||||
is(addons.children.length, 1, "Have 1 menu entry for sideloaded extensions");
|
||||
|
||||
// Click the second sideloaded extension
|
||||
tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "about:addons");
|
||||
// Click the second sideloaded extension and wait for the notification
|
||||
popupPromise = promisePopupNotificationShown("addon-webext-permissions");
|
||||
addons.children[0].click();
|
||||
|
||||
tab = yield tabPromise;
|
||||
is(tab.linkedBrowser.currentURI.spec, "about:addons", "Newly opened tab is at about:addons");
|
||||
panel = yield popupPromise;
|
||||
|
||||
isnot(menuButton.getAttribute("badge-status"), "addon-alert", "Should no longer have addon alert badge");
|
||||
|
||||
yield promiseViewLoaded(tab, VIEW);
|
||||
win = tab.linkedBrowser.contentWindow;
|
||||
// Again we should be at the extentions list in about:addons
|
||||
is(gBrowser.currentURI.spec, "about:addons", "Foreground tab is at about:addons");
|
||||
|
||||
win = gBrowser.selectedBrowser.contentWindow;
|
||||
ok(!win.gViewController.isLoading, "about:addons view is fully loaded");
|
||||
is(win.gViewController.currentViewId, VIEW, "about:addons is at extensions list");
|
||||
|
||||
// Wait for the permission prompt and accept it this time
|
||||
panel = yield popupPromise;
|
||||
// Check the notification contents, this time accept the install
|
||||
icon = panel.getAttribute("icon");
|
||||
is(icon, DEFAULT_ICON_URL, "Permissions notification has the default icon");
|
||||
disablePromise = promiseSetDisabled(mock2);
|
||||
@ -256,5 +244,5 @@ add_task(function* () {
|
||||
is(addon1.userDisabled, true, "Addon 1 should still be disabled");
|
||||
is(addon2.userDisabled, false, "Addon 2 should now be enabled");
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
});
|
||||
|
@ -74,8 +74,8 @@ function promiseInstallEvent(addon, event) {
|
||||
}
|
||||
|
||||
// Set some prefs that apply to all the tests in this file
|
||||
add_task(function setup() {
|
||||
return SpecialPowers.pushPrefEnv({set: [
|
||||
add_task(function* setup() {
|
||||
yield SpecialPowers.pushPrefEnv({set: [
|
||||
// We don't have pre-pinned certificates for the local mochitest server
|
||||
["extensions.install.requireBuiltInCerts", false],
|
||||
["extensions.update.requireBuiltInCerts", false],
|
||||
@ -83,6 +83,17 @@ add_task(function setup() {
|
||||
// XXX remove this when prompts are enabled by default
|
||||
["extensions.webextPermissionPrompts", true],
|
||||
]});
|
||||
|
||||
// Navigate away from the initial page so that about:addons always
|
||||
// opens in a new tab during tests
|
||||
gBrowser.selectedBrowser.loadURI("about:robots");
|
||||
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
|
||||
registerCleanupFunction(function*() {
|
||||
// Return to about:blank when we're done
|
||||
gBrowser.selectedBrowser.loadURI("about:blank");
|
||||
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
});
|
||||
});
|
||||
|
||||
// Helper function to test background updates.
|
||||
@ -123,7 +134,7 @@ function* backgroundUpdateTest(url, id, checkIconFn) {
|
||||
|
||||
// about:addons should load and go to the list of extensions
|
||||
let tab = yield tabPromise;
|
||||
is(tab.linkedBrowser.currentURI.spec, "about:addons");
|
||||
is(tab.linkedBrowser.currentURI.spec, "about:addons", "Browser is at about:addons");
|
||||
|
||||
const VIEW = "addons://list/extension";
|
||||
yield promiseViewLoaded(tab, VIEW);
|
||||
@ -211,114 +222,45 @@ function checkNonDefaultIcon(icon) {
|
||||
add_task(() => backgroundUpdateTest(`${URL_BASE}/browser_webext_update_icon1.xpi`,
|
||||
ID_ICON, checkNonDefaultIcon));
|
||||
|
||||
// Helper function to test a specific scenario for interactive updates.
|
||||
// `checkFn` is a callable that triggers a check for updates.
|
||||
// `autoUpdate` specifies whether the test should be run with
|
||||
// updates applied automatically or not.
|
||||
function* interactiveUpdateTest(autoUpdate, checkFn) {
|
||||
// Test that an update that adds new non-promptable permissions is just
|
||||
// applied without showing a notification dialog.
|
||||
add_task(function*() {
|
||||
yield SpecialPowers.pushPrefEnv({set: [
|
||||
["extensions.update.autoUpdateDefault", autoUpdate],
|
||||
// Turn on background updates
|
||||
["extensions.update.enabled", true],
|
||||
|
||||
// Point updates to the local mochitest server
|
||||
["extensions.update.url", `${URL_BASE}/browser_webext_update.json`],
|
||||
["extensions.update.background.url", `${URL_BASE}/browser_webext_update.json`],
|
||||
]});
|
||||
|
||||
// Trigger an update check, manually applying the update if we're testing
|
||||
// without auto-update.
|
||||
function* triggerUpdate(win, addon) {
|
||||
let manualUpdatePromise;
|
||||
if (!autoUpdate) {
|
||||
manualUpdatePromise = new Promise(resolve => {
|
||||
let listener = {
|
||||
onNewInstall() {
|
||||
AddonManager.removeInstallListener(listener);
|
||||
resolve();
|
||||
},
|
||||
};
|
||||
AddonManager.addInstallListener(listener);
|
||||
});
|
||||
}
|
||||
|
||||
checkFn(win, addon);
|
||||
|
||||
if (manualUpdatePromise) {
|
||||
yield manualUpdatePromise;
|
||||
|
||||
let item = win.document.getElementById("addon-list")
|
||||
.children.find(_item => _item.value == ID);
|
||||
EventUtils.synthesizeMouseAtCenter(item._updateBtn, {}, win);
|
||||
}
|
||||
}
|
||||
|
||||
// Install version 1.0 of the test extension
|
||||
let addon = yield promiseInstallAddon(`${URL_BASE}/browser_webext_update1.xpi`);
|
||||
let addon = yield promiseInstallAddon(`${URL_BASE}/browser_webext_update_perms1.xpi`);
|
||||
|
||||
ok(addon, "Addon was installed");
|
||||
is(addon.version, "1.0", "Version 1 of the addon is installed");
|
||||
|
||||
// Open add-ons manager and navigate to extensions list
|
||||
let loadPromise = new Promise(resolve => {
|
||||
let listener = (subject, topic) => {
|
||||
if (subject.location.href == "about:addons") {
|
||||
Services.obs.removeObserver(listener, topic);
|
||||
resolve(subject);
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(listener, "EM-loaded", false);
|
||||
});
|
||||
let tab = gBrowser.addTab("about:addons");
|
||||
gBrowser.selectedTab = tab;
|
||||
let win = yield loadPromise;
|
||||
let sawPopup = false;
|
||||
PopupNotifications.panel.addEventListener("popupshown",
|
||||
() => sawPopup = true,
|
||||
{once: true});
|
||||
|
||||
const VIEW = "addons://list/extension";
|
||||
let viewPromise = promiseViewLoaded(tab, VIEW);
|
||||
win.loadView(VIEW);
|
||||
yield viewPromise;
|
||||
|
||||
// Trigger an update check
|
||||
let popupPromise = promisePopupNotificationShown("addon-webext-permissions");
|
||||
yield triggerUpdate(win, addon);
|
||||
let panel = yield popupPromise;
|
||||
|
||||
// Click the cancel button, wait to see the cancel event
|
||||
let cancelPromise = promiseInstallEvent(addon, "onInstallCancelled");
|
||||
panel.secondaryButton.click();
|
||||
yield cancelPromise;
|
||||
|
||||
addon = yield AddonManager.getAddonByID(ID);
|
||||
is(addon.version, "1.0", "Should still be running the old version");
|
||||
|
||||
// Trigger a new update check
|
||||
popupPromise = promisePopupNotificationShown("addon-webext-permissions");
|
||||
yield triggerUpdate(win, addon);
|
||||
|
||||
// This time, accept the upgrade
|
||||
// Trigger an update check and wait for the update to be applied.
|
||||
let updatePromise = promiseInstallEvent(addon, "onInstallEnded");
|
||||
panel = yield popupPromise;
|
||||
panel.button.click();
|
||||
AddonManagerPrivate.backgroundUpdateCheck();
|
||||
yield updatePromise;
|
||||
|
||||
addon = yield updatePromise;
|
||||
is(addon.version, "2.0", "Should have upgraded");
|
||||
// There should be no notifications about the update
|
||||
is(getBadgeStatus(), "", "Should not have addon alert badge");
|
||||
|
||||
yield PanelUI.show();
|
||||
let addons = document.getElementById("PanelUI-footer-addons");
|
||||
is(addons.children.length, 0, "Have 0 updates in the PanelUI menu");
|
||||
yield PanelUI.hide();
|
||||
|
||||
ok(!sawPopup, "Should not have seen permissions notification");
|
||||
|
||||
addon = yield AddonManager.getAddonByID("update_perms@tests.mozilla.org");
|
||||
is(addon.version, "2.0", "Update should have applied");
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
addon.uninstall();
|
||||
yield SpecialPowers.popPrefEnv();
|
||||
}
|
||||
|
||||
// Invoke the "Check for Updates" menu item
|
||||
function checkAll(win) {
|
||||
win.gViewController.doCommand("cmd_findAllUpdates");
|
||||
}
|
||||
|
||||
// Test "Check for Updates" with both auto-update settings
|
||||
add_task(() => interactiveUpdateTest(true, checkAll));
|
||||
add_task(() => interactiveUpdateTest(false, checkAll));
|
||||
|
||||
|
||||
// Invoke an invidual extension's "Find Updates" menu item
|
||||
function checkOne(win, addon) {
|
||||
win.gViewController.doCommand("cmd_findItemUpdates", addon);
|
||||
}
|
||||
|
||||
// Test "Find Updates" with both auto-update settings
|
||||
add_task(() => interactiveUpdateTest(true, checkOne));
|
||||
add_task(() => interactiveUpdateTest(false, checkOne));
|
||||
});
|
@ -0,0 +1,192 @@
|
||||
const {AddonManagerPrivate} = Cu.import("resource://gre/modules/AddonManager.jsm", {});
|
||||
|
||||
const URL_BASE = "https://example.com/browser/browser/base/content/test/general";
|
||||
const ID = "update@tests.mozilla.org";
|
||||
|
||||
function promiseInstallAddon(url) {
|
||||
return AddonManager.getInstallForURL(url, null, "application/x-xpinstall")
|
||||
.then(install => {
|
||||
ok(install, "Created install");
|
||||
return new Promise(resolve => {
|
||||
install.addListener({
|
||||
onInstallEnded(_install, addon) {
|
||||
resolve(addon);
|
||||
},
|
||||
});
|
||||
install.install();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function promiseViewLoaded(tab, viewid) {
|
||||
let win = tab.linkedBrowser.contentWindow;
|
||||
if (win.gViewController && !win.gViewController.isLoading &&
|
||||
win.gViewController.currentViewId == viewid) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
function listener() {
|
||||
if (win.gViewController.currentViewId != viewid) {
|
||||
return;
|
||||
}
|
||||
win.document.removeEventListener("ViewChanged", listener);
|
||||
resolve();
|
||||
}
|
||||
win.document.addEventListener("ViewChanged", listener);
|
||||
});
|
||||
}
|
||||
|
||||
function promisePopupNotificationShown(name) {
|
||||
return new Promise(resolve => {
|
||||
function popupshown() {
|
||||
let notification = PopupNotifications.getNotification(name);
|
||||
if (!notification) { return; }
|
||||
|
||||
ok(notification, `${name} notification shown`);
|
||||
ok(PopupNotifications.isPanelOpen, "notification panel open");
|
||||
|
||||
PopupNotifications.panel.removeEventListener("popupshown", popupshown);
|
||||
resolve(PopupNotifications.panel.firstChild);
|
||||
}
|
||||
|
||||
PopupNotifications.panel.addEventListener("popupshown", popupshown);
|
||||
});
|
||||
}
|
||||
|
||||
function promiseInstallEvent(addon, event) {
|
||||
return new Promise(resolve => {
|
||||
let listener = {};
|
||||
listener[event] = (install, ...args) => {
|
||||
if (install.addon.id == addon.id) {
|
||||
AddonManager.removeInstallListener(listener);
|
||||
resolve(...args);
|
||||
}
|
||||
};
|
||||
AddonManager.addInstallListener(listener);
|
||||
});
|
||||
}
|
||||
|
||||
// Set some prefs that apply to all the tests in this file
|
||||
add_task(function* setup() {
|
||||
yield SpecialPowers.pushPrefEnv({set: [
|
||||
// We don't have pre-pinned certificates for the local mochitest server
|
||||
["extensions.install.requireBuiltInCerts", false],
|
||||
["extensions.update.requireBuiltInCerts", false],
|
||||
|
||||
// XXX remove this when prompts are enabled by default
|
||||
["extensions.webextPermissionPrompts", true],
|
||||
]});
|
||||
});
|
||||
|
||||
// Helper function to test a specific scenario for interactive updates.
|
||||
// `checkFn` is a callable that triggers a check for updates.
|
||||
// `autoUpdate` specifies whether the test should be run with
|
||||
// updates applied automatically or not.
|
||||
function* interactiveUpdateTest(autoUpdate, checkFn) {
|
||||
yield SpecialPowers.pushPrefEnv({set: [
|
||||
["extensions.update.autoUpdateDefault", autoUpdate],
|
||||
|
||||
// Point updates to the local mochitest server
|
||||
["extensions.update.url", `${URL_BASE}/browser_webext_update.json`],
|
||||
]});
|
||||
|
||||
// Trigger an update check, manually applying the update if we're testing
|
||||
// without auto-update.
|
||||
function* triggerUpdate(win, addon) {
|
||||
let manualUpdatePromise;
|
||||
if (!autoUpdate) {
|
||||
manualUpdatePromise = new Promise(resolve => {
|
||||
let listener = {
|
||||
onNewInstall() {
|
||||
AddonManager.removeInstallListener(listener);
|
||||
resolve();
|
||||
},
|
||||
};
|
||||
AddonManager.addInstallListener(listener);
|
||||
});
|
||||
}
|
||||
|
||||
checkFn(win, addon);
|
||||
|
||||
if (manualUpdatePromise) {
|
||||
yield manualUpdatePromise;
|
||||
|
||||
let item = win.document.getElementById("addon-list")
|
||||
.children.find(_item => _item.value == ID);
|
||||
EventUtils.synthesizeMouseAtCenter(item._updateBtn, {}, win);
|
||||
}
|
||||
}
|
||||
|
||||
// Install version 1.0 of the test extension
|
||||
let addon = yield promiseInstallAddon(`${URL_BASE}/browser_webext_update1.xpi`);
|
||||
ok(addon, "Addon was installed");
|
||||
is(addon.version, "1.0", "Version 1 of the addon is installed");
|
||||
|
||||
// Open add-ons manager and navigate to extensions list
|
||||
let loadPromise = new Promise(resolve => {
|
||||
let listener = (subject, topic) => {
|
||||
if (subject.location.href == "about:addons") {
|
||||
Services.obs.removeObserver(listener, topic);
|
||||
resolve(subject);
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(listener, "EM-loaded", false);
|
||||
});
|
||||
let tab = gBrowser.addTab("about:addons");
|
||||
gBrowser.selectedTab = tab;
|
||||
let win = yield loadPromise;
|
||||
|
||||
const VIEW = "addons://list/extension";
|
||||
let viewPromise = promiseViewLoaded(tab, VIEW);
|
||||
win.loadView(VIEW);
|
||||
yield viewPromise;
|
||||
|
||||
// Trigger an update check
|
||||
let popupPromise = promisePopupNotificationShown("addon-webext-permissions");
|
||||
yield triggerUpdate(win, addon);
|
||||
let panel = yield popupPromise;
|
||||
|
||||
// Click the cancel button, wait to see the cancel event
|
||||
let cancelPromise = promiseInstallEvent(addon, "onInstallCancelled");
|
||||
panel.secondaryButton.click();
|
||||
yield cancelPromise;
|
||||
|
||||
addon = yield AddonManager.getAddonByID(ID);
|
||||
is(addon.version, "1.0", "Should still be running the old version");
|
||||
|
||||
// Trigger a new update check
|
||||
popupPromise = promisePopupNotificationShown("addon-webext-permissions");
|
||||
yield triggerUpdate(win, addon);
|
||||
|
||||
// This time, accept the upgrade
|
||||
let updatePromise = promiseInstallEvent(addon, "onInstallEnded");
|
||||
panel = yield popupPromise;
|
||||
panel.button.click();
|
||||
|
||||
addon = yield updatePromise;
|
||||
is(addon.version, "2.0", "Should have upgraded");
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
addon.uninstall();
|
||||
yield SpecialPowers.popPrefEnv();
|
||||
}
|
||||
|
||||
// Invoke the "Check for Updates" menu item
|
||||
function checkAll(win) {
|
||||
win.gViewController.doCommand("cmd_findAllUpdates");
|
||||
}
|
||||
|
||||
// Test "Check for Updates" with both auto-update settings
|
||||
add_task(() => interactiveUpdateTest(true, checkAll));
|
||||
add_task(() => interactiveUpdateTest(false, checkAll));
|
||||
|
||||
|
||||
// Invoke an invidual extension's "Find Updates" menu item
|
||||
function checkOne(win, addon) {
|
||||
win.gViewController.doCommand("cmd_findItemUpdates", addon);
|
||||
}
|
||||
|
||||
// Test "Find Updates" with both auto-update settings
|
||||
add_task(() => interactiveUpdateTest(true, checkOne));
|
||||
add_task(() => interactiveUpdateTest(false, checkOne));
|
@ -94,6 +94,18 @@ const PAGECONTENT_COLORS =
|
||||
' <option value="Four" class="defaultColor defaultBackground">{"color": "-moz-ComboboxText", "backgroundColor": "transparent", "unstyled": "true"}</option>' +
|
||||
' <option value="Five" class="defaultColor">{"color": "-moz-ComboboxText", "backgroundColor": "transparent", "unstyled": "true"}</option>' +
|
||||
' <option value="Six" class="defaultBackground">{"color": "-moz-ComboboxText", "backgroundColor": "transparent", "unstyled": "true"}</option>' +
|
||||
' <option value="Seven" selected="true">{"unstyled": "true"}</option>' +
|
||||
"</select></body></html>";
|
||||
|
||||
const PAGECONTENT_COLORS_ON_SELECT =
|
||||
"<html><head><style>" +
|
||||
" #one { background-color: #7E3A3A; color: #fff }" +
|
||||
"</style>" +
|
||||
"<body><select id='one'>" +
|
||||
' <option value="One">{"color": "rgb(255, 255, 255)", "backgroundColor": "transparent"}</option>' +
|
||||
' <option value="Two">{"color": "rgb(255, 255, 255)", "backgroundColor": "transparent"}</option>' +
|
||||
' <option value="Three">{"color": "rgb(255, 255, 255)", "backgroundColor": "transparent"}</option>' +
|
||||
' <option value="Four" selected="true">{"end": "true"}</option>' +
|
||||
"</select></body></html>";
|
||||
|
||||
function openSelectPopup(selectPopup, mode = "key", selector = "select", win = window) {
|
||||
@ -150,6 +162,38 @@ function getClickEvents() {
|
||||
});
|
||||
}
|
||||
|
||||
function testOptionColors(index, item, menulist) {
|
||||
let expected = JSON.parse(item.label);
|
||||
|
||||
for (let color of Object.keys(expected)) {
|
||||
if (color.toLowerCase().includes("color") &&
|
||||
!expected[color].startsWith("rgb")) {
|
||||
// Need to convert system color to RGB color.
|
||||
let textarea = document.createElementNS("http://www.w3.org/1999/xhtml", "textarea");
|
||||
textarea.style.color = expected[color];
|
||||
expected[color] = getComputedStyle(textarea).color;
|
||||
}
|
||||
}
|
||||
|
||||
// Press Down to move the selected item to the next item in the
|
||||
// list and check the colors of this item when it's not selected.
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
|
||||
|
||||
if (expected.end) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (expected.unstyled) {
|
||||
ok(!item.hasAttribute("customoptionstyling"),
|
||||
`Item ${index} should not have any custom option styling`);
|
||||
} else {
|
||||
is(getComputedStyle(item).color, expected.color,
|
||||
"Item " + (index) + " has correct foreground color");
|
||||
is(getComputedStyle(item).backgroundColor, expected.backgroundColor,
|
||||
"Item " + (index) + " has correct background color");
|
||||
}
|
||||
}
|
||||
|
||||
function* doSelectTests(contentType, dtd) {
|
||||
const pageUrl = "data:" + contentType + "," + escape(dtd + "\n" + PAGECONTENT);
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
|
||||
@ -745,11 +789,13 @@ add_task(function* test_somehidden() {
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(function* test_colors_applied_to_popup() {
|
||||
// This test checks when a <select> element has styles applied to <option>s within it.
|
||||
add_task(function* test_colors_applied_to_popup_items() {
|
||||
const pageUrl = "data:text/html," + escape(PAGECONTENT_COLORS);
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
|
||||
|
||||
let selectPopup = document.getElementById("ContentSelectDropdown").menupopup;
|
||||
let menulist = document.getElementById("ContentSelectDropdown");
|
||||
let selectPopup = menulist.menupopup;
|
||||
|
||||
let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
|
||||
yield BrowserTestUtils.synthesizeMouseAtCenter("#one", { type: "mousedown" }, gBrowser.selectedBrowser);
|
||||
@ -757,38 +803,47 @@ add_task(function* test_colors_applied_to_popup() {
|
||||
|
||||
// The label contains a JSON string of the expected colors for
|
||||
// `color` and `background-color`.
|
||||
is(selectPopup.parentNode.itemCount, 6, "Correct number of items");
|
||||
is(selectPopup.parentNode.itemCount, 7, "Correct number of items");
|
||||
let child = selectPopup.firstChild;
|
||||
let idx = 1;
|
||||
|
||||
ok(child.selected, "The first child should be selected");
|
||||
ok(!child.selected, "The first child should not be selected");
|
||||
while (child) {
|
||||
let expected = JSON.parse(child.label);
|
||||
|
||||
for (let color of Object.keys(expected)) {
|
||||
if (color.toLowerCase().includes("color") &&
|
||||
!expected[color].startsWith("rgb")) {
|
||||
// Need to convert system color to RGB color.
|
||||
let textarea = document.createElementNS("http://www.w3.org/1999/xhtml", "textarea");
|
||||
textarea.style.color = expected[color];
|
||||
expected[color] = getComputedStyle(textarea).color;
|
||||
}
|
||||
}
|
||||
|
||||
// Press Down to move the selected item to the next item in the
|
||||
// list and check the colors of this item when it's not selected.
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
|
||||
|
||||
if (expected.unstyled) {
|
||||
ok(!child.hasAttribute("customoptionstyling"),
|
||||
`Item ${idx} should not have any custom option styling`);
|
||||
} else {
|
||||
is(getComputedStyle(child).color, expected.color,
|
||||
"Item " + (idx) + " has correct foreground color");
|
||||
is(getComputedStyle(child).backgroundColor, expected.backgroundColor,
|
||||
"Item " + (idx) + " has correct background color");
|
||||
}
|
||||
|
||||
testOptionColors(idx, child, menulist);
|
||||
idx++;
|
||||
child = child.nextSibling;
|
||||
}
|
||||
|
||||
yield hideSelectPopup(selectPopup, "escape");
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
// This test checks when a <select> element has styles applied to itself.
|
||||
add_task(function* test_colors_applied_to_popup() {
|
||||
const pageUrl = "data:text/html," + escape(PAGECONTENT_COLORS_ON_SELECT);
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
|
||||
|
||||
let menulist = document.getElementById("ContentSelectDropdown");
|
||||
let selectPopup = menulist.menupopup;
|
||||
|
||||
let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
|
||||
yield BrowserTestUtils.synthesizeMouseAtCenter("#one", { type: "mousedown" }, gBrowser.selectedBrowser);
|
||||
yield popupShownPromise;
|
||||
|
||||
// The label contains a JSON string of the expected colors for
|
||||
// `color` and `background-color`.
|
||||
is(selectPopup.parentNode.itemCount, 4, "Correct number of items");
|
||||
let child = selectPopup.firstChild;
|
||||
let idx = 1;
|
||||
|
||||
is(getComputedStyle(selectPopup).color, "rgb(255, 255, 255)",
|
||||
"popup has expected foreground color");
|
||||
is(getComputedStyle(selectPopup).backgroundColor, "rgb(126, 58, 58)",
|
||||
"popup has expected background color");
|
||||
|
||||
ok(!child.selected, "The first child should not be selected");
|
||||
while (child) {
|
||||
testOptionColors(idx, child, menulist);
|
||||
idx++;
|
||||
child = child.nextSibling;
|
||||
}
|
||||
|
@ -27,6 +27,20 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"update_perms@tests.mozilla.org": {
|
||||
"updates": [
|
||||
{
|
||||
"version": "2.0",
|
||||
"update_link": "https://example.com/browser/browser/base/content/test/general/browser_webext_update_perms2.xpi",
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"strict_min_version": "1",
|
||||
"advisory_max_version": "55.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
@ -148,9 +148,11 @@ var tests = [
|
||||
EventUtils.synthesizeMouseAtCenter(checkbox, {});
|
||||
dismissNotification(popup);
|
||||
},
|
||||
onHidden(popup) {
|
||||
*onHidden(popup) {
|
||||
let icon = document.getElementById("default-notification-icon");
|
||||
let shown = waitForNotificationPanel();
|
||||
EventUtils.synthesizeMouseAtCenter(icon, {});
|
||||
yield shown;
|
||||
let notification = popup.childNodes[0];
|
||||
let checkbox = notification.checkbox;
|
||||
checkCheckbox(checkbox, "This is a checkbox", true);
|
||||
|
@ -101,8 +101,8 @@ function* runNextTest() {
|
||||
});
|
||||
onPopupEvent("popuphidden", function() {
|
||||
info("[" + nextTest.id + "] popup hidden");
|
||||
nextTest.onHidden(this);
|
||||
goNext();
|
||||
Task.spawn(() => nextTest.onHidden(this))
|
||||
.then(() => goNext(), ex => Assert.ok(false, "onHidden failed: " + ex));
|
||||
}, () => shownState);
|
||||
info("[" + nextTest.id + "] added listeners; panel is open: " + PopupNotifications.isPanelOpen);
|
||||
}
|
||||
|
@ -6,9 +6,7 @@ support-files =
|
||||
head.js
|
||||
|
||||
[browser_referrer_middle_click.js]
|
||||
skip-if = true # Bug 1315042
|
||||
[browser_referrer_middle_click_in_container.js]
|
||||
skip-if = true # Bug 1315042
|
||||
[browser_referrer_open_link_in_private.js]
|
||||
skip-if = os == 'linux' # Bug 1145199
|
||||
[browser_referrer_open_link_in_tab.js]
|
||||
|
@ -5,9 +5,10 @@ function startMiddleClickTestCase(aTestNumber) {
|
||||
info("browser_referrer_middle_click: " +
|
||||
getReferrerTestDescription(aTestNumber));
|
||||
someTabLoaded(gTestWindow).then(function(aNewTab) {
|
||||
gTestWindow.gBrowser.selectedTab = aNewTab;
|
||||
checkReferrerAndStartNextTest(aTestNumber, null, aNewTab,
|
||||
startMiddleClickTestCase);
|
||||
BrowserTestUtils.switchTab(gTestWindow.gBrowser, aNewTab).then(() => {
|
||||
checkReferrerAndStartNextTest(aTestNumber, null, aNewTab,
|
||||
startMiddleClickTestCase);
|
||||
});
|
||||
});
|
||||
|
||||
clickTheLink(gTestWindow, "testlink", {button: 1});
|
||||
|
@ -5,10 +5,11 @@ function startMiddleClickTestCase(aTestNumber) {
|
||||
info("browser_referrer_middle_click: " +
|
||||
getReferrerTestDescription(aTestNumber));
|
||||
someTabLoaded(gTestWindow).then(function(aNewTab) {
|
||||
gTestWindow.gBrowser.selectedTab = aNewTab;
|
||||
checkReferrerAndStartNextTest(aTestNumber, null, aNewTab,
|
||||
startMiddleClickTestCase,
|
||||
{ userContextId: 3 });
|
||||
BrowserTestUtils.switchTab(gTestWindow.gBrowser, aNewTab).then(() => {
|
||||
checkReferrerAndStartNextTest(aTestNumber, null, aNewTab,
|
||||
startMiddleClickTestCase,
|
||||
{ userContextId: 3 });
|
||||
});
|
||||
});
|
||||
|
||||
clickTheLink(gTestWindow, "testlink", {button: 1});
|
||||
|
@ -142,6 +142,9 @@ function delayedStartupFinished(aWindow) {
|
||||
function someTabLoaded(aWindow) {
|
||||
return new Promise(function(resolve) {
|
||||
aWindow.gBrowser.addEventListener("load", function onLoad(aEvent) {
|
||||
if (aWindow.location === "about:blank") {
|
||||
return;
|
||||
}
|
||||
let tab = aWindow.gBrowser._getTabForContentWindow(
|
||||
aEvent.target.defaultView.top);
|
||||
if (tab) {
|
||||
|
@ -541,28 +541,32 @@ Cookies.prototype = {
|
||||
},
|
||||
|
||||
_readCookieFile(aFile, aCallback) {
|
||||
let fileReader = new FileReader();
|
||||
let onLoadEnd = () => {
|
||||
fileReader.removeEventListener("loadend", onLoadEnd);
|
||||
File.createFromNsIFile(aFile).then(aFile => {
|
||||
let fileReader = new FileReader();
|
||||
let onLoadEnd = () => {
|
||||
fileReader.removeEventListener("loadend", onLoadEnd);
|
||||
|
||||
if (fileReader.readyState != fileReader.DONE) {
|
||||
Cu.reportError("Could not read cookie contents: " + fileReader.error);
|
||||
aCallback(false);
|
||||
return;
|
||||
}
|
||||
if (fileReader.readyState != fileReader.DONE) {
|
||||
Cu.reportError("Could not read cookie contents: " + fileReader.error);
|
||||
aCallback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let success = true;
|
||||
try {
|
||||
this._parseCookieBuffer(fileReader.result);
|
||||
} catch (ex) {
|
||||
Components.utils.reportError("Unable to migrate cookie: " + ex);
|
||||
success = false;
|
||||
} finally {
|
||||
aCallback(success);
|
||||
}
|
||||
};
|
||||
fileReader.addEventListener("loadend", onLoadEnd);
|
||||
fileReader.readAsText(File.createFromNsIFile(aFile));
|
||||
let success = true;
|
||||
try {
|
||||
this._parseCookieBuffer(fileReader.result);
|
||||
} catch (ex) {
|
||||
Components.utils.reportError("Unable to migrate cookie: " + ex);
|
||||
success = false;
|
||||
} finally {
|
||||
aCallback(success);
|
||||
}
|
||||
};
|
||||
fileReader.addEventListener("loadend", onLoadEnd);
|
||||
fileReader.readAsText(aFile);
|
||||
}, () => {
|
||||
aCallback(false);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -12,6 +12,7 @@ Services.scriptloader.loadSubScript("resource://testing-common/sinon-1.16.1.js")
|
||||
const TEST_HOST = "example.com";
|
||||
const TEST_ORIGIN = "http://" + TEST_HOST;
|
||||
const TEST_BASE_URL = TEST_ORIGIN + "/browser/browser/components/preferences/in-content/tests/";
|
||||
const REMOVE_DIALOG_URL = "chrome://browser/content/preferences/siteDataRemoveSelected.xul";
|
||||
|
||||
const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
|
||||
const { SiteDataManager } = Cu.import("resource:///modules/SiteDataManager.jsm", {});
|
||||
@ -187,6 +188,22 @@ function promiseCookiesCleared() {
|
||||
});
|
||||
}
|
||||
|
||||
function assertSitesListed(doc, origins) {
|
||||
let frameDoc = doc.getElementById("dialogFrame").contentDocument;
|
||||
let removeBtn = frameDoc.getElementById("removeSelected");
|
||||
let removeAllBtn = frameDoc.getElementById("removeAll");
|
||||
let sitesList = frameDoc.getElementById("sitesList");
|
||||
let totalSitesNumber = sitesList.getElementsByTagName("richlistitem").length;
|
||||
is(totalSitesNumber, origins.length, "Should list the right sites number");
|
||||
origins.forEach(origin => {
|
||||
let site = sitesList.querySelector(`richlistitem[data-origin="${origin}"]`);
|
||||
let host = site.getAttribute("host");
|
||||
ok(origin.includes(host), `Should list the site of ${origin}`);
|
||||
});
|
||||
is(removeBtn.disabled, false, "Should enable the removeSelected button");
|
||||
is(removeAllBtn.disabled, false, "Should enable the removeAllBtn button");
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
delete window.sinon;
|
||||
delete window.setImmediate;
|
||||
@ -370,28 +387,18 @@ add_task(function* () {
|
||||
|
||||
searchBox.value = "xyz";
|
||||
searchBox.doCommand();
|
||||
assertSitesListed(mockOrigins.filter(o => o.includes("xyz")));
|
||||
assertSitesListed(doc, mockOrigins.filter(o => o.includes("xyz")));
|
||||
|
||||
searchBox.value = "bar";
|
||||
searchBox.doCommand();
|
||||
assertSitesListed(mockOrigins.filter(o => o.includes("bar")));
|
||||
assertSitesListed(doc, mockOrigins.filter(o => o.includes("bar")));
|
||||
|
||||
searchBox.value = "";
|
||||
searchBox.doCommand();
|
||||
assertSitesListed(mockOrigins);
|
||||
assertSitesListed(doc, mockOrigins);
|
||||
|
||||
mockSiteDataManager.unregister();
|
||||
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
|
||||
function assertSitesListed(origins) {
|
||||
let sitesList = frameDoc.getElementById("sitesList");
|
||||
let totalSitesNumber = sitesList.getElementsByTagName("richlistitem").length;
|
||||
is(totalSitesNumber, origins.length, "Should list the right sites number");
|
||||
origins.forEach(origin => {
|
||||
let site = sitesList.querySelector(`richlistitem[data-origin="${origin}"]`);
|
||||
ok(site instanceof XULElement, `Should list the site of ${origin}`);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Test selecting and removing all sites one by one
|
||||
@ -478,19 +485,23 @@ add_task(function* () {
|
||||
function assertAllSitesListed() {
|
||||
frameDoc = doc.getElementById("dialogFrame").contentDocument;
|
||||
let removeBtn = frameDoc.getElementById("removeSelected");
|
||||
let removeAllBtn = frameDoc.getElementById("removeAll");
|
||||
let sitesList = frameDoc.getElementById("sitesList");
|
||||
let sites = sitesList.getElementsByTagName("richlistitem");
|
||||
is(sites.length, fakeOrigins.length, "Should list all sites");
|
||||
is(removeBtn.disabled, false, "Should enable the removeSelected button");
|
||||
is(removeAllBtn.disabled, false, "Should enable the removeAllBtn button");
|
||||
}
|
||||
|
||||
function assertAllSitesNotListed() {
|
||||
frameDoc = doc.getElementById("dialogFrame").contentDocument;
|
||||
let removeBtn = frameDoc.getElementById("removeSelected");
|
||||
let removeAllBtn = frameDoc.getElementById("removeAll");
|
||||
let sitesList = frameDoc.getElementById("sitesList");
|
||||
let sites = sitesList.getElementsByTagName("richlistitem");
|
||||
is(sites.length, 0, "Should not list all sites");
|
||||
is(removeBtn.disabled, true, "Should disable the removeSelected button");
|
||||
is(removeAllBtn.disabled, true, "Should disable the removeAllBtn button");
|
||||
}
|
||||
});
|
||||
|
||||
@ -512,7 +523,6 @@ add_task(function* () {
|
||||
yield updatePromise;
|
||||
yield openSettingsDialog();
|
||||
|
||||
const removeDialogURL = "chrome://browser/content/preferences/siteDataRemoveSelected.xul";
|
||||
let doc = gBrowser.selectedBrowser.contentDocument;
|
||||
let frameDoc = null;
|
||||
let saveBtn = null;
|
||||
@ -521,44 +531,44 @@ add_task(function* () {
|
||||
let settingsDialogClosePromise = null;
|
||||
|
||||
// Test the initial state
|
||||
assertSitesListed(fakeOrigins);
|
||||
assertSitesListed(doc, fakeOrigins);
|
||||
|
||||
// Test the "Cancel" button
|
||||
settingsDialogClosePromise = promiseSettingsDialogClose();
|
||||
frameDoc = doc.getElementById("dialogFrame").contentDocument;
|
||||
cancelBtn = frameDoc.getElementById("cancel");
|
||||
removeSelectedSite(fakeOrigins.slice(0, 4));
|
||||
assertSitesListed(fakeOrigins.slice(4));
|
||||
assertSitesListed(doc, fakeOrigins.slice(4));
|
||||
cancelBtn.doCommand();
|
||||
yield settingsDialogClosePromise;
|
||||
yield openSettingsDialog();
|
||||
assertSitesListed(fakeOrigins);
|
||||
assertSitesListed(doc, fakeOrigins);
|
||||
|
||||
// Test the "Save Changes" button but canceling save
|
||||
removeDialogOpenPromise = promiseWindowDialogOpen("cancel", removeDialogURL);
|
||||
removeDialogOpenPromise = promiseWindowDialogOpen("cancel", REMOVE_DIALOG_URL);
|
||||
settingsDialogClosePromise = promiseSettingsDialogClose();
|
||||
frameDoc = doc.getElementById("dialogFrame").contentDocument;
|
||||
saveBtn = frameDoc.getElementById("save");
|
||||
removeSelectedSite(fakeOrigins.slice(0, 4));
|
||||
assertSitesListed(fakeOrigins.slice(4));
|
||||
assertSitesListed(doc, fakeOrigins.slice(4));
|
||||
saveBtn.doCommand();
|
||||
yield removeDialogOpenPromise;
|
||||
yield settingsDialogClosePromise;
|
||||
yield openSettingsDialog();
|
||||
assertSitesListed(fakeOrigins);
|
||||
assertSitesListed(doc, fakeOrigins);
|
||||
|
||||
// Test the "Save Changes" button and accepting save
|
||||
removeDialogOpenPromise = promiseWindowDialogOpen("accept", removeDialogURL);
|
||||
removeDialogOpenPromise = promiseWindowDialogOpen("accept", REMOVE_DIALOG_URL);
|
||||
settingsDialogClosePromise = promiseSettingsDialogClose();
|
||||
frameDoc = doc.getElementById("dialogFrame").contentDocument;
|
||||
saveBtn = frameDoc.getElementById("save");
|
||||
removeSelectedSite(fakeOrigins.slice(0, 4));
|
||||
assertSitesListed(fakeOrigins.slice(4));
|
||||
assertSitesListed(doc, fakeOrigins.slice(4));
|
||||
saveBtn.doCommand();
|
||||
yield removeDialogOpenPromise;
|
||||
yield settingsDialogClosePromise;
|
||||
yield openSettingsDialog();
|
||||
assertSitesListed(fakeOrigins.slice(4));
|
||||
assertSitesListed(doc, fakeOrigins.slice(4));
|
||||
|
||||
// Always clean up the fake origins
|
||||
fakeOrigins.forEach(origin => removePersistentStoragePerm(origin));
|
||||
@ -578,17 +588,48 @@ add_task(function* () {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function assertSitesListed(origins) {
|
||||
frameDoc = doc.getElementById("dialogFrame").contentDocument;
|
||||
let removeBtn = frameDoc.getElementById("removeSelected");
|
||||
let sitesList = frameDoc.getElementById("sitesList");
|
||||
let totalSitesNumber = sitesList.getElementsByTagName("richlistitem").length;
|
||||
is(totalSitesNumber, origins.length, "Should list the right sites number");
|
||||
origins.forEach(origin => {
|
||||
let site = sitesList.querySelector(`richlistitem[data-origin="${origin}"]`);
|
||||
ok(!!site, `Should list the site of ${origin}`);
|
||||
});
|
||||
is(removeBtn.disabled, false, "Should enable the removeSelected button");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* () {
|
||||
yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
|
||||
let fakeOrigins = [
|
||||
"https://news.foo.com/",
|
||||
"https://books.foo.com/",
|
||||
"https://mails.bar.com/",
|
||||
"https://account.bar.com/",
|
||||
"https://videos.xyz.com/",
|
||||
"https://shopping.xyz.com/"
|
||||
];
|
||||
fakeOrigins.forEach(origin => addPersistentStoragePerm(origin));
|
||||
|
||||
let updatePromise = promiseSitesUpdated();
|
||||
yield openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
|
||||
yield updatePromise;
|
||||
yield openSettingsDialog();
|
||||
|
||||
// Search "foo" to only list foo.com sites
|
||||
let doc = gBrowser.selectedBrowser.contentDocument;
|
||||
let frameDoc = doc.getElementById("dialogFrame").contentDocument;
|
||||
let searchBox = frameDoc.getElementById("searchBox");
|
||||
searchBox.value = "foo";
|
||||
searchBox.doCommand();
|
||||
assertSitesListed(doc, fakeOrigins.slice(0, 2));
|
||||
|
||||
// Test only removing all visible sites listed
|
||||
updatePromise = promiseSitesUpdated();
|
||||
let acceptRemovePromise = promiseWindowDialogOpen("accept", REMOVE_DIALOG_URL);
|
||||
let settingsDialogClosePromise = promiseSettingsDialogClose();
|
||||
let removeAllBtn = frameDoc.getElementById("removeAll");
|
||||
let saveBtn = frameDoc.getElementById("save");
|
||||
removeAllBtn.doCommand();
|
||||
saveBtn.doCommand();
|
||||
yield acceptRemovePromise;
|
||||
yield settingsDialogClosePromise;
|
||||
yield updatePromise;
|
||||
yield openSettingsDialog();
|
||||
assertSitesListed(doc, fakeOrigins.slice(2));
|
||||
|
||||
// Always clean up the fake origins
|
||||
fakeOrigins.forEach(origin => removePersistentStoragePerm(origin));
|
||||
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
});
|
||||
|
@ -40,23 +40,25 @@ let gSiteDataSettings = {
|
||||
let sortCol = document.getElementById("hostCol");
|
||||
this._sortSites(this._sites, sortCol);
|
||||
this._buildSitesList(this._sites);
|
||||
this._updateButtonsState();
|
||||
Services.obs.notifyObservers(null, "sitedata-settings-init", null);
|
||||
});
|
||||
|
||||
setEventListener("hostCol", "click", this.onClickTreeCol);
|
||||
setEventListener("usageCol", "click", this.onClickTreeCol);
|
||||
setEventListener("statusCol", "click", this.onClickTreeCol);
|
||||
setEventListener("searchBox", "command", this.onCommandSearch);
|
||||
setEventListener("cancel", "command", this.close);
|
||||
setEventListener("save", "command", this.saveChanges);
|
||||
setEventListener("removeSelected", "command", this.removeSelected);
|
||||
setEventListener("searchBox", "command", this.onCommandSearch);
|
||||
setEventListener("removeAll", "command", this.onClickRemoveAll);
|
||||
setEventListener("removeSelected", "command", this.onClickRemoveSelected);
|
||||
},
|
||||
|
||||
_updateButtonsState() {
|
||||
let items = this._list.getElementsByTagName("richlistitem");
|
||||
let removeBtn = document.getElementById("removeSelected");
|
||||
removeBtn.disabled = !(items.length > 0);
|
||||
let removeSelectedBtn = document.getElementById("removeSelected");
|
||||
let removeAllBtn = document.getElementById("removeAll");
|
||||
removeSelectedBtn.disabled = items.length == 0;
|
||||
removeAllBtn.disabled = removeSelectedBtn.disabled;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -136,30 +138,22 @@ let gSiteDataSettings = {
|
||||
item.setAttribute("usage", prefStrBundle.getFormattedString("siteUsage", size));
|
||||
this._list.appendChild(item);
|
||||
}
|
||||
this._updateButtonsState();
|
||||
},
|
||||
|
||||
onClickTreeCol(e) {
|
||||
this._sortSites(this._sites, e.target);
|
||||
this._buildSitesList(this._sites);
|
||||
},
|
||||
|
||||
onCommandSearch() {
|
||||
this._buildSitesList(this._sites);
|
||||
},
|
||||
|
||||
removeSelected() {
|
||||
let selected = this._list.selectedItem;
|
||||
if (selected) {
|
||||
let origin = selected.getAttribute("data-origin");
|
||||
_removeSiteItems(items) {
|
||||
for (let i = items.length - 1; i >= 0; --i) {
|
||||
let item = items[i];
|
||||
let origin = item.getAttribute("data-origin");
|
||||
for (let site of this._sites) {
|
||||
if (site.uri.spec === origin) {
|
||||
site.userAction = "remove";
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._list.removeChild(selected);
|
||||
this._updateButtonsState();
|
||||
item.remove();
|
||||
}
|
||||
this._updateButtonsState();
|
||||
},
|
||||
|
||||
saveChanges() {
|
||||
@ -235,5 +229,28 @@ let gSiteDataSettings = {
|
||||
|
||||
close() {
|
||||
window.close();
|
||||
},
|
||||
|
||||
onClickTreeCol(e) {
|
||||
this._sortSites(this._sites, e.target);
|
||||
this._buildSitesList(this._sites);
|
||||
},
|
||||
|
||||
onCommandSearch() {
|
||||
this._buildSitesList(this._sites);
|
||||
},
|
||||
|
||||
onClickRemoveSelected() {
|
||||
let selected = this._list.selectedItem;
|
||||
if (selected) {
|
||||
this._removeSiteItems([selected]);
|
||||
}
|
||||
},
|
||||
|
||||
onClickRemoveAll() {
|
||||
let siteItems = this._list.getElementsByTagName("richlistitem");
|
||||
if (siteItems.length > 0) {
|
||||
this._removeSiteItems(siteItems);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -44,6 +44,7 @@
|
||||
|
||||
<hbox align="start">
|
||||
<button id="removeSelected" label="&removeSelected.label;" accesskey="&removeSelected.accesskey;"/>
|
||||
<button id="removeAll" label="&removeAll.label;" accesskey="&removeAll.accesskey;"/>
|
||||
</hbox>
|
||||
|
||||
<vbox align="end">
|
||||
|
@ -1561,10 +1561,22 @@
|
||||
if (rowCount == 1 && hasDummyItems) {
|
||||
// When there's only one row, make the compact settings button
|
||||
// hug the right edge of the panel. It may not due to the panel's
|
||||
// width not being an integral factor of the button width. (See
|
||||
// width not being an integral multiple of the button width. (See
|
||||
// the "There will be an emtpy area" comment above.) Increase the
|
||||
// width of the last dummy item by the remainder.
|
||||
//
|
||||
// There's one weird thing to guard against. When layout pixels
|
||||
// aren't an integral multiple of device pixels, the calculated
|
||||
// remainder can end up being ~1px too big, at least on Windows,
|
||||
// which pushes the settings button to a new row. The remainder
|
||||
// is integral, not a fraction, so that's not the problem. To
|
||||
// work around that, unscale the remainder, floor it, scale it
|
||||
// back, and then floor that.
|
||||
let scale = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.screenPixelsPerCSSPixel;
|
||||
let remainder = panelWidth - (enginesPerRow * buttonWidth);
|
||||
remainder = Math.floor(Math.floor(remainder * scale) / scale);
|
||||
let width = remainder + buttonWidth;
|
||||
let lastDummyItem = this.settingsButton.previousSibling;
|
||||
lastDummyItem.setAttribute("width", width);
|
||||
|
@ -27,9 +27,12 @@ skip-if = os == "mac" # bug 967013
|
||||
[browser_ddg.js]
|
||||
[browser_ddg_behavior.js]
|
||||
[browser_google.js]
|
||||
skip-if = artifact # bug 1315953
|
||||
[browser_google_codes.js]
|
||||
skip-if = artifact # bug 1315953
|
||||
[browser_google_nocodes.js]
|
||||
[browser_google_behavior.js]
|
||||
skip-if = artifact # bug 1315953
|
||||
[browser_healthreport.js]
|
||||
[browser_hiddenOneOffs_cleanup.js]
|
||||
[browser_hiddenOneOffs_diacritics.js]
|
||||
|
3
browser/config/mozconfigs/linux32/debug-qr
Normal file
3
browser/config/mozconfigs/linux32/debug-qr
Normal file
@ -0,0 +1,3 @@
|
||||
. "$topsrcdir/browser/config/mozconfigs/linux32/debug"
|
||||
|
||||
ac_add_options --enable-webrender
|
3
browser/config/mozconfigs/linux32/opt-qr
Normal file
3
browser/config/mozconfigs/linux32/opt-qr
Normal file
@ -0,0 +1,3 @@
|
||||
. "$topsrcdir/browser/config/mozconfigs/linux32/nightly"
|
||||
|
||||
ac_add_options --enable-webrender
|
3
browser/config/mozconfigs/win32/debug-qr
Normal file
3
browser/config/mozconfigs/win32/debug-qr
Normal file
@ -0,0 +1,3 @@
|
||||
. "$topsrcdir/browser/config/mozconfigs/win32/debug"
|
||||
|
||||
ac_add_options --enable-webrender
|
3
browser/config/mozconfigs/win32/opt-qr
Normal file
3
browser/config/mozconfigs/win32/opt-qr
Normal file
@ -0,0 +1,3 @@
|
||||
. "$topsrcdir/browser/config/mozconfigs/win32/nightly"
|
||||
|
||||
ac_add_options --enable-webrender
|
@ -1,10 +0,0 @@
|
||||
[
|
||||
{
|
||||
"version": "clang 3.8.0, libgcc 4.8.5",
|
||||
"size": 140319580,
|
||||
"digest": "34e219d7e8eaffa81710631c34d21355563d06335b3c00851e94c1f42f9098788fded8463dd0f67dd699f77b47a0245dd7aff754943a7a03fb5fd145a808254f",
|
||||
"algorithm": "sha512",
|
||||
"filename": "clang.tar.xz",
|
||||
"unpack": true
|
||||
}
|
||||
]
|
@ -1,19 +1,19 @@
|
||||
[
|
||||
{
|
||||
"version": "clang 3.8.0, libgcc 4.8.5",
|
||||
"size": 140319580,
|
||||
"digest": "34e219d7e8eaffa81710631c34d21355563d06335b3c00851e94c1f42f9098788fded8463dd0f67dd699f77b47a0245dd7aff754943a7a03fb5fd145a808254f",
|
||||
"version": "clang 3.9.0",
|
||||
"size": 168062128,
|
||||
"digest": "2a5458a25792fcade86a56ff0f4acdfa284d2b62966991a7c34a92c2e8c0b4a162ce00512d4467754e7f74598d64c56e91517e1606ed3fba011f7c10e8ad3288",
|
||||
"algorithm": "sha512",
|
||||
"filename": "clang.tar.xz",
|
||||
"unpack": true
|
||||
},
|
||||
{
|
||||
"size": 3008804,
|
||||
"size": 6075028,
|
||||
"visibility": "public",
|
||||
"digest": "ba6937f14f3d8b26dcb2d39490dee6b0a8afb60f672f5debb71d7b62c1ec52103201b4b1a3d258f945567de531384b36ddb2ce4aa73dc63d72305b11c146847c",
|
||||
"digest": "0b962ba55a5a2fbae44218683bdf6ea0dfe8165aba436173a065f7190976184586b9acf4c23478bc5b6d81a3e00f681bf16df0536c9c9718ad0570d064f69027",
|
||||
"algorithm": "sha512",
|
||||
"unpack": true,
|
||||
"filename": "cctools.tar.gz"
|
||||
"filename": "cctools.tar.xz"
|
||||
},
|
||||
{
|
||||
"size": 35215976,
|
||||
@ -41,19 +41,19 @@
|
||||
"filename": "dmg.tar.xz"
|
||||
},
|
||||
{
|
||||
"size": 188880,
|
||||
"visibility": "public",
|
||||
"digest": "1ffddd43efb03aed897ee42035d9d8d758a8d66ab6c867599ef755e1a586768fc22011ce03698af61454920b00fe8bed08c9a681e7bd324d7f8f78c026c83943",
|
||||
"algorithm": "sha512",
|
||||
"unpack": true,
|
||||
"filename": "genisoimage.tar.xz"
|
||||
},
|
||||
{
|
||||
"version": "rustc 1.14.0 (e8a012324 2016-12-16) repack",
|
||||
"size": 152573516,
|
||||
"digest": "eef2f10bf57005d11c34b9b49eb76d0f09d026295055039fea89952a3be51580abdab29366278ed4bfa393b33c5cee4d51d3af4221e9e7d7d833d0fc1347597c",
|
||||
"algorithm": "sha512",
|
||||
"filename": "rustc.tar.xz",
|
||||
"unpack": true
|
||||
},
|
||||
{
|
||||
"size": 281576,
|
||||
"visibility": "public",
|
||||
"digest": "71616564533d138fb12f08e761c2638d054814fdf9c9439638ec57b201e100445c364d73d8d7a4f0e3b784898d5fe6264e8242863fc5ac40163f1791468bbc46",
|
||||
"algorithm": "sha512",
|
||||
"filename": "hfsplus-tools.tar.xz",
|
||||
"unpack": true
|
||||
}
|
||||
]
|
||||
|
@ -0,0 +1,89 @@
|
||||
'use strict';
|
||||
|
||||
class PasswordPrompt {
|
||||
/**
|
||||
* @constructs PasswordPrompt
|
||||
*/
|
||||
constructor(viewport) {
|
||||
this.overlayName = 'passwordOverlay';
|
||||
this.container = document.getElementById('passwordOverlay');
|
||||
this.overlaysParent = document.getElementById('overlayContainer');
|
||||
this.label = document.getElementById('passwordText');
|
||||
this.input = document.getElementById('password');
|
||||
this.submitButton = document.getElementById('passwordSubmit');
|
||||
this.cancelButton = document.getElementById('passwordCancel');
|
||||
|
||||
this.updateCallback = null;
|
||||
this.reason = null;
|
||||
this.active = false;
|
||||
this._viewport = viewport;
|
||||
// PDFium doesn't return the result of password check. We count the retries
|
||||
// to determine whether to show "invalid password" prompt instead.
|
||||
// PDFium allows at most 3 times of tries.
|
||||
this._passwordTries = 0;
|
||||
this._viewport.onPasswordRequest = this.open.bind(this);
|
||||
|
||||
// Attach the event listeners.
|
||||
this.submitButton.addEventListener('click', this);
|
||||
this.cancelButton.addEventListener('click', this);
|
||||
this.input.addEventListener('keydown', this);
|
||||
}
|
||||
|
||||
handleEvent(e) {
|
||||
switch(e.type) {
|
||||
case 'keydown':
|
||||
if (e.target == this.input && e.keyCode === KeyEvent.DOM_VK_RETURN) {
|
||||
this.verify();
|
||||
e.stopPropagation();
|
||||
} else if (e.currentTarget == window &&
|
||||
e.keyCode === KeyEvent.DOM_VK_ESCAPE) {
|
||||
this.close();
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
break;
|
||||
case 'click':
|
||||
if (e.target == this.submitButton) {
|
||||
this.verify();
|
||||
} else if (e.target == this.cancelButton) {
|
||||
this.close();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
open() {
|
||||
this.container.classList.remove('hidden');
|
||||
this.overlaysParent.classList.remove('hidden');
|
||||
window.addEventListener('keydown', this);
|
||||
this.active = true;
|
||||
let promptKey = this._passwordTries ? 'password_invalid' : 'password_label';
|
||||
this._passwordTries++;
|
||||
|
||||
this.input.type = 'password';
|
||||
this.input.focus();
|
||||
|
||||
document.l10n.formatValue(promptKey).then(promptString => {
|
||||
this.label.textContent = promptString;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
close() {
|
||||
this.container.classList.add('hidden');
|
||||
this.overlaysParent.classList.add('hidden');
|
||||
window.removeEventListener('keydown', this);
|
||||
this.active = false;
|
||||
|
||||
this.input.value = '';
|
||||
this.input.type = '';
|
||||
}
|
||||
|
||||
verify() {
|
||||
let password = this.input.value;
|
||||
if (password && password.length > 0) {
|
||||
this.close();
|
||||
this._viewport.verifyPassword(password);
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
let viewport = new Viewport();
|
||||
let toolbar = new Toolbar(viewport);
|
||||
let passwordPrompt = new PasswordPrompt(viewport);
|
||||
|
||||
// Expose the custom viewport object to runtime
|
||||
window.createCustomViewport = function(actionHandler) {
|
||||
|
@ -36,6 +36,7 @@ class Viewport {
|
||||
this.onZoomChanged = null;
|
||||
this.onDimensionChanged = null;
|
||||
this.onPageChanged = null;
|
||||
this.onPasswordRequest = null;
|
||||
|
||||
this._viewportController.addEventListener('scroll', this);
|
||||
window.addEventListener('resize', this);
|
||||
@ -123,14 +124,14 @@ class Viewport {
|
||||
}
|
||||
|
||||
_getScrollbarWidth() {
|
||||
var div = document.createElement('div');
|
||||
let div = document.createElement('div');
|
||||
div.style.visibility = 'hidden';
|
||||
div.style.overflow = 'scroll';
|
||||
div.style.width = '50px';
|
||||
div.style.height = '50px';
|
||||
div.style.position = 'absolute';
|
||||
document.body.appendChild(div);
|
||||
var result = div.offsetWidth - div.clientWidth;
|
||||
let result = div.offsetWidth - div.clientWidth;
|
||||
div.remove();
|
||||
return result;
|
||||
}
|
||||
@ -468,6 +469,13 @@ class Viewport {
|
||||
}
|
||||
}
|
||||
|
||||
verifyPassword(password) {
|
||||
this._doAction({
|
||||
type: 'getPasswordComplete',
|
||||
password: password
|
||||
});
|
||||
}
|
||||
|
||||
handleEvent(evt) {
|
||||
switch(evt.type) {
|
||||
case 'resize':
|
||||
@ -591,6 +599,9 @@ class Viewport {
|
||||
case 'fullscreenChange':
|
||||
this._handleFullscreenChange(message.fullscreen);
|
||||
break;
|
||||
case 'getPassword':
|
||||
this.onPasswordRequest && this.onPasswordRequest();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
<script src="js/polyfill.js"></script>
|
||||
<script src="js/toolbar.js"></script>
|
||||
<script src="js/viewport.js"></script>
|
||||
<script src="js/password-prompt.js"></script>
|
||||
<script src="js/viewer.js"></script>
|
||||
</head>
|
||||
|
||||
|
@ -7,6 +7,14 @@ const {utils: Cu} = Components;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LogManager",
|
||||
"resource://shield-recipe-client/lib/LogManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RecipeRunner",
|
||||
"resource://shield-recipe-client/lib/RecipeRunner.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "CleanupManager",
|
||||
"resource://shield-recipe-client/lib/CleanupManager.jsm");
|
||||
|
||||
const REASONS = {
|
||||
APP_STARTUP: 1, // The application is starting up.
|
||||
@ -16,7 +24,7 @@ const REASONS = {
|
||||
ADDON_INSTALL: 5, // The add-on is being installed.
|
||||
ADDON_UNINSTALL: 6, // The add-on is being uninstalled.
|
||||
ADDON_UPGRADE: 7, // The add-on is being upgraded.
|
||||
ADDON_DOWNGRADE: 8, //The add-on is being downgraded.
|
||||
ADDON_DOWNGRADE: 8, // The add-on is being downgraded.
|
||||
};
|
||||
|
||||
const PREF_BRANCH = "extensions.shield-recipe-client.";
|
||||
@ -53,19 +61,15 @@ this.startup = function() {
|
||||
}
|
||||
|
||||
// Setup logging and listen for changes to logging prefs
|
||||
Cu.import("resource://shield-recipe-client/lib/LogManager.jsm");
|
||||
LogManager.configure(Services.prefs.getIntPref(PREF_LOGGING_LEVEL));
|
||||
Preferences.observe(PREF_LOGGING_LEVEL, LogManager.configure);
|
||||
CleanupManager.addCleanupHandler(
|
||||
() => Preferences.ignore(PREF_LOGGING_LEVEL, LogManager.configure));
|
||||
|
||||
Cu.import("resource://shield-recipe-client/lib/RecipeRunner.jsm");
|
||||
RecipeRunner.init();
|
||||
};
|
||||
|
||||
this.shutdown = function(data, reason) {
|
||||
Preferences.ignore(PREF_LOGGING_LEVEL, LogManager.configure);
|
||||
|
||||
Cu.import("resource://shield-recipe-client/lib/CleanupManager.jsm");
|
||||
|
||||
CleanupManager.cleanup();
|
||||
|
||||
if (reason === REASONS.ADDON_DISABLE || reason === REASONS.ADDON_UNINSTALL) {
|
||||
|
@ -5,7 +5,6 @@
|
||||
"use strict";
|
||||
|
||||
const {utils: Cu} = Components;
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["CleanupManager"];
|
||||
|
||||
const cleanupHandlers = new Set();
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/TelemetryArchive.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
@ -74,14 +75,16 @@ this.EnvExpressions = {
|
||||
|
||||
eval(expr, extraContext = {}) {
|
||||
// First clone the extra context
|
||||
const context = Object.assign({}, extraContext);
|
||||
const context = Object.assign({normandy: {}}, extraContext);
|
||||
// jexl handles promises, so it is fine to include them in this data.
|
||||
context.telemetry = EnvExpressions.getLatestTelemetry();
|
||||
context.normandy = context.normandy || {};
|
||||
context.normandy.userId = EnvExpressions.getUserId();
|
||||
|
||||
context.normandy = Object.assign(context.normandy, {
|
||||
userId: EnvExpressions.getUserId(),
|
||||
distribution: Preferences.get("distribution.id", "default"),
|
||||
});
|
||||
|
||||
const onelineExpr = expr.replace(/[\t\n\r]/g, " ");
|
||||
|
||||
return jexl.eval(onelineExpr, context);
|
||||
},
|
||||
};
|
||||
|
@ -9,7 +9,7 @@ Cu.import("resource://gre/modules/Log.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["LogManager"];
|
||||
|
||||
const ROOT_LOGGER_NAME = "extensions.shield-recipe-client"
|
||||
const ROOT_LOGGER_NAME = "extensions.shield-recipe-client";
|
||||
let rootLogger = null;
|
||||
|
||||
this.LogManager = {
|
||||
|
@ -75,8 +75,12 @@ this.NormandyDriver = function(sandboxManager, extraContext = {}) {
|
||||
isDefaultBrowser: ShellService.isDefaultBrowser() || null,
|
||||
searchEngine: null,
|
||||
syncSetup: Preferences.isSet("services.sync.username"),
|
||||
syncDesktopDevices: Preferences.get("services.sync.clients.devices.desktop", 0),
|
||||
syncMobileDevices: Preferences.get("services.sync.clients.devices.mobile", 0),
|
||||
syncTotalDevices: Preferences.get("services.sync.numClients", 0),
|
||||
plugins: {},
|
||||
doNotTrack: Preferences.get("privacy.donottrackheader.enabled", false),
|
||||
distribution: Preferences.get("distribution.id", "default"),
|
||||
};
|
||||
|
||||
const searchEnginePromise = new Promise(resolve => {
|
||||
|
@ -82,4 +82,13 @@ add_task(function* () {
|
||||
"[normandy.userId, normandy.injectedValue]",
|
||||
{normandy: {injectedValue: "injected"}});
|
||||
Assert.deepEqual(val, ["fake id", "injected"], "context is correctly merged");
|
||||
|
||||
// distribution id defaults to "default"
|
||||
val = yield EnvExpressions.eval("normandy.distribution");
|
||||
Assert.equal(val, "default", "distribution has a default value");
|
||||
|
||||
// distribution id is in the context
|
||||
yield SpecialPowers.pushPrefEnv({set: [["distribution.id", "funnelcake"]]});
|
||||
val = yield EnvExpressions.eval("normandy.distribution");
|
||||
Assert.equal(val, "funnelcake", "distribution is read from preferences");
|
||||
});
|
||||
|
@ -18,3 +18,32 @@ add_task(Utils.withDriver(Assert, function* userId(driver) {
|
||||
// Test that userId is a UUID
|
||||
ok(Utils.UUID_REGEX.test(driver.userId), "userId is a uuid");
|
||||
}));
|
||||
|
||||
add_task(Utils.withDriver(Assert, function* syncDeviceCounts(driver) {
|
||||
let client = yield driver.client();
|
||||
is(client.syncMobileDevices, 0, "syncMobileDevices defaults to zero");
|
||||
is(client.syncDesktopDevices, 0, "syncDesktopDevices defaults to zero");
|
||||
is(client.syncTotalDevices, 0, "syncTotalDevices defaults to zero");
|
||||
|
||||
yield SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["services.sync.numClients", 9],
|
||||
["services.sync.clients.devices.mobile", 5],
|
||||
["services.sync.clients.devices.desktop", 4],
|
||||
],
|
||||
});
|
||||
|
||||
client = yield driver.client();
|
||||
is(client.syncMobileDevices, 5, "syncMobileDevices is read when set");
|
||||
is(client.syncDesktopDevices, 4, "syncDesktopDevices is read when set");
|
||||
is(client.syncTotalDevices, 9, "syncTotalDevices is read when set");
|
||||
}));
|
||||
|
||||
add_task(Utils.withDriver(Assert, function* distribution(driver) {
|
||||
let client = yield driver.client();
|
||||
is(client.distribution, "default", "distribution has a default value");
|
||||
|
||||
yield SpecialPowers.pushPrefEnv({set: [["distribution.id", "funnelcake"]]});
|
||||
client = yield driver.client();
|
||||
is(client.distribution, "funnelcake", "distribution is read from preferences");
|
||||
}));
|
||||
|
@ -465,7 +465,6 @@
|
||||
@RESPATH@/components/nsWebHandlerApp.js
|
||||
@RESPATH@/components/satchel.manifest
|
||||
@RESPATH@/components/nsFormAutoComplete.js
|
||||
@RESPATH@/components/nsFormHistory.js
|
||||
@RESPATH@/components/FormHistoryStartup.js
|
||||
@RESPATH@/components/nsInputListAutoComplete.js
|
||||
@RESPATH@/components/formautofill.manifest
|
||||
|
@ -108,6 +108,7 @@ Var PreviousInstallArch
|
||||
Var ControlHeightPX
|
||||
Var ControlRightPX
|
||||
Var ControlTopAdjustment
|
||||
Var OptionsItemWidthPX
|
||||
|
||||
; Uncomment the following to prevent pinging the metrics server when testing
|
||||
; the stub installer
|
||||
@ -905,6 +906,16 @@ Function createOptions
|
||||
StrCpy $ExistingTopDir ""
|
||||
StrCpy $ControlTopAdjustment 0
|
||||
|
||||
; Convert the options item width to pixels, so we can tell when a text string
|
||||
; exceeds this width and needs multiple lines.
|
||||
StrCpy $2 "${OPTIONS_ITEM_WIDTH_DU}" -1
|
||||
IntOp $2 $2 - 14 ; subtract approximate width of a checkbox
|
||||
System::Call "*(i r2,i,i,i) p .r3"
|
||||
System::Call "user32::MapDialogRect(p $HWNDPARENT, p r3)"
|
||||
System::Call "*$3(i .s,i,i,i)"
|
||||
Pop $OptionsItemWidthPX
|
||||
System::Free $3
|
||||
|
||||
nsDialogs::Create /NOUNLOAD 1018
|
||||
Pop $Dialog
|
||||
; Since the text color for controls is set in this Dialog the foreground and
|
||||
@ -999,7 +1010,7 @@ Function createOptions
|
||||
; In that case, we'll need to give the control two lines worth of height.
|
||||
StrCpy $1 12 ; single line height
|
||||
${GetTextExtent} $0 $FontNormal $R1 $R2
|
||||
${If} $R1 > ${OPTIONS_ITEM_WIDTH_DU}
|
||||
${If} $R1 > $OptionsItemWidthPX
|
||||
; Add a second line to the control height.
|
||||
IntOp $1 $1 + 12
|
||||
; The rest of the controls will have to be lower to account for this label
|
||||
@ -1021,14 +1032,14 @@ Function createOptions
|
||||
; In that case, we'll need to give the control two lines worth of height.
|
||||
StrCpy $1 12 ; single line height
|
||||
${GetTextExtent} "$(SEND_PING)" $FontNormal $R1 $R2
|
||||
${If} $R1 > ${OPTIONS_ITEM_WIDTH_DU}
|
||||
${If} $R1 > $OptionsItemWidthPX
|
||||
; Add a second line to the control height.
|
||||
IntOp $1 $1 + 12
|
||||
; The rest of the controls will have to be lower to account for this label
|
||||
; needing two lines worth of height.
|
||||
IntOp $ControlTopAdjustment $ControlTopAdjustment + 12
|
||||
${EndIf}
|
||||
${NSD_CreateCheckbox} ${OPTIONS_ITEM_EDGE_DU} "$0u" ${OPTIONS_SUBITEM_WIDTH_DU} \
|
||||
${NSD_CreateCheckbox} ${OPTIONS_ITEM_EDGE_DU} "$0u" ${OPTIONS_ITEM_WIDTH_DU} \
|
||||
"$1u" "$(SEND_PING)"
|
||||
Pop $CheckboxSendPing
|
||||
; The uxtheme must be disabled on checkboxes in order to override the system
|
||||
@ -1058,8 +1069,19 @@ Function createOptions
|
||||
ReadRegStr $0 HKLM "SYSTEM\CurrentControlSet\services\MozillaMaintenance" "ImagePath"
|
||||
${If} ${Errors}
|
||||
IntOp $0 132 + $ControlTopAdjustment
|
||||
; In some locales, this string may be too long to fit on one line.
|
||||
; In that case, we'll need to give the control two lines worth of height.
|
||||
StrCpy $1 12 ; single line height
|
||||
${GetTextExtent} "$(INSTALL_MAINT_SERVICE)" $FontNormal $R1 $R2
|
||||
${If} $R1 > $OptionsItemWidthPX
|
||||
; Add a second line to the control height.
|
||||
IntOp $1 $1 + 12
|
||||
; The rest of the controls will have to be lower to account for this label
|
||||
; needing two lines worth of height.
|
||||
IntOp $ControlTopAdjustment $ControlTopAdjustment + 12
|
||||
${EndIf}
|
||||
${NSD_CreateCheckbox} ${OPTIONS_ITEM_EDGE_DU} "$0u" ${OPTIONS_ITEM_WIDTH_DU} \
|
||||
12u "$(INSTALL_MAINT_SERVICE)"
|
||||
"$1u" "$(INSTALL_MAINT_SERVICE)"
|
||||
Pop $CheckboxInstallMaintSvc
|
||||
System::Call 'uxtheme::SetWindowTheme(i $CheckboxInstallMaintSvc, w " ", w " ")'
|
||||
SetCtlColors $CheckboxInstallMaintSvc ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
|
||||
|
@ -61,12 +61,12 @@ webextPerms.sideloadMenuItem=%1$S added to %2$S
|
||||
# %S is replaced with the localized name of the extension being installed.
|
||||
# Note, this string will be used as raw markup. Avoid characters like <, >, &
|
||||
webextPerms.sideloadHeader=%S added
|
||||
webextPerms.sideloadText=Another program on your computer installed an add-on that may affect your browser. Please review this add-on’s permissions requests and choose to Enable or Disable.
|
||||
webextPerms.sideloadText2=Another program on your computer installed an add-on that may affect your browser. Please review this add-on’s permissions requests and choose to Enable or Cancel (to leave it disabled).
|
||||
|
||||
webextPerms.sideloadEnable.label=Enable
|
||||
webextPerms.sideloadEnable.accessKey=E
|
||||
webextPerms.sideloadDisable.label=Disable
|
||||
webextPerms.sideloadDisable.accessKey=D
|
||||
webextPerms.sideloadCancel.label=Cancel
|
||||
webextPerms.sideloadCancel.accessKey=C
|
||||
|
||||
# LOCALIZATION NOTE (webextPerms.updateMenuItem)
|
||||
# %S will be replaced with the localized name of the extension which
|
||||
|
@ -11,6 +11,8 @@
|
||||
<!ENTITY search.accesskey "S">
|
||||
<!ENTITY removeSelected.label "Remove Selected">
|
||||
<!ENTITY removeSelected.accesskey "r">
|
||||
<!ENTITY removeAll.label "Remove All">
|
||||
<!ENTITY removeAll.accesskey "e">
|
||||
<!ENTITY save.label "Save Changes">
|
||||
<!ENTITY save.accesskey "a">
|
||||
<!ENTITY cancel.label "Cancel">
|
||||
|
@ -24,6 +24,9 @@ XPCOMUtils.defineLazyPreferenceGetter(this, "WEBEXT_PERMISSION_PROMPTS",
|
||||
|
||||
const DEFAULT_EXTENSION_ICON = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
|
||||
|
||||
const BROWSER_PROPERTIES = "chrome://browser/locale/browser.properties";
|
||||
const BRAND_PROPERTIES = "chrome://browser/locale/brand.properties";
|
||||
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
this.ExtensionsUI = {
|
||||
@ -65,22 +68,13 @@ this.ExtensionsUI = {
|
||||
});
|
||||
},
|
||||
|
||||
showAddonsManager(browser, info) {
|
||||
let loadPromise = new Promise(resolve => {
|
||||
let listener = (subject, topic) => {
|
||||
if (subject.location.href == "about:addons") {
|
||||
Services.obs.removeObserver(listener, topic);
|
||||
resolve(subject);
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(listener, "EM-loaded", false);
|
||||
});
|
||||
let tab = browser.addTab("about:addons");
|
||||
browser.selectedTab = tab;
|
||||
|
||||
return loadPromise.then(win => {
|
||||
win.loadView("addons://list/extension");
|
||||
return this.showPermissionsPrompt(browser.selectedBrowser, info);
|
||||
showAddonsManager(browser, strings, icon) {
|
||||
let global = browser.selectedBrowser.ownerGlobal;
|
||||
return global.BrowserOpenAddonsMgr("addons://list/extension").then(aomWin => {
|
||||
let aomBrowser = aomWin.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDocShell)
|
||||
.chromeEventHandler;
|
||||
return this.showPermissionsPrompt(aomBrowser, strings, icon);
|
||||
});
|
||||
},
|
||||
|
||||
@ -89,31 +83,29 @@ this.ExtensionsUI = {
|
||||
this.sideloaded.delete(addon);
|
||||
this.emit("change");
|
||||
|
||||
let info = {
|
||||
let strings = this._buildStrings({
|
||||
addon,
|
||||
permissions: addon.userPermissions,
|
||||
icon: addon.iconURL,
|
||||
type: "sideload",
|
||||
};
|
||||
this.showAddonsManager(browser, info).then(answer => {
|
||||
});
|
||||
this.showAddonsManager(browser, strings, addon.iconURL).then(answer => {
|
||||
addon.userDisabled = !answer;
|
||||
});
|
||||
},
|
||||
|
||||
showUpdate(browser, info) {
|
||||
info.icon = info.addon.iconURL;
|
||||
info.type = "update";
|
||||
this.showAddonsManager(browser, info).then(answer => {
|
||||
if (answer) {
|
||||
info.resolve();
|
||||
} else {
|
||||
info.reject();
|
||||
}
|
||||
// At the moment, this prompt will re-appear next time we do an update
|
||||
// check. See bug 1332360 for proposal to avoid this.
|
||||
this.updates.delete(info);
|
||||
this.emit("change");
|
||||
});
|
||||
this.showAddonsManager(browser, info.strings, info.addon.iconURL)
|
||||
.then(answer => {
|
||||
if (answer) {
|
||||
info.resolve();
|
||||
} else {
|
||||
info.reject();
|
||||
}
|
||||
// At the moment, this prompt will re-appear next time we do an update
|
||||
// check. See bug 1332360 for proposal to avoid this.
|
||||
this.updates.delete(info);
|
||||
this.emit("change");
|
||||
});
|
||||
},
|
||||
|
||||
observe(subject, topic, data) {
|
||||
@ -141,10 +133,27 @@ this.ExtensionsUI = {
|
||||
reply(true);
|
||||
} else {
|
||||
info.permissions = perms;
|
||||
this.showPermissionsPrompt(target, info).then(reply);
|
||||
let strings = this._buildStrings(info);
|
||||
this.showPermissionsPrompt(target, strings, info.icon).then(reply);
|
||||
}
|
||||
} else if (topic == "webextension-update-permissions") {
|
||||
this.updates.add(subject.wrappedJSObject);
|
||||
let info = subject.wrappedJSObject;
|
||||
let strings = this._buildStrings(info);
|
||||
|
||||
// If we don't prompt for any new permissions, just apply it
|
||||
if (strings.msgs.length == 0) {
|
||||
info.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
let update = {
|
||||
strings,
|
||||
addon: info.addon,
|
||||
resolve: info.resolve,
|
||||
reject: info.reject,
|
||||
};
|
||||
|
||||
this.updates.add(update);
|
||||
this.emit("change");
|
||||
} else if (topic == "webextension-install-notify") {
|
||||
let {target, addon, callback} = subject.wrappedJSObject;
|
||||
@ -164,56 +173,54 @@ this.ExtensionsUI = {
|
||||
.replace(/>/g, ">");
|
||||
},
|
||||
|
||||
showPermissionsPrompt(target, info) {
|
||||
let perms = info.permissions;
|
||||
if (!perms) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
// Create a set of formatted strings for a permission prompt
|
||||
_buildStrings(info) {
|
||||
let result = {};
|
||||
|
||||
let win = target.ownerGlobal;
|
||||
let bundle = Services.strings.createBundle(BROWSER_PROPERTIES);
|
||||
|
||||
let name = info.addon.name;
|
||||
if (name.length > 50) {
|
||||
name = name.slice(0, 49) + "…";
|
||||
}
|
||||
name = this._sanitizeName(name);
|
||||
|
||||
let addonName = `<span class="addon-webext-name">${name}</span>`;
|
||||
let bundle = win.gNavigatorBundle;
|
||||
|
||||
let header = bundle.getFormattedString("webextPerms.header", [addonName]);
|
||||
let text = "";
|
||||
let listIntro = bundle.getString("webextPerms.listIntro");
|
||||
result.header = bundle.formatStringFromName("webextPerms.header", [addonName], 1);
|
||||
result.text = "";
|
||||
result.listIntro = bundle.GetStringFromName("webextPerms.listIntro");
|
||||
|
||||
let acceptText = bundle.getString("webextPerms.add.label");
|
||||
let acceptKey = bundle.getString("webextPerms.add.accessKey");
|
||||
let cancelText = bundle.getString("webextPerms.cancel.label");
|
||||
let cancelKey = bundle.getString("webextPerms.cancel.accessKey");
|
||||
result.acceptText = bundle.GetStringFromName("webextPerms.add.label");
|
||||
result.acceptKey = bundle.GetStringFromName("webextPerms.add.accessKey");
|
||||
result.cancelText = bundle.GetStringFromName("webextPerms.cancel.label");
|
||||
result.cancelKey = bundle.GetStringFromName("webextPerms.cancel.accessKey");
|
||||
|
||||
if (info.type == "sideload") {
|
||||
header = bundle.getFormattedString("webextPerms.sideloadHeader", [addonName]);
|
||||
text = bundle.getString("webextPerms.sideloadText");
|
||||
acceptText = bundle.getString("webextPerms.sideloadEnable.label");
|
||||
acceptKey = bundle.getString("webextPerms.sideloadEnable.accessKey");
|
||||
cancelText = bundle.getString("webextPerms.sideloadDisable.label");
|
||||
cancelKey = bundle.getString("webextPerms.sideloadDisable.accessKey");
|
||||
result.header = bundle.formatStringFromName("webextPerms.sideloadHeader", [addonName], 1);
|
||||
result.text = bundle.GetStringFromName("webextPerms.sideloadText2");
|
||||
result.acceptText = bundle.GetStringFromName("webextPerms.sideloadEnable.label");
|
||||
result.acceptKey = bundle.GetStringFromName("webextPerms.sideloadEnable.accessKey");
|
||||
result.cancelText = bundle.GetStringFromName("webextPerms.sideloadCancel.label");
|
||||
result.cancelKey = bundle.GetStringFromName("webextPerms.sideloadCancel.accessKey");
|
||||
} else if (info.type == "update") {
|
||||
header = "";
|
||||
text = bundle.getFormattedString("webextPerms.updateText", [addonName]);
|
||||
acceptText = bundle.getString("webextPerms.updateAccept.label");
|
||||
acceptKey = bundle.getString("webextPerms.updateAccept.accessKey");
|
||||
result.header = "";
|
||||
result.text = bundle.formatStringFromName("webextPerms.updateText", [addonName], 1);
|
||||
result.acceptText = bundle.GetStringFromName("webextPerms.updateAccept.label");
|
||||
result.acceptKey = bundle.GetStringFromName("webextPerms.updateAccept.accessKey");
|
||||
}
|
||||
|
||||
let msgs = [];
|
||||
let perms = info.permissions || {hosts: [], permissions: []};
|
||||
|
||||
result.msgs = [];
|
||||
for (let permission of perms.permissions) {
|
||||
let key = `webextPerms.description.${permission}`;
|
||||
if (permission == "nativeMessaging") {
|
||||
let brandBundle = win.document.getElementById("bundle_brand");
|
||||
let appName = brandBundle.getString("brandShortName");
|
||||
msgs.push(bundle.getFormattedString(key, [appName]));
|
||||
let brandBundle = Services.strings.createBundle(BRAND_PROPERTIES);
|
||||
let appName = brandBundle.GetStringFromName("brandShortName");
|
||||
result.msgs.push(bundle.formatStringFromName(key, [appName], 1));
|
||||
} else {
|
||||
try {
|
||||
msgs.push(bundle.getString(key));
|
||||
result.msgs.push(bundle.GetStringFromName(key));
|
||||
} catch (err) {
|
||||
// We deliberately do not include all permissions in the prompt.
|
||||
// So if we don't find one then just skip it.
|
||||
@ -241,14 +248,14 @@ this.ExtensionsUI = {
|
||||
}
|
||||
|
||||
if (allUrls) {
|
||||
msgs.push(bundle.getString("webextPerms.hostDescription.allUrls"));
|
||||
result.msgs.push(bundle.GetStringFromName("webextPerms.hostDescription.allUrls"));
|
||||
} else {
|
||||
// Formats a list of host permissions. If we have 4 or fewer, display
|
||||
// them all, otherwise display the first 3 followed by an item that
|
||||
// says "...plus N others"
|
||||
function format(list, itemKey, moreKey) {
|
||||
function formatItems(items) {
|
||||
msgs.push(...items.map(item => bundle.getFormattedString(itemKey, [item])));
|
||||
result.msgs.push(...items.map(item => bundle.formatStringFromName(itemKey, [item], 1)));
|
||||
}
|
||||
if (list.length < 5) {
|
||||
formatItems(list);
|
||||
@ -256,8 +263,8 @@ this.ExtensionsUI = {
|
||||
formatItems(list.slice(0, 3));
|
||||
|
||||
let remaining = list.length - 3;
|
||||
msgs.push(PluralForm.get(remaining, bundle.getString(moreKey))
|
||||
.replace("#1", remaining));
|
||||
result.msgs.push(PluralForm.get(remaining, bundle.GetStringFromName(moreKey))
|
||||
.replace("#1", remaining));
|
||||
}
|
||||
}
|
||||
|
||||
@ -267,56 +274,64 @@ this.ExtensionsUI = {
|
||||
"webextPerms.hostDescription.tooManySites");
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
showPermissionsPrompt(browser, strings, icon) {
|
||||
function eventCallback(topic) {
|
||||
if (topic == "showing") {
|
||||
let doc = this.browser.ownerDocument;
|
||||
doc.getElementById("addon-webext-perm-header").innerHTML = strings.header;
|
||||
|
||||
let textEl = doc.getElementById("addon-webext-perm-text");
|
||||
textEl.innerHTML = strings.text;
|
||||
textEl.hidden = !strings.text;
|
||||
|
||||
let listIntroEl = doc.getElementById("addon-webext-perm-intro");
|
||||
listIntroEl.value = strings.listIntro;
|
||||
listIntroEl.hidden = (strings.msgs.length == 0);
|
||||
|
||||
let list = doc.getElementById("addon-webext-perm-list");
|
||||
while (list.firstChild) {
|
||||
list.firstChild.remove();
|
||||
}
|
||||
|
||||
for (let msg of strings.msgs) {
|
||||
let item = doc.createElementNS(HTML_NS, "li");
|
||||
item.textContent = msg;
|
||||
list.appendChild(item);
|
||||
}
|
||||
} else if (topic == "swapping") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
let popupOptions = {
|
||||
hideClose: true,
|
||||
popupIconURL: info.icon || DEFAULT_EXTENSION_ICON,
|
||||
popupIconURL: icon || DEFAULT_EXTENSION_ICON,
|
||||
persistent: true,
|
||||
|
||||
eventCallback(topic) {
|
||||
if (topic == "showing") {
|
||||
let doc = this.browser.ownerDocument;
|
||||
doc.getElementById("addon-webext-perm-header").innerHTML = header;
|
||||
|
||||
let textEl = doc.getElementById("addon-webext-perm-text");
|
||||
textEl.innerHTML = text;
|
||||
textEl.hidden = !text;
|
||||
|
||||
let listIntroEl = doc.getElementById("addon-webext-perm-intro");
|
||||
listIntroEl.value = listIntro;
|
||||
listIntroEl.hidden = (msgs.length == 0);
|
||||
|
||||
let list = doc.getElementById("addon-webext-perm-list");
|
||||
while (list.firstChild) {
|
||||
list.firstChild.remove();
|
||||
}
|
||||
|
||||
for (let msg of msgs) {
|
||||
let item = doc.createElementNS(HTML_NS, "li");
|
||||
item.textContent = msg;
|
||||
list.appendChild(item);
|
||||
}
|
||||
} else if (topic == "swapping") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
eventCallback,
|
||||
};
|
||||
|
||||
let win = browser.ownerGlobal;
|
||||
return new Promise(resolve => {
|
||||
win.PopupNotifications.show(target, "addon-webext-permissions", "",
|
||||
let action = {
|
||||
label: strings.acceptText,
|
||||
accessKey: strings.acceptKey,
|
||||
callback: () => resolve(true),
|
||||
};
|
||||
let secondaryActions = [
|
||||
{
|
||||
label: strings.cancelText,
|
||||
accessKey: strings.cancelKey,
|
||||
callback: () => resolve(false),
|
||||
},
|
||||
];
|
||||
|
||||
win.PopupNotifications.show(browser, "addon-webext-permissions", "",
|
||||
"addons-notification-icon",
|
||||
{
|
||||
label: acceptText,
|
||||
accessKey: acceptKey,
|
||||
callback: () => resolve(true),
|
||||
},
|
||||
[
|
||||
{
|
||||
label: cancelText,
|
||||
accessKey: cancelKey,
|
||||
callback: () => resolve(false),
|
||||
},
|
||||
], popupOptions);
|
||||
action, secondaryActions, popupOptions);
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -112,6 +112,20 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Override tab close icon (to disable inversion) for better contrast with
|
||||
light theme on Windows 7 Classic theme. */
|
||||
@media not all and (min-resolution: 1.1dppx) {
|
||||
#TabsToolbar[brighttext] .tab-close-button:-moz-lwtheme-darktext:not([selected="true"]) {
|
||||
list-style-image: url("chrome://global/skin/icons/close.png");
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-resolution: 1.1dppx) {
|
||||
#TabsToolbar[brighttext] .tab-close-button:-moz-lwtheme-darktext:not([selected="true"]) {
|
||||
list-style-image: url("chrome://global/skin/icons/close@2x.png");
|
||||
}
|
||||
}
|
||||
|
||||
@media (-moz-os-version: windows-win7),
|
||||
(-moz-os-version: windows-win8) {
|
||||
:root {
|
||||
|
@ -15,6 +15,8 @@
|
||||
"cxx": "/home/worker/workspace/build/src/gcc/bin/g++",
|
||||
"as": "/home/worker/workspace/build/src/gcc/bin/gcc",
|
||||
"patches": [
|
||||
"llvm-debug-frame.patch"
|
||||
"llvm-debug-frame.patch",
|
||||
"r277806.patch",
|
||||
"r285657.patch"
|
||||
]
|
||||
}
|
||||
|
@ -22,17 +22,18 @@ CROSS_CCTOOLS_PATH=$topsrcdir/cctools
|
||||
# `browser/config/tooltool-manifests/macosx64/cross-releng.manifest`.
|
||||
CROSS_SYSROOT=$topsrcdir/MacOSX10.7.sdk
|
||||
CROSS_PRIVATE_FRAMEWORKS=$CROSS_SYSROOT/System/Library/PrivateFrameworks
|
||||
FLAGS="-target x86_64-apple-darwin10 -mlinker-version=136 -B $CROSS_CCTOOLS_PATH/bin -isysroot $CROSS_SYSROOT"
|
||||
FLAGS="-target x86_64-apple-darwin11 -B $CROSS_CCTOOLS_PATH/bin -isysroot $CROSS_SYSROOT"
|
||||
|
||||
export CC="$topsrcdir/clang/bin/clang $FLAGS"
|
||||
export CXX="$topsrcdir/clang/bin/clang++ $FLAGS"
|
||||
export CPP="$topsrcdir/clang/bin/clang $FLAGS -E"
|
||||
export LLVMCONFIG=$topsrcdir/clang/bin/llvm-config
|
||||
export LDFLAGS="-Wl,-syslibroot,$CROSS_SYSROOT -Wl,-dead_strip"
|
||||
export TOOLCHAIN_PREFIX=$CROSS_CCTOOLS_PATH/bin/x86_64-apple-darwin10-
|
||||
export TOOLCHAIN_PREFIX=$CROSS_CCTOOLS_PATH/bin/x86_64-apple-darwin11-
|
||||
export DSYMUTIL=$topsrcdir/clang/bin/llvm-dsymutil
|
||||
export GENISOIMAGE=$topsrcdir/genisoimage/genisoimage
|
||||
export MKFSHFS=$topsrcdir/hfsplus-tools/newfs_hfs
|
||||
export DMG_TOOL=$topsrcdir/dmg/dmg
|
||||
export HFS_TOOL=$topsrcdir/dmg/hfsplus
|
||||
|
||||
export HOST_CC="$topsrcdir/clang/bin/clang"
|
||||
export HOST_CXX="$topsrcdir/clang/bin/clang++"
|
||||
|
@ -763,6 +763,8 @@ def compiler(language, host_or_target, c_compiler=None, other_compiler=None,
|
||||
# Set CC_TYPE/CC_VERSION/HOST_CC_TYPE/HOST_CC_VERSION to allow
|
||||
# old-configure to do some of its still existing checks.
|
||||
if language == 'C':
|
||||
set_config(
|
||||
'%s_TYPE' % var, delayed_getattr(valid_compiler, 'type'))
|
||||
add_old_configure_assignment(
|
||||
'%s_TYPE' % var, delayed_getattr(valid_compiler, 'type'))
|
||||
add_old_configure_assignment(
|
||||
|
@ -0,0 +1,53 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
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
|
||||
Lt44/DBv052TOb47tw==
|
||||
=ERlK
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
@ -0,0 +1,76 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
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
|
||||
9ig=
|
||||
=2zZQ
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
@ -0,0 +1,33 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
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
|
||||
f1vcugCdHV/JIhOwETTqOQEbkw3y9ng2+4U=
|
||||
=K9Jj
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
@ -0,0 +1,35 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQFNBFDrIWMBCgCyyYoTAD/aL6Yl90eSJ1xuFpODTcwyRZsNSUZKSmKwnqXo9LgS
|
||||
2B00yVZ2nO2OrSmWPiYikTciitv04bAqFaggSstx6hlni6n3h2PL0jXpf9EI6qOO
|
||||
oKwi2IVtbBnJAhWpfRcAce6WEqvnav6KjuBM3lr8/5GzDV8tm6+X/G/paTnBqTB9
|
||||
pBxrH7smB+iRjDt/6ykWkbYLd6uBKzIkAp4HqAZb/aZMvxI28PeWGjZJQYq2nVPf
|
||||
LroM6Ub/sNlXpv/bmHJusFQjUL368njhZD1+aVLCUfBCCDzvZc3EYt3wBkbmuCiA
|
||||
xOb9ramHgiVkNENtzXR+sbQHtKRQv/jllY1qxROM2/rWmL+HohdxL5E0VPple2bg
|
||||
U/zqX0Hg2byb8FbpzPJO5PnBD+1PME3Uirsly4N7XT80OvhXlYe4t+9X0QARAQAB
|
||||
tCROaWVscyBNw7ZsbGVyIDxuaXNzZUBseXNhdG9yLmxpdS5zZT6JAX4EEwECACgF
|
||||
AlDrIWMCGwMFCRLMAwAGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEPNZn/go
|
||||
xnKYqm0J/A4b6TE5qPWiWj0kriUBSmpys3qUz93gR6Ft7w2f478KJuzbSadvyn0u
|
||||
PcnP26AGTOQq75RhtgCJgdYbvRocTjlMh9jOX584Hx8hi/QSrpCSYMnj6dQKbu0Y
|
||||
QIFjZx8gPeYvzG8t34FCNEzZ09RQZqy/ukRyN99LkwEuP4FWq486b7dpgv7GC+SH
|
||||
lZcMco6VW8FLOT7KMalH06cmdhFPrFSYAIHDu3CsYhC8knIQV99Xzno/KeSkEwkq
|
||||
tYDOdz0x4HWdOwHrl2S2X6Ex1q3QRXcq84EYQwHz2WEGaPR7Vd76P5J1wiHN6rwO
|
||||
4exfgsRyTvc6NDQPTFqmoCzwuPviYk6JNnHr9E5TkLT7lAnESEhMLyyIG/7Uwpgu
|
||||
5C71IMaTpOpf8DEU9NU/zuxgHoMaKBZaeYKs0S26s1zwGOlQX0T9uQFNBFDrIWMB
|
||||
CgDKlONI+5Bqcu69+72fmLZPizzEUsIRA2Y0w2RE7+uJ5Es9/YTp5PnWANpPT7GS
|
||||
8JJnc6NJJeh6GkMkGGwq5Op7CDsjW9pQZ0vAW90XjnyniDa9W0W+m5+X/LPOzh+n
|
||||
is9Zcf17P91tprLCLi+TOOb35xt396pZ+S+PwuV0dLiIYdVYV3e6LNCV0LjhEqp5
|
||||
3TRwTrLTNPQVnt0DPYTh/Kn1x6d5zOS0MK4QybKN1WJU6nYIQRXyWKkixjbs++jc
|
||||
gV/juck96Ve0blvn6DfqfpG8YzbmqRCufLo683LtlBUZ0c+znrD1nouqX2Eb/Cyl
|
||||
G8Q8ZUHXimCJ+g6RfH9kOmtVH/208u/nDofVL/Q0dvAXfU5MX49c7XYy7B2rTlk+
|
||||
4nuNeaHM0aU2Y14+SQy+sR6zydu7eGLdqjzV0CX/ekgrjQARAQABiQFlBBgBAgAP
|
||||
BQJQ6yFjAhsMBQkSzAMAAAoJEPNZn/goxnKYGUcJ/j+L0/uzfwCR1aTBZ6FBT9Od
|
||||
NyatVjmz20ahskF3BySmkT1R06K08YOGJ//LPajj0eKqU8WKgxMc7pWi5SG+yMFn
|
||||
2db5HnJDGiSmSjCXW/BzsSt1786LtO0m0ehatj9kl6JrxQNXazOkRJ2ww13P6/91
|
||||
RBaV6R08BmFTrUco2P6w+djCF4NlnkOLa7fM6QtNZM+yB+EzaPjSBFjZG52BVWZk
|
||||
cXEVN0cEjPuznuQOmx8Dny7lQikp49NumrbamaxZEilx2Bi9gSbovNaKBuncKi9X
|
||||
boiEiNbAarGxP40Qvlk2AuXWvq+fiBnU1e1nU2oV7/7nAWH7kj/Vr/JxcBeOpsND
|
||||
GkW7Yrd3mkJCrhG+jMs1V2qNb9Uhr5ZLOA40sIz2PHfDrR+gc8THm2p5OvCWEAeu
|
||||
kYJ22XTUIt6XoPO0ERYD
|
||||
=MH4q
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
@ -0,0 +1,57 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
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
|
||||
8GOBvibrC/azKvoBKLY1/stpKCrecZdRFkk=
|
||||
=SDN9
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
@ -0,0 +1,29 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQGiBEj3S14RBACUE+e2hRWwM6AFWaNKsLgDg6ebDNCI6z/Pk38t6JUeM+D5MAvq
|
||||
fnL45bF/3CUrZRK+/qLg5iwRWehKh08VQ7GqDxMerZkPmfvirVxLwpc5ngCOGJwv
|
||||
ba13xdaxfTLMkHxaQyGWiUqHIwdzFoNBgjq9XTY0GGqHwVA1Hb+xTAL8PwCg+wru
|
||||
41p/aOq9cfPN1U1BjulWCSMD/2YP23pI19o9Hr26ltyJcd/xkSRiCUk84efIw5JH
|
||||
7QlxoMoW/SdJQAGi2pZN9o4I/fPDB3Gna9M9rZfacKda857dwkALPK8xTsfHiZzH
|
||||
40g+eYUvl9nloNidneSFcxbLO/euURcCJ6Ri6nb4QWWLVH1XF6wQxEGyn7ojYMOn
|
||||
ihlkA/9KATtSt+T0zgWskqckgV1ZQg9Ysqwp3GAvezJuyUTXlB02ApVFEsRTQtLZ
|
||||
2WPvo/gzfih/EdNLrq7UeeB4dr/nlpANn4IyBRN6EmBHaJ4MMKN77B7bB0GaBfGJ
|
||||
rgNNp7W0xG0FLjaMoQzP62qqAtZHNlW1qCmkyJGUpAa6tJw9CbQlVHJpc3RhbiBH
|
||||
aW5nb2xkIDxnaW5nb2xkQGFkYWNvcmUuY29tPohgBBMRAgAgBQJI90teAhsDBgsJ
|
||||
CAcDAgQVAggDBBYCAwECHgECF4AACgkQwxJtO0rlXpMVcwCgoQ91OLI+m2bsu2SS
|
||||
d5MsRQH3FWsAnRFG2YGk5o5zuoLzdZd6KlL9xJ+uuQINBEj3S2MQCACnDo5dHujc
|
||||
u7QHRPnxNwiKhMP6eIZaEm9tavab3UxsRufMyVC8nQ8+EmCOwfBrqstfRVoQnoDI
|
||||
s5UY1XAM3mBFXYqfY9wR6NISUlzK/HPyFhGE7t3lVjOkiqbWOftDt6GgRETeqYsW
|
||||
XkDV/dL4+P3eSaOSP6KMZwdjgXPOciN59KIiii9NK4icxP0lJHDk5WJFwfucEyUt
|
||||
Sz7uwuUFcajHZmMxxHAnWT3uJ+ZasSijduZevsHhKTTXaZRideqf+ur1/TcUaZDQ
|
||||
O3wist1qc03NkL+oGu6HYPx9ZV40p/axdTaUXMcBjtAZIzvy984HF9EsFQnvbiXt
|
||||
R8zg6SYXLRmbAAMGB/9JMKWsCuxUzXmU1jyJvMXdRBZ4YQYkKFYWrEXwjYlBEGx6
|
||||
01PkR//4QJVR4zFjy4zVnaUrOxtR+65Eedf+9fNZzSNeI24TGaqyVM0OYYQtp9cH
|
||||
kRDu3wif1k2NW3BnrmTjVefdAWVH6zKT9lP9m6RPHCwVGyORhVQtB3+ZXOehNJwL
|
||||
9NBU4MUpGKpoQCuODdgZ8iQXbo+plg0eCxcpNaYzSnq9DMAU+2qnP6d3x4DeWzlL
|
||||
wvJ2K2Mw89gvCImy/JDe05EXqKowR6aiIPvw5ou9xSHmjT6rcaIBiROCe+1hh4XC
|
||||
djCdb4kOskWCEXfFKcHax4N5fI9vmk3P5068BELMiEkEGBECAAkFAkj3S2MCGwwA
|
||||
CgkQwxJtO0rlXpNxdwCgjr4sQRf2cyDkCSWe4AElbI74BREAoPdet3XvE6ZcZJGl
|
||||
UIZySRkdpk/A
|
||||
=dGh0
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
@ -1,5 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
set -x
|
||||
|
||||
gcc_version=4.9.4
|
||||
binutils_version=2.25.1
|
||||
this_path=$(readlink -f $(dirname $0))
|
||||
@ -15,38 +19,102 @@ if test -z $TMPDIR; then
|
||||
TMPDIR=/tmp/
|
||||
fi
|
||||
|
||||
wget -c -P $TMPDIR ftp://ftp.gnu.org/gnu/binutils/binutils-$binutils_version.tar.bz2 || exit 1
|
||||
mkdir gpg
|
||||
GPG="gpg --homedir $root_dir/gpg"
|
||||
# GPG key used to sign GCC
|
||||
$GPG --import $this_path/13975A70E63C361C73AE69EF6EEB81F8981C74C7.key
|
||||
# GPG key used to sign binutils
|
||||
$GPG --import $this_path/EAF1C276A747E9ED86210CBAC3126D3B4AE55E93.key
|
||||
# GPG key used to sign GMP
|
||||
$GPG --import $this_path/343C2FF0FBEE5EC2EDBEF399F3599FF828C67298.key
|
||||
# GPG key used to sign MPFR
|
||||
$GPG --import $this_path/07F3DBBECC1A39605078094D980C197698C3739D.key
|
||||
# GPG key used to sign MPC
|
||||
$GPG --import $this_path/AD17A21EF8AED8F1CC02DBD9F7D5C9BF765C61E3.key
|
||||
|
||||
> $root_dir/downloads
|
||||
|
||||
download() {
|
||||
wget -c -P $TMPDIR $1/$2
|
||||
(cd $TMPDIR; sha256sum $2) >> $root_dir/downloads
|
||||
}
|
||||
|
||||
download_and_check() {
|
||||
download $1 ${2%.*}
|
||||
wget -c -P $TMPDIR $1/$2
|
||||
$GPG --verify $TMPDIR/$2 $TMPDIR/${2%.*}
|
||||
}
|
||||
|
||||
download_and_check ftp://ftp.gnu.org/gnu/binutils binutils-$binutils_version.tar.bz2.sig
|
||||
tar xjf $TMPDIR/binutils-$binutils_version.tar.bz2
|
||||
mkdir binutils-objdir
|
||||
cd binutils-objdir
|
||||
# gold is disabled because we don't use it on automation, and also we ran into
|
||||
# some issues with it using this script in build-clang.py.
|
||||
../binutils-$binutils_version/configure --prefix /tools/gcc/ --disable-gold --enable-plugins --disable-nls || exit 1
|
||||
make $make_flags || exit 1
|
||||
make install $make_flags DESTDIR=$root_dir || exit 1
|
||||
../binutils-$binutils_version/configure --prefix /tools/gcc/ --disable-gold --enable-plugins --disable-nls
|
||||
make $make_flags
|
||||
make install $make_flags DESTDIR=$root_dir
|
||||
cd ..
|
||||
|
||||
case "$gcc_version" in
|
||||
*-*)
|
||||
wget -c -P $TMPDIR ftp://gcc.gnu.org/pub/gcc/snapshots/$gcc_version/gcc-$gcc_version.tar.bz2 || exit 1
|
||||
download ftp://gcc.gnu.org/pub/gcc/snapshots/$gcc_version/gcc-$gcc_version.tar.bz2
|
||||
;;
|
||||
*)
|
||||
wget -c -P $TMPDIR ftp://ftp.gnu.org/gnu/gcc/gcc-$gcc_version/gcc-$gcc_version.tar.bz2 || exit 1
|
||||
download_and_check ftp://ftp.gnu.org/gnu/gcc/gcc-$gcc_version gcc-$gcc_version.tar.bz2.sig
|
||||
;;
|
||||
esac
|
||||
tar xjf $TMPDIR/gcc-$gcc_version.tar.bz2
|
||||
cd gcc-$gcc_version
|
||||
|
||||
./contrib/download_prerequisites
|
||||
(
|
||||
# Divert commands that download_prerequisites use
|
||||
ln() { :; }
|
||||
tar() { :; }
|
||||
sed() { :; }
|
||||
wget() {
|
||||
echo $1
|
||||
}
|
||||
|
||||
patch -p1 < "${this_path}/PR64905.patch" || exit 1
|
||||
. ./contrib/download_prerequisites
|
||||
) | while read url; do
|
||||
file=$(basename $url)
|
||||
case "$file" in
|
||||
gmp-*.tar.*)
|
||||
# If download_prerequisites wants 4.3.2, use 5.1.3 instead.
|
||||
file=${file/4.3.2/5.1.3}
|
||||
download_and_check https://gmplib.org/download/gmp $file.sig
|
||||
;;
|
||||
mpfr-*.tar.*)
|
||||
# If download_prerequisites wants 2.4.2, use 3.1.5 instead.
|
||||
file=${file/2.4.2/3.1.5}
|
||||
download_and_check http://www.mpfr.org/${file%.tar.*} $file.asc
|
||||
;;
|
||||
mpc-*.tar.*)
|
||||
# If download_prerequisites wants 0.8.1, use 0.8.2 instead.
|
||||
file=${file/0.8.1/0.8.2}
|
||||
download_and_check http://www.multiprecision.org/mpc/download $file.asc
|
||||
;;
|
||||
*)
|
||||
download $(dirname $url) $file
|
||||
;;
|
||||
esac
|
||||
tar xaf $TMPDIR/$file
|
||||
ln -sf ${file%.tar.*} ${file%-*}
|
||||
done
|
||||
|
||||
# Check all the downloads we did are in the checksums list, and that the
|
||||
# checksums match.
|
||||
diff -u <(sort -k 2 $root_dir/downloads) $this_path/checksums
|
||||
|
||||
patch -p1 < "${this_path}/PR64905.patch"
|
||||
|
||||
cd ..
|
||||
mkdir gcc-objdir
|
||||
cd gcc-objdir
|
||||
../gcc-$gcc_version/configure --prefix=/tools/gcc --enable-languages=c,c++ --disable-nls --disable-gnu-unique-object --enable-__cxa_atexit --with-arch-32=pentiumpro || exit 1
|
||||
make $make_flags || exit 1
|
||||
make $make_flags install DESTDIR=$root_dir || exit 1
|
||||
../gcc-$gcc_version/configure --prefix=/tools/gcc --enable-languages=c,c++ --disable-nls --disable-gnu-unique-object --enable-__cxa_atexit --with-arch-32=pentiumpro
|
||||
make $make_flags
|
||||
make $make_flags install DESTDIR=$root_dir
|
||||
|
||||
cd $root_dir/tools
|
||||
tar caf $root_dir/gcc.tar.xz gcc/
|
||||
|
7
build/unix/build-gcc/checksums
Normal file
7
build/unix/build-gcc/checksums
Normal file
@ -0,0 +1,7 @@
|
||||
b5b14added7d78a8d1ca70b5cb75fef57ce2197264f4f5835326b0df22ac9f22 binutils-2.25.1.tar.bz2
|
||||
02500a4edd14875f94fe84cbeda4290425cb0c1c2474c6f75d75a303d64b4196 cloog-0.18.1.tar.gz
|
||||
6c11d292cd01b294f9f84c9a59c230d80e9e4a47e5c6355f046bb36d4f358092 gcc-4.9.4.tar.bz2
|
||||
752079520b4690531171d0f4532e40f08600215feefede70b24fabdc6f1ab160 gmp-5.1.3.tar.bz2
|
||||
f4b3dbee9712850006e44f0db2103441ab3d13b406f77996d1df19ee89d11fb4 isl-0.12.2.tar.bz2
|
||||
ae79f8d41d8a86456b68607e9ca398d00f8b7342d1d83bcf4428178ac45380c7 mpc-0.8.2.tar.gz
|
||||
ca498c1c7a74dd37a576f353312d1e68d490978de4395fa28f1cbd46a364e658 mpfr-3.1.5.tar.bz2
|
46
build/unix/build-hfsplus/build-hfsplus.sh
Executable file
46
build/unix/build-hfsplus/build-hfsplus.sh
Executable file
@ -0,0 +1,46 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -x
|
||||
|
||||
hfplus_version=540.1.linux3
|
||||
md5sum=0435afc389b919027b69616ad1b05709
|
||||
filename=diskdev_cmds-${hfplus_version}.tar.gz
|
||||
make_flags="-j$(getconf _NPROCESSORS_ONLN)"
|
||||
|
||||
root_dir="$1"
|
||||
if [ -z "$root_dir" -o ! -d "$root_dir" ]; then
|
||||
root_dir=$(mktemp -d)
|
||||
fi
|
||||
cd $root_dir
|
||||
|
||||
if test -z $TMPDIR; then
|
||||
TMPDIR=/tmp/
|
||||
fi
|
||||
|
||||
# Install clang first
|
||||
yum install -y clang
|
||||
|
||||
# Set an md5 check file to validate input
|
||||
echo "${md5sum} *${TMPDIR}/${filename}" > $TMPDIR/hfsplus.MD5
|
||||
|
||||
# Most-upstream is https://opensource.apple.com/source/diskdev_cmds/
|
||||
|
||||
# Download the source of the specified version of binutils
|
||||
wget -c -P $TMPDIR http://pkgs.fedoraproject.org/repo/pkgs/hfsplus-tools/${filename}/${md5sum}/${filename} || exit 1
|
||||
md5sum -c $TMPDIR/hfsplus.MD5 || exit 1
|
||||
mkdir hfsplus-source
|
||||
tar xzf $TMPDIR/${filename} -C hfsplus-source --strip-components=1
|
||||
|
||||
# Build
|
||||
cd hfsplus-source
|
||||
make $make_flags || exit 1
|
||||
cd ..
|
||||
|
||||
mkdir hfsplus-tools
|
||||
cp hfsplus-source/newfs_hfs.tproj/newfs_hfs hfsplus-tools/newfs_hfs
|
||||
## XXX fsck_hfs is unused, but is small and built from the package.
|
||||
cp hfsplus-source/fsck_hfs.tproj/fsck_hfs hfsplus-tools/fsck_hfs
|
||||
|
||||
# Make a package of the built utils
|
||||
cd $root_dir
|
||||
tar caf $root_dir/hfsplus-tools.tar.xz hfsplus-tools
|
3
config/external/ffi/moz.build
vendored
3
config/external/ffi/moz.build
vendored
@ -85,6 +85,9 @@ else:
|
||||
elif CONFIG['FFI_TARGET'] == 'X86_64':
|
||||
ffi_srcs = ('ffi64.c', 'unix64.S', 'ffi.c', 'sysv.S')
|
||||
elif CONFIG['FFI_TARGET'] == 'X86_WIN32':
|
||||
# MinGW Build for 32 bit
|
||||
if CONFIG['CC_TYPE'] == 'gcc':
|
||||
DEFINES['SYMBOL_UNDERSCORE'] = True
|
||||
ffi_srcs = ('ffi.c', 'win32.S')
|
||||
elif CONFIG['FFI_TARGET'] == 'X86_WIN64':
|
||||
ffi_srcs = ('ffi.c', 'win64.S')
|
||||
|
@ -958,6 +958,7 @@ CARGO_BUILD = env $(rustflags_override) \
|
||||
MOZ_DIST=$(ABS_DIST) \
|
||||
LIBCLANG_PATH=$(MOZ_LIBCLANG_PATH) \
|
||||
CLANG_PATH=$(MOZ_CLANG_PATH) \
|
||||
PKG_CONFIG_ALLOW_CROSS=1 \
|
||||
$(CARGO) build $(cargo_build_flags)
|
||||
|
||||
ifdef RUST_LIBRARY_FILE
|
||||
|
@ -5620,7 +5620,7 @@ var Debugger =
|
||||
"unicode-bidi": {
|
||||
inherited: false,
|
||||
supports: 0,
|
||||
values: ["-moz-isolate", "-moz-isolate-override", "-moz-plaintext", "bidi-override", "embed", "inherit", "initial", "normal", "unset", ],
|
||||
values: ["isolate", "isolate-override", "plaintext", "bidi-override", "embed", "inherit", "initial", "normal", "unset", ],
|
||||
},
|
||||
"-moz-user-focus": {
|
||||
inherited: true,
|
||||
|
@ -79,12 +79,20 @@ var submit = Task.async(function* () {
|
||||
var onConnectionReady = Task.async(function* ([aType, aTraits]) {
|
||||
clearTimeout(gConnectionTimeout);
|
||||
|
||||
let response = yield gClient.listAddons();
|
||||
let addons = [];
|
||||
try {
|
||||
let response = yield gClient.listAddons();
|
||||
if (!response.error && response.addons.length > 0) {
|
||||
addons = response.addons;
|
||||
}
|
||||
} catch(e) {
|
||||
// listAddons throws if the runtime doesn't support addons
|
||||
}
|
||||
|
||||
let parent = document.getElementById("addonActors");
|
||||
if (!response.error && response.addons.length > 0) {
|
||||
if (addons.length > 0) {
|
||||
// Add one entry for each add-on.
|
||||
for (let addon of response.addons) {
|
||||
for (let addon of addons) {
|
||||
if (!addon.debuggable) {
|
||||
continue;
|
||||
}
|
||||
@ -97,7 +105,7 @@ var onConnectionReady = Task.async(function* ([aType, aTraits]) {
|
||||
parent.remove();
|
||||
}
|
||||
|
||||
response = yield gClient.listTabs();
|
||||
let response = yield gClient.listTabs();
|
||||
|
||||
parent = document.getElementById("tabActors");
|
||||
|
||||
|
@ -34,8 +34,6 @@ const { BrowserLoader } =
|
||||
const {LocalizationHelper} = require("devtools/shared/l10n");
|
||||
const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
|
||||
|
||||
loader.lazyRequireGetter(this, "CommandUtils",
|
||||
"devtools/client/shared/developer-toolbar", true);
|
||||
loader.lazyRequireGetter(this, "getHighlighterUtils",
|
||||
"devtools/client/framework/toolbox-highlighter-utils", true);
|
||||
loader.lazyRequireGetter(this, "Selection",
|
||||
@ -1204,15 +1202,6 @@ Toolbox.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the toolbar spec for toolbox
|
||||
*/
|
||||
getToolbarSpec: function () {
|
||||
let spec = CommandUtils.getCommandbarSpec("devtools.toolbox.toolbarSpec");
|
||||
|
||||
return spec;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return all toolbox buttons (command buttons, plus any others that were
|
||||
* added manually).
|
||||
|
@ -28,6 +28,7 @@ const App = createClass({
|
||||
boxModel: PropTypes.shape(Types.boxModel).isRequired,
|
||||
grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
|
||||
highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired,
|
||||
showBoxModelProperties: PropTypes.bool.isRequired,
|
||||
onShowBoxModelEditor: PropTypes.func.isRequired,
|
||||
onHideBoxModelHighlighter: PropTypes.func.isRequired,
|
||||
onShowBoxModelHighlighter: PropTypes.func.isRequired,
|
||||
|
@ -9,6 +9,7 @@ const { addons, createClass, createFactory, DOM: dom, PropTypes } =
|
||||
|
||||
const BoxModelInfo = createFactory(require("./BoxModelInfo"));
|
||||
const BoxModelMain = createFactory(require("./BoxModelMain"));
|
||||
const BoxModelProperties = createFactory(require("./BoxModelProperties"));
|
||||
|
||||
const Types = require("../types");
|
||||
|
||||
@ -18,6 +19,7 @@ module.exports = createClass({
|
||||
|
||||
propTypes: {
|
||||
boxModel: PropTypes.shape(Types.boxModel).isRequired,
|
||||
showBoxModelProperties: PropTypes.bool.isRequired,
|
||||
onHideBoxModelHighlighter: PropTypes.func.isRequired,
|
||||
onShowBoxModelEditor: PropTypes.func.isRequired,
|
||||
onShowBoxModelHighlighter: PropTypes.func.isRequired,
|
||||
@ -28,6 +30,7 @@ module.exports = createClass({
|
||||
render() {
|
||||
let {
|
||||
boxModel,
|
||||
showBoxModelProperties,
|
||||
onHideBoxModelHighlighter,
|
||||
onShowBoxModelEditor,
|
||||
onShowBoxModelHighlighter,
|
||||
@ -45,7 +48,13 @@ module.exports = createClass({
|
||||
}),
|
||||
BoxModelInfo({
|
||||
boxModel,
|
||||
})
|
||||
}),
|
||||
showBoxModelProperties ?
|
||||
BoxModelProperties({
|
||||
boxModel,
|
||||
})
|
||||
:
|
||||
null,
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -0,0 +1,89 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { addons, createClass, createFactory, DOM: dom, PropTypes } =
|
||||
require("devtools/client/shared/vendor/react");
|
||||
|
||||
const { LocalizationHelper } = require("devtools/shared/l10n");
|
||||
|
||||
const ComputedProperty = createFactory(require("./ComputedProperty"));
|
||||
|
||||
const Types = require("../types");
|
||||
|
||||
const BOXMODEL_STRINGS_URI = "devtools/client/locales/boxmodel.properties";
|
||||
const BOXMODEL_L10N = new LocalizationHelper(BOXMODEL_STRINGS_URI);
|
||||
|
||||
module.exports = createClass({
|
||||
|
||||
displayName: "BoxModelProperties",
|
||||
|
||||
propTypes: {
|
||||
boxModel: PropTypes.shape(Types.boxModel).isRequired,
|
||||
},
|
||||
|
||||
mixins: [ addons.PureRenderMixin ],
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
isOpen: true,
|
||||
};
|
||||
},
|
||||
|
||||
onToggleExpander() {
|
||||
this.setState({
|
||||
isOpen: !this.state.isOpen,
|
||||
});
|
||||
},
|
||||
|
||||
render() {
|
||||
let { boxModel } = this.props;
|
||||
let { layout } = boxModel;
|
||||
|
||||
let layoutInfo = ["box-sizing", "display", "float",
|
||||
"line-height", "position", "z-index"];
|
||||
|
||||
const properties = layoutInfo.map(info => ComputedProperty({
|
||||
name: info,
|
||||
key: info,
|
||||
value: layout[info],
|
||||
}));
|
||||
|
||||
return dom.div(
|
||||
{
|
||||
className: "boxmodel-properties",
|
||||
},
|
||||
dom.div(
|
||||
{
|
||||
className: "boxmodel-properties-header",
|
||||
onDoubleClick: this.onToggleExpander,
|
||||
},
|
||||
dom.div(
|
||||
{
|
||||
className: "boxmodel-properties-expander theme-twisty",
|
||||
open: this.state.isOpen,
|
||||
onClick: this.onToggleExpander,
|
||||
},
|
||||
),
|
||||
dom.span(
|
||||
{
|
||||
className: "boxmodel-properties-label",
|
||||
title: BOXMODEL_L10N.getStr("boxmodel.propertiesLabel"),
|
||||
},
|
||||
BOXMODEL_L10N.getStr("boxmodel.propertiesLabel"),
|
||||
),
|
||||
),
|
||||
dom.div(
|
||||
{
|
||||
className: "boxmodel-properties-wrapper",
|
||||
hidden: !this.state.isOpen,
|
||||
tabIndex: 0,
|
||||
},
|
||||
properties,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
});
|
@ -0,0 +1,67 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { addons, createClass, DOM: dom, PropTypes } =
|
||||
require("devtools/client/shared/vendor/react");
|
||||
|
||||
module.exports = createClass({
|
||||
|
||||
displayName: "ComputedProperty",
|
||||
|
||||
propTypes: {
|
||||
name: PropTypes.string.isRequired,
|
||||
value: PropTypes.string,
|
||||
},
|
||||
|
||||
mixins: [ addons.PureRenderMixin ],
|
||||
|
||||
onFocus() {
|
||||
this.container.focus();
|
||||
},
|
||||
|
||||
render() {
|
||||
const { name, value } = this.props;
|
||||
|
||||
return dom.div(
|
||||
{
|
||||
className: "property-view",
|
||||
tabIndex: "0",
|
||||
ref: container => {
|
||||
this.container = container;
|
||||
},
|
||||
},
|
||||
dom.div(
|
||||
{
|
||||
className: "property-name-container",
|
||||
},
|
||||
dom.div(
|
||||
{
|
||||
className: "property-name theme-fg-color5",
|
||||
tabIndex: "",
|
||||
title: name,
|
||||
onClick: this.onFocus,
|
||||
},
|
||||
name
|
||||
)
|
||||
),
|
||||
dom.div(
|
||||
{
|
||||
className: "property-value-container",
|
||||
},
|
||||
dom.div(
|
||||
{
|
||||
className: "property-value theme-fg-color1",
|
||||
dir: "ltr",
|
||||
tabIndex: "",
|
||||
onClick: this.onFocus,
|
||||
},
|
||||
value
|
||||
)
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
});
|
@ -12,6 +12,8 @@ DevToolsModules(
|
||||
'BoxModelEditable.js',
|
||||
'BoxModelInfo.js',
|
||||
'BoxModelMain.js',
|
||||
'BoxModelProperties.js',
|
||||
'ComputedProperty.js',
|
||||
'Grid.js',
|
||||
'GridDisplaySettings.js',
|
||||
'GridList.js',
|
||||
|
@ -77,6 +77,12 @@ LayoutView.prototype = {
|
||||
this.loadHighlighterSettings();
|
||||
|
||||
let app = App({
|
||||
/**
|
||||
* Shows the box model properties under the box model if true, otherwise, hidden by
|
||||
* default.
|
||||
*/
|
||||
showBoxModelProperties: true,
|
||||
|
||||
/**
|
||||
* Hides the box-model highlighter on the currently selected element.
|
||||
*/
|
||||
|
@ -35,3 +35,7 @@ boxmodel.content=content
|
||||
# tooltip that appears when hovering over the button that allows users to edit the
|
||||
# position of an element in the page.
|
||||
boxmodel.geometryButton.tooltip=Edit position
|
||||
|
||||
# LOCALIZATION NOTE: (boxmodel.propertiesLabel) This label is displayed as the header
|
||||
# for showing and collapsing the properties underneath the box model in the layout view
|
||||
boxmodel.propertiesLabel=Box Model Properties
|
||||
|
@ -82,3 +82,55 @@ responsive.devicePixelRatio=Device Pixel Ratio
|
||||
# The argument (%1$S) is the selected device (e.g. iPhone 6) that set
|
||||
# automatically the DPR value.
|
||||
responsive.autoDPR=DPR automatically set by %1$S
|
||||
|
||||
# LOCALIZATION NOTE (responsive.customDeviceName): Default value in a form to
|
||||
# add a custom device based on an arbitrary size (no association to an existing
|
||||
# device).
|
||||
responsive.customDeviceName=Custom Device
|
||||
|
||||
# LOCALIZATION NOTE (responsive.customDeviceNameFromBase): Default value in a
|
||||
# form to add a custom device based on the properties of another. %1$S is the
|
||||
# name of the device we're staring from, such as "Apple iPhone 6".
|
||||
responsive.customDeviceNameFromBase=%1$S (Custom)
|
||||
|
||||
# LOCALIZATION NOTE (responsive.addDevice): Button text that reveals a form to
|
||||
# be used for adding custom devices.
|
||||
responsive.addDevice=Add Device
|
||||
|
||||
# LOCALIZATION NOTE (responsive.deviceAdderName): Label of form field for the
|
||||
# name of a new device. The available width is very low, so you might see
|
||||
# overlapping text if the length is much longer than 5 or so characters.
|
||||
responsive.deviceAdderName=Name
|
||||
|
||||
# LOCALIZATION NOTE (responsive.deviceAdderSize): Label of form field for the
|
||||
# size of a new device. The available width is very low, so you might see
|
||||
# overlapping text if the length is much longer than 5 or so characters.
|
||||
responsive.deviceAdderSize=Size
|
||||
|
||||
# LOCALIZATION NOTE (responsive.deviceAdderPixelRatio): Label of form field for
|
||||
# the devicePixelRatio of a new device. The available width is very low, so you
|
||||
# might see overlapping text if the length is much longer than 5 or so
|
||||
# characters.
|
||||
responsive.deviceAdderPixelRatio=DPR
|
||||
|
||||
# LOCALIZATION NOTE (responsive.deviceAdderUserAgent): Label of form field for
|
||||
# the user agent of a new device. The available width is very low, so you might
|
||||
# see overlapping text if the length is much longer than 5 or so characters.
|
||||
responsive.deviceAdderUserAgent=UA
|
||||
|
||||
# LOCALIZATION NOTE (responsive.deviceAdderTouch): Label of form field for the
|
||||
# touch input support of a new device. The available width is very low, so you
|
||||
# might see overlapping text if the length is much longer than 5 or so
|
||||
# characters.
|
||||
responsive.deviceAdderTouch=Touch
|
||||
|
||||
# LOCALIZATION NOTE (responsive.deviceAdderSave): Button text that submits a
|
||||
# form to add a new device.
|
||||
responsive.deviceAdderSave=Save
|
||||
|
||||
# LOCALIZATION NOTE (responsive.deviceDetails): Tooltip that appears when
|
||||
# hovering on a device in the device modal. %1$S is the width of the device.
|
||||
# %2$S is the height of the device. %3$S is the devicePixelRatio value of the
|
||||
# device. %4$S is the user agent of the device. %5$S is a boolean value
|
||||
# noting whether touch input is supported.
|
||||
responsive.deviceDetails=Size: %1$S x %2$S\nDPR: %3$S\nUA: %4$S\nTouch: %5$S
|
||||
|
@ -63,14 +63,25 @@ function ParamsPanel({
|
||||
name ? Object.assign(acc, { [name]: value }) : acc
|
||||
, {});
|
||||
}
|
||||
|
||||
// Form Data section
|
||||
if (formDataSections && formDataSections.length > 0) {
|
||||
let sections = formDataSections.filter((str) => /\S/.test(str)).join("&");
|
||||
object[PARAMS_FORM_DATA] =
|
||||
parseQueryString(sections)
|
||||
.reduce((acc, { name, value }) =>
|
||||
name ? Object.assign(acc, { [name]: value }) : acc
|
||||
, {});
|
||||
.reduce((map, obj) => {
|
||||
let value = map[obj.name];
|
||||
// Deal with duplicate key case (ex: multiple selection)
|
||||
if (value) {
|
||||
if (typeof value !== "object") {
|
||||
map[obj.name] = [value];
|
||||
}
|
||||
map[obj.name].push(obj.value);
|
||||
} else {
|
||||
map[obj.name] = obj.value;
|
||||
}
|
||||
return map;
|
||||
}, {});
|
||||
}
|
||||
|
||||
// Request payload section
|
||||
|
@ -10,11 +10,13 @@ const {
|
||||
LOAD_DEVICE_LIST_START,
|
||||
LOAD_DEVICE_LIST_ERROR,
|
||||
LOAD_DEVICE_LIST_END,
|
||||
REMOVE_DEVICE,
|
||||
UPDATE_DEVICE_DISPLAYED,
|
||||
UPDATE_DEVICE_MODAL_OPEN,
|
||||
UPDATE_DEVICE_MODAL,
|
||||
} = require("./index");
|
||||
const { removeDeviceAssociation } = require("./viewports");
|
||||
|
||||
const { getDevices } = require("devtools/client/shared/devices");
|
||||
const { addDevice, getDevices, removeDevice } = require("devtools/client/shared/devices");
|
||||
|
||||
const Services = require("Services");
|
||||
const DISPLAYED_DEVICES_PREF = "devtools.responsive.html.displayedDeviceList";
|
||||
@ -71,6 +73,18 @@ module.exports = {
|
||||
|
||||
updatePreferredDevices: updatePreferredDevices,
|
||||
|
||||
addCustomDevice(device) {
|
||||
return function* (dispatch) {
|
||||
// Add custom device to device storage
|
||||
yield addDevice(device, "custom");
|
||||
dispatch({
|
||||
type: ADD_DEVICE,
|
||||
device,
|
||||
deviceType: "custom",
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
addDevice(device, deviceType) {
|
||||
return {
|
||||
type: ADD_DEVICE,
|
||||
@ -86,6 +100,26 @@ module.exports = {
|
||||
};
|
||||
},
|
||||
|
||||
removeCustomDevice(device) {
|
||||
return function* (dispatch, getState) {
|
||||
// Check if the custom device is currently associated with any viewports
|
||||
let { viewports } = getState();
|
||||
for (let viewport of viewports) {
|
||||
if (viewport.device == device.name) {
|
||||
dispatch(removeDeviceAssociation(viewport.id));
|
||||
}
|
||||
}
|
||||
|
||||
// Remove custom device from device storage
|
||||
yield removeDevice(device, "custom");
|
||||
dispatch({
|
||||
type: REMOVE_DEVICE,
|
||||
device,
|
||||
deviceType: "custom",
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
updateDeviceDisplayed(device, deviceType, displayed) {
|
||||
return {
|
||||
type: UPDATE_DEVICE_DISPLAYED,
|
||||
@ -96,8 +130,8 @@ module.exports = {
|
||||
},
|
||||
|
||||
loadDevices() {
|
||||
return function* (dispatch, getState) {
|
||||
yield dispatch({ type: LOAD_DEVICE_LIST_START });
|
||||
return function* (dispatch) {
|
||||
dispatch({ type: LOAD_DEVICE_LIST_START });
|
||||
let preferredDevices = loadPreferredDevices();
|
||||
let devices;
|
||||
|
||||
@ -124,14 +158,21 @@ module.exports = {
|
||||
dispatch(module.exports.addDevice(newDevice, type));
|
||||
}
|
||||
}
|
||||
|
||||
// Add an empty "custom" type if it doesn't exist in device storage
|
||||
if (!devices.TYPES.find(type => type == "custom")) {
|
||||
dispatch(module.exports.addDeviceType("custom"));
|
||||
}
|
||||
|
||||
dispatch({ type: LOAD_DEVICE_LIST_END });
|
||||
};
|
||||
},
|
||||
|
||||
updateDeviceModalOpen(isOpen) {
|
||||
updateDeviceModal(isOpen, modalOpenedFromViewport = null) {
|
||||
return {
|
||||
type: UPDATE_DEVICE_MODAL_OPEN,
|
||||
type: UPDATE_DEVICE_MODAL,
|
||||
isOpen,
|
||||
modalOpenedFromViewport,
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -53,9 +53,12 @@ createEnum([
|
||||
// Indicates that the device list has been loaded successfully
|
||||
"LOAD_DEVICE_LIST_END",
|
||||
|
||||
// Remove the viewport's device assocation.
|
||||
// Remove a device.
|
||||
"REMOVE_DEVICE",
|
||||
|
||||
// Remove the viewport's device assocation.
|
||||
"REMOVE_DEVICE_ASSOCIATION",
|
||||
|
||||
// Resize the viewport.
|
||||
"RESIZE_VIEWPORT",
|
||||
|
||||
@ -71,7 +74,7 @@ createEnum([
|
||||
// Update the device display state in the device selector.
|
||||
"UPDATE_DEVICE_DISPLAYED",
|
||||
|
||||
// Update the device modal open state.
|
||||
"UPDATE_DEVICE_MODAL_OPEN",
|
||||
// Update the device modal state.
|
||||
"UPDATE_DEVICE_MODAL",
|
||||
|
||||
], module.exports);
|
||||
|
@ -8,7 +8,7 @@ const {
|
||||
ADD_VIEWPORT,
|
||||
CHANGE_DEVICE,
|
||||
CHANGE_PIXEL_RATIO,
|
||||
REMOVE_DEVICE,
|
||||
REMOVE_DEVICE_ASSOCIATION,
|
||||
RESIZE_VIEWPORT,
|
||||
ROTATE_VIEWPORT
|
||||
} = require("./index");
|
||||
@ -27,11 +27,12 @@ module.exports = {
|
||||
/**
|
||||
* Change the viewport device.
|
||||
*/
|
||||
changeDevice(id, device) {
|
||||
changeDevice(id, device, deviceType) {
|
||||
return {
|
||||
type: CHANGE_DEVICE,
|
||||
id,
|
||||
device,
|
||||
deviceType,
|
||||
};
|
||||
},
|
||||
|
||||
@ -49,9 +50,9 @@ module.exports = {
|
||||
/**
|
||||
* Remove the viewport's device assocation.
|
||||
*/
|
||||
removeDevice(id) {
|
||||
removeDeviceAssociation(id) {
|
||||
return {
|
||||
type: REMOVE_DEVICE,
|
||||
type: REMOVE_DEVICE_ASSOCIATION,
|
||||
id,
|
||||
};
|
||||
},
|
||||
|
@ -11,8 +11,10 @@ const { createClass, createFactory, PropTypes, DOM: dom } =
|
||||
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||
|
||||
const {
|
||||
addCustomDevice,
|
||||
removeCustomDevice,
|
||||
updateDeviceDisplayed,
|
||||
updateDeviceModalOpen,
|
||||
updateDeviceModal,
|
||||
updatePreferredDevices,
|
||||
} = require("./actions/devices");
|
||||
const { changeNetworkThrottling } = require("./actions/network-throttling");
|
||||
@ -21,7 +23,7 @@ const { changeTouchSimulation } = require("./actions/touch-simulation");
|
||||
const {
|
||||
changeDevice,
|
||||
changePixelRatio,
|
||||
removeDevice,
|
||||
removeDeviceAssociation,
|
||||
resizeViewport,
|
||||
rotateViewport,
|
||||
} = require("./actions/viewports");
|
||||
@ -44,16 +46,20 @@ let App = createClass({
|
||||
viewports: PropTypes.arrayOf(PropTypes.shape(Types.viewport)).isRequired,
|
||||
},
|
||||
|
||||
onAddCustomDevice(device) {
|
||||
this.props.dispatch(addCustomDevice(device));
|
||||
},
|
||||
|
||||
onBrowserMounted() {
|
||||
window.postMessage({ type: "browser-mounted" }, "*");
|
||||
},
|
||||
|
||||
onChangeDevice(id, device) {
|
||||
onChangeDevice(id, device, deviceType) {
|
||||
window.postMessage({
|
||||
type: "change-device",
|
||||
device,
|
||||
}, "*");
|
||||
this.props.dispatch(changeDevice(id, device.name));
|
||||
this.props.dispatch(changeDevice(id, device.name, deviceType));
|
||||
this.props.dispatch(changeTouchSimulation(device.touch));
|
||||
this.props.dispatch(changePixelRatio(id, device.pixelRatio));
|
||||
},
|
||||
@ -99,12 +105,16 @@ let App = createClass({
|
||||
window.postMessage({ type: "exit" }, "*");
|
||||
},
|
||||
|
||||
onRemoveDevice(id) {
|
||||
onRemoveCustomDevice(device) {
|
||||
this.props.dispatch(removeCustomDevice(device));
|
||||
},
|
||||
|
||||
onRemoveDeviceAssociation(id) {
|
||||
// TODO: Bug 1332754: Move messaging and logic into the action creator.
|
||||
window.postMessage({
|
||||
type: "remove-device",
|
||||
type: "remove-device-association",
|
||||
}, "*");
|
||||
this.props.dispatch(removeDevice(id));
|
||||
this.props.dispatch(removeDeviceAssociation(id));
|
||||
this.props.dispatch(changeTouchSimulation(false));
|
||||
this.props.dispatch(changePixelRatio(id, 0));
|
||||
},
|
||||
@ -125,8 +135,8 @@ let App = createClass({
|
||||
this.props.dispatch(updateDeviceDisplayed(device, deviceType, displayed));
|
||||
},
|
||||
|
||||
onUpdateDeviceModalOpen(isOpen) {
|
||||
this.props.dispatch(updateDeviceModalOpen(isOpen));
|
||||
onUpdateDeviceModal(isOpen, modalOpenedFromViewport) {
|
||||
this.props.dispatch(updateDeviceModal(isOpen, modalOpenedFromViewport));
|
||||
},
|
||||
|
||||
render() {
|
||||
@ -141,6 +151,7 @@ let App = createClass({
|
||||
} = this.props;
|
||||
|
||||
let {
|
||||
onAddCustomDevice,
|
||||
onBrowserMounted,
|
||||
onChangeDevice,
|
||||
onChangeNetworkThrottling,
|
||||
@ -149,12 +160,13 @@ let App = createClass({
|
||||
onContentResize,
|
||||
onDeviceListUpdate,
|
||||
onExit,
|
||||
onRemoveDevice,
|
||||
onRemoveCustomDevice,
|
||||
onRemoveDeviceAssociation,
|
||||
onResizeViewport,
|
||||
onRotateViewport,
|
||||
onScreenshot,
|
||||
onUpdateDeviceDisplayed,
|
||||
onUpdateDeviceModalOpen,
|
||||
onUpdateDeviceModal,
|
||||
} = this;
|
||||
|
||||
let selectedDevice = "";
|
||||
@ -165,6 +177,11 @@ let App = createClass({
|
||||
selectedPixelRatio = viewports[0].pixelRatio;
|
||||
}
|
||||
|
||||
let deviceAdderViewportTemplate = {};
|
||||
if (devices.modalOpenedFromViewport !== null) {
|
||||
deviceAdderViewportTemplate = viewports[devices.modalOpenedFromViewport];
|
||||
}
|
||||
|
||||
return dom.div(
|
||||
{
|
||||
id: "app",
|
||||
@ -191,16 +208,19 @@ let App = createClass({
|
||||
onBrowserMounted,
|
||||
onChangeDevice,
|
||||
onContentResize,
|
||||
onRemoveDevice,
|
||||
onRemoveDeviceAssociation,
|
||||
onRotateViewport,
|
||||
onResizeViewport,
|
||||
onUpdateDeviceModalOpen,
|
||||
onUpdateDeviceModal,
|
||||
}),
|
||||
DeviceModal({
|
||||
deviceAdderViewportTemplate,
|
||||
devices,
|
||||
onAddCustomDevice,
|
||||
onDeviceListUpdate,
|
||||
onRemoveCustomDevice,
|
||||
onUpdateDeviceDisplayed,
|
||||
onUpdateDeviceModalOpen,
|
||||
onUpdateDeviceModal,
|
||||
})
|
||||
);
|
||||
},
|
||||
|
252
devtools/client/responsive.html/components/device-adder.js
Normal file
252
devtools/client/responsive.html/components/device-adder.js
Normal file
@ -0,0 +1,252 @@
|
||||
/* 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/. */
|
||||
|
||||
/* eslint-env browser */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { DOM: dom, createClass, createFactory, PropTypes, addons } =
|
||||
require("devtools/client/shared/vendor/react");
|
||||
|
||||
const { getFormatStr, getStr } = require("../utils/l10n");
|
||||
const Types = require("../types");
|
||||
const ViewportDimension = createFactory(require("./viewport-dimension"));
|
||||
|
||||
module.exports = createClass({
|
||||
displayName: "DeviceAdder",
|
||||
|
||||
propTypes: {
|
||||
devices: PropTypes.shape(Types.devices).isRequired,
|
||||
viewportTemplate: PropTypes.shape(Types.viewport).isRequired,
|
||||
onAddCustomDevice: PropTypes.func.isRequired,
|
||||
},
|
||||
|
||||
mixins: [ addons.PureRenderMixin ],
|
||||
|
||||
getInitialState() {
|
||||
return {};
|
||||
},
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
let {
|
||||
width,
|
||||
height,
|
||||
} = nextProps.viewportTemplate;
|
||||
|
||||
this.setState({
|
||||
width,
|
||||
height,
|
||||
});
|
||||
},
|
||||
|
||||
onChangeSize(width, height) {
|
||||
this.setState({
|
||||
width,
|
||||
height,
|
||||
});
|
||||
},
|
||||
|
||||
onDeviceAdderShow() {
|
||||
this.setState({
|
||||
deviceAdderDisplayed: true,
|
||||
});
|
||||
},
|
||||
|
||||
onDeviceAdderSave() {
|
||||
let {
|
||||
devices,
|
||||
onAddCustomDevice,
|
||||
} = this.props;
|
||||
|
||||
if (!this.pixelRatioInput.checkValidity()) {
|
||||
return;
|
||||
}
|
||||
if (devices.custom.find(device => device.name == this.nameInput.value)) {
|
||||
this.nameInput.setCustomValidity("Device name already in use");
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
deviceAdderDisplayed: false,
|
||||
});
|
||||
onAddCustomDevice({
|
||||
name: this.nameInput.value,
|
||||
width: this.state.width,
|
||||
height: this.state.height,
|
||||
pixelRatio: parseFloat(this.pixelRatioInput.value),
|
||||
userAgent: this.userAgentInput.value,
|
||||
touch: this.touchInput.checked,
|
||||
});
|
||||
},
|
||||
|
||||
render() {
|
||||
let {
|
||||
devices,
|
||||
viewportTemplate,
|
||||
} = this.props;
|
||||
|
||||
let {
|
||||
deviceAdderDisplayed,
|
||||
height,
|
||||
width,
|
||||
} = this.state;
|
||||
|
||||
if (!deviceAdderDisplayed) {
|
||||
return dom.div(
|
||||
{
|
||||
id: "device-adder"
|
||||
},
|
||||
dom.button(
|
||||
{
|
||||
id: "device-adder-show",
|
||||
onClick: this.onDeviceAdderShow,
|
||||
},
|
||||
getStr("responsive.addDevice")
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// If a device is currently selected, fold its attributes into a single object for use
|
||||
// as the starting values of the form. If no device is selected, use the values for
|
||||
// the current window.
|
||||
let deviceName;
|
||||
let normalizedViewport = Object.assign({}, viewportTemplate);
|
||||
if (viewportTemplate.device) {
|
||||
let device = devices[viewportTemplate.deviceType].find(d => {
|
||||
return d.name == viewportTemplate.device;
|
||||
});
|
||||
deviceName = getFormatStr("responsive.customDeviceNameFromBase", device.name);
|
||||
Object.assign(normalizedViewport, {
|
||||
pixelRatio: device.pixelRatio,
|
||||
userAgent: device.userAgent,
|
||||
touch: device.touch,
|
||||
});
|
||||
} else {
|
||||
deviceName = getStr("responsive.customDeviceName");
|
||||
Object.assign(normalizedViewport, {
|
||||
pixelRatio: window.devicePixelRatio,
|
||||
userAgent: navigator.userAgent,
|
||||
touch: false,
|
||||
});
|
||||
}
|
||||
|
||||
return dom.div(
|
||||
{
|
||||
id: "device-adder"
|
||||
},
|
||||
dom.div(
|
||||
{
|
||||
id: "device-adder-content",
|
||||
},
|
||||
dom.div(
|
||||
{
|
||||
id: "device-adder-column-1",
|
||||
},
|
||||
dom.label(
|
||||
{
|
||||
id: "device-adder-name",
|
||||
},
|
||||
dom.span(
|
||||
{
|
||||
className: "device-adder-label",
|
||||
},
|
||||
getStr("responsive.deviceAdderName")
|
||||
),
|
||||
dom.input({
|
||||
defaultValue: deviceName,
|
||||
ref: input => {
|
||||
this.nameInput = input;
|
||||
},
|
||||
})
|
||||
),
|
||||
dom.label(
|
||||
{
|
||||
id: "device-adder-size",
|
||||
},
|
||||
dom.span(
|
||||
{
|
||||
className: "device-adder-label"
|
||||
},
|
||||
getStr("responsive.deviceAdderSize")
|
||||
),
|
||||
ViewportDimension({
|
||||
viewport: {
|
||||
width,
|
||||
height,
|
||||
},
|
||||
onChangeSize: this.onChangeSize,
|
||||
onRemoveDeviceAssociation: () => {},
|
||||
})
|
||||
),
|
||||
dom.label(
|
||||
{
|
||||
id: "device-adder-pixel-ratio",
|
||||
},
|
||||
dom.span(
|
||||
{
|
||||
className: "device-adder-label"
|
||||
},
|
||||
getStr("responsive.deviceAdderPixelRatio")
|
||||
),
|
||||
dom.input({
|
||||
type: "number",
|
||||
step: "any",
|
||||
defaultValue: normalizedViewport.pixelRatio,
|
||||
ref: input => {
|
||||
this.pixelRatioInput = input;
|
||||
},
|
||||
})
|
||||
)
|
||||
),
|
||||
dom.div(
|
||||
{
|
||||
id: "device-adder-column-2",
|
||||
},
|
||||
dom.label(
|
||||
{
|
||||
id: "device-adder-user-agent",
|
||||
},
|
||||
dom.span(
|
||||
{
|
||||
className: "device-adder-label"
|
||||
},
|
||||
getStr("responsive.deviceAdderUserAgent")
|
||||
),
|
||||
dom.input({
|
||||
defaultValue: normalizedViewport.userAgent,
|
||||
ref: input => {
|
||||
this.userAgentInput = input;
|
||||
},
|
||||
})
|
||||
),
|
||||
dom.label(
|
||||
{
|
||||
id: "device-adder-touch",
|
||||
},
|
||||
dom.span(
|
||||
{
|
||||
className: "device-adder-label"
|
||||
},
|
||||
getStr("responsive.deviceAdderTouch")
|
||||
),
|
||||
dom.input({
|
||||
defaultChecked: normalizedViewport.touch,
|
||||
type: "checkbox",
|
||||
ref: input => {
|
||||
this.touchInput = input;
|
||||
},
|
||||
})
|
||||
)
|
||||
),
|
||||
),
|
||||
dom.button(
|
||||
{
|
||||
id: "device-adder-save",
|
||||
onClick: this.onDeviceAdderSave,
|
||||
},
|
||||
getStr("responsive.deviceAdderSave")
|
||||
)
|
||||
);
|
||||
},
|
||||
});
|
@ -6,19 +6,24 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const { DOM: dom, createClass, PropTypes, addons } =
|
||||
const { DOM: dom, createClass, createFactory, PropTypes, addons } =
|
||||
require("devtools/client/shared/vendor/react");
|
||||
const { getStr } = require("../utils/l10n");
|
||||
|
||||
const { getStr, getFormatStr } = require("../utils/l10n");
|
||||
const Types = require("../types");
|
||||
const DeviceAdder = createFactory(require("./device-adder"));
|
||||
|
||||
module.exports = createClass({
|
||||
displayName: "DeviceModal",
|
||||
|
||||
propTypes: {
|
||||
deviceAdderViewportTemplate: PropTypes.shape(Types.viewport).isRequired,
|
||||
devices: PropTypes.shape(Types.devices).isRequired,
|
||||
onAddCustomDevice: PropTypes.func.isRequired,
|
||||
onDeviceListUpdate: PropTypes.func.isRequired,
|
||||
onRemoveCustomDevice: PropTypes.func.isRequired,
|
||||
onUpdateDeviceDisplayed: PropTypes.func.isRequired,
|
||||
onUpdateDeviceModalOpen: PropTypes.func.isRequired,
|
||||
onUpdateDeviceModal: PropTypes.func.isRequired,
|
||||
},
|
||||
|
||||
mixins: [ addons.PureRenderMixin ],
|
||||
@ -63,7 +68,7 @@ module.exports = createClass({
|
||||
devices,
|
||||
onDeviceListUpdate,
|
||||
onUpdateDeviceDisplayed,
|
||||
onUpdateDeviceModalOpen,
|
||||
onUpdateDeviceModal,
|
||||
} = this.props;
|
||||
|
||||
let preferredDevices = {
|
||||
@ -88,7 +93,7 @@ module.exports = createClass({
|
||||
}
|
||||
|
||||
onDeviceListUpdate(preferredDevices);
|
||||
onUpdateDeviceModalOpen(false);
|
||||
onUpdateDeviceModal(false);
|
||||
},
|
||||
|
||||
onKeyDown(event) {
|
||||
@ -98,16 +103,19 @@ module.exports = createClass({
|
||||
// Escape keycode
|
||||
if (event.keyCode === 27) {
|
||||
let {
|
||||
onUpdateDeviceModalOpen
|
||||
onUpdateDeviceModal
|
||||
} = this.props;
|
||||
onUpdateDeviceModalOpen(false);
|
||||
onUpdateDeviceModal(false);
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
let {
|
||||
deviceAdderViewportTemplate,
|
||||
devices,
|
||||
onUpdateDeviceModalOpen,
|
||||
onAddCustomDevice,
|
||||
onRemoveCustomDevice,
|
||||
onUpdateDeviceModal,
|
||||
} = this.props;
|
||||
|
||||
const sortedDevices = {};
|
||||
@ -128,7 +136,7 @@ module.exports = createClass({
|
||||
dom.button({
|
||||
id: "device-close-button",
|
||||
className: "toolbar-button devtools-button",
|
||||
onClick: () => onUpdateDeviceModalOpen(false),
|
||||
onClick: () => onUpdateDeviceModal(false),
|
||||
}),
|
||||
dom.div(
|
||||
{
|
||||
@ -147,10 +155,24 @@ module.exports = createClass({
|
||||
type
|
||||
),
|
||||
sortedDevices[type].map(device => {
|
||||
let details = getFormatStr(
|
||||
"responsive.deviceDetails", device.width, device.height,
|
||||
device.pixelRatio, device.userAgent, device.touch
|
||||
);
|
||||
|
||||
let removeDeviceButton;
|
||||
if (type == "custom") {
|
||||
removeDeviceButton = dom.button({
|
||||
className: "device-remove-button toolbar-button devtools-button",
|
||||
onClick: () => onRemoveCustomDevice(device),
|
||||
});
|
||||
}
|
||||
|
||||
return dom.label(
|
||||
{
|
||||
className: "device-label",
|
||||
key: device.name,
|
||||
title: details,
|
||||
},
|
||||
dom.input({
|
||||
className: "device-input-checkbox",
|
||||
@ -159,12 +181,23 @@ module.exports = createClass({
|
||||
checked: this.state[device.name],
|
||||
onChange: this.onDeviceCheckboxChange,
|
||||
}),
|
||||
device.name
|
||||
dom.span(
|
||||
{
|
||||
className: "device-name",
|
||||
},
|
||||
device.name
|
||||
),
|
||||
removeDeviceButton
|
||||
);
|
||||
})
|
||||
);
|
||||
})
|
||||
),
|
||||
DeviceAdder({
|
||||
devices,
|
||||
viewportTemplate: deviceAdderViewportTemplate,
|
||||
onAddCustomDevice,
|
||||
}),
|
||||
dom.button(
|
||||
{
|
||||
id: "device-submit-button",
|
||||
@ -176,7 +209,7 @@ module.exports = createClass({
|
||||
dom.div(
|
||||
{
|
||||
className: "modal-overlay",
|
||||
onClick: () => onUpdateDeviceModalOpen(false),
|
||||
onClick: () => onUpdateDeviceModal(false),
|
||||
}
|
||||
)
|
||||
);
|
||||
|
@ -17,9 +17,10 @@ module.exports = createClass({
|
||||
propTypes: {
|
||||
devices: PropTypes.shape(Types.devices).isRequired,
|
||||
selectedDevice: PropTypes.string.isRequired,
|
||||
viewportId: PropTypes.number.isRequired,
|
||||
onChangeDevice: PropTypes.func.isRequired,
|
||||
onResizeViewport: PropTypes.func.isRequired,
|
||||
onUpdateDeviceModalOpen: PropTypes.func.isRequired,
|
||||
onUpdateDeviceModal: PropTypes.func.isRequired,
|
||||
},
|
||||
|
||||
mixins: [ addons.PureRenderMixin ],
|
||||
@ -27,20 +28,21 @@ module.exports = createClass({
|
||||
onSelectChange({ target }) {
|
||||
let {
|
||||
devices,
|
||||
viewportId,
|
||||
onChangeDevice,
|
||||
onResizeViewport,
|
||||
onUpdateDeviceModalOpen,
|
||||
onUpdateDeviceModal,
|
||||
} = this.props;
|
||||
|
||||
if (target.value === OPEN_DEVICE_MODAL_VALUE) {
|
||||
onUpdateDeviceModalOpen(true);
|
||||
onUpdateDeviceModal(true, viewportId);
|
||||
return;
|
||||
}
|
||||
for (let type of devices.types) {
|
||||
for (let device of devices[type]) {
|
||||
if (device.name === target.value) {
|
||||
onResizeViewport(device.width, device.height);
|
||||
onChangeDevice(device);
|
||||
onChangeDevice(device, type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
DevToolsModules(
|
||||
'browser.js',
|
||||
'device-adder.js',
|
||||
'device-modal.js',
|
||||
'device-selector.js',
|
||||
'dpr-selector.js',
|
||||
|
@ -30,10 +30,10 @@ module.exports = createClass({
|
||||
onBrowserMounted: PropTypes.func.isRequired,
|
||||
onChangeDevice: PropTypes.func.isRequired,
|
||||
onContentResize: PropTypes.func.isRequired,
|
||||
onRemoveDevice: PropTypes.func.isRequired,
|
||||
onRemoveDeviceAssociation: PropTypes.func.isRequired,
|
||||
onResizeViewport: PropTypes.func.isRequired,
|
||||
onRotateViewport: PropTypes.func.isRequired,
|
||||
onUpdateDeviceModalOpen: PropTypes.func.isRequired,
|
||||
onUpdateDeviceModal: PropTypes.func.isRequired,
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
@ -114,7 +114,7 @@ module.exports = createClass({
|
||||
// the properties of the device on resize. However, at the moment, there is no
|
||||
// way to edit dPR when a device is selected, and there is no UI at all for editing
|
||||
// UA, so it's important to keep doing this for now.
|
||||
this.props.onRemoveDevice();
|
||||
this.props.onRemoveDeviceAssociation();
|
||||
}
|
||||
|
||||
this.setState({
|
||||
@ -135,7 +135,7 @@ module.exports = createClass({
|
||||
onContentResize,
|
||||
onResizeViewport,
|
||||
onRotateViewport,
|
||||
onUpdateDeviceModalOpen,
|
||||
onUpdateDeviceModal,
|
||||
} = this.props;
|
||||
|
||||
let resizeHandleClass = "viewport-resize-handle";
|
||||
@ -154,11 +154,11 @@ module.exports = createClass({
|
||||
},
|
||||
ViewportToolbar({
|
||||
devices,
|
||||
selectedDevice: viewport.device,
|
||||
viewport,
|
||||
onChangeDevice,
|
||||
onResizeViewport,
|
||||
onRotateViewport,
|
||||
onUpdateDeviceModalOpen,
|
||||
onUpdateDeviceModal,
|
||||
}),
|
||||
dom.div(
|
||||
{
|
||||
|
@ -15,8 +15,8 @@ module.exports = createClass({
|
||||
|
||||
propTypes: {
|
||||
viewport: PropTypes.shape(Types.viewport).isRequired,
|
||||
onRemoveDevice: PropTypes.func.isRequired,
|
||||
onResizeViewport: PropTypes.func.isRequired,
|
||||
onChangeSize: PropTypes.func.isRequired,
|
||||
onRemoveDeviceAssociation: PropTypes.func.isRequired,
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
@ -116,10 +116,10 @@ module.exports = createClass({
|
||||
// Change the device selector back to an unselected device
|
||||
// TODO: Bug 1332754: Logic like this probably belongs in the action creator.
|
||||
if (this.props.viewport.device) {
|
||||
this.props.onRemoveDevice();
|
||||
this.props.onRemoveDeviceAssociation();
|
||||
}
|
||||
this.props.onResizeViewport(parseInt(this.state.width, 10),
|
||||
parseInt(this.state.height, 10));
|
||||
this.props.onChangeSize(parseInt(this.state.width, 10),
|
||||
parseInt(this.state.height, 10));
|
||||
},
|
||||
|
||||
render() {
|
||||
|
@ -16,11 +16,11 @@ module.exports = createClass({
|
||||
|
||||
propTypes: {
|
||||
devices: PropTypes.shape(Types.devices).isRequired,
|
||||
selectedDevice: PropTypes.string.isRequired,
|
||||
viewport: PropTypes.shape(Types.viewport).isRequired,
|
||||
onChangeDevice: PropTypes.func.isRequired,
|
||||
onResizeViewport: PropTypes.func.isRequired,
|
||||
onRotateViewport: PropTypes.func.isRequired,
|
||||
onUpdateDeviceModalOpen: PropTypes.func.isRequired,
|
||||
onUpdateDeviceModal: PropTypes.func.isRequired,
|
||||
},
|
||||
|
||||
mixins: [ addons.PureRenderMixin ],
|
||||
@ -28,11 +28,11 @@ module.exports = createClass({
|
||||
render() {
|
||||
let {
|
||||
devices,
|
||||
selectedDevice,
|
||||
viewport,
|
||||
onChangeDevice,
|
||||
onResizeViewport,
|
||||
onRotateViewport,
|
||||
onUpdateDeviceModalOpen,
|
||||
onUpdateDeviceModal,
|
||||
} = this.props;
|
||||
|
||||
return dom.div(
|
||||
@ -41,10 +41,11 @@ module.exports = createClass({
|
||||
},
|
||||
DeviceSelector({
|
||||
devices,
|
||||
selectedDevice,
|
||||
selectedDevice: viewport.device,
|
||||
viewportId: viewport.id,
|
||||
onChangeDevice,
|
||||
onResizeViewport,
|
||||
onUpdateDeviceModalOpen,
|
||||
onUpdateDeviceModal,
|
||||
}),
|
||||
dom.button({
|
||||
className: "viewport-rotate-button toolbar-button devtools-button",
|
||||
|
@ -24,28 +24,28 @@ module.exports = createClass({
|
||||
onBrowserMounted: PropTypes.func.isRequired,
|
||||
onChangeDevice: PropTypes.func.isRequired,
|
||||
onContentResize: PropTypes.func.isRequired,
|
||||
onRemoveDevice: PropTypes.func.isRequired,
|
||||
onRemoveDeviceAssociation: PropTypes.func.isRequired,
|
||||
onResizeViewport: PropTypes.func.isRequired,
|
||||
onRotateViewport: PropTypes.func.isRequired,
|
||||
onUpdateDeviceModalOpen: PropTypes.func.isRequired,
|
||||
onUpdateDeviceModal: PropTypes.func.isRequired,
|
||||
},
|
||||
|
||||
onChangeDevice(device) {
|
||||
onChangeDevice(device, deviceType) {
|
||||
let {
|
||||
viewport,
|
||||
onChangeDevice,
|
||||
} = this.props;
|
||||
|
||||
onChangeDevice(viewport.id, device);
|
||||
onChangeDevice(viewport.id, device, deviceType);
|
||||
},
|
||||
|
||||
onRemoveDevice() {
|
||||
onRemoveDeviceAssociation() {
|
||||
let {
|
||||
viewport,
|
||||
onRemoveDevice,
|
||||
onRemoveDeviceAssociation,
|
||||
} = this.props;
|
||||
|
||||
onRemoveDevice(viewport.id);
|
||||
onRemoveDeviceAssociation(viewport.id);
|
||||
},
|
||||
|
||||
onResizeViewport(width, height) {
|
||||
@ -75,12 +75,12 @@ module.exports = createClass({
|
||||
viewport,
|
||||
onBrowserMounted,
|
||||
onContentResize,
|
||||
onUpdateDeviceModalOpen,
|
||||
onUpdateDeviceModal,
|
||||
} = this.props;
|
||||
|
||||
let {
|
||||
onChangeDevice,
|
||||
onRemoveDevice,
|
||||
onRemoveDeviceAssociation,
|
||||
onRotateViewport,
|
||||
onResizeViewport,
|
||||
} = this;
|
||||
@ -91,8 +91,8 @@ module.exports = createClass({
|
||||
},
|
||||
ViewportDimension({
|
||||
viewport,
|
||||
onRemoveDevice,
|
||||
onResizeViewport,
|
||||
onChangeSize: onResizeViewport,
|
||||
onRemoveDeviceAssociation,
|
||||
}),
|
||||
ResizableViewport({
|
||||
devices,
|
||||
@ -103,10 +103,10 @@ module.exports = createClass({
|
||||
onBrowserMounted,
|
||||
onChangeDevice,
|
||||
onContentResize,
|
||||
onRemoveDevice,
|
||||
onRemoveDeviceAssociation,
|
||||
onResizeViewport,
|
||||
onRotateViewport,
|
||||
onUpdateDeviceModalOpen,
|
||||
onUpdateDeviceModal,
|
||||
})
|
||||
);
|
||||
},
|
||||
|
@ -22,10 +22,10 @@ module.exports = createClass({
|
||||
onBrowserMounted: PropTypes.func.isRequired,
|
||||
onChangeDevice: PropTypes.func.isRequired,
|
||||
onContentResize: PropTypes.func.isRequired,
|
||||
onRemoveDevice: PropTypes.func.isRequired,
|
||||
onRemoveDeviceAssociation: PropTypes.func.isRequired,
|
||||
onResizeViewport: PropTypes.func.isRequired,
|
||||
onRotateViewport: PropTypes.func.isRequired,
|
||||
onUpdateDeviceModalOpen: PropTypes.func.isRequired,
|
||||
onUpdateDeviceModal: PropTypes.func.isRequired,
|
||||
},
|
||||
|
||||
render() {
|
||||
@ -37,10 +37,10 @@ module.exports = createClass({
|
||||
onBrowserMounted,
|
||||
onChangeDevice,
|
||||
onContentResize,
|
||||
onRemoveDevice,
|
||||
onRemoveDeviceAssociation,
|
||||
onResizeViewport,
|
||||
onRotateViewport,
|
||||
onUpdateDeviceModalOpen,
|
||||
onUpdateDeviceModal,
|
||||
} = this.props;
|
||||
|
||||
return dom.div(
|
||||
@ -58,10 +58,10 @@ module.exports = createClass({
|
||||
onBrowserMounted,
|
||||
onChangeDevice,
|
||||
onContentResize,
|
||||
onRemoveDevice,
|
||||
onRemoveDeviceAssociation,
|
||||
onResizeViewport,
|
||||
onRotateViewport,
|
||||
onUpdateDeviceModalOpen,
|
||||
onUpdateDeviceModal,
|
||||
});
|
||||
})
|
||||
);
|
||||
|
@ -38,7 +38,8 @@
|
||||
}
|
||||
|
||||
#root,
|
||||
html, body {
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
@ -366,6 +367,7 @@ select > option.divider {
|
||||
.viewport-dimension-editable.editing,
|
||||
.viewport-dimension-input.editing {
|
||||
color: var(--viewport-active-color);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.viewport-dimension-editable.editing {
|
||||
@ -424,7 +426,7 @@ select > option.divider {
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 642px;
|
||||
height: 612px;
|
||||
height: 650px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@ -457,9 +459,9 @@ select > option.divider {
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
overflow: auto;
|
||||
height: 550px;
|
||||
height: 515px;
|
||||
width: 600px;
|
||||
margin: 20px;
|
||||
margin: 20px 20px 0;
|
||||
}
|
||||
|
||||
#device-close-button,
|
||||
@ -494,12 +496,29 @@ select > option.divider {
|
||||
padding-bottom: 3px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
/* Largest size without horizontal scrollbars */
|
||||
max-width: 181px;
|
||||
}
|
||||
|
||||
.device-input-checkbox {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.device-name {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.device-remove-button,
|
||||
.device-remove-button::before {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.device-remove-button::before {
|
||||
background-image: url("./images/close.svg");
|
||||
margin: -6px 0 0 -6px;
|
||||
}
|
||||
|
||||
#device-submit-button {
|
||||
background-color: var(--theme-tab-toolbar-background);
|
||||
border-width: 1px 0 0 0;
|
||||
@ -509,6 +528,8 @@ select > option.divider {
|
||||
color: var(--theme-body-color);
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
#device-submit-button:hover {
|
||||
@ -519,3 +540,74 @@ select > option.divider {
|
||||
background-color: var(--submit-button-active-background-color);
|
||||
color: var(--submit-button-active-color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Device Adder
|
||||
*/
|
||||
|
||||
#device-adder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
#device-adder-content {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#device-adder-column-1 {
|
||||
flex: 1;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#device-adder-column-2 {
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
#device-adder button {
|
||||
background-color: var(--theme-tab-toolbar-background);
|
||||
border: 1px solid var(--theme-splitter-color);
|
||||
border-radius: 2px;
|
||||
color: var(--theme-body-color);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#device-adder label {
|
||||
display: flex;
|
||||
margin-bottom: 5px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#device-adder label > input,
|
||||
#device-adder label > .viewport-dimension {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#device-adder input {
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
text-align: center;
|
||||
color: var(--theme-body-color-inactive);
|
||||
transition: all 0.25s ease;
|
||||
}
|
||||
|
||||
#device-adder input:focus {
|
||||
color: var(--viewport-active-color);
|
||||
}
|
||||
|
||||
#device-adder label > input:focus,
|
||||
#device-adder label > .viewport-dimension:focus {
|
||||
border-bottom: 1px solid var(--theme-selection-background);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.device-adder-label {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
min-width: 35px;
|
||||
}
|
||||
|
||||
#device-adder #device-adder-save {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
@ -467,8 +467,8 @@ ResponsiveUI.prototype = {
|
||||
case "exit":
|
||||
this.onExit();
|
||||
break;
|
||||
case "remove-device":
|
||||
this.onRemoveDevice(event);
|
||||
case "remove-device-association":
|
||||
this.onRemoveDeviceAssociation(event);
|
||||
break;
|
||||
}
|
||||
},
|
||||
@ -512,7 +512,7 @@ ResponsiveUI.prototype = {
|
||||
ResponsiveUIManager.closeIfNeeded(browserWindow, tab);
|
||||
},
|
||||
|
||||
onRemoveDevice: Task.async(function* (event) {
|
||||
onRemoveDeviceAssociation: Task.async(function* (event) {
|
||||
yield this.updateUserAgent();
|
||||
yield this.updateDPPX();
|
||||
yield this.updateTouchSimulation();
|
||||
|
@ -10,8 +10,9 @@ const {
|
||||
LOAD_DEVICE_LIST_START,
|
||||
LOAD_DEVICE_LIST_ERROR,
|
||||
LOAD_DEVICE_LIST_END,
|
||||
REMOVE_DEVICE,
|
||||
UPDATE_DEVICE_DISPLAYED,
|
||||
UPDATE_DEVICE_MODAL_OPEN,
|
||||
UPDATE_DEVICE_MODAL,
|
||||
} = require("../actions/index");
|
||||
|
||||
const Types = require("../types");
|
||||
@ -19,6 +20,7 @@ const Types = require("../types");
|
||||
const INITIAL_DEVICES = {
|
||||
types: [],
|
||||
isModalOpen: false,
|
||||
modalOpenedFromViewport: null,
|
||||
listState: Types.deviceListState.INITIALIZED,
|
||||
};
|
||||
|
||||
@ -69,9 +71,23 @@ let reducers = {
|
||||
});
|
||||
},
|
||||
|
||||
[UPDATE_DEVICE_MODAL_OPEN](devices, { isOpen }) {
|
||||
[REMOVE_DEVICE](devices, { device, deviceType }) {
|
||||
let index = devices[deviceType].indexOf(device);
|
||||
if (index < 0) {
|
||||
return devices;
|
||||
}
|
||||
|
||||
let list = [...devices[deviceType]];
|
||||
list.splice(index, 1);
|
||||
return Object.assign({}, devices, {
|
||||
[deviceType]: list
|
||||
});
|
||||
},
|
||||
|
||||
[UPDATE_DEVICE_MODAL](devices, { isOpen, modalOpenedFromViewport }) {
|
||||
return Object.assign({}, devices, {
|
||||
isModalOpen: isOpen,
|
||||
modalOpenedFromViewport,
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -8,7 +8,7 @@ const {
|
||||
ADD_VIEWPORT,
|
||||
CHANGE_DEVICE,
|
||||
CHANGE_PIXEL_RATIO,
|
||||
REMOVE_DEVICE,
|
||||
REMOVE_DEVICE_ASSOCIATION,
|
||||
RESIZE_VIEWPORT,
|
||||
ROTATE_VIEWPORT,
|
||||
} = require("../actions/index");
|
||||
@ -19,6 +19,7 @@ const INITIAL_VIEWPORTS = [];
|
||||
const INITIAL_VIEWPORT = {
|
||||
id: nextViewportId++,
|
||||
device: "",
|
||||
deviceType: "",
|
||||
width: 320,
|
||||
height: 480,
|
||||
pixelRatio: {
|
||||
@ -36,7 +37,7 @@ let reducers = {
|
||||
return [...viewports, Object.assign({}, INITIAL_VIEWPORT)];
|
||||
},
|
||||
|
||||
[CHANGE_DEVICE](viewports, { id, device }) {
|
||||
[CHANGE_DEVICE](viewports, { id, device, deviceType }) {
|
||||
return viewports.map(viewport => {
|
||||
if (viewport.id !== id) {
|
||||
return viewport;
|
||||
@ -44,6 +45,7 @@ let reducers = {
|
||||
|
||||
return Object.assign({}, viewport, {
|
||||
device,
|
||||
deviceType,
|
||||
});
|
||||
});
|
||||
},
|
||||
@ -62,7 +64,7 @@ let reducers = {
|
||||
});
|
||||
},
|
||||
|
||||
[REMOVE_DEVICE](viewports, { id }) {
|
||||
[REMOVE_DEVICE_ASSOCIATION](viewports, { id }) {
|
||||
return viewports.map(viewport => {
|
||||
if (viewport.id !== id) {
|
||||
return viewport;
|
||||
@ -70,6 +72,7 @@ let reducers = {
|
||||
|
||||
return Object.assign({}, viewport, {
|
||||
device: "",
|
||||
deviceType: "",
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -17,6 +17,7 @@ support-files =
|
||||
!/devtools/client/shared/test/test-actor-registry.js
|
||||
|
||||
[browser_device_change.js]
|
||||
[browser_device_custom.js]
|
||||
[browser_device_modal_error.js]
|
||||
[browser_device_modal_exit.js]
|
||||
[browser_device_modal_submit.js]
|
||||
|
@ -0,0 +1,152 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test adding and removing custom devices via the modal.
|
||||
|
||||
const device = {
|
||||
name: "Test Device",
|
||||
width: 400,
|
||||
height: 570,
|
||||
pixelRatio: 1.5,
|
||||
userAgent: "Mozilla/5.0 (Mobile; rv:39.0) Gecko/39.0 Firefox/39.0",
|
||||
touch: true,
|
||||
firefoxOS: false,
|
||||
os: "android",
|
||||
};
|
||||
|
||||
const TEST_URL = "data:text/html;charset=utf-8,";
|
||||
const Types = require("devtools/client/responsive.html/types");
|
||||
|
||||
addRDMTask(TEST_URL, function* ({ ui }) {
|
||||
let { toolWindow } = ui;
|
||||
let { store, document } = toolWindow;
|
||||
let React = toolWindow.require("devtools/client/shared/vendor/react");
|
||||
let { Simulate } = React.addons.TestUtils;
|
||||
|
||||
// Wait until the viewport has been added and the device list has been loaded
|
||||
yield waitUntilState(store, state => state.viewports.length == 1
|
||||
&& state.devices.listState == Types.deviceListState.LOADED);
|
||||
|
||||
let deviceSelector = document.querySelector(".viewport-device-selector");
|
||||
let submitButton = document.querySelector("#device-submit-button");
|
||||
|
||||
openDeviceModal(ui);
|
||||
|
||||
info("Reveal device adder form, check that defaults match the viewport");
|
||||
let adderShow = document.querySelector("#device-adder-show");
|
||||
Simulate.click(adderShow);
|
||||
testDeviceAdder(ui, {
|
||||
name: "Custom Device",
|
||||
width: 320,
|
||||
height: 480,
|
||||
pixelRatio: window.devicePixelRatio,
|
||||
userAgent: navigator.userAgent,
|
||||
touch: false,
|
||||
});
|
||||
|
||||
info("Fill out device adder form and save");
|
||||
setDeviceAdder(ui, device);
|
||||
let adderSave = document.querySelector("#device-adder-save");
|
||||
let saved = waitUntilState(store, state => state.devices.custom.length == 1);
|
||||
Simulate.click(adderSave);
|
||||
yield saved;
|
||||
|
||||
info("Enable device in modal");
|
||||
let deviceCb = [...document.querySelectorAll(".device-input-checkbox")].find(cb => {
|
||||
return cb.value == device.name;
|
||||
});
|
||||
ok(deviceCb, "Custom device checkbox added to modal");
|
||||
deviceCb.click();
|
||||
Simulate.click(submitButton);
|
||||
|
||||
info("Look for custom device in device selector");
|
||||
let selectorOption = [...deviceSelector.options].find(opt => opt.value == device.name);
|
||||
ok(selectorOption, "Custom device option added to device selector");
|
||||
});
|
||||
|
||||
addRDMTask(TEST_URL, function* ({ ui }) {
|
||||
let { toolWindow } = ui;
|
||||
let { store, document } = toolWindow;
|
||||
let React = toolWindow.require("devtools/client/shared/vendor/react");
|
||||
let { Simulate } = React.addons.TestUtils;
|
||||
|
||||
// Wait until the viewport has been added and the device list has been loaded
|
||||
yield waitUntilState(store, state => state.viewports.length == 1
|
||||
&& state.devices.listState == Types.deviceListState.LOADED);
|
||||
|
||||
let deviceSelector = document.querySelector(".viewport-device-selector");
|
||||
let submitButton = document.querySelector("#device-submit-button");
|
||||
|
||||
info("Select existing device from the selector");
|
||||
yield selectDevice(ui, "Test Device");
|
||||
|
||||
openDeviceModal(ui);
|
||||
|
||||
info("Reveal device adder form, check that defaults are based on selected device");
|
||||
let adderShow = document.querySelector("#device-adder-show");
|
||||
Simulate.click(adderShow);
|
||||
testDeviceAdder(ui, Object.assign({}, device, {
|
||||
name: "Test Device (Custom)",
|
||||
}));
|
||||
|
||||
info("Remove previously added custom device");
|
||||
let deviceRemoveButton = document.querySelector(".device-remove-button");
|
||||
let removed = waitUntilState(store, state => state.devices.custom.length == 0);
|
||||
Simulate.click(deviceRemoveButton);
|
||||
yield removed;
|
||||
Simulate.click(submitButton);
|
||||
|
||||
info("Ensure custom device was removed from device selector");
|
||||
yield waitUntilState(store, state => state.viewports[0].device == "");
|
||||
is(deviceSelector.value, "", "Device selector reset to no device");
|
||||
let selectorOption = [...deviceSelector.options].find(opt => opt.value == device.name);
|
||||
ok(!selectorOption, "Custom device option removed from device selector");
|
||||
});
|
||||
|
||||
function testDeviceAdder(ui, expected) {
|
||||
let { document } = ui.toolWindow;
|
||||
|
||||
let nameInput = document.querySelector("#device-adder-name input");
|
||||
let [ widthInput, heightInput ] = document.querySelectorAll("#device-adder-size input");
|
||||
let pixelRatioInput = document.querySelector("#device-adder-pixel-ratio input");
|
||||
let userAgentInput = document.querySelector("#device-adder-user-agent input");
|
||||
let touchInput = document.querySelector("#device-adder-touch input");
|
||||
|
||||
is(nameInput.value, expected.name, "Device name matches");
|
||||
is(parseInt(widthInput.value, 10), expected.width, "Width matches");
|
||||
is(parseInt(heightInput.value, 10), expected.height, "Height matches");
|
||||
is(parseFloat(pixelRatioInput.value), expected.pixelRatio,
|
||||
"devicePixelRatio matches");
|
||||
is(userAgentInput.value, expected.userAgent, "User agent matches");
|
||||
is(touchInput.checked, expected.touch, "Touch matches");
|
||||
}
|
||||
|
||||
function setDeviceAdder(ui, value) {
|
||||
let { toolWindow } = ui;
|
||||
let { document } = ui.toolWindow;
|
||||
let React = toolWindow.require("devtools/client/shared/vendor/react");
|
||||
let { Simulate } = React.addons.TestUtils;
|
||||
|
||||
let nameInput = document.querySelector("#device-adder-name input");
|
||||
let [ widthInput, heightInput ] = document.querySelectorAll("#device-adder-size input");
|
||||
let pixelRatioInput = document.querySelector("#device-adder-pixel-ratio input");
|
||||
let userAgentInput = document.querySelector("#device-adder-user-agent input");
|
||||
let touchInput = document.querySelector("#device-adder-touch input");
|
||||
|
||||
nameInput.value = value.name;
|
||||
Simulate.change(nameInput);
|
||||
widthInput.value = value.width;
|
||||
Simulate.change(widthInput);
|
||||
Simulate.blur(widthInput);
|
||||
heightInput.value = value.height;
|
||||
Simulate.change(heightInput);
|
||||
Simulate.blur(heightInput);
|
||||
pixelRatioInput.value = value.pixelRatio;
|
||||
Simulate.change(pixelRatioInput);
|
||||
userAgentInput.value = value.userAgent;
|
||||
Simulate.change(userAgentInput);
|
||||
touchInput.checked = value.touch;
|
||||
Simulate.change(touchInput);
|
||||
}
|
@ -60,6 +60,7 @@ registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("devtools.responsive.html.enabled");
|
||||
Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
|
||||
asyncStorage.removeItem("devtools.devices.url_cache");
|
||||
asyncStorage.removeItem("devtools.devices.local");
|
||||
});
|
||||
|
||||
// This depends on the "devtools.responsive.html.enabled" pref
|
||||
@ -242,30 +243,20 @@ function openDeviceModal({ toolWindow }) {
|
||||
}
|
||||
|
||||
function changeSelectValue({ toolWindow }, selector, value) {
|
||||
let { document } = toolWindow;
|
||||
let React = toolWindow.require("devtools/client/shared/vendor/react");
|
||||
let { Simulate } = React.addons.TestUtils;
|
||||
|
||||
info(`Selecting ${value} in ${selector}.`);
|
||||
|
||||
return new Promise(resolve => {
|
||||
let select = toolWindow.document.querySelector(selector);
|
||||
isnot(select, null, `selector "${selector}" should match an existing element.`);
|
||||
let select = document.querySelector(selector);
|
||||
isnot(select, null, `selector "${selector}" should match an existing element.`);
|
||||
|
||||
let option = [...select.options].find(o => o.value === String(value));
|
||||
isnot(option, undefined, `value "${value}" should match an existing option.`);
|
||||
let option = [...select.options].find(o => o.value === String(value));
|
||||
isnot(option, undefined, `value "${value}" should match an existing option.`);
|
||||
|
||||
let event = new toolWindow.UIEvent("change", {
|
||||
view: toolWindow,
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
});
|
||||
|
||||
select.addEventListener("change", () => {
|
||||
is(select.value, value,
|
||||
`Select's option with value "${value}" should be selected.`);
|
||||
resolve();
|
||||
}, { once: true });
|
||||
|
||||
select.value = value;
|
||||
select.dispatchEvent(event);
|
||||
});
|
||||
select.value = value;
|
||||
Simulate.change(select);
|
||||
}
|
||||
|
||||
const selectDevice = (ui, value) => Promise.all([
|
||||
|
@ -34,7 +34,7 @@ add_task(function* () {
|
||||
let viewport = getState().viewports[0];
|
||||
equal(viewport.device, "", "Default device is unselected");
|
||||
|
||||
dispatch(changeDevice(0, "Firefox OS Flame"));
|
||||
dispatch(changeDevice(0, "Firefox OS Flame", "phones"));
|
||||
|
||||
viewport = getState().viewports[0];
|
||||
equal(viewport.device, "Firefox OS Flame",
|
||||
|
@ -89,6 +89,9 @@ exports.devices = {
|
||||
// Whether or not the device modal is open
|
||||
isModalOpen: PropTypes.bool,
|
||||
|
||||
// Viewport id that triggered the modal to open
|
||||
modalOpenedFromViewport: PropTypes.number,
|
||||
|
||||
// Device list state, possible values are exported above in an enum
|
||||
listState: PropTypes.oneOf(Object.keys(exports.deviceListState)),
|
||||
|
||||
@ -140,6 +143,9 @@ exports.viewport = {
|
||||
// The currently selected device applied to the viewport
|
||||
device: PropTypes.string,
|
||||
|
||||
// The currently selected device type applied to the viewport
|
||||
deviceType: PropTypes.string,
|
||||
|
||||
// The width of the viewport
|
||||
width: PropTypes.number,
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user