author | Coroiu Cristina <ccoroiu@mozilla.com> |
Fri, 13 Apr 2018 19:09:10 +0300 | |
changeset 413251 | e2dc38fde43a9296fdb6d5bc2ab2e91a8298ee55 |
parent 413250 | 9bacb7ba98d4e17e9de5849f3efd3a3c94aa2eb9 (current diff) |
parent 413183 | 2243b83d2c5ca7a13b592e0fed4639657a2882f1 (diff) |
child 413252 | bc424899c8f89c3765cdb7b9325f83d2541e0aea |
push id | 33840 |
push user | apavel@mozilla.com |
push date | Fri, 13 Apr 2018 21:56:54 +0000 |
treeherder | mozilla-central@6547c27303bc [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 61.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/.eslintignore +++ b/.eslintignore @@ -111,44 +111,41 @@ devtools/client/shared/components/test/m devtools/client/shared/shim/test/test_*.html devtools/client/shared/test/browser_toolbar_webconsole_errors_count.html devtools/client/storage/test/*.html !devtools/client/storage/test/storage-cookies.html !devtools/client/storage/test/storage-overflow.html !devtools/client/storage/test/storage-search.html !devtools/client/storage/test/storage-unsecured-iframe.html !devtools/client/storage/test/storage-unsecured-iframe-usercontextid.html -devtools/client/webaudioeditor/** -devtools/client/webconsole/old/net/** -!devtools/client/webconsole/test/mochitest/** -devtools/client/webconsole/old/test/** -devtools/client/webconsole/old/webconsole.js -devtools/client/webide/** -!devtools/client/webide/components/webideCli.js devtools/server/tests/browser/storage-*.html !devtools/server/tests/browser/storage-unsecured-iframe.html devtools/server/tests/browser/stylesheets-nested-iframes.html -devtools/server/tests/unit/xpcshell_debugging_script.js devtools/client/shared/webpack/shims/test/test_clipboard.html devtools/shared/qrcode/tests/mochitest/test_decode.html devtools/shared/tests/mochitest/*.html devtools/shared/webconsole/test/test_*.html # Soon to be removed devtools/client/commandline/** # Soon to be removed, the new/ directory is explicitly excluded below due to # also being an imported repository. devtools/client/debugger/** +# Soon to be removed +devtools/client/webconsole/old/net/** +devtools/client/webconsole/old/test/** +devtools/client/webconsole/old/webconsole.js # Ignore devtools imported repositories devtools/client/debugger/new/** devtools/client/shared/components/reps/** # Ignore devtools preferences files devtools/client/preferences/** +devtools/client/webide/preferences/** devtools/shared/preferences/** devtools/startup/preferences/devtools-startup.js # Ignore devtools third-party libs devtools/shared/jsbeautify/* devtools/shared/acorn/* devtools/shared/gcli/source/* devtools/shared/node-properties/* @@ -175,16 +172,19 @@ devtools/client/framework/test/code_* devtools/client/inspector/markup/test/events_bundle.js devtools/client/netmonitor/test/xhr_bundle.js devtools/client/webconsole/test/mochitest/code_bundle_nosource.js devtools/client/webconsole/test/mochitest/code_bundle_invalidmap.js devtools/server/tests/unit/babel_and_browserify_script_with_source_map.js devtools/server/tests/unit/setBreakpoint* devtools/server/tests/unit/sourcemapped.js +# devtools specific format test file +devtools/server/tests/unit/xpcshell_debugging_script.js + # dom/ exclusions dom/abort/** dom/animation/** dom/archivereader/** dom/asmjscache/** dom/audiochannel/** dom/base/** dom/battery/** @@ -338,17 +338,16 @@ security/nss/** # Uses `#filter substitution` services/sync/modules/constants.js services/sync/services-sync.js # Servo is imported. servo/** # Remote protocol exclusions -testing/marionette/test_*.js testing/marionette/atom.js testing/marionette/legacyaction.js testing/marionette/client testing/marionette/doc testing/marionette/harness # other testing/ exclusions testing/mochitest/**
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -494,18 +494,17 @@ const gSessionHistoryObserver = { gURLBar.editor.transactionManager.clear(); } }; const gStoragePressureObserver = { _lastNotificationTime: -1, observe(subject, topic, data) { - if (topic != "QuotaManager::StoragePressure" || - !Services.prefs.getBoolPref("browser.storageManager.enabled")) { + if (topic != "QuotaManager::StoragePressure") { return; } const NOTIFICATION_VALUE = "storage-pressure-notification"; let notificationBox = document.getElementById("high-priority-global-notificationbox"); if (notificationBox.getNotificationWithValue(NOTIFICATION_VALUE)) { // Do not display the 2nd notification when there is already one return;
--- a/browser/base/content/test/general/browser_storagePressure_notification.js +++ b/browser/base/content/test/general/browser_storagePressure_notification.js @@ -18,17 +18,16 @@ function openAboutPrefPromise() { TestUtils.topicObserved("privacy-pane-loaded", () => true) ]; return Promise.all(promises); } // Test only displaying notification once within the given interval add_task(async function() { const TEST_NOTIFICATION_INTERVAL_MS = 2000; - await SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]}); await SpecialPowers.pushPrefEnv({set: [["browser.storageManager.pressureNotification.minIntervalMS", TEST_NOTIFICATION_INTERVAL_MS]]}); // Commenting this to see if we really need it // await SpecialPowers.pushPrefEnv({set: [["privacy.reduceTimerPrecision", false]]}); await notifyStoragePressure(); let notificationbox = document.getElementById("high-priority-global-notificationbox"); let notification = notificationbox.getNotificationWithValue("storage-pressure-notification"); ok(notification instanceof XULElement, "Should display storage pressure notification"); @@ -42,17 +41,16 @@ add_task(async function() { await notifyStoragePressure(); notification = notificationbox.getNotificationWithValue("storage-pressure-notification"); ok(notification instanceof XULElement, "Should display storage pressure notification after the given interval"); notification.close(); }); // Test guiding user to the about:preferences when usage exceeds the given threshold add_task(async function() { - await SpecialPowers.pushPrefEnv({ set: [["browser.storageManager.enabled", true]] }); await SpecialPowers.pushPrefEnv({ set: [["browser.storageManager.pressureNotification.minIntervalMS", 0]] }); let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "https://example.com"); const BYTES_IN_GIGABYTE = 1073741824; const USAGE_THRESHOLD_BYTES = BYTES_IN_GIGABYTE * Services.prefs.getIntPref("browser.storageManager.pressureNotification.usageThresholdGB"); await notifyStoragePressure(USAGE_THRESHOLD_BYTES); let notificationbox = document.getElementById("high-priority-global-notificationbox"); @@ -69,17 +67,16 @@ add_task(async function() { is_element_visible(siteDataGroup, "Should open to the siteDataGroup section in about:preferences"); BrowserTestUtils.removeTab(aboutPrefTab); BrowserTestUtils.removeTab(tab); }); // Test not displaying the 2nd notification if one is already being displayed add_task(async function() { const TEST_NOTIFICATION_INTERVAL_MS = 0; - await SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]}); await SpecialPowers.pushPrefEnv({set: [["browser.storageManager.pressureNotification.minIntervalMS", TEST_NOTIFICATION_INTERVAL_MS]]}); await notifyStoragePressure(); await notifyStoragePressure(); let notificationbox = document.getElementById("high-priority-global-notificationbox"); let allNotifications = notificationbox.allNotifications; let pressureNotificationCount = 0; allNotifications.forEach(notification => {
--- a/browser/components/customizableui/CustomizableWidgets.jsm +++ b/browser/components/customizableui/CustomizableWidgets.jsm @@ -739,17 +739,19 @@ if (Services.prefs.getBoolPref("identity item.setAttribute("class", "subviewbutton"); item.setAttribute("targetURI", tabInfo.url); item.setAttribute("label", tabInfo.title != "" ? tabInfo.title : tabInfo.url); item.setAttribute("image", tabInfo.icon); item.setAttribute("tooltiptext", tooltipText); // We need to use "click" instead of "command" here so openUILink // respects different buttons (eg, to open in a new tab). item.addEventListener("click", e => { - doc.defaultView.openWebLinkIn(tabInfo.url, e); + doc.defaultView.openUILink(tabInfo.url, e, { + triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}) + }); if (doc.defaultView.whereToOpenLink(e) != "current") { e.preventDefault(); e.stopPropagation(); } else { CustomizableUI.hidePanelForNode(item); } BrowserUITelemetry.countSyncedTabEvent("open", "toolbarbutton-subview"); });
--- a/browser/components/customizableui/test/browser_synced_tabs_menu.js +++ b/browser/components/customizableui/test/browser_synced_tabs_menu.js @@ -14,16 +14,18 @@ ChromeUtils.defineModuleGetter(this, "UI // These are available on the widget implementation, but it seems impossible // to grab that impl at runtime. const DECKINDEX_TABS = 0; const DECKINDEX_TABSDISABLED = 1; const DECKINDEX_FETCHING = 2; const DECKINDEX_NOCLIENTS = 3; +const SAMPLE_TAB_URL = "https://example.com/"; + var initialLocation = gBrowser.currentURI.spec; var newTab = null; // A helper to notify there are new tabs. Returns a promise that is resolved // once the UI has been updated. function updateTabsPanel() { let promiseTabsUpdated = promiseObserverNotified("synced-tabs-menu:test:tabs-updated"); Services.obs.notifyObservers(null, SyncedTabs.TOPIC_TABS_CHANGED); @@ -330,24 +332,25 @@ add_task(async function() { name: "My Desktop", lastModified: 1492201200, tabs: function() { let allTabsDesktop = []; // We choose 77 tabs, because TABS_PER_PAGE is 25, which means // on the second to last page we should have 22 items shown // (because we have to show at least NEXT_PAGE_MIN_TABS=5 tabs on the last page) for (let i = 1; i <= 77; i++) { - allTabsDesktop.push({ title: "Tab #" + i }); + allTabsDesktop.push({ title: "Tab #" + i, url: SAMPLE_TAB_URL }); } return allTabsDesktop; }(), } ]); }; + gSync.updateAllUI({ status: UIState.STATUS_SIGNED_IN, lastSync: new Date(), email: "foo@bar.com" }); await document.getElementById("nav-bar").overflowable.show(); let tabsUpdatedPromise = promiseObserverNotified("synced-tabs-menu:test:tabs-updated"); let syncPanel = document.getElementById("PanelUI-remotetabs"); let viewShownPromise = BrowserTestUtils.waitForEvent(syncPanel, "ViewShown"); let syncButton = document.getElementById("sync-button"); @@ -365,29 +368,38 @@ add_task(async function() { let tabList = document.getElementById("PanelUI-remotetabs-tabslist"); let node = tabList.firstChild; is(node.getAttribute("itemtype"), "client", "node is a client entry"); is(node.textContent, "My Desktop", "correct client"); for (let i = 0; i < tabsShownCount; i++) { node = node.nextSibling; is(node.getAttribute("itemtype"), "tab", "node is a tab"); is(node.getAttribute("label"), "Tab #" + (i + 1), "the tab is the correct one"); + is(node.getAttribute("targetURI"), SAMPLE_TAB_URL, "url is the correct one"); } let showMoreButton; if (showMoreLabel) { node = showMoreButton = node.nextSibling; is(node.getAttribute("itemtype"), "showmorebutton", "node is a show more button"); is(node.getAttribute("label"), showMoreLabel); } node = node.nextSibling; is(node, null, "no more entries"); return showMoreButton; } + async function checkCanOpenURL() { + let tabList = document.getElementById("PanelUI-remotetabs-tabslist"); + let node = tabList.firstChild.nextSibling; + let promiseTabOpened = BrowserTestUtils.waitForLocationChange(gBrowser, SAMPLE_TAB_URL); + node.click(); + await promiseTabOpened; + } + let showMoreButton; function clickShowMoreButton() { let promise = promiseObserverNotified("synced-tabs-menu:test:tabs-updated"); showMoreButton.click(); return promise; } showMoreButton = checkTabsPage(25, "Show More"); @@ -395,11 +407,11 @@ add_task(async function() { showMoreButton = checkTabsPage(50, "Show More"); await clickShowMoreButton(); showMoreButton = checkTabsPage(72, "Show All"); await clickShowMoreButton(); checkTabsPage(77, null); - - await hideOverflow(); + /* calling this will close the overflow menu */ + await checkCanOpenURL(); });
--- a/browser/components/extensions/ExtensionPopups.jsm +++ b/browser/components/extensions/ExtensionPopups.jsm @@ -363,17 +363,17 @@ class BasePopup { // of the extension content. Passing `null` should be treated the same as no argument, // which is why we can't use default parameters here. if (!background) { background = "#fff"; } this.panel.style.setProperty("--arrowpanel-background", background); if (background == "#fff") { // Set a usable default color that work with the default background-color. - this.panel.style.setProperty("--arrowpanel-border-color", "hsla(210,4%,10%,.05)"); + this.panel.style.setProperty("--arrowpanel-border-color", "hsla(210,4%,10%,.15)"); } this.background = background; } } /** * A map of active popups for a given browser window. *
--- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -2762,19 +2762,17 @@ const ContentPermissionIntegration = { switch (type) { case "geolocation": { return new PermissionUI.GeolocationPermissionPrompt(request); } case "desktop-notification": { return new PermissionUI.DesktopNotificationPermissionPrompt(request); } case "persistent-storage": { - if (Services.prefs.getBoolPref("browser.storageManager.enabled")) { - return new PermissionUI.PersistentStoragePermissionPrompt(request); - } + return new PermissionUI.PersistentStoragePermissionPrompt(request); } case "midi": { return new PermissionUI.MIDIPermissionPrompt(request); } } return undefined; }, };
--- a/browser/components/preferences/in-content/privacy.js +++ b/browser/components/preferences/in-content/privacy.js @@ -332,35 +332,31 @@ var gPrivacyPane = { document.l10n.setAttributes(checkbox, "permissions-notification-pause"); if (AlertsServiceDND.manualDoNotDisturb) { let notificationsDoNotDisturb = document.getElementById("notificationsDoNotDisturb"); notificationsDoNotDisturb.setAttribute("checked", true); } } - if (Services.prefs.getBoolPref("browser.storageManager.enabled")) { - Services.obs.addObserver(this, "sitedatamanager:sites-updated"); - Services.obs.addObserver(this, "sitedatamanager:updating-sites"); - let unload = () => { - window.removeEventListener("unload", unload); - Services.obs.removeObserver(this, "sitedatamanager:sites-updated"); - Services.obs.removeObserver(this, "sitedatamanager:updating-sites"); - }; - window.addEventListener("unload", unload); - SiteDataManager.updateSites(); - setEventListener("clearSiteDataButton", "command", - gPrivacyPane.clearSiteData); - setEventListener("siteDataSettings", "command", - gPrivacyPane.showSiteDataSettings); - let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "storage-permissions"; - document.getElementById("siteDataLearnMoreLink").setAttribute("href", url); - let siteDataGroup = document.getElementById("siteDataGroup"); - siteDataGroup.removeAttribute("data-hidden-from-search"); - } + Services.obs.addObserver(this, "sitedatamanager:sites-updated"); + Services.obs.addObserver(this, "sitedatamanager:updating-sites"); + let unload = () => { + window.removeEventListener("unload", unload); + Services.obs.removeObserver(this, "sitedatamanager:sites-updated"); + Services.obs.removeObserver(this, "sitedatamanager:updating-sites"); + }; + window.addEventListener("unload", unload); + SiteDataManager.updateSites(); + setEventListener("clearSiteDataButton", "command", + gPrivacyPane.clearSiteData); + setEventListener("siteDataSettings", "command", + gPrivacyPane.showSiteDataSettings); + let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "storage-permissions"; + document.getElementById("siteDataLearnMoreLink").setAttribute("href", url); let notificationInfoURL = Services.urlFormatter.formatURLPref("app.support.baseURL") + "push"; document.getElementById("notificationPermissionsLearnMore").setAttribute("href", notificationInfoURL); let drmInfoURL = Services.urlFormatter.formatURLPref("app.support.baseURL") + "drm-content"; document.getElementById("playDRMContentLink").setAttribute("href", drmInfoURL);
--- a/browser/components/preferences/in-content/privacy.xul +++ b/browser/components/preferences/in-content/privacy.xul @@ -155,17 +155,17 @@ class="accessory-button" icon="clear" data-l10n-id="history-clear-button"/> </vbox> </hbox> </groupbox> <!-- Site Data --> -<groupbox id="siteDataGroup" hidden="true" data-category="panePrivacy" data-hidden-from-search="true"> +<groupbox id="siteDataGroup" data-category="panePrivacy"> <caption><label data-l10n-id="sitedata-header"/></caption> <hbox data-subcategory="sitedata" align="baseline"> <vbox flex="1"> <description class="description-with-side-element" flex="1"> <html:span id="totalSiteDataSize" class="tail-with-learn-more"></html:span> <label id="siteDataLearnMoreLink" class="learnMore text-link" data-l10n-id="sitedata-learn-more"/>
--- a/browser/components/preferences/in-content/tests/browser_bug731866.js +++ b/browser/components/preferences/in-content/tests/browser_bug731866.js @@ -1,15 +1,14 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ ChromeUtils.import("resource://gre/modules/PlacesUtils.jsm"); ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); -const storageManagerDisabled = !SpecialPowers.getBoolPref("browser.storageManager.enabled"); const browserContainersGroupDisabled = !SpecialPowers.getBoolPref("privacy.userContext.ui.enabled"); function test() { waitForExplicitFinish(); open_preferences(runTest); } var gElements; @@ -17,24 +16,17 @@ var gElements; function checkElements(expectedPane) { for (let element of gElements) { // keyset elements fail is_element_visible checks because they are never visible. // special-case the drmGroup item because its visibility depends on pref + OS version if (element.nodeName == "keyset" || element.id === "drmGroup") { continue; } - // The siteDataGroup in the Storage Management project is currently only pref-on on Nightly for testing purpose. - // During the test and the transition period, we have to check the pref to see if the siteDataGroup - // should be hidden always. This would be a bit bothersome, same as the offlineGroup as below. - // However, this checking is necessary to make sure we don't leak the siteDataGroup into beta/release build - if (element.id == "siteDataGroup" && storageManagerDisabled) { - is_element_hidden(element, "Disabled siteDataGroup should be hidden"); - continue; - } + // The browserContainersGroup is still only pref-on on Nightly if (element.id == "browserContainersGroup" && browserContainersGroupDisabled) { is_element_hidden(element, "Disabled browserContainersGroup should be hidden"); continue; } let attributeValue = element.getAttribute("data-category"); let suffix = " (id=" + element.id + ")";
--- a/browser/components/preferences/in-content/tests/browser_search_subdialogs_within_preferences_3.js +++ b/browser/components/preferences/in-content/tests/browser_search_subdialogs_within_preferences_3.js @@ -1,17 +1,16 @@ /* * This file contains tests for the Preferences search bar. */ // Enabling Searching functionatily. Will display search bar form this testcase forward. add_task(async function() { await SpecialPowers.pushPrefEnv({"set": [ - ["browser.preferences.search", true], - ["browser.storageManager.enabled", true] + ["browser.preferences.search", true] ]}); }); /** * Test for searching for the "Allowed Sites - Add-ons Installation" subdialog. */ add_task(async function() { await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
--- a/browser/components/preferences/in-content/tests/browser_search_subdialogs_within_preferences_7.js +++ b/browser/components/preferences/in-content/tests/browser_search_subdialogs_within_preferences_7.js @@ -2,18 +2,17 @@ * This file contains tests for the Preferences search bar. */ requestLongerTimeout(2); // Enabling Searching functionatily. Will display search bar form this testcase forward. add_task(async function() { await SpecialPowers.pushPrefEnv({"set": [ - ["browser.preferences.search", true], - ["browser.storageManager.enabled", true] + ["browser.preferences.search", true] ]}); }); /** * Test for searching for the "Device Manager" subdialog. */ add_task(async function() { await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
--- a/browser/components/preferences/in-content/tests/browser_search_within_preferences_1.js +++ b/browser/components/preferences/in-content/tests/browser_search_within_preferences_1.js @@ -208,59 +208,16 @@ add_task(async function exiting_search_r // Checks if back to normal is_element_visible(generalPane, "Should be in generalPane"); BrowserTestUtils.removeTab(gBrowser.selectedTab); }); /** - * Test for "Site Data" case, verifying elements with data-hidden-from-search = true - * are hidden in search result. - */ -add_task(async function verify_hidden_from_search_elements_dont_show_up() { - await SpecialPowers.pushPrefEnv({ "set": [["browser.storageManager.enabled", false]] }); - await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true }); - let generalPane = gBrowser.contentDocument.getElementById("generalCategory"); - - is_element_hidden(generalPane, "Should not be in general"); - - // Performs search - let searchInput = gBrowser.contentDocument.getElementById("searchInput"); - - is(searchInput, gBrowser.contentDocument.activeElement.closest("#searchInput"), - "Search input should be focused when visiting preferences"); - - let query = "site data"; - let searchCompletedPromise = BrowserTestUtils.waitForEvent( - gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == query); - EventUtils.sendString(query); - await searchCompletedPromise; - - let mainPrefTag = gBrowser.contentDocument.getElementById("mainPrefPane"); - - let child = mainPrefTag.querySelector("#siteDataGroup"); - is_element_hidden(child, "Should be hidden in search results"); - - // Takes search off - searchCompletedPromise = BrowserTestUtils.waitForEvent( - gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == ""); - let count = query.length; - while (count--) { - EventUtils.sendKey("BACK_SPACE"); - } - await searchCompletedPromise; - - // Checks if back to normal - is_element_visible(generalPane, "Should be in generalPane"); - - BrowserTestUtils.removeTab(gBrowser.selectedTab); -}); - -/** * Test for if we go to another tab after searching */ add_task(async function changing_tabs_after_searching() { await openPreferencesViaOpenPreferencesAPI("paneGeneral", { leaveOpen: true }); let searchInput = gBrowser.contentDocument.getElementById("searchInput"); is(searchInput, gBrowser.contentDocument.activeElement.closest("#searchInput"), "Search input should be focused when visiting preferences");
--- a/browser/components/preferences/in-content/tests/browser_search_within_preferences_command.js +++ b/browser/components/preferences/in-content/tests/browser_search_within_preferences_command.js @@ -1,15 +1,14 @@ "use strict"; /** * Test for "command" event on search input (when user clicks the x button) */ add_task(async function() { - await SpecialPowers.pushPrefEnv({"set": [["browser.storageManager.enabled", false]]}); await openPreferencesViaOpenPreferencesAPI("privacy", {leaveOpen: true}); let generalPane = gBrowser.contentDocument.getElementById("generalCategory"); is_element_hidden(generalPane, "Should not be in general"); // Performs search let searchInput = gBrowser.contentDocument.getElementById("searchInput"); is(searchInput, gBrowser.contentDocument.activeElement.closest("#searchInput"),
--- a/browser/modules/SitePermissions.jsm +++ b/browser/modules/SitePermissions.jsm @@ -679,22 +679,16 @@ var gPermissionObject = { exactHostMatch: true }, "midi-sysex": { exactHostMatch: true } }; -// Delete this entry while being pre-off -// or the persistent-storage permission would appear in Page info's Permission section -if (!Services.prefs.getBoolPref("browser.storageManager.enabled")) { - delete gPermissionObject["persistent-storage"]; -} - if (!Services.prefs.getBoolPref("dom.webmidi.enabled")) { // ESLint gets angry about array versus dot notation here, but some permission // names use hyphens. Disabling rule for line to keep things consistent. // eslint-disable-next-line dot-notation delete gPermissionObject["midi"]; delete gPermissionObject["midi-sysex"]; }
--- a/browser/modules/test/unit/test_SitePermissions.js +++ b/browser/modules/test/unit/test_SitePermissions.js @@ -1,29 +1,23 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; ChromeUtils.import("resource:///modules/SitePermissions.jsm"); ChromeUtils.import("resource://gre/modules/Services.jsm"); -const STORAGE_MANAGER_ENABLED = Services.prefs.getBoolPref("browser.storageManager.enabled"); const RESIST_FINGERPRINTING_ENABLED = Services.prefs.getBoolPref("privacy.resistFingerprinting"); const MIDI_ENABLED = Services.prefs.getBoolPref("dom.webmidi.enabled"); add_task(async function testPermissionsListing() { let expectedPermissions = ["camera", "cookie", "desktop-notification", "focus-tab-by-prompt", - "geo", "image", "install", "microphone", "plugin:flash", "popup", "screen", "shortcuts"]; - if (STORAGE_MANAGER_ENABLED) { - // The persistent-storage permission is still only pref-on on Nightly - // so we add it only when it's pref-on. - // Should remove this checking and add it as default after it is fully pref-on. - expectedPermissions.push("persistent-storage"); - } + "geo", "image", "install", "microphone", "plugin:flash", "popup", "screen", "shortcuts", + "persistent-storage"]; if (RESIST_FINGERPRINTING_ENABLED) { // Canvas permission should be hidden unless privacy.resistFingerprinting // is true. expectedPermissions.push("canvas"); } if (MIDI_ENABLED) { // Should remove this checking and add it as default after it is fully pref'd-on. expectedPermissions.push("midi"); @@ -108,23 +102,17 @@ add_task(async function testGetAvailable SitePermissions.BLOCK ]); }); add_task(async function testExactHostMatch() { let uri = Services.io.newURI("https://example.com"); let subUri = Services.io.newURI("https://test1.example.com"); let exactHostMatched = ["desktop-notification", "focus-tab-by-prompt", "camera", - "microphone", "screen", "geo"]; - if (STORAGE_MANAGER_ENABLED) { - // The persistent-storage permission is still only pref-on on Nightly - // so we add it only when it's pref-on. - // Should remove this checking and add it as default after it is fully pref-on. - exactHostMatched.push("persistent-storage"); - } + "microphone", "screen", "geo", "persistent-storage"]; if (RESIST_FINGERPRINTING_ENABLED) { // Canvas permission should be hidden unless privacy.resistFingerprinting // is true. exactHostMatched.push("canvas"); } if (MIDI_ENABLED) { // WebMIDI is only pref'd on in nightly. // Should remove this checking and add it as default after it is fully pref-on.
--- a/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/PermissionPrompts.jsm +++ b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/PermissionPrompts.jsm @@ -11,17 +11,16 @@ ChromeUtils.import("resource://gre/modul ChromeUtils.import("resource://testing-common/ContentTask.jsm"); ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm"); const URL = "https://test1.example.com/browser/browser/tools/mozscreenshots/mozscreenshots/extension/mozscreenshots/browser/chrome/mozscreenshots/lib/permissionPrompts.html"; let lastTab = null; var PermissionPrompts = { init(libDir) { - Services.prefs.setBoolPref("browser.storageManager.enabled", true); Services.prefs.setBoolPref("media.navigator.permission.fake", true); Services.prefs.setBoolPref("extensions.install.requireBuiltInCerts", false); Services.prefs.setBoolPref("signon.rememberSignons", true); }, configurations: { shareDevices: { selectors: ["#notification-popup"],
--- a/devtools/.eslintrc.js +++ b/devtools/.eslintrc.js @@ -42,35 +42,38 @@ module.exports = { "camelcase": "off", } }, { "files": [ "client/framework/**", "client/scratchpad/**", "client/shared/*.jsm", "client/shared/widgets/*.jsm", + "client/webide/**", ], "rules": { "consistent-return": "off", } }, { "files": [ "client/framework/**", "client/scratchpad/**", "client/shared/AppCacheUtils.jsm", + "client/webide/**", ], "rules": { "max-nested-callbacks": "off", } }, { "files": [ "client/framework/**", "client/scratchpad/**", "client/shared/*.jsm", "client/shared/widgets/*.jsm", + "client/webide/**", ], "rules": { "max-len": "off", } }, { "files": [ "client/scratchpad/test/browser_scratchpad_inspect.js", "client/scratchpad/test/browser_scratchpad_inspect_primitives.js", @@ -79,16 +82,17 @@ module.exports = { "no-labels": "off", } }, { "files": [ "client/framework/**", "client/scratchpad/**", "client/shared/*.jsm", "client/shared/widgets/*.jsm", + "client/webide/**", ], "rules": { "mozilla/no-aArgs": "off", } }, { "files": [ "client/framework/test/**", "client/scratchpad/**", @@ -97,46 +101,53 @@ module.exports = { "mozilla/var-only-at-top-level": "off", } }, { "files": [ "client/framework/**", "client/scratchpad/**", "client/shared/AppCacheUtils.jsm", "client/shared/widgets/*.jsm", + "client/webide/**", ], "rules": { "no-shadow": "off", } }, { "files": [ "client/framework/**", "client/scratchpad/**", + "client/webide/**", ], "rules": { "strict": "off", } }, { "files": [ // Note: Bug 1403938 may be removing canvasdebugger, check before // doing more work on enabling these rules. "client/canvasdebugger/**", // Note: Bug 1342237 may be removing shadereditor, check before // doing more work on enabling these rules. "client/shadereditor/**", + // Note: Bug 1403944 may be removing webaudioeditor, check before + // doing more work on enabling these rules. + "client/webaudioeditor/**", ], "rules": { "consistent-return": "off", "max-len": "off", "mozilla/no-aArgs": "off", "mozilla/var-only-at-top-level": "off", + "no-redeclare": "off", "no-return-assign": "off", "no-shadow": "off", "no-undef": "off", "no-unused-vars": "off", + "no-useless-call": "off", "strict": "off", } }, { // For all head*.js files, turn off no-unused-vars at a global level "files": [ "**/head*.js", ], "rules": {
--- a/devtools/client/aboutdebugging/test/head.js +++ b/devtools/client/aboutdebugging/test/head.js @@ -10,21 +10,16 @@ // Load the shared-head file first. Services.scriptloader.loadSubScript( "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js", this); const { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm", {}); const { Management } = ChromeUtils.import("resource://gre/modules/Extension.jsm", {}); -flags.testing = true; -registerCleanupFunction(() => { - flags.testing = false; -}); - async function openAboutDebugging(page, win) { info("opening about:debugging"); let url = "about:debugging"; if (page) { url += "#" + page; } let tab = await addTab(url, { window: win });
--- a/devtools/client/commandline/test/head.js +++ b/devtools/client/commandline/test/head.js @@ -14,21 +14,16 @@ var { require } = ChromeUtils.import("re var flags = require("devtools/shared/flags"); var { Task } = require("devtools/shared/task"); // Import the GCLI test helper var testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/")); Services.scriptloader.loadSubScript(testDir + "/helpers.js", this); Services.scriptloader.loadSubScript(testDir + "/mockCommands.js", this, "UTF-8"); -flags.testing = true; -SimpleTest.registerCleanupFunction(() => { - flags.testing = false; -}); - function whenDelayedStartupFinished(aWindow, aCallback) { Services.obs.addObserver(function observer(aSubject, aTopic) { if (aWindow == aSubject) { Services.obs.removeObserver(observer, aTopic); executeSoon(aCallback); } }, "browser-delayed-startup-finished"); }
--- a/devtools/client/framework/test/browser_ignore_toolbox_network_requests.js +++ b/devtools/client/framework/test/browser_ignore_toolbox_network_requests.js @@ -7,28 +7,26 @@ // Test that network requests originating from the toolbox don't get recorded in // the network panel. add_task(async function() { // TODO: This test tries to verify the normal behavior of the netmonitor and // therefore needs to avoid the explicit check for tests. Bug 1167188 will // allow us to remove this workaround. - let isTesting = flags.testing; - flags.testing = false; + await pushPref("devtools.testing", false); let tab = await addTab(URL_ROOT + "doc_viewsource.html"); let target = TargetFactory.forTab(tab); let toolbox = await gDevTools.showToolbox(target, "styleeditor"); let panel = toolbox.getPanel("styleeditor"); is(panel.UI.editors.length, 1, "correct number of editors opened"); let monitor = await toolbox.selectTool("netmonitor"); let { store, windowRequire } = monitor.panelWin; is(store.getState().requests.requests.size, 0, "No network requests appear in the network panel"); await gDevTools.closeToolbox(target); tab = target = toolbox = panel = null; gBrowser.removeCurrentTab(); - flags.testing = isTesting; });
--- a/devtools/client/framework/toolbox.js +++ b/devtools/client/framework/toolbox.js @@ -18,16 +18,17 @@ const HTML_NS = "http://www.w3.org/1999/ var {Ci, Cc} = require("chrome"); var promise = require("promise"); var defer = require("devtools/shared/defer"); var Services = require("Services"); var ChromeUtils = require("ChromeUtils"); var {gDevTools} = require("devtools/client/framework/devtools"); var EventEmitter = require("devtools/shared/event-emitter"); var Telemetry = require("devtools/client/shared/telemetry"); +const { getUnicodeUrl } = require("devtools/client/shared/unicode-url"); var { attachThread, detachThread } = require("./attach-thread"); var Menu = require("devtools/client/framework/menu"); var MenuItem = require("devtools/client/framework/menu-item"); var { DOMHelpers } = require("resource://devtools/client/shared/DOMHelpers.jsm"); const { KeyCodes } = require("devtools/client/shared/keycodes"); var Startup = Cc["@mozilla.org/devtools/startup-clh;1"].getService(Ci.nsISupports) .wrappedJSObject; @@ -2052,21 +2053,23 @@ Toolbox.prototype = { /** * Refresh the host's title. */ _refreshHostTitle: function() { let title; if (this.target.name && this.target.name != this.target.url) { const url = this.target.isWebExtension ? - this.target.getExtensionPathName(this.target.url) : this.target.url; + this.target.getExtensionPathName(this.target.url) : + getUnicodeUrl(this.target.url); title = L10N.getFormatStr("toolbox.titleTemplate2", this.target.name, url); } else { - title = L10N.getFormatStr("toolbox.titleTemplate1", this.target.url); + title = L10N.getFormatStr("toolbox.titleTemplate1", + getUnicodeUrl(this.target.url)); } this.postMessage({ name: "set-host-title", title }); }, // Returns an instance of the preference actor @@ -2143,21 +2146,22 @@ Toolbox.prototype = { await this.target.actorHasMethod("domwalker", "getNodeActorFromWindowID"); } // Generate list of menu items from the list of frames. this.frameMap.forEach(frame => { // A frame is checked if it's the selected one. let checked = frame.id == this.selectedFrameId; - let label = frame.url; - + let label; if (this.target.isWebExtension) { // Show a shorter url for extensions page. label = this.target.getExtensionPathName(frame.url); + } else { + label = getUnicodeUrl(frame.url); } // Create menu item. menu.append(new MenuItem({ label, type: "radio", checked, click: () => {
--- a/devtools/client/inspector/markup/test/head.js +++ b/devtools/client/inspector/markup/test/head.js @@ -14,22 +14,16 @@ Services.scriptloader.loadSubScript( var {getInplaceEditorForSpan: inplaceEditor} = require("devtools/client/shared/inplace-editor"); var clipboard = require("devtools/shared/platform/clipboard"); var {ActorRegistryFront} = require("devtools/shared/fronts/actor-registry"); // If a test times out we want to see the complete log and not just the last few // lines. SimpleTest.requestCompleteLog(); -// Set the testing flag on DevToolsUtils and reset it when the test ends -flags.testing = true; -registerCleanupFunction(() => { - flags.testing = false; -}); - // Toggle this pref on to see all DevTools event communication. This is hugely // useful for fixing race conditions. // Services.prefs.setBoolPref("devtools.dump.emit", true); // Clear preferences that may be set during the course of tests. registerCleanupFunction(() => { Services.prefs.clearUserPref("devtools.dump.emit"); Services.prefs.clearUserPref("devtools.inspector.htmlPanelOpen");
--- a/devtools/client/inspector/test/head.js +++ b/devtools/client/inspector/test/head.js @@ -27,21 +27,16 @@ Services.scriptloader.loadSubScript( Services.scriptloader.loadSubScript( "chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js", this); const {LocalizationHelper} = require("devtools/shared/l10n"); const INSPECTOR_L10N = new LocalizationHelper("devtools/client/locales/inspector.properties"); -flags.testing = true; -registerCleanupFunction(() => { - flags.testing = false; -}); - registerCleanupFunction(() => { Services.prefs.clearUserPref("devtools.inspector.activeSidebar"); }); registerCleanupFunction(function() { // Move the mouse outside inspector. If the test happened fake a mouse event // somewhere over inspector the pointer is considered to be there when the // next test begins. This might cause unexpected events to be emitted when
--- a/devtools/client/memory/test/chrome/head.js +++ b/devtools/client/memory/test/chrome/head.js @@ -18,18 +18,16 @@ SimpleTest.registerCleanupFunction(funct ok(false, "Should have had the expected number of DevToolsUtils.assert() failures." + "Expected " + EXPECTED_DTU_ASSERT_FAILURE_COUNT + ", got " + DevToolsUtils.assertionFailureCount); } }); var DevToolsUtils = require("devtools/shared/DevToolsUtils"); var { immutableUpdate } = DevToolsUtils; -var flags = require("devtools/shared/flags"); -flags.testing = true; var constants = require("devtools/client/memory/constants"); var { censusDisplays, diffingState, labelDisplays, dominatorTreeState, snapshotState,
--- a/devtools/client/memory/test/unit/head.js +++ b/devtools/client/memory/test/unit/head.js @@ -5,20 +5,23 @@ // via xpcshell.ini /* import-globals-from ../../../shared/test/shared-redux-head.js */ var { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {}); var Services = require("Services"); var DevToolsUtils = require("devtools/shared/DevToolsUtils"); -var flags = require("devtools/shared/flags"); -flags.testing = true; -flags.wantLogging = true; -flags.wantVerbose = false; + +Services.prefs.setBoolPref("devtools.testing", true); +Services.prefs.setBoolPref("devtools.debugger.log", true); +registerCleanupFunction(() => { + Services.prefs.clearUserPref("devtools.testing"); + Services.prefs.clearUserPref("devtools.debugger.log"); +}); var { OS } = require("resource://gre/modules/osfile.jsm"); var { FileUtils } = require("resource://gre/modules/FileUtils.jsm"); var { TargetFactory } = require("devtools/client/framework/target"); var promise = require("promise"); var defer = require("devtools/shared/defer"); var { expectState } = require("devtools/server/actors/common"); var HeapSnapshotFileUtils = require("devtools/shared/heapsnapshot/HeapSnapshotFileUtils");
--- a/devtools/client/netmonitor/src/utils/firefox/open-request-in-tab.js +++ b/devtools/client/netmonitor/src/utils/firefox/open-request-in-tab.js @@ -1,13 +1,22 @@ /* 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-disable mozilla/reject-some-requires */ +// This file is a chrome-API-dependent version of the module +// devtools/client/netmonitor/src/utils/open-request-in-tab.js, so that it can +// take advantage of utilizing chrome APIs. But because of this, it isn't +// intended to be used in Chrome-API-free applications, such as the Launchpad. +// +// Please keep in mind that if the feature in this file has changed, don't +// forget to also change that accordingly in +// devtools/client/netmonitor/src/utils/open-request-in-tab.js. + "use strict"; let { Cc, Ci } = require("chrome"); const Services = require("Services"); const { gDevTools } = require("devtools/client/framework/devtools"); /** * Opens given request in a new tab.
--- a/devtools/client/netmonitor/src/utils/open-request-in-tab.js +++ b/devtools/client/netmonitor/src/utils/open-request-in-tab.js @@ -1,12 +1,22 @@ /* 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/. */ +// This file is a chrome-API-free version of the module +// devtools/client/netmonitor/src/utils/firefox/open-request-in-tab.js, so that +// it can be used in Chrome-API-free applications, such as the Launchpad. But +// because of this, it cannot take advantage of utilizing chrome APIs and should +// implement the similar functionalities on its own. +// +// Please keep in mind that if the feature in this file has changed, don't +// forget to also change that accordingly in +// devtools/client/netmonitor/src/utils/firefox/open-request-in-tab.js. + "use strict"; const Services = require("Services"); const { gDevTools } = require("devtools/client/framework/devtools"); /** * Opens given request in a new tab. */
--- a/devtools/client/netmonitor/src/utils/request-utils.js +++ b/devtools/client/netmonitor/src/utils/request-utils.js @@ -1,16 +1,19 @@ /* 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-disable mozilla/reject-some-requires */ "use strict"; +const { getUnicodeUrl, getUnicodeUrlPath, getUnicodeHostname } = + require("devtools/client/shared/unicode-url"); + const { UPDATE_PROPS, } = require("devtools/client/netmonitor/src/constants"); const CONTENT_MIME_TYPE_ABBREVIATIONS = { "ecmascript": "js", "javascript": "js", "x-javascript": "js" @@ -116,32 +119,16 @@ function formDataURI(mimeType, encoding, * @param {array} headers - array of headers info { name, value } * @return {string} list of headers in text format */ function writeHeaderText(headers) { return headers.map(({name, value}) => name + ": " + value).join("\n"); } /** - * Convert a string into unicode if string is valid. - * If there is a malformed URI sequence, it returns input string. - * - * @param {string} url - a string - * @return {string} unicode string - */ -function decodeUnicodeUrl(string) { - try { - return decodeURIComponent(string); - } catch (err) { - // Ignore error and return input string directly. - } - return string; -} - -/** * Decode base64 string. * * @param {string} url - a string * @return {string} decoded string */ function decodeUnicodeBase64(string) { try { return decodeURIComponent(atob(string)); @@ -170,17 +157,17 @@ function getAbbreviatedMimeType(mimeType * For example helper returns "basename" from http://domain.com/path/basename * If basename portion is empty, it returns the url pathname. * * @param {string} url - url string * @return {string} unicode basename of a url */ function getUrlBaseName(url) { const pathname = (new URL(url)).pathname; - return decodeUnicodeUrl( + return getUnicodeUrlPath( pathname.replace(/\S*\//, "") || pathname || "/"); } /** * Helpers for getting the query portion of a url. * * @param {string} url - url string * @return {string} unicode query of a url @@ -191,37 +178,37 @@ function getUrlQuery(url) { /** * Helpers for getting unicode name and query portions of a url. * * @param {string} url - url string * @return {string} unicode basename and query portions of a url */ function getUrlBaseNameWithQuery(url) { - return getUrlBaseName(url) + decodeUnicodeUrl((new URL(url)).search); + return getUrlBaseName(url) + getUnicodeUrlPath((new URL(url)).search); } /** - * Helpers for getting unicode hostname portion of an URL. + * Helpers for getting hostname portion of an URL. * * @param {string} url - url string * @return {string} unicode hostname of a url */ function getUrlHostName(url) { - return decodeUnicodeUrl((new URL(url)).hostname); + return new URL(url).hostname; } /** - * Helpers for getting unicode host portion of an URL. + * Helpers for getting host portion of an URL. * * @param {string} url - url string * @return {string} unicode host of a url */ function getUrlHost(url) { - return decodeUnicodeUrl((new URL(url)).host); + return new URL(url).host; } /** * Helpers for getting the shceme portion of a url. * For example helper returns "http" from http://domain.com/path/basename * * @param {string} url - url string * @return {string} string scheme of a url @@ -232,19 +219,32 @@ function getUrlScheme(url) { /** * Extract several details fields from a URL at once. */ function getUrlDetails(url) { let baseNameWithQuery = getUrlBaseNameWithQuery(url); let host = getUrlHost(url); let hostname = getUrlHostName(url); - let unicodeUrl = decodeUnicodeUrl(url); + let unicodeUrl = getUnicodeUrl(url); let scheme = getUrlScheme(url); + // If the hostname contains unreadable ASCII characters, we need to do the + // following two steps: + // 1. Converting the unreadable hostname to a readable Unicode domain name. + // For example, converting xn--g6w.xn--8pv into a Unicode domain name. + // 2. Replacing the unreadable hostname portion in the `host` with the + // readable hostname. + // For example, replacing xn--g6w.xn--8pv:8000 with [Unicode domain]:8000 + // After finishing the two steps, we get a readable `host`. + const unicodeHostname = getUnicodeHostname(hostname); + if (unicodeHostname !== hostname) { + host = host.replace(hostname, unicodeHostname); + } + // Mark local hosts specially, where "local" is as defined in the W3C // spec for secure contexts. // http://www.w3.org/TR/powerful-features/ // // * If the name falls under 'localhost' // * If the name is an IPv4 address within 127.0.0.0/8 // * If the name is an IPv6 address within ::1/128 // @@ -272,18 +272,18 @@ function getUrlDetails(url) { function parseQueryString(query) { if (!query) { return null; } return query.replace(/^[?&]/, "").split("&").map(e => { let param = e.split("="); return { - name: param[0] ? decodeUnicodeUrl(param[0]) : "", - value: param[1] ? decodeUnicodeUrl(param[1]) : "", + name: param[0] ? getUnicodeUrlPath(param[0]) : "", + value: param[1] ? getUnicodeUrlPath(param[1]) : "", }; }); } /** * Parse a string of formdata sections into its components * * @param {string} sections - sections of formdata joined by & @@ -292,18 +292,18 @@ function parseQueryString(query) { function parseFormData(sections) { if (!sections) { return null; } return sections.replace(/^&/, "").split("&").map(e => { let param = e.split("="); return { - name: param[0] ? decodeUnicodeUrl(param[0]) : "", - value: param[1] ? decodeUnicodeUrl(param[1]) : "", + name: param[0] ? getUnicodeUrlPath(param[0]) : "", + value: param[1] ? getUnicodeUrlPath(param[1]) : "", }; }); } /** * Reduces an IP address into a number for easier sorting * * @param {string} ip - IP address to reduce @@ -486,17 +486,16 @@ function processNetworkUpdates(request = module.exports = { decodeUnicodeBase64, getFormDataSections, fetchHeaders, fetchNetworkUpdatePacket, formDataURI, writeHeaderText, - decodeUnicodeUrl, getAbbreviatedMimeType, getEndTime, getFormattedProtocol, getResponseHeader, getResponseTime, getStartTime, getUrlBaseName, getUrlBaseNameWithQuery,
--- a/devtools/client/netmonitor/test/head.js +++ b/devtools/client/netmonitor/test/head.js @@ -13,18 +13,18 @@ Services.scriptloader.loadSubScript( "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js", this); const { getFormattedIPAndPort, getFormattedTime, } = require("devtools/client/netmonitor/src/utils/format-utils"); +const { getUnicodeUrl } = require("devtools/client/shared/unicode-url"); const { - decodeUnicodeUrl, getFormattedProtocol, getUrlBaseName, getUrlHost, getUrlQuery, getUrlScheme, } = require("devtools/client/netmonitor/src/utils/request-utils"); const { EVENTS } = require("devtools/client/netmonitor/src/constants"); @@ -405,17 +405,17 @@ function verifyRequestItemTarget(documen info("Visible index of item: " + visibleIndex); let { fuzzyUrl, status, statusText, cause, type, fullMimeType, transferred, size, time, displayedStatus } = data; let target = document.querySelectorAll(".request-list-item")[visibleIndex]; // Bug 1414981 - Request URL should not show #hash - let unicodeUrl = decodeUnicodeUrl(url).split("#")[0]; + let unicodeUrl = getUnicodeUrl(url.split("#")[0]); let name = getUrlBaseName(url); let query = getUrlQuery(url); let host = getUrlHost(url); let scheme = getUrlScheme(url); let { remoteAddress, remotePort, totalTime,
--- a/devtools/client/netmonitor/webpack.config.js +++ b/devtools/client/netmonitor/webpack.config.js @@ -83,16 +83,17 @@ let webpackConfig = { "devtools/client/shared/vendor/jszip": "jszip", "devtools/client/sourceeditor/editor": "devtools-source-editor/src/source-editor", "devtools/shared/event-emitter": "devtools-modules/src/utils/event-emitter", "devtools/shared/fronts/timeline": path.join(__dirname, "../../client/shared/webpack/shims/fronts-timeline-shim"), "devtools/shared/platform/clipboard": path.join(__dirname, "../../client/shared/webpack/shims/platform-clipboard-stub"), "devtools/client/netmonitor/src/utils/firefox/open-request-in-tab": path.join(__dirname, "src/utils/open-request-in-tab"), + "devtools/client/shared/unicode-url": path.join(__dirname, "../../client/shared/webpack/shims/unicode-url-stub"), // Locales need to be explicitly mapped to the en-US subfolder "devtools/client/locales": path.join(__dirname, "../../client/locales/en-US"), "devtools/shared/locales": path.join(__dirname, "../../shared/locales/en-US"), "devtools/startup/locales": path.join(__dirname, "../../shared/locales/en-US"), "toolkit/locales": path.join(__dirname, "../../../toolkit/locales/en-US"), // Unless a path explicitly needs to be rewritten or shimmed, all devtools paths can
--- a/devtools/client/performance-new/test/chrome/head.js +++ b/devtools/client/performance-new/test/chrome/head.js @@ -10,19 +10,17 @@ const { BrowserLoader } = ChromeUtils.im var { require } = BrowserLoader({ baseURI: "resource://devtools/client/performance-new/", window }); const EventEmitter = require("devtools/shared/event-emitter"); const { perfDescription } = require("devtools/shared/specs/perf"); const DevToolsUtils = require("devtools/shared/DevToolsUtils"); -const flags = require("devtools/shared/flags"); -flags.testing = true; let EXPECTED_DTU_ASSERT_FAILURE_COUNT = 0; SimpleTest.registerCleanupFunction(function() { if (DevToolsUtils.assertionFailureCount !== EXPECTED_DTU_ASSERT_FAILURE_COUNT) { ok(false, "Should have had the expected number of DevToolsUtils.assert() failures." + "Expected " + EXPECTED_DTU_ASSERT_FAILURE_COUNT + ", got " + DevToolsUtils.assertionFailureCount); } });
--- a/devtools/client/performance/components/test/head.js +++ b/devtools/client/performance/components/test/head.js @@ -9,17 +9,16 @@ let { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {}); let { Assert } = require("resource://testing-common/Assert.jsm"); let { BrowserLoader } = ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", {}); let defer = require("devtools/shared/defer"); let flags = require("devtools/shared/flags"); let { TargetFactory } = require("devtools/client/framework/target"); let { Toolbox } = require("devtools/client/framework/toolbox"); -flags.testing = true; let { require: browserRequire } = BrowserLoader({ baseURI: "resource://devtools/client/performance/", window }); let $ = (selector, scope = document) => scope.querySelector(selector); let $$ = (selector, scope = document) => scope.querySelectorAll(selector);
--- a/devtools/client/performance/test/head.js +++ b/devtools/client/performance/test/head.js @@ -47,36 +47,32 @@ const rightMousedown = (node, win = wind // Shortcut for firing a key event, like "VK_UP", "VK_DOWN", etc. const key = (id, win = window) => { EventUtils.synthesizeKey(id, {}, win); }; // Don't pollute global scope. (() => { - const flags = require("devtools/shared/flags"); const PrefUtils = require("devtools/client/performance/test/helpers/prefs"); - flags.testing = true; - // Make sure all the prefs are reverted to their defaults once tests finish. let stopObservingPrefs = PrefUtils.whenUnknownPrefChanged("devtools.performance", pref => { ok(false, `Unknown pref changed: ${pref}. Please add it to test/helpers/prefs.js ` + "to make sure it's reverted to its default value when the tests finishes, " + "and avoid interfering with future tests.\n"); }); // By default, enable memory flame graphs for tests for now. // TODO: remove when we have flame charts via bug 1148663. Services.prefs.setBoolPref(PrefUtils.UI_ENABLE_MEMORY_FLAME_CHART, true); registerCleanupFunction(() => { info("finish() was called, cleaning up..."); - flags.testing = false; PrefUtils.rollbackPrefsToDefault(); stopObservingPrefs(); // Manually stop the profiler module at the end of all tests, to hopefully // avoid at least some leaks on OSX. Theoretically the module should never // be active at this point. We shouldn't have to do this, but rather // find and fix the leak in the module itself. Bug 1257439.
--- a/devtools/client/responsive.html/test/browser/head.js +++ b/devtools/client/responsive.html/test/browser/head.js @@ -43,24 +43,22 @@ const { addDevice, removeDevice, removeL SimpleTest.requestCompleteLog(); SimpleTest.waitForExplicitFinish(); // Toggling the RDM UI involves several docShell swap operations, which are somewhat slow // on debug builds. Usually we are just barely over the limit, so a blanket factor of 2 // should be enough. requestLongerTimeout(2); -flags.testing = true; Services.prefs.setCharPref("devtools.devices.url", TEST_URI_ROOT + "devices.json"); // The appearance of this notification causes intermittent behavior in some tests that // send mouse events, since it causes the content to shift when it appears. Services.prefs.setBoolPref("devtools.responsive.reloadNotification.enabled", false); registerCleanupFunction(async () => { - flags.testing = false; Services.prefs.clearUserPref("devtools.devices.url"); Services.prefs.clearUserPref("devtools.responsive.reloadNotification.enabled"); Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList"); Services.prefs.clearUserPref("devtools.responsive.reloadConditions.touchSimulation"); Services.prefs.clearUserPref("devtools.responsive.reloadConditions.userAgent"); await asyncStorage.removeItem("devtools.devices.url_cache"); await removeLocalDevices(); });
--- a/devtools/client/responsive.html/test/unit/head.js +++ b/devtools/client/responsive.html/test/unit/head.js @@ -3,17 +3,17 @@ "use strict"; /* eslint no-unused-vars: [2, {"vars": "local"}] */ const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {}); const promise = require("promise"); +const Services = require("Services"); const Store = require("devtools/client/responsive.html/store"); const DevToolsUtils = require("devtools/shared/DevToolsUtils"); -const flags = require("devtools/shared/flags"); -flags.testing = true; +Services.prefs.setBoolPref("devtools.testing", true); registerCleanupFunction(() => { - flags.testing = false; + Services.prefs.clearUserPref("devtools.testing"); });
--- a/devtools/client/scratchpad/test/head.js +++ b/devtools/client/scratchpad/test/head.js @@ -6,27 +6,21 @@ const {NetUtil} = ChromeUtils.import("resource://gre/modules/NetUtil.jsm", {}); const {FileUtils} = ChromeUtils.import("resource://gre/modules/FileUtils.jsm", {}); const {ScratchpadManager} = ChromeUtils.import("resource://devtools/client/scratchpad/scratchpad-manager.jsm", {}); const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {}); const {gDevTools} = require("devtools/client/framework/devtools"); const Services = require("Services"); const DevToolsUtils = require("devtools/shared/DevToolsUtils"); -const flags = require("devtools/shared/flags"); const promise = require("promise"); const defer = require("devtools/shared/defer"); var gScratchpadWindow; // Reference to the Scratchpad chrome window object -flags.testing = true; -registerCleanupFunction(() => { - flags.testing = false; -}); - /** * Open a Scratchpad window. * * @param function aReadyCallback * Optional. The function you want invoked when the Scratchpad instance * is ready. * @param object aOptions * Optional. Options for opening the scratchpad:
--- a/devtools/client/shared/components/Frame.js +++ b/devtools/client/shared/components/Frame.js @@ -2,16 +2,18 @@ * 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 { Component } = require("devtools/client/shared/vendor/react"); const dom = require("devtools/client/shared/vendor/react-dom-factories"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); +const { getUnicodeUrl, getUnicodeUrlPath, getUnicodeHostname } = + require("devtools/client/shared/unicode-url"); const { getSourceNames, parseURL, isScratchpadScheme, getSourceMappedFile } = require("devtools/client/shared/source-utils"); const { LocalizationHelper } = require("devtools/shared/l10n"); const l10n = new LocalizationHelper("devtools/client/locales/components.properties"); const webl10n = new LocalizationHelper("devtools/client/locales/webconsole.properties"); class Frame extends Component { @@ -128,28 +130,32 @@ class Frame extends Component { // What's needed is only the last part after " -> ". let source = frame.source ? String(frame.source).split(" -> ").pop() : ""; let line = frame.line != void 0 ? Number(frame.line) : null; let column = frame.column != void 0 ? Number(frame.column) : null; const { short, long, host } = getSourceNames(source); + const unicodeShort = getUnicodeUrlPath(short); + const unicodeLong = getUnicodeUrl(long); + const unicodeHost = host ? getUnicodeHostname(host) : ""; + // Reparse the URL to determine if we should link this; `getSourceNames` // has already cached this indirectly. We don't want to attempt to // link to "self-hosted" and "(unknown)". However, we do want to link // to Scratchpad URIs. // Source mapped sources might not necessary linkable, but they // are still valid in the debugger. const isLinkable = !!(isScratchpadScheme(source) || parseURL(source)) || isSourceMapped; const elements = []; const sourceElements = []; let sourceEl; - let tooltip = long; + let tooltip = unicodeLong; // Exclude all falsy values, including `0`, as line numbers start with 1. if (line) { tooltip += `:${line}`; // Intentionally exclude 0 if (column) { tooltip += `:${column}`; } @@ -172,17 +178,17 @@ class Frame extends Component { key: "function-display-name", className: "frame-link-function-display-name", }, functionDisplayName), " " ); } } - let displaySource = showFullSourceUrl ? long : short; + let displaySource = showFullSourceUrl ? unicodeLong : unicodeShort; if (isSourceMapped) { displaySource = getSourceMappedFile(displaySource); } else if (showEmptyPathAsHost && (displaySource === "" || displaySource === "/")) { displaySource = host; } sourceElements.push(dom.span({ key: "filename", @@ -233,21 +239,21 @@ class Frame extends Component { } else { sourceEl = dom.span({ key: "source", className: "frame-link-source", }, sourceInnerEl); } elements.push(sourceEl); - if (showHost && host) { + if (showHost && unicodeHost) { elements.push(" "); elements.push(dom.span({ key: "host", className: "frame-link-host", - }, host)); + }, unicodeHost)); } return dom.span(attributes, ...elements); } } module.exports = Frame;
--- a/devtools/client/shared/components/test/mochitest/head.js +++ b/devtools/client/shared/components/test/mochitest/head.js @@ -10,21 +10,19 @@ var { Assert } = require("resource://tes var { gDevTools } = require("devtools/client/framework/devtools"); var { BrowserLoader } = ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", {}); var promise = require("promise"); var defer = require("devtools/shared/defer"); var Services = require("Services"); var { DebuggerServer } = require("devtools/server/main"); var { DebuggerClient } = require("devtools/shared/client/debugger-client"); var DevToolsUtils = require("devtools/shared/DevToolsUtils"); -var flags = require("devtools/shared/flags"); var { TargetFactory } = require("devtools/client/framework/target"); var { Toolbox } = require("devtools/client/framework/toolbox"); -flags.testing = true; var { require: browserRequire } = BrowserLoader({ baseURI: "resource://devtools/client/shared/", window }); const React = browserRequire("devtools/client/shared/vendor/react"); const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom"); const dom = browserRequire("devtools/client/shared/vendor/react-dom-factories");
--- a/devtools/client/shared/moz.build +++ b/devtools/client/shared/moz.build @@ -48,16 +48,17 @@ DevToolsModules( 'scroll.js', 'source-utils.js', 'SplitView.jsm', 'stylesheet-utils.js', 'suggestion-picker.js', 'telemetry.js', 'theme.js', 'undo.js', + 'unicode-url.js', 'view-source.js', 'webgl-utils.js', 'zoom-keys.js', ) with Files('**'): BUG_COMPONENT = ('Firefox', 'Developer Tools')
--- a/devtools/client/shared/redux/middleware/test/head.js +++ b/devtools/client/shared/redux/middleware/test/head.js @@ -2,19 +2,16 @@ * 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/. */ /* exported waitUntilState */ "use strict"; const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {}); -const flags = require("devtools/shared/flags"); - -flags.testing = true; function waitUntilState(store, predicate) { return new Promise(resolve => { let unsubscribe = store.subscribe(check); function check() { if (predicate(store.getState())) { unsubscribe(); resolve();
--- a/devtools/client/shared/test/browser_telemetry_button_responsive.js +++ b/devtools/client/shared/test/browser_telemetry_button_responsive.js @@ -12,23 +12,23 @@ const TOOL_DELAY = 200; const asyncStorage = require("devtools/shared/async-storage"); // Toggling the RDM UI involves several docShell swap operations, which are somewhat slow // on debug builds. Usually we are just barely over the limit, so a blanket factor of 2 // should be enough. requestLongerTimeout(2); -flags.testing = true; +Services.prefs.setBoolPref("devtools.testing", true); Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList"); Services.prefs.setCharPref("devtools.devices.url", "http://example.com/browser/devtools/client/responsive.html/test/browser/devices.json"); registerCleanupFunction(() => { - flags.testing = false; + Services.prefs.clearUserPref("devtools.testing"); Services.prefs.clearUserPref("devtools.devices.url"); Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList"); asyncStorage.removeItem("devtools.devices.url_cache"); asyncStorage.removeItem("devtools.devices.local"); }); loader.lazyRequireGetter(this, "ResponsiveUIManager", "devtools/client/responsive.html/manager", true);
--- a/devtools/client/shared/test/shared-head.js +++ b/devtools/client/shared/test/shared-head.js @@ -20,17 +20,16 @@ function scopedCuImport(path) { } const {ScratchpadManager} = scopedCuImport("resource://devtools/client/scratchpad/scratchpad-manager.jsm"); const {loader, require} = scopedCuImport("resource://devtools/shared/Loader.jsm"); const {gDevTools} = require("devtools/client/framework/devtools"); const {TargetFactory} = require("devtools/client/framework/target"); const DevToolsUtils = require("devtools/shared/DevToolsUtils"); -const flags = require("devtools/shared/flags"); let promise = require("promise"); let defer = require("devtools/shared/defer"); const Services = require("Services"); const KeyShortcuts = require("devtools/client/shared/key-shortcuts"); const TEST_DIR = gTestPath.substr(0, gTestPath.lastIndexOf("/")); const CHROME_URL_ROOT = TEST_DIR + "/"; const URL_ROOT = CHROME_URL_ROOT.replace("chrome://mochitests/content/", @@ -104,19 +103,17 @@ function loadFrameScriptUtils(browser = info("Loading the helper frame script " + frameURL); mm.loadFrameScript(frameURL, false); SimpleTest.registerCleanupFunction(() => { mm = null; }); return mm; } -flags.testing = true; registerCleanupFunction(() => { - flags.testing = false; Services.prefs.clearUserPref("devtools.dump.emit"); Services.prefs.clearUserPref("devtools.toolbox.host"); Services.prefs.clearUserPref("devtools.toolbox.previousHost"); Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled"); Services.prefs.clearUserPref("devtools.toolbox.splitconsoleHeight"); }); registerCleanupFunction(async function cleanup() {
new file mode 100644 --- /dev/null +++ b/devtools/client/shared/test/unit/test_unicode-url.js @@ -0,0 +1,240 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests utility functions contained in `unicode-url.js` + */ + +const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {}); +const { getUnicodeUrl, getUnicodeUrlPath, getUnicodeHostname } = + require("devtools/client/shared/unicode-url"); + +// List of URLs used to test Unicode URL conversion +const TEST_URLS = [ + // Type: Readable ASCII URLs + // Expected: All of Unicode versions should equal to the raw. + { + raw: "https://example.org", + expectedUnicode: "https://example.org", + }, + { + raw: "http://example.org", + expectedUnicode: "http://example.org", + }, + { + raw: "ftp://example.org", + expectedUnicode: "ftp://example.org", + }, + { + raw: "https://example.org.", + expectedUnicode: "https://example.org.", + }, + { + raw: "https://example.org/", + expectedUnicode: "https://example.org/", + }, + { + raw: "https://example.org/test", + expectedUnicode: "https://example.org/test", + }, + { + raw: "https://example.org/test.html", + expectedUnicode: "https://example.org/test.html", + }, + { + raw: "https://example.org/test.html?one=1&two=2", + expectedUnicode: "https://example.org/test.html?one=1&two=2", + }, + { + raw: "https://example.org/test.html#here", + expectedUnicode: "https://example.org/test.html#here", + }, + { + raw: "https://example.org/test.html?one=1&two=2#here", + expectedUnicode: "https://example.org/test.html?one=1&two=2#here", + }, + // Type: Unreadable URLs with either Punycode domain names or URI-encoded + // paths + // Expected: Unreadable domain names and URI-encoded paths should be converted + // to readable Unicode. + { + raw: "https://xn--g6w.xn--8pv/test.html", + // Do not type Unicode characters directly, because this test file isn't + // specified with a known encoding. + expectedUnicode: "https://\u6e2c.\u672c/test.html", + }, + { + raw: "https://example.org/%E6%B8%AC%E8%A9%A6.html", + // Do not type Unicode characters directly, because this test file isn't + // specified with a known encoding. + expectedUnicode: "https://example.org/\u6e2c\u8a66.html", + }, + { + raw: "https://example.org/test.html?One=%E4%B8%80", + // Do not type Unicode characters directly, because this test file isn't + // specified with a known encoding. + expectedUnicode: "https://example.org/test.html?One=\u4e00", + }, + { + raw: "https://example.org/test.html?%E4%B8%80=1", + // Do not type Unicode characters directly, because this test file isn't + // specified with a known encoding. + expectedUnicode: "https://example.org/test.html?\u4e00=1", + }, + { + raw: "https://xn--g6w.xn--8pv/%E6%B8%AC%E8%A9%A6.html" + + "?%E4%B8%80=%E4%B8%80" + + "#%E6%AD%A4", + // Do not type Unicode characters directly, because this test file isn't + // specified with a known encoding. + expectedUnicode: "https://\u6e2c.\u672c/\u6e2c\u8a66.html" + + "?\u4e00=\u4e00" + + "#\u6b64", + }, + // Type: data: URIs + // Expected: All should not be converted. + { + raw: "data:text/plain;charset=UTF-8;Hello%20world", + expectedUnicode: "data:text/plain;charset=UTF-8;Hello%20world", + }, + { + raw: "data:text/plain;charset=UTF-8;%E6%B8%AC%20%E8%A9%A6", + expectedUnicode: "data:text/plain;charset=UTF-8;%E6%B8%AC%20%E8%A9%A6", + }, + { + raw: "data:image/png;base64,iVBORw0KGgoAAA" + + "ANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4" + + "//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU" + + "5ErkJggg==", + expectedUnicode: "data:image/png;base64,iVBORw0KGgoAAA" + + "ANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4" + + "//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU" + + "5ErkJggg==", + }, + // Type: Malformed URLs + // Expected: All should not be converted. + { + raw: "://example.org/test", + expectedUnicode: "://example.org/test", + }, + { + raw: "://xn--g6w.xn--8pv/%E6%B8%AC%E8%A9%A6.html" + + "?%E4%B8%80=%E4%B8%80", + expectedUnicode: "://xn--g6w.xn--8pv/%E6%B8%AC%E8%A9%A6.html" + + "?%E4%B8%80=%E4%B8%80", + }, + { + // %E8%A9 isn't a valid UTF-8 code, so this URL is malformed. + raw: "https://xn--g6w.xn--8pv/%E6%B8%AC%E8%A9", + expectedUnicode: "https://xn--g6w.xn--8pv/%E6%B8%AC%E8%A9", + }, +]; + +// List of hostanmes used to test Unicode hostname conversion +const TEST_HOSTNAMES = [ + // Type: Readable ASCII hostnames + // Expected: All of Unicode versions should equal to the raw. + { + raw: "example", + expectedUnicode: "example", + }, + { + raw: "example.org", + expectedUnicode: "example.org", + }, + // Type: Unreadable Punycode hostnames + // Expected: Punycode should be converted to readable Unicode. + { + raw: "xn--g6w", + // Do not type Unicode characters directly, because this test file isn't + // specified with a known encoding. + expectedUnicode: "\u6e2c", + }, + { + raw: "xn--g6w.xn--8pv", + // Do not type Unicode characters directly, because this test file isn't + // specified with a known encoding. + expectedUnicode: "\u6e2c.\u672c", + }, +]; + +// List of URL paths used to test Unicode URL path conversion +const TEST_URL_PATHS = [ + // Type: Readable ASCII URL paths + // Expected: All of Unicode versions should equal to the raw. + { + raw: "test", + expectedUnicode: "test", + }, + { + raw: "/", + expectedUnicode: "/", + }, + { + raw: "/test", + expectedUnicode: "/test", + }, + { + raw: "/test.html?one=1&two=2#here", + expectedUnicode: "/test.html?one=1&two=2#here", + }, + // Type: Unreadable URI-encoded URL paths + // Expected: URL paths should be converted to readable Unicode. + { + raw: "/%E6%B8%AC%E8%A9%A6", + // Do not type Unicode characters directly, because this test file isn't + // specified with a known encoding. + expectedUnicode: "/\u6e2c\u8a66", + }, + { + raw: "/%E6%B8%AC%E8%A9%A6.html", + // Do not type Unicode characters directly, because this test file isn't + // specified with a known encoding. + expectedUnicode: "/\u6e2c\u8a66.html", + }, + { + raw: "/%E6%B8%AC%E8%A9%A6.html" + + "?%E4%B8%80=%E4%B8%80&%E4%BA%8C=%E4%BA%8C" + + "#%E6%AD%A4", + // Do not type Unicode characters directly, because this test file isn't + // specified with a known encoding. + expectedUnicode: "/\u6e2c\u8a66.html" + + "?\u4e00=\u4e00&\u4e8c=\u4e8c" + + "#\u6b64", + }, + // Type: Malformed URL paths + // Expected: All should not be converted. + { + // %E8%A9 isn't a valid UTF-8 code, so this URL is malformed. + raw: "/%E6%B8%AC%E8%A9", + expectedUnicode: "/%E6%B8%AC%E8%A9", + }, +]; + +function run_test() { + // Test URLs + for (let url of TEST_URLS) { + let result = getUnicodeUrl(url.raw); + equal(result, url.expectedUnicode, + "Test getUnicodeUrl: " + url.raw + + " should be unicodized to " + url.expectedUnicode); + } + + // Test hostnames + for (let hostname of TEST_HOSTNAMES) { + let result = getUnicodeHostname(hostname.raw); + equal(result, hostname.expectedUnicode, + "Test getUnicodeHostname: " + hostname.raw + + " should be unicodized to " + hostname.expectedUnicode); + } + + // Test URL paths + for (let urlPath of TEST_URL_PATHS) { + let result = getUnicodeUrlPath(urlPath.raw); + equal(result, urlPath.expectedUnicode, + "Test getUnicodeUrlPath: " + urlPath.raw + + " should be unicodized to " + urlPath.expectedUnicode); + } +}
--- a/devtools/client/shared/test/unit/xpcshell.ini +++ b/devtools/client/shared/test/unit/xpcshell.ini @@ -20,10 +20,11 @@ support-files = [test_escapeCSSComment.js] [test_parseDeclarations.js] [test_parsePseudoClassesAndAttributes.js] [test_parseSingleValue.js] [test_rewriteDeclarations.js] [test_source-utils.js] [test_suggestion-picker.js] [test_undoStack.js] +[test_unicode-url.js] [test_VariablesView_filtering-without-controller.js] [test_VariablesView_getString_promise.js]
new file mode 100644 --- /dev/null +++ b/devtools/client/shared/unicode-url.js @@ -0,0 +1,115 @@ +/* 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"; + +// This file is a chrome-API-dependent version of the module +// devtools/client/shared/webpack/shims/unicode-url-stub.js, so that it can +// take advantage of utilizing chrome APIs. But because of this, it isn't +// intended to be used in Chrome-API-free applications, such as the Launchpad. +// +// Please keep in mind that if the feature in this file has changed, don't +// forget to also change that accordingly in +// devtools/client/shared/webpack/shims/unicode-url-stub.js. + +const { Cc, Ci } = require("chrome"); +const idnService = + Cc["@mozilla.org/network/idn-service;1"].getService(Ci.nsIIDNService); + +/** + * Gets a readble Unicode hostname from a hostname. + * + * If the `hostname` is a readable ASCII hostname, such as example.org, then + * this function will simply return the original `hostname`. + * + * If the `hostname` is a Punycode hostname representing a Unicode domain name, + * such as xn--g6w.xn--8pv, then this function will return the readable Unicode + * domain name by decoding the Punycode hostname. + * + * @param {string} hostname + * the hostname from which the Unicode hostname will be + * parsed, such as example.org, xn--g6w.xn--8pv. + * @return {string} The Unicode hostname. It may be the same as the `hostname` + * passed to this function if the `hostname` itself is + * a readable ASCII hostname or a Unicode hostname. + */ +function getUnicodeHostname(hostname) { + return idnService.convertToDisplayIDN(hostname, {}); +} + +/** + * Gets a readble Unicode URL pathname from a URL pathname. + * + * If the `urlPath` is a readable ASCII URL pathname, such as /a/b/c.js, then + * this function will simply return the original `urlPath`. + * + * If the `urlPath` is a URI-encoded pathname, such as %E8%A9%A6/%E6%B8%AC.js, + * then this function will return the readable Unicode pathname. + * + * If the `urlPath` is a malformed URL pathname, then this function will simply + * return the original `urlPath`. + * + * @param {string} urlPath + * the URL path from which the Unicode URL path will be parsed, + * such as /a/b/c.js, %E8%A9%A6/%E6%B8%AC.js. + * @return {string} The Unicode URL Path. It may be the same as the `urlPath` + * passed to this function if the `urlPath` itself is a readable + * ASCII url or a Unicode url. + */ +function getUnicodeUrlPath(urlPath) { + try { + return decodeURIComponent(urlPath); + } catch (err) { + dump("Warning: getUnicodeUrlPath failed to get a Unicode URL from" + + `${urlPath}, reason: ${err}`); + } + return urlPath; +} + +/** + * Gets a readable Unicode URL from a URL. + * + * If the `url` is a readable ASCII URL, such as http://example.org/a/b/c.js, + * then this function will simply return the original `url`. + * + * If the `url` includes either an unreadable Punycode domain name or an + * unreadable URI-encoded pathname, such as + * http://xn--g6w.xn--8pv/%E8%A9%A6/%E6%B8%AC.js, then this function will return + * the readable URL by decoding all its unreadable URL components to Unicode + * characters. + * + * If the `url` is a malformed URL, then this function will return the original + * `url`. + * + * If the `url` is a data: URI, then this function will return the original + * `url`. + * + * @param {string} url + * the full URL, or a data: URI. from which the readable URL + * will be parsed, such as, http://example.org/a/b/c.js, + * http://xn--g6w.xn--8pv/%E8%A9%A6/%E6%B8%AC.js + * @return {string} The readable URL. It may be the same as the `url` passed to + * this function if the `url` itself is readable. + */ +function getUnicodeUrl(url) { + try { + const { protocol, hostname } = new URL(url); + if (protocol === "data:") { + // Never convert a data: URI. + return url; + } + const readableHostname = getUnicodeHostname(hostname); + url = decodeURIComponent(url); + return url.replace(hostname, readableHostname); + } catch (err) { + dump("Warning: getUnicodeUrl failed to get a Unicode URL from" + + `${url}, reason: ${err}`); + } + return url; +} + +module.exports = { + getUnicodeHostname, + getUnicodeUrlPath, + getUnicodeUrl, +};
new file mode 100644 --- /dev/null +++ b/devtools/client/shared/webpack/shims/unicode-url-stub.js @@ -0,0 +1,32 @@ +/* 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/. */ + +// TODO This file aims to implement a Chrome-API-free replacement for +// devtools/client/shared/unicode-url.js, so that it can be used in the +// Launchpad. +// +// Currently this is just a dummpy mock of +// devtools/client/shared/unicode-url.js, no actual functionaly involved. +// Eventually we'll want to implement it. Once implemented, we should keep the +// feature the same as devtools/client/shared/unicode-url.js. + +"use strict"; + +function getUnicodeHostname(hostname) { + return hostname; +} + +function getUnicodeUrlPath(urlPath) { + return urlPath; +} + +function getUnicodeUrl(url) { + return url; +} + +module.exports = { + getUnicodeHostname, + getUnicodeUrlPath, + getUnicodeUrl, +};
--- a/devtools/client/sourceeditor/test/head.js +++ b/devtools/client/sourceeditor/test/head.js @@ -11,21 +11,16 @@ Services.scriptloader.loadSubScript( "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js", this); const { NetUtil } = require("resource://gre/modules/NetUtil.jsm"); const Editor = require("devtools/client/sourceeditor/editor"); const {getClientCssProperties} = require("devtools/shared/fronts/css-properties"); -flags.testing = true; -SimpleTest.registerCleanupFunction(() => { - flags.testing = false; -}); - function promiseWaitForFocus() { return new Promise(resolve => waitForFocus(resolve)); } function setup(cb, additionalOpts = {}) { cb = cb || function() {}; return new Promise(resolve => {
--- a/devtools/client/storage/ui.js +++ b/devtools/client/storage/ui.js @@ -5,16 +5,17 @@ "use strict"; const EventEmitter = require("devtools/shared/event-emitter"); const {LocalizationHelper, ELLIPSIS} = require("devtools/shared/l10n"); const KeyShortcuts = require("devtools/client/shared/key-shortcuts"); const JSOL = require("devtools/client/shared/vendor/jsol"); const {KeyCodes} = require("devtools/client/shared/keycodes"); +const { getUnicodeHostname } = require("devtools/client/shared/unicode-url"); // GUID to be used as a separator in compound keys. This must match the same // constant in devtools/server/actors/storage.js, // devtools/client/storage/test/head.js and // devtools/server/tests/browser/head.js const SEPARATOR_GUID = "{9d414cc5-8319-0a04-0586-c0a6ae01670a}"; loader.lazyRequireGetter(this, "TreeWidget", @@ -450,17 +451,18 @@ class StorageUI { /** * Handle added items received by onEdit * * @param {object} See onEdit docs */ async handleAddedItems(added) { for (let type in added) { for (let host in added[type]) { - this.tree.add([type, {id: host, type: "url"}]); + const label = this.getReadableLabelFromHostname(host); + this.tree.add([type, {id: host, label: label, type: "url"}]); for (let name of added[type][host]) { try { name = JSON.parse(name); if (name.length == 3) { name.splice(2, 1); } this.tree.add([type, host, ...name]); if (!this.tree.selectedItem) { @@ -667,17 +669,18 @@ class StorageUI { console.error("Unable to localize tree label type:" + type); } this.tree.add([{id: type, label: typeLabel, type: "store"}]); if (!storageTypes[type].hosts) { continue; } this.storageTypes[type] = storageTypes[type]; for (let host in storageTypes[type].hosts) { - this.tree.add([type, {id: host, type: "url"}]); + const label = this.getReadableLabelFromHostname(host); + this.tree.add([type, {id: host, label: label, type: "url"}]); for (let name of storageTypes[type].hosts[host]) { try { let names = JSON.parse(name); this.tree.add([type, host, ...names]); if (!this.tree.selectedItem) { this.tree.selectedItem = [type, host, names[0], names[1]]; } } catch (ex) { @@ -768,23 +771,52 @@ class StorageUI { this.parseItemValue(key, item[key]); } } this.emit("sidebar-updated"); } /** + * Gets a readable label from the hostname. If the hostname is a Punycode + * domain(I.e. an ASCII domain name representing a Unicode domain name), then + * this function decodes it to the readable Unicode domain name, and label + * the Unicode domain name toggether with the original domian name, and then + * return the label; if the hostname isn't a Punycode domain(I.e. it isn't + * encoded and is readable on its own), then this function simply returns the + * original hostname. + * + * @param {string} host + * The string representing a host, e.g, example.com, example.com:8000 + */ + getReadableLabelFromHostname(host) { + try { + const { hostname } = new URL(host); + const unicodeHostname = getUnicodeHostname(hostname); + if (hostname !== unicodeHostname) { + // If the hostname is a Punycode domain representing a Unicode domain, + // we decode it to the Unicode domain name, and then label the Unicode + // domain name together with the original domain name. + return host.replace(hostname, unicodeHostname) + " [ " + host + " ]"; + } + } catch (_) { + // Skip decoding for a host which doesn't include a domain name, simply + // consider them to be readable. + } + return host; + } + + /** * Tries to parse a string value into either a json or a key-value separated * object and populates the sidebar with the parsed value. The value can also * be a key separated array. * * @param {string} name * The key corresponding to the `value` string in the object - * @param {string} value + * @param {string} originalValue * The string to be parsed into an object */ parseItemValue(name, originalValue) { // Find if value is URLEncoded ie let decodedValue = ""; try { decodedValue = decodeURIComponent(originalValue); } catch (e) { @@ -876,18 +908,16 @@ class StorageUI { } return null; } /** * Select handler for the storage tree. Fetches details of the selected item * from the storage details and populates the storage tree. * - * @param {string} event - * The name of the event fired * @param {array} item * An array of ids which represent the location of the selected item in * the storage tree */ async onHostSelect(item) { this.table.clear(); this.hideSidebar(); this.searchBox.value = "";
--- a/devtools/client/webaudioeditor/controller.js +++ b/devtools/client/webaudioeditor/controller.js @@ -78,44 +78,44 @@ var WebAudioEditorController = { // early request to ensure the CallWatcherActor is running and watching for new window // globals. gFront.setup({ reload: false }); }, /** * Remove events emitted by the current tab target. */ - destroy: function () { + destroy: function() { gTarget.off("will-navigate", this._onTabWillNavigate); gFront.off("start-context", this._onStartContext); gFront.off("create-node", this._onCreateNode); gFront.off("connect-node", this._onConnectNode); gFront.off("connect-param", this._onConnectParam); gFront.off("disconnect-node", this._onDisconnectNode); gFront.off("change-param", this._onChangeParam); gFront.off("destroy-node", this._onDestroyNode); this._prefObserver.off("devtools.theme", this._onThemeChange); this._prefObserver.destroy(); }, /** * Called when page is reloaded to show the reload notice and waiting * for an audio context notice. */ - reset: function () { + reset: function() { $("#content").hidden = true; ContextView.resetUI(); InspectorView.resetUI(); PropertiesView.resetUI(); }, // Since node events (create, disconnect, connect) are all async, // we have to make sure to wait that the node has finished creating // before performing an operation on it. - getNode: async function (nodeActor) { + getNode: async function(nodeActor) { let id = nodeActor.actorID; let node = gAudioNodes.get(id); if (!node) { let { resolve, promise } = defer(); gAudioNodes.on("add", function createNodeListener(createdNode) { if (createdNode.id === id) { gAudioNodes.off("add", createNodeListener); @@ -127,17 +127,17 @@ var WebAudioEditorController = { return node; }, /** * Fired when the devtools theme changes (light, dark, etc.) * so that the graph can update marker styling, as that * cannot currently be done with CSS. */ - _onThemeChange: function () { + _onThemeChange: function() { let newValue = Services.prefs.getCharPref("devtools.theme"); window.emit(EVENTS.THEME_CHANGE, newValue); }, /** * Called for each location change in the debugged tab. */ _onTabWillNavigate: function({isFrameSwitching}) { @@ -162,36 +162,36 @@ var WebAudioEditorController = { window.emit(EVENTS.UI_RESET); }, /** * Called after the first audio node is created in an audio context, * signaling that the audio context is being used. */ - _onStartContext: function () { + _onStartContext: function() { $("#reload-notice").hidden = true; $("#waiting-notice").hidden = true; $("#content").hidden = false; window.emit(EVENTS.START_CONTEXT); }, /** * Called when a new node is created. Creates an `AudioNodeView` instance * for tracking throughout the editor. */ - _onCreateNode: function (nodeActor) { + _onCreateNode: function(nodeActor) { gAudioNodes.add(nodeActor); }, /** * Called on `destroy-node` when an AudioNode is GC'd. Removes * from the AudioNode array and fires an event indicating the removal. */ - _onDestroyNode: function (nodeActor) { + _onDestroyNode: function(nodeActor) { gAudioNodes.remove(gAudioNodes.get(nodeActor.actorID)); }, /** * Called when a node is connected to another node. */ async _onConnectNode({ source: sourceActor, dest: destActor }) { let source = await WebAudioEditorController.getNode(sourceActor);
--- a/devtools/client/webaudioeditor/includes.js +++ b/devtools/client/webaudioeditor/includes.js @@ -79,18 +79,22 @@ var gToolbox, gTarget, gFront; /** * Convenient way of emitting events from the panel window. */ EventEmitter.decorate(this); /** * DOM query helper. */ -function $(selector, target = document) { return target.querySelector(selector); } -function $$(selector, target = document) { return target.querySelectorAll(selector); } +function $(selector, target = document) { + return target.querySelector(selector); +} +function $$(selector, target = document) { + return target.querySelectorAll(selector); +} /** * Takes an iterable collection, and a hash. Return the first * object in the collection that matches the values in the hash. * From Backbone.Collection#findWhere * http://backbonejs.org/#Collection-findWhere */ function findWhere(collection, attrs) {
--- a/devtools/client/webaudioeditor/models.js +++ b/devtools/client/webaudioeditor/models.js @@ -136,17 +136,16 @@ class AudioNodeModel extends EventEmitte } } toString() { return "[object AudioNodeModel]"; } } - /** * Constructor for a Collection of `AudioNodeModel` models. * * Events: * - `add`: node * - `remove`: node * - `connect`: node, destinationNode, parameter * - `disconnect`: node @@ -176,16 +175,17 @@ class AudioNodesCollection extends Event * instance. * * Emits "add" event on instance when completed. * * @param Object obj * @return AudioNodeModel */ add(obj) { + // eslint-disable-next-line new-cap let node = new this.model(obj); node.collection = this; this.models.add(node); node.on("*", this._onModelEvent); EventEmitter.emit(this, "add", node); return node;
--- a/devtools/client/webaudioeditor/panel.js +++ b/devtools/client/webaudioeditor/panel.js @@ -15,17 +15,17 @@ function WebAudioEditorPanel(iframeWindo this._destroyer = null; EventEmitter.decorate(this); } exports.WebAudioEditorPanel = WebAudioEditorPanel; WebAudioEditorPanel.prototype = { - open: function () { + open: function() { let targetPromise; // Local debugging needs to make the target remote. if (!this.target.isRemote) { targetPromise = this.target.makeRemote(); } else { targetPromise = Promise.resolve(this.target); } @@ -50,17 +50,17 @@ WebAudioEditorPanel.prototype = { }, // DevToolPanel API get target() { return this._toolbox.target; }, - destroy: function () { + destroy: function() { // Make sure this panel is not already destroyed. if (this._destroyer) { return this._destroyer; } return this._destroyer = this.panelWin.shutdownWebAudioEditor().then(() => { // Destroy front to ensure packet handler is removed from client this.panelWin.gFront.destroy();
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-add-automation-event.js +++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-add-automation-event.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Test AudioNode#addAutomationEvent(); */ -add_task(async function () { +add_task(async function() { let { target, front } = await initBackend(SIMPLE_CONTEXT_URL); let [_, [destNode, oscNode, gainNode]] = await Promise.all([ front.setup({ reload: true }), get3(front, "create-node") ]); let count = 0; let counter = () => count++; front.on("automation-event", counter);
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-bypass.js +++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-bypass.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Test AudioNode#bypass(), AudioNode#isBypassed() */ -add_task(async function () { +add_task(async function() { let { target, front } = await initBackend(SIMPLE_CONTEXT_URL); let [_, [destNode, oscNode, gainNode]] = await Promise.all([ front.setup({ reload: true }), get3(front, "create-node") ]); is((await gainNode.isBypassed()), false, "Nodes start off unbypassed.");
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-bypassable.js +++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-bypassable.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Test AudioNode#bypassable */ -add_task(async function () { +add_task(async function() { let { target, front } = await initBackend(SIMPLE_NODES_URL); let [_, nodes] = await Promise.all([ front.setup({ reload: true }), getN(front, "create-node", 14) ]); let actualBypassability = nodes.map(node => node.bypassable); let expectedBypassability = [
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-connectnode-disconnect.js +++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-connectnode-disconnect.js @@ -1,17 +1,17 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests that AudioNodeActor#connectNode() and AudioNodeActor#disconnect() work. * Uses the editor front as the actors do not retain connect state. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin; let events = Promise.all([ get3(gFront, "create-node"), waitForGraphRendered(panelWin, 3, 2) ]);
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-connectparam.js +++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-connectparam.js @@ -1,17 +1,17 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests that AudioNodeActor#connectParam() work. * Uses the editor front as the actors do not retain connect state. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin; let events = Promise.all([ get3(gFront, "create-node"), waitForGraphRendered(panelWin, 3, 2) ]);
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-01.js +++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-01.js @@ -1,17 +1,17 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Test AudioNode#addAutomationEvent() checking automation values, also using * a curve as the last event to check duration spread. */ -add_task(async function () { +add_task(async function() { let { target, front } = await initBackend(SIMPLE_CONTEXT_URL); let [_, [destNode, oscNode, gainNode]] = await Promise.all([ front.setup({ reload: true }), get3(front, "create-node") ]); let t0 = 0, t1 = 0.1, t2 = 0.2, t3 = 0.3, t4 = 0.4, t5 = 0.6, t6 = 0.7, t7 = 1; let curve = [-1, 0, 1];
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-02.js +++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-02.js @@ -1,17 +1,17 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Test AudioNode#addAutomationEvent() when automation series ends with * `setTargetAtTime`, which approaches its target to infinity. */ -add_task(async function () { +add_task(async function() { let { target, front } = await initBackend(SIMPLE_CONTEXT_URL); let [_, [destNode, oscNode, gainNode]] = await Promise.all([ front.setup({ reload: true }), get3(front, "create-node") ]); await oscNode.addAutomationEvent("frequency", "setValueAtTime", [300, 0.1]); await oscNode.addAutomationEvent("frequency", "linearRampToValueAtTime", [500, 0.4]);
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-03.js +++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-03.js @@ -1,17 +1,17 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Test that `cancelScheduledEvents` clears out events on and after * its argument. */ -add_task(async function () { +add_task(async function() { let { target, front } = await initBackend(SIMPLE_CONTEXT_URL); let [_, [destNode, oscNode, gainNode]] = await Promise.all([ front.setup({ reload: true }), get3(front, "create-node") ]); await oscNode.addAutomationEvent("frequency", "setValueAtTime", [300, 0]); await oscNode.addAutomationEvent("frequency", "linearRampToValueAtTime", [500, 0.9]);
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-param-flags.js +++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-param-flags.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Test AudioNode#getParamFlags() */ -add_task(async function () { +add_task(async function() { let { target, front } = await initBackend(SIMPLE_NODES_URL); let [_, nodes] = await Promise.all([ front.setup({ reload: true }), getN(front, "create-node", 15) ]); let allNodeParams = await Promise.all(nodes.map(node => node.getParams())); let nodeTypes = [ @@ -28,20 +28,18 @@ add_task(async function () { let params = allNodeParams[i]; for (let {param, value, flags} of params) { let testFlags = await nodes[i].getParamFlags(param); ok(typeof testFlags === "object", type + " has flags from #getParamFlags(" + param + ")"); if (param === "buffer") { is(flags.Buffer, true, "`buffer` params have Buffer flag"); - } - else if (param === "bufferSize" || param === "frequencyBinCount") { + } else if (param === "bufferSize" || param === "frequencyBinCount") { is(flags.readonly, true, param + " is readonly"); - } - else if (param === "curve") { - is(flags["Float32Array"], true, "`curve` param has Float32Array flag"); + } else if (param === "curve") { + is(flags.Float32Array, true, "`curve` param has Float32Array flag"); } } } await removeTab(target.tab); });
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-params-01.js +++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-params-01.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Test AudioNode#getParams() */ -add_task(async function () { +add_task(async function() { let { target, front } = await initBackend(SIMPLE_NODES_URL); let [_, nodes] = await Promise.all([ front.setup({ reload: true }), getN(front, "create-node", 15) ]); await loadFrameScriptUtils(); @@ -30,20 +30,18 @@ add_task(async function () { params.forEach(({param, value, flags}) => { ok(param in defaults[i], "expected parameter for " + type); ok(typeof flags === "object", type + " has a flags object"); if (param === "buffer") { is(flags.Buffer, true, "`buffer` params have Buffer flag"); - } - else if (param === "bufferSize" || param === "frequencyBinCount") { + } else if (param === "bufferSize" || param === "frequencyBinCount") { is(flags.readonly, true, param + " is readonly"); - } - else if (param === "curve") { - is(flags["Float32Array"], true, "`curve` param has Float32Array flag"); + } else if (param === "curve") { + is(flags.Float32Array, true, "`curve` param has Float32Array flag"); } }); }); await removeTab(target.tab); });
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-params-02.js +++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-params-02.js @@ -1,17 +1,17 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests that default properties are returned with the correct type * from the AudioNode actors. */ -add_task(async function () { +add_task(async function() { let { target, front } = await initBackend(SIMPLE_NODES_URL); let [_, nodes] = await Promise.all([ front.setup({ reload: true }), getN(front, "create-node", 15) ]); await loadFrameScriptUtils(); @@ -34,18 +34,17 @@ add_task(async function () { await removeTab(target.tab); }); function compare(actual, expected, type) { actual.forEach(({ value, param }) => { value = getGripValue(value); if (typeof expected[param] === "function") { ok(expected[param](value), type + " has a passing value for " + param); - } - else { + } else { is(value, expected[param], type + " has correct default value and type for " + param); } }); info(Object.keys(expected).join(",") + " - " + JSON.stringify(expected)); is(actual.length, Object.keys(expected).length, type + " has correct amount of properties.");
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-set-param.js +++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-set-param.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Test AudioNode#getParam() / AudioNode#setParam() */ -add_task(async function () { +add_task(async function() { let { target, front } = await initBackend(SIMPLE_CONTEXT_URL); let [_, [destNode, oscNode, gainNode]] = await Promise.all([ front.setup({ reload: true }), get3(front, "create-node") ]); let freq = await oscNode.getParam("frequency"); info(typeof freq);
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-source.js +++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-source.js @@ -1,27 +1,28 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Test AudioNode#source */ -add_task(async function () { +add_task(async function() { let { target, front } = await initBackend(SIMPLE_NODES_URL); let [_, nodes] = await Promise.all([ front.setup({ reload: true }), getN(front, "create-node", 14) ]); let actualTypes = nodes.map(node => node.type); let isSourceResult = nodes.map(node => node.source); actualTypes.forEach((type, i) => { let shouldBeSource = type === "AudioBufferSourceNode" || type === "OscillatorNode"; - if (shouldBeSource) + if (shouldBeSource) { is(isSourceResult[i], true, type + "'s `source` is `true`"); - else + } else { is(isSourceResult[i], false, type + "'s `source` is `false`"); + } }); await removeTab(target.tab); });
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-type.js +++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-type.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Test AudioNode#type */ -add_task(async function () { +add_task(async function() { let { target, front } = await initBackend(SIMPLE_NODES_URL); let [_, nodes] = await Promise.all([ front.setup({ reload: true }), getN(front, "create-node", 14) ]); let actualTypes = nodes.map(node => node.type); let expectedTypes = [
--- a/devtools/client/webaudioeditor/test/browser_callwatcher-01.js +++ b/devtools/client/webaudioeditor/test/browser_callwatcher-01.js @@ -5,17 +5,17 @@ * Bug 1130901 * Tests to ensure that calling call/apply on methods wrapped * via CallWatcher do not throw a security permissions error: * "Error: Permission denied to access property 'call'" */ const BUG_1130901_URL = EXAMPLE_URL + "doc_bug_1130901.html"; -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(BUG_1130901_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin; let rendered = waitForGraphRendered(panelWin, 3, 0); reload(target); await rendered;
--- a/devtools/client/webaudioeditor/test/browser_callwatcher-02.js +++ b/devtools/client/webaudioeditor/test/browser_callwatcher-02.js @@ -4,17 +4,17 @@ /** * Bug 1112378 * Tests to ensure that errors called on wrapped functions via call-watcher * correctly looks like the error comes from the content, not from within the devtools. */ const BUG_1112378_URL = EXAMPLE_URL + "doc_bug_1112378.html"; -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(BUG_1112378_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin; loadFrameScriptUtils(); let rendered = waitForGraphRendered(panelWin, 2, 0); reload(target);
--- a/devtools/client/webaudioeditor/test/browser_wa_automation-view-01.js +++ b/devtools/client/webaudioeditor/test/browser_wa_automation-view-01.js @@ -1,17 +1,17 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests that automation view shows the correct view depending on if events * or params exist. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(AUTOMATION_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS } = panelWin; let started = once(gFront, "start-context"); let events = Promise.all([ get3(gFront, "create-node"),
--- a/devtools/client/webaudioeditor/test/browser_wa_automation-view-02.js +++ b/devtools/client/webaudioeditor/test/browser_wa_automation-view-02.js @@ -1,17 +1,17 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests that automation view selects the first parameter by default and * switching between AudioParam rerenders the graph. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(AUTOMATION_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS, AutomationView } = panelWin; let started = once(gFront, "start-context"); let events = Promise.all([ get3(gFront, "create-node"),
--- a/devtools/client/webaudioeditor/test/browser_wa_controller-01.js +++ b/devtools/client/webaudioeditor/test/browser_wa_controller-01.js @@ -4,17 +4,17 @@ /** * Bug 1125817 * Tests to ensure that disconnecting a node immediately * after creating it does not fail. */ const BUG_1125817_URL = EXAMPLE_URL + "doc_bug_1125817.html"; -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(BUG_1125817_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin; let events = Promise.all([ once(gAudioNodes, "add", 2), once(gAudioNodes, "disconnect"), waitForGraphRendered(panelWin, 2, 0)
--- a/devtools/client/webaudioeditor/test/browser_wa_destroy-node-01.js +++ b/devtools/client/webaudioeditor/test/browser_wa_destroy-node-01.js @@ -4,17 +4,17 @@ /** * Tests that the destruction node event is fired and that the nodes are no * longer stored internally in the tool, that the graph is updated properly, and * that selecting a soon-to-be dead node clears the inspector. * * All done in one test since this test takes a few seconds to clear GC. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(DESTROY_NODES_URL); let { panelWin } = panel; let { gFront, $, $$, gAudioNodes } = panelWin; let started = once(gFront, "start-context"); let events = Promise.all([ getNSpread(gAudioNodes, "add", 13),
--- a/devtools/client/webaudioeditor/test/browser_wa_first-run.js +++ b/devtools/client/webaudioeditor/test/browser_wa_first-run.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests that the reloading/onContentLoaded hooks work. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL); let { gFront, $ } = panel.panelWin; is($("#reload-notice").hidden, false, "The 'reload this page' notice should initially be visible."); is($("#waiting-notice").hidden, true, "The 'waiting for an audio context' notice should initially be hidden."); is($("#content").hidden, true,
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-click.js +++ b/devtools/client/webaudioeditor/test/browser_wa_graph-click.js @@ -1,17 +1,17 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests that the clicking on a node in the GraphView opens and sets * the correct node in the InspectorView */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(COMPLEX_CONTEXT_URL); let panelWin = panel.panelWin; let { gFront, $, $$, InspectorView } = panelWin; let started = once(gFront, "start-context"); let events = Promise.all([ getN(gFront, "create-node", 8),
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-markers.js +++ b/devtools/client/webaudioeditor/test/browser_wa_graph-markers.js @@ -2,17 +2,17 @@ http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests that the SVG marker styling is updated when devtools theme changes. */ const { setTheme } = require("devtools/client/shared/theme"); -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL); let { panelWin } = panel; let { gFront, $, $$, MARKER_STYLING } = panelWin; let currentTheme = Services.prefs.getCharPref("devtools.theme"); ok(MARKER_STYLING.light, "Marker styling exists for light theme."); ok(MARKER_STYLING.dark, "Marker styling exists for dark theme.");
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-01.js +++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-01.js @@ -2,17 +2,17 @@ http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests that SVG nodes and edges were created for the Graph View. */ var connectCount = 0; -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin; let started = once(gFront, "start-context"); gAudioNodes.on("connect", onConnectNode);
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-02.js +++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-02.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests more edge rendering for complex graphs. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(COMPLEX_CONTEXT_URL); let { panelWin } = panel; let { gFront, $, $$ } = panelWin; let started = once(gFront, "start-context"); let events = Promise.all([ getN(gFront, "create-node", 8), @@ -18,17 +18,16 @@ add_task(async function () { ]); reload(target); let [actors] = await events; let nodeIDs = actors.map(actor => actor.actorID); let types = ["AudioDestinationNode", "OscillatorNode", "GainNode", "ScriptProcessorNode", "OscillatorNode", "GainNode", "AudioBufferSourceNode", "BiquadFilterNode"]; - types.forEach((type, i) => { ok(findGraphNode(panelWin, nodeIDs[i]).classList.contains("type-" + type), "found " + type + " with class"); }); let edges = [ [1, 2, "osc1 -> gain1"], [1, 3, "osc1 -> proc"], [2, 0, "gain1 -> dest"],
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-03.js +++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-03.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests to ensure that selected nodes stay selected on graph redraw. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS } = panelWin; let events = Promise.all([ getN(gFront, "create-node", 3), waitForGraphRendered(panelWin, 3, 2) ]);
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-04.js +++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-04.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests audio param connection rendering. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(CONNECT_MULTI_PARAM_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS } = panelWin; let started = once(gFront, "start-context"); let events = Promise.all([ getN(gFront, "create-node", 5),
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-05.js +++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-05.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests to ensure that param connections trigger graph redraws */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS } = panelWin; let events = Promise.all([ getN(gFront, "create-node", 3), waitForGraphRendered(panelWin, 3, 2, 0) ]);
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-06.js +++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-06.js @@ -2,17 +2,17 @@ http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests to ensure that param connections trigger graph redraws */ const BUG_1141261_URL = EXAMPLE_URL + "doc_bug_1141261.html"; -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(BUG_1141261_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS } = panelWin; let events = Promise.all([ getN(gFront, "create-node", 3), waitForGraphRendered(panelWin, 3, 1, 0) ]);
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-selected.js +++ b/devtools/client/webaudioeditor/test/browser_wa_graph-selected.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests that SVG nodes and edges were created for the Graph View. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS } = panelWin; let started = once(gFront, "start-context"); let events = Promise.all([ get3(gFront, "create-node"),
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-zoom.js +++ b/devtools/client/webaudioeditor/test/browser_wa_graph-zoom.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests that the graph's scale and position is reset on a page reload. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS, ContextView } = panelWin; let started = once(gFront, "start-context"); await Promise.all([ waitForGraphRendered(panelWin, 3, 2),
--- a/devtools/client/webaudioeditor/test/browser_wa_inspector-bypass-01.js +++ b/devtools/client/webaudioeditor/test/browser_wa_inspector-bypass-01.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests that nodes are correctly bypassed when bypassing. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin; let events = Promise.all([ get3(gFront, "create-node"), waitForGraphRendered(panelWin, 3, 2) ]);
--- a/devtools/client/webaudioeditor/test/browser_wa_inspector-toggle.js +++ b/devtools/client/webaudioeditor/test/browser_wa_inspector-toggle.js @@ -1,17 +1,17 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests that the inspector toggle button shows and hides * the inspector panel as intended. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS, InspectorView } = panelWin; let gVars = InspectorView._propsView; let started = once(gFront, "start-context"); let events = Promise.all([
--- a/devtools/client/webaudioeditor/test/browser_wa_inspector-width.js +++ b/devtools/client/webaudioeditor/test/browser_wa_inspector-width.js @@ -1,17 +1,17 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Test that the WebAudioInspector's Width is saved as * a preference */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS, InspectorView } = panelWin; let gVars = InspectorView._propsView; let started = once(gFront, "start-context"); let events = Promise.all([
--- a/devtools/client/webaudioeditor/test/browser_wa_inspector.js +++ b/devtools/client/webaudioeditor/test/browser_wa_inspector.js @@ -1,17 +1,17 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests that inspector view opens on graph node click, and * loads the correct node inside the inspector. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS, InspectorView } = panelWin; let gVars = InspectorView._propsView; let started = once(gFront, "start-context"); let events = Promise.all([
--- a/devtools/client/webaudioeditor/test/browser_wa_navigate.js +++ b/devtools/client/webaudioeditor/test/browser_wa_navigate.js @@ -1,17 +1,17 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests naviating from a page to another will repopulate * the audio graph if both pages have an AudioContext. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL); let { panelWin } = panel; let { gFront, $ } = panelWin; let events = Promise.all([ get3(gFront, "create-node"), waitForGraphRendered(panelWin, 3, 2) ]);
--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view-edit-01.js +++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view-edit-01.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests that properties are updated when modifying the VariablesView. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS, PropertiesView } = panelWin; let gVars = PropertiesView._propsView; let started = once(gFront, "start-context"); let events = Promise.all([ @@ -32,34 +32,34 @@ add_task(async function () { checkVariableView(gVars, 0, { "type": "sine", "frequency": 440, "detune": 0 }, "default loaded string"); click(panelWin, findGraphNode(panelWin, nodeIds[2])); - await waitForInspectorRender(panelWin, EVENTS), + await waitForInspectorRender(panelWin, EVENTS); checkVariableView(gVars, 0, { "gain": 0 }, "default loaded number"); click(panelWin, findGraphNode(panelWin, nodeIds[1])); - await waitForInspectorRender(panelWin, EVENTS), + await waitForInspectorRender(panelWin, EVENTS); await setAndCheck(0, "type", "square", "square", "sets string as string"); click(panelWin, findGraphNode(panelWin, nodeIds[2])); - await waitForInspectorRender(panelWin, EVENTS), + await waitForInspectorRender(panelWin, EVENTS); await setAndCheck(0, "gain", "0.005", 0.005, "sets number as number"); await setAndCheck(0, "gain", "0.1", 0.1, "sets float as float"); await setAndCheck(0, "gain", ".2", 0.2, "sets float without leading zero as float"); await teardown(target); }); function setAndCheckVariable(panelWin, gVars) { - return async function (varNum, prop, value, expected, desc) { + return async function(varNum, prop, value, expected, desc) { await modifyVariableView(panelWin, gVars, varNum, prop, value); var props = {}; props[prop] = expected; checkVariableView(gVars, varNum, props, desc); }; }
--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view-edit-02.js +++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view-edit-02.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests that properties are not updated when modifying the VariablesView. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(COMPLEX_CONTEXT_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS, PropertiesView } = panelWin; let gVars = PropertiesView._propsView; let started = once(gFront, "start-context"); let events = Promise.all([
--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view-media-nodes.js +++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view-media-nodes.js @@ -10,32 +10,33 @@ var MEDIA_PERMISSION = "media.navigator. function waitForDeviceClosed() { info("Checking that getUserMedia streams are no longer in use."); let temp = {}; ChromeUtils.import("resource:///modules/webrtcUI.jsm", temp); let webrtcUI = temp.webrtcUI; - if (!webrtcUI.showGlobalIndicator) + if (!webrtcUI.showGlobalIndicator) { return Promise.resolve(); + } return new Promise((resolve, reject) => { const message = "webrtc:UpdateGlobalIndicators"; Services.ppmm.addMessageListener(message, function listener(aMessage) { info("Received " + message + " message"); if (!aMessage.data.showGlobalIndicator) { Services.ppmm.removeMessageListener(message, listener); resolve(); } }); }); } -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(MEDIA_NODES_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS, PropertiesView } = panelWin; let gVars = PropertiesView._propsView; // Auto enable getUserMedia let mediaPermissionPref = Services.prefs.getBoolPref(MEDIA_PERMISSION); Services.prefs.setBoolPref(MEDIA_PERMISSION, true);
--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view-params-objects.js +++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view-params-objects.js @@ -1,17 +1,17 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests that params view correctly displays non-primitive properties * like AudioBuffer and Float32Array in properties of AudioNodes. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(BUFFER_AND_ARRAY_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS, PropertiesView } = panelWin; let gVars = PropertiesView._propsView; let started = once(gFront, "start-context"); let events = Promise.all([
--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view-params.js +++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view-params.js @@ -1,17 +1,17 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests that params view correctly displays all properties for nodes * correctly, with default values and correct types. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(SIMPLE_NODES_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS, PropertiesView } = panelWin; let gVars = PropertiesView._propsView; let started = once(gFront, "start-context"); await loadFrameScriptUtils();
--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view.js +++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests that params view shows params when they exist, and are hidden otherwise. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL); let { panelWin } = panel; let { gFront, $, $$, EVENTS, PropertiesView } = panelWin; let gVars = PropertiesView._propsView; let started = once(gFront, "start-context"); let events = Promise.all([
--- a/devtools/client/webaudioeditor/test/browser_wa_reset-01.js +++ b/devtools/client/webaudioeditor/test/browser_wa_reset-01.js @@ -1,17 +1,17 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests that reloading a tab will properly listen for the `start-context` * event and reshow the tools after reloading. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL); let { gFront, $ } = panel.panelWin; is($("#reload-notice").hidden, false, "The 'reload this page' notice should initially be visible."); is($("#waiting-notice").hidden, true, "The 'waiting for an audio context' notice should initially be hidden."); is($("#content").hidden, true,
--- a/devtools/client/webaudioeditor/test/browser_wa_reset-02.js +++ b/devtools/client/webaudioeditor/test/browser_wa_reset-02.js @@ -1,17 +1,17 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests reloading a tab with the tools open properly cleans up * the graph. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL); let { panelWin } = panel; let { gFront, $ } = panelWin; let events = Promise.all([ get3(gFront, "create-node"), waitForGraphRendered(panelWin, 3, 2) ]);
--- a/devtools/client/webaudioeditor/test/browser_wa_reset-03.js +++ b/devtools/client/webaudioeditor/test/browser_wa_reset-03.js @@ -1,17 +1,17 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests reloading a tab with the tools open properly cleans up * the inspector and selected node. */ -add_task(async function () { +add_task(async function() { let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL); let { panelWin } = panel; let { gFront, $, InspectorView } = panelWin; let events = Promise.all([ get3(gFront, "create-node"), waitForGraphRendered(panelWin, 3, 2) ]);
--- a/devtools/client/webaudioeditor/test/browser_wa_reset-04.js +++ b/devtools/client/webaudioeditor/test/browser_wa_reset-04.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests that switching to an iframe works fine. */ -add_task(async function () { +add_task(async function() { Services.prefs.setBoolPref("devtools.command-button-frames.enabled", true); let { target, panel, toolbox } = await initWebAudioEditor(IFRAME_CONTEXT_URL); let { gFront, $ } = panel.panelWin; is($("#reload-notice").hidden, false, "The 'reload this page' notice should initially be visible."); is($("#waiting-notice").hidden, true,
--- a/devtools/client/webaudioeditor/test/browser_webaudio-actor-automation-event.js +++ b/devtools/client/webaudioeditor/test/browser_webaudio-actor-automation-event.js @@ -1,17 +1,17 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Test that the WebAudioActor receives and emits the `automation-event` events * with correct arguments from the content. */ -add_task(async function () { +add_task(async function() { let { target, front } = await initBackend(AUTOMATION_URL); let events = []; let expected = [ ["setValueAtTime", 0.2, 0], ["linearRampToValueAtTime", 1, 0.3], ["exponentialRampToValueAtTime", 0.75, 0.6], ["setValueCurveAtTime", [-1, 0, 1], 0.7, 0.3],
--- a/devtools/client/webaudioeditor/test/browser_webaudio-actor-connect-param.js +++ b/devtools/client/webaudioeditor/test/browser_webaudio-actor-connect-param.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Test the `connect-param` event on the web audio actor. */ -add_task(async function () { +add_task(async function() { let { target, front } = await initBackend(CONNECT_PARAM_URL); let [, , [destNode, carrierNode, modNode, gainNode], , connectParam] = await Promise.all([ front.setup({ reload: true }), once(front, "start-context"), getN(front, "create-node", 4), get2(front, "connect-node"), once(front, "connect-param") ]);
--- a/devtools/client/webaudioeditor/test/browser_webaudio-actor-destroy-node.js +++ b/devtools/client/webaudioeditor/test/browser_webaudio-actor-destroy-node.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Test `destroy-node` event on WebAudioActor. */ -add_task(async function () { +add_task(async function() { let { target, front } = await initBackend(DESTROY_NODES_URL); let [, , created] = await Promise.all([ front.setup({ reload: true }), once(front, "start-context"), // Should create dest, gain, and oscillator node and 10 // disposable buffer nodes getN(front, "create-node", 13) @@ -29,13 +29,14 @@ add_task(async function () { "`destroy-node` called only on AudioNodes in current document."); }); await removeTab(target.tab); }); function actorIsInList(list, actor) { for (let i = 0; i < list.length; i++) { - if (list[i].actorID === actor.actorID) + if (list[i].actorID === actor.actorID) { return list[i]; + } } return null; }
--- a/devtools/client/webaudioeditor/test/browser_webaudio-actor-simple.js +++ b/devtools/client/webaudioeditor/test/browser_webaudio-actor-simple.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Test basic communication of Web Audio actor */ -add_task(async function () { +add_task(async function() { let { target, front } = await initBackend(SIMPLE_CONTEXT_URL); let [_, __, [destNode, oscNode, gainNode], [connect1, connect2]] = await Promise.all([ front.setup({ reload: true }), once(front, "start-context"), get3(front, "create-node"), get2(front, "connect-node") ]);
--- a/devtools/client/webaudioeditor/test/doc_buffer-and-array.html +++ b/devtools/client/webaudioeditor/test/doc_buffer-and-array.html @@ -37,17 +37,17 @@ for (let i = 0; i < n; ++i) { let x = (i - n2) / n2; let y = Math.atan(5 * x) / (0.5 * Math.PI); } return curve; } - function getBuffer (url, callback) { + function getBuffer(url, callback) { let xhr = new XMLHttpRequest(); xhr.open("GET", url, true); xhr.responseType = "arraybuffer"; xhr.onload = callback; xhr.send(); return xhr; } </script>
--- a/devtools/client/webaudioeditor/test/doc_bug_1112378.html +++ b/devtools/client/webaudioeditor/test/doc_bug_1112378.html @@ -11,47 +11,46 @@ <body> <script type="text/javascript"> "use strict"; let ctx = new AudioContext(); let osc = ctx.createOscillator(); - function throwError () { + function throwError() { try { osc.connect({}); } catch (e) { return { lineNumber: e.lineNumber, fileName: e.fileName, columnNumber: e.columnNumber, message: e.message, instanceof: e instanceof TypeError, stringified: e.toString(), name: e.name - } + }; } } - function throwDOMException () { + function throwDOMException() { try { osc.frequency.setValueAtTime(0, -1); } catch (e) { return { lineNumber: e.lineNumber, columnNumber: e.columnNumber, filename: e.filename, message: e.message, code: e.code, result: e.result, instanceof: e instanceof DOMException, stringified: e.toString(), name: e.name - } + }; } } - </script> </body> </html>
--- a/devtools/client/webaudioeditor/test/doc_destroy-nodes.html +++ b/devtools/client/webaudioeditor/test/doc_destroy-nodes.html @@ -11,26 +11,26 @@ <body> <script type="text/javascript"> "use strict"; // Keep the nodes we want to GC alive until we are ready for them to // be collected. We will zero this reference by force from the devtools // side. var keepAlive = []; - (function () { - let ctx = new AudioContext(); - let osc = ctx.createOscillator(); - let gain = ctx.createGain(); + (function() { + let ctx = new AudioContext(); + let osc = ctx.createOscillator(); + let gain = ctx.createGain(); - for (let i = 0; i < 10; i++) { - keepAlive.push(ctx.createBufferSource()); - } + for (let i = 0; i < 10; i++) { + keepAlive.push(ctx.createBufferSource()); + } - osc.connect(gain); - gain.connect(ctx.destination); - gain.gain.value = 0; - osc.start(); + osc.connect(gain); + gain.connect(ctx.destination); + gain.gain.value = 0; + osc.start(); })(); </script> </body> </html>
--- a/devtools/client/webaudioeditor/test/head.js +++ b/devtools/client/webaudioeditor/test/head.js @@ -57,17 +57,17 @@ function navigate(aTarget, aUrl, aWaitFo * This requires calling removeTab before the test ends. */ function initBackend(aUrl) { info("Initializing a web audio editor front."); DebuggerServer.init(); DebuggerServer.registerAllActors(); - return (async function () { + return (async function() { let tab = await addTab(aUrl); let target = TargetFactory.forTab(tab); await target.makeRemote(); let front = new WebAudioFront(target.client, target.form); return { target, front }; })(); @@ -76,17 +76,17 @@ function initBackend(aUrl) { /** * Adds a new tab, and open the toolbox for that tab, selecting the audio editor * panel. * This requires calling teardown before the test ends. */ function initWebAudioEditor(aUrl) { info("Initializing a web audio editor pane."); - return (async function () { + return (async function() { let tab = await addTab(aUrl); let target = TargetFactory.forTab(tab); await target.makeRemote(); Services.prefs.setBoolPref("devtools.webaudioeditor.enabled", true); let toolbox = await gDevTools.showToolbox(target, "webaudioeditor"); let panel = toolbox.getCurrentPanel(); @@ -113,49 +113,63 @@ function teardown(aTarget) { // // Takes a `front` object that is an event emitter, the number of // programs that should be listened to and waited on, and an optional // `onAdd` function that calls with the entire actors array on program link function getN(front, eventName, count, spread) { let actors = []; info(`Waiting for ${count} ${eventName} events`); - return new Promise((resolve, reject) => { + return new Promise((resolve) => { front.on(eventName, function onEvent(...args) { let actor = args[0]; if (actors.length !== count) { actors.push(spread ? args : actor); } info(`Got ${actors.length} / ${count} ${eventName} events`); if (actors.length === count) { front.off(eventName, onEvent); resolve(actors); } }); }); } -function get(front, eventName) { return getN(front, eventName, 1); } -function get2(front, eventName) { return getN(front, eventName, 2); } -function get3(front, eventName) { return getN(front, eventName, 3); } -function getSpread(front, eventName) { return getN(front, eventName, 1, true); } -function get2Spread(front, eventName) { return getN(front, eventName, 2, true); } -function get3Spread(front, eventName) { return getN(front, eventName, 3, true); } -function getNSpread(front, eventName, count) { return getN(front, eventName, count, true); } +function get(front, eventName) { + return getN(front, eventName, 1); +} +function get2(front, eventName) { + return getN(front, eventName, 2); +} +function get3(front, eventName) { + return getN(front, eventName, 3); +} +function getSpread(front, eventName) { + return getN(front, eventName, 1, true); +} +function get2Spread(front, eventName) { + return getN(front, eventName, 2, true); +} +function get3Spread(front, eventName) { + return getN(front, eventName, 3, true); +} +function getNSpread(front, eventName, count) { + return getN(front, eventName, count, true); +} /** * Waits for the UI_GRAPH_RENDERED event to fire, but only * resolves when the graph was rendered with the correct count of * nodes and edges. */ function waitForGraphRendered(front, nodeCount, edgeCount, paramEdgeCount) { let eventName = front.EVENTS.UI_GRAPH_RENDERED; info(`Wait for graph rendered with ${nodeCount} nodes, ${edgeCount} edges`); - return new Promise((resolve, reject) => { + return new Promise((resolve) => { front.on(eventName, function onGraphRendered(nodes, edges, pEdges) { let paramEdgesDone = paramEdgeCount != null ? paramEdgeCount === pEdges : true; info(`Got graph rendered with ${nodes} / ${nodeCount} nodes, ` + `${edges} / ${edgeCount} edges`); if (nodes === nodeCount && edges === edgeCount && paramEdgesDone) { front.off(eventName, onGraphRendered); resolve(); } @@ -183,44 +197,42 @@ function checkVariableView(view, index, "Correct property name for " + variable); let value = aVar.target.querySelector(".value").getAttribute("value"); // Cast value with JSON.parse if possible; // will fail when displaying Object types like "ArrayBuffer" // and "Float32Array", but will match the original value. try { value = JSON.parse(value); - } - catch (e) {} + } catch (e) {} if (typeof hash[variable] === "function") { ok(hash[variable](value), "Passing property value of " + value + " for " + variable + " " + description); - } - else { + } else { is(value, hash[variable], "Correct property value of " + hash[variable] + " for " + variable + " " + description); } }); } function modifyVariableView(win, view, index, prop, value) { let scope = view.getScopeAtIndex(index); let aVar = scope.get(prop); scope.expand(); return new Promise((resolve, reject) => { const onParamSetSuccess = () => { win.off(win.EVENTS.UI_SET_PARAM_ERROR, onParamSetError); resolve(); - } + }; const onParamSetError = () => { win.off(win.EVENTS.UI_SET_PARAM, onParamSetSuccess); reject(); - } + }; win.once(win.EVENTS.UI_SET_PARAM, onParamSetSuccess); win.once(win.EVENTS.UI_SET_PARAM_ERROR, onParamSetError); // Focus and select the variable to begin editing win.focus(); aVar.focus(); EventUtils.sendKey("RETURN", win); @@ -320,17 +332,17 @@ function countGraphObjects(win) { edges: win.document.querySelectorAll(".edgePaths > .edgePath").length }; } /** * Forces cycle collection and GC, used in AudioNode destruction tests. */ function forceNodeCollection() { - ContentTask.spawn(gBrowser.selectedBrowser, {}, async function () { + ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() { // Kill the reference keeping stuff alive. content.wrappedJSObject.keepAlive = null; // Collect the now-deceased nodes. Cu.forceGC(); Cu.forceCC(); Cu.forceGC(); Cu.forceCC(); @@ -379,17 +391,19 @@ function waitForInspectorRender(panelWin /** * Takes an AudioNode type and returns it's properties (from audionode.json) * as keys and their default values as keys */ function nodeDefaultValues(nodeName) { let fn = NODE_CONSTRUCTORS[nodeName]; - if (typeof fn === "undefined") return {}; + if (typeof fn === "undefined") { + return {}; + } let init = nodeName === "AudioDestinationNode" ? "destination" : `create${fn}()`; let definition = JSON.stringify(audioNodes[nodeName].properties); let evalNode = evalInDebuggee(` let ins = (new AudioContext()).${init}; let props = ${definition};
--- a/devtools/client/webaudioeditor/views/automation.js +++ b/devtools/client/webaudioeditor/views/automation.js @@ -7,43 +7,43 @@ * Functions handling the audio node inspector UI. */ var AutomationView = { /** * Initialization function called when the tool starts up. */ - initialize: function () { + initialize: function() { this._buttons = $("#automation-param-toolbar-buttons"); this.graph = new LineGraphWidget($("#automation-graph"), { avg: false }); this.graph.selectionEnabled = false; this._onButtonClick = this._onButtonClick.bind(this); this._onNodeSet = this._onNodeSet.bind(this); this._onResize = this._onResize.bind(this); this._buttons.addEventListener("click", this._onButtonClick); window.on(EVENTS.UI_INSPECTOR_RESIZE, this._onResize); window.on(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSet); }, /** * Destruction function called when the tool cleans up. */ - destroy: function () { + destroy: function() { this._buttons.removeEventListener("click", this._onButtonClick); window.off(EVENTS.UI_INSPECTOR_RESIZE, this._onResize); window.off(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSet); }, /** * Empties out the props view. */ - resetUI: function () { + resetUI: function() { this._currentNode = null; }, /** * On a new node selection, create the Automation panel for * that specific node. */ async build() { @@ -78,17 +78,17 @@ var AutomationView = { await this.graph.setDataWhenReady(values); window.emit(EVENTS.UI_AUTOMATION_TAB_RENDERED, node.id); }, /** * Create the buttons for each AudioParam, that when clicked, * render the graph for that AudioParam. */ - _createParamButtons: function (params) { + _createParamButtons: function(params) { this._buttons.innerHTML = ""; params.forEach((param, i) => { let button = document.createElement("toolbarbutton"); button.setAttribute("class", "devtools-toolbarbutton automation-param-button"); button.setAttribute("data-param", param.param); // Set label to the parameter name, should not be L10N'd button.setAttribute("label", param.param); @@ -100,29 +100,29 @@ var AutomationView = { this._buttons.appendChild(button); }); }, /** * Internally sets the current audio node and rebuilds appropriate * views. */ - _setAudioNode: function (node) { + _setAudioNode: function(node) { this._currentNode = node; if (this._currentNode) { this.build(); } }, /** * Toggles the subviews to display messages whether or not * the audio node has no AudioParams, no automation events, or * shows the graph. */ - _setState: function (state) { + _setState: function(state) { let contentView = $("#automation-content"); let emptyView = $("#automation-empty"); let graphView = $("#automation-graph-container"); let noEventsView = $("#automation-no-events"); contentView.hidden = state === "no-params"; emptyView.hidden = state !== "no-params"; @@ -130,30 +130,30 @@ var AutomationView = { graphView.hidden = state !== "show"; noEventsView.hidden = state !== "no-events"; }, /** * Event handlers */ - _onButtonClick: function (e) { + _onButtonClick: function(e) { Array.forEach($$(".automation-param-button"), $btn => $btn.removeAttribute("selected")); let paramName = e.target.getAttribute("data-param"); e.target.setAttribute("selected", true); this._selectedParamName = paramName; this.render(); }, /** * Called when the inspector is resized. */ - _onResize: function () { + _onResize: function() { this.graph.refresh(); }, /** * Called when the inspector view determines a node is selected. */ - _onNodeSet: function (id) { + _onNodeSet: function(id) { this._setAudioNode(id != null ? gAudioNodes.get(id) : null); } };
--- a/devtools/client/webaudioeditor/views/context.js +++ b/devtools/client/webaudioeditor/views/context.js @@ -38,151 +38,151 @@ const GRAPH_REDRAW_EVENTS = ["add", "con /** * Functions handling the graph UI. */ var ContextView = { /** * Initialization function, called when the tool is started. */ - initialize: function () { + initialize: function() { this._onGraphClick = this._onGraphClick.bind(this); this._onThemeChange = this._onThemeChange.bind(this); this._onStartContext = this._onStartContext.bind(this); this._onEvent = this._onEvent.bind(this); this.draw = debounce(this.draw.bind(this), GRAPH_DEBOUNCE_TIMER); $("#graph-target").addEventListener("click", this._onGraphClick); window.on(EVENTS.THEME_CHANGE, this._onThemeChange); window.on(EVENTS.START_CONTEXT, this._onStartContext); gAudioNodes.on("*", this._onEvent); }, /** * Destruction function, called when the tool is closed. */ - destroy: function () { + destroy: function() { // If the graph was rendered at all, then the handler // for zooming in will be set. We must remove it to prevent leaks. if (this._zoomBinding) { this._zoomBinding.on("zoom", null); } $("#graph-target").removeEventListener("click", this._onGraphClick); window.off(EVENTS.THEME_CHANGE, this._onThemeChange); window.off(EVENTS.START_CONTEXT, this._onStartContext); gAudioNodes.off("*", this._onEvent); }, /** * Called when a page is reloaded and waiting for a "start-context" event * and clears out old content */ - resetUI: function () { + resetUI: function() { this.clearGraph(); this.resetGraphTransform(); }, /** * Clears out the rendered graph, called when resetting the SVG elements to draw again, * or when resetting the entire UI tool */ - clearGraph: function () { + clearGraph: function() { $("#graph-target").innerHTML = ""; }, /** * Moves the graph back to its original scale and translation. */ - resetGraphTransform: function () { + resetGraphTransform: function() { // Only reset if the graph was ever drawn. if (this._zoomBinding) { let { translate, scale } = GRAPH_DEFAULTS; // Must set the `zoomBinding` so the next `zoom` event is in sync with // where the graph is visually (set by the `transform` attribute). this._zoomBinding.scale(scale); this._zoomBinding.translate(translate); d3.select("#graph-target") .attr("transform", "translate(" + translate + ") scale(" + scale + ")"); } }, - getCurrentScale: function () { + getCurrentScale: function() { return this._zoomBinding ? this._zoomBinding.scale() : null; }, - getCurrentTranslation: function () { + getCurrentTranslation: function() { return this._zoomBinding ? this._zoomBinding.translate() : null; }, /** * Makes the corresponding graph node appear "focused", removing * focused styles from all other nodes. If no `actorID` specified, * make all nodes appear unselected. */ - focusNode: function (actorID) { + focusNode: function(actorID) { // Remove class "selected" from all nodes Array.forEach($$(".nodes > g"), $node => $node.classList.remove("selected")); // Add to "selected" if (actorID) { this._getNodeByID(actorID).classList.add("selected"); } }, /** * Takes an actorID and returns the corresponding DOM SVG element in the graph */ - _getNodeByID: function (actorID) { + _getNodeByID: function(actorID) { return $(".nodes > g[data-id='" + actorID + "']"); }, /** * Sets the appropriate class on an SVG node when its bypass * status is toggled. */ - _bypassNode: function (node, enabled) { + _bypassNode: function(node, enabled) { let el = this._getNodeByID(node.id); el.classList[enabled ? "add" : "remove"]("bypassed"); }, /** * This method renders the nodes currently available in `gAudioNodes` and is * throttled to be called at most every `GRAPH_DEBOUNCE_TIMER` milliseconds. * It's called whenever the audio context routing changes, after being debounced. */ - draw: function () { + draw: function() { // Clear out previous SVG information this.clearGraph(); let graph = new dagreD3.Digraph(); let renderer = new dagreD3.Renderer(); gAudioNodes.populateGraph(graph); // Post-render manipulation of the nodes let oldDrawNodes = renderer.drawNodes(); - renderer.drawNodes(function (graph, root) { + renderer.drawNodes(function(graph, root) { let svgNodes = oldDrawNodes(graph, root); - svgNodes.each(function (n) { + svgNodes.each(function(n) { let node = graph.node(n); let classString = "audionode type-" + node.type + (node.bypassed ? " bypassed" : ""); this.setAttribute("class", classString); this.setAttribute("data-id", node.id); this.setAttribute("data-type", node.type); }); return svgNodes; }); // Post-render manipulation of edges let oldDrawEdgePaths = renderer.drawEdgePaths(); let defaultClasses = "edgePath enter"; - renderer.drawEdgePaths(function (graph, root) { + renderer.drawEdgePaths(function(graph, root) { let svgEdges = oldDrawEdgePaths(graph, root); - svgEdges.each(function (e) { + svgEdges.each(function(e) { let edge = graph.edge(e); // We have to manually specify the default classes on the edges // as to not overwrite them let edgeClass = defaultClasses + (edge.param ? (" param-connection " + edge.param) : ""); this.setAttribute("data-source", edge.source); this.setAttribute("data-target", edge.target); @@ -237,17 +237,17 @@ var ContextView = { }); let layout = dagreD3.layout().rankDir("LR"); renderer.layout(layout).run(graph, d3.select("#graph-target")); // Handle the sliding and zooming of the graph, // store as `this._zoomBinding` so we can unbind during destruction if (!this._zoomBinding) { - this._zoomBinding = d3.behavior.zoom().on("zoom", function () { + this._zoomBinding = d3.behavior.zoom().on("zoom", function() { var ev = d3.event; d3.select("#graph-target") .attr("transform", "translate(" + ev.translate + ") scale(" + ev.scale + ")"); }); d3.select("svg").call(this._zoomBinding); // Set initial translation and scale -- this puts D3's awareness of // the graph in sync with what the user sees originally. @@ -258,57 +258,58 @@ var ContextView = { /** * Event handlers */ /** * Called once "start-context" is fired, indicating that there is an audio * context being created to view so render the graph. */ - _onStartContext: function () { + _onStartContext: function() { this.draw(); }, /** * Called when `gAudioNodes` fires an event -- most events (listed * in GRAPH_REDRAW_EVENTS) qualify as a redraw event. */ - _onEvent: function (eventName, ...args) { + _onEvent: function(eventName, ...args) { // If bypassing, just toggle the class on the SVG node // rather than rerendering everything if (eventName === "bypass") { this._bypassNode.apply(this, args); } if (~GRAPH_REDRAW_EVENTS.indexOf(eventName)) { this.draw(); } }, /** * Fired when the devtools theme changes. */ - _onThemeChange: function (theme) { + _onThemeChange: function(theme) { let markerColor = MARKER_STYLING[theme]; let marker = $("#arrowhead"); if (marker) { marker.setAttribute("style", "fill: " + markerColor); } }, /** * Fired when a click occurs in the graph. * * @param Event e * Click event. */ - _onGraphClick: function (e) { + _onGraphClick: function(e) { let node = findGraphNodeParent(e.target); // If node not found (clicking outside of an audio node in the graph), // then ignore this event - if (!node) + if (!node) { return; + } let id = node.getAttribute("data-id"); this.focusNode(id); window.emit(EVENTS.UI_SELECT_NODE, id); } };
--- a/devtools/client/webaudioeditor/views/inspector.js +++ b/devtools/client/webaudioeditor/views/inspector.js @@ -23,17 +23,17 @@ var InspectorView = { _expandString: EXPAND_INSPECTOR_STRING, _toggleEvent: EVENTS.UI_INSPECTOR_TOGGLED, _animated: true, _delayed: true, /** * Initialization function called when the tool starts up. */ - initialize: function () { + initialize: function() { // Set up view controller this.el = $("#web-audio-inspector"); this.splitter = $("#inspector-splitter"); this.el.setAttribute("width", Services.prefs.getIntPref("devtools.webaudioeditor.inspectorWidth")); this.button = $("#inspector-pane-toggle"); mixin(this, ToggleMixin); this.bindToggle(); @@ -51,17 +51,17 @@ var InspectorView = { } window.on(EVENTS.UI_SELECT_NODE, this._onNodeSelect); gAudioNodes.on("remove", this._onDestroyNode); }, /** * Destruction function called when the tool cleans up. */ - destroy: function () { + destroy: function() { this.unbindToggle(); this.splitter.removeEventListener("mouseup", this._onResize); $("#audio-node-toolbar toolbarbutton").removeEventListener("command", this._onCommandClick); for (let $el of $$("#audio-node-toolbar toolbarbutton")) { $el.removeEventListener("command", this._onCommandClick); } window.off(EVENTS.UI_SELECT_NODE, this._onNodeSelect); @@ -80,45 +80,44 @@ var InspectorView = { this._currentNode = node || null; // If no node selected, set the inspector back to "no AudioNode selected" // view. if (!node) { $("#web-audio-editor-details-pane-empty").removeAttribute("hidden"); $("#web-audio-editor-tabs").setAttribute("hidden", "true"); window.emit(EVENTS.UI_INSPECTOR_NODE_SET, null); - } - // Otherwise load up the tabs view and hide the empty placeholder - else { + } else { + // Otherwise load up the tabs view and hide the empty placeholder $("#web-audio-editor-details-pane-empty").setAttribute("hidden", "true"); $("#web-audio-editor-tabs").removeAttribute("hidden"); this._buildToolbar(); window.emit(EVENTS.UI_INSPECTOR_NODE_SET, this._currentNode.id); } }, /** * Returns the current AudioNodeView. */ - getCurrentAudioNode: function () { + getCurrentAudioNode: function() { return this._currentNode; }, /** * Empties out the props view. */ - resetUI: function () { + resetUI: function() { // Set current node to empty to load empty view this.setCurrentAudioNode(); // Reset AudioNode inspector and hide this.hideImmediately(); }, - _buildToolbar: function () { + _buildToolbar: function() { let node = this.getCurrentAudioNode(); let bypassable = node.bypassable; let bypassed = node.isBypassed(); let button = $("#audio-node-toolbar .bypass"); if (!bypassable) { button.setAttribute("disabled", true); @@ -136,42 +135,42 @@ var InspectorView = { /** * Event handlers */ /** * Called on EVENTS.UI_SELECT_NODE, and takes an actorID `id` * and calls `setCurrentAudioNode` to scaffold the inspector view. */ - _onNodeSelect: function (id) { + _onNodeSelect: function(id) { this.setCurrentAudioNode(gAudioNodes.get(id)); // Ensure inspector is visible when selecting a new node this.show(); }, - _onResize: function () { + _onResize: function() { if (this.el.getAttribute("width") < MIN_INSPECTOR_WIDTH) { this.el.setAttribute("width", MIN_INSPECTOR_WIDTH); } Services.prefs.setIntPref("devtools.webaudioeditor.inspectorWidth", this.el.getAttribute("width")); window.emit(EVENTS.UI_INSPECTOR_RESIZE); }, /** * Called when `DESTROY_NODE` is fired to remove the node from props view if * it's currently selected. */ - _onDestroyNode: function (node) { + _onDestroyNode: function(node) { if (this._currentNode && this._currentNode.id === node.id) { this.setCurrentAudioNode(null); } }, - _onCommandClick: function (e) { + _onCommandClick: function(e) { let node = this.getCurrentAudioNode(); let button = e.target; let command = button.getAttribute("data-command"); let checked = button.getAttribute("checked"); if (button.getAttribute("disabled")) { return; }
--- a/devtools/client/webaudioeditor/views/properties.js +++ b/devtools/client/webaudioeditor/views/properties.js @@ -18,46 +18,46 @@ const GENERIC_VARIABLES_VIEW_SETTINGS = * Functions handling the audio node inspector UI. */ var PropertiesView = { /** * Initialization function called when the tool starts up. */ - initialize: function () { + initialize: function() { this._onEval = this._onEval.bind(this); this._onNodeSet = this._onNodeSet.bind(this); window.on(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSet); this._propsView = new VariablesView($("#properties-content"), GENERIC_VARIABLES_VIEW_SETTINGS); this._propsView.eval = this._onEval; }, /** * Destruction function called when the tool cleans up. */ - destroy: function () { + destroy: function() { window.off(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSet); this._propsView = null; }, /** * Empties out the props view. */ - resetUI: function () { + resetUI: function() { this._propsView.empty(); this._currentNode = null; }, /** * Internally sets the current audio node and rebuilds appropriate * views. */ - _setAudioNode: function (node) { + _setAudioNode: function(node) { this._currentNode = node; if (this._currentNode) { this._buildPropertiesView(); } }, /** * Reconstructs the `Properties` tab in the inspector @@ -90,41 +90,41 @@ var PropertiesView = { window.emit(EVENTS.UI_PROPERTIES_TAB_RENDERED, node.id); }, /** * Toggles the display of the "empty" properties view when * node has no properties to display. */ - _togglePropertiesView: function (show) { + _togglePropertiesView: function(show) { let propsView = $("#properties-content"); let emptyView = $("#properties-empty"); (show ? propsView : emptyView).removeAttribute("hidden"); (show ? emptyView : propsView).setAttribute("hidden", "true"); }, /** * Returns the scope for AudioParams in the * VariablesView. * * @return Scope */ - _getAudioPropertiesScope: function () { + _getAudioPropertiesScope: function() { return this._propsView.getScopeAtIndex(0); }, /** * Event handlers */ /** * Called when the inspector view determines a node is selected. */ - _onNodeSet: function (id) { + _onNodeSet: function(id) { this._setAudioNode(gAudioNodes.get(id)); }, /** * Executed when an audio prop is changed in the UI. */ async _onEval(variable, value) { let ownerScope = variable.ownerView; @@ -139,18 +139,17 @@ var PropertiesView = { try { let number = parseFloat(value); if (!isNaN(number)) { value = number; } else { value = JSON.parse(value); } error = await node.actor.setParam(propName, value); - } - catch (e) { + } catch (e) { error = e; } } // TODO figure out how to handle and display set prop errors // and enable `test/brorwser_wa_properties-view-edit.js` // Bug 994258 if (!error) {
--- a/devtools/client/webaudioeditor/views/utils.js +++ b/devtools/client/webaudioeditor/views/utils.js @@ -6,24 +6,25 @@ /** * Takes an element in an SVG graph and iterates over * ancestors until it finds the graph node container. If not found, * returns null. */ function findGraphNodeParent(el) { // Some targets may not contain `classList` property - if (!el.classList) + if (!el.classList) { return null; + } while (!el.classList.contains("nodes")) { - if (el.classList.contains("audionode")) + if (el.classList.contains("audionode")) { return el; - else - el = el.parentNode; + } + el = el.parentNode; } return null; } /** * Object for use with `mix` into a view. * Must have the following properties defined on the view: * - `el` @@ -34,70 +35,69 @@ function findGraphNodeParent(el) { * * Optional properties on the view can be defined to specify default * visibility options. * - `_animated` * - `_delayed` */ var ToggleMixin = { - bindToggle: function () { + bindToggle: function() { this._onToggle = this._onToggle.bind(this); this.button.addEventListener("mousedown", this._onToggle); }, - unbindToggle: function () { + unbindToggle: function() { this.button.removeEventListener("mousedown", this._onToggle); }, - show: function () { + show: function() { this._viewController({ visible: true }); }, - hide: function () { + hide: function() { this._viewController({ visible: false }); }, - hideImmediately: function () { + hideImmediately: function() { this._viewController({ visible: false, delayed: false, animated: false }); }, /** * Returns a boolean indicating whether or not the view. * is currently being shown. */ - isVisible: function () { + isVisible: function() { return !this.el.classList.contains("pane-collapsed"); }, /** * Toggles the visibility of the view. * * @param object visible * - visible: boolean indicating whether the panel should be shown or not * - animated: boolean indiciating whether the pane should be animated * - delayed: boolean indicating whether the pane's opening should wait * a few cycles or not */ - _viewController: function ({ visible, animated, delayed }) { + _viewController: function({ visible, animated, delayed }) { let flags = { visible: visible, animated: animated != null ? animated : !!this._animated, delayed: delayed != null ? delayed : !!this._delayed, callback: () => window.emit(this._toggleEvent, visible) }; ViewHelpers.togglePane(flags, this.el); if (flags.visible) { this.button.classList.remove("pane-collapsed"); this.button.setAttribute("tooltiptext", this._collapseString); - } - else { + } else { this.button.classList.add("pane-collapsed"); this.button.setAttribute("tooltiptext", this._expandString); } }, - _onToggle: function () { + _onToggle: function() { this._viewController({ visible: !this.isVisible() }); } };
--- a/devtools/client/webconsole/old/test/head.js +++ b/devtools/client/webconsole/old/test/head.js @@ -38,18 +38,16 @@ const SEVERITY_LOG = 3; const GROUP_INDENT = 12; var WCUL10n = require("devtools/client/webconsole/webconsole-l10n"); const DOCS_GA_PARAMS = "?utm_source=mozilla" + "&utm_medium=firefox-console-errors" + "&utm_campaign=default"; -flags.testing = true; - Services.prefs.setBoolPref("devtools.browserconsole.new-frontend-enabled", false); registerCleanupFunction(async function () { Services.prefs.clearUserPref("devtools.browserconsole.new-frontend-enabled"); }); function loadTab(url, preferredRemoteType) { return addTab(url, { preferredRemoteType }).then( tab => { return { tab, browser: tab.linkedBrowser }; @@ -320,18 +318,16 @@ var finishTest = Task.async(function* () // Always use the 'old' frontend for tests that rely on it Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", false); registerCleanupFunction(function* () { Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled"); }); registerCleanupFunction(function* () { - flags.testing = false; - // Remove stored console commands in between tests yield asyncStorage.removeItem("webConsoleHistory"); dumpConsoles(); let browserConsole = HUDService.getBrowserConsole(); if (browserConsole) { if (browserConsole.jsterm) {
--- a/devtools/client/webconsole/reducers/messages.js +++ b/devtools/client/webconsole/reducers/messages.js @@ -13,16 +13,17 @@ const { const constants = require("devtools/client/webconsole/constants"); const { DEFAULT_FILTERS, FILTERS, MESSAGE_TYPE, MESSAGE_SOURCE, } = constants; const { getGripPreviewItems } = require("devtools/client/shared/components/reps/reps"); +const { getUnicodeUrlPath } = require("devtools/client/shared/unicode-url"); const { getSourceNames } = require("devtools/client/shared/source-utils"); const { UPDATE_REQUEST, } = require("devtools/client/netmonitor/src/constants"); const { processNetworkUpdates, @@ -777,20 +778,23 @@ function isTextInFrame(text, frame) { const { functionName, line, column, source } = frame; const { short } = getSourceNames(source); + const unicodeShort = getUnicodeUrlPath(short); - return `${functionName ? functionName + " " : ""}${short}:${line}:${column}` + const includes = + `${functionName ? functionName + " " : ""}${unicodeShort}:${line}:${column}` .toLocaleLowerCase() .includes(text.toLocaleLowerCase()); + return includes; } /** * Returns true if given text is included in provided parameters. */ function isTextInParameters(text, parameters) { if (!parameters) { return false;
--- a/devtools/client/webconsole/test/mochitest/browser.ini +++ b/devtools/client/webconsole/test/mochitest/browser.ini @@ -15,19 +15,16 @@ support-files = test_bug_770099_violation.html test_bug_770099_violation.html^headers^ test_console_csp_ignore_reflected_xss_message.html test_console_csp_ignore_reflected_xss_message.html^headers^ test_hpkp-invalid-headers.sjs test_hsts-invalid-headers.sjs test-autocomplete-in-stackframe.html test-batching.html - test-bug_923281_console_log_filter.html - test-bug_923281_test1.js - test-bug_923281_test2.js test-console-trace-duplicates.html test-bug-585956-console-trace.html test-bug-599725-response-headers.sjs test-bug-601177-log-levels.html test-bug-601177-log-levels.js test-bug-630733-response-redirect-headers.sjs test-bug-632275-getters.html test-bug-646025-console-file-location.html @@ -273,16 +270,17 @@ skip-if = (e10s && debug) || (e10s && os [browser_webconsole_duplicate_errors.js] [browser_webconsole_errors_after_page_reload.js] [browser_webconsole_eval_in_debugger_stackframe.js] [browser_webconsole_eval_in_debugger_stackframe2.js] [browser_webconsole_execution_scope.js] [browser_webconsole_external_script_errors.js] [browser_webconsole_file_uri.js] skip-if = true # Bug 1404382 +[browser_webconsole_filter_by_input.js] [browser_webconsole_filter_scroll.js] [browser_webconsole_filters.js] [browser_webconsole_filters_persist.js] [browser_webconsole_highlighter_console_helper.js] [browser_webconsole_history_arrow_keys.js] [browser_webconsole_hpkp_invalid-headers.js] [browser_webconsole_hsts_invalid-headers.js] [browser_webconsole_iframe_wrong_hud.js]
new file mode 100644 --- /dev/null +++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_filter_by_input.js @@ -0,0 +1,242 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the text filter box works to filter based on location. + +"use strict"; + +// In this test, we are trying to test the filtering functionality of the filter +// input. We test filtering not only for the contents of logs themseleves but +// also for the filenames. +// +// We simulate an HTML file which executes two Javascript files, one with an +// ASCII filename outputs some ASCII logs and the other one with a Unicode +// filename outputs some Unicode logs. + +const SEASON = { + english: "season", + chinese: "\u5b63", +}; +const SEASONS = [ + { + english: "spring", + chinese: "\u6625", + escapedChinese: "\\u6625", + }, + { + english: "summer", + chinese: "\u590f", + escapedChinese: "\\u590f", + }, + { + english: "autumn", + chinese: "\u79cb", + escapedChinese: "\\u79cb", + }, + { + english: "winter", + chinese: "\u51ac", + escapedChinese: "\\u51ac", + }, +]; + +// filenames +const HTML_FILENAME = `test.html`; +const JS_ASCII_FILENAME = `${SEASON.english}.js`; +const JS_UNICODE_FILENAME = `${SEASON.chinese}.js`; +const ENCODED_JS_UNICODE_FILENAME = encodeURIComponent(JS_UNICODE_FILENAME); + +// file contents +const HTML_CONSOLE_OUTPUT = `Test filtering ${SEASON.english} names.`; +const HTML_CONTENT = `<!DOCTYPE html> +<meta charset="utf-8"> +<title>Test filtering logs by filling keywords in the filter input.</title> +<script> +console.log("${HTML_CONSOLE_OUTPUT}"); +</script> +<script src="/${JS_ASCII_FILENAME}"></script> +<script src="/${ENCODED_JS_UNICODE_FILENAME}"></script>`; + +add_task(async function() { + const testUrl = createServerAndGetTestUrl(); + const hud = await openNewTabAndConsole(testUrl); + + // Let's wait for the last logged message of each file to be displayed in the + // output, in order to make sure all the logged messages have been displayed. + const lastSeason = SEASONS[SEASONS.length - 1]; + await waitFor(() => + findMessage(hud, lastSeason.english) && + findMessage(hud, lastSeason.chinese) + ); + + // One external Javascript file outputs every season name in English, and the + // other Javascript file outputs every season name in Chinese. + // The HTML file outputs one line on its own. + // So the total number of all the logs is the doubled number of seasons plus + // one. + let visibleLogs = getVisibleLogs(hud); + is(visibleLogs.length, SEASONS.length * 2 + 1, + "the total number of all the logs before starting filtering"); + checkLogContent(visibleLogs[0], HTML_CONSOLE_OUTPUT, HTML_FILENAME); + for (let i = 0; i < SEASONS.length; i++) { + checkLogContent(visibleLogs[i + 1], SEASONS[i].english, JS_ASCII_FILENAME); + } + for (let i = 0; i < SEASONS.length; i++) { + checkLogContent(visibleLogs[i + 1 + SEASONS.length], SEASONS[i].chinese, + JS_UNICODE_FILENAME); + } + + // All the logs outputted by the ASCII Javascript file are visible, the others + // are hidden. + setFilterInput(hud, JS_ASCII_FILENAME); + visibleLogs = getVisibleLogs(hud); + is(visibleLogs.length, SEASONS.length, + `the number of all the logs containing ${JS_ASCII_FILENAME}`); + for (let i = 0; i < SEASONS.length; i++) { + checkLogContent(visibleLogs[i], SEASONS[i].english, JS_ASCII_FILENAME); + } + + // Every season name in English is outputted once. + for (let curSeason of SEASONS) { + setFilterInput(hud, curSeason.english); + visibleLogs = getVisibleLogs(hud); + is(visibleLogs.length, 1, + `the number of all the logs containing ${curSeason.english}`); + checkLogContent(visibleLogs[0], curSeason.english, JS_ASCII_FILENAME); + } + + // All the logs outputted by the Unicode Javascript file are visible, the + // others are hidden. + setFilterInput(hud, JS_UNICODE_FILENAME); + visibleLogs = getVisibleLogs(hud); + is(visibleLogs.length, SEASONS.length, + `the number of all the logs containing ${JS_UNICODE_FILENAME}`); + for (let i = 0; i < SEASONS.length; i++) { + checkLogContent(visibleLogs[i], SEASONS[i].chinese, JS_UNICODE_FILENAME); + } + + // Every season name in Chinese is outputted once. + for (let curSeason of SEASONS) { + setFilterInput(hud, curSeason.chinese); + visibleLogs = getVisibleLogs(hud); + is(visibleLogs.length, 1, + `the number of all the logs containing ${curSeason.chinese}`); + checkLogContent(visibleLogs[0], curSeason.chinese, JS_UNICODE_FILENAME); + } + + // The filename of the ASCII Javascript file contains the English word season, + // so all the logs outputted by the file are visible, besides, the HTML + // outputs one line containing the English word season, so it is also visible. + // The other logs are hidden. So the number of all the visible logs is the + // season number plus one. + setFilterInput(hud, SEASON.english); + visibleLogs = getVisibleLogs(hud); + is(visibleLogs.length, SEASONS.length + 1, + `the number of all the logs containing ${SEASON.english}`); + checkLogContent(visibleLogs[0], HTML_CONSOLE_OUTPUT, HTML_FILENAME); + for (let i = 0; i < SEASONS.length; i++) { + checkLogContent(visibleLogs[i + 1], SEASONS[i].english, JS_ASCII_FILENAME); + } + + // The filename of the Unicode Javascript file contains the Chinese word + // season, so all the logs outputted by the file are visible. The other logs + // are hidden. So the number of all the visible logs is the season number. + setFilterInput(hud, SEASON.chinese); + visibleLogs = getVisibleLogs(hud); + is(visibleLogs.length, SEASONS.length, + `the number of all the logs containing ${SEASON.chinese}`); + for (let i = 0; i < SEASONS.length; i++) { + checkLogContent(visibleLogs[i], SEASONS[i].chinese, JS_UNICODE_FILENAME); + } + + // After clearing the text in the filter input box, all the logs are visible + // again. + clearFilterInput(hud); + visibleLogs = getVisibleLogs(hud); + is(visibleLogs.length, SEASONS.length * 2 + 1, + "the total number of all the logs after clearing filtering"); + checkLogContent(visibleLogs[0], HTML_CONSOLE_OUTPUT, HTML_FILENAME); + for (let i = 0; i < SEASONS.length; i++) { + checkLogContent(visibleLogs[i + 1], SEASONS[i].english, JS_ASCII_FILENAME); + } + for (let i = 0; i < SEASONS.length; i++) { + checkLogContent(visibleLogs[i + 1 + SEASONS.length], SEASONS[i].chinese, + JS_UNICODE_FILENAME); + } +}); + +// Create an HTTP server to simulate a response for the a URL request and return +// the URL. +function createServerAndGetTestUrl() { + const httpServer = createTestHTTPServer(); + + httpServer.registerContentType("html", "text/html"); + httpServer.registerContentType("js", "application/javascript"); + + httpServer.registerPathHandler("/" + HTML_FILENAME, + function(request, response) { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write(HTML_CONTENT); + } + ); + httpServer.registerPathHandler("/" + JS_ASCII_FILENAME, + function(request, response) { + response.setStatusLine(request.httpVersion, 200, "OK"); + let content = ""; + for (let curSeason of SEASONS) { + content += `console.log("${curSeason.english}");`; + } + response.write(content); + } + ); + httpServer.registerPathHandler("/" + ENCODED_JS_UNICODE_FILENAME, + function(request, response) { + response.setStatusLine(request.httpVersion, 200, "OK"); + let content = ""; + for (let curSeason of SEASONS) { + content += `console.log("${curSeason.escapedChinese}");`; + } + response.write(content); + } + ); + const port = httpServer.identity.primaryPort; + return `http://localhost:${port}/${HTML_FILENAME}`; +} + +function setFilterInput(hud, value) { + hud.ui.filterBox.focus(); + hud.ui.filterBox.select(); + EventUtils.sendString(value); +} + +function clearFilterInput(hud) { + hud.ui.filterBox.focus(); + hud.ui.filterBox.select(); + EventUtils.synthesizeKey("KEY_Delete"); +} + +function getVisibleLogs(hud) { + let outputNode = hud.outputNode; + return outputNode.querySelectorAll(".message"); +} + +/** + * Check if the content of a log message is what we expect. + * + * @param Object node + * The node for the log message. + * @param String expectedMessageBody + * The string we expect to match the message body in the log message. + * @param String expectedFilename + * The string we expect to match the filename in the log message. + */ +function checkLogContent(node, expectedMessageBody, expectedFilename) { + const messageBody = node.querySelector(".message-body").textContent; + const location = node.querySelector(".message-location").textContent; + // The location detail contains the line number and the column number, let's + // strip them to get the filename. + const filename = location.split(":")[0]; + + is(messageBody, expectedMessageBody, "the expected message body"); + is(filename, expectedFilename, "the expected filename"); +}
--- a/devtools/client/webconsole/webpack.config.js +++ b/devtools/client/webconsole/webpack.config.js @@ -87,16 +87,17 @@ webpackConfig.resolve = { "devtools/client/shared/vendor/reselect": "reselect", "resource://gre/modules/AppConstants.jsm": path.join(__dirname, "../../client/shared/webpack/shims/app-constants-stub"), "devtools/client/framework/devtools": path.join(__dirname, "../../client/shared/webpack/shims/framework-devtools-shim"), "devtools/client/framework/menu": "devtools-modules/src/menu", "devtools/client/sourceeditor/editor": "devtools-source-editor/src/source-editor", + "devtools/client/shared/unicode-url": path.join(__dirname, "../../client/shared/webpack/shims/unicode-url-stub"), "devtools/client/shared/zoom-keys": "devtools-modules/src/zoom-keys", "devtools/shared/fronts/timeline": path.join(__dirname, "../../client/shared/webpack/shims/fronts-timeline-shim"), "devtools/shared/old-event-emitter": "devtools-modules/src/utils/event-emitter", "devtools/shared/event-emitter": "devtools-modules/src/utils/event-emitter", "devtools/shared/client/debugger-client": path.join(__dirname, "test/fixtures/DebuggerClient"), "devtools/shared/platform/clipboard": path.join(__dirname, "../../client/shared/webpack/shims/platform-clipboard-stub"), "devtools/shared/platform/stack": path.join(__dirname, "../../client/shared/webpack/shims/platform-stack-stub"),
--- a/devtools/client/webide/content/addons.js +++ b/devtools/client/webide/content/addons.js @@ -3,41 +3,40 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {}); const Services = require("Services"); const {gDevTools} = require("devtools/client/framework/devtools"); const {GetAvailableAddons, ForgetAddonsList} = require("devtools/client/webide/modules/addons"); const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties"); -window.addEventListener("load", function () { - document.querySelector("#aboutaddons").onclick = function () { +window.addEventListener("load", function() { + document.querySelector("#aboutaddons").onclick = function() { let browserWin = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType); if (browserWin && browserWin.BrowserOpenAddonsMgr) { browserWin.BrowserOpenAddonsMgr("addons://list/extension"); } }; document.querySelector("#close").onclick = CloseUI; BuildUI(GetAvailableAddons()); }, {capture: true, once: true}); -window.addEventListener("unload", function () { +window.addEventListener("unload", function() { ForgetAddonsList(); }, {capture: true, once: true}); function CloseUI() { window.parent.UI.openProject(); } function BuildUI(addons) { BuildItem(addons.adb, "adb"); } function BuildItem(addon, type) { - function onAddonUpdate(arg) { progress.removeAttribute("value"); li.setAttribute("status", addon.status); status.textContent = Strings.GetStringFromName("addons_status_" + addon.status); } function onAddonFailure(arg) { window.parent.UI.reportError("error_operationFail", arg); @@ -50,17 +49,17 @@ function BuildItem(addon, type) { progress.value = arg; } } addon.on("update", onAddonUpdate); addon.on("failure", onAddonFailure); addon.on("progress", onAddonProgress); - window.addEventListener("unload", function () { + window.addEventListener("unload", function() { addon.off("update", onAddonUpdate); addon.off("failure", onAddonFailure); addon.off("progress", onAddonProgress); }, {once: true}); let li = document.createElement("li"); li.setAttribute("status", addon.status);
--- a/devtools/client/webide/content/details.js +++ b/devtools/client/webide/content/details.js @@ -1,23 +1,22 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {}); -const Services = require("Services"); const {AppManager} = require("devtools/client/webide/modules/app-manager"); -window.addEventListener("load", function () { +window.addEventListener("load", function() { document.addEventListener("visibilitychange", updateUI, true); AppManager.on("app-manager-update", onAppManagerUpdate); updateUI(); }, {capture: true, once: true}); -window.addEventListener("unload", function () { +window.addEventListener("unload", function() { AppManager.off("app-manager-update", onAppManagerUpdate); }, {capture: true, once: true}); function onAppManagerUpdate(what, details) { if (what == "project" || what == "project-validated") { updateUI(); } @@ -35,17 +34,16 @@ function resetUI() { document.querySelector("h1").textContent = ""; document.querySelector("#description").textContent = ""; document.querySelector("#type").textContent = ""; document.querySelector("#manifestURL").textContent = ""; document.querySelector("#location").textContent = ""; document.querySelector("#errorslist").innerHTML = ""; document.querySelector("#warningslist").innerHTML = ""; - } function updateUI() { resetUI(); let project = AppManager.selectedProject; if (!project) { return; @@ -113,11 +111,13 @@ function updateUI() { li.textContent = w; warningsNode.appendChild(li); } } AppManager.update("details"); } +// Used in details.xhtml. +/* exported removeProject */ function removeProject() { AppManager.removeSelectedProject(); }
--- a/devtools/client/webide/content/devicepreferences.js +++ b/devtools/client/webide/content/devicepreferences.js @@ -4,29 +4,29 @@ const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {}); const {AppManager} = require("devtools/client/webide/modules/app-manager"); const {Connection} = require("devtools/shared/client/connection-manager"); const ConfigView = require("devtools/client/webide/modules/config-view"); var configView = new ConfigView(window); -window.addEventListener("load", function () { +window.addEventListener("load", function() { AppManager.on("app-manager-update", OnAppManagerUpdate); document.getElementById("close").onclick = CloseUI; document.getElementById("device-fields").onchange = UpdateField; document.getElementById("device-fields").onclick = CheckReset; document.getElementById("search-bar").onkeyup = document.getElementById("search-bar").onclick = SearchField; document.getElementById("custom-value").onclick = UpdateNewField; document.getElementById("custom-value-type").onchange = ClearNewFields; document.getElementById("add-custom-field").onkeyup = CheckNewFieldSubmit; BuildUI(); }, {capture: true, once: true}); -window.addEventListener("unload", function () { +window.addEventListener("unload", function() { AppManager.off("app-manager-update", OnAppManagerUpdate); }, {once: true}); function CloseUI() { window.parent.UI.openProject(); } function OnAppManagerUpdate(what) { @@ -54,17 +54,19 @@ function CheckReset(event) { function UpdateField(event) { configView.updateField(event); } function SearchField(event) { configView.search(event); } -var getAllPrefs; // Used by tests +// Used by tests +/* exported getAllPrefs */ +var getAllPrefs; function BuildUI() { configView.resetTable(); if (AppManager.connection && AppManager.connection.status == Connection.Status.CONNECTED && AppManager.preferenceFront) { configView.front = AppManager.preferenceFront; configView.kind = "Pref";
--- a/devtools/client/webide/content/newapp.js +++ b/devtools/client/webide/content/newapp.js @@ -1,30 +1,29 @@ /* 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 {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {}); -const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm"); const Services = require("Services"); const {FileUtils} = require("resource://gre/modules/FileUtils.jsm"); const {AppProjects} = require("devtools/client/webide/modules/app-projects"); const {AppManager} = require("devtools/client/webide/modules/app-manager"); const {getJSON} = require("devtools/client/shared/getjson"); ChromeUtils.defineModuleGetter(this, "ZipUtils", "resource://gre/modules/ZipUtils.jsm"); ChromeUtils.defineModuleGetter(this, "Downloads", "resource://gre/modules/Downloads.jsm"); const TEMPLATES_URL = "devtools.webide.templatesURL"; var gTemplateList = null; -window.addEventListener("load", function () { +window.addEventListener("load", function() { let projectNameNode = document.querySelector("#project-name"); projectNameNode.addEventListener("input", canValidate, true); getTemplatesJSON(); }, {capture: true, once: true}); function getTemplatesJSON() { getJSON(TEMPLATES_URL).then(list => { if (!Array.isArray(list)) { @@ -62,18 +61,17 @@ function getTemplatesJSON() { doOK(); } }, (e) => { failAndBail("Can't download app templates: " + e); }); } function failAndBail(msg) { - let promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci.nsIPromptService); - promptService.alert(window, "error", msg); + Services.prompt.alert(window, "error", msg); window.close(); } function canValidate() { let projectNameNode = document.querySelector("#project-name"); let dialogNode = document.querySelector("dialog"); if (projectNameNode.value.length > 0) { dialogNode.removeAttribute("buttondisabledaccept"); @@ -149,17 +147,19 @@ function doOK() { target.remove(false); AppProjects.addPackaged(folder).then((project) => { window.arguments[0].location = project.location; AppManager.validateAndUpdateProject(project).then(() => { if (project.manifest) { project.manifest.name = projectName; AppManager.writeManifest(project).then(() => { AppManager.validateAndUpdateProject(project).then( - () => {window.close();}, bail); + () => { + window.close(); + }, bail); }, bail); } else { bail("Manifest not found"); } }, bail); }, bail); }, bail); }, bail);
--- a/devtools/client/webide/content/prefs.js +++ b/devtools/client/webide/content/prefs.js @@ -1,36 +1,35 @@ /* 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 {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm", {}); -window.addEventListener("load", function () { +window.addEventListener("load", function() { // Listen to preference changes let inputs = document.querySelectorAll("[data-pref]"); for (let i of inputs) { let pref = i.dataset.pref; Services.prefs.addObserver(pref, FillForm); i.addEventListener("change", SaveForm); } // Buttons document.querySelector("#close").onclick = CloseUI; document.querySelector("#restore").onclick = RestoreDefaults; document.querySelector("#manageComponents").onclick = ShowAddons; // Initialize the controls FillForm(); - }, {capture: true, once: true}); -window.addEventListener("unload", function () { +window.addEventListener("unload", function() { let inputs = document.querySelectorAll("[data-pref]"); for (let i of inputs) { let pref = i.dataset.pref; i.removeEventListener("change", SaveForm); Services.prefs.removeObserver(pref, FillForm); } }, {capture: true, once: true});
--- a/devtools/client/webide/content/project-listing.js +++ b/devtools/client/webide/content/project-listing.js @@ -4,26 +4,26 @@ /* eslint-env browser */ const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {}); const ProjectList = require("devtools/client/webide/modules/project-list"); var projectList = new ProjectList(window, window.parent); -window.addEventListener("load", function () { +window.addEventListener("load", function() { document.getElementById("new-app").onclick = CreateNewApp; document.getElementById("hosted-app").onclick = ImportHostedApp; document.getElementById("packaged-app").onclick = ImportPackagedApp; document.getElementById("refresh-tabs").onclick = RefreshTabs; projectList.update(); projectList.updateCommands(); }, {capture: true, once: true}); -window.addEventListener("unload", function () { +window.addEventListener("unload", function() { projectList.destroy(); }, {once: true}); function RefreshTabs() { projectList.refreshTabs(); } function CreateNewApp() {
--- a/devtools/client/webide/content/project-panel.js +++ b/devtools/client/webide/content/project-panel.js @@ -1,11 +1,12 @@ /* 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/. */ +/* exported ProjectPanel */ var ProjectPanel = { // TODO: Expand function to save toggle state. - toggleSidebar: function () { + toggleSidebar: function() { document.querySelector("#project-listing-panel").setAttribute("sidebar-displayed", true); document.querySelector("#project-listing-splitter").setAttribute("sidebar-displayed", true); } };
--- a/devtools/client/webide/content/runtime-listing.js +++ b/devtools/client/webide/content/runtime-listing.js @@ -2,30 +2,30 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {}); const RuntimeList = require("devtools/client/webide/modules/runtime-list"); var runtimeList = new RuntimeList(window, window.parent); -window.addEventListener("load", function () { +window.addEventListener("load", function() { document.getElementById("runtime-screenshot").onclick = TakeScreenshot; document.getElementById("runtime-details").onclick = ShowRuntimeDetails; document.getElementById("runtime-disconnect").onclick = DisconnectRuntime; document.getElementById("runtime-preferences").onclick = ShowDevicePreferences; document.getElementById("runtime-settings").onclick = ShowSettings; document.getElementById("runtime-panel-noadbhelper").onclick = ShowAddons; document.getElementById("runtime-panel-nousbdevice").onclick = ShowTroubleShooting; document.getElementById("refresh-devices").onclick = RefreshScanners; runtimeList.update(); runtimeList.updateCommands(); }, {capture: true, once: true}); -window.addEventListener("unload", function () { +window.addEventListener("unload", function() { runtimeList.destroy(); }, {once: true}); function TakeScreenshot() { runtimeList.takeScreenshot(); } function ShowRuntimeDetails() {
--- a/devtools/client/webide/content/runtime-panel.js +++ b/devtools/client/webide/content/runtime-panel.js @@ -1,11 +1,12 @@ /* 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/. */ +/* exported RuntimePanel */ var RuntimePanel = { // TODO: Expand function to save toggle state. - toggleSidebar: function () { + toggleSidebar: function() { document.querySelector("#runtime-listing-panel").setAttribute("sidebar-displayed", true); document.querySelector("#runtime-listing-splitter").setAttribute("sidebar-displayed", true); } };
--- a/devtools/client/webide/content/runtimedetails.js +++ b/devtools/client/webide/content/runtimedetails.js @@ -6,29 +6,29 @@ const {require} = ChromeUtils.import("re const Services = require("Services"); const {AppManager} = require("devtools/client/webide/modules/app-manager"); const {Connection} = require("devtools/shared/client/connection-manager"); const {RuntimeTypes} = require("devtools/client/webide/modules/runtimes"); const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties"); const UNRESTRICTED_HELP_URL = "https://developer.mozilla.org/docs/Tools/WebIDE/Running_and_debugging_apps#Unrestricted_app_debugging_%28including_certified_apps_main_process_etc.%29"; -window.addEventListener("load", function () { +window.addEventListener("load", function() { document.querySelector("#close").onclick = CloseUI; document.querySelector("#devtools-check button").onclick = EnableCertApps; document.querySelector("#adb-check button").onclick = RootADB; - document.querySelector("#unrestricted-privileges").onclick = function () { + document.querySelector("#unrestricted-privileges").onclick = function() { window.parent.UI.openInBrowser(UNRESTRICTED_HELP_URL); }; AppManager.on("app-manager-update", OnAppManagerUpdate); BuildUI(); CheckLockState(); }, {capture: true, once: true}); -window.addEventListener("unload", function () { +window.addEventListener("unload", function() { AppManager.off("app-manager-update", OnAppManagerUpdate); }, {once: true}); function CloseUI() { window.parent.UI.openProject(); } function OnAppManagerUpdate(what) { @@ -47,17 +47,19 @@ function generateFields(json) { tr.appendChild(td); td = document.createElement("td"); td.textContent = json[name]; tr.appendChild(td); table.appendChild(tr); } } -var getDescriptionPromise; // Used by tests +// Used by tests +/* exported getDescriptionPromise */ +var getDescriptionPromise; function BuildUI() { let table = document.querySelector("table"); table.innerHTML = ""; if (AppManager.connection && AppManager.connection.status == Connection.Status.CONNECTED && AppManager.deviceFront) { getDescriptionPromise = AppManager.deviceFront.getDescription() .then(json => generateFields(json)); @@ -65,17 +67,16 @@ function BuildUI() { CloseUI(); } } function CheckLockState() { let adbCheckResult = document.querySelector("#adb-check > .yesno"); let devtoolsCheckResult = document.querySelector("#devtools-check > .yesno"); let flipCertPerfButton = document.querySelector("#devtools-check button"); - let adbRootButton = document.querySelector("#adb-check button"); let flipCertPerfAction = document.querySelector("#devtools-check > .action"); let adbRootAction = document.querySelector("#adb-check > .action"); let sYes = Strings.GetStringFromName("runtimedetails_checkyes"); let sNo = Strings.GetStringFromName("runtimedetails_checkno"); let sUnknown = Strings.GetStringFromName("runtimedetails_checkunknown"); let sNotUSB = Strings.GetStringFromName("runtimedetails_notUSBDevice"); @@ -83,17 +84,16 @@ function CheckLockState() { flipCertPerfAction.setAttribute("hidden", "true"); adbRootAction.setAttribute("hidden", "true"); adbCheckResult.textContent = sUnknown; devtoolsCheckResult.textContent = sUnknown; if (AppManager.connection && AppManager.connection.status == Connection.Status.CONNECTED) { - // ADB check if (AppManager.selectedRuntime.type === RuntimeTypes.USB) { let device = AppManager.selectedRuntime.device; if (device && device.summonRoot) { device.isRoot().then(isRoot => { if (isRoot) { adbCheckResult.textContent = sYes; flipCertPerfButton.removeAttribute("disabled"); @@ -120,19 +120,17 @@ function CheckLockState() { devtoolsCheckResult.textContent = sYes; } }, console.error); } catch (e) { // Exception. pref actor is only accessible if forbird-certified-apps is false devtoolsCheckResult.textContent = sNo; flipCertPerfAction.removeAttribute("hidden"); } - } - } function EnableCertApps() { let device = AppManager.selectedRuntime.device; // TODO: Remove `network.disable.ipc.security` once bug 1125916 is fixed. device.shell( "stop b2g && " + "cd /data/b2g/mozilla/*.default/ && " +
--- a/devtools/client/webide/content/webide.js +++ b/devtools/client/webide/content/webide.js @@ -1,32 +1,34 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +// These files are loaded via webide.xul +/* import-globals-from project-panel.js */ +/* import-globals-from runtime-panel.js */ + const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {}); const {gDevTools} = require("devtools/client/framework/devtools"); const {gDevToolsBrowser} = require("devtools/client/framework/devtools-browser"); const {Toolbox} = require("devtools/client/framework/toolbox"); const Services = require("Services"); const {AppProjects} = require("devtools/client/webide/modules/app-projects"); const {Connection} = require("devtools/shared/client/connection-manager"); const {AppManager} = require("devtools/client/webide/modules/app-manager"); const EventEmitter = require("devtools/shared/event-emitter"); const promise = require("promise"); const {GetAvailableAddons} = require("devtools/client/webide/modules/addons"); const {getJSON} = require("devtools/client/shared/getjson"); -const utils = require("devtools/client/webide/modules/utils"); const Telemetry = require("devtools/client/shared/telemetry"); const {RuntimeScanners} = require("devtools/client/webide/modules/runtimes"); const {showDoorhanger} = require("devtools/client/shared/doorhanger"); const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties"); -const HTML = "http://www.w3.org/1999/xhtml"; const HELP_URL = "https://developer.mozilla.org/docs/Tools/WebIDE/Troubleshooting"; const MAX_ZOOM = 1.4; const MIN_ZOOM = 0.6; [["AppManager", AppManager], ["AppProjects", AppProjects], ["Connection", Connection]].forEach(([key, value]) => { @@ -36,26 +38,26 @@ const MIN_ZOOM = 0.6; writable: false }); }); // Download remote resources early getJSON("devtools.webide.templatesURL"); getJSON("devtools.devices.url"); -window.addEventListener("load", function () { +window.addEventListener("load", function() { UI.init(); }, {once: true}); -window.addEventListener("unload", function () { +window.addEventListener("unload", function() { UI.destroy(); }, {once: true}); var UI = { - init: function () { + init: function() { this._telemetry = new Telemetry(); this._telemetry.toolOpened("webide"); AppManager.init(); this.appManagerUpdate = this.appManagerUpdate.bind(this); AppManager.on("app-manager-update", this.appManagerUpdate); @@ -90,26 +92,26 @@ var UI = { .getInterface(Ci.nsIWebNavigation) .QueryInterface(Ci.nsIDocShell) .contentViewer; this.contentViewer.fullZoom = Services.prefs.getCharPref("devtools.webide.zoom"); gDevToolsBrowser.isWebIDEInitialized.resolve(); }, - destroy: function () { + destroy: function() { window.removeEventListener("focus", this.onfocus, true); AppManager.off("app-manager-update", this.appManagerUpdate); AppManager.destroy(); this.updateConnectionTelemetry(); this._telemetry.toolClosed("webide"); this._telemetry.destroy(); }, - onfocus: function () { + onfocus: function() { // Because we can't track the activity in the folder project, // we need to validate the project regularly. Let's assume that // if a modification happened, it happened when the window was // not focused. if (AppManager.selectedProject && AppManager.selectedProject.type != "mainProcess" && AppManager.selectedProject.type != "runtimeApp" && AppManager.selectedProject.type != "tab") { @@ -117,30 +119,30 @@ var UI = { } // Hook to display promotional Developer Edition doorhanger. Only displayed once. // Hooked into the `onfocus` event because sometimes does not work // when run at the end of `init`. ¯\(°_o)/¯ showDoorhanger({ window, type: "deveditionpromo", anchor: document.querySelector("#deck") }); }, - appManagerUpdate: function (what, details) { + appManagerUpdate: function(what, details) { // Got a message from app-manager.js // See AppManager.update() for descriptions of what these events mean. switch (what) { case "runtime-list": this.autoConnectRuntime(); break; case "connection": this.updateRuntimeButton(); this.updateCommands(); this.updateConnectionTelemetry(); break; case "project": - this._updatePromise = (async function () { + this._updatePromise = (async function() { UI.updateTitle(); await UI.destroyToolbox(); UI.updateCommands(); UI.openProject(); await UI.autoStartProject(); UI.autoOpenToolbox(); UI.saveLastSelectedProject(); UI.updateRemoveProjectButton(); @@ -173,83 +175,83 @@ var UI = { break; case "runtime-targets": this.autoSelectProject(); break; } this._updatePromise = promise.resolve(); }, - openInBrowser: function (url) { + openInBrowser: function(url) { // Open a URL in a Firefox window let mainWindow = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType); if (mainWindow) { mainWindow.openWebLinkIn(url, "tab"); - mainWindow.focus() + mainWindow.focus(); } else { window.open(url); } }, - updateTitle: function () { + updateTitle: function() { let project = AppManager.selectedProject; if (project) { window.document.title = Strings.formatStringFromName("title_app", [project.name], 1); } else { window.document.title = Strings.GetStringFromName("title_noApp"); } }, /** ******** BUSY UI **********/ _busyTimeout: null, _busyOperationDescription: null, _busyPromise: null, - busy: function () { + busy: function() { let win = document.querySelector("window"); win.classList.add("busy"); win.classList.add("busy-undetermined"); this.updateCommands(); this.update("busy"); }, - unbusy: function () { + unbusy: function() { let win = document.querySelector("window"); win.classList.remove("busy"); win.classList.remove("busy-determined"); win.classList.remove("busy-undetermined"); this.updateCommands(); this.update("unbusy"); this._busyPromise = null; }, - setupBusyTimeout: function () { + setupBusyTimeout: function() { this.cancelBusyTimeout(); this._busyTimeout = setTimeout(() => { this.unbusy(); UI.reportError("error_operationTimeout", this._busyOperationDescription); }, Services.prefs.getIntPref("devtools.webide.busyTimeout")); }, - cancelBusyTimeout: function () { + cancelBusyTimeout: function() { clearTimeout(this._busyTimeout); }, - busyWithProgressUntil: function (promise, operationDescription) { + busyWithProgressUntil: function(promise, operationDescription) { let busy = this.busyUntil(promise, operationDescription); let win = document.querySelector("window"); let progress = document.querySelector("#action-busy-determined"); progress.mode = "undetermined"; win.classList.add("busy-determined"); win.classList.remove("busy-undetermined"); return busy; }, - busyUntil: function (promise, operationDescription) { + busyUntil: function(promise, operationDescription) { // Freeze the UI until the promise is resolved. A timeout will unfreeze the // UI, just in case the promise never gets resolved. this._busyPromise = promise; this._busyOperationDescription = operationDescription; this.setupBusyTimeout(); this.busy(); promise.then(() => { this.cancelBusyTimeout(); @@ -271,62 +273,62 @@ var UI = { console.error(e); } } this.unbusy(); }); return promise; }, - reportError: function (l10nProperty, ...l10nArgs) { + reportError: function(l10nProperty, ...l10nArgs) { let text; if (l10nArgs.length > 0) { text = Strings.formatStringFromName(l10nProperty, l10nArgs, l10nArgs.length); } else { text = Strings.GetStringFromName(l10nProperty); } console.error(text); let buttons = [{ label: Strings.GetStringFromName("notification_showTroubleShooting_label"), accessKey: Strings.GetStringFromName("notification_showTroubleShooting_accesskey"), - callback: function () { + callback: function() { Cmds.showTroubleShooting(); } }]; let nbox = document.querySelector("#notificationbox"); nbox.removeAllNotifications(true); nbox.appendNotification(text, "webide:errornotification", null, nbox.PRIORITY_WARNING_LOW, buttons); }, - dismissErrorNotification: function () { + dismissErrorNotification: function() { let nbox = document.querySelector("#notificationbox"); nbox.removeAllNotifications(true); }, /** ******** COMMANDS **********/ /** * This module emits various events when state changes occur. * * The events this module may emit include: * busy: * The window is currently busy and certain UI functions may be disabled. * unbusy: * The window is not busy and certain UI functions may be re-enabled. */ - update: function (what, details) { + update: function(what, details) { this.emit("webide-update", what, details); }, - updateCommands: function () { + updateCommands: function() { // Action commands let playCmd = document.querySelector("#cmd_play"); let stopCmd = document.querySelector("#cmd_stop"); let debugCmd = document.querySelector("#cmd_toggleToolbox"); let playButton = document.querySelector("#action-button-play"); let projectPanelCmd = document.querySelector("#cmd_showProjectPanel"); if (document.querySelector("window").classList.contains("busy")) { @@ -357,23 +359,21 @@ var UI = { if (AppManager.selectedProject.type == "runtimeApp") { playCmd.removeAttribute("disabled"); } else if (AppManager.selectedProject.type == "tab") { playCmd.removeAttribute("disabled"); stopCmd.setAttribute("disabled", "true"); } else if (AppManager.selectedProject.type == "mainProcess") { playCmd.setAttribute("disabled", "true"); stopCmd.setAttribute("disabled", "true"); - } else { - if (AppManager.selectedProject.errorsCount == 0 && + } else if (AppManager.selectedProject.errorsCount == 0 && AppManager.runtimeCanHandleApps()) { - playCmd.removeAttribute("disabled"); - } else { - playCmd.setAttribute("disabled", "true"); - } + playCmd.removeAttribute("disabled"); + } else { + playCmd.setAttribute("disabled", "true"); } } // Runtime commands let screenshotCmd = document.querySelector("#cmd_takeScreenshot"); let detailsCmd = document.querySelector("#cmd_showRuntimeDetails"); let disconnectCmd = document.querySelector("#cmd_disconnectRuntime"); let devicePrefsCmd = document.querySelector("#cmd_showDevicePrefs"); @@ -404,17 +404,17 @@ var UI = { } else { runtimePanelButton.removeAttribute("active"); runtimePanelButton.setAttribute("hidden", "true"); } projectPanelCmd.removeAttribute("disabled"); }, - updateRemoveProjectButton: function () { + updateRemoveProjectButton: function() { // Remove command let removeCmdNode = document.querySelector("#cmd_removeProject"); if (AppManager.selectedProject) { removeCmdNode.removeAttribute("disabled"); } else { removeCmdNode.setAttribute("disabled", "true"); } }, @@ -424,25 +424,25 @@ var UI = { get lastConnectedRuntime() { return Services.prefs.getCharPref("devtools.webide.lastConnectedRuntime"); }, set lastConnectedRuntime(runtime) { Services.prefs.setCharPref("devtools.webide.lastConnectedRuntime", runtime); }, - autoConnectRuntime: function () { + autoConnectRuntime: function() { // Automatically reconnect to the previously selected runtime, // if available and has an ID and feature is enabled if (AppManager.selectedRuntime || !Services.prefs.getBoolPref("devtools.webide.autoConnectRuntime") || !this.lastConnectedRuntime) { return; } - let [_, type, id] = this.lastConnectedRuntime.match(/^(\w+):(.+)$/); + let [ , type, id] = this.lastConnectedRuntime.match(/^(\w+):(.+)$/); type = type.toLowerCase(); // Local connection is mapped to AppManager.runtimeList.other array if (type == "local") { type = "other"; } @@ -456,17 +456,17 @@ var UI = { // Only want one auto-connect attempt, so clear last runtime value this.lastConnectedRuntime = ""; this.connectToRuntime(runtime); } } } }, - connectToRuntime: function (runtime) { + connectToRuntime: function(runtime) { let name = runtime.name; let promise = AppManager.connectToRuntime(runtime); promise.then(() => this.initConnectionTelemetry()) .catch(() => { // Empty rejection handler to silence uncaught rejection warnings // |busyUntil| will listen for rejections. // Bug 1121100 may find a better way to silence these. }); @@ -474,27 +474,27 @@ var UI = { // Stop busy timeout for runtimes that take unknown or long amounts of time // to connect. if (runtime.prolongedConnection) { this.cancelBusyTimeout(); } return promise; }, - updateRuntimeButton: function () { + updateRuntimeButton: function() { let labelNode = document.querySelector("#runtime-panel-button > .panel-button-label"); if (!AppManager.selectedRuntime) { labelNode.setAttribute("value", Strings.GetStringFromName("runtimeButton_label")); } else { let name = AppManager.selectedRuntime.name; labelNode.setAttribute("value", name); } }, - saveLastConnectedRuntime: function () { + saveLastConnectedRuntime: function() { if (AppManager.selectedRuntime && AppManager.selectedRuntime.id !== undefined) { this.lastConnectedRuntime = AppManager.selectedRuntime.type + ":" + AppManager.selectedRuntime.id; } else { this.lastConnectedRuntime = ""; } }, @@ -503,53 +503,53 @@ var UI = { _actionsToLog: new Set(), /** * For each new connection, track whether play and debug were ever used. Only * one value is collected for each button, even if they are used multiple * times during a connection. */ - initConnectionTelemetry: function () { + initConnectionTelemetry: function() { this._actionsToLog.add("play"); this._actionsToLog.add("debug"); }, /** * Action occurred. Log that it happened, and remove it from the loggable * set. */ - onAction: function (action) { + onAction: function(action) { if (!this._actionsToLog.has(action)) { return; } this.logActionState(action, true); this._actionsToLog.delete(action); }, /** * Connection status changed or we are shutting down. Record any loggable * actions as having not occurred. */ - updateConnectionTelemetry: function () { + updateConnectionTelemetry: function() { for (let action of this._actionsToLog.values()) { this.logActionState(action, false); } this._actionsToLog.clear(); }, - logActionState: function (action, state) { + logActionState: function(action, state) { let histogramId = "DEVTOOLS_WEBIDE_CONNECTION_" + action.toUpperCase() + "_USED"; this._telemetry.log(histogramId, state); }, /** ******** PROJECTS **********/ - openProject: function () { + openProject: function() { let project = AppManager.selectedProject; if (!project) { this.resetDeck(); return; } this.selectDeckPanel("details"); @@ -606,17 +606,17 @@ var UI = { // Select project AppManager.selectedProject = project; this._telemetry.actionOccurred("webideImportProject"); }, // Remember the last selected project on the runtime - saveLastSelectedProject: function () { + saveLastSelectedProject: function() { let shouldRestore = Services.prefs.getBoolPref("devtools.webide.restoreLastProject"); if (!shouldRestore) { return; } // Ignore unselection of project on runtime disconnection if (!AppManager.connected) { return; @@ -639,33 +639,33 @@ var UI = { if (type) { Services.prefs.setCharPref("devtools.webide.lastSelectedProject", type + ":" + project); } else { Services.prefs.clearUserPref("devtools.webide.lastSelectedProject"); } }, - autoSelectProject: function () { + autoSelectProject: function() { if (AppManager.selectedProject) { return; } let shouldRestore = Services.prefs.getBoolPref("devtools.webide.restoreLastProject"); if (!shouldRestore) { return; } let pref = Services.prefs.getCharPref("devtools.webide.lastSelectedProject"); if (!pref) { return; } let m = pref.match(/^(\w+):(.*)$/); if (!m) { return; } - let [_, type, project] = m; + let [ , type, project] = m; if (type == "local") { let lastProject = AppProjects.get(project); if (lastProject) { AppManager.selectedProject = lastProject; } } @@ -690,44 +690,44 @@ var UI = { name: app.manifest.name }; } } }, /** ******** DECK **********/ - setupDeck: function () { + setupDeck: function() { let iframes = document.querySelectorAll("#deck > iframe"); for (let iframe of iframes) { iframe.tooltip = "aHTMLTooltip"; } }, - resetFocus: function () { + resetFocus: function() { document.commandDispatcher.focusedElement = document.documentElement; }, - selectDeckPanel: function (id) { + selectDeckPanel: function(id) { let deck = document.querySelector("#deck"); if (deck.selectedPanel && deck.selectedPanel.id === "deck-panel-" + id) { // This panel is already displayed. return; } this.resetFocus(); let panel = deck.querySelector("#deck-panel-" + id); let lazysrc = panel.getAttribute("lazysrc"); if (lazysrc) { panel.removeAttribute("lazysrc"); panel.setAttribute("src", lazysrc); } deck.selectedPanel = panel; }, - resetDeck: function () { + resetDeck: function() { this.resetFocus(); let deck = document.querySelector("#deck"); deck.selectedPanel = null; }, async checkRuntimeVersion() { if (AppManager.connected) { let { client } = AppManager.connection; @@ -751,17 +751,17 @@ var UI = { * * Toggle toolbox wrench in WebIDE * * Disconnect the current runtime gracefully * * Yank cord out of device * * Close or crash the app/tab * We can't know for sure which one was used here, so reset the * |toolboxPromise| since someone must be destroying it to reach here, * and call our own close method. */ - _onToolboxClosed: function (promise, iframe) { + _onToolboxClosed: function(promise, iframe) { // Only save toolbox size, disable wrench button, workaround focus issue... // if we are closing the last toolbox: // - toolboxPromise is nullified by destroyToolbox and is still null here // if no other toolbox has been opened in between, // - having two distinct promise means we are receiving closed event // for a previous, non-current, toolbox. if (!this.toolboxPromise || this.toolboxPromise === promise) { this.toolboxPromise = null; @@ -772,27 +772,27 @@ var UI = { splitter.setAttribute("hidden", "true"); document.querySelector("#action-button-debug").removeAttribute("active"); } // We have to destroy the iframe, otherwise, the keybindings of webide don't work // properly anymore. iframe.remove(); }, - destroyToolbox: function () { + destroyToolbox: function() { // Only have a live toolbox if |this.toolboxPromise| exists if (this.toolboxPromise) { let toolboxPromise = this.toolboxPromise; this.toolboxPromise = null; return toolboxPromise.then(toolbox => toolbox.destroy()); } return promise.resolve(); }, - createToolbox: function () { + createToolbox: function() { // If |this.toolboxPromise| exists, there is already a live toolbox if (this.toolboxPromise) { return this.toolboxPromise; } let iframe = document.createElement("iframe"); iframe.id = "toolbox"; @@ -810,70 +810,70 @@ var UI = { // toolbox.destroy's promise resolves. toolbox.once("destroyed", this._onToolboxClosed.bind(this, promise, iframe)); return toolbox; }, console.error); return this.busyUntil(this.toolboxPromise, "opening toolbox"); }, - _showToolbox: function (target, iframe) { + _showToolbox: function(target, iframe) { let splitter = document.querySelector(".devtools-horizontal-splitter"); splitter.removeAttribute("hidden"); document.querySelector("notificationbox").insertBefore(iframe, splitter.nextSibling); let host = Toolbox.HostType.CUSTOM; let options = { customIframe: iframe, zoom: false, uid: iframe.uid }; document.querySelector("#action-button-debug").setAttribute("active", "true"); return gDevTools.showToolbox(target, null, host, options); }, }; EventEmitter.decorate(UI); var Cmds = { - quit: function () { + quit: function() { window.close(); }, - showProjectPanel: function () { + showProjectPanel: function() { ProjectPanel.toggleSidebar(); return promise.resolve(); }, - showRuntimePanel: function () { + showRuntimePanel: function() { RuntimeScanners.scan(); RuntimePanel.toggleSidebar(); }, - disconnectRuntime: function () { - let disconnecting = (async function () { + disconnectRuntime: function() { + let disconnecting = (async function() { await UI.destroyToolbox(); await AppManager.disconnectRuntime(); })(); return UI.busyUntil(disconnecting, "disconnecting from runtime"); }, - takeScreenshot: function () { + takeScreenshot: function() { let url = AppManager.deviceFront.screenshotToDataURL(); return UI.busyUntil(url.then(longstr => { return longstr.string().then(dataURL => { longstr.release().catch(console.error); UI.openInBrowser(dataURL); }); }), "taking screenshot"); }, - showRuntimeDetails: function () { + showRuntimeDetails: function() { UI.selectDeckPanel("runtimedetails"); }, - showDevicePrefs: function () { + showDevicePrefs: function() { UI.selectDeckPanel("devicepreferences"); }, async play() { let busy; switch (AppManager.selectedProject.type) { case "packaged": busy = UI.busyWithProgressUntil(AppManager.installAndRunProject(), @@ -892,57 +892,56 @@ var Cmds = { } if (!busy) { return promise.reject(); } UI.onAction("play"); return busy; }, - stop: function () { + stop: function() { return UI.busyUntil(AppManager.stopRunningApp(), "stopping app"); }, - toggleToolbox: function () { + toggleToolbox: function() { UI.onAction("debug"); if (UI.toolboxPromise) { UI.destroyToolbox(); return promise.resolve(); - } else { - return UI.createToolbox(); } + return UI.createToolbox(); }, - removeProject: function () { + removeProject: function() { AppManager.removeSelectedProject(); }, - showTroubleShooting: function () { + showTroubleShooting: function() { UI.openInBrowser(HELP_URL); }, - showAddons: function () { + showAddons: function() { UI.selectDeckPanel("addons"); }, - showPrefs: function () { + showPrefs: function() { UI.selectDeckPanel("prefs"); }, - zoomIn: function () { + zoomIn: function() { if (UI.contentViewer.fullZoom < MAX_ZOOM) { UI.contentViewer.fullZoom += 0.1; Services.prefs.setCharPref("devtools.webide.zoom", UI.contentViewer.fullZoom); } }, - zoomOut: function () { + zoomOut: function() { if (UI.contentViewer.fullZoom > MIN_ZOOM) { UI.contentViewer.fullZoom -= 0.1; Services.prefs.setCharPref("devtools.webide.zoom", UI.contentViewer.fullZoom); } }, - resetZoom: function () { + resetZoom: function() { UI.contentViewer.fullZoom = 1; Services.prefs.setCharPref("devtools.webide.zoom", 1); } };
--- a/devtools/client/webide/content/wifi-auth.js +++ b/devtools/client/webide/content/wifi-auth.js @@ -1,20 +1,19 @@ /* 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 { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {}); -const Services = require("Services"); const QR = require("devtools/shared/qrcode/index"); -window.addEventListener("load", function () { +window.addEventListener("load", function() { document.getElementById("close").onclick = () => window.close(); document.getElementById("no-scanner").onclick = showToken; document.getElementById("yes-scanner").onclick = hideToken; buildUI(); }, {once: true}); function buildUI() { let { oob } = window.arguments[0];
--- a/devtools/client/webide/modules/addons.js +++ b/devtools/client/webide/modules/addons.js @@ -1,17 +1,16 @@ /* 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 {AddonManager} = require("resource://gre/modules/AddonManager.jsm"); const Services = require("Services"); -const {getJSON} = require("devtools/client/shared/getjson"); const EventEmitter = require("devtools/shared/event-emitter"); var ADB_LINK = Services.prefs.getCharPref("devtools.webide.adbAddonURL"); var ADB_ADDON_ID = Services.prefs.getCharPref("devtools.webide.adbAddonID"); var platform = Services.appShell.hiddenDOMWindow.navigator.platform; var OS = ""; if (platform.includes("Win")) { @@ -32,53 +31,53 @@ addonsListener.onDisabled = addonsListener.onInstalled = addonsListener.onUninstalled = (updatedAddon) => { let addons = GetAvailableAddons(); addons.adb.updateInstallStatus(); }; AddonManager.addAddonListener(addonsListener); var AvailableAddons = null; -var GetAvailableAddons = exports.GetAvailableAddons = function () { +var GetAvailableAddons = exports.GetAvailableAddons = function() { if (!AvailableAddons) { AvailableAddons = { adb: new ADBAddon() }; } return AvailableAddons; }; -exports.ForgetAddonsList = function () { +exports.ForgetAddonsList = function() { AvailableAddons = null; }; function Addon() {} Addon.prototype = { _status: "unknown", set status(value) { if (this._status != value) { this._status = value; this.emit("update"); } }, get status() { return this._status; }, - updateInstallStatus: function () { + updateInstallStatus: function() { AddonManager.getAddonByID(this.addonID, (addon) => { if (addon && !addon.userDisabled) { this.status = "installed"; } else { this.status = "uninstalled"; } }); }, - install: function () { + install: function() { AddonManager.getAddonByID(this.addonID, (addon) => { if (addon && !addon.userDisabled) { this.status = "installed"; return; } this.status = "preparing"; if (addon && addon.userDisabled) { addon.userDisabled = false; @@ -86,57 +85,57 @@ Addon.prototype = { AddonManager.getInstallForURL(this.xpiLink, (install) => { install.addListener(this); install.install(); }, "application/x-xpinstall"); } }); }, - uninstall: function () { + uninstall: function() { AddonManager.getAddonByID(this.addonID, (addon) => { addon.uninstall(); }); }, - installFailureHandler: function (install, message) { + installFailureHandler: function(install, message) { this.status = "uninstalled"; this.emit("failure", message); }, - onDownloadStarted: function () { + onDownloadStarted: function() { this.status = "downloading"; }, - onInstallStarted: function () { + onInstallStarted: function() { this.status = "installing"; }, - onDownloadProgress: function (install) { + onDownloadProgress: function(install) { if (install.maxProgress == -1) { this.emit("progress", -1); } else { this.emit("progress", install.progress / install.maxProgress); } }, - onInstallEnded: function ({addon}) { + onInstallEnded: function({addon}) { addon.userDisabled = false; }, - onDownloadCancelled: function (install) { + onDownloadCancelled: function(install) { this.installFailureHandler(install, "Download cancelled"); }, - onDownloadFailed: function (install) { + onDownloadFailed: function(install) { this.installFailureHandler(install, "Download failed"); }, - onInstallCancelled: function (install) { + onInstallCancelled: function(install) { this.installFailureHandler(install, "Install cancelled"); }, - onInstallFailed: function (install) { + onInstallFailed: function(install) { this.installFailureHandler(install, "Install failed"); }, }; function ADBAddon() { EventEmitter.decorate(this); // This addon uses the string "linux" for "linux32" let fixedOS = OS == "linux32" ? "linux" : OS;
--- a/devtools/client/webide/modules/app-manager.js +++ b/devtools/client/webide/modules/app-manager.js @@ -1,14 +1,12 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -const {Cu} = require("chrome"); - const {TargetFactory} = require("devtools/client/framework/target"); const Services = require("Services"); const {FileUtils} = require("resource://gre/modules/FileUtils.jsm"); const EventEmitter = require("devtools/shared/event-emitter"); const {OS} = require("resource://gre/modules/osfile.jsm"); const {AppProjects} = require("devtools/client/webide/modules/app-projects"); const TabStore = require("devtools/client/webide/modules/tab-store"); const {AppValidator} = require("devtools/client/webide/modules/app-validator"); @@ -23,17 +21,17 @@ const Strings = Services.strings.createB var AppManager = exports.AppManager = { DEFAULT_PROJECT_ICON: "chrome://webide/skin/default-app-icon.png", DEFAULT_PROJECT_NAME: "--", _initialized: false, - init: function () { + init: function() { if (this._initialized) { return; } this._initialized = true; let port = Services.prefs.getIntPref("devtools.debugger.remote-port"); this.connection = ConnectionManager.createConnection("localhost", port); this.onConnectionChanged = this.onConnectionChanged.bind(this); @@ -51,17 +49,17 @@ var AppManager = exports.AppManager = { this._rebuildRuntimeList = this._rebuildRuntimeList.bind(this); RuntimeScanners.on("runtime-list-updated", this._rebuildRuntimeList); RuntimeScanners.enable(); this._rebuildRuntimeList(); this._telemetry = new Telemetry(); }, - destroy: function () { + destroy: function() { if (!this._initialized) { return; } this._initialized = false; this.selectedProject = null; this.selectedRuntime = null; RuntimeScanners.off("runtime-list-updated", this._rebuildRuntimeList); @@ -117,37 +115,37 @@ var AppManager = exports.AppManager = { * Detailed runtime telemetry has been recorded. Used by tests. * runtime-targets: * The list of remote runtime targets available from the currently * connected runtime (such as tabs or apps) has changed, or any of the * user-visible details (like names) for the non-selected runtime targets * has changed. This event includes |type| in the details, to distinguish * "apps" and "tabs". */ - update: function (what, details) { + update: function(what, details) { // Anything we want to forward to the UI this.emit("app-manager-update", what, details); }, - reportError: function (l10nProperty, ...l10nArgs) { + reportError: function(l10nProperty, ...l10nArgs) { let win = Services.wm.getMostRecentWindow("devtools:webide"); if (win) { win.UI.reportError(l10nProperty, ...l10nArgs); } else { let text; if (l10nArgs.length > 0) { text = Strings.formatStringFromName(l10nProperty, l10nArgs, l10nArgs.length); } else { text = Strings.GetStringFromName(l10nProperty); } console.error(text); } }, - onConnectionChanged: function () { + onConnectionChanged: function() { console.log("Connection status changed: " + this.connection.status); if (this.connection.status == Connection.Status.DISCONNECTED) { this.selectedRuntime = null; } if (!this.connected) { this._listTabsResponse = null; @@ -165,51 +163,50 @@ var AppManager = exports.AppManager = { get connected() { return this.connection && this.connection.status == Connection.Status.CONNECTED; }, get apps() { if (this._appsFront) { return this._appsFront.apps; - } else { - return new Map(); } + return new Map(); }, - isProjectRunning: function () { + isProjectRunning: function() { if (this.selectedProject.type == "mainProcess" || this.selectedProject.type == "tab") { return true; } let app = this._getProjectFront(this.selectedProject); return app && app.running; }, - checkIfProjectIsRunning: function () { + checkIfProjectIsRunning: function() { if (this.selectedProject) { if (this.isProjectRunning()) { this.update("project-started"); } else { this.update("project-stopped"); } } }, - listTabs: function () { + listTabs: function() { return this.tabStore.listTabs(); }, - onTabList: function () { + onTabList: function() { this.update("runtime-targets", { type: "tabs" }); }, // TODO: Merge this into TabProject as part of project-agnostic work - onTabNavigate: function () { + onTabNavigate: function() { this.update("runtime-targets", { type: "tabs" }); if (this.selectedProject.type !== "tab") { return; } let tab = this.selectedProject.app = this.tabStore.selectedTab; let uri = NetUtil.newURI(tab.url); // Wanted to use nsIFaviconService here, but it only works for visited // tabs, so that's no help for any remote tabs. Maybe some favicon wizard @@ -221,65 +218,64 @@ var AppManager = exports.AppManager = { tab.name = uri.host + ": " + tab.name; } this.selectedProject.location = tab.url; this.selectedProject.name = tab.name; this.selectedProject.icon = tab.favicon; this.update("project-validated"); }, - onTabClosed: function () { + onTabClosed: function() { if (this.selectedProject.type !== "tab") { return; } this.selectedProject = null; }, - reloadTab: function () { + reloadTab: function() { if (this.selectedProject && this.selectedProject.type != "tab") { return Promise.reject("tried to reload non-tab project"); } return this.getTarget().then(target => { target.activeTab.reload(); }, console.error); }, - getTarget: function () { + getTarget: function() { if (this.selectedProject.type == "mainProcess") { // Fx >=39 exposes a ChromeActor to debug the main process if (this.connection.client.mainRoot.traits.allowChromeProcess) { return this.connection.client.getProcess() .then(aResponse => { return TargetFactory.forRemoteTab({ form: aResponse.form, client: this.connection.client, chrome: true }); }); - } else { + } // Fx <39 exposes tab actors on the root actor - return TargetFactory.forRemoteTab({ + return TargetFactory.forRemoteTab({ form: this._listTabsResponse, client: this.connection.client, chrome: true, isTabActor: false - }); - } + }); } if (this.selectedProject.type == "tab") { return this.tabStore.getTargetForTab(); } let app = this._getProjectFront(this.selectedProject); if (!app) { return Promise.reject("Can't find app front for selected project"); } - return (async function () { + return (async function() { // Once we asked the app to launch, the app isn't necessary completely loaded. // launch request only ask the app to launch and immediatly returns. // We have to keep trying to get app tab actors required to create its target. for (let i = 0; i < 10; i++) { try { return await app.getTarget(); } catch (e) {} @@ -288,34 +284,34 @@ var AppManager = exports.AppManager = { }); } AppManager.reportError("error_cantConnectToApp", app.manifest.manifestURL); throw new Error("can't connect to app"); })(); }, - getProjectManifestURL: function (project) { + getProjectManifestURL: function(project) { let manifest = null; if (project.type == "runtimeApp") { manifest = project.app.manifestURL; } if (project.type == "hosted") { manifest = project.location; } if (project.type == "packaged" && project.packagedAppOrigin) { manifest = "app://" + project.packagedAppOrigin + "/manifest.webapp"; } return manifest; }, - _getProjectFront: function (project) { + _getProjectFront: function(project) { let manifest = this.getProjectManifestURL(project); if (manifest && this._appsFront) { return this._appsFront.apps.get(manifest); } return null; }, _selectedProject: null, @@ -341,17 +337,19 @@ var AppManager = exports.AppManager = { } else if (type === "mainProcess") { return; } else { throw new Error("Unsupported project type: " + type); } } let cancelled = false; - this.update("before-project", { cancel: () => { cancelled = true; } }); + this.update("before-project", { cancel: () => { + cancelled = true; + } }); if (cancelled) { return; } this._selectedProject = project; // Clear out tab store's selected state, if any this.tabStore.selectedTab = null; @@ -396,18 +394,17 @@ var AppManager = exports.AppManager = { } this.update("runtime"); }, get selectedRuntime() { return this._selectedRuntime; }, - connectToRuntime: function (runtime) { - + connectToRuntime: function(runtime) { if (this.connected && this.selectedRuntime === runtime) { // Already connected return Promise.resolve(); } let deferred = new Promise((resolve, reject) => { this.disconnectRuntime().then(() => { this.selectedRuntime = runtime; @@ -484,17 +481,17 @@ var AppManager = exports.AppManager = { d.platformversion, true); this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_APP_TYPE", d.apptype, true); this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_VERSION", d.version, true); this.update("runtime-telemetry"); }, - isMainProcessDebuggable: function () { + isMainProcessDebuggable: function() { // Fx <39 exposes chrome tab actors on RootActor // Fx >=39 exposes a dedicated actor via getProcess request return this.connection.client && this.connection.client.mainRoot && this.connection.client.mainRoot.traits.allowChromeProcess || (this._listTabsResponse && this._listTabsResponse.consoleActor); }, @@ -512,52 +509,51 @@ var AppManager = exports.AppManager = { get preferenceFront() { if (!this._listTabsResponse) { return null; } return getPreferenceFront(this.connection.client, this._listTabsResponse); }, - disconnectRuntime: function () { + disconnectRuntime: function() { if (!this.connected) { return Promise.resolve(); } return new Promise(resolve => { this.connection.once(Connection.Events.DISCONNECTED, () => resolve()); this.connection.disconnect(); }); }, - launchRuntimeApp: function () { + launchRuntimeApp: function() { if (this.selectedProject && this.selectedProject.type != "runtimeApp") { return Promise.reject("attempting to launch a non-runtime app"); } let app = this._getProjectFront(this.selectedProject); return app.launch(); }, - launchOrReloadRuntimeApp: function () { + launchOrReloadRuntimeApp: function() { if (this.selectedProject && this.selectedProject.type != "runtimeApp") { return Promise.reject("attempting to launch / reload a non-runtime app"); } let app = this._getProjectFront(this.selectedProject); if (!app.running) { return app.launch(); - } else { - return app.reload(); } + return app.reload(); }, - runtimeCanHandleApps: function () { + runtimeCanHandleApps: function() { return !!this._appsFront; }, - installAndRunProject: function () { + installAndRunProject: function() { let project = this.selectedProject; if (!project || (project.type != "packaged" && project.type != "hosted")) { console.error("Can't install project. Unknown type of project."); return Promise.reject("Can't install"); } if (!this._listTabsResponse) { @@ -565,29 +561,27 @@ var AppManager = exports.AppManager = { return Promise.reject("Can't install"); } if (!this._appsFront) { console.error("Runtime doesn't have a webappsActor"); return Promise.reject("Can't install"); } - return (async function () { + return (async function() { let self = AppManager; // Validate project await self.validateAndUpdateProject(project); if (project.errorsCount > 0) { self.reportError("error_cantInstallValidationErrors"); return; } - let installPromise; - if (project.type != "packaged" && project.type != "hosted") { return Promise.reject("Don't know how to install project"); } let response; if (project.type == "packaged") { let packageDir = project.location; console.log("Installing app from " + packageDir); @@ -634,30 +628,29 @@ var AppManager = exports.AppManager = { await app.launch(); await deferred; } else { await app.reload(); } })(); }, - stopRunningApp: function () { + stopRunningApp: function() { let app = this._getProjectFront(this.selectedProject); return app.close(); }, /* PROJECT VALIDATION */ - validateAndUpdateProject: function (project) { + validateAndUpdateProject: function(project) { if (!project) { return Promise.reject(); } - return (async function () { - + return (async function() { let packageDir = project.location; let validation = new AppValidator({ type: project.type, // Build process may place the manifest in a non-root directory location: packageDir }); await validation.validate(); @@ -668,26 +661,24 @@ var AppManager = exports.AppManager = { if (manifest.icons) { let size = Object.keys(manifest.icons).sort((a, b) => b - a)[0]; if (size) { iconPath = manifest.icons[size]; } } if (!iconPath) { project.icon = AppManager.DEFAULT_PROJECT_ICON; - } else { - if (project.type == "hosted") { - let manifestURL = Services.io.newURI(project.location); - let origin = Services.io.newURI(manifestURL.prePath); - project.icon = Services.io.newURI(iconPath, null, origin).spec; - } else if (project.type == "packaged") { - let projectFolder = FileUtils.File(packageDir); - let folderURI = Services.io.newFileURI(projectFolder).spec; - project.icon = folderURI + iconPath.replace(/^\/|\\/, ""); - } + } else if (project.type == "hosted") { + let manifestURL = Services.io.newURI(project.location); + let origin = Services.io.newURI(manifestURL.prePath); + project.icon = Services.io.newURI(iconPath, null, origin).spec; + } else if (project.type == "packaged") { + let projectFolder = FileUtils.File(packageDir); + let folderURI = Services.io.newFileURI(projectFolder).spec; + project.icon = folderURI + iconPath.replace(/^\/|\\/, ""); } project.manifest = validation.manifest; if ("name" in project.manifest) { project.name = project.manifest.name; } else { project.name = AppManager.DEFAULT_PROJECT_NAME; } @@ -730,25 +721,25 @@ var AppManager = exports.AppManager = { if (AppManager.selectedProject === project) { AppManager.update("project-validated"); } })(); }, /* RUNTIME LIST */ - _clearRuntimeList: function () { + _clearRuntimeList: function() { this.runtimeList = { usb: [], wifi: [], other: [] }; }, - _rebuildRuntimeList: function () { + _rebuildRuntimeList: function() { let runtimes = RuntimeScanners.listRuntimes(); this._clearRuntimeList(); // Reorganize runtimes by type for (let runtime of runtimes) { switch (runtime.type) { case RuntimeTypes.USB: this.runtimeList.usb.push(runtime); @@ -762,17 +753,17 @@ var AppManager = exports.AppManager = { } this.update("runtime-details"); this.update("runtime-list"); }, /* MANIFEST UTILS */ - writeManifest: function (project) { + writeManifest: function(project) { if (project.type != "packaged") { return Promise.reject("Not a packaged app"); } if (!project.manifest) { project.manifest = {}; }
--- a/devtools/client/webide/modules/app-projects.js +++ b/devtools/client/webide/modules/app-projects.js @@ -1,58 +1,56 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -const {Cc, Ci, Cu, Cr} = require("chrome"); +const {Cc, Ci, Cr} = require("chrome"); const EventEmitter = require("devtools/shared/event-emitter"); const {generateUUID} = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); const {FileUtils} = require("resource://gre/modules/FileUtils.jsm"); /** * IndexedDB wrapper that just save project objects * * The only constraint is that project objects have to have * a unique `location` object. */ const IDB = { _db: null, databaseName: "AppProjects", - open: function () { + open: function() { return new Promise((resolve, reject) => { let request = indexedDB.open(IDB.databaseName, 5); - request.onerror = function (event) { + request.onerror = function(event) { reject("Unable to open AppProjects indexedDB: " + this.error.name + " - " + this.error.message); }; - request.onupgradeneeded = function (event) { + request.onupgradeneeded = function(event) { let db = event.target.result; db.createObjectStore("projects", { keyPath: "location" }); }; - request.onsuccess = function () { + request.onsuccess = function() { let db = IDB._db = request.result; let objectStore = db.transaction("projects").objectStore("projects"); let projects = []; let toRemove = []; - objectStore.openCursor().onsuccess = function (event) { + objectStore.openCursor().onsuccess = function(event) { let cursor = event.target.result; if (cursor) { if (cursor.value.location) { - // We need to make sure this object has a `.location` property. // The UI depends on this property. // This should not be needed as we make sure to register valid // projects, but in the past (before bug 924568), we might have // registered invalid objects. - // We also want to make sure the location is valid. // If the location doesn't exist, we remove the project. try { let file = FileUtils.File(cursor.value.location); if (file.exists()) { projects.push(cursor.value); } else { @@ -75,78 +73,78 @@ const IDB = { resolve(projects); }); } }; }; }); }, - add: function (project) { + add: function(project) { return new Promise((resolve, reject) => { if (!project.location) { // We need to make sure this object has a `.location` property. reject("Missing location property on project object."); } else { let transaction = IDB._db.transaction(["projects"], "readwrite"); let objectStore = transaction.objectStore("projects"); let request = objectStore.add(project); - request.onerror = function (event) { + request.onerror = function(event) { reject("Unable to add project to the AppProjects indexedDB: " + this.error.name + " - " + this.error.message); }; - request.onsuccess = function () { + request.onsuccess = function() { resolve(); }; } }); }, - update: function (project) { + update: function(project) { return new Promise((resolve, reject) => { - var transaction = IDB._db.transaction(["projects"], "readwrite"); - var objectStore = transaction.objectStore("projects"); - var request = objectStore.put(project); - request.onerror = function (event) { + let transaction = IDB._db.transaction(["projects"], "readwrite"); + let objectStore = transaction.objectStore("projects"); + let request = objectStore.put(project); + request.onerror = function(event) { reject("Unable to update project to the AppProjects indexedDB: " + this.error.name + " - " + this.error.message); }; - request.onsuccess = function () { + request.onsuccess = function() { resolve(); }; }); }, - remove: function (location) { + remove: function(location) { return new Promise((resolve, reject) => { let request = IDB._db.transaction(["projects"], "readwrite") .objectStore("projects") .delete(location); - request.onsuccess = function (event) { + request.onsuccess = function(event) { resolve(); }; - request.onerror = function () { + request.onerror = function() { reject("Unable to delete project to the AppProjects indexedDB: " + this.error.name + " - " + this.error.message); }; }); } }; -var loadDeferred = IDB.open().then(function (projects) { +var loadDeferred = IDB.open().then(function(projects) { AppProjects.projects = projects; AppProjects.emit("ready", projects); }); const AppProjects = { - load: function () { + load: function() { return loadDeferred; }, - addPackaged: function (folder) { + addPackaged: function(folder) { let file = FileUtils.File(folder.path); if (!file.exists()) { return Promise.reject("path doesn't exist"); } let existingProject = this.get(folder.path); if (existingProject) { return Promise.reject("Already added"); } @@ -163,56 +161,56 @@ const AppProjects = { packagedAppOrigin: generateUUID().toString().slice(1, -1) }; return IDB.add(project).then(() => { this.projects.push(project); return project; }); }, - addHosted: function (manifestURL) { + addHosted: function(manifestURL) { let existingProject = this.get(manifestURL); if (existingProject) { return Promise.reject("Already added"); } let project = { type: "hosted", location: manifestURL }; return IDB.add(project).then(() => { this.projects.push(project); return project; }); }, - update: function (project) { + update: function(project) { return IDB.update(project); }, - updateLocation: function (project, newLocation) { + updateLocation: function(project, newLocation) { return IDB.remove(project.location) .then(() => { project.location = newLocation; return IDB.add(project); }); }, - remove: function (location) { + remove: function(location) { return IDB.remove(location).then(() => { for (let i = 0; i < this.projects.length; i++) { if (this.projects[i].location == location) { this.projects.splice(i, 1); return; } } throw new Error("Unable to find project in AppProjects store"); }); }, - get: function (location) { + get: function(location) { for (let i = 0; i < this.projects.length; i++) { if (this.projects[i].location == location) { return this.projects[i]; } } return null; },
--- a/devtools/client/webide/modules/app-validator.js +++ b/devtools/client/webide/modules/app-validator.js @@ -1,35 +1,35 @@ /* 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"; -var {Ci, Cu} = require("chrome"); +var {Ci} = require("chrome"); const {FileUtils} = require("resource://gre/modules/FileUtils.jsm"); const Services = require("Services"); var strings = Services.strings.createBundle("chrome://devtools/locale/app-manager.properties"); function AppValidator({ type, location }) { this.type = type; this.location = location; this.errors = []; this.warnings = []; } -AppValidator.prototype.error = function (message) { +AppValidator.prototype.error = function(message) { this.errors.push(message); }; -AppValidator.prototype.warning = function (message) { +AppValidator.prototype.warning = function(message) { this.warnings.push(message); }; -AppValidator.prototype._getPackagedManifestFile = function () { +AppValidator.prototype._getPackagedManifestFile = function() { let manifestFile = FileUtils.File(this.location); if (!manifestFile.exists()) { this.error(strings.GetStringFromName("validator.nonExistingFolder")); return null; } if (!manifestFile.isDirectory()) { this.error(strings.GetStringFromName("validator.expectProjectFolder")); return null; @@ -47,95 +47,95 @@ AppValidator.prototype._getPackagedManif if (!hasAppManifest && !hasJsonManifest) { this.error(strings.GetStringFromName("validator.noManifestFile")); return null; } return hasAppManifest ? appManifestFile : jsonManifestFile; }; -AppValidator.prototype._getPackagedManifestURL = function () { +AppValidator.prototype._getPackagedManifestURL = function() { let manifestFile = this._getPackagedManifestFile(); if (!manifestFile) { return null; } return Services.io.newFileURI(manifestFile).spec; }; -AppValidator.checkManifest = function (manifestURL) { +AppValidator.checkManifest = function(manifestURL) { return new Promise((resolve, reject) => { let error; let req = new XMLHttpRequest(); req.overrideMimeType("text/plain"); try { req.open("GET", manifestURL, true); req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING; } catch (e) { error = strings.formatStringFromName("validator.invalidManifestURL", [manifestURL], 1); return reject(error); } - req.onload = function () { + req.onload = function() { let manifest = null; try { manifest = JSON.parse(req.responseText); } catch (e) { error = strings.formatStringFromName("validator.invalidManifestJSON", [e, manifestURL], 2); reject(error); } resolve({manifest, manifestURL}); }; - req.onerror = function () { + req.onerror = function() { error = strings.formatStringFromName("validator.noAccessManifestURL", [req.statusText, manifestURL], 2); reject(error); }; try { req.send(null); } catch (e) { error = strings.formatStringFromName("validator.noAccessManifestURL", [e, manifestURL], 2); reject(error); } }); }; -AppValidator.findManifestAtOrigin = function (manifestURL) { +AppValidator.findManifestAtOrigin = function(manifestURL) { let fixedManifest = Services.io.newURI(manifestURL).prePath + "/manifest.webapp"; return AppValidator.checkManifest(fixedManifest); }; -AppValidator.findManifestPath = function (manifestURL) { +AppValidator.findManifestPath = function(manifestURL) { return new Promise((resolve, reject) => { if (manifestURL.endsWith("manifest.webapp")) { reject(); } else { let fixedManifest = manifestURL + "/manifest.webapp"; resolve(AppValidator.checkManifest(fixedManifest)); } }); }; -AppValidator.checkAlternateManifest = function (manifestURL) { - return (async function () { +AppValidator.checkAlternateManifest = function(manifestURL) { + return (async function() { let result; try { result = await AppValidator.findManifestPath(manifestURL); } catch (e) { result = await AppValidator.findManifestAtOrigin(manifestURL); } return result; })(); }; -AppValidator.prototype._fetchManifest = function (manifestURL) { +AppValidator.prototype._fetchManifest = function(manifestURL) { return new Promise(resolve => { this.manifestURL = manifestURL; AppValidator.checkManifest(manifestURL) .then(({manifest, manifestURL}) => { resolve(manifest); }, error => { AppValidator.checkAlternateManifest(manifestURL) @@ -145,59 +145,60 @@ AppValidator.prototype._fetchManifest = }, () => { this.error(error); resolve(null); }); }); }); }; -AppValidator.prototype._getManifest = function () { +AppValidator.prototype._getManifest = function() { let manifestURL; if (this.type == "packaged") { manifestURL = this._getPackagedManifestURL(); - if (!manifestURL) + if (!manifestURL) { return Promise.resolve(null); + } } else if (this.type == "hosted") { manifestURL = this.location; try { Services.io.newURI(manifestURL); } catch (e) { this.error(strings.formatStringFromName("validator.invalidHostedManifestURL", [manifestURL, e.message], 2)); return Promise.resolve(null); } } else { this.error(strings.formatStringFromName("validator.invalidProjectType", [this.type], 1)); return Promise.resolve(null); } return this._fetchManifest(manifestURL); }; -AppValidator.prototype.validateManifest = function (manifest) { +AppValidator.prototype.validateManifest = function(manifest) { if (!manifest.name) { this.error(strings.GetStringFromName("validator.missNameManifestProperty")); } if (!manifest.icons || Object.keys(manifest.icons).length === 0) { this.warning(strings.GetStringFromName("validator.missIconsManifestProperty")); } else if (!manifest.icons["128"]) { this.warning(strings.GetStringFromName("validator.missIconMarketplace2")); } }; -AppValidator.prototype._getOriginURL = function () { +AppValidator.prototype._getOriginURL = function() { if (this.type == "packaged") { let manifestURL = Services.io.newURI(this.manifestURL); return Services.io.newURI(".", null, manifestURL).spec; } else if (this.type == "hosted") { return Services.io.newURI(this.location).prePath; } }; -AppValidator.prototype.validateLaunchPath = function (manifest) { +AppValidator.prototype.validateLaunchPath = function(manifest) { return new Promise(resolve => { // The launch_path field has to start with a `/` if (manifest.launch_path && manifest.launch_path[0] !== "/") { this.error(strings.formatStringFromName("validator.nonAbsoluteLaunchPath", [manifest.launch_path], 1)); resolve(); } let origin = this._getOriginURL(); let path; @@ -219,54 +220,55 @@ AppValidator.prototype.validateLaunchPat try { req.open("HEAD", indexURL, true); req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING; } catch (e) { this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1)); return resolve(); } req.onload = () => { - if (req.status >= 400) + if (req.status >= 400) { this.error(strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [indexURL, req.status], 2)); + } resolve(); }; req.onerror = () => { this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1)); resolve(); }; try { req.send(null); } catch (e) { this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1)); resolve(); } }); }; -AppValidator.prototype.validateType = function (manifest) { +AppValidator.prototype.validateType = function(manifest) { let appType = manifest.type || "web"; if (!["web", "privileged", "certified"].includes(appType)) { this.error(strings.formatStringFromName("validator.invalidAppType", [appType], 1)); } else if (this.type == "hosted" && ["certified", "privileged"].includes(appType)) { this.error(strings.formatStringFromName("validator.invalidHostedPriviledges", [appType], 1)); } // certified app are not fully supported on the simulator if (appType === "certified") { this.warning(strings.GetStringFromName("validator.noCertifiedSupport")); } }; -AppValidator.prototype.validate = function () { +AppValidator.prototype.validate = function() { this.errors = []; this.warnings = []; - return this._getManifest(). - then((manifest) => { + return this._getManifest() + .then((manifest) => { if (manifest) { this.manifest = manifest; // Skip validations for add-ons if (manifest.role === "addon" || manifest.manifest_version) { return Promise.resolve(); }
--- a/devtools/client/webide/modules/config-view.js +++ b/devtools/client/webide/modules/config-view.js @@ -1,29 +1,27 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -const {Cu} = require("chrome"); - const EventEmitter = require("devtools/shared/event-emitter"); const Services = require("Services"); const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties"); var ConfigView; -module.exports = ConfigView = function (window) { +module.exports = ConfigView = function(window) { EventEmitter.decorate(this); this._doc = window.document; this._keys = []; return this; }; ConfigView.prototype = { - _renderByType: function (input, name, value, customType) { + _renderByType: function(input, name, value, customType) { value = customType || typeof value; switch (value) { case "boolean": input.setAttribute("data-type", "boolean"); input.setAttribute("type", "checkbox"); break; case "number": @@ -57,49 +55,49 @@ ConfigView.prototype = { set kind(kind) { this._kind = kind; }, set includeTypeName(include) { this._includeTypeName = include; }, - search: function (event) { + search: function(event) { if (event.target.value.length) { let stringMatch = new RegExp(event.target.value, "i"); for (let i = 0; i < this._keys.length; i++) { let key = this._keys[i]; let row = this._doc.getElementById("row-" + key); if (key.match(stringMatch)) { row.classList.remove("hide"); } else if (row) { row.classList.add("hide"); } } } else { - var trs = this._doc.getElementById("device-fields").querySelectorAll("tr"); + let trs = this._doc.getElementById("device-fields").querySelectorAll("tr"); for (let i = 0; i < trs.length; i++) { trs[i].classList.remove("hide"); } } }, - generateDisplay: function (json) { + generateDisplay: function(json) { let deviceItems = Object.keys(json); deviceItems.sort(); this.keys = deviceItems; for (let i = 0; i < this.keys.length; i++) { let key = this.keys[i]; this.generateField(key, json[key].value, json[key].hasUserValue); } }, - generateField: function (name, value, hasUserValue, customType, newRow) { + generateField: function(name, value, hasUserValue, customType, newRow) { let table = this._doc.querySelector("table"); let sResetDefault = Strings.GetStringFromName("device_reset_default"); if (!this._keys.includes(name)) { this._keys.push(name); } let input = this._doc.createElement("input"); @@ -155,48 +153,48 @@ ConfigView.prototype = { } else { existing.value = value; } } else { table.appendChild(tr); } }, - resetTable: function () { + resetTable: function() { let table = this._doc.querySelector("table"); let trs = table.querySelectorAll("tr:not(#add-custom-field)"); - for (var i = 0; i < trs.length; i++) { + for (let i = 0; i < trs.length; i++) { table.removeChild(trs[i]); } return table; }, - _getCallType: function (type, name) { + _getCallType: function(type, name) { let frontName = "get"; if (this._includeTypeName) { frontName += type; } return this._front[frontName + this._kind](name); }, - _setCallType: function (type, name, value) { + _setCallType: function(type, name, value) { let frontName = "set"; if (this._includeTypeName) { frontName += type; } return this._front[frontName + this._kind](name, value); }, - _saveByType: function (options) { + _saveByType: function(options) { let fieldName = options.id; let inputType = options.type; let value = options.value; let input = this._doc.getElementById(fieldName); switch (inputType) { case "boolean": this._setCallType("Bool", fieldName, input.checked); @@ -211,17 +209,17 @@ ConfigView.prototype = { this._setCallType("Object", fieldName, value); break; default: this._setCallType("Char", fieldName, value); break; } }, - updateField: function (event) { + updateField: function(event) { if (event.target) { let inputType = event.target.getAttribute("data-type"); let inputValue = event.target.checked || event.target.value; if (event.target.nodeName == "input" && event.target.validity.valid && event.target.classList.contains("editable")) { let id = event.target.id; @@ -238,17 +236,17 @@ ConfigView.prototype = { type: inputType, value: inputValue }); this._doc.getElementById("btn-" + id).classList.remove("hide"); } } }, - _resetToDefault: function (name, input, button) { + _resetToDefault: function(name, input, button) { this._front["clearUser" + this._kind](name); let dataType = input.getAttribute("data-type"); let tr = this._doc.getElementById("row-" + name); switch (dataType) { case "boolean": this._defaultField = this._getCallType("Bool", name); this._defaultField.then(boolean => { @@ -282,64 +280,61 @@ ConfigView.prototype = { tr.remove(); }); break; } button.classList.add("hide"); }, - checkReset: function (event) { + checkReset: function(event) { if (event.target.classList.contains("reset")) { let btnId = event.target.getAttribute("data-id"); let input = this._doc.getElementById(btnId); this._resetToDefault(btnId, input, event.target); } }, - updateFieldType: function () { + updateFieldType: function() { let table = this._doc.querySelector("table"); let customValueType = table.querySelector("#custom-value-type").value; let customTextEl = table.querySelector("#custom-value-text"); - let customText = customTextEl.value; if (customValueType.length === 0) { return false; } switch (customValueType) { case "boolean": customTextEl.type = "checkbox"; - customText = customTextEl.checked; break; case "number": - customText = parseInt(customText, 10) || 0; customTextEl.type = "number"; break; default: customTextEl.type = "text"; break; } return customValueType; }, - clearNewFields: function () { + clearNewFields: function() { let table = this._doc.querySelector("table"); let customTextEl = table.querySelector("#custom-value-text"); if (customTextEl.checked) { customTextEl.checked = false; } else { customTextEl.value = ""; } this.updateFieldType(); }, - updateNewField: function () { + updateNewField: function() { let table = this._doc.querySelector("table"); let customValueType = this.updateFieldType(); if (!customValueType) { return; } let customRow = table.querySelector("tr:nth-of-type(2)"); @@ -360,14 +355,14 @@ ConfigView.prototype = { type: customValueType, value: customText }); customTextNameEl.value = ""; this.clearNewFields(); } }, - checkNewFieldSubmit: function (event) { + checkNewFieldSubmit: function(event) { if (event.keyCode === 13) { this._doc.getElementById("custom-value").click(); } } };
--- a/devtools/client/webide/modules/project-list.js +++ b/devtools/client/webide/modules/project-list.js @@ -1,26 +1,24 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -const {Cu} = require("chrome"); - const Services = require("Services"); const {AppProjects} = require("devtools/client/webide/modules/app-projects"); const {AppManager} = require("devtools/client/webide/modules/app-manager"); const EventEmitter = require("devtools/shared/event-emitter"); const utils = require("devtools/client/webide/modules/utils"); const Telemetry = require("devtools/client/shared/telemetry"); const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties"); var ProjectList; -module.exports = ProjectList = function (win, parentWindow) { +module.exports = ProjectList = function(win, parentWindow) { EventEmitter.decorate(this); this._doc = win.document; this._UI = parentWindow.UI; this._parentWindow = parentWindow; this._telemetry = new Telemetry(); this._panelNodeEl = "div"; this.onWebIDEUpdate = this.onWebIDEUpdate.bind(this); @@ -31,84 +29,85 @@ module.exports = ProjectList = function AppManager.on("app-manager-update", this.appManagerUpdate); }; ProjectList.prototype = { get doc() { return this._doc; }, - appManagerUpdate: function (what, details) { + appManagerUpdate: function(what, details) { // Got a message from app-manager.js // See AppManager.update() for descriptions of what these events mean. switch (what) { case "project-removed": case "runtime-targets": case "connection": this.update(details); break; case "project": this.updateCommands(); this.update(details); break; } }, - onWebIDEUpdate: function (what, details) { + onWebIDEUpdate: function(what, details) { if (what == "busy" || what == "unbusy") { this.updateCommands(); } }, /** * testOptions: { chrome mochitest support * folder: nsIFile, where to store the app * index: Number, index of the app in the template list * name: String name of the app * } */ - newApp: function (testOptions) { + newApp: function(testOptions) { let parentWindow = this._parentWindow; let self = this; - return this._UI.busyUntil((async function () { + return this._UI.busyUntil((async function() { // Open newapp.xul, which will feed ret.location let ret = {location: null, testOptions: testOptions}; parentWindow.openDialog("chrome://webide/content/newapp.xul", "newapp", "chrome,modal", ret); - if (!ret.location) + if (!ret.location) { return; + } // Retrieve added project let project = AppProjects.get(ret.location); // Select project AppManager.selectedProject = project; self._telemetry.actionOccurred("webideNewProject"); })(), "creating new app"); }, - importPackagedApp: function (location) { + importPackagedApp: function(location) { let parentWindow = this._parentWindow; let UI = this._UI; - return UI.busyUntil((async function () { + return UI.busyUntil((async function() { let directory = await utils.getPackagedDirectory(parentWindow, location); if (!directory) { // User cancelled directory selection return; } await UI.importAndSelectApp(directory); })(), "importing packaged app"); }, - importHostedApp: function (location) { + importHostedApp: function(location) { let parentWindow = this._parentWindow; let UI = this._UI; - return UI.busyUntil((async function () { + return UI.busyUntil((async function() { let url = utils.getHostedURL(parentWindow, location); if (!url) { return; } await UI.importAndSelectApp(url); })(), "importing hosted app"); @@ -116,36 +115,36 @@ ProjectList.prototype = { /** * opts: { * panel: Object, currenl project panel node * name: String, name of the project * icon: String path of the project icon * } */ - _renderProjectItem: function (opts) { + _renderProjectItem: function(opts) { let span = opts.panel.querySelector("span") || this._doc.createElement("span"); span.textContent = opts.name; let icon = opts.panel.querySelector("img") || this._doc.createElement("img"); icon.className = "project-image"; icon.setAttribute("src", opts.icon); opts.panel.appendChild(icon); opts.panel.appendChild(span); opts.panel.setAttribute("title", opts.name); }, - refreshTabs: function () { + refreshTabs: function() { if (AppManager.connected) { return AppManager.listTabs().then(() => { this.updateTabs(); }).catch(console.error); } }, - updateTabs: function () { + updateTabs: function() { let tabsHeaderNode = this._doc.querySelector("#panel-header-tabs"); let tabsNode = this._doc.querySelector("#project-panel-tabs"); while (tabsNode.hasChildNodes()) { tabsNode.firstChild.remove(); } if (!AppManager.connected) { @@ -195,21 +194,21 @@ ProjectList.prototype = { name: tab.name }; }, true); } return Promise.resolve(); }, - updateApps: function () { + updateApps: function() { let doc = this._doc; let runtimeappsHeaderNode = doc.querySelector("#panel-header-runtimeapps"); let sortedApps = []; - for (let [manifestURL, app] of AppManager.apps) { + for (let [/* manifestURL */, app] of AppManager.apps) { sortedApps.push(app); } sortedApps = sortedApps.sort((a, b) => { return a.manifest.name > b.manifest.name; }); let mainProcess = AppManager.isMainProcessDebuggable(); if (AppManager.connected && (sortedApps.length > 0 || mainProcess)) { runtimeappsHeaderNode.removeAttribute("hidden"); @@ -258,17 +257,17 @@ ProjectList.prototype = { name: app.manifest.name }; }, true); } return Promise.resolve(); }, - updateCommands: function () { + updateCommands: function() { let doc = this._doc; let newAppCmd; let packagedAppCmd; let hostedAppCmd; newAppCmd = doc.querySelector("#new-app"); packagedAppCmd = doc.querySelector("#packaged-app"); hostedAppCmd = doc.querySelector("#hosted-app"); @@ -290,17 +289,17 @@ ProjectList.prototype = { }, /** * Trigger an update of the project and remote runtime list. * @param options object (optional) * An |options| object containing a type of |apps| or |tabs| will limit * what is updated to only those sections. */ - update: function (options) { + update: function(options) { if (options && options.type === "apps") { return this.updateApps(); } else if (options && options.type === "tabs") { return this.updateTabs(); } return new Promise((resolve, reject) => { let doc = this._doc; @@ -354,17 +353,17 @@ ProjectList.prototype = { if (AppManager.connected) { AppManager.listTabs().then(() => { this.updateTabs(); }).catch(console.error); } }); }, - destroy: function () { + destroy: function() { this._doc = null; AppManager.off("app-manager-update", this.appManagerUpdate); this._UI.off("webide-update", this.onWebIDEUpdate); this._UI = null; this._parentWindow = null; this._panelNodeEl = null; } };
--- a/devtools/client/webide/modules/runtime-list.js +++ b/devtools/client/webide/modules/runtime-list.js @@ -1,26 +1,22 @@ /* 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 Services = require("Services"); const {AppManager} = require("devtools/client/webide/modules/app-manager"); const EventEmitter = require("devtools/shared/event-emitter"); const {RuntimeScanners, WiFiScanner} = require("devtools/client/webide/modules/runtimes"); const {Devices} = require("resource://devtools/shared/apps/Devices.jsm"); -const utils = require("devtools/client/webide/modules/utils"); - -const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties"); var RuntimeList; -module.exports = RuntimeList = function (window, parentWindow) { +module.exports = RuntimeList = function(window, parentWindow) { EventEmitter.decorate(this); this._doc = window.document; this._UI = parentWindow.UI; this._Cmds = parentWindow.Cmds; this._parentWindow = parentWindow; this._panelNodeEl = "button"; this._panelBoxEl = "div"; @@ -32,65 +28,65 @@ module.exports = RuntimeList = function AppManager.on("app-manager-update", this.appManagerUpdate); }; RuntimeList.prototype = { get doc() { return this._doc; }, - appManagerUpdate: function (what, details) { + appManagerUpdate: function(what, details) { // Got a message from app-manager.js // See AppManager.update() for descriptions of what these events mean. switch (what) { case "runtime-list": this.update(); break; case "connection": case "runtime-global-actors": this.updateCommands(); break; } }, - onWebIDEUpdate: function (what, details) { + onWebIDEUpdate: function(what, details) { if (what == "busy" || what == "unbusy") { this.updateCommands(); } }, - takeScreenshot: function () { + takeScreenshot: function() { this._Cmds.takeScreenshot(); }, - showRuntimeDetails: function () { + showRuntimeDetails: function() { this._Cmds.showRuntimeDetails(); }, - showDevicePreferences: function () { + showDevicePreferences: function() { this._Cmds.showDevicePrefs(); }, - showSettings: function () { + showSettings: function() { this._Cmds.showSettings(); }, - showTroubleShooting: function () { + showTroubleShooting: function() { this._Cmds.showTroubleShooting(); }, - showAddons: function () { + showAddons: function() { this._Cmds.showAddons(); }, - refreshScanners: function () { + refreshScanners: function() { RuntimeScanners.scan(); }, - updateCommands: function () { + updateCommands: function() { let doc = this._doc; // Runtime commands let screenshotCmd = doc.querySelector("#runtime-screenshot"); let detailsCmd = doc.querySelector("#runtime-details"); let disconnectCmd = doc.querySelector("#runtime-disconnect"); let devicePrefsCmd = doc.querySelector("#runtime-preferences"); let settingsCmd = doc.querySelector("#runtime-settings"); @@ -108,17 +104,17 @@ RuntimeList.prototype = { detailsCmd.setAttribute("disabled", "true"); screenshotCmd.setAttribute("disabled", "true"); disconnectCmd.setAttribute("disabled", "true"); devicePrefsCmd.setAttribute("disabled", "true"); settingsCmd.setAttribute("disabled", "true"); } }, - update: function () { + update: function() { let doc = this._doc; let wifiHeaderNode = doc.querySelector("#runtime-header-wifi"); if (WiFiScanner.allowed) { wifiHeaderNode.removeAttribute("hidden"); } else { wifiHeaderNode.setAttribute("hidden", "true"); } @@ -177,17 +173,17 @@ RuntimeList.prototype = { panelItemNode.appendChild(configButton); } parent.appendChild(panelItemNode); } } }, - destroy: function () { + destroy: function() { this._doc = null; AppManager.off("app-manager-update", this.appManagerUpdate); this._UI.off("webide-update", this.onWebIDEUpdate); this._UI = null; this._Cmds = null; this._parentWindow = null; this._panelNodeEl = null; }
--- a/devtools/client/webide/modules/runtimes.js +++ b/devtools/client/webide/modules/runtimes.js @@ -2,17 +2,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; const {Ci} = require("chrome"); const Services = require("Services"); const {Devices} = require("resource://devtools/shared/apps/Devices.jsm"); -const {Connection} = require("devtools/shared/client/connection-manager"); const {DebuggerServer} = require("devtools/server/main"); const discovery = require("devtools/shared/discovery/discovery"); const EventEmitter = require("devtools/shared/event-emitter"); const promise = require("promise"); loader.lazyRequireGetter(this, "AuthenticationResult", "devtools/shared/security/auth", true); loader.lazyRequireGetter(this, "DevToolsUtils", "devtools/shared/DevToolsUtils"); @@ -209,17 +208,17 @@ var LazyAdbScanner = { disable() { Devices.emit("adb-stop-polling"); }, scan() { return promise.resolve(); }, - listRuntimes: function () { + listRuntimes: function() { return []; } }; EventEmitter.decorate(LazyAdbScanner); RuntimeScanners.add(LazyAdbScanner); @@ -258,17 +257,17 @@ var WiFiScanner = { this._emitUpdated(); }, scan() { discovery.scan(); return promise.resolve(); }, - listRuntimes: function () { + listRuntimes: function() { return this._runtimes; }, ALLOWED_PREF: "devtools.remote.wifi.scan", get allowed() { return Services.prefs.getBoolPref(this.ALLOWED_PREF); }, @@ -294,17 +293,19 @@ var WiFiScanner = { EventEmitter.decorate(WiFiScanner); WiFiScanner.init(); exports.WiFiScanner = WiFiScanner; var StaticScanner = { enable() {}, disable() {}, - scan() { return promise.resolve(); }, + scan() { + return promise.resolve(); + }, listRuntimes() { let runtimes = [gRemoteRuntime]; if (Services.prefs.getBoolPref("devtools.webide.enableLocalRuntime")) { runtimes.push(gLocalRuntime); } return runtimes; } }; @@ -327,17 +328,17 @@ var RuntimeTypes = exports.RuntimeTypes function WiFiRuntime(deviceName) { this.deviceName = deviceName; } WiFiRuntime.prototype = { type: RuntimeTypes.WIFI, // Mark runtime as taking a long time to connect prolongedConnection: true, - connect: function (connection) { + connect: function(connection) { let service = discovery.getRemoteService("devtools", this.deviceName); if (!service) { return promise.reject(new Error("Can't find device: " + this.name)); } connection.advertisement = service; connection.authenticator.sendOOB = this.sendOOB; // Disable the default connection timeout, since QR scanning can take an // unknown amount of time. This prevents spurious errors (even after @@ -387,17 +388,17 @@ WiFiRuntime.prototype = { } // Listen for the window our prompt opens, so we can close it programatically let promptWindow; let windowListener = { onOpenWindow(xulWindow) { let win = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindow); - win.addEventListener("load", function () { + win.addEventListener("load", function() { if (win.document.documentElement.getAttribute("id") != WINDOW_ID) { return; } // Found the window promptWindow = win; Services.wm.removeListener(windowListener); }, {once: true}); }, @@ -430,17 +431,17 @@ WiFiRuntime.prototype = { } }; // For testing use only exports._WiFiRuntime = WiFiRuntime; var gLocalRuntime = { type: RuntimeTypes.LOCAL, - connect: function (connection) { + connect: function(connection) { DebuggerServer.init(); DebuggerServer.registerAllActors(); DebuggerServer.allowChromeProcess = true; connection.host = null; // Force Pipe transport connection.port = null; connection.connect(); return promise.resolve(); }, @@ -452,17 +453,17 @@ var gLocalRuntime = { }, }; // For testing use only exports._gLocalRuntime = gLocalRuntime; var gRemoteRuntime = { type: RuntimeTypes.REMOTE, - connect: function (connection) { + connect: function(connection) { let win = Services.wm.getMostRecentWindow("devtools:webide"); if (!win) { return promise.reject(new Error("No WebIDE window found")); } let ret = {value: connection.host + ":" + connection.port}; let title = Strings.GetStringFromName("remote_runtime_promptTitle"); let message = Strings.GetStringFromName("remote_runtime_promptMessage"); let ok = Services.prompt.prompt(win, title, message, ret, null, {});
--- a/devtools/client/webide/modules/tab-store.js +++ b/devtools/client/webide/modules/tab-store.js @@ -1,23 +1,21 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -const { Cu } = require("chrome"); - const { TargetFactory } = require("devtools/client/framework/target"); const EventEmitter = require("devtools/shared/event-emitter"); const { Connection } = require("devtools/shared/client/connection-manager"); const _knownTabStores = new WeakMap(); var TabStore; -module.exports = TabStore = function (connection) { +module.exports = TabStore = function(connection) { // If we already know about this connection, // let's re-use the existing store. if (_knownTabStores.has(connection)) { return _knownTabStores.get(connection); } _knownTabStores.set(connection, this); @@ -34,36 +32,36 @@ module.exports = TabStore = function (co this._onTabListChanged = this._onTabListChanged.bind(this); this._onTabNavigated = this._onTabNavigated.bind(this); this._onStatusChanged(); return this; }; TabStore.prototype = { - destroy: function () { + destroy: function() { if (this._connection) { // While this.destroy is bound using .once() above, that event may not // have occurred when the TabStore client calls destroy, so we // manually remove it here. this._connection.off(Connection.Events.DESTROYED, this.destroy); this._connection.off(Connection.Events.STATUS_CHANGED, this._onStatusChanged); _knownTabStores.delete(this._connection); this._connection = null; } }, - _resetStore: function () { + _resetStore: function() { this.response = null; this.tabs = []; this._selectedTab = null; this._selectedTabTargetPromise = null; }, - _onStatusChanged: function () { + _onStatusChanged: function() { if (this._connection.status == Connection.Status.CONNECTED) { // Watch for changes to remote browser tabs this._connection.client.addListener("tabListChanged", this._onTabListChanged); this._connection.client.addListener("tabNavigated", this._onTabNavigated); this.listTabs(); } else { @@ -72,31 +70,31 @@ TabStore.prototype = { this._onTabListChanged); this._connection.client.removeListener("tabNavigated", this._onTabNavigated); } this._resetStore(); } }, - _onTabListChanged: function () { + _onTabListChanged: function() { this.listTabs().then(() => this.emit("tab-list")) .catch(console.error); }, - _onTabNavigated: function (e, { from, title, url }) { + _onTabNavigated: function(e, { from, title, url }) { if (!this._selectedTab || from !== this._selectedTab.actor) { return; } this._selectedTab.url = url; this._selectedTab.title = title; this.emit("navigate"); }, - listTabs: function () { + listTabs: function() { if (!this._connection || !this._connection.client) { return Promise.reject(new Error("Can't listTabs, not connected.")); } return new Promise((resolve, reject) => { this._connection.client.listTabs().then(response => { if (response.error) { this._connection.disconnect(); @@ -130,36 +128,36 @@ TabStore.prototype = { this._selectedTab = tab; this._selectedTabTargetPromise = null; // Attach to the tab to follow navigation events if (this._selectedTab) { this.getTargetForTab(); } }, - _checkSelectedTab: function () { + _checkSelectedTab: function() { if (!this._selectedTab) { return; } let alive = this.tabs.some(tab => { return tab.actor === this._selectedTab.actor; }); if (!alive) { this._selectedTab = null; this._selectedTabTargetPromise = null; this.emit("closed"); } }, - getTargetForTab: function () { + getTargetForTab: function() { if (this._selectedTabTargetPromise) { return this._selectedTabTargetPromise; } let store = this; - this._selectedTabTargetPromise = (async function () { + this._selectedTabTargetPromise = (async function() { // If you connect to a tab, then detach from it, the root actor may have // de-listed the actors that belong to the tab. This breaks the toolbox // if you try to connect to the same tab again. To work around this // issue, we force a "listTabs" request before connecting to a tab. await store.listTabs(); return TargetFactory.forRemoteTab({ form: store._selectedTab, client: store._connection.client,
--- a/devtools/client/webide/modules/utils.js +++ b/devtools/client/webide/modules/utils.js @@ -1,13 +1,13 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -const { Cc, Cu, Ci } = require("chrome"); +const { Cc, Ci } = require("chrome"); const { FileUtils } = require("resource://gre/modules/FileUtils.jsm"); const Services = require("Services"); const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties"); function doesFileExist(location) { let file = new FileUtils.File(location); return file.exists(); }
--- a/devtools/client/webide/test/browser_tabs.js +++ b/devtools/client/webide/test/browser_tabs.js @@ -1,19 +1,19 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; const TEST_URI = "http://example.com/browser/devtools/client/webide/test/doc_tabs.html"; function test() { waitForExplicitFinish(); - requestCompleteLog(); + SimpleTest.requestCompleteLog(); - (async function () { + (async function() { // Since we test the connections set below, destroy the server in case it // was left open. DebuggerServer.destroy(); DebuggerServer.init(); DebuggerServer.registerAllActors(); let tab = await addTab(TEST_URI); @@ -68,17 +68,17 @@ function connectToLocal(win, docRuntime) win.AppManager.connection.once( win.Connection.Events.CONNECTED, resolve); docRuntime.querySelectorAll(".runtime-panel-item-other")[1].click(); }); } function selectTabProject(win, docProject) { - return (async function () { + return (async function() { await waitForUpdate(win, "runtime-targets"); let tabsNode = docProject.querySelector("#project-panel-tabs"); let tabNode = tabsNode.querySelectorAll(".panel-item")[1]; let project = waitForUpdate(win, "project"); tabNode.click(); await project; })(); }
--- a/devtools/client/webide/test/device_front_shared.js +++ b/devtools/client/webide/test/device_front_shared.js @@ -1,11 +1,13 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint no-unused-vars: ["error", {"args": "none", "vars": "local"}] */ + "use strict"; var customName; var customValue; var customValueType; var customBtn; var newField; var change; @@ -104,17 +106,17 @@ function addNewFieldInteger() { is(newField.type, "number", "Custom type is a number"); is(newField.value, "1", "Custom integer value is correct"); } ok(found, "Found new integer field line"); is(customName.value, "", "Custom integer name reset"); is(customValue.value, "", "Custom integer value reset"); } -var editFieldInteger = async function () { +var editFieldInteger = async function() { // Edit existing custom integer preference newField.value = 3; newField.click(); is(newField.value, "3", "Custom integer existing value is correct"); // Reset a custom field let resetBtn = doc.querySelector("#btn-new-integer-field"); resetBtn.click(); @@ -125,30 +127,30 @@ var editFieldInteger = async function () let fieldRow = doc.querySelector("#row-new-integer-field"); if (!fieldRow) { found = false; } ok(!found, "Custom field removed"); } }; -var resetExistingField = async function (id) { +var resetExistingField = async function(id) { let existing = doc.getElementById(id); existing.click(); is(existing.checked, true, "Existing boolean value is correct"); resetBtn = doc.getElementById("btn-" + id); resetBtn.click(); await iframe.contentWindow.configView._defaultField; ok(resetBtn.classList.contains("hide"), true, "Reset button hidden"); is(existing.checked, true, "Existing field reset"); }; -var resetNewField = async function (id) { +var resetNewField = async function(id) { let custom = doc.getElementById(id); custom.click(); is(custom.value, "test", "New string value is correct"); resetBtn = doc.getElementById("btn-" + id); resetBtn.click(); await iframe.contentWindow.configView._defaultField; @@ -166,17 +168,17 @@ function addNewFieldBoolean() { if (newField) { found = true; is(newField.type, "checkbox", "Custom type is a checkbox"); is(newField.checked, true, "Custom boolean value is correctly true"); } ok(found, "Found new boolean field line"); // Mouse event trigger - var mouseClick = new MouseEvent("click", { + let mouseClick = new MouseEvent("click", { canBubble: true, cancelable: true, view: doc.parent, }); found = false; customValueType.value = "boolean"; customValueType.dispatchEvent(change);
--- a/devtools/client/webide/test/head.js +++ b/devtools/client/webide/test/head.js @@ -5,18 +5,16 @@ const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {}); const { FileUtils } = require("resource://gre/modules/FileUtils.jsm"); const { gDevTools } = require("devtools/client/framework/devtools"); const Services = require("Services"); const { AppProjects } = require("devtools/client/webide/modules/app-projects"); const DevToolsUtils = require("devtools/shared/DevToolsUtils"); const { DebuggerServer } = require("devtools/server/main"); -const flags = require("devtools/shared/flags"); -flags.testing = true; var TEST_BASE; if (window.location === "chrome://browser/content/browser.xul") { TEST_BASE = "chrome://mochitests/content/browser/devtools/client/webide/test/"; } else { TEST_BASE = "chrome://mochitests/content/chrome/devtools/client/webide/test/"; } @@ -25,60 +23,59 @@ Services.prefs.setBoolPref("devtools.web Services.prefs.setCharPref("devtools.webide.adbAddonURL", TEST_BASE + "addons/adbhelper-#OS#.xpi"); Services.prefs.setCharPref("devtools.webide.templatesURL", TEST_BASE + "templates.json"); Services.prefs.setCharPref("devtools.devices.url", TEST_BASE + "browser_devices.json"); var registerCleanupFunction = registerCleanupFunction || SimpleTest.registerCleanupFunction; registerCleanupFunction(() => { - flags.testing = false; Services.prefs.clearUserPref("devtools.webide.enabled"); Services.prefs.clearUserPref("devtools.webide.enableLocalRuntime"); Services.prefs.clearUserPref("devtools.webide.autoinstallADBHelper"); Services.prefs.clearUserPref("devtools.webide.busyTimeout"); Services.prefs.clearUserPref("devtools.webide.lastSelectedProject"); Services.prefs.clearUserPref("devtools.webide.lastConnectedRuntime"); }); -var openWebIDE = async function (autoInstallAddons) { +var openWebIDE = async function(autoInstallAddons) { info("opening WebIDE"); Services.prefs.setBoolPref("devtools.webide.autoinstallADBHelper", !!autoInstallAddons); - let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci.nsIWindowWatcher); - let win = ww.openWindow(null, "chrome://webide/content/", "webide", "chrome,centerscreen,resizable", null); + let win = Services.ww.openWindow(null, "chrome://webide/content/", "webide", + "chrome,centerscreen,resizable", null); await new Promise(resolve => { - win.addEventListener("load", function () { + win.addEventListener("load", function() { SimpleTest.requestCompleteLog(); SimpleTest.executeSoon(resolve); }, {once: true}); }); info("WebIDE open"); return win; }; function closeWebIDE(win) { info("Closing WebIDE"); return new Promise(resolve => { - win.addEventListener("unload", function () { + win.addEventListener("unload", function() { info("WebIDE closed"); SimpleTest.executeSoon(resolve); }, {once: true}); win.close(); }); } function removeAllProjects() { - return (async function () { + return (async function() { await AppProjects.load(); // use a new array so we're not iterating over the same // underlying array that's being modified by AppProjects let projects = AppProjects.projects.map(p => p.location); for (let i = 0; i < projects.length; i++) { await AppProjects.remove(projects[i]); } })(); @@ -122,49 +119,49 @@ function documentIsLoaded(doc) { } }); } }); } function lazyIframeIsLoaded(iframe) { return new Promise(resolve => { - iframe.addEventListener("load", function () { + iframe.addEventListener("load", function() { resolve(nextTick()); }, {capture: true, once: true}); }); } function addTab(aUrl, aWindow) { info("Adding tab: " + aUrl); return new Promise(resolve => { let targetWindow = aWindow || window; let targetBrowser = targetWindow.gBrowser; targetWindow.focus(); let tab = targetBrowser.selectedTab = targetBrowser.addTab(aUrl); let linkedBrowser = tab.linkedBrowser; - BrowserTestUtils.browserLoaded(linkedBrowser).then(function () { + BrowserTestUtils.browserLoaded(linkedBrowser).then(function() { info("Tab added and finished loading: " + aUrl); resolve(tab); }); }); } function removeTab(aTab, aWindow) { info("Removing tab."); return new Promise(resolve => { let targetWindow = aWindow || window; let targetBrowser = targetWindow.gBrowser; let tabContainer = targetBrowser.tabContainer; - tabContainer.addEventListener("TabClose", function (aEvent) { + tabContainer.addEventListener("TabClose", function(aEvent) { info("Tab removed and finished closing."); resolve(); }, {once: true}); targetBrowser.removeTab(aTab); }); }
--- a/devtools/client/webide/test/test_addons.html +++ b/devtools/client/webide/test/test_addons.html @@ -10,65 +10,63 @@ <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"></script> <script type="application/javascript" src="head.js"></script> <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> </head> <body> <script type="application/javascript"> + // XXX Bug 1072167 - When updating after migration, fix the no-undef issues. + /* eslint-disable no-undef */ + window.onload = function() { SimpleTest.waitForExplicitFinish(); - const {Devices} = ChromeUtils.import("resource://devtools/shared/apps/Devices.jsm"); - - let adbAddonsInstalled; + const {Devices} = ChromeUtils.import("resource://devtools/shared/apps/Devices.jsm", {}); function uninstallADBFromUI(doc) { return new Promise((resolve, reject) => { Devices.on("addon-status-updated", function onUpdate() { nextTick().then(() => { let li = doc.querySelector('[status="uninstalled"][addon="adb"]'); if (li) { Devices.off("addon-status-updated", onUpdate); resolve(); } else { reject("Can't find item"); } - }) + }); }); let li = doc.querySelector('[status="installed"][addon="adb"]'); li.querySelector(".uninstall-button").click(); }); } - (async function () { - + (async function() { ok(!Devices.helperAddonInstalled, "Helper not installed"); let win = await openWebIDE(true); - let docRuntime = getRuntimeDocument(win); ok(Devices.helperAddonInstalled, "Helper has been auto-installed"); await nextTick(); let w = addonDoc.querySelector(".warning"); - let display = addonDoc.defaultView.getComputedStyle(w).display + let display = addonDoc.defaultView.getComputedStyle(w).display; is(display, "none", "Warning about missing ADB hidden"); await uninstallADBFromUI(addonDoc, "adb"); items = panelNode.querySelectorAll(".runtime-panel-item-usb"); is(items.length, 0, "No usb runtime listed"); - display = addonDoc.defaultView.getComputedStyle(w).display + display = addonDoc.defaultView.getComputedStyle(w).display; is(display, "block", "Warning about missing ADB present"); await closeWebIDE(win); SimpleTest.finish(); - })(); - } + }; </script> </body> </html>
--- a/devtools/client/webide/test/test_app_validator.html +++ b/devtools/client/webide/test/test_app_validator.html @@ -16,160 +16,158 @@ <script type="application/javascript"> ChromeUtils.import("resource://testing-common/httpd.js"); const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {}); const {AppValidator} = require("devtools/client/webide/modules/app-validator"); const Services = require("Services"); const nsFile = Components.Constructor("@mozilla.org/file/local;1", "nsIFile", "initWithPath"); - const cr = Cc["@mozilla.org/chrome/chrome-registry;1"] - .getService(Ci.nsIChromeRegistry); const strings = Services.strings.createBundle("chrome://devtools/locale/app-manager.properties"); - let httpserver, origin; + let httpserver, fakeOrigin; window.onload = function() { SimpleTest.waitForExplicitFinish(); httpserver = new HttpServer(); httpserver.start(-1); - origin = "http://localhost:" + httpserver.identity.primaryPort + "/"; + fakeOrigin = "http://localhost:" + httpserver.identity.primaryPort + "/"; next(); - } + }; - function createHosted(path, manifestFile="/manifest.webapp") { + function createHosted(path, manifestFile = "/manifest.webapp") { let dirPath = getTestFilePath("validator/" + path); httpserver.registerDirectory("/", nsFile(dirPath)); return new AppValidator({ type: "hosted", - location: origin + manifestFile + location: fakeOrigin + manifestFile }); } function createPackaged(path) { let dirPath = getTestFilePath("validator/" + path); return new AppValidator({ type: "packaged", location: dirPath }); } function next() { let test = tests.shift(); if (test) { try { test(); - } catch(e) { + } catch (e) { console.error("exception", String(e), e, e.stack); } } else { httpserver.stop(function() { SimpleTest.finish(); }); } } let tests = [ // Test a 100% valid example - function () { + function() { let validator = createHosted("valid"); validator.validate().then(() => { - is(validator.errors.length, 0, "valid app got no error"); - is(validator.warnings.length, 0, "valid app got no warning"); + is(validator.errors.length, 0, "valid app got no error"); + is(validator.warnings.length, 0, "valid app got no warning"); - next(); - }); + next(); + }); }, - function () { + function() { let validator = createPackaged("valid"); validator.validate().then(() => { - is(validator.errors.length, 0, "valid packaged app got no error"); - is(validator.warnings.length, 0, "valid packaged app got no warning"); + is(validator.errors.length, 0, "valid packaged app got no error"); + is(validator.warnings.length, 0, "valid packaged app got no warning"); - next(); - }); + next(); + }); }, // Test a launch path that returns a 404 - function () { + function() { let validator = createHosted("wrong-launch-path"); validator.validate().then(() => { - is(validator.errors.length, 1, "app with non-existant launch path got an error"); - is(validator.errors[0], strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [origin + "wrong-path.html", 404], 2), + is(validator.errors.length, 1, "app with non-existant launch path got an error"); + is(validator.errors[0], strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [fakeOrigin + "wrong-path.html", 404], 2), "with the right error message"); - is(validator.warnings.length, 0, "but no warning"); - next(); - }); + is(validator.warnings.length, 0, "but no warning"); + next(); + }); }, - function () { + function() { let validator = createPackaged("wrong-launch-path"); validator.validate().then(() => { - is(validator.errors.length, 1, "app with wrong path got an error"); - let file = nsFile(validator.location); - file.append("wrong-path.html"); - let url = Services.io.newFileURI(file); - is(validator.errors[0], strings.formatStringFromName("validator.accessFailedLaunchPath", [url.spec], 1), + is(validator.errors.length, 1, "app with wrong path got an error"); + let file = nsFile(validator.location); + file.append("wrong-path.html"); + let url = Services.io.newFileURI(file); + is(validator.errors[0], strings.formatStringFromName("validator.accessFailedLaunchPath", [url.spec], 1), "with the expected message"); - is(validator.warnings.length, 0, "but no warning"); + is(validator.warnings.length, 0, "but no warning"); - next(); - }); + next(); + }); }, // Test when using a non-absolute path for launch_path - function () { + function() { let validator = createHosted("non-absolute-path"); validator.validate().then(() => { - is(validator.errors.length, 1, "app with non absolute path got an error"); - is(validator.errors[0], strings.formatStringFromName("validator.nonAbsoluteLaunchPath", ["non-absolute.html"], 1), + is(validator.errors.length, 1, "app with non absolute path got an error"); + is(validator.errors[0], strings.formatStringFromName("validator.nonAbsoluteLaunchPath", ["non-absolute.html"], 1), "with expected message"); - is(validator.warnings.length, 0, "but no warning"); - next(); - }); + is(validator.warnings.length, 0, "but no warning"); + next(); + }); }, - function () { + function() { let validator = createPackaged("non-absolute-path"); validator.validate().then(() => { - is(validator.errors.length, 1, "app with non absolute path got an error"); - is(validator.errors[0], strings.formatStringFromName("validator.nonAbsoluteLaunchPath", ["non-absolute.html"], 1), + is(validator.errors.length, 1, "app with non absolute path got an error"); + is(validator.errors[0], strings.formatStringFromName("validator.nonAbsoluteLaunchPath", ["non-absolute.html"], 1), "with expected message"); - is(validator.warnings.length, 0, "but no warning"); - next(); - }); + is(validator.warnings.length, 0, "but no warning"); + next(); + }); }, // Test multiple failures (missing name [error] and icon [warning]) - function () { + function() { let validator = createHosted("no-name-or-icon"); validator.validate().then(() => { checkNoNameOrIcon(validator); }); }, - function () { + function() { let validator = createPackaged("no-name-or-icon"); validator.validate().then(() => { checkNoNameOrIcon(validator); }); }, // Test a regular URL instead of a direct link to the manifest - function () { + function() { let validator = createHosted("valid", "/"); validator.validate().then(() => { is(validator.warnings.length, 0, "manifest found got no warning"); is(validator.errors.length, 0, "manifest found got no error"); next(); }); }, // Test finding a manifest at origin's root - function () { + function() { let validator = createHosted("valid", "/unexisting-dir"); validator.validate().then(() => { is(validator.warnings.length, 0, "manifest found at origin root got no warning"); is(validator.errors.length, 0, "manifest found at origin root got no error"); next(); }); },
--- a/devtools/client/webide/test/test_autoconnect_runtime.html +++ b/devtools/client/webide/test/test_autoconnect_runtime.html @@ -13,17 +13,17 @@ </head> <body> <script type="application/javascript"> window.onload = function() { SimpleTest.waitForExplicitFinish(); - (async function () { + (async function() { DebuggerServer.init(); DebuggerServer.registerAllActors(); let win = await openWebIDE(); let docRuntime = getRuntimeDocument(win); let fakeRuntime = { type: "USB", @@ -81,12 +81,12 @@ await win.Cmds.disconnectRuntime(); await closeWebIDE(win); DebuggerServer.destroy(); SimpleTest.finish(); })(); - } + }; </script> </body> </html>
--- a/devtools/client/webide/test/test_autoselect_project.html +++ b/devtools/client/webide/test/test_autoselect_project.html @@ -13,17 +13,17 @@ </head> <body> <script type="application/javascript"> window.onload = function() { SimpleTest.waitForExplicitFinish(); - (async function () { + (async function() { DebuggerServer.init(); DebuggerServer.registerAllActors(); let win = await openWebIDE(); let docRuntime = getRuntimeDocument(win); let docProject = getProjectDocument(win); let panelNode = docRuntime.querySelector("#runtime-panel"); @@ -97,12 +97,12 @@ await win.Cmds.disconnectRuntime(); await closeWebIDE(win); DebuggerServer.destroy(); SimpleTest.finish(); })(); - } + }; </script> </body> </html>
--- a/devtools/client/webide/test/test_basic.html +++ b/devtools/client/webide/test/test_basic.html @@ -13,44 +13,44 @@ </head> <body> <script type="application/javascript"> window.onload = function() { SimpleTest.waitForExplicitFinish(); - (async function () { - let win = await openWebIDE(); + (async function() { + let win = await openWebIDE(); - const {gDevToolsBrowser} = require("devtools/client/framework/devtools-browser"); - await gDevToolsBrowser.isWebIDEInitialized.promise; - ok(true, "WebIDE was initialized"); + const {gDevToolsBrowser} = require("devtools/client/framework/devtools-browser"); + await gDevToolsBrowser.isWebIDEInitialized.promise; + ok(true, "WebIDE was initialized"); - ok(win, "Found a window"); - ok(win.AppManager, "App Manager accessible"); - let appmgr = win.AppManager; - ok(appmgr.connection, "App Manager connection ready"); - ok(appmgr.runtimeList, "Runtime list ready"); + ok(win, "Found a window"); + ok(win.AppManager, "App Manager accessible"); + let appmgr = win.AppManager; + ok(appmgr.connection, "App Manager connection ready"); + ok(appmgr.runtimeList, "Runtime list ready"); // test error reporting - let nbox = win.document.querySelector("#notificationbox"); - let notification = nbox.getNotificationWithValue("webide:errornotification"); - ok(!notification, "No notification yet"); - let deferred = new Promise((resolve, reject) => { - nextTick().then(() => { - reject("BOOM!"); - }); + let nbox = win.document.querySelector("#notificationbox"); + let notification = nbox.getNotificationWithValue("webide:errornotification"); + ok(!notification, "No notification yet"); + let deferred = new Promise((resolve, reject) => { + nextTick().then(() => { + reject("BOOM!"); }); - try { - await win.UI.busyUntil(deferred, "xx"); - } catch(e) {/* This *will* fail */} - notification = nbox.getNotificationWithValue("webide:errornotification"); - ok(notification, "Error has been reported"); + }); + try { + await win.UI.busyUntil(deferred, "xx"); + } catch (e) { /* This *will* fail */ } + notification = nbox.getNotificationWithValue("webide:errornotification"); + ok(notification, "Error has been reported"); - await closeWebIDE(win); + await closeWebIDE(win); - SimpleTest.finish(); + SimpleTest.finish(); })(); - } + }; </script> </body> </html>
--- a/devtools/client/webide/test/test_device_preferences.html +++ b/devtools/client/webide/test/test_device_preferences.html @@ -10,20 +10,21 @@ <script type="application/javascript" src="head.js"></script> <script type="application/javascript" src="device_front_shared.js"></script> <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> </head> <body> <script type="application/javascript"> + /* import-globals-from device_front_shared.js */ window.onload = function() { SimpleTest.waitForExplicitFinish(); - (async function () { + (async function() { DebuggerServer.init(); DebuggerServer.registerAllActors(); let win = await openWebIDE(); let prefIframe = win.document.querySelector("#deck-panel-devicepreferences"); let docRuntime = getRuntimeDocument(win); @@ -74,12 +75,12 @@ await closeWebIDE(win); SimpleTest.finish(); })().catch(e => { ok(false, "Exception: " + e); SimpleTest.finish(); }); - } + }; </script> </body> </html>
--- a/devtools/client/webide/test/test_device_runtime.html +++ b/devtools/client/webide/test/test_device_runtime.html @@ -13,17 +13,17 @@ </head> <body> <script type="application/javascript"> window.onload = function() { SimpleTest.waitForExplicitFinish(); - (async function () { + (async function() { DebuggerServer.init(); DebuggerServer.registerAllActors(); let win = await openWebIDE(); let detailsIframe = win.document.querySelector("#deck-panel-runtimedetails"); await connectToLocalRuntime(win); @@ -46,17 +46,17 @@ // device info and permissions content is checked in other tests // We just test one value to make sure we get something let doc = detailsIframe.contentWindow.document; let trs = doc.querySelectorAll("tr"); let found = false; for (let tr of trs) { - let [name,val] = tr.querySelectorAll("td"); + let [name, val] = tr.querySelectorAll("td"); if (name.textContent == "appid") { found = true; is(val.textContent, Services.appinfo.ID, "appid has the right value"); break; } } ok(found, "Found appid line"); @@ -68,12 +68,12 @@ await closeWebIDE(win); SimpleTest.finish(); })().catch(e => { ok(false, "Exception: " + e); SimpleTest.finish(); }); - } + }; </script> </body> </html>
--- a/devtools/client/webide/test/test_duplicate_import.html +++ b/devtools/client/webide/test/test_duplicate_import.html @@ -12,17 +12,17 @@ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> </head> <body> <script type="application/javascript"> window.onload = function() { SimpleTest.waitForExplicitFinish(); - (async function () { + (async function() { let win = await openWebIDE(); let docProject = getProjectDocument(win); let winProject = getProjectWindow(win); let packagedAppLocation = getTestFilePath("app"); let hostedAppManifest = TEST_BASE + "hosted_app.manifest"; await win.AppProjects.load(); is(win.AppProjects.projects.length, 0, "IDB is empty"); @@ -65,13 +65,13 @@ await removeAllProjects(); SimpleTest.finish(); })().catch(e => { ok(false, "Exception: " + e); SimpleTest.finish(); }); - } + }; </script> </body> </html>
--- a/devtools/client/webide/test/test_fullscreenToolbox.html +++ b/devtools/client/webide/test/test_fullscreenToolbox.html @@ -22,17 +22,17 @@ resolve); docRuntime.querySelectorAll(".runtime-panel-item-other")[1].click(); }); } window.onload = function() { SimpleTest.waitForExplicitFinish(); - (async function () { + (async function() { let win = await openWebIDE(); let docProject = getProjectDocument(win); let docRuntime = getRuntimeDocument(win); win.AppManager.update("runtime-list"); connectToLocal(win, docRuntime); // Select main process @@ -56,12 +56,12 @@ await win.Cmds.disconnectRuntime(); await closeWebIDE(win); DebuggerServer.destroy(); SimpleTest.finish(); })(); - } + }; </script> </body> </html>
--- a/devtools/client/webide/test/test_import.html +++ b/devtools/client/webide/test/test_import.html @@ -12,17 +12,17 @@ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> </head> <body> <script type="application/javascript"> window.onload = function() { SimpleTest.waitForExplicitFinish(); - (async function () { + (async function() { let win = await openWebIDE(); let docProject = getProjectDocument(win); let winProject = getProjectWindow(win); let packagedAppLocation = getTestFilePath("app"); await win.AppProjects.load(); is(win.AppProjects.projects.length, 0, "IDB is empty"); @@ -53,17 +53,17 @@ await nextTick(); hostedAppManifest = TEST_BASE + "/app"; await winProject.projectList.importHostedApp(hostedAppManifest); await waitForUpdate(win, "project-validated"); project = win.AppManager.selectedProject; - ok(project.location.endsWith('manifest.webapp'), "The manifest was found and the project was updated"); + ok(project.location.endsWith("manifest.webapp"), "The manifest was found and the project was updated"); let panelNode = docProject.querySelector("#project-panel"); let items = panelNode.querySelectorAll(".panel-item"); // 4 controls, + 2 projects is(items.length, 6, "6 projects in panel"); is(items[3].querySelector("span").textContent, "A name (in app directory)", "Panel text is correct"); is(items[4].querySelector("span").textContent, "hosted manifest name property", "Panel text is correct"); @@ -71,12 +71,12 @@ await removeAllProjects(); SimpleTest.finish(); })().catch(e => { ok(false, "Exception: " + e); SimpleTest.finish(); }); - } + }; </script> </body> </html>
--- a/devtools/client/webide/test/test_manifestUpdate.html +++ b/devtools/client/webide/test/test_manifestUpdate.html @@ -15,84 +15,83 @@ <body> <script type="application/javascript"> window.onload = function() { SimpleTest.waitForExplicitFinish(); let {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm", {}); - (async function () { - let win = await openWebIDE(); - let winProject = getProjectWindow(win); - let AppManager = win.AppManager; + (async function() { + let win = await openWebIDE(); + let winProject = getProjectWindow(win); + let AppManager = win.AppManager; - function isProjectMarkedAsValid() { - let details = win.frames[1]; - return !details.document.body.classList.contains("error"); - } + function isProjectMarkedAsValid() { + let details = win.frames[1]; + return !details.document.body.classList.contains("error"); + } - let packagedAppLocation = getTestFilePath("app"); + let packagedAppLocation = getTestFilePath("app"); - let onValidated = waitForUpdate(win, "project-validated"); - let onDetails = waitForUpdate(win, "details"); - await winProject.projectList.importPackagedApp(packagedAppLocation); - await onValidated; - await onDetails; + let onValidated = waitForUpdate(win, "project-validated"); + let onDetails = waitForUpdate(win, "details"); + await winProject.projectList.importPackagedApp(packagedAppLocation); + await onValidated; + await onDetails; - let project = win.AppManager.selectedProject; + let project = win.AppManager.selectedProject; - ok("name" in project.manifest, "manifest includes name"); - is(project.name, project.manifest.name, "Display name uses manifest name"); - ok(isProjectMarkedAsValid(), "project is marked as valid"); + ok("name" in project.manifest, "manifest includes name"); + is(project.name, project.manifest.name, "Display name uses manifest name"); + ok(isProjectMarkedAsValid(), "project is marked as valid"); // Change the name - let originalName = project.manifest.name; + let originalName = project.manifest.name; - project.manifest.name = "xxx"; + project.manifest.name = "xxx"; // Write to disk - await AppManager.writeManifest(project); + await AppManager.writeManifest(project); // Read file - let manifestPath = OS.Path.join(packagedAppLocation, "manifest.webapp"); - let Decoder = new TextDecoder(); - let data = await OS.File.read(manifestPath); - data = new TextDecoder().decode(data); - let json = JSON.parse(data); - is(json.name, "xxx", "manifest written on disc"); + let manifestPath = OS.Path.join(packagedAppLocation, "manifest.webapp"); + let data = await OS.File.read(manifestPath); + data = new TextDecoder().decode(data); + let json = JSON.parse(data); + is(json.name, "xxx", "manifest written on disc"); // Make the manifest invalid on disk - delete json.name; - let Encoder = new TextEncoder(); - data = Encoder.encode(JSON.stringify(json)); - await OS.File.writeAtomic(manifestPath, data , {tmpPath: manifestPath + ".tmp"}); + delete json.name; + let Encoder = new TextEncoder(); + data = Encoder.encode(JSON.stringify(json)); + await OS.File.writeAtomic(manifestPath, data, {tmpPath: manifestPath + ".tmp"}); // Trigger validation - await AppManager.validateAndUpdateProject(AppManager.selectedProject); - await nextTick(); + await AppManager.validateAndUpdateProject(AppManager.selectedProject); + await nextTick(); - ok(!("name" in project.manifest), "manifest has been updated"); - is(project.name, "--", "Placeholder is used for display name"); - ok(!isProjectMarkedAsValid(), "project is marked as invalid"); + ok(!("name" in project.manifest), "manifest has been updated"); + is(project.name, "--", "Placeholder is used for display name"); + ok(!isProjectMarkedAsValid(), "project is marked as invalid"); // Make the manifest valid on disk - project.manifest.name = originalName; - await AppManager.writeManifest(project); + project.manifest.name = originalName; + await AppManager.writeManifest(project); // Trigger validation - await AppManager.validateAndUpdateProject(AppManager.selectedProject); - await nextTick(); + await AppManager.validateAndUpdateProject(AppManager.selectedProject); + await nextTick(); - ok("name" in project.manifest, "manifest includes name"); - is(project.name, originalName, "Display name uses original manifest name"); - ok(isProjectMarkedAsValid(), "project is marked as valid"); + ok("name" in project.manifest, "manifest includes name"); + is(project.name, originalName, "Display name uses original manifest name"); + ok(isProjectMarkedAsValid(), "project is marked as valid"); - await closeWebIDE(win); + await closeWebIDE(win); - await removeAllProjects(); + await removeAllProjects(); - SimpleTest.finish(); + SimpleTest.finish(); })(); - } + }; </script> </body> </html>
--- a/devtools/client/webide/test/test_newapp.html +++ b/devtools/client/webide/test/test_newapp.html @@ -13,17 +13,17 @@ </head> <body> <script type="application/javascript"> window.onload = function() { SimpleTest.waitForExplicitFinish(); - (async function () { + (async function() { let win = await openWebIDE(); let winProject = getProjectWindow(win); let tmpDir = FileUtils.getDir("TmpD", []); await winProject.projectList.newApp({ index: 0, name: "webideTmpApp", folder: tmpDir }); @@ -35,12 +35,12 @@ is(project.name, "webideTmpApp", "name field has been updated"); // Clean up tmpDir.remove(true); await closeWebIDE(win); await removeAllProjects(); SimpleTest.finish(); })(); - } + }; </script> </body> </html>
--- a/devtools/client/webide/test/test_runtime.html +++ b/devtools/client/webide/test/test_runtime.html @@ -16,26 +16,26 @@ <script type="application/javascript"> window.onload = function() { SimpleTest.waitForExplicitFinish(); let win; SimpleTest.registerCleanupFunction(() => { - (async function () { + (async function() { if (win) { await closeWebIDE(win); } DebuggerServer.destroy(); await removeAllProjects(); })(); }); - (async function () { + (async function() { function isPlayActive() { return !win.document.querySelector("#cmd_play").hasAttribute("disabled"); } function isStopActive() { return !win.document.querySelector("#cmd_stop").hasAttribute("disabled"); } @@ -160,17 +160,17 @@ Services.prefs.setIntPref("devtools.webide.busyTimeout", 100); // Click the infinite runtime items[1].click(); ok(win.document.querySelector("window").className, "busy", "UI is busy"); // Wait for error message since connection never completes - let errorDeferred = new Promise(resolve => { + await new Promise(resolve => { win.UI.reportError = errorName => { if (errorName === "error_operationTimeout") { resolve(); } }; }); // Click the prolonged runtime @@ -185,14 +185,16 @@ } }; setTimeout(() => { resolve(); }, 1000); }); + await noErrorDeferred; + SimpleTest.finish(); })(); - } + }; </script> </body> </html>
--- a/devtools/client/webide/test/test_toolbox.html +++ b/devtools/client/webide/test/test_toolbox.html @@ -16,46 +16,48 @@ <script type="application/javascript"> window.onload = function() { SimpleTest.waitForExplicitFinish(); let win; SimpleTest.registerCleanupFunction(() => { - (async function () { + (async function() { if (win) { await closeWebIDE(win); } DebuggerServer.destroy(); await removeAllProjects(); })(); }); - (async function () { + (async function() { DebuggerServer.init(); DebuggerServer.registerAllActors(); win = await openWebIDE(); let docRuntime = getRuntimeDocument(win); let docProject = getProjectDocument(win); win.AppManager.update("runtime-list"); let deferred = new Promise(resolve => { - win.AppManager.connection.once( + win.AppManager.connection.once( win.Connection.Events.CONNECTED, resolve); }); docRuntime.querySelectorAll(".runtime-panel-item-other")[1].click(); ok(win.document.querySelector("window").className, "busy", "UI is busy"); await win.UI._busyPromise; + await deferred; + is(Object.keys(DebuggerServer._connections).length, 1, "Connected"); await waitForUpdate(win, "runtime-global-actors"); ok(win.AppManager.isMainProcessDebuggable(), "Main process available"); // Select main process SimpleTest.executeSoon(() => { @@ -81,12 +83,12 @@ await win.UI.destroyToolbox(); ok(!win.UI.toolboxPromise, "Toolbox promise is also nullified the second times"); await win.Cmds.disconnectRuntime(); SimpleTest.finish(); })(); - } + }; </script> </body> </html>
--- a/devtools/client/webide/test/test_zoom.html +++ b/devtools/client/webide/test/test_zoom.html @@ -13,65 +13,65 @@ </head> <body> <script type="application/javascript"> window.onload = function() { SimpleTest.waitForExplicitFinish(); - (async function () { - let win = await openWebIDE(); - let viewer = win.QueryInterface(Ci.nsIInterfaceRequestor) + (async function() { + let win = await openWebIDE(); + let viewer = win.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIWebNavigation) .QueryInterface(Ci.nsIDocShell) .contentViewer; - win.Cmds.zoomOut(); - win.Cmds.zoomOut(); - win.Cmds.zoomOut(); - win.Cmds.zoomOut(); - win.Cmds.zoomOut(); - win.Cmds.zoomOut(); - win.Cmds.zoomOut(); + win.Cmds.zoomOut(); + win.Cmds.zoomOut(); + win.Cmds.zoomOut(); + win.Cmds.zoomOut(); + win.Cmds.zoomOut(); + win.Cmds.zoomOut(); + win.Cmds.zoomOut(); - let roundZoom = Math.round(10 * viewer.fullZoom) / 10; - is(roundZoom, 0.6, "Reach min zoom"); + let roundZoom = Math.round(10 * viewer.fullZoom) / 10; + is(roundZoom, 0.6, "Reach min zoom"); - win.Cmds.zoomIn(); - win.Cmds.zoomIn(); - win.Cmds.zoomIn(); - win.Cmds.zoomIn(); - win.Cmds.zoomIn(); - win.Cmds.zoomIn(); - win.Cmds.zoomIn(); - win.Cmds.zoomIn(); - win.Cmds.zoomIn(); - win.Cmds.zoomIn(); - win.Cmds.zoomIn(); - win.Cmds.zoomIn(); + win.Cmds.zoomIn(); + win.Cmds.zoomIn(); + win.Cmds.zoomIn(); + win.Cmds.zoomIn(); + win.Cmds.zoomIn(); + win.Cmds.zoomIn(); + win.Cmds.zoomIn(); + win.Cmds.zoomIn(); + win.Cmds.zoomIn(); + win.Cmds.zoomIn(); + win.Cmds.zoomIn(); + win.Cmds.zoomIn(); - roundZoom = Math.round(10 * viewer.fullZoom) / 10; - is(roundZoom, 1.4, "Reach max zoom"); + roundZoom = Math.round(10 * viewer.fullZoom) / 10; + is(roundZoom, 1.4, "Reach max zoom"); - await closeWebIDE(win); + await closeWebIDE(win); - win = await openWebIDE(); - viewer = win.QueryInterface(Ci.nsIInterfaceRequestor) + win = await openWebIDE(); + viewer = win.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIWebNavigation) .QueryInterface(Ci.nsIDocShell) .contentViewer; - roundZoom = Math.round(10 * viewer.fullZoom) / 10; - is(roundZoom, 1.4, "Zoom restored"); + roundZoom = Math.round(10 * viewer.fullZoom) / 10; + is(roundZoom, 1.4, "Zoom restored"); - win.Cmds.resetZoom(); + win.Cmds.resetZoom(); - is(viewer.fullZoom, 1, "Zoom reset"); + is(viewer.fullZoom, 1, "Zoom reset"); - await closeWebIDE(win); + await closeWebIDE(win); - SimpleTest.finish(); + SimpleTest.finish(); })(); - } + }; </script> </body> </html>
--- a/devtools/server/main.js +++ b/devtools/server/main.js @@ -11,17 +11,16 @@ var { Ci, Cc } = require("chrome"); var Services = require("Services"); var { ActorPool, OriginalLocation, RegisteredActorFactory, ObservedActorFactory } = require("devtools/server/actors/common"); var { LocalDebuggerTransport, ChildDebuggerTransport, WorkerDebuggerTransport } = require("devtools/shared/transport/transport"); var DevToolsUtils = require("devtools/shared/DevToolsUtils"); var { dumpn } = DevToolsUtils; -var flags = require("devtools/shared/flags"); DevToolsUtils.defineLazyGetter(this, "DebuggerSocket", () => { let { DebuggerSocket } = require("devtools/shared/security/socket"); return DebuggerSocket; }); DevToolsUtils.defineLazyGetter(this, "Authentication", () => { return require("devtools/shared/security/auth"); }); @@ -34,29 +33,16 @@ DevToolsUtils.defineLazyGetter(this, "ge // Overload `Components` to prevent DevTools loader exception on Components // object usage Object.defineProperty(this, "Components", { get() { return require("chrome").components; } }); -if (isWorker) { - flags.wantLogging = true; - flags.wantVerbose = true; -} else { - const LOG_PREF = "devtools.debugger.log"; - const VERBOSE_PREF = "devtools.debugger.log.verbose"; - - flags.wantLogging = Services.prefs.getBoolPref(LOG_PREF); - flags.wantVerbose = - Services.prefs.getPrefType(VERBOSE_PREF) !== Services.prefs.PREF_INVALID && - Services.prefs.getBoolPref(VERBOSE_PREF); -} - const CONTENT_PROCESS_SERVER_STARTUP_SCRIPT = "resource://devtools/server/startup/content-process.js"; function loadSubScript(url) { try { Services.scriptloader.loadSubScript(url, this); } catch (e) { let errorStr = "Error loading: " + url + ":\n" +
--- a/devtools/server/tests/mochitest/test_inspector_getImageData-wait-for-load.html +++ b/devtools/server/tests/mochitest/test_inspector_getImageData-wait-for-load.html @@ -14,33 +14,34 @@ https://bugzilla.mozilla.org/show_bug.cg <title>Test for Bug 1192536</title> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> <script type="application/javascript" src="inspector-helpers.js"></script> <script type="application/javascript"> "use strict"; -const flags = require("devtools/shared/flags"); -const wasTesting = flags.testing; -SimpleTest.registerCleanupFunction(function() { - flags.testing = wasTesting; -}); - const PATH = "http://mochi.test:8888/chrome/devtools/server/tests/mochitest/"; const BASE_IMAGE = PATH + "inspector-delay-image-response.sjs"; const DELAYED_IMAGE = BASE_IMAGE + "?delay=300"; const TIMEOUT_IMAGE = BASE_IMAGE + "?delay=50000"; const NONEXISTENT_IMAGE = PATH + "this-does-not-exist.png"; window.onload = function() { SimpleTest.waitForExplicitFinish(); runNextTest(); }; +function pushPref(preferenceName, value) { + return new Promise(resolve => { + let options = {"set": [[preferenceName, value]]}; + SpecialPowers.pushPrefEnv(options, resolve); + }); +} + let gImg = null; let gNodeFront = null; let gWalker = null; addTest(function setup() { let url = document.getElementById("inspectorContent").href; attachURL(url, function(err, client, tab, doc) { let {InspectorFront} = require("devtools/shared/fronts/inspector"); @@ -55,45 +56,45 @@ addTest(function setup() { ok(gNodeFront, "Got the image NodeFront."); ok(gImg, "Got the image Node."); }); }).then(runNextTest)); }); }); -addTest(function testTimeout() { +addTest(async function testTimeout() { info("Testing that the method aborts if the image takes too long to load."); // imageToImageData() only times out when flags.testing is not set. - flags.testing = false; + await pushPref("devtools.testing", false); gImg.src = TIMEOUT_IMAGE; info("Calling getImageData()."); ensureRejects(gNodeFront.getImageData(), "Timeout image").then(runNextTest); }); -addTest(function testNonExistentImage() { +addTest(async function testNonExistentImage() { info("Testing that non-existent image causes a rejection."); // This test shouldn't hit the timeout. - flags.testing = true; + await pushPref("devtools.testing", true); gImg.src = NONEXISTENT_IMAGE; info("Calling getImageData()."); ensureRejects(gNodeFront.getImageData(), "Non-existent image").then(runNextTest); }); -addTest(function testDelayedImage() { +addTest(async function testDelayedImage() { info("Testing that the method waits for an image to load."); // This test shouldn't hit the timeout. - flags.testing = true; + await pushPref("devtools.testing", true); gImg.src = DELAYED_IMAGE; info("Calling getImageData()."); checkImageData(gNodeFront.getImageData()).then(runNextTest); }); addTest(function cleanup() {
--- a/devtools/server/tests/mochitest/test_inspector_getImageDataFromURL.html +++ b/devtools/server/tests/mochitest/test_inspector_getImageDataFromURL.html @@ -13,69 +13,70 @@ https://bugzilla.mozilla.org/show_bug.cg <title>Test for Bug 1192536</title> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> <script type="application/javascript" src="inspector-helpers.js"></script> <script type="application/javascript"> "use strict"; -const flags = require("devtools/shared/flags"); -const wasTesting = flags.testing; -SimpleTest.registerCleanupFunction(function() { - flags.testing = wasTesting; -}); - const PATH = "http://mochi.test:8888/chrome/devtools/server/tests/mochitest/"; const BASE_IMAGE = PATH + "inspector-delay-image-response.sjs"; const DELAYED_IMAGE = BASE_IMAGE + "?delay=300"; const TIMEOUT_IMAGE = BASE_IMAGE + "?delay=50000"; const NONEXISTENT_IMAGE = PATH + "this-does-not-exist.png"; window.onload = function() { SimpleTest.waitForExplicitFinish(); runNextTest(); }; +function pushPref(preferenceName, value) { + return new Promise(resolve => { + let options = {"set": [[preferenceName, value]]}; + SpecialPowers.pushPrefEnv(options, resolve); + }); +} + let gInspector = null; addTest(function setup() { let url = document.getElementById("inspectorContent").href; attachURL(url, function(err, client, tab, doc) { let {InspectorFront} = require("devtools/shared/fronts/inspector"); gInspector = InspectorFront(client, tab); runNextTest(); }); }); -addTest(function testTimeout() { +addTest(async function testTimeout() { info("Testing that the method aborts if the image takes too long to load."); // imageToImageData() only times out when flags.testing is not set. - flags.testing = false; + await pushPref("devtools.testing", false); ensureRejects(gInspector.getImageDataFromURL(TIMEOUT_IMAGE), "Image that loads for too long").then(runNextTest); }); -addTest(function testNonExistentImage() { +addTest(async function testNonExistentImage() { info("Testing that non-existent image causes a rejection."); // This test shouldn't hit the timeout. - flags.testing = true; + await pushPref("devtools.testing", true); ensureRejects(gInspector.getImageDataFromURL(NONEXISTENT_IMAGE), "Non-existent image").then(runNextTest); }); -addTest(function testNormalImage() { +addTest(async function testNormalImage() { info("Testing that the method waits for an image to load."); // This test shouldn't hit the timeout. - flags.testing = true; + await pushPref("devtools.testing", true); checkImageData(gInspector.getImageDataFromURL(DELAYED_IMAGE)).then(runNextTest); }); addTest(function cleanup() { gInspector = null; runNextTest(); });
--- a/devtools/shared/flags.js +++ b/devtools/shared/flags.js @@ -1,26 +1,42 @@ "use strict"; +const Services = require("Services"); + /* * Create a writable property by tracking it with a private variable. * We cannot make a normal property writeable on `exports` because * the module system freezes it. */ -function makeWritableFlag(exports, name) { - let flag = false; +function makeWritableFlag(exports, name, pref) { + let flag; + // We don't have access to pref in worker, so disable all logs by default + if (isWorker) { + flag = false; + } else { + flag = Services.prefs.getBoolPref(pref, false); + let prefObserver = () => { + flag = Services.prefs.getBoolPref(pref, false); + }; + Services.prefs.addObserver(pref, prefObserver); + + // Also listen for Loader unload to unregister the pref observer and prevent leaking + let unloadObserver = function() { + Services.prefs.removeObserver(pref, prefObserver); + Services.obs.removeObserver(unloadObserver, "devtools:loader:destroy"); + }; + Services.obs.addObserver(unloadObserver, "devtools:loader:destroy"); + } Object.defineProperty(exports, name, { get: function() { return flag; - }, - set: function(state) { - flag = state; } }); } -makeWritableFlag(exports, "wantLogging"); -makeWritableFlag(exports, "wantVerbose"); +makeWritableFlag(exports, "wantLogging", "devtools.debugger.log"); +makeWritableFlag(exports, "wantVerbose", "devtools.debugger.log.verbose"); // When the testing flag is set, various behaviors may be altered from // production mode, typically to enable easier testing or enhanced // debugging. -makeWritableFlag(exports, "testing"); +makeWritableFlag(exports, "testing", "devtools.testing");
--- a/devtools/shared/heapsnapshot/tests/unit/head_heapsnapshot.js +++ b/devtools/shared/heapsnapshot/tests/unit/head_heapsnapshot.js @@ -13,32 +13,33 @@ var CC = Components.Constructor; const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {}); const { Match } = ChromeUtils.import("resource://test/Match.jsm", {}); const { Census } = ChromeUtils.import("resource://test/Census.jsm", {}); const { addDebuggerToGlobal } = ChromeUtils.import("resource://gre/modules/jsdebugger.jsm", {}); const DevToolsUtils = require("devtools/shared/DevToolsUtils"); -const flags = require("devtools/shared/flags"); const HeapAnalysesClient = require("devtools/shared/heapsnapshot/HeapAnalysesClient"); const Services = require("Services"); const { censusReportToCensusTreeNode } = require("devtools/shared/heapsnapshot/census-tree-node"); const CensusUtils = require("devtools/shared/heapsnapshot/CensusUtils"); const DominatorTreeNode = require("devtools/shared/heapsnapshot/DominatorTreeNode"); const { deduplicatePaths } = require("devtools/shared/heapsnapshot/shortest-paths"); const { LabelAndShallowSizeVisitor } = DominatorTreeNode; // Always log packets when running tests. runxpcshelltests.py will throw // the output away anyway, unless you give it the --verbose flag. if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_DEFAULT) { Services.prefs.setBoolPref("devtools.debugger.log", true); + registerCleanupFunction(() => { + Services.prefs.clearUserPref("devtools.debugger.log"); + }); } -flags.wantLogging = true; const SYSTEM_PRINCIPAL = Cc["@mozilla.org/systemprincipal;1"] .createInstance(Ci.nsIPrincipal); function dumpn(msg) { dump("HEAPSNAPSHOT-TEST: " + msg + "\n"); }
--- a/devtools/shared/tests/unit/head_devtools.js +++ b/devtools/shared/tests/unit/head_devtools.js @@ -3,21 +3,20 @@ /* exported DevToolsUtils, DevToolsLoader */ "use strict"; const { require, DevToolsLoader } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {}); const Services = require("Services"); const DevToolsUtils = require("devtools/shared/DevToolsUtils"); -const flags = require("devtools/shared/flags"); -flags.testing = true; +Services.prefs.setBoolPref("devtools.testing", true); registerCleanupFunction(() => { - flags.testing = false; + Services.prefs.clearUserPref("devtools.testing"); }); // Register a console listener, so console messages don't just disappear // into the ether. // If for whatever reason the test needs to post console errors that aren't // failures, set this to true. var ALLOW_CONSOLE_ERRORS = false;
--- a/devtools/shared/tests/unit/test_assert.js +++ b/devtools/shared/tests/unit/test_assert.js @@ -4,19 +4,16 @@ "use strict"; // Test DevToolsUtils.assert ALLOW_CONSOLE_ERRORS = true; function run_test() { - // Enable assertions. - flags.testing = true; - const { assert } = DevToolsUtils; equal(typeof assert, "function"); try { assert(true, "this assertion should not fail"); } catch (e) { // If you catch assertion failures in practice, I will hunt you down. I get // email notifications every time it happens.
--- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -7979,17 +7979,17 @@ nsContentUtils::DataTransferItemToImage( if (!size.width || !size.height) { return NS_ERROR_FAILURE; } Shmem data = aItem.data().get_Shmem(); RefPtr<DataSourceSurface> image = CreateDataSourceSurfaceFromData(size, - static_cast<SurfaceFormat>(imageDetails.format()), + imageDetails.format(), data.get<uint8_t>(), imageDetails.stride()); RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(image, size); nsCOMPtr<imgIContainer> imageContainer = image::ImageOps::CreateFromDrawable(drawable); imageContainer.forget(aContainer); @@ -8137,17 +8137,17 @@ nsContentUtils::TransferableToIPCTransfe // Turn item->data() into an nsCString prior to accessing it. item->data() = surfaceData.ref(); IPCDataTransferImage& imageDetails = item->imageDetails(); mozilla::gfx::IntSize size = dataSurface->GetSize(); imageDetails.width() = size.width; imageDetails.height() = size.height; imageDetails.stride() = stride; - imageDetails.format() = static_cast<uint8_t>(dataSurface->GetFormat()); + imageDetails.format() = dataSurface->GetFormat(); continue; } // Otherwise, handle this as a file. nsCOMPtr<BlobImpl> blobImpl; nsCOMPtr<nsIFile> file = do_QueryInterface(data); if (file) {
--- a/dom/ipc/DOMTypes.ipdlh +++ b/dom/ipc/DOMTypes.ipdlh @@ -23,16 +23,18 @@ using DesktopToLayoutDeviceScale from "U using CSSToLayoutDeviceScale from "Units.h"; using CSSRect from "Units.h"; using CSSSize from "Units.h"; using mozilla::LayoutDeviceIntPoint from "Units.h"; using mozilla::dom::ScreenOrientationInternal from "mozilla/dom/ScreenOrientation.h"; using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h"; using mozilla::layers::CompositorOptions from "mozilla/layers/CompositorOptions.h"; using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h"; +using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h"; + namespace mozilla { namespace dom { struct MessagePortIdentifier { nsID uuid; nsID destinationUuid; @@ -61,17 +63,17 @@ union IPCDataTransferData IPCBlob; // files }; struct IPCDataTransferImage { uint32_t width; uint32_t height; uint32_t stride; - uint8_t format; + SurfaceFormat format; }; struct IPCDataTransferItem { nsCString flavor; // The image details are only used when transferring images. IPCDataTransferImage imageDetails; IPCDataTransferData data;
--- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -28,16 +28,17 @@ include URIParams; include PPrintingTypes; include PTabContext; include "mozilla/GfxMessageUtils.h"; include "mozilla/layers/LayersMessageUtils.h"; using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h"; using mozilla::gfx::Matrix from "mozilla/gfx/Matrix.h"; +using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h"; using mozilla::LayoutDeviceIntPoint from "Units.h"; using mozilla::LayoutDevicePoint from "Units.h"; using mozilla::ScreenIntPoint from "Units.h"; using ScreenIntSize from "Units.h"; using struct mozilla::layers::FrameMetrics from "FrameMetrics.h"; using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h"; using struct mozilla::layers::ZoomConstraints from "FrameMetrics.h"; using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h"; @@ -389,17 +390,17 @@ parent: * Horizontal hotspot of the image, as specified by the css cursor property. * @param hotspotY * Vertical hotspot of the image, as specified by the css cursor property. * @param force * Invalidate any locally cached cursor settings and force an * update. */ async SetCustomCursor(nsCString cursorData, uint32_t width, uint32_t height, - uint32_t stride, uint8_t format, + uint32_t stride, SurfaceFormat format, uint32_t hotspotX, uint32_t hotspotY, bool force); /** * Used to set the current text of the status tooltip. * Nowadays this is mainly used for link locations on hover. */ async SetStatus(uint32_t type, nsString status); @@ -563,17 +564,17 @@ parent: async SetDimensions(uint32_t aFlags, int32_t aX, int32_t aY, int32_t aCx, int32_t aCy); nested(inside_sync) sync DispatchWheelEvent(WidgetWheelEvent event); nested(inside_sync) sync DispatchMouseEvent(WidgetMouseEvent event); nested(inside_sync) sync DispatchKeyboardEvent(WidgetKeyboardEvent event); async InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action, OptionalShmem visualData, - uint32_t stride, uint8_t format, + uint32_t stride, SurfaceFormat format, LayoutDeviceIntRect dragRect, nsCString principalURISpec); // After a compositor reset, it is necessary to reconnect each layers ID to // the compositor of the widget that will render those layers. Note that // this is sync so we can ensure that messages to the window compositor // arrive before the TabChild attempts to use its cross-process compositor // bridge.
--- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -1738,17 +1738,17 @@ TabParent::RecvSetCursor(const nsCursor& return IPC_OK(); } mozilla::ipc::IPCResult TabParent::RecvSetCustomCursor(const nsCString& aCursorData, const uint32_t& aWidth, const uint32_t& aHeight, const uint32_t& aStride, - const uint8_t& aFormat, + const gfx::SurfaceFormat& aFormat, const uint32_t& aHotspotX, const uint32_t& aHotspotY, const bool& aForce) { mCursor = eCursorInvalid; nsCOMPtr<nsIWidget> widget = GetWidget(); if (widget) { @@ -1756,17 +1756,17 @@ TabParent::RecvSetCustomCursor(const nsC widget->ClearCachedCursor(); } if (mTabSetsCursor) { const gfx::IntSize size(aWidth, aHeight); RefPtr<gfx::DataSourceSurface> customCursor = gfx::CreateDataSourceSurfaceFromData(size, - static_cast<gfx::SurfaceFormat>(aFormat), + aFormat, reinterpret_cast<const uint8_t*>(aCursorData.BeginReading()), aStride); RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(customCursor, size); nsCOMPtr<imgIContainer> cursorImage(image::ImageOps::CreateFromDrawable(drawable)); widget->SetCursor(cursorImage, aHotspotX, aHotspotY); mCustomCursor = cursorImage; mCustomCursorHotspotX = aHotspotX; @@ -3290,17 +3290,17 @@ TabParent::RecvAsyncAuthPrompt(const nsC } return IPC_OK(); } mozilla::ipc::IPCResult TabParent::RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers, const uint32_t& aAction, const OptionalShmem& aVisualDnDData, - const uint32_t& aStride, const uint8_t& aFormat, + const uint32_t& aStride, const gfx::SurfaceFormat& aFormat, const LayoutDeviceIntRect& aDragRect, const nsCString& aPrincipalURISpec) { mInitialDataTransferItems.Clear(); nsIPresShell* shell = mFrameElement->OwnerDoc()->GetShell(); if (!shell) { if (Manager()->IsContentParent()) { Unused << Manager()->AsContentParent()->SendEndDragSession(true, true, @@ -3327,17 +3327,17 @@ TabParent::RecvInvokeDragSession(nsTArra if (aVisualDnDData.type() == OptionalShmem::Tvoid_t || !aVisualDnDData.get_Shmem().IsReadable() || aVisualDnDData.get_Shmem().Size<char>() < aDragRect.height * aStride) { mDnDVisualization = nullptr; } else { mDnDVisualization = gfx::CreateDataSourceSurfaceFromData(gfx::IntSize(aDragRect.width, aDragRect.height), - static_cast<gfx::SurfaceFormat>(aFormat), + aFormat, aVisualDnDData.get_Shmem().get<uint8_t>(), aStride); } mDragValid = true; mDragRect = aDragRect; mDragPrincipalURISpec = aPrincipalURISpec;
--- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -275,17 +275,17 @@ public: virtual mozilla::ipc::IPCResult RecvSetCursor(const nsCursor& aValue, const bool& aForce) override; virtual mozilla::ipc::IPCResult RecvSetCustomCursor(const nsCString& aUri, const uint32_t& aWidth, const uint32_t& aHeight, const uint32_t& aStride, - const uint8_t& aFormat, + const gfx::SurfaceFormat& aFormat, const uint32_t& aHotspotX, const uint32_t& aHotspotY, const bool& aForce) override; virtual mozilla::ipc::IPCResult RecvSetStatus(const uint32_t& aType, const nsString& aStatus) override; virtual mozilla::ipc::IPCResult RecvIsParentWindowMainWidgetVisible(bool* aIsVisible) override; @@ -566,17 +566,17 @@ public: const bool& aRunInGlobalScope); void LayerTreeUpdate(uint64_t aEpoch, bool aActive); virtual mozilla::ipc::IPCResult RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers, const uint32_t& aAction, const OptionalShmem& aVisualDnDData, - const uint32_t& aStride, const uint8_t& aFormat, + const uint32_t& aStride, const gfx::SurfaceFormat& aFormat, const LayoutDeviceIntRect& aDragRect, const nsCString& aPrincipalURISpec) override; void AddInitialDnDDataTo(DataTransfer* aDataTransfer, nsACString& aPrincipalURISpec); bool TakeDragVisualization(RefPtr<mozilla::gfx::SourceSurface>& aSurface, LayoutDeviceIntRect* aDragRect);
--- a/dom/quota/test/browser_permissionsPrompt.html +++ b/dom/quota/test/browser_permissionsPrompt.html @@ -8,18 +8,17 @@ <title>Persistent-Storage Permission Prompt Test</title> <script type="text/javascript"> function* testSteps() { SpecialPowers.pushPrefEnv({ "set": [["dom.storageManager.enabled", true], ["dom.storageManager.prompt.testing", false], - ["dom.storageManager.prompt.testing.allow", false], - ["browser.storageManager.enabled", true]] + ["dom.storageManager.prompt.testing.allow", false]] }, continueToNextStep); yield undefined; navigator.storage.persist().then(result => { testGenerator.next(result); }); testResult = yield undefined;
--- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -8359,19 +8359,24 @@ nsDisplayTransform::ShouldPrerenderTrans // Only prerender if the transformed frame's size is <= a multiple of the // reference frame size (~viewport), and less than an absolute limit. // Both the ratio and the absolute limit are configurable. nsSize relativeLimit(nscoord(refSize.width * viewportRatioX), nscoord(refSize.height * viewportRatioY)); nsSize absoluteLimit(aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitX), aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitY)); nsSize maxSize = Min(relativeLimit, absoluteLimit); - gfxSize scale = nsLayoutUtils::GetTransformToAncestorScale(aFrame); - nsSize frameSize(overflow.Size().width * scale.width, - overflow.Size().height * scale.height); + + const auto transform = nsLayoutUtils::GetTransformToAncestor(aFrame, + nsLayoutUtils::GetDisplayRootFrame(aFrame)); + const gfxRect transformedBounds = transform.TransformAndClipBounds( + gfxRect(overflow.x, overflow.y, overflow.width, overflow.height), + gfxRect::MaxIntRect()); + const nsSize frameSize = nsSize(transformedBounds.width, transformedBounds.height); + uint64_t maxLimitArea = uint64_t(maxSize.width) * maxSize.height; uint64_t frameArea = uint64_t(frameSize.width) * frameSize.height; if (frameArea <= maxLimitArea && frameSize <= absoluteLimit) { *aDirtyRect = overflow; return FullPrerender; } else if (gfxPrefs::PartiallyPrerenderAnimatedContent()) { *aDirtyRect = nsLayoutUtils::ComputePartialPrerenderArea(*aDirtyRect, overflow, maxSize); return PartialPrerender;
--- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -5771,19 +5771,17 @@ pref("security.block_ftp_subresources", #if !defined(MOZ_WIDGET_ANDROID) pref("dom.storageManager.enabled", true); #else pref("dom.storageManager.enabled", false); #endif pref("dom.storageManager.prompt.testing", false); pref("dom.storageManager.prompt.testing.allow", false); -// Enable the Storage management in about:preferences and persistent-storage permission request -// To enable the DOM implementation, turn on "dom.storageManager.enabled" -pref("browser.storageManager.enabled", true); + pref("browser.storageManager.pressureNotification.minIntervalMS", 1200000); pref("browser.storageManager.pressureNotification.usageThresholdGB", 5); // When a user cancels this number of authentication dialogs coming from // a single web page in a row, all following authentication dialogs will // be blocked (automatically canceled) for that page. The counter resets // when the page is reloaded. To turn this feature off, just set the limit to 0. pref("prompts.authentication_dialog_abuse_limit", 3);
--- a/testing/marionette/doc/Testing.md +++ b/testing/marionette/doc/Testing.md @@ -1,111 +1,136 @@ -# Running Tests +Running tests +============= -We verify and test Marionette in a couple of different ways. -While for the server component XPCShell tests are used, the -client and harness packages both use Python. +We verify and test Marionette in a couple of different ways. The +server uses a combination of xpcshell unit tests and functional +Python-based tests written with the Marionette harness and WPT; the +client is tested with the same harness; and the Marionette test +harness uses a collection of Python tests. -For debugging test failures which are happing in CI, a one-click loaner from -[Taskcluster](Taskcluster.html) can be used. +Additionally, for debugging hard-to-reproduce test failures in CI, +a one-click loaner from [Taskcluster] can be used. + +[TaskCluster]: Taskcluster.html + -## Setting up the tests +Setup +----- -Marionette-based tests can be run in-tree with a locally built -or downloaded Firefox through `mach`, as well as with out-of-tree -tests with `marionette`. +Marionette-based tests can be run through `mach` with a local +checkout of _central_ and a build of Firefox, as well as out-of-tree +with the `marionette` test harness. Running in-tree tests, `mach` will automatically manage the Python virtual environment in which your tests are run. The Marionette client that is picked up is the one that is in-tree at _testing/marionette/client_. If you want to run tests from a downloaded test archive, you will -need to download the `target.common.tests.zip` artifact as attached to -Treeherder [build jobs] `B` for your system. Extract that file and set up -the Python Marionette client and harness by executing the following -command: +need to download the `target.common.tests.zip` artifact as attached +to Treeherder [build jobs] `B` for your system. Extract that file +and set up the Python Marionette client and harness by executing +the following command in a virtual environment: - % pip install -r config/marionette_requirements.txt + % pip install -r config/marionette_requirements.txt The tests can then be found under -*marionette/tests/testing/marionette/harness/marionette_harness/tests* and -can be executed with the command `marionette`. It supports the same options as -described below for `mach`. +_marionette/tests/testing/marionette/harness/marionette_harness/tests_ and +can be executed with the command `marionette`. It supports the +same options as described below for `mach`. [build jobs]: https://treeherder.mozilla.org/#/jobs?repo=mozilla-central&filter-searchStr=build -## XPCShell tests +xpcshell unit tests +------------------- Marionette has a set of [xpcshell] unit tests located in _testing/marionette/test*.js_. These can be run this way: - % ./mach test testing/marionette/test_*.js + % ./mach test testing/marionette/test/unit Because tests are run in parallel and xpcshell itself is quite chatty, it can sometimes be useful to run the tests sequentially: - % ./mach test --sequential testing/marionette/test_error.js + % ./mach test --sequential testing/marionette/test/unit/test_error.js These unit tests run as part of the `X` jobs on Treeherder. [xpcshell]: https://developer.mozilla.org/en-US/docs/Mozilla/QA/Writing_xpcshell-based_unit_tests -## Python functional tests +Marionette functional tests +--------------------------- We also have a set of functional tests that make use of the Marionette Python client. These start a Firefox process and tests the Marionette -protocol input and output. The following command will run all tests: +protocol input and output. The following command will run all +tests: - % ./mach marionette-test + % ./mach marionette test But you can also run individual tests: - % ./mach marionette-test testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py + % ./mach marionette test testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py -In case you want to run the tests with eg. a Nightly build of Firefox, -mach let you do this with the --binary option: +In case you want to run the tests with another Firefox binary: - % ./mach marionette-test --binary=/path/to/firefox-executable TEST + % ./mach marionette test --binary /path/to/firefox TEST -When working on Marionette code it is often useful to surface the -stdout from Firefox, which can be achived with the `--gecko-log` option. -See [Debugging](Debugging.html) for usage instructions. +When working on Marionette it is often useful to surface the stdout +from Firefox, which can be achived using the `--gecko-log` option. +See [Debugging] for usage instructions. As these are functional integration tests and pop up Firefox windows -sporadically, a helpful tip is to surpress the window whilst you +sporadically, a helpful tip is to suppress the window whilst you are running them by using Firefox’ [headless mode]: - % ./mach marionette-test -z TEST + % ./mach marionette test -z TEST `-z` is an alias for `--headless` and equivalent to setting the -`MOZ_HEADLESS` output variable. In addition to `MOZ_HEADLESS` -there is also `MOZ_HEADLESS_WIDTH` and `MOZ_HEADLESS_HEIGHT` for -controlling the dimensions of the no-op virtual display. This is -similar to using xvfb(1) which you may know from the X windowing system, -but has the additional benefit of also working on macOS and Windows. +`MOZ_HEADLESS` output variable. In addition to `MOZ_HEADLESS` there +is also `MOZ_HEADLESS_WIDTH` and `MOZ_HEADLESS_HEIGHT` for controlling +the dimensions of the no-op virtual display. This is similar to +using xvfb(1) which you may know from the X windowing system, but +has the additional benefit of also working on macOS and Windows. These functional tests will run as part of the `Mn` job on Treeherder. In addition to these two test types that specifically test the Marionette protocol, Marionette is used as the backend for the [geckodriver] WebDriver implementation. It is served by a WPT test suite which effectively tests conformance to the W3C specification. +[Debugging]: Debugging.html [headless mode]: https://developer.mozilla.org/en-US/Firefox/Headless_mode [geckodriver]: ../geckodriver/README.md -## Python harness tests +WPT functional tests +-------------------- + +Marionette is also indirectly tested through [geckodriver] with +WPT. WPT tests for WebDriver turn up with the `Wd` try job and can +be run this way: + + % ./mach wpt testing/web-platform/tests/webdriver/tests + +This command supports a `--webdriver-arg '-vv'` argument which +enables more detailed logging, as well as `--jsdebugger` for opening +the Browser Toolbox. + + +Harness tests +------------- The Marionette harness Python package has a set of unit tests, which -are written by using the [pytest] framework. The following command will -run all tests: +are written by using the [pytest] framework. The following command +will run all tests: - % ./mach python-test testing/marionette/ + % ./mach python-test testing/marionette/ To run a specific test specify the full path to the module: - % ./mach python-test testing/marionette/harness/marionette_harness/tests/harness_unit/test_serve.py + % ./mach python-test testing/marionette/harness/marionette_harness/tests/harness_unit/test_serve.py [pytest]: https://docs.pytest.org/en/latest/
--- a/testing/marionette/moz.build +++ b/testing/marionette/moz.build @@ -4,17 +4,17 @@ DIRS += ["components"] JAR_MANIFESTS += ["jar.mn"] JS_PREFERENCE_FILES += ["prefs/marionette.js"] MARIONETTE_UNIT_MANIFESTS += ["harness/marionette_harness/tests/unit/unit-tests.ini"] MARIONETTE_WEBAPI_MANIFESTS += ["harness/marionette_harness/tests/webapi-tests.ini"] -XPCSHELL_TESTS_MANIFESTS += ["unit.ini"] +XPCSHELL_TESTS_MANIFESTS += ["test/unit/xpcshell.ini"] with Files("**"): BUG_COMPONENT = ("Testing", "Marionette") with Files("harness/**"): SCHEDULES.exclusive = ["marionette", "firefox-ui"] SPHINX_TREES["marionette"] = "doc"
new file mode 100644 --- /dev/null +++ b/testing/marionette/test/unit/.eslintrc.js @@ -0,0 +1,13 @@ +"use strict"; + +module.exports = { + "extends": ["plugin:mozilla/xpcshell-test"], + "rules": { + "camelcase": "off", + "max-len": ["error", 100, { + "ignoreStrings": true, + "ignoreTemplateLiterals": true, + "ignoreUrls": true, + }], + }, +};
new file mode 100644 --- /dev/null +++ b/testing/marionette/test/unit/README.md @@ -0,0 +1,14 @@ +To run the tests in this directory, from the top source directory, +either invoke the test despatcher in mach: + + % ./mach test testing/marionette/test/unit + +Or call out the harness specifically: + + % ./mach xpcshell-test testing/marionette/test/unit + +The latter gives you the `--sequential` option which can be useful +when debugging to prevent tests from running in parallel. + +When adding new tests you must make sure they are listed in +xpcshell.ini, otherwise they will not run on try.
rename from testing/marionette/test_action.js rename to testing/marionette/test/unit/test_action.js --- a/testing/marionette/test_action.js +++ b/testing/marionette/test/unit/test_action.js @@ -1,17 +1,16 @@ /* 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"; ChromeUtils.import("chrome://marionette/content/action.js"); -const {ContentWebElement} = ChromeUtils.import("chrome://marionette/content/element.js", {}); -ChromeUtils.import("chrome://marionette/content/error.js"); +const {InvalidArgumentError} = ChromeUtils.import("chrome://marionette/content/error.js", {}); const XHTMLNS = "http://www.w3.org/1999/xhtml"; const domEl = { nodeType: 1, ELEMENT_NODE: 1, namespaceURI: XHTMLNS, }; @@ -41,19 +40,19 @@ add_test(function test_processPointerPar let check = (regex, message, arg) => checkErrors( regex, action.PointerParameters.fromJSON, [arg], message); let parametersData; for (let d of ["foo", "", "get", "Get"]) { parametersData = {pointerType: d}; let message = `parametersData: [pointerType: ${parametersData.pointerType}]`; check(/Unknown pointerType/, message, parametersData); } - parametersData.pointerType = "mouse"; //TODO "pen"; + parametersData.pointerType = "mouse"; // TODO "pen"; deepEqual(action.PointerParameters.fromJSON(parametersData), - {pointerType: "mouse"}); //TODO action.PointerType.Pen}); + {pointerType: "mouse"}); // TODO action.PointerType.Pen});