diff --git a/.eslintignore b/.eslintignore index ee5e8e09ed36..57b01695e3cd 100644 --- a/.eslintignore +++ b/.eslintignore @@ -84,7 +84,6 @@ browser/extensions/loop/** # devtools/ exclusions devtools/*.js -devtools/client/*.js devtools/client/aboutdebugging/** devtools/client/animationinspector/** devtools/client/canvasdebugger/** diff --git a/addon-sdk/source/lib/toolkit/loader.js b/addon-sdk/source/lib/toolkit/loader.js index 152fa32fd00c..6fd98422bae8 100644 --- a/addon-sdk/source/lib/toolkit/loader.js +++ b/addon-sdk/source/lib/toolkit/loader.js @@ -302,7 +302,7 @@ const load = iced(function load(loader, module) { let sandbox; if (loader.sharedGlobalSandbox && - loader.sharedGlobalBlacklist.indexOf(module.id) == -1) { + loader.sharedGlobalBlocklist.indexOf(module.id) == -1) { // Create a new object in this sandbox, that will be used as // the scope object for this particular module sandbox = new loader.sharedGlobalSandbox.Object(); @@ -810,9 +810,12 @@ Loader.unload = unload; // If `resolve` does not returns `uri` string exception will be thrown by // an associated `require` call. function Loader(options) { + if (options.sharedGlobalBlacklist && !options.sharedGlobalBlocklist) { + options.sharedGlobalBlocklist = options.sharedGlobalBlacklist; + } let { modules, globals, resolve, paths, rootURI, manifest, requireMap, isNative, - metadata, sharedGlobal, sharedGlobalBlacklist, checkCompatibility, waiveIntereposition + metadata, sharedGlobal, sharedGlobalBlocklist, checkCompatibility, waiveIntereposition } = override({ paths: {}, modules: {}, @@ -832,7 +835,7 @@ function Loader(options) { // Make the returned resolve function have the same signature (id, requirer) => Loader.nodeResolve(id, requirer, { rootURI: rootURI }) : Loader.resolve, - sharedGlobalBlacklist: ["sdk/indexed-db"], + sharedGlobalBlocklist: ["sdk/indexed-db"], waiveIntereposition: false }, options); @@ -923,7 +926,8 @@ function Loader(options) { modules: { enumerable: false, value: modules }, metadata: { enumerable: false, value: metadata }, sharedGlobalSandbox: { enumerable: false, value: sharedGlobalSandbox }, - sharedGlobalBlacklist: { enumerable: false, value: sharedGlobalBlacklist }, + sharedGlobalBlocklist: { enumerable: false, value: sharedGlobalBlocklist }, + sharedGlobalBlacklist: { enumerable: false, value: sharedGlobalBlocklist }, // Map of module sandboxes indexed by module URIs. sandboxes: { enumerable: false, value: {} }, resolve: { enumerable: false, value: resolve }, diff --git a/addon-sdk/source/test/test-loader.js b/addon-sdk/source/test/test-loader.js index ca70dbfdb1d7..8111d2a9cae4 100644 --- a/addon-sdk/source/test/test-loader.js +++ b/addon-sdk/source/test/test-loader.js @@ -349,12 +349,41 @@ exports['test console global by default'] = function (assert) { }; exports['test shared globals'] = function(assert) { + let uri = root + '/fixtures/loader/cycles/'; + let loader = Loader({ paths: { '': uri }, sharedGlobal: true, + sharedGlobalBlocklist: ['b'] }); + + let program = main(loader, 'main'); + + // As it is hard to verify what is the global of an object + // (due to wrappers) we check that we see the `foo` symbol + // being manually injected into the shared global object + loader.sharedGlobalSandbox.foo = true; + + let m = loader.sandboxes[uri + 'main.js']; + let a = loader.sandboxes[uri + 'a.js']; + let b = loader.sandboxes[uri + 'b.js']; + + assert.ok(Cu.getGlobalForObject(m).foo, "main is shared"); + assert.ok(Cu.getGlobalForObject(a).foo, "a is shared"); + assert.ok(!Cu.getGlobalForObject(b).foo, "b isn't shared"); + + unload(loader); +} + +exports['test deprecated shared globals exception name'] = function(assert) { let uri = root + '/fixtures/loader/cycles/'; let loader = Loader({ paths: { '': uri }, sharedGlobal: true, sharedGlobalBlacklist: ['b'] }); let program = main(loader, 'main'); + assert.ok(loader.sharedGlobalBlocklist.includes("b"), "b should be in the blocklist"); + assert.equal(loader.sharedGlobalBlocklist.length, loader.sharedGlobalBlacklist.length, + "both blocklists should have the same number of items."); + assert.equal(loader.sharedGlobalBlocklist.join(","), loader.sharedGlobalBlacklist.join(","), + "both blocklists should have the same items."); + // As it is hard to verify what is the global of an object // (due to wrappers) we check that we see the `foo` symbol // being manually injected into the shared global object diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index fdad091790df..b6bfff53abec 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1589,6 +1589,9 @@ pref("browser.defaultbrowser.notificationbar", false); // the hang monitor. pref("dom.ipc.cpow.timeout", 500); +// Causes access on unsafe CPOWs from browser code to throw by default. +pref("dom.ipc.cpows.forbid-unsafe-from-browser", true); + // Enable e10s hang monitoring (slow script checking and plugin hang // detection). pref("dom.ipc.processHangMonitor", true); diff --git a/browser/base/content/baseMenuOverlay.xul b/browser/base/content/baseMenuOverlay.xul index 40207123b9a3..da74ca077c0c 100644 --- a/browser/base/content/baseMenuOverlay.xul +++ b/browser/base/content/baseMenuOverlay.xul @@ -61,7 +61,7 @@ onclick="checkForMiddleClick(this, event);" label="&helpKeyboardShortcuts.label;" accesskey="&helpKeyboardShortcuts.accesskey;"/> -#ifdef MOZ_SERVICES_HEALTHREPORT +#ifdef MOZ_TELEMETRY_REPORTING - + PopupNotifications.getNotification(aName), - () => { - ok(!!PopupNotifications.getNotification(aName), - aName + " notification appeared"); - - deferred.resolve(); - }, "timeout waiting for popup notification " + aName); - - return deferred.promise; -} - -function promiseNoPopupNotification(aName) { - let deferred = Promise.defer(); - info("Waiting for " + aName + " to be removed"); - - waitForCondition(() => !PopupNotifications.getNotification(aName), - () => { - ok(!PopupNotifications.getNotification(aName), - aName + " notification removed"); - deferred.resolve(); - }, "timeout waiting for popup notification " + aName + " to disappear"); - - return deferred.promise; -} - -function expectObserverCalled(aTopic) { - is(gObservedTopics[aTopic], 1, "expected notification " + aTopic); - if (aTopic in gObservedTopics) - --gObservedTopics[aTopic]; -} - -function expectNoObserverCalled() { - for (let topic in gObservedTopics) { - if (gObservedTopics[topic]) - is(gObservedTopics[topic], 0, topic + " notification unexpected"); - } - gObservedTopics = {}; -} - -function promiseMessage(aMessage, aAction) { - let deferred = Promise.defer(); - - content.addEventListener("message", function messageListener(event) { - content.removeEventListener("message", messageListener); - is(event.data, aMessage, "received " + aMessage); - if (event.data == aMessage) - deferred.resolve(); - else - deferred.reject(); - }); - - if (aAction) - aAction(); - - return deferred.promise; -} - -function getMediaCaptureState() { - let hasVideo = {}; - let hasAudio = {}; - let hasScreenShare = {}; - let hasWindowShare = {}; - MediaManagerService.mediaCaptureWindowState(content, hasVideo, hasAudio, - hasScreenShare, hasWindowShare); - if (hasVideo.value && hasAudio.value) - return "CameraAndMicrophone"; - if (hasVideo.value) - return "Camera"; - if (hasAudio.value) - return "Microphone"; - if (hasScreenShare) - return "Screen"; - if (hasWindowShare) - return "Window"; - return "none"; -} - -function* closeStream(aAlreadyClosed) { - expectNoObserverCalled(); - - info("closing the stream"); - content.wrappedJSObject.closeStream(); - - if (!aAlreadyClosed) - yield promiseObserverCalled("recording-device-events"); - - yield promiseNoPopupNotification("webRTC-sharingDevices"); - if (!aAlreadyClosed) - expectObserverCalled("recording-window-ended"); - - yield* assertWebRTCIndicatorStatus(null); -} - function loadPage(aUrl) { let deferred = Promise.defer(); @@ -214,6 +75,7 @@ fakeLoopAboutModule.prototype = { getURIFlags: function (aURI) { return Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT | Ci.nsIAboutModule.ALLOW_SCRIPT | + Ci.nsIAboutModule.URI_CAN_LOAD_IN_CHILD | Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT; } }; @@ -221,14 +83,12 @@ fakeLoopAboutModule.prototype = { var factory = XPCOMUtils._getFactory(fakeLoopAboutModule); var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); -var originalLoopCsp = Services.prefs.getCharPref(PREF_LOOP_CSP); +var classIDLoopconversation, classIDEvil; + registerCleanupFunction(function() { gBrowser.removeCurrentTab(); - kObservedTopics.forEach(topic => { - Services.obs.removeObserver(observer, topic); - }); - Services.prefs.clearUserPref(PREF_PERMISSION_FAKE); - Services.prefs.setCharPref(PREF_LOOP_CSP, originalLoopCsp); + registrar.unregisterFactory(classIDLoopconversation, factory); + registrar.unregisterFactory(classIDEvil, factory); }); const permissionError = "error: SecurityError: The operation is insecure."; @@ -238,31 +98,27 @@ var gTests = [ { desc: "getUserMedia about:loopconversation shouldn't prompt", run: function checkAudioVideoLoop() { - Services.prefs.setCharPref(PREF_LOOP_CSP, "default-src 'unsafe-inline'"); - - let classID = Cc["@mozilla.org/uuid-generator;1"] - .getService(Ci.nsIUUIDGenerator).generateUUID(); - registrar.registerFactory(classID, "", - "@mozilla.org/network/protocol/about;1?what=loopconversation", - factory); + yield new Promise(resolve => SpecialPowers.pushPrefEnv({ + "set": [[PREF_LOOP_CSP, "default-src 'unsafe-inline'"]], + }, resolve)); yield loadPage("about:loopconversation"); - yield promiseObserverCalled("recording-device-events", () => { - info("requesting devices"); - content.wrappedJSObject.requestDevice(true, true); - }); + info("requesting devices"); + let promise = promiseObserverCalled("recording-device-events"); + yield promiseRequestDevice(true, true); + yield promise; + // Wait for the devices to actually be captured and running before // proceeding. yield promisePopupNotification("webRTC-sharingDevices"); - is(getMediaCaptureState(), "CameraAndMicrophone", + is((yield getMediaCaptureState()), "CameraAndMicrophone", "expected camera and microphone to be shared"); yield closeStream(); - registrar.unregisterFactory(classID, factory); - Services.prefs.setCharPref(PREF_LOOP_CSP, originalLoopCsp); + yield new Promise((resolve) => SpecialPowers.popPrefEnv(resolve)); } }, @@ -273,59 +129,45 @@ var gTests = [ return; } - Services.prefs.setCharPref(PREF_LOOP_CSP, "default-src 'unsafe-inline'"); - - let classID = Cc["@mozilla.org/uuid-generator;1"] - .getService(Ci.nsIUUIDGenerator).generateUUID(); - registrar.registerFactory(classID, "", - "@mozilla.org/network/protocol/about;1?what=loopconversation", - factory); + yield new Promise(resolve => SpecialPowers.pushPrefEnv({ + "set": [[PREF_LOOP_CSP, "default-src 'unsafe-inline'"]], + }, resolve)); yield loadPage("about:loopconversation"); - yield promiseObserverCalled("getUserMedia:request", () => { - info("requesting screen"); - content.wrappedJSObject.requestDevice(false, true, "window"); - }); + info("requesting screen"); + let promise = promiseObserverCalled("getUserMedia:request"); + yield promiseRequestDevice(false, true, null, "window"); + // Wait for the devices to actually be captured and running before // proceeding. yield promisePopupNotification("webRTC-shareDevices"); - isnot(getMediaCaptureState(), "Window", + is((yield getMediaCaptureState()), "none", "expected camera and microphone not to be shared"); yield promiseMessage(permissionError, () => { PopupNotifications.panel.firstChild.button.click(); }); - expectObserverCalled("getUserMedia:response:deny"); - expectObserverCalled("recording-window-ended"); + yield expectObserverCalled("getUserMedia:response:deny"); + yield expectObserverCalled("recording-window-ended"); - registrar.unregisterFactory(classID, factory); - Services.prefs.setCharPref(PREF_LOOP_CSP, originalLoopCsp); + yield new Promise((resolve) => SpecialPowers.popPrefEnv(resolve)); } }, { desc: "getUserMedia about:evil should prompt", run: function checkAudioVideoNonLoop() { - let classID = Cc["@mozilla.org/uuid-generator;1"] - .getService(Ci.nsIUUIDGenerator).generateUUID(); - registrar.registerFactory(classID, "", - "@mozilla.org/network/protocol/about;1?what=evil", - factory); - yield loadPage("about:evil"); - yield promiseObserverCalled("getUserMedia:request", () => { - info("requesting devices"); - content.wrappedJSObject.requestDevice(true, true); - }); + let promise = promiseObserverCalled("getUserMedia:request"); + yield promiseRequestDevice(true, true); + yield promise; - isnot(getMediaCaptureState(), "CameraAndMicrophone", + is((yield getMediaCaptureState()), "none", "expected camera and microphone not to be shared"); - - registrar.unregisterFactory(classID, factory); } }, @@ -334,18 +176,29 @@ var gTests = [ function test() { waitForExplicitFinish(); - Services.prefs.setBoolPref(PREF_PERMISSION_FAKE, true); - // Ensure this is always true - Services.prefs.setBoolPref("media.getusermedia.screensharing.enabled", true); - gTab = gBrowser.addTab(); gBrowser.selectedTab = gTab; - kObservedTopics.forEach(topic => { - Services.obs.addObserver(observer, topic, false); - }); + gTab.linkedBrowser.messageManager.loadFrameScript(CONTENT_SCRIPT_HELPER, true); + + classIDLoopconversation = Cc["@mozilla.org/uuid-generator;1"] + .getService(Ci.nsIUUIDGenerator).generateUUID(); + registrar.registerFactory(classIDLoopconversation, "", + "@mozilla.org/network/protocol/about;1?what=loopconversation", + factory); + + classIDEvil = Cc["@mozilla.org/uuid-generator;1"] + .getService(Ci.nsIUUIDGenerator).generateUUID(); + registrar.registerFactory(classIDEvil, "", + "@mozilla.org/network/protocol/about;1?what=evil", + factory); Task.spawn(function () { + yield new Promise(resolve => SpecialPowers.pushPrefEnv({ + "set": [[PREF_PERMISSION_FAKE, true], + ["media.getusermedia.screensharing.enabled", true]], + }, resolve)); + for (let test of gTests) { info(test.desc); yield test.run(); @@ -358,9 +211,3 @@ function test() { finish(); }); } - -function wait(time) { - let deferred = Promise.defer(); - setTimeout(deferred.resolve, time); - return deferred.promise; -} diff --git a/browser/base/content/test/general/get_user_media_content_script.js b/browser/base/content/test/general/get_user_media_content_script.js index ad68e31ec47d..7786fe00cb32 100644 --- a/browser/base/content/test/general/get_user_media_content_script.js +++ b/browser/base/content/test/general/get_user_media_content_script.js @@ -49,13 +49,20 @@ addMessageListener("Test:ExpectNoObserverCalled", data => { function _getMediaCaptureState() { let hasVideo = {}; let hasAudio = {}; - MediaManagerService.mediaCaptureWindowState(content, hasVideo, hasAudio); + let hasScreenShare = {}; + let hasWindowShare = {}; + MediaManagerService.mediaCaptureWindowState(content, hasVideo, hasAudio, + hasScreenShare, hasWindowShare); if (hasVideo.value && hasAudio.value) return "CameraAndMicrophone"; if (hasVideo.value) return "Camera"; if (hasAudio.value) return "Microphone"; + if (hasScreenShare.value) + return "Screen"; + if (hasWindowShare.value) + return "Window"; return "none"; } diff --git a/browser/base/content/test/general/get_user_media_helpers.js b/browser/base/content/test/general/get_user_media_helpers.js index b8992eb583f1..8983d8f92573 100644 --- a/browser/base/content/test/general/get_user_media_helpers.js +++ b/browser/base/content/test/general/get_user_media_helpers.js @@ -161,15 +161,15 @@ function getMediaCaptureState() { }); } -function promiseRequestDevice(aRequestAudio, aRequestVideo, aFrameId) { +function promiseRequestDevice(aRequestAudio, aRequestVideo, aFrameId, aType) { info("requesting devices"); return ContentTask.spawn(gBrowser.selectedBrowser, - {aRequestAudio, aRequestVideo, aFrameId}, + {aRequestAudio, aRequestVideo, aFrameId, aType}, function*(args) { let global = content.wrappedJSObject; if (args.aFrameId) global = global.document.getElementById(args.aFrameId).contentWindow; - global.requestDevice(args.aRequestAudio, args.aRequestVideo); + global.requestDevice(args.aRequestAudio, args.aRequestVideo, args.aType); }); } diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index 515f04d83042..4e7201e63b1f 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -535,7 +535,7 @@ BrowserGlue.prototype = { this._isPlacesShutdownObserver = true; os.addObserver(this, "handle-xul-text-link", false); os.addObserver(this, "profile-before-change", false); - if (AppConstants.MOZ_SERVICES_HEALTHREPORT) { + if (AppConstants.MOZ_TELEMETRY_REPORTING) { os.addObserver(this, "keyword-search", false); } os.addObserver(this, "browser-search-engine-modified", false); @@ -594,7 +594,7 @@ BrowserGlue.prototype = { os.removeObserver(this, "places-shutdown"); os.removeObserver(this, "handle-xul-text-link"); os.removeObserver(this, "profile-before-change"); - if (AppConstants.MOZ_SERVICES_HEALTHREPORT) { + if (AppConstants.MOZ_TELEMETRY_REPORTING) { os.removeObserver(this, "keyword-search"); } os.removeObserver(this, "browser-search-engine-modified"); @@ -1141,19 +1141,24 @@ BrowserGlue.prototype = { // For any add-ons that were installed disabled and can be enabled offer // them to the user. - let changedIDs = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED); - if (changedIDs.length > 0) { - let win = RecentWindow.getMostRecentBrowserWindow(); - AddonManager.getAddonsByIDs(changedIDs, function(aAddons) { - aAddons.forEach(function(aAddon) { - // If the add-on isn't user disabled or can't be enabled then skip it. - if (!aAddon.userDisabled || !(aAddon.permissions & AddonManager.PERM_CAN_ENABLE)) - return; + let win = RecentWindow.getMostRecentBrowserWindow(); + AddonManager.getAllAddons(addons => { + for (let addon of addons) { + // If this add-on has already seen (or seen is undefined for non-XPI + // add-ons) then skip it. + if (addon.seen !== false) { + continue; + } - win.openUILinkIn("about:newaddon?id=" + aAddon.id, "tab"); - }) - }); - } + // If this add-on cannot be enabled (either already enabled or + // appDisabled) then skip it. + if (!(addon.permissions & AddonManager.PERM_CAN_ENABLE)) { + continue; + } + + win.openUILinkIn("about:newaddon?id=" + addon.id, "tab"); + } + }); let signingRequired; if (AppConstants.MOZ_REQUIRE_SIGNING) { diff --git a/browser/components/places/tests/browser/head.js b/browser/components/places/tests/browser/head.js index 0876dbd784f4..67023db2866c 100644 --- a/browser/components/places/tests/browser/head.js +++ b/browser/components/places/tests/browser/head.js @@ -350,7 +350,9 @@ var withBookmarksDialog = Task.async(function* (autoCancel, openFn, taskFn) { * * @param selector * Valid selector syntax - * @return the target DOM node. + * @return Promise + * Returns a Promise that resolves once the context menu has been + * opened. */ var openContextMenuForContentSelector = Task.async(function* (browser, selector) { info("wait for the context menu"); @@ -372,8 +374,6 @@ var openContextMenuForContentSelector = Task.async(function* (browser, selector) 1, 0, false, 0, 0, true); }); yield contextPromise; - - return gContextMenuContentData.popupNode; }); /** diff --git a/browser/components/preferences/in-content/advanced.js b/browser/components/preferences/in-content/advanced.js index 9affcb79da0f..cfa42dc2dec0 100644 --- a/browser/components/preferences/in-content/advanced.js +++ b/browser/components/preferences/in-content/advanced.js @@ -44,7 +44,7 @@ var gAdvancedPane = { this.initSubmitCrashes(); #endif this.initTelemetry(); -#ifdef MOZ_SERVICES_HEALTHREPORT +#ifdef MOZ_TELEMETRY_REPORTING this.initSubmitHealthReport(); #endif this.updateOnScreenKeyboardVisibility(); @@ -56,7 +56,7 @@ var gAdvancedPane = { gAdvancedPane.updateHardwareAcceleration); setEventListener("advancedPrefs", "select", gAdvancedPane.tabSelectionChanged); -#ifdef MOZ_SERVICES_HEALTHREPORT +#ifdef MOZ_TELEMETRY_REPORTING setEventListener("submitHealthReportBox", "command", gAdvancedPane.updateSubmitHealthReport); #endif @@ -289,7 +289,7 @@ var gAdvancedPane = { #endif }, -#ifdef MOZ_SERVICES_HEALTHREPORT +#ifdef MOZ_TELEMETRY_REPORTING /** * Initialize the health report service reference and checkbox. */ diff --git a/browser/components/preferences/in-content/advanced.xul b/browser/components/preferences/in-content/advanced.xul index ce3640d67027..3db14d216487 100644 --- a/browser/components/preferences/in-content/advanced.xul +++ b/browser/components/preferences/in-content/advanced.xul @@ -199,7 +199,7 @@ #ifdef MOZ_DATA_REPORTING -#ifdef MOZ_SERVICES_HEALTHREPORT +#ifdef MOZ_TELEMETRY_REPORTING &healthReportLearnMore.label; -#ifdef MOZ_TELEMETRY_REPORTING @@ -228,7 +227,6 @@ -#endif #endif diff --git a/browser/components/sessionstore/test/browser_broadcast.js b/browser/components/sessionstore/test/browser_broadcast.js index 49dddc3a66cc..95984d6d0cf1 100644 --- a/browser/components/sessionstore/test/browser_broadcast.js +++ b/browser/components/sessionstore/test/browser_broadcast.js @@ -74,7 +74,7 @@ add_task(function flush_on_settabstate() { // Flush all data contained in the content script but send it using // asynchronous messages. - TabState.flushAsync(browser); + TabStateFlusher.flush(browser); yield promiseTabState(tab, state); @@ -101,7 +101,7 @@ add_task(function flush_on_tabclose_racy() { // Flush all data contained in the content script but send it using // asynchronous messages. - TabState.flushAsync(browser); + TabStateFlusher.flush(browser); yield promiseRemoveTab(tab); let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window)); diff --git a/browser/components/translation/test/browser_translation_bing.js b/browser/components/translation/test/browser_translation_bing.js index 69a96410e2fd..e3b9f91f41be 100644 --- a/browser/components/translation/test/browser_translation_bing.js +++ b/browser/components/translation/test/browser_translation_bing.js @@ -38,12 +38,18 @@ add_task(function* test_bing_translation() { // Translating the contents of the loaded tab. gBrowser.selectedTab = tab; let browser = tab.linkedBrowser; - let client = new BingTranslator( - new TranslationDocument(browser.contentDocument), "fr", "en"); - let result = yield client.translate(); - // XXXmikedeboer; here you would continue the test/ content inspection. - Assert.ok(result, "There should be a result."); + yield ContentTask.spawn(browser, null, function*() { + Cu.import("resource:///modules/translation/BingTranslator.jsm"); + Cu.import("resource:///modules/translation/TranslationDocument.jsm"); + + let client = new BingTranslator( + new TranslationDocument(content.document), "fr", "en"); + let result = yield client.translate(); + + // XXXmikedeboer; here you would continue the test/ content inspection. + ok(result, "There should be a result"); + }); gBrowser.removeTab(tab); }); @@ -67,18 +73,24 @@ add_task(function* test_handling_out_of_valid_key_error() { // Translating the contents of the loaded tab. gBrowser.selectedTab = tab; let browser = tab.linkedBrowser; - let client = new BingTranslator( - new TranslationDocument(browser.contentDocument), "fr", "en"); - client._resetToken(); - try { - yield client.translate(); - } catch (ex) { - // It is alright that the translation fails. - } - client._resetToken(); - // Checking if the client detected service and unavailable. - Assert.ok(client._serviceUnavailable, "Service should be detected unavailable."); + yield ContentTask.spawn(browser, null, function*() { + Cu.import("resource:///modules/translation/BingTranslator.jsm"); + Cu.import("resource:///modules/translation/TranslationDocument.jsm"); + + let client = new BingTranslator( + new TranslationDocument(content.document), "fr", "en"); + client._resetToken(); + try { + yield client.translate(); + } catch (ex) { + // It is alright that the translation fails. + } + client._resetToken(); + + // Checking if the client detected service and unavailable. + ok(client._serviceUnavailable, "Service should be detected unavailable."); + }); // Cleaning up. Services.prefs.setCharPref(kClientIdPref, "testClient"); diff --git a/browser/components/translation/test/browser_translation_yandex.js b/browser/components/translation/test/browser_translation_yandex.js index c126be04219f..9788c14a3f4e 100644 --- a/browser/components/translation/test/browser_translation_yandex.js +++ b/browser/components/translation/test/browser_translation_yandex.js @@ -10,8 +10,6 @@ const kEnginePref = "browser.translation.engine"; const kApiKeyPref = "browser.translation.yandex.apiKeyOverride"; const kShowUIPref = "browser.translation.ui.show"; -const {YandexTranslator} = Cu.import("resource:///modules/translation/YandexTranslator.jsm", {}); -const {TranslationDocument} = Cu.import("resource:///modules/translation/TranslationDocument.jsm", {}); const {Promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); const {Translation} = Cu.import("resource:///modules/translation/Translation.jsm", {}); @@ -40,11 +38,17 @@ add_task(function* test_yandex_translation() { // Translating the contents of the loaded tab. gBrowser.selectedTab = tab; let browser = tab.linkedBrowser; - let client = new YandexTranslator( - new TranslationDocument(browser.contentDocument), "fr", "en"); - let result = yield client.translate(); - Assert.ok(result, "There should be a result."); + yield ContentTask.spawn(browser, null, function*() { + Cu.import("resource:///modules/translation/TranslationDocument.jsm"); + Cu.import("resource:///modules/translation/YandexTranslator.jsm"); + + let client = new YandexTranslator( + new TranslationDocument(content.document), "fr", "en"); + let result = yield client.translate(); + + ok(result, "There should be a result."); + }); gBrowser.removeTab(tab); }); diff --git a/browser/extensions/loop/manifest.ini b/browser/extensions/loop/manifest.ini index 98e51b9dbb6a..1336ea8cfd0a 100644 --- a/browser/extensions/loop/manifest.ini +++ b/browser/extensions/loop/manifest.ini @@ -18,5 +18,4 @@ browser = true qemu = false [chrome/content/shared/test/test_shared_all.py] -skip-if = os == "win" # Bug 1149955 [chrome/content/panels/test/test_desktop_all.py] diff --git a/configure.in b/configure.in index 451f9450c2f3..728cefb6e1ce 100644 --- a/configure.in +++ b/configure.in @@ -8402,12 +8402,6 @@ if test -n "$MOZ_SERVICES_HEALTHREPORT"; then AC_DEFINE(MOZ_SERVICES_HEALTHREPORT) fi -dnl Build Services metrics component -AC_SUBST(MOZ_SERVICES_METRICS) -if test -n "$MOZ_SERVICES_METRICS"; then - AC_DEFINE(MOZ_SERVICES_METRICS) -fi - dnl Build Notifications if required AC_SUBST(MOZ_SERVICES_NOTIFICATIONS) if test -n "$MOZ_SERVICES_NOTIFICATIONS"; then diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_scripts-switching-01.js b/devtools/client/debugger/test/mochitest/browser_dbg_scripts-switching-01.js index fd43c66ea480..c4bfb1269898 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg_scripts-switching-01.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg_scripts-switching-01.js @@ -136,11 +136,11 @@ function test() { is(gEditor.getText().search(/debugger/), -1, "The second source is not displayed."); - ok(isCaretPos(gPanel, 6), + ok(isCaretPos(gPanel, 5), "Editor caret location is correct."); - is(gEditor.getDebugLocation(), 5, + is(gEditor.getDebugLocation(), 4, "Editor debugger location is correct."); - ok(gEditor.hasLineClass(5, "debug-line"), + ok(gEditor.hasLineClass(4, "debug-line"), "The debugged line is highlighted appropriately (3)."); } diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_scripts-switching-02.js b/devtools/client/debugger/test/mochitest/browser_dbg_scripts-switching-02.js index 3a9923e1c21b..0800bc930363 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg_scripts-switching-02.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg_scripts-switching-02.js @@ -131,11 +131,11 @@ function test() { "The second source is not displayed."); // The editor's debug location takes a tick to update. - ok(isCaretPos(gPanel, 6), + ok(isCaretPos(gPanel, 5), "Editor caret location is correct."); - is(gEditor.getDebugLocation(), 5, + is(gEditor.getDebugLocation(), 4, "Editor debugger location is correct."); - ok(gEditor.hasLineClass(5, "debug-line"), + ok(gEditor.hasLineClass(4, "debug-line"), "The debugged line is highlighted appropriately."); deferred.resolve(); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_split-console-keypress.js b/devtools/client/debugger/test/mochitest/browser_dbg_split-console-keypress.js index ac0f4566adc5..66c111ad8d36 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg_split-console-keypress.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg_split-console-keypress.js @@ -36,13 +36,13 @@ function test() { let stepTests = [ {key: 'VK_F11', keyRepeat: 1, caretLine: 16}, {key: 'VK_F11', keyRepeat: 2, caretLine: 18}, - {key: 'VK_F11', keyRepeat: 2, caretLine: 27}, - {key: 'VK_F10', keyRepeat: 1, caretLine: 27}, - {key: 'VK_F11', keyRepeat: 1, caretLine: 18}, - {key: 'VK_F11', keyRepeat: 5, caretLine: 32}, - {key: 'VK_F11', modifier:'Shift', keyRepeat: 1, caretLine: 29}, - {key: 'VK_F11', modifier:'Shift', keyRepeat: 2, caretLine: 34}, - {key: 'VK_F11', modifier:'Shift', keyRepeat: 2, caretLine: 34} + {key: 'VK_F11', keyRepeat: 2, caretLine: 26}, + {key: 'VK_F10', keyRepeat: 1, caretLine: 18}, + {key: 'VK_F11', keyRepeat: 1, caretLine: 19}, + {key: 'VK_F11', keyRepeat: 5, caretLine: 29}, + {key: 'VK_F11', modifier:'Shift', keyRepeat: 1, caretLine: 32}, + {key: 'VK_F11', modifier:'Shift', keyRepeat: 2, caretLine: 32}, + {key: 'VK_F11', modifier:'Shift', keyRepeat: 2, caretLine: 20} ]; // Trigger script that stops at debugger statement executeSoon(() => generateMouseClickInTab(gTab, diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_step-out.js b/devtools/client/debugger/test/mochitest/browser_dbg_step-out.js index fff8c33b4da5..bf23a3562445 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg_step-out.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg_step-out.js @@ -23,7 +23,7 @@ function test() { function testNormalReturn() { waitForSourceAndCaretAndScopes(gPanel, ".html", 17).then(() => { - waitForCaretAndScopes(gPanel, 20).then(() => { + waitForCaretAndScopes(gPanel, 19).then(() => { let innerScope = gVars.getScopeAtIndex(0); let returnVar = innerScope.get(""); diff --git a/devtools/client/definitions.js b/devtools/client/definitions.js index 8fb9030c199c..658ffda17dcd 100644 --- a/devtools/client/definitions.js +++ b/devtools/client/definitions.js @@ -4,7 +4,7 @@ "use strict"; -const {Cc, Ci, Cu} = require("chrome"); +const {Cc, Ci} = require("chrome"); const { Services } = require("resource://gre/modules/Services.jsm"); @@ -41,7 +41,7 @@ const scratchpadProps = "chrome://devtools/locale/scratchpad.properties"; const memoryProps = "chrome://devtools/locale/memory.properties"; loader.lazyGetter(this, "toolboxStrings", () => Services.strings.createBundle(toolboxProps)); -loader.lazyGetter(this, "performanceStrings",() => Services.strings.createBundle(performanceProps)); +loader.lazyGetter(this, "performanceStrings", () => Services.strings.createBundle(performanceProps)); loader.lazyGetter(this, "webConsoleStrings", () => Services.strings.createBundle(webConsoleProps)); loader.lazyGetter(this, "debuggerStrings", () => Services.strings.createBundle(debuggerProps)); loader.lazyGetter(this, "styleEditorStrings", () => Services.strings.createBundle(styleEditorProps)); @@ -71,14 +71,14 @@ Tools.options = { tooltip: l10n("optionsButton.tooltip", toolboxStrings), inMenu: false, - isTargetSupported: function(target) { + isTargetSupported: function() { return true; }, build: function(iframeWindow, toolbox) { return new OptionsPanel(iframeWindow, toolbox); } -} +}; Tools.inspector = { id: "inspector", @@ -93,7 +93,7 @@ Tools.inspector = { panelLabel: l10n("inspector.panelLabel", inspectorStrings), get tooltip() { return l10n("inspector.tooltip2", inspectorStrings, - ( osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+" ) + this.key); + (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") + this.key); }, inMenu: true, commands: [ @@ -127,20 +127,21 @@ Tools.webConsole = { panelLabel: l10n("ToolboxWebConsole.panelLabel", webConsoleStrings), get tooltip() { return l10n("ToolboxWebconsole.tooltip2", webConsoleStrings, - ( osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+" ) + this.key); + (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") + this.key); }, inMenu: true, commands: "devtools/client/webconsole/console-commands", preventClosingOnKey: true, onkey: function(panel, toolbox) { - if (toolbox.splitConsole) + if (toolbox.splitConsole) { return toolbox.focusConsoleInput(); + } panel.focusInput(); }, - isTargetSupported: function(target) { + isTargetSupported: function() { return true; }, @@ -163,12 +164,12 @@ Tools.jsdebugger = { panelLabel: l10n("ToolboxDebugger.panelLabel", debuggerStrings), get tooltip() { return l10n("ToolboxDebugger.tooltip2", debuggerStrings, - ( osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+" ) + this.key); + (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") + this.key); }, inMenu: true, commands: "devtools/client/debugger/debugger-commands", - isTargetSupported: function(target) { + isTargetSupported: function() { return true; }, @@ -241,7 +242,7 @@ Tools.canvasDebugger = { return target.hasActor("canvas") && !target.chrome; }, - build: function (iframeWindow, toolbox) { + build: function(iframeWindow, toolbox) { return new CanvasDebuggerPanel(iframeWindow, toolbox); } }; @@ -265,11 +266,11 @@ Tools.performance = { modifiers: "shift", inMenu: true, - isTargetSupported: function (target) { + isTargetSupported: function(target) { return target.hasActor("profiler"); }, - build: function (frame, target) { + build: function(frame, target) { return new PerformancePanel(frame, target); } }; @@ -286,11 +287,11 @@ Tools.memory = { panelLabel: l10n("memory.panelLabel", memoryStrings), tooltip: l10n("memory.tooltip", memoryStrings), - isTargetSupported: function (target) { + isTargetSupported: function(target) { return target.getTrait("heapSnapshots"); }, - build: function (frame, target) { + build: function(frame, target) { return new MemoryPanel(frame, target); } }; @@ -309,7 +310,7 @@ Tools.netMonitor = { panelLabel: l10n("netmonitor.panelLabel", netMonitorStrings), get tooltip() { return l10n("netmonitor.tooltip2", netMonitorStrings, - ( osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+" ) + this.key); + (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") + this.key); }, inMenu: true, @@ -343,8 +344,7 @@ Tools.storage = { isTargetSupported: function(target) { return target.isLocalTab || - ( target.hasActor("storage") && - target.getTrait("storageInspector") ); + (target.hasActor("storage") && target.getTrait("storageInspector")); }, build: function(iframeWindow, toolbox) { @@ -442,8 +442,7 @@ exports.defaultThemes = [ * The key to lookup. * @returns A localized version of the given key. */ -function l10n(name, bundle, arg) -{ +function l10n(name, bundle, arg) { try { return arg ? bundle.formatStringFromName(name, [arg], 1) : bundle.GetStringFromName(name); @@ -453,7 +452,6 @@ function l10n(name, bundle, arg) } } -function functionkey(shortkey) -{ +function functionkey(shortkey) { return shortkey.split("_")[1]; } diff --git a/devtools/client/devtools-clhandler.js b/devtools/client/devtools-clhandler.js index cfecf91db89b..593a1aec472e 100644 --- a/devtools/client/devtools-clhandler.js +++ b/devtools/client/devtools-clhandler.js @@ -2,7 +2,13 @@ * 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 { classes: Cc, interfaces: Ci, utils: Cu } = Components; +/* FIXME: remove this globals comment and replace with import-globals-from when + bug 1242893 is fixed */ +/* globals BrowserToolboxProcess */ + +"use strict"; + +const { interfaces: Ci, utils: Cu } = Components; const kDebuggerPrefs = [ "devtools.debugger.remote-enabled", "devtools.chrome.enabled" @@ -10,8 +16,8 @@ const kDebuggerPrefs = [ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm"); -function devtoolsCommandlineHandler() { -} +function devtoolsCommandlineHandler() {} + devtoolsCommandlineHandler.prototype = { handle: function(cmdLine) { let consoleFlag = cmdLine.handleFlag("jsconsole", false); @@ -31,7 +37,7 @@ devtoolsCommandlineHandler.prototype = { try { debuggerServerFlag = cmdLine.handleFlagWithParam("start-debugger-server", false); - } catch(e) { + } catch (e) { // We get an error if the option is given but not followed by a value. // By catching and trying again, the value is effectively optional. debuggerServerFlag = cmdLine.handleFlag("start-debugger-server", false); @@ -51,7 +57,8 @@ devtoolsCommandlineHandler.prototype = { let { console } = Cu.import("resource://gre/modules/Console.jsm", {}); hudservice.toggleBrowserConsole().then(null, console.error); } else { - window.focus(); // the Browser Console was already open + // the Browser Console was already open + window.focus(); } if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) { @@ -62,7 +69,8 @@ devtoolsCommandlineHandler.prototype = { // Open the toolbox on the selected tab once the browser starts up. handleDevToolsFlag: function() { Services.obs.addObserver(function onStartup(window) { - Services.obs.removeObserver(onStartup, "browser-delayed-startup-finished"); + Services.obs.removeObserver(onStartup, + "browser-delayed-startup-finished"); const {gDevTools} = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {}); const {devtools} = Cu.import("resource://devtools/shared/Loader.jsm", {}); let target = devtools.TargetFactory.forTab(window.gBrowser.selectedTab); @@ -73,14 +81,16 @@ devtoolsCommandlineHandler.prototype = { _isRemoteDebuggingEnabled() { let remoteDebuggingEnabled = false; try { - remoteDebuggingEnabled = kDebuggerPrefs.every((pref) => Services.prefs.getBoolPref(pref)); + remoteDebuggingEnabled = kDebuggerPrefs.every(pref => { + return Services.prefs.getBoolPref(pref); + }); } catch (ex) { Cu.reportError(ex); return false; } if (!remoteDebuggingEnabled) { - let errorMsg = "Could not run chrome debugger! You need the following prefs " + - "to be set to true: " + kDebuggerPrefs.join(", "); + let errorMsg = "Could not run chrome debugger! You need the following " + + "prefs to be set to true: " + kDebuggerPrefs.join(", "); Cu.reportError(errorMsg); // Dump as well, as we're doing this from a commandline, make sure people // don't miss it: @@ -131,7 +141,7 @@ devtoolsCommandlineHandler.prototype = { listener.portOrPath = portOrPath; listener.open(); dump("Started debugger server on " + portOrPath + "\n"); - } catch(e) { + } catch (e) { dump("Unable to start debugger server on " + portOrPath + ": " + e); } @@ -140,15 +150,16 @@ devtoolsCommandlineHandler.prototype = { } }, - helpInfo : " --jsconsole Open the Browser Console.\n" + - " --jsdebugger Open the Browser Toolbox.\n" + - " --devtools Open DevTools on initial load.\n" + - " --start-debugger-server [port|path] " + - "Start the debugger server on a TCP port or " + - "Unix domain socket path. Defaults to TCP port 6000.\n", + helpInfo: " --jsconsole Open the Browser Console.\n" + + " --jsdebugger Open the Browser Toolbox.\n" + + " --devtools Open DevTools on initial load.\n" + + " --start-debugger-server [port|path] " + + "Start the debugger server on a TCP port or " + + "Unix domain socket path. Defaults to TCP port 6000.\n", classID: Components.ID("{9e9a9283-0ce9-4e4a-8f1c-ba129a032c32}"), QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]), }; -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([devtoolsCommandlineHandler]); +this.NSGetFactory = XPCOMUtils.generateNSGetFactory( + [devtoolsCommandlineHandler]); diff --git a/devtools/client/framework/sidebar.js b/devtools/client/framework/sidebar.js index 7c7f25d759d2..ca551aed4526 100644 --- a/devtools/client/framework/sidebar.js +++ b/devtools/client/framework/sidebar.js @@ -264,12 +264,7 @@ ToolSidebar.prototype = { this._tabs.set(id, tab); if (selected) { - // For some reason I don't understand, if we call this.select in this - // event loop (after inserting the tab), the tab will never get the - // the "selected" attribute set to true. - this._panelDoc.defaultView.setTimeout(() => { - this.select(id); - }, 10); + this._selectTabSoon(id); } this.emit("new-tab-registered", id); @@ -291,6 +286,12 @@ ToolSidebar.prototype = { // Find an ID for this unknown tab let id = tab.getAttribute("id") || "untitled-tab-" + (this.untitledTabsIndex++); + // If the existing tab contains the tab ID prefix, extract the ID of the + // tab + if (id.startsWith(this.TAB_ID_PREFIX)) { + id = id.split(this.TAB_ID_PREFIX).pop(); + } + // Register the tab this._tabs.set(id, tab); this.emit("new-tab-registered", id); @@ -357,6 +358,18 @@ ToolSidebar.prototype = { } }, + /** + * Hack required to select a tab right after it was created. + * + * @param {String} id + * The sidebar tab id to select. + */ + _selectTabSoon: function(id) { + this._panelDoc.defaultView.setTimeout(() => { + this.select(id); + }, 0); + }, + /** * Return the id of the selected tab. */ @@ -450,13 +463,28 @@ ToolSidebar.prototype = { /** * Show the sidebar. + * + * @param {String} id + * The sidebar tab id to select. */ - show: function() { + show: function(id) { if (this._width) { this._tabbox.width = this._width; } this._tabbox.removeAttribute("hidden"); + // If an id is given, select the corresponding sidebar tab and record the + // tool opened. + if (id) { + this._currentTool = id; + + if (this._telemetry) { + this._telemetry.toolOpened(this._currentTool); + } + + this._selectTabSoon(id); + } + this.emit("show"); }, diff --git a/devtools/client/framework/toolbox-options.js b/devtools/client/framework/toolbox-options.js index 708e0bd9f6ef..c161cc63990a 100644 --- a/devtools/client/framework/toolbox-options.js +++ b/devtools/client/framework/toolbox-options.js @@ -8,6 +8,7 @@ const {Cu, Cc, Ci} = require("chrome"); const Services = require("Services"); const promise = require("promise"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "gDevTools", "resource://devtools/client/framework/gDevTools.jsm"); exports.OptionsPanel = OptionsPanel; @@ -94,32 +95,21 @@ OptionsPanel.prototype = { return this.toolbox.target; }, - open: function() { - let targetPromise; - + open: Task.async(function*() { // For local debugging we need to make the target remote. if (!this.target.isRemote) { - targetPromise = this.target.makeRemote(); - } else { - targetPromise = promise.resolve(this.target); + yield this.target.makeRemote(); } - return targetPromise.then(() => { - this.setupToolsList(); - this.setupToolbarButtonsList(); - this.setupThemeList(); - this.updateDefaultTheme(); - }).then(() => { - return this.populatePreferences(); - }).then(() => { - this.isReady = true; - this.emit("ready"); - return this; - }).then(null, function onError(aReason) { - Cu.reportError("OptionsPanel open failed. " + - aReason.error + ": " + aReason.message); - }); - }, + this.setupToolsList(); + this.setupToolbarButtonsList(); + this.setupThemeList(); + this.updateDefaultTheme(); + yield this.populatePreferences(); + this.isReady = true; + this.emit("ready"); + return this; + }), _addListeners: function() { gDevTools.on("pref-changed", this._prefChanged); diff --git a/devtools/client/inspector/computed/computed.js b/devtools/client/inspector/computed/computed.js index 42e13425cfb7..f71332450fa6 100644 --- a/devtools/client/inspector/computed/computed.js +++ b/devtools/client/inspector/computed/computed.js @@ -963,13 +963,15 @@ PropertyView.prototype = { }; this.element.addEventListener("keydown", this.onKeyDown, false); + let nameContainer = doc.createElementNS(HTML_NS, "div"); + nameContainer.className = "property-name-container"; + this.element.appendChild(nameContainer); + // Build the twisty expand/collapse this.matchedExpander = doc.createElementNS(HTML_NS, "div"); this.matchedExpander.className = "expander theme-twisty"; this.matchedExpander.addEventListener("click", this.onMatchedToggle, false); - this.element.appendChild(this.matchedExpander); - - this.focusElement = () => this.element.focus(); + nameContainer.appendChild(this.matchedExpander); // Build the style name element this.nameNode = doc.createElementNS(HTML_NS, "div"); @@ -981,7 +983,11 @@ PropertyView.prototype = { // Make it hand over the focus to the container this.onFocus = () => this.element.focus(); this.nameNode.addEventListener("click", this.onFocus, false); - this.element.appendChild(this.nameNode); + nameContainer.appendChild(this.nameNode); + + let valueContainer = doc.createElementNS(HTML_NS, "div"); + valueContainer.className = "property-value-container"; + this.element.appendChild(valueContainer); // Build the style value element this.valueNode = doc.createElementNS(HTML_NS, "div"); @@ -992,7 +998,7 @@ PropertyView.prototype = { this.valueNode.setAttribute("dir", "ltr"); // Make it hand over the focus to the container this.valueNode.addEventListener("click", this.onFocus, false); - this.element.appendChild(this.valueNode); + valueContainer.appendChild(this.valueNode); return this.element; }, diff --git a/devtools/client/inspector/computed/computed.xhtml b/devtools/client/inspector/computed/computed.xhtml deleted file mode 100644 index 80cf5651f711..000000000000 --- a/devtools/client/inspector/computed/computed.xhtml +++ /dev/null @@ -1,78 +0,0 @@ - - - - - %inspectorDTD; - - %globalDTD; - - - - - - - - - - - -]> - - - - - - &computedViewTitle; - - - - - - - - - - -
-
- - -
-
- - -
-
- - - - - - diff --git a/devtools/client/inspector/computed/test/browser_computed_cycle_color.js b/devtools/client/inspector/computed/test/browser_computed_cycle_color.js index 6e8cfdcba190..1f0bb21c776b 100644 --- a/devtools/client/inspector/computed/test/browser_computed_cycle_color.js +++ b/devtools/client/inspector/computed/test/browser_computed_cycle_color.js @@ -22,17 +22,17 @@ add_task(function*() { info("Checking the property itself"); let container = getComputedViewPropertyView(view, "color").valueNode; - checkColorCycling(container, inspector); + checkColorCycling(container, view); info("Checking matched selectors"); container = yield getComputedViewMatchedRules(view, "color"); - checkColorCycling(container, inspector); + checkColorCycling(container, view); }); -function checkColorCycling(container, inspector) { +function checkColorCycling(container, view) { let swatch = container.querySelector(".computedview-colorswatch"); let valueNode = container.querySelector(".computedview-color"); - let win = inspector.sidebar.getWindowForTab("computedview"); + let win = view.styleWindow; // "Authored" (default; currently the computed value) is(valueNode.textContent, "rgb(255, 0, 0)", diff --git a/devtools/client/inspector/computed/test/browser_computed_search-filter_context-menu.js b/devtools/client/inspector/computed/test/browser_computed_search-filter_context-menu.js index 8e1794134f49..b7cfa6aa1ee4 100644 --- a/devtools/client/inspector/computed/test/browser_computed_search-filter_context-menu.js +++ b/devtools/client/inspector/computed/test/browser_computed_search-filter_context-menu.js @@ -37,8 +37,8 @@ add_task(function*() { is(cmdUndo.getAttribute("disabled"), "true", "cmdUndo is disabled"); is(cmdDelete.getAttribute("disabled"), "true", "cmdDelete is disabled"); is(cmdSelectAll.getAttribute("disabled"), "", "cmdSelectAll is enabled"); - is(cmdCut.getAttribute("disabled"), "", "cmdCut is enabled"); - is(cmdCopy.getAttribute("disabled"), "", "cmdCopy is enabled"); + is(cmdCut.getAttribute("disabled"), "true", "cmdCut is disabled"); + is(cmdCopy.getAttribute("disabled"), "true", "cmdCopy is disabled"); is(cmdPaste.getAttribute("disabled"), "true", "cmdPaste is disabled"); info("Closing context menu"); diff --git a/devtools/client/inspector/computed/test/browser_computed_select-and-copy-styles.js b/devtools/client/inspector/computed/test/browser_computed_select-and-copy-styles.js index 322d032c3e29..9875c4ce674f 100644 --- a/devtools/client/inspector/computed/test/browser_computed_select-and-copy-styles.js +++ b/devtools/client/inspector/computed/test/browser_computed_select-and-copy-styles.js @@ -53,7 +53,7 @@ function* checkCopySelection(view) { let range = contentDocument.createRange(); range.setStart(props[1], 0); - range.setEnd(props[3], 3); + range.setEnd(props[3], 2); contentDocument.defaultView.getSelection().addRange(range); info("Checking that cssHtmlTree.siBoundCopy() returns the correct " + diff --git a/devtools/client/inspector/computed/test/head.js b/devtools/client/inspector/computed/test/head.js index 6a6c2607839e..9302d16c56b4 100644 --- a/devtools/client/inspector/computed/test/head.js +++ b/devtools/client/inspector/computed/test/head.js @@ -19,11 +19,11 @@ registerCleanupFunction(() => { * view is visible and ready */ function openComputedView() { - return openInspectorSidebarTab("computedview").then(objects => { + return openInspectorSidebarTab("computedview").then(({toolbox, inspector}) => { return { - toolbox: objects.toolbox, - inspector: objects.inspector, - view: objects.view.view + toolbox, + inspector, + view: inspector.computedview.view }; }); } diff --git a/devtools/client/inspector/fonts/fonts.js b/devtools/client/inspector/fonts/fonts.js index 4b1889e8267b..c5531dfab98a 100644 --- a/devtools/client/inspector/fonts/fonts.js +++ b/devtools/client/inspector/fonts/fonts.js @@ -6,7 +6,10 @@ "use strict"; -var { utils: Cu } = Components; +const {Cu} = require("chrome"); +const {setTimeout, clearTimeout} = + Cu.import("resource://gre/modules/Timer.jsm", {}); + const DEFAULT_PREVIEW_TEXT = "Abc"; const PREVIEW_UPDATE_DELAY = 150; @@ -16,8 +19,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "Task", XPCOMUtils.defineLazyModuleGetter(this, "console", "resource://gre/modules/Console.jsm"); -function FontInspector(inspector, window) -{ +function FontInspector(inspector, window) { this.inspector = inspector; this.pageStyle = this.inspector.pageStyle; this.chromeDoc = window.document; @@ -32,10 +34,11 @@ FontInspector.prototype = { this.inspector.selection.on("new-node", this.onNewNode); this.inspector.sidebar.on("fontinspector-selected", this.onNewNode); this.showAll = this.showAll.bind(this); - this.showAllButton = this.chromeDoc.getElementById("showall"); + this.showAllButton = this.chromeDoc.getElementById("font-showall"); this.showAllButton.addEventListener("click", this.showAll); this.previewTextChanged = this.previewTextChanged.bind(this); - this.previewInput = this.chromeDoc.getElementById("preview-text-input"); + this.previewInput = + this.chromeDoc.getElementById("font-preview-text-input"); this.previewInput.addEventListener("input", this.previewTextChanged); // Listen for theme changes as the color of the previews depend on the theme @@ -123,7 +126,8 @@ FontInspector.prototype = { * Hide the font list. No node are selected. */ dim: function() { - this.chromeDoc.body.classList.add("dim"); + let panel = this.chromeDoc.getElementById("sidebar-panel-fontinspector"); + panel.classList.add("dim"); this.clear(); }, @@ -131,7 +135,8 @@ FontInspector.prototype = { * Show the font list. A node is selected. */ undim: function() { - this.chromeDoc.body.classList.remove("dim"); + let panel = this.chromeDoc.getElementById("sidebar-panel-fontinspector"); + panel.classList.remove("dim"); }, /** @@ -146,12 +151,13 @@ FontInspector.prototype = { */ update: Task.async(function*(showAllFonts) { let node = this.inspector.selection.nodeFront; + let panel = this.chromeDoc.getElementById("sidebar-panel-fontinspector"); if (!node || !this.isActive() || !this.inspector.selection.isConnected() || !this.inspector.selection.isElementNode() || - this.chromeDoc.body.classList.contains("dim")) { + panel.classList.contains("dim")) { return; } @@ -205,7 +211,7 @@ FontInspector.prototype = { * Display the information of one font. */ render: function(font) { - let s = this.chromeDoc.querySelector("#template > section"); + let s = this.chromeDoc.querySelector("#font-template > section"); s = s.cloneNode(true); s.querySelector(".font-name").textContent = font.name; @@ -247,12 +253,4 @@ FontInspector.prototype = { }, }; -window.setPanel = function(panel) { - window.fontInspector = new FontInspector(panel, window); -}; - -window.onunload = function() { - if (window.fontInspector) { - window.fontInspector.destroy(); - } -}; +exports.FontInspector = FontInspector; diff --git a/devtools/client/inspector/fonts/fonts.xhtml b/devtools/client/inspector/fonts/fonts.xhtml deleted file mode 100644 index 48aa42556179..000000000000 --- a/devtools/client/inspector/fonts/fonts.xhtml +++ /dev/null @@ -1,52 +0,0 @@ - - - - %fontinspectorDTD; -]> - - - - &fontInspectorTitle; - - - - -
-
- -
-
-
-
    - -
    -
    -
    -
    - -
    -
    -

    - &system; - &remote; -

    - - -

    -

    &usedAs; ""

    -
    
    -        
    -
    -
    - - diff --git a/devtools/client/inspector/fonts/test/browser_fontinspector.js b/devtools/client/inspector/fonts/test/browser_fontinspector.js index 99362568cfb2..7852cbcafa9a 100644 --- a/devtools/client/inspector/fonts/test/browser_fontinspector.js +++ b/devtools/client/inspector/fonts/test/browser_fontinspector.js @@ -82,7 +82,7 @@ function* testShowAllFonts(inspector, viewDoc) { info("testing showing all fonts"); let updated = inspector.once("fontinspector-updated"); - viewDoc.querySelector("#showall").click(); + viewDoc.querySelector("#font-showall").click(); yield updated; // shouldn't change the node selection diff --git a/devtools/client/inspector/fonts/test/browser_fontinspector_edit-previews-show-all.js b/devtools/client/inspector/fonts/test/browser_fontinspector_edit-previews-show-all.js index 6413b4253ef3..6c4cba2393a0 100644 --- a/devtools/client/inspector/fonts/test/browser_fontinspector_edit-previews-show-all.js +++ b/devtools/client/inspector/fonts/test/browser_fontinspector_edit-previews-show-all.js @@ -21,7 +21,7 @@ add_task(function*() { let onUpdated = inspector.once("fontinspector-updated"); info("Clicking 'Select all' button."); - viewDoc.getElementById("showall").click(); + viewDoc.getElementById("font-showall").click(); info("Waiting for font-inspector to update."); yield onUpdated; diff --git a/devtools/client/inspector/fonts/test/head.js b/devtools/client/inspector/fonts/test/head.js index b09cd96ba909..26bbdf8aed86 100644 --- a/devtools/client/inspector/fonts/test/head.js +++ b/devtools/client/inspector/fonts/test/head.js @@ -36,7 +36,7 @@ var openFontInspectorForURL = Task.async(function*(url) { return { toolbox, inspector, - view: inspector.sidebar.getWindowForTab("fontinspector").fontInspector + view: inspector.fontInspector }; }); @@ -51,7 +51,7 @@ function* updatePreviewText(view, text) { info(`Changing the preview text to '${text}'`); let doc = view.chromeDoc; - let input = doc.getElementById("preview-text-input"); + let input = doc.getElementById("font-preview-text-input"); let update = view.inspector.once("fontinspector-updated"); info("Focusing the input field."); diff --git a/devtools/client/inspector/inspector-panel.js b/devtools/client/inspector/inspector-panel.js index f6ce1439c0e1..40e43c124160 100644 --- a/devtools/client/inspector/inspector-panel.js +++ b/devtools/client/inspector/inspector-panel.js @@ -4,7 +4,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -const {Cc, Ci, Cu, Cr} = require("chrome"); +"use strict"; + +const {Cc, Ci, Cu} = require("chrome"); Cu.import("resource://gre/modules/Services.jsm"); @@ -19,6 +21,10 @@ loader.lazyGetter(this, "MarkupView", () => require("devtools/client/inspector/m loader.lazyGetter(this, "HTMLBreadcrumbs", () => require("devtools/client/inspector/breadcrumbs").HTMLBreadcrumbs); loader.lazyGetter(this, "ToolSidebar", () => require("devtools/client/framework/sidebar").ToolSidebar); loader.lazyGetter(this, "InspectorSearch", () => require("devtools/client/inspector/inspector-search").InspectorSearch); +loader.lazyGetter(this, "RuleViewTool", () => require("devtools/client/inspector/rules/rules").RuleViewTool); +loader.lazyGetter(this, "ComputedViewTool", () => require("devtools/client/inspector/computed/computed").ComputedViewTool); +loader.lazyGetter(this, "FontInspector", () => require("devtools/client/inspector/fonts/fonts").FontInspector); +loader.lazyGetter(this, "LayoutView", () => require("devtools/client/inspector/layout/layout").LayoutView); loader.lazyGetter(this, "strings", () => { return Services.strings.createBundle("chrome://devtools/locale/inspector.properties"); @@ -352,23 +358,16 @@ InspectorPanel.prototype = { this.sidebar.on("select", this._setDefaultSidebar); - this.sidebar.addTab("ruleview", - "chrome://devtools/content/inspector/rules/rules.xhtml", - "ruleview" == defaultTab); + this.ruleview = new RuleViewTool(this, this.panelWin); + this.computedview = new ComputedViewTool(this, this.panelWin); - this.sidebar.addTab("computedview", - "chrome://devtools/content/inspector/computed/computed.xhtml", - "computedview" == defaultTab); - - if (Services.prefs.getBoolPref("devtools.fontinspector.enabled") && this.canGetUsedFontFaces) { - this.sidebar.addTab("fontinspector", - "chrome://devtools/content/inspector/fonts/fonts.xhtml", - "fontinspector" == defaultTab); + if (Services.prefs.getBoolPref("devtools.fontinspector.enabled") && + this.canGetUsedFontFaces) { + this.fontInspector = new FontInspector(this, this.panelWin); + this.panelDoc.getElementById("sidebar-tab-fontinspector").hidden = false; } - this.sidebar.addTab("layoutview", - "chrome://devtools/content/inspector/layout/layout.xhtml", - "layoutview" == defaultTab); + this.layoutview = new LayoutView(this, this.panelWin); if (this.target.form.animationsActor) { this.sidebar.addTab("animationinspector", @@ -376,7 +375,7 @@ InspectorPanel.prototype = { "animationinspector" == defaultTab); } - this.sidebar.show(); + this.sidebar.show(defaultTab); this.setupSidebarToggle(); }, @@ -586,6 +585,22 @@ InspectorPanel.prototype = { this._toolbox.off("select", this.updateDebuggerPausedWarning); this._toolbox.off("host-changed", this.onToolboxHostChanged); + if (this.ruleview) { + this.ruleview.destroy(); + } + + if (this.computedview) { + this.computedview.destroy(); + } + + if (this.fontInspector) { + this.fontInspector.destroy(); + } + + if (this.layoutview) { + this.layoutview.destroy(); + } + this.sidebar.off("select", this._setDefaultSidebar); let sidebarDestroyer = this.sidebar.destroy(); this.sidebar = null; diff --git a/devtools/client/inspector/inspector.xul b/devtools/client/inspector/inspector.xul index 8c616751d3a3..842c45d33316 100644 --- a/devtools/client/inspector/inspector.xul +++ b/devtools/client/inspector/inspector.xul @@ -8,12 +8,20 @@ + + + + + - %inspectorDTD; + %inspectorDTD; + %styleinspectorDTD; + %fontinspectorDTD; + %layoutviewDTD; ]> - + - - - - - - - - - -
    - - &margin.tooltip; -
    - &border.tooltip; -
    - &padding.tooltip; -
    -
    -
    -
    -
    -
    - -

    -

    -

    -

    - -

    -

    -

    -

    - -

    -

    -

    -

    - -

    - -
    - -
    -

    -
    - - diff --git a/devtools/client/inspector/layout/test/browser_layout.js b/devtools/client/inspector/layout/test/browser_layout.js index d744706297c2..950fdd508d85 100644 --- a/devtools/client/inspector/layout/test/browser_layout.js +++ b/devtools/client/inspector/layout/test/browser_layout.js @@ -9,42 +9,126 @@ // Expected values: var res1 = [ - {selector: "#element-size", value: "160" + "\u00D7" + "160.117"}, - {selector: ".size > span", value: "100" + "\u00D7" + "100.117"}, - {selector: ".margin.top > span", value: 30}, - {selector: ".margin.left > span", value: "auto"}, - {selector: ".margin.bottom > span", value: 30}, - {selector: ".margin.right > span", value: "auto"}, - {selector: ".padding.top > span", value: 20}, - {selector: ".padding.left > span", value: 20}, - {selector: ".padding.bottom > span", value: 20}, - {selector: ".padding.right > span", value: 20}, - {selector: ".border.top > span", value: 10}, - {selector: ".border.left > span", value: 10}, - {selector: ".border.bottom > span", value: 10}, - {selector: ".border.right > span", value: 10}, + { + selector: "#layout-element-size", + value: "160" + "\u00D7" + "160.117" + }, + { + selector: ".layout-size > span", + value: "100" + "\u00D7" + "100.117" + }, + { + selector: ".layout-margin.layout-top > span", + value: 30 + }, + { + selector: ".layout-margin.layout-left > span", + value: "auto" + }, + { + selector: ".layout-margin.layout-bottom > span", + value: 30 + }, + { + selector: ".layout-margin.layout-right > span", + value: "auto" + }, + { + selector: ".layout-padding.layout-top > span", + value: 20 + }, + { + selector: ".layout-padding.layout-left > span", + value: 20 + }, + { + selector: ".layout-padding.layout-bottom > span", + value: 20 + }, + { + selector: ".layout-padding.layout-right > span", + value: 20 + }, + { + selector: ".layout-border.layout-top > span", + value: 10 + }, + { + selector: ".layout-border.layout-left > span", + value: 10 + }, + { + selector: ".layout-border.layout-bottom > span", + value: 10 + }, + { + selector: ".layout-border.layout-right > span", + value: 10 + }, ]; var res2 = [ - {selector: "#element-size", value: "190" + "\u00D7" + "210"}, - {selector: ".size > span", value: "100" + "\u00D7" + "150"}, - {selector: ".margin.top > span", value: 30}, - {selector: ".margin.left > span", value: "auto"}, - {selector: ".margin.bottom > span", value: 30}, - {selector: ".margin.right > span", value: "auto"}, - {selector: ".padding.top > span", value: 20}, - {selector: ".padding.left > span", value: 20}, - {selector: ".padding.bottom > span", value: 20}, - {selector: ".padding.right > span", value: 50}, - {selector: ".border.top > span", value: 10}, - {selector: ".border.left > span", value: 10}, - {selector: ".border.bottom > span", value: 10}, - {selector: ".border.right > span", value: 10}, + { + selector: "#layout-element-size", + value: "190" + "\u00D7" + "210" + }, + { + selector: ".layout-size > span", + value: "100" + "\u00D7" + "150" + }, + { + selector: ".layout-margin.layout-top > span", + value: 30 + }, + { + selector: ".layout-margin.layout-left > span", + value: "auto" + }, + { + selector: ".layout-margin.layout-bottom > span", + value: 30 + }, + { + selector: ".layout-margin.layout-right > span", + value: "auto" + }, + { + selector: ".layout-padding.layout-top > span", + value: 20 + }, + { + selector: ".layout-padding.layout-left > span", + value: 20 + }, + { + selector: ".layout-padding.layout-bottom > span", + value: 20 + }, + { + selector: ".layout-padding.layout-right > span", + value: 50 + }, + { + selector: ".layout-border.layout-top > span", + value: 10 + }, + { + selector: ".layout-border.layout-left > span", + value: 10 + }, + { + selector: ".layout-border.layout-bottom > span", + value: 10 + }, + { + selector: ".layout-border.layout-right > span", + value: 10 + }, ]; add_task(function*() { let style = "div { position: absolute; top: 42px; left: 42px; height: 100.111px; width: 100px; border: 10px solid black; padding: 20px; margin: 30px auto;}"; - let html = "
    " + let html = "
    "; yield addTab("data:text/html," + encodeURIComponent(html)); let {toolbox, inspector, view} = yield openLayoutView(); diff --git a/devtools/client/inspector/layout/test/browser_layout_editablemodel.js b/devtools/client/inspector/layout/test/browser_layout_editablemodel.js index 6e1423d5646a..15163284e3a0 100644 --- a/devtools/client/inspector/layout/test/browser_layout_editablemodel.js +++ b/devtools/client/inspector/layout/test/browser_layout_editablemodel.js @@ -34,7 +34,7 @@ function*(inspector, view) { is(getStyle(node, "margin-top"), "", "Should be no margin-top on the element.") yield selectNode("#div1", inspector); - let span = view.doc.querySelector(".margin.top > span"); + let span = view.doc.querySelector(".layout-margin.layout-top > span"); is(span.textContent, 5, "Should have the right value in the box model."); EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); @@ -60,7 +60,7 @@ function*(inspector, view) { is(getStyle(node, "margin-left"), "", "Should be no margin-top on the element.") yield selectNode("#div1", inspector); - let span = view.doc.querySelector(".margin.left > span"); + let span = view.doc.querySelector(".layout-margin.layout-left > span"); is(span.textContent, 10, "Should have the right value in the box model."); EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); @@ -97,7 +97,7 @@ function*(inspector, view) { is(getStyle(node, "margin-left"), "20px", "Should be the right margin-top on the element.") yield selectNode("#div1", inspector); - let span = view.doc.querySelector(".margin.left > span"); + let span = view.doc.querySelector(".layout-margin.layout-left > span"); is(span.textContent, 20, "Should have the right value in the box model."); EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); @@ -127,7 +127,7 @@ function*(inspector, view) { yield selectNode("#div1", inspector); - let span = view.doc.querySelector(".margin.right > span"); + let span = view.doc.querySelector(".layout-margin.layout-right > span"); is(span.textContent, 15, "Should have the right value in the box model."); EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); @@ -153,7 +153,7 @@ function*(inspector, view) { yield selectNode("#div4", inspector); - let span = view.doc.querySelector(".margin.top > span"); + let span = view.doc.querySelector(".layout-margin.layout-top > span"); is(span.textContent, 1, "Should have the right value in the box model."); EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); diff --git a/devtools/client/inspector/layout/test/browser_layout_editablemodel_allproperties.js b/devtools/client/inspector/layout/test/browser_layout_editablemodel_allproperties.js index d0ce7459770c..6b0566d15bfb 100644 --- a/devtools/client/inspector/layout/test/browser_layout_editablemodel_allproperties.js +++ b/devtools/client/inspector/layout/test/browser_layout_editablemodel_allproperties.js @@ -34,7 +34,7 @@ function*(inspector, view) { yield selectNode("#div1", inspector); - let span = view.doc.querySelector(".padding.bottom > span"); + let span = view.doc.querySelector(".layout-padding.layout-bottom > span"); is(span.textContent, 5, "Should have the right value in the box model."); EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); @@ -63,7 +63,7 @@ function*(inspector, view) { yield selectNode("#div1", inspector); - let span = view.doc.querySelector(".padding.left > span"); + let span = view.doc.querySelector(".layout-padding.layout-left > span"); is(span.textContent, 5, "Should have the right value in the box model."); EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); @@ -92,7 +92,7 @@ function*(inspector, view) { yield selectNode("#div1", inspector); - let span = view.doc.querySelector(".padding.left > span"); + let span = view.doc.querySelector(".layout-padding.layout-left > span"); is(span.textContent, 5, "Should have the right value in the box model."); EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); @@ -121,7 +121,7 @@ function*(inspector, view) { yield selectNode("#div1", inspector); - let span = view.doc.querySelector(".padding.left > span"); + let span = view.doc.querySelector(".layout-padding.layout-left > span"); is(span.textContent, 5, "Should have the right value in the box model."); EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); diff --git a/devtools/client/inspector/layout/test/browser_layout_editablemodel_border.js b/devtools/client/inspector/layout/test/browser_layout_editablemodel_border.js index 0433890929e2..fbcaefbe1116 100644 --- a/devtools/client/inspector/layout/test/browser_layout_editablemodel_border.js +++ b/devtools/client/inspector/layout/test/browser_layout_editablemodel_border.js @@ -27,7 +27,7 @@ add_task(function*() { is(getStyle(node, "border-top-style"), "", "Should have the right border"); yield selectNode("#div1", inspector); - let span = view.doc.querySelector(".border.top > span"); + let span = view.doc.querySelector(".layout-border.layout-top > span"); is(span.textContent, 0, "Should have the right value in the box model."); EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); diff --git a/devtools/client/inspector/layout/test/browser_layout_editablemodel_stylerules.js b/devtools/client/inspector/layout/test/browser_layout_editablemodel_stylerules.js index 68ab5e073d5e..e8e6d843549d 100644 --- a/devtools/client/inspector/layout/test/browser_layout_editablemodel_stylerules.js +++ b/devtools/client/inspector/layout/test/browser_layout_editablemodel_stylerules.js @@ -32,7 +32,7 @@ function*(inspector, view) { is(getStyle(node, "padding-top"), "", "Should have the right padding"); yield selectNode("#div1", inspector); - let span = view.doc.querySelector(".padding.top > span"); + let span = view.doc.querySelector(".layout-padding.layout-top > span"); is(span.textContent, 3, "Should have the right value in the box model."); EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); @@ -65,7 +65,7 @@ function*(inspector, view) { is(getStyle(node, "border-bottom-width"), "", "Should have the right border-bottom-width"); yield selectNode("#div2", inspector); - let span = view.doc.querySelector(".border.bottom > span"); + let span = view.doc.querySelector(".layout-border.layout-bottom > span"); is(span.textContent, 16, "Should have the right value in the box model."); EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); @@ -91,7 +91,7 @@ function*(inspector, view) { is(getStyle(node, "padding-right"), "", "Should have the right padding"); yield selectNode("#div3", inspector); - let span = view.doc.querySelector(".padding.right > span"); + let span = view.doc.querySelector(".layout-padding.layout-right > span"); is(span.textContent, 32, "Should have the right value in the box model."); EventUtils.synthesizeMouseAtCenter(span, {}, view.doc.defaultView); diff --git a/devtools/client/inspector/layout/test/browser_layout_guides.js b/devtools/client/inspector/layout/test/browser_layout_guides.js index 3934d331be98..07db6d424404 100644 --- a/devtools/client/inspector/layout/test/browser_layout_guides.js +++ b/devtools/client/inspector/layout/test/browser_layout_guides.js @@ -27,16 +27,16 @@ add_task(function*() { highlighterOptions = options; } - let elt = view.doc.getElementById("margins"); + let elt = view.doc.getElementById("layout-margins"); yield testGuideOnLayoutHover(elt, "margin", inspector, view); - elt = view.doc.getElementById("borders"); + elt = view.doc.getElementById("layout-borders"); yield testGuideOnLayoutHover(elt, "border", inspector, view); - elt = view.doc.getElementById("padding"); + elt = view.doc.getElementById("layout-padding"); yield testGuideOnLayoutHover(elt, "padding", inspector, view); - elt = view.doc.getElementById("content"); + elt = view.doc.getElementById("layout-content"); yield testGuideOnLayoutHover(elt, "content", inspector, view); }); diff --git a/devtools/client/inspector/layout/test/browser_layout_rotate-labels-on-sides.js b/devtools/client/inspector/layout/test/browser_layout_rotate-labels-on-sides.js index 6d73b4f26ca1..e34eb02a6c06 100644 --- a/devtools/client/inspector/layout/test/browser_layout_rotate-labels-on-sides.js +++ b/devtools/client/inspector/layout/test/browser_layout_rotate-labels-on-sides.js @@ -7,18 +7,18 @@ // Test that longer values are rotated on the side const res1 = [ - {selector: ".margin.top > span", value: 30}, - {selector: ".margin.left > span", value: "auto"}, - {selector: ".margin.bottom > span", value: 30}, - {selector: ".margin.right > span", value: "auto"}, - {selector: ".padding.top > span", value: 20}, - {selector: ".padding.left > span", value: 2000000}, - {selector: ".padding.bottom > span", value: 20}, - {selector: ".padding.right > span", value: 20}, - {selector: ".border.top > span", value: 10}, - {selector: ".border.left > span", value: 10}, - {selector: ".border.bottom > span", value: 10}, - {selector: ".border.right > span", value: 10}, + {selector: ".layout-margin.layout-top > span", value: 30}, + {selector: ".layout-margin.layout-left > span", value: "auto"}, + {selector: ".layout-margin.layout-bottom > span", value: 30}, + {selector: ".layout-margin.layout-right > span", value: "auto"}, + {selector: ".layout-padding.layout-top > span", value: 20}, + {selector: ".layout-padding.layout-left > span", value: 2000000}, + {selector: ".layout-padding.layout-bottom > span", value: 20}, + {selector: ".layout-padding.layout-right > span", value: 20}, + {selector: ".layout-border.layout-top > span", value: 10}, + {selector: ".layout-border.layout-left > span", value: 10}, + {selector: ".layout-border.layout-bottom > span", value: 10}, + {selector: ".layout-border.layout-right > span", value: 10}, ]; const TEST_URI = encodeURIComponent([ @@ -37,10 +37,12 @@ add_task(function*() { for (let i = 0; i < res1.length; i++) { let elt = view.doc.querySelector(res1[i].selector); let isLong = elt.textContent.length > LONG_TEXT_ROTATE_LIMIT; - let classList = elt.parentNode.classList - let canBeRotated = classList.contains("left") || classList.contains("right"); - let isRotated = classList.contains("rotate"); + let classList = elt.parentNode.classList; + let canBeRotated = classList.contains("layout-left") || + classList.contains("layout-right"); + let isRotated = classList.contains("layout-rotate"); - is(canBeRotated && isLong, isRotated, res1[i].selector + " correctly rotated."); + is(canBeRotated && isLong, + isRotated, res1[i].selector + " correctly rotated."); } }); diff --git a/devtools/client/inspector/layout/test/browser_layout_tooltips.js b/devtools/client/inspector/layout/test/browser_layout_tooltips.js index e8af63f0bb5d..028836aa4826 100644 --- a/devtools/client/inspector/layout/test/browser_layout_tooltips.js +++ b/devtools/client/inspector/layout/test/browser_layout_tooltips.js @@ -76,24 +76,24 @@ add_task(function*() { info("Checking the regions tooltips"); - ok(view.doc.querySelector("#margins").hasAttribute("title"), + ok(view.doc.querySelector("#layout-margins").hasAttribute("title"), "The margin region has a tooltip"); - is(view.doc.querySelector("#margins").getAttribute("title"), "margin", + is(view.doc.querySelector("#layout-margins").getAttribute("title"), "margin", "The margin region has the correct tooltip content"); - ok(view.doc.querySelector("#borders").hasAttribute("title"), + ok(view.doc.querySelector("#layout-borders").hasAttribute("title"), "The border region has a tooltip"); - is(view.doc.querySelector("#borders").getAttribute("title"), "border", + is(view.doc.querySelector("#layout-borders").getAttribute("title"), "border", "The border region has the correct tooltip content"); - ok(view.doc.querySelector("#padding").hasAttribute("title"), + ok(view.doc.querySelector("#layout-padding").hasAttribute("title"), "The padding region has a tooltip"); - is(view.doc.querySelector("#padding").getAttribute("title"), "padding", + is(view.doc.querySelector("#layout-padding").getAttribute("title"), "padding", "The padding region has the correct tooltip content"); - ok(view.doc.querySelector("#content").hasAttribute("title"), + ok(view.doc.querySelector("#layout-content").hasAttribute("title"), "The content region has a tooltip"); - is(view.doc.querySelector("#content").getAttribute("title"), "content", + is(view.doc.querySelector("#layout-content").getAttribute("title"), "content", "The content region has the correct tooltip content"); for (let {selector, values} of VALUES_TEST_DATA) { diff --git a/devtools/client/inspector/layout/test/browser_layout_update-after-navigation.js b/devtools/client/inspector/layout/test/browser_layout_update-after-navigation.js index 046e0cb98a40..ee62306412b3 100644 --- a/devtools/client/inspector/layout/test/browser_layout_update-after-navigation.js +++ b/devtools/client/inspector/layout/test/browser_layout_update-after-navigation.js @@ -19,7 +19,7 @@ function*(inspector, view) { yield selectNode("p", inspector); info("Checking that the layout-view shows the right value"); - let paddingElt = view.doc.querySelector(".padding.top > span"); + let paddingElt = view.doc.querySelector(".layout-padding.layout-top > span"); is(paddingElt.textContent, "50"); info("Listening for layout-view changes and modifying the padding"); @@ -44,7 +44,7 @@ function*(inspector, view) { yield selectNode("p", inspector); info("Checking that the layout-view shows the right value"); - let sizeElt = view.doc.querySelector(".size > span"); + let sizeElt = view.doc.querySelector(".layout-size > span"); is(sizeElt.textContent, "100" + "\u00D7" + "100"); info("Listening for layout-view changes and modifying the size"); @@ -70,7 +70,7 @@ function*(inspector, view) { info("Checking that the layout-view shows the right value, which is the" + "modified value from step one because of the bfcache"); - let paddingElt = view.doc.querySelector(".padding.top > span"); + let paddingElt = view.doc.querySelector(".layout-padding.layout-top > span"); is(paddingElt.textContent, "20"); info("Listening for layout-view changes and modifying the padding"); diff --git a/devtools/client/inspector/layout/test/browser_layout_update-after-reload.js b/devtools/client/inspector/layout/test/browser_layout_update-after-reload.js index 77382f024a41..565f6f9dfd95 100644 --- a/devtools/client/inspector/layout/test/browser_layout_update-after-reload.js +++ b/devtools/client/inspector/layout/test/browser_layout_update-after-reload.js @@ -26,7 +26,7 @@ function* assertLayoutView(inspector, view) { yield selectNode("p", inspector); info("Checking that the layout-view shows the right value"); - let paddingElt = view.doc.querySelector(".padding.top > span"); + let paddingElt = view.doc.querySelector(".layout-padding.layout-top > span"); is(paddingElt.textContent, "50"); info("Listening for layout-view changes and modifying the padding"); diff --git a/devtools/client/inspector/layout/test/browser_layout_update-in-iframes.js b/devtools/client/inspector/layout/test/browser_layout_update-in-iframes.js index 7af38f37d771..327371884227 100644 --- a/devtools/client/inspector/layout/test/browser_layout_update-in-iframes.js +++ b/devtools/client/inspector/layout/test/browser_layout_update-in-iframes.js @@ -22,7 +22,7 @@ function*(inspector, view, iframe2) { yield selectNodeInIframe2("div", inspector); info("Checking that the layout-view shows the right value"); - let sizeElt = view.doc.querySelector(".size > span"); + let sizeElt = view.doc.querySelector(".layout-size > span"); is(sizeElt.textContent, "400\u00D7200"); info("Listening for layout-view changes and modifying its size"); @@ -46,7 +46,7 @@ function*(inspector, view, iframe2) { yield selectNodeInIframe1("p", inspector); info("Checking that the layout-view shows the right value"); - let sizeElt = view.doc.querySelector(".size > span"); + let sizeElt = view.doc.querySelector(".layout-size > span"); is(sizeElt.textContent, "100\u00D7100"); info("Listening for layout-view changes and modifying its size"); diff --git a/devtools/client/inspector/layout/test/head.js b/devtools/client/inspector/layout/test/head.js index 64e958383d33..33d85ec256f0 100644 --- a/devtools/client/inspector/layout/test/head.js +++ b/devtools/client/inspector/layout/test/head.js @@ -52,7 +52,7 @@ function selectAndHighlightNode(nodeOrSelector, inspector) { * view is visible and ready */ function openLayoutView() { - return openInspectorSidebarTab("layoutview").then(objects => { + return openInspectorSidebarTab("layoutview").then(({toolbox, inspector}) => { // The actual highligher show/hide methods are mocked in layoutview tests. // The highlighter is tested in devtools/inspector/test. function mockHighlighter({highlighter}) { @@ -63,9 +63,13 @@ function openLayoutView() { return promise.resolve(); }; } - mockHighlighter(objects.toolbox); + mockHighlighter(toolbox); - return objects; + return { + toolbox, + inspector, + view: inspector.layoutview + }; }); } diff --git a/devtools/client/inspector/markup/markup.css b/devtools/client/inspector/markup/markup.css deleted file mode 100644 index a3bb29871c0e..000000000000 --- a/devtools/client/inspector/markup/markup.css +++ /dev/null @@ -1,209 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -:root { - -moz-control-character-visibility: visible; -} - -body { - -moz-user-select: none; -} - -/* Force height and width (possibly overflowing) from inline elements. - * This allows long overflows of text or input fields to still be styled with - * the container, rather than the background disappearing when scrolling */ -#root { - float: left; - min-width: 100%; -} - -body.dragging .tag-line { - cursor: grabbing; -} - -#root-wrapper:after { - content: ""; - display: block; - clear: both; - position:relative; -} - -.html-editor { - display: none; - position: absolute; - z-index: 2; - - /* Use the same margin/padding trick used by .child tags to ensure that - * the editor covers up any content to the left (including expander arrows - * and hover effects). */ - margin-left: -1000em; - padding-left: 1000em; -} - -.html-editor-inner { - border: solid .1px; - flex: 1 1 auto; - - /* Keep the editor away from the markup view floating scrollbars */ - -moz-margin-end: 12px; -} - -.html-editor iframe { - height: 100%; - width: 100%; - border: none; - margin: 0; - padding: 0; -} - -.children { - list-style: none; - padding: 0; - margin: 0; -} - -/* Tags are organized in a UL/LI tree and indented thanks to a left padding. - * A very large padding is used in combination with a slightly smaller margin - * to make sure childs actually span from edge-to-edge. */ -.child { - margin-left: -1000em; - padding-left: 1001em; -} - -/* Normally this element takes space in the layout even if it's position: relative - * by adding height: 0 we let surrounding elements to fill the blank space */ -.child.dragging { - position: relative; - pointer-events: none; - opacity: 0.7; - z-index: 1; - height: 0; -} - -/* Indicates a tag-line in the markup-view as being an active drop target by - * drawing a horizontal line where the dragged element would be inserted if - * dropped here */ -.tag-line.drop-target::before, -.tag-line.drag-target::before { - content: ''; - position: absolute; - top: 0; - width: 100%; - /* Offset these by 1000px to make sure they cover the full width of the view */ - padding-left: 1000px; - left: -1000px; -} - -.tag-line.drag-target::before { - border-top: 2px solid var(--theme-content-color2); -} - -.tag-line.drop-target::before { - border-top: 2px solid var(--theme-contrast-background); -} - -/* In case the indicator is put on the closing .tag-line, the indentation level - * will become misleading, so we push it forward to match the indentation level */ -ul.children + .tag-line::before { - margin-left: 14px; -} - -.tag-line { - min-height: 1.4em; - line-height: 1.4em; - position: relative; -} - -.html-editor-container { - position: relative; - min-height: 200px; -} - -/* This extra element placed in each tag is positioned absolutely to cover the - * whole tag line and is used for background styling (when a selection is made - * or when the tag is flashing) */ -.tag-line .tag-state { - position: absolute; - left: -1000em; - right: 0; - height: 100%; - z-index: 0; -} - -.expander { - display: inline-block; - margin-left: -14px; - vertical-align: middle; - /* Make sure the expander still appears above the tag-state */ - position: relative; - z-index: 1; -} - -.child.collapsed .child { - display: none; -} - -.child > .tag-line:first-child .close { - display: none; -} - -.child.collapsed > .tag-line:first-child .close { - display: inline; -} - -.child.collapsed > .tag-line ~ .tag-line { - display: none; -} - -.child.collapsed .close { - display: inline; -} - -.closing-bracket { - pointer-events: none; -} - -.newattr { - display: inline-block; - width: 1em; - height: 1ex; - margin-right: -1em; - padding: 1px 0; -} - -.attr-value .link { - text-decoration: underline; -} - -.newattr:focus { - margin-right: 0; -} - -.flash-out { - transition: background .5s; -} - -.tag-line { - cursor: default; -} - -.markupview-events { - display: none; - cursor: pointer; -} - -.editor { - /* Make sure the editor still appears above the tag-state */ - position: relative; - z-index: 1; -} - -.editor.text { - display: inline-block; -} - -.editor.text pre, -.editor.comment pre { - font: inherit; -} diff --git a/devtools/client/inspector/markup/test/browser_markup_css_completion_style_attribute.js b/devtools/client/inspector/markup/test/browser_markup_css_completion_style_attribute.js index be22ae41eea3..f829bc832206 100644 --- a/devtools/client/inspector/markup/test/browser_markup_css_completion_style_attribute.js +++ b/devtools/client/inspector/markup/test/browser_markup_css_completion_style_attribute.js @@ -76,14 +76,23 @@ add_task(function*() { EventUtils.sendKey("return", inspector.panelWin); let editor = inplaceEditor(attr); - for (let i = 0; i < TEST_DATA.length; i ++) { + for (let i = 0; i < TEST_DATA.length; i++) { + // Expect a markupmutation event at the last iteration since that's when the + // attribute is actually created. + let onMutation = i === TEST_DATA.length - 1 + ? inspector.once("markupmutation") : null; yield enterData(i, editor, inspector); yield checkData(i, editor, inspector); + yield onMutation; } + // Undoing the action will remove the new attribute, so make sure to wait for + // the markupmutation event here again. + let onMutation = inspector.once("markupmutation"); while (inspector.markup.undo.canUndo()) { yield undoChange(inspector); } + yield onMutation; }); function enterData(index, editor, inspector) { diff --git a/devtools/client/inspector/markup/test/browser_markup_dragdrop_escapeKeyPress.js b/devtools/client/inspector/markup/test/browser_markup_dragdrop_escapeKeyPress.js index 3e5c532a8a39..fad442ae7339 100644 --- a/devtools/client/inspector/markup/test/browser_markup_dragdrop_escapeKeyPress.js +++ b/devtools/client/inspector/markup/test/browser_markup_dragdrop_escapeKeyPress.js @@ -13,6 +13,7 @@ add_task(function*() { let {markup} = inspector; info("Get a test container"); + yield selectNode("#test", inspector); let container = yield getContainerForSelector("#test", inspector); info("Simulate a drag/drop on this container"); diff --git a/devtools/client/inspector/markup/test/browser_markup_dragdrop_invalidNodes.js b/devtools/client/inspector/markup/test/browser_markup_dragdrop_invalidNodes.js index 536bfc850788..74bee2c06c11 100644 --- a/devtools/client/inspector/markup/test/browser_markup_dragdrop_invalidNodes.js +++ b/devtools/client/inspector/markup/test/browser_markup_dragdrop_invalidNodes.js @@ -18,10 +18,11 @@ add_task(function*() { yield inspector.markup.expandNode(parentFront); yield waitForMultipleChildrenUpdates(inspector); - info("Getting the ::before pseudo element"); + info("Getting the ::before pseudo element and selecting it"); let parentContainer = yield getContainerForNodeFront(parentFront, inspector); let beforePseudo = parentContainer.elt.children[1].firstChild.container; parentContainer.elt.scrollIntoView(true); + yield selectNode(beforePseudo.node, inspector); info("Simulate dragging the ::before pseudo element"); yield simulateNodeDrag(inspector, beforePseudo); @@ -33,10 +34,11 @@ add_task(function*() { yield inspector.markup.expandNode(inputFront); yield waitForMultipleChildrenUpdates(inspector); - info("Getting the anonymous node"); + info("Getting the anonymous node and selecting it"); let inputContainer = yield getContainerForNodeFront(inputFront, inspector); let anonymousDiv = inputContainer.elt.children[1].firstChild.container; inputContainer.elt.scrollIntoView(true); + yield selectNode(anonymousDiv.node, inspector); info("Simulate dragging the anonymous node"); yield simulateNodeDrag(inspector, anonymousDiv); diff --git a/devtools/client/inspector/markup/test/browser_markup_keybindings_04.js b/devtools/client/inspector/markup/test/browser_markup_keybindings_04.js index 5425e85ecb8d..e406619e4faf 100644 --- a/devtools/client/inspector/markup/test/browser_markup_keybindings_04.js +++ b/devtools/client/inspector/markup/test/browser_markup_keybindings_04.js @@ -48,27 +48,24 @@ function selectPreviousNodeWithArrowUp(inspector) { } function* selectWithBrowserMenu(inspector) { + let contentAreaContextMenu = document.querySelector("#contentAreaContextMenu"); + let contextOpened = once(contentAreaContextMenu, "popupshown"); + yield BrowserTestUtils.synthesizeMouseAtCenter("div", { type: "contextmenu", button: 2 }, gBrowser.selectedBrowser); - // nsContextMenu also requires the popupNode to be set, but we can't set it to - // node under e10s as it's a CPOW, not a DOM node. But under e10s, - // nsContextMenu won't use the property anyway, so just try/catching is ok. - try { - document.popupNode = getNode("div"); - } catch (e) {} + yield contextOpened; - let contentAreaContextMenu = document.querySelector("#contentAreaContextMenu"); - let contextMenu = new nsContextMenu(contentAreaContextMenu); - yield contextMenu.inspectNode(); + yield gContextMenu.inspectNode(); + let contextClosed = once(contentAreaContextMenu, "popuphidden"); contentAreaContextMenu.hidden = true; contentAreaContextMenu.hidePopup(); - contextMenu.hiding(); yield inspector.once("inspector-updated"); + yield contextClosed; } function* selectWithElementPicker(inspector, testActor) { diff --git a/devtools/client/inspector/markup/test/browser_markup_navigation.js b/devtools/client/inspector/markup/test/browser_markup_navigation.js index 37b868caf6da..6fd3cad5685b 100644 --- a/devtools/client/inspector/markup/test/browser_markup_navigation.js +++ b/devtools/client/inspector/markup/test/browser_markup_navigation.js @@ -24,7 +24,7 @@ const TEST_DATA = [ ["down", "node5"], ["down", "node6"], ["down", "*comment*"], - ["down" , "node7"], + ["down", "node7"], ["right", "node7"], ["down", "*text*"], ["down", "node8"], @@ -83,10 +83,17 @@ add_task(function*() { info("Checking the right node is selected"); checkSelectedNode(key, className, inspector); } + + // In theory, we should wait for the inspector-updated event at each iteration + // of the previous loop where we expect the current node to change (because + // changing the current node ends up refreshing the rule-view, breadcrumbs, + // ...), but this would make this test a *lot* slower. Instead, having a final + // catch-all event works too. + yield inspector.once("inspector-updated"); }); function pressKey(key) { - switch(key) { + switch (key) { case "right": EventUtils.synthesizeKey("VK_RIGHT", {}); break; @@ -115,12 +122,16 @@ function checkSelectedNode(key, className, inspector) { let node = inspector.selection.nodeFront; if (className == "*comment*") { - is(node.nodeType, Node.COMMENT_NODE, "Found a comment after pressing " + key); + is(node.nodeType, Node.COMMENT_NODE, + "Found a comment after pressing " + key); } else if (className == "*text*") { - is(node.nodeType, Node.TEXT_NODE, "Found text after pressing " + key); + is(node.nodeType, Node.TEXT_NODE, + "Found text after pressing " + key); } else if (className == "*doctype*") { - is(node.nodeType, Node.DOCUMENT_TYPE_NODE, "Found the doctype after pressing " + key); + is(node.nodeType, Node.DOCUMENT_TYPE_NODE, + "Found the doctype after pressing " + key); } else { - is(node.className, className, "Found node: " + className + " after pressing " + key); + is(node.className, className, + "Found node: " + className + " after pressing " + key); } } diff --git a/devtools/client/inspector/rules/rules.js b/devtools/client/inspector/rules/rules.js index 1f8310abb4bc..026017638646 100644 --- a/devtools/client/inspector/rules/rules.js +++ b/devtools/client/inspector/rules/rules.js @@ -219,7 +219,7 @@ function CssRuleView(inspector, document, store, pageStyle) { autoSelect: true, theme: "auto" }; - this.popup = new AutocompletePopup(this.styleWindow.parent.document, options); + this.popup = new AutocompletePopup(this.styleDocument, options); this._showEmpty(); diff --git a/devtools/client/inspector/rules/rules.xhtml b/devtools/client/inspector/rules/rules.xhtml deleted file mode 100644 index 7044916def1e..000000000000 --- a/devtools/client/inspector/rules/rules.xhtml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - %inspectorDTD; - - %globalDTD; -]> - - - - &ruleViewTitle; - - - - - - -
    -
    - - - -
    - -
    - -
    -
    - - diff --git a/devtools/client/inspector/rules/test/browser_rules_add-property-cancel_01.js b/devtools/client/inspector/rules/test/browser_rules_add-property-cancel_01.js index 97106c09f7f7..209870165101 100644 --- a/devtools/client/inspector/rules/test/browser_rules_add-property-cancel_01.js +++ b/devtools/client/inspector/rules/test/browser_rules_add-property-cancel_01.js @@ -43,6 +43,6 @@ function* testCancelNew(view) { "Should have cancelled creating a new text property."); ok(!elementRuleEditor.propertyList.hasChildNodes(), "Should not have any properties."); - is(view.styleDocument.body, view.styleDocument.activeElement, + is(view.styleDocument.activeElement, view.styleDocument.documentElement, "Correct element has focus"); } diff --git a/devtools/client/inspector/rules/test/browser_rules_add-property-cancel_02.js b/devtools/client/inspector/rules/test/browser_rules_add-property-cancel_02.js index e49baabecc98..b49b5dc57e7b 100644 --- a/devtools/client/inspector/rules/test/browser_rules_add-property-cancel_02.js +++ b/devtools/client/inspector/rules/test/browser_rules_add-property-cancel_02.js @@ -71,7 +71,7 @@ add_task(function*() { EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow); yield onRuleViewChanged; - is(view.styleDocument.body, view.styleDocument.activeElement, + is(view.styleDocument.documentElement, view.styleDocument.activeElement, "Correct element has focus"); is(elementRuleEditor.rule.textProps.length, 1, diff --git a/devtools/client/inspector/rules/test/browser_rules_add-property-cancel_03.js b/devtools/client/inspector/rules/test/browser_rules_add-property-cancel_03.js index f4d0e3de9729..d903f9d0a84e 100644 --- a/devtools/client/inspector/rules/test/browser_rules_add-property-cancel_03.js +++ b/devtools/client/inspector/rules/test/browser_rules_add-property-cancel_03.js @@ -48,6 +48,6 @@ function* testCancelNewOnEscape(inspector, view) { "Should have canceled creating a new text property."); ok(!elementRuleEditor.propertyList.hasChildNodes(), "Should not have any properties."); - is(view.styleDocument.body, view.styleDocument.activeElement, + is(view.styleDocument.documentElement, view.styleDocument.activeElement, "Correct element has focus"); } diff --git a/devtools/client/inspector/rules/test/browser_rules_authored_color.js b/devtools/client/inspector/rules/test/browser_rules_authored_color.js index 819e0778f33c..e3462250a5e0 100644 --- a/devtools/client/inspector/rules/test/browser_rules_authored_color.js +++ b/devtools/client/inspector/rules/test/browser_rules_authored_color.js @@ -41,7 +41,7 @@ add_task(function* () { swatch.click(); yield onShown; - let testNode = yield getNode("#testid"); + let testNode = getNode("#testid"); yield simulateColorPickerChange(view, cPicker, [0, 255, 0, 1], { element: testNode, diff --git a/devtools/client/inspector/rules/test/browser_rules_colorUnit.js b/devtools/client/inspector/rules/test/browser_rules_colorUnit.js index 90414e80e82f..afbe7d261dd3 100644 --- a/devtools/client/inspector/rules/test/browser_rules_colorUnit.js +++ b/devtools/client/inspector/rules/test/browser_rules_colorUnit.js @@ -46,7 +46,7 @@ function* basicTest(view, name, result) { swatch.click(); yield onShown; - let testNode = yield getNode("#testid"); + let testNode = getNode("#testid"); yield simulateColorPickerChange(view, cPicker, [0, 255, 0, 1], { element: testNode, diff --git a/devtools/client/inspector/rules/test/browser_rules_content_02.js b/devtools/client/inspector/rules/test/browser_rules_content_02.js index 5faac6954a6f..6d4849c5218f 100644 --- a/devtools/client/inspector/rules/test/browser_rules_content_02.js +++ b/devtools/client/inspector/rules/test/browser_rules_content_02.js @@ -44,7 +44,7 @@ add_task(function*() { let inspector = toolbox.getPanel("inspector"); yield inspector.once("inspector-updated"); - let view = inspector.sidebar.getWindowForTab("ruleview")["ruleview"].view; + let view = inspector.ruleview.view; checkRuleViewContent(view); }); diff --git a/devtools/client/inspector/rules/test/browser_rules_cycle-color.js b/devtools/client/inspector/rules/test/browser_rules_cycle-color.js index 0e87ab1ee3c5..a022a625d59a 100644 --- a/devtools/client/inspector/rules/test/browser_rules_cycle-color.js +++ b/devtools/client/inspector/rules/test/browser_rules_cycle-color.js @@ -17,15 +17,15 @@ const TEST_URI = ` add_task(function*() { yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); - let {inspector, view} = yield openRuleView(); + let {view} = yield openRuleView(); let container = getRuleViewProperty(view, "body", "color").valueSpan; - checkColorCycling(container, inspector); + checkColorCycling(container, view); }); -function checkColorCycling(container, inspector) { +function checkColorCycling(container, view) { let swatch = container.querySelector(".ruleview-colorswatch"); let valueNode = container.querySelector(".ruleview-color"); - let win = inspector.sidebar.getWindowForTab("ruleview"); + let win = view.styleWindow; // Hex is(valueNode.textContent, "#f00", "Color displayed as a hex value."); diff --git a/devtools/client/inspector/rules/test/browser_rules_edit-property-increments.js b/devtools/client/inspector/rules/test/browser_rules_edit-property-increments.js index 08aaa582b2eb..62b7605ab4e9 100644 --- a/devtools/client/inspector/rules/test/browser_rules_edit-property-increments.js +++ b/devtools/client/inspector/rules/test/browser_rules_edit-property-increments.js @@ -10,8 +10,8 @@ add_task(function*() { yield addTab("data:text/html;charset=utf-8,sample document for bug 722691"); createDocument(); - let {toolbox, inspector, view} = yield openRuleView(); + let {inspector, view} = yield openRuleView(); yield selectNode("#test", inspector); yield testMarginIncrements(view); @@ -23,16 +23,16 @@ add_task(function*() { }); function createDocument() { - content.document.body.innerHTML = '' + - '' + - '
    '; + content.document.body.innerHTML = "" + + "" + + "
    "; } function* testMarginIncrements(view) { @@ -64,13 +64,13 @@ function* testVariousUnitIncrements(view) { 2: {start: "0pt", end: "1pt", selectAll: true}, 3: {start: "0pc", end: "1pc", selectAll: true}, 4: {start: "0em", end: "1em", selectAll: true}, - 5: {start: "0%", end: "1%", selectAll: true}, + 5: {start: "0%", end: "1%", selectAll: true}, 6: {start: "0in", end: "1in", selectAll: true}, 7: {start: "0cm", end: "1cm", selectAll: true}, 8: {start: "0mm", end: "1mm", selectAll: true}, 9: {start: "0ex", end: "1ex", selectAll: true} }); -}; +} function* testHexIncrements(view) { info("Testing keyboard increments with hex colors"); @@ -81,12 +81,12 @@ function* testHexIncrements(view) { yield runIncrementTest(hexColorPropEditor, view, { 1: {start: "#CCCCCC", end: "#CDCDCD", selectAll: true}, 2: {shift: true, start: "#CCCCCC", end: "#DCDCDC", selectAll: true}, - 3: {start: "#CCCCCC", end: "#CDCCCC", selection: [1,3]}, - 4: {shift: true, start: "#CCCCCC", end: "#DCCCCC", selection: [1,3]}, + 3: {start: "#CCCCCC", end: "#CDCCCC", selection: [1, 3]}, + 4: {shift: true, start: "#CCCCCC", end: "#DCCCCC", selection: [1, 3]}, 5: {start: "#FFFFFF", end: "#FFFFFF", selectAll: true}, 6: {down: true, shift: true, start: "#000000", end: "#000000", selectAll: true} }); -}; +} function* testRgbIncrements(view) { info("Testing keyboard increments with rgb colors"); @@ -95,14 +95,14 @@ function* testRgbIncrements(view) { let rgbColorPropEditor = idRuleEditor.rule.textProps[3].editor; yield runIncrementTest(rgbColorPropEditor, view, { - 1: {start: "rgb(0,0,0)", end: "rgb(0,1,0)", selection: [6,7]}, - 2: {shift: true, start: "rgb(0,0,0)", end: "rgb(0,10,0)", selection: [6,7]}, - 3: {start: "rgb(0,255,0)", end: "rgb(0,255,0)", selection: [6,9]}, - 4: {shift: true, start: "rgb(0,250,0)", end: "rgb(0,255,0)", selection: [6,9]}, - 5: {down: true, start: "rgb(0,0,0)", end: "rgb(0,0,0)", selection: [6,7]}, - 6: {down: true, shift: true, start: "rgb(0,5,0)", end: "rgb(0,0,0)", selection: [6,7]} + 1: {start: "rgb(0,0,0)", end: "rgb(0,1,0)", selection: [6, 7]}, + 2: {shift: true, start: "rgb(0,0,0)", end: "rgb(0,10,0)", selection: [6, 7]}, + 3: {start: "rgb(0,255,0)", end: "rgb(0,255,0)", selection: [6, 9]}, + 4: {shift: true, start: "rgb(0,250,0)", end: "rgb(0,255,0)", selection: [6, 9]}, + 5: {down: true, start: "rgb(0,0,0)", end: "rgb(0,0,0)", selection: [6, 7]}, + 6: {down: true, shift: true, start: "rgb(0,5,0)", end: "rgb(0,0,0)", selection: [6, 7]} }); -}; +} function* testShorthandIncrements(view) { info("Testing keyboard increments within shorthand values"); @@ -111,17 +111,17 @@ function* testShorthandIncrements(view) { let paddingPropEditor = idRuleEditor.rule.textProps[1].editor; yield runIncrementTest(paddingPropEditor, view, { - 1: {start: "0px 0px 0px 0px", end: "0px 1px 0px 0px", selection: [4,7]}, - 2: {shift: true, start: "0px 0px 0px 0px", end: "0px 10px 0px 0px", selection: [4,7]}, + 1: {start: "0px 0px 0px 0px", end: "0px 1px 0px 0px", selection: [4, 7]}, + 2: {shift: true, start: "0px 0px 0px 0px", end: "0px 10px 0px 0px", selection: [4, 7]}, 3: {start: "0px 0px 0px 0px", end: "1px 0px 0px 0px", selectAll: true}, 4: {shift: true, start: "0px 0px 0px 0px", end: "10px 0px 0px 0px", selectAll: true}, - 5: {down: true, start: "0px 0px 0px 0px", end: "0px 0px -1px 0px", selection: [8,11]}, + 5: {down: true, start: "0px 0px 0px 0px", end: "0px 0px -1px 0px", selection: [8, 11]}, 6: {down: true, shift: true, start: "0px 0px 0px 0px", end: "-10px 0px 0px 0px", selectAll: true}, 7: {up: true, start: "0.1em .1em 0em 0em", end: "0.1em 1.1em 0em 0em", selection: [6, 9]}, 8: {up: true, alt: true, start: "0.1em .9em 0em 0em", end: "0.1em 1em 0em 0em", selection: [6, 9]}, 9: {up: true, shift: true, start: "0.2em .2em 0em 0em", end: "0.2em 10.2em 0em 0em", selection: [6, 9]} }); -}; +} function* testOddCases(view) { info("Testing some more odd cases"); @@ -130,32 +130,38 @@ function* testOddCases(view) { let marginPropEditor = idRuleEditor.rule.textProps[0].editor; yield runIncrementTest(marginPropEditor, view, { - 1: {start: "98.7%", end: "99.7%", selection: [3,3]}, - 2: {alt: true, start: "98.7%", end: "98.8%", selection: [3,3]}, + 1: {start: "98.7%", end: "99.7%", selection: [3, 3]}, + 2: {alt: true, start: "98.7%", end: "98.8%", selection: [3, 3]}, 3: {start: "0", end: "1"}, 4: {down: true, start: "0", end: "-1"}, - 5: {start: "'a=-1'", end: "'a=0'", selection: [4,4]}, - 6: {start: "0 -1px", end: "0 0px", selection: [2,2]}, - 7: {start: "url(-1)", end: "url(-1)", selection: [4,4]}, - 8: {start: "url('test1.1.png')", end: "url('test1.2.png')", selection: [11,11]}, - 9: {start: "url('test1.png')", end: "url('test2.png')", selection: [9,9]}, - 10: {shift: true, start: "url('test1.1.png')", end: "url('test11.1.png')", selection: [9,9]}, - 11: {down: true, start: "url('test-1.png')", end: "url('test-2.png')", selection: [9,11]}, - 12: {start: "url('test1.1.png')", end: "url('test1.2.png')", selection: [11,12]}, - 13: {down: true, alt: true, start: "url('test-0.png')", end: "url('test--0.1.png')", selection: [10,11]}, - 14: {alt: true, start: "url('test--0.1.png')", end: "url('test-0.png')", selection: [10,14]} + 5: {start: "'a=-1'", end: "'a=0'", selection: [4, 4]}, + 6: {start: "0 -1px", end: "0 0px", selection: [2, 2]}, + 7: {start: "url(-1)", end: "url(-1)", selection: [4, 4]}, + 8: {start: "url('test1.1.png')", end: "url('test1.2.png')", selection: [11, 11]}, + 9: {start: "url('test1.png')", end: "url('test2.png')", selection: [9, 9]}, + 10: {shift: true, start: "url('test1.1.png')", end: "url('test11.1.png')", selection: [9, 9]}, + 11: {down: true, start: "url('test-1.png')", end: "url('test-2.png')", selection: [9, 11]}, + 12: {start: "url('test1.1.png')", end: "url('test1.2.png')", selection: [11, 12]}, + 13: {down: true, alt: true, start: "url('test-0.png')", end: "url('test--0.1.png')", selection: [10, 11]}, + 14: {alt: true, start: "url('test--0.1.png')", end: "url('test-0.png')", selection: [10, 14]} }); -}; +} function* runIncrementTest(propertyEditor, view, tests) { let editor = yield focusEditableField(view, propertyEditor.valueSpan); - for(let test in tests) { + for (let test in tests) { yield testIncrement(editor, tests[test], view, propertyEditor); } + + // Blur the field to put back the UI in its initial state (and avoid pending + // requests when the test ends). + let onRuleViewChanged = view.once("ruleview-changed"); + EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow); + yield onRuleViewChanged; } -function* testIncrement(editor, options, view, {ruleEditor}) { +function* testIncrement(editor, options, view) { editor.input.value = options.start; let input = editor.input; diff --git a/devtools/client/inspector/rules/test/browser_rules_edit-property_03.js b/devtools/client/inspector/rules/test/browser_rules_edit-property_03.js index b9ba3fe629de..43e2c32c3938 100644 --- a/devtools/client/inspector/rules/test/browser_rules_edit-property_03.js +++ b/devtools/client/inspector/rules/test/browser_rules_edit-property_03.js @@ -33,14 +33,16 @@ add_task(function*() { yield focusEditableField(view, propEditor.valueSpan); info("Deleting all the text out of a value field"); - let waitForUpdates = view.once("ruleview-changed"); + let onRuleViewChanged = view.once("ruleview-changed"); yield sendCharsAndWaitForFocus(view, ruleEditor.element, ["VK_DELETE", "VK_RETURN"]); - yield waitForUpdates; + yield onRuleViewChanged; info("Pressing enter a couple times to cycle through editors"); yield sendCharsAndWaitForFocus(view, ruleEditor.element, ["VK_RETURN"]); + onRuleViewChanged = view.once("ruleview-changed"); yield sendCharsAndWaitForFocus(view, ruleEditor.element, ["VK_RETURN"]); + yield onRuleViewChanged; isnot(ruleEditor.rule.textProps[1].editor.nameSpan.style.display, "none", "The name span is visible"); diff --git a/devtools/client/inspector/rules/test/browser_rules_edit-property_08.js b/devtools/client/inspector/rules/test/browser_rules_edit-property_08.js index 5cd0515f80da..d250a7d514b5 100644 --- a/devtools/client/inspector/rules/test/browser_rules_edit-property_08.js +++ b/devtools/client/inspector/rules/test/browser_rules_edit-property_08.js @@ -19,25 +19,27 @@ add_task(function*() { yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); let {inspector, view} = yield openRuleView(); yield selectNode("#testid", inspector); - yield testRenameProperty(inspector, view); -}); -function* testRenameProperty(inspector, view) { + info("Get the color property editor"); let ruleEditor = getRuleViewRuleEditor(view, 0); let propEditor = ruleEditor.rule.textProps[0].editor; - is(ruleEditor.rule.textProps[0].name, "color"); - info("Focus on property name"); - let editor = yield focusEditableField(ruleEditor.ruleView, - propEditor.nameSpan, 32, 1); + info("Focus the property name field"); + yield focusEditableField(ruleEditor.ruleView, propEditor.nameSpan, 32, 1); - info("Renaming the property"); + info("Rename the property to background-color"); + // Expect 3 events: the value editor being focused, the ruleview-changed event + // which signals that the new value has been previewed (fires once when the + // value gets focused), and the markupmutation event since we're modifying an + // inline style. let onValueFocus = once(ruleEditor.element, "focus", true); - let onModifications = ruleEditor.ruleView.once("ruleview-changed"); + let onRuleViewChanged = ruleEditor.ruleView.once("ruleview-changed"); + let onMutation = inspector.once("markupmutation"); EventUtils.sendString("background-color:", ruleEditor.doc.defaultView); yield onValueFocus; - yield onModifications; + yield onRuleViewChanged; + yield onMutation; is(ruleEditor.rule.textProps[0].name, "background-color"); yield waitForComputedStyleProperty("#testid", null, "background-color", @@ -45,4 +47,11 @@ function* testRenameProperty(inspector, view) { is((yield getComputedStyleProperty("#testid", null, "color")), "rgb(255, 255, 255)", "color is white"); -} + + // The value field is still focused. Blur it now and wait for the + // ruleview-changed event to avoid pending requests. + onRuleViewChanged = view.once("ruleview-changed"); + EventUtils.synthesizeKey("VK_ESCAPE", {}); + yield onRuleViewChanged; +}); + diff --git a/devtools/client/inspector/rules/test/browser_rules_edit-selector_05.js b/devtools/client/inspector/rules/test/browser_rules_edit-selector_05.js index 4afcb5a8643c..60591238b432 100644 --- a/devtools/client/inspector/rules/test/browser_rules_edit-selector_05.js +++ b/devtools/client/inspector/rules/test/browser_rules_edit-selector_05.js @@ -57,6 +57,11 @@ function* testEditSelector(view, name) { ok(getRuleViewRule(view, name), "Rule with " + name + " selector exists."); ok(getRuleViewRuleEditor(view, 1).element.getAttribute("unmatched"), "Rule with " + name + " does not match the current element."); + + // Escape the new property editor after editing the selector + let onBlur = once(view.styleDocument.activeElement, "blur"); + EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow); + yield onBlur; } function* checkModifiedElement(view, name) { diff --git a/devtools/client/inspector/rules/test/browser_rules_editable-field-focus_01.js b/devtools/client/inspector/rules/test/browser_rules_editable-field-focus_01.js index 9981b1233e3b..fe9dc1f2bc9f 100644 --- a/devtools/client/inspector/rules/test/browser_rules_editable-field-focus_01.js +++ b/devtools/client/inspector/rules/test/browser_rules_editable-field-focus_01.js @@ -30,51 +30,65 @@ add_task(function*() { yield testEditableFieldFocus(inspector, view, "VK_TAB"); }); -function* testEditableFieldFocus(inspector, view, commitKey, options={}) { +function* testEditableFieldFocus(inspector, view, commitKey) { + info("Click on the selector of the inline style ('element')"); let ruleEditor = getRuleViewRuleEditor(view, 0); - let onFocus = once(ruleEditor.element, "focus", true); ruleEditor.selectorText.click(); yield onFocus; assertEditor(view, ruleEditor.newPropSpan, "Focus should be in the element property span"); + info("Focus the next field with " + commitKey); ruleEditor = getRuleViewRuleEditor(view, 1); - - yield focusNextEditableField(view, ruleEditor, commitKey, options); - yield assertEditor(view, ruleEditor.selectorText, + yield focusNextEditableField(view, ruleEditor, commitKey); + assertEditor(view, ruleEditor.selectorText, "Focus should have moved to the next rule selector"); - for (let textProp of ruleEditor.rule.textProps) { + for (let i = 0; i < ruleEditor.rule.textProps.length; i++) { + let textProp = ruleEditor.rule.textProps[i]; let propEditor = textProp.editor; - yield focusNextEditableField(view, ruleEditor, commitKey, options); - yield assertEditor(view, propEditor.nameSpan, + info("Focus the next field with " + commitKey); + // Expect a ruleview-changed event if we are moving from a property value + // to the next property name (which occurs after the first iteration, as for + // i=0, the previous field is the selector). + let onRuleViewChanged = i > 0 ? view.once("ruleview-changed") : null; + yield focusNextEditableField(view, ruleEditor, commitKey); + yield onRuleViewChanged; + assertEditor(view, propEditor.nameSpan, "Focus should have moved to the property name"); - yield focusNextEditableField(view, ruleEditor, commitKey, options); - yield assertEditor(view, propEditor.valueSpan, + info("Focus the next field with " + commitKey); + yield focusNextEditableField(view, ruleEditor, commitKey); + assertEditor(view, propEditor.valueSpan, "Focus should have moved to the property value"); } - yield focusNextEditableField(view, ruleEditor, commitKey, options); - yield assertEditor(view, ruleEditor.newPropSpan, + // Expect a ruleview-changed event again as we're bluring a property value. + let onRuleViewChanged = view.once("ruleview-changed"); + yield focusNextEditableField(view, ruleEditor, commitKey); + yield onRuleViewChanged; + assertEditor(view, ruleEditor.newPropSpan, "Focus should have moved to the new property span"); ruleEditor = getRuleViewRuleEditor(view, 2); - yield focusNextEditableField(view, ruleEditor, commitKey, options); - yield assertEditor(view, ruleEditor.selectorText, + yield focusNextEditableField(view, ruleEditor, commitKey); + assertEditor(view, ruleEditor.selectorText, "Focus should have moved to the next rule selector"); + + info("Blur the selector field"); + EventUtils.synthesizeKey("VK_ESCAPE", {}); } -function* focusNextEditableField(view, ruleEditor, commitKey, options) { +function* focusNextEditableField(view, ruleEditor, commitKey) { let onFocus = once(ruleEditor.element, "focus", true); - EventUtils.synthesizeKey(commitKey, options, view.styleWindow); + EventUtils.synthesizeKey(commitKey, {}, view.styleWindow); yield onFocus; } -function* assertEditor(view, element, message) { +function assertEditor(view, element, message) { let editor = inplaceEditor(view.styleDocument.activeElement); is(inplaceEditor(element), editor, message); } diff --git a/devtools/client/inspector/rules/test/browser_rules_editable-field-focus_02.js b/devtools/client/inspector/rules/test/browser_rules_editable-field-focus_02.js index af5e2b9bc630..bb2d58a0919f 100644 --- a/devtools/client/inspector/rules/test/browser_rules_editable-field-focus_02.js +++ b/devtools/client/inspector/rules/test/browser_rules_editable-field-focus_02.js @@ -37,36 +37,42 @@ function* testEditableFieldFocus(inspector, view, commitKey, options={}) { ruleEditor = getRuleViewRuleEditor(view, 1); - yield focusNextEditableField(view, ruleEditor, commitKey, options); + yield focusNextField(view, ruleEditor, commitKey, options); assertEditor(view, ruleEditor.newPropSpan, "Focus should have moved to the new property span"); for (let textProp of ruleEditor.rule.textProps.slice(0).reverse()) { let propEditor = textProp.editor; - yield focusNextEditableField(view, ruleEditor, commitKey, options); + yield focusNextField(view, ruleEditor, commitKey, options); yield assertEditor(view, propEditor.valueSpan, "Focus should have moved to the property value"); - yield focusNextEditableField(view, ruleEditor, commitKey, options); + yield focusNextFieldAndExpectChange(view, ruleEditor, commitKey, options); yield assertEditor(view, propEditor.nameSpan, "Focus should have moved to the property name"); } ruleEditor = getRuleViewRuleEditor(view, 1); - yield focusNextEditableField(view, ruleEditor, commitKey, options); + yield focusNextField(view, ruleEditor, commitKey, options); yield assertEditor(view, ruleEditor.selectorText, "Focus should have moved to the '#testid' rule selector"); ruleEditor = getRuleViewRuleEditor(view, 0); - yield focusNextEditableField(view, ruleEditor, commitKey, options); + yield focusNextField(view, ruleEditor, commitKey, options); assertEditor(view, ruleEditor.newPropSpan, "Focus should have moved to the new property span"); } -function* focusNextEditableField(view, ruleEditor, commitKey, options) { +function* focusNextFieldAndExpectChange(view, ruleEditor, commitKey, options) { + let onRuleViewChanged = view.once("ruleview-changed"); + yield focusNextField(view, ruleEditor, commitKey, options); + yield onRuleViewChanged; +} + +function* focusNextField(view, ruleEditor, commitKey, options) { let onFocus = once(ruleEditor.element, "focus", true); EventUtils.synthesizeKey(commitKey, options, view.styleWindow); yield onFocus; diff --git a/devtools/client/inspector/rules/test/browser_rules_filtereditor-commit-on-ENTER.js b/devtools/client/inspector/rules/test/browser_rules_filtereditor-commit-on-ENTER.js index 3735e51f4a7a..2323d754238d 100644 --- a/devtools/client/inspector/rules/test/browser_rules_filtereditor-commit-on-ENTER.js +++ b/devtools/client/inspector/rules/test/browser_rules_filtereditor-commit-on-ENTER.js @@ -11,11 +11,11 @@ add_task(function*() { yield addTab(TEST_URL); let {view} = yield openRuleView(); - info("Getting the filter swatch element"); + info("Get the filter swatch element"); let swatch = getRuleViewProperty(view, "body", "filter").valueSpan .querySelector(".ruleview-filterswatch"); - let filterTooltip = view.tooltips.filterEditor; + info("Click on the filter swatch element"); // Clicking on a cssfilter swatch sets the current filter value in the tooltip // which, in turn, makes the FilterWidget emit an "updated" event that causes // the rule-view to refresh. So we must wait for the ruleview-changed event. @@ -23,17 +23,20 @@ add_task(function*() { swatch.click(); yield onRuleViewChanged; + info("Get the cssfilter widget instance"); + let filterTooltip = view.tooltips.filterEditor; let widget = yield filterTooltip.widget; + info("Set a new value in the cssfilter widget"); onRuleViewChanged = view.once("ruleview-changed"); widget.setCssValue("blur(2px)"); yield waitForComputedStyleProperty("body", null, "filter", "blur(2px)"); yield onRuleViewChanged; - ok(true, "Changes previewed on the element"); - onRuleViewChanged = view.once("ruleview-changed"); - info("Pressing RETURN to commit changes"); + info("Press RETURN to commit changes"); + // Pressing return in the cssfilter tooltip triggeres 2 ruleview-changed + onRuleViewChanged = waitForNEvents(view, "ruleview-changed", 2); EventUtils.sendKey("RETURN", widget.styleWindow); yield onRuleViewChanged; diff --git a/devtools/client/inspector/rules/test/browser_rules_search-filter_context-menu.js b/devtools/client/inspector/rules/test/browser_rules_search-filter_context-menu.js index 6d1f1d485a22..e362a6467c93 100644 --- a/devtools/client/inspector/rules/test/browser_rules_search-filter_context-menu.js +++ b/devtools/client/inspector/rules/test/browser_rules_search-filter_context-menu.js @@ -36,8 +36,8 @@ add_task(function*() { is(cmdUndo.getAttribute("disabled"), "true", "cmdUndo is disabled"); is(cmdDelete.getAttribute("disabled"), "true", "cmdDelete is disabled"); is(cmdSelectAll.getAttribute("disabled"), "", "cmdSelectAll is enabled"); - is(cmdCut.getAttribute("disabled"), "", "cmdCut is enabled"); - is(cmdCopy.getAttribute("disabled"), "", "cmdCopy is enabled"); + is(cmdCut.getAttribute("disabled"), "true", "cmdCut is disabled"); + is(cmdCopy.getAttribute("disabled"), "true", "cmdCopy is disabled"); is(cmdPaste.getAttribute("disabled"), "true", "cmdPaste is disabled"); info("Closing context menu"); diff --git a/devtools/client/inspector/rules/test/browser_rules_select-and-copy-styles.js b/devtools/client/inspector/rules/test/browser_rules_select-and-copy-styles.js index accfaf345598..b14c8da0a7e9 100644 --- a/devtools/client/inspector/rules/test/browser_rules_select-and-copy-styles.js +++ b/devtools/client/inspector/rules/test/browser_rules_select-and-copy-styles.js @@ -97,8 +97,7 @@ function* checkSelectAll(view) { info("Checking that _SelectAll() then copy returns the correct " + "clipboard value"); view._contextmenu._onSelectAll(); - let expectedPattern = "[\\r\\n]+" + - "element {[\\r\\n]+" + + let expectedPattern = "element {[\\r\\n]+" + " margin: 10em;[\\r\\n]+" + " font-size: 14pt;[\\r\\n]+" + " font-family: helvetica,sans-serif;[\\r\\n]+" + diff --git a/devtools/client/inspector/rules/test/head.js b/devtools/client/inspector/rules/test/head.js index ef16f87f07c4..fcb3a33c587c 100644 --- a/devtools/client/inspector/rules/test/head.js +++ b/devtools/client/inspector/rules/test/head.js @@ -48,11 +48,11 @@ addTab = function(url) { * view is visible and ready */ function openRuleView() { - return openInspectorSidebarTab("ruleview").then(objects => { + return openInspectorSidebarTab("ruleview").then(({toolbox, inspector}) => { return { - toolbox: objects.toolbox, - inspector: objects.inspector, - view: objects.view.view + toolbox, + inspector, + view: inspector.ruleview.view }; }); } diff --git a/devtools/client/inspector/shared/style-inspector-menu.js b/devtools/client/inspector/shared/style-inspector-menu.js index 36da7fa29fb3..b6bd225165a4 100644 --- a/devtools/client/inspector/shared/style-inspector-menu.js +++ b/devtools/client/inspector/shared/style-inspector-menu.js @@ -369,7 +369,7 @@ StyleInspectorMenu.prototype = { */ _onSelectAll: function() { let selection = this.styleWindow.getSelection(); - selection.selectAllChildren(this.styleDocument.documentElement); + selection.selectAllChildren(this.view.element); }, /** diff --git a/devtools/client/inspector/shared/test/browser_styleinspector_refresh_when_active.js b/devtools/client/inspector/shared/test/browser_styleinspector_refresh_when_active.js index 4a1f96516892..576f7840e288 100644 --- a/devtools/client/inspector/shared/test/browser_styleinspector_refresh_when_active.js +++ b/devtools/client/inspector/shared/test/browser_styleinspector_refresh_when_active.js @@ -19,8 +19,7 @@ add_task(function*() { is(getRuleViewPropertyValue(view, "element", "color"), "red", "The rule-view shows the properties for test node one"); - let cView = inspector.sidebar.getWindowForTab("computedview") - .computedview.view; + let cView = inspector.computedview.view; let prop = getComputedViewProperty(cView, "color"); ok(!prop, "The computed-view doesn't show the properties for test node one"); diff --git a/devtools/client/inspector/shared/test/head.js b/devtools/client/inspector/shared/test/head.js index 13eff23db296..8d1bf4f12506 100644 --- a/devtools/client/inspector/shared/test/head.js +++ b/devtools/client/inspector/shared/test/head.js @@ -250,18 +250,13 @@ function waitForToolboxFrameFocus(toolbox) { var openInspectorSideBar = Task.async(function*(id) { let {toolbox, inspector} = yield openInspector(); - if (!hasSideBarTab(inspector, id)) { - info("Waiting for the " + id + " sidebar to be ready"); - yield inspector.sidebar.once(id + "-ready"); - } - info("Selecting the " + id + " sidebar"); inspector.sidebar.select(id); return { toolbox: toolbox, inspector: inspector, - view: inspector.sidebar.getWindowForTab(id)[id].view + view: inspector[id].view }; }); diff --git a/devtools/client/inspector/test/browser_inspector_initialization.js b/devtools/client/inspector/test/browser_inspector_initialization.js index 017d432c852f..dd9784458794 100644 --- a/devtools/client/inspector/test/browser_inspector_initialization.js +++ b/devtools/client/inspector/test/browser_inspector_initialization.js @@ -117,29 +117,24 @@ function* testBreadcrumbs(selector, inspector) { function* clickOnInspectMenuItem(testActor, selector) { info("Showing the contextual menu on node " + selector); + let contentAreaContextMenu = document.querySelector("#contentAreaContextMenu"); + let contextOpened = once(contentAreaContextMenu, "popupshown"); + yield testActor.synthesizeMouse({ selector: selector, center: true, options: {type: "contextmenu", button: 2} }); - // nsContextMenu also requires the popupNode to be set, but we can't set it to - // node under e10s as it's a CPOW, not a DOM node. But under e10s, - // nsContextMenu won't use the property anyway, so just try/catching is ok. - try { - document.popupNode = content.document.querySelector(selector); - } catch (e) {} - - let contentAreaContextMenu = document.querySelector("#contentAreaContextMenu"); - let contextMenu = new nsContextMenu(contentAreaContextMenu); + yield contextOpened; info("Triggering inspect action and hiding the menu."); - yield contextMenu.inspectNode(); + yield gContextMenu.inspectNode(); - contentAreaContextMenu.hidden = true; + let contextClosed = once(contentAreaContextMenu, "popuphidden"); contentAreaContextMenu.hidePopup(); - contextMenu.hiding(); info("Waiting for inspector to update."); yield getActiveInspector().once("inspector-updated"); + yield contextClosed; } diff --git a/devtools/client/inspector/test/browser_inspector_pseudoclass-lock.js b/devtools/client/inspector/test/browser_inspector_pseudoclass-lock.js index ac56a95e3df5..24ad5b90ceae 100644 --- a/devtools/client/inspector/test/browser_inspector_pseudoclass-lock.js +++ b/devtools/client/inspector/test/browser_inspector_pseudoclass-lock.js @@ -21,7 +21,10 @@ add_task(function*() { info("Creating the test tab and opening the rule-view"); let {toolbox, inspector, testActor} = yield openInspectorForURL(TEST_URL); - let view = yield ensureRuleView(inspector); + info("Selecting the ruleview sidebar"); + inspector.sidebar.select("ruleview"); + + let view = inspector.ruleview.view; info("Selecting the test node"); yield selectNode("#div-1", inspector); @@ -134,15 +137,3 @@ function* assertPseudoRemovedFromView(inspector, testActor, ruleview) { is(value, "", "pseudo-class removed from infobar selector"); yield inspector.toolbox.highlighter.hideBoxModel(); } - -function* ensureRuleView(inspector) { - if (!inspector.sidebar.getWindowForTab("ruleview")) { - info("Waiting for ruleview initialization to complete."); - yield inspector.sidebar.once("ruleview-ready"); - } - - info("Selecting the ruleview sidebar"); - inspector.sidebar.select("ruleview"); - - return inspector.sidebar.getWindowForTab("ruleview")["ruleview"].view; -} diff --git a/devtools/client/inspector/test/head.js b/devtools/client/inspector/test/head.js index 1d89849e4be6..088d11dfab85 100644 --- a/devtools/client/inspector/test/head.js +++ b/devtools/client/inspector/test/head.js @@ -170,32 +170,15 @@ function getActiveInspector() { var openInspectorSidebarTab = Task.async(function*(id, hostType) { let {toolbox, inspector} = yield openInspector(); - if (!hasSideBarTab(inspector, id)) { - info("Waiting for the " + id + " sidebar to be ready"); - yield inspector.sidebar.once(id + "-ready"); - } - info("Selecting the " + id + " sidebar"); inspector.sidebar.select(id); return { - toolbox: toolbox, - inspector: inspector, - view: inspector.sidebar.getWindowForTab(id)[id] + toolbox, + inspector }; }); -/** - * Checks whether the inspector's sidebar corresponding to the given id already - * exists - * @param {InspectorPanel} - * @param {String} - * @return {Boolean} - */ -function hasSideBarTab(inspector, id) { - return !!inspector.sidebar.getWindowForTab(id); -} - /** * Get the NodeFront for a node that matches a given css selector, via the * protocol. diff --git a/devtools/client/jar.mn b/devtools/client/jar.mn index e2b202e9265a..29a8bc0c0852 100644 --- a/devtools/client/jar.mn +++ b/devtools/client/jar.mn @@ -26,14 +26,9 @@ devtools.jar: content/styleeditor/styleeditor.xul (styleeditor/styleeditor.xul) content/styleeditor/styleeditor.css (styleeditor/styleeditor.css) content/storage/storage.xul (storage/storage.xul) - content/inspector/computed/computed.xhtml (inspector/computed/computed.xhtml) content/inspector/fonts/fonts.js (inspector/fonts/fonts.js) - content/inspector/fonts/fonts.xhtml (inspector/fonts/fonts.xhtml) content/inspector/layout/layout.js (inspector/layout/layout.js) - content/inspector/layout/layout.xhtml (inspector/layout/layout.xhtml) - content/inspector/markup/markup.css (inspector/markup/markup.css) content/inspector/markup/markup.xhtml (inspector/markup/markup.xhtml) - content/inspector/rules/rules.xhtml (inspector/rules/rules.xhtml) content/animationinspector/animation-controller.js (animationinspector/animation-controller.js) content/animationinspector/animation-panel.js (animationinspector/animation-panel.js) content/animationinspector/animation-inspector.xhtml (animationinspector/animation-inspector.xhtml) diff --git a/devtools/client/main.js b/devtools/client/main.js index 17fa414c78aa..7e9dc705b4f3 100644 --- a/devtools/client/main.js +++ b/devtools/client/main.js @@ -27,7 +27,7 @@ Object.defineProperty(exports, "TargetFactory", { }); const unloadObserver = { - observe: function(subject, topic, data) { + observe: function(subject) { if (subject.wrappedJSObject === require("@loader/unload")) { Services.obs.removeObserver(unloadObserver, "sdk:loader:destroy"); for (let definition of gDevTools.getToolDefinitionArray()) { diff --git a/devtools/client/netmonitor/netmonitor-controller.js b/devtools/client/netmonitor/netmonitor-controller.js index 2d2ea5300b48..0ec70712697b 100644 --- a/devtools/client/netmonitor/netmonitor-controller.js +++ b/devtools/client/netmonitor/netmonitor-controller.js @@ -253,7 +253,9 @@ var NetMonitorController = { this._disconnection = promise.defer(); // Wait for the connection to finish first. - yield this._connection.promise; + if (!this.isConnected()) { + yield this._connection.promise; + } // When debugging local or a remote instance, the connection is closed by // the RemoteTarget. The webconsole actor is stopped on disconnect. diff --git a/devtools/client/preferences/devtools.js b/devtools/client/preferences/devtools.js index 5e69b3f42c88..fe793f54a75f 100644 --- a/devtools/client/preferences/devtools.js +++ b/devtools/client/preferences/devtools.js @@ -17,6 +17,9 @@ pref("devtools.devedition.promo.url", "https://www.mozilla.org/firefox/developer // Disable the error console pref("devtools.errorconsole.enabled", false); +// DevTools development workflow +pref("devtools.loader.hotreload", false); + // Developer toolbar preferences pref("devtools.toolbar.enabled", true); pref("devtools.toolbar.visible", false); diff --git a/devtools/client/projecteditor/chrome/content/projecteditor.xul b/devtools/client/projecteditor/chrome/content/projecteditor.xul index 382d1aa66c58..b4ea0a0b6add 100644 --- a/devtools/client/projecteditor/chrome/content/projecteditor.xul +++ b/devtools/client/projecteditor/chrome/content/projecteditor.xul @@ -6,7 +6,6 @@ - diff --git a/devtools/client/responsive.html/test/browser/browser_viewport_basics.js b/devtools/client/responsive.html/test/browser/browser_viewport_basics.js index 5b34435a4e92..e2ab7c2bd020 100644 --- a/devtools/client/responsive.html/test/browser/browser_viewport_basics.js +++ b/devtools/client/responsive.html/test/browser/browser_viewport_basics.js @@ -3,9 +3,6 @@ "use strict"; -/* TODO: Bug 1242584 should make the following comment unnecessary */ -/* import-globals-from ../../../framework/test/shared-redux-head.js */ - // Test viewports basics after opening, like size and location const TEST_URL = "http://example.org/"; diff --git a/devtools/client/responsivedesign/test/head.js b/devtools/client/responsivedesign/test/head.js index 1fe076e120af..6481a4713552 100644 --- a/devtools/client/responsivedesign/test/head.js +++ b/devtools/client/responsivedesign/test/head.js @@ -125,18 +125,13 @@ function waitForToolboxFrameFocus(toolbox) { var openInspectorSideBar = Task.async(function*(id) { let {toolbox, inspector} = yield openInspector(); - if (!hasSideBarTab(inspector, id)) { - info("Waiting for the " + id + " sidebar to be ready"); - yield inspector.sidebar.once(id + "-ready"); - } - info("Selecting the " + id + " sidebar"); inspector.sidebar.select(id); return { toolbox: toolbox, inspector: inspector, - view: inspector.sidebar.getWindowForTab(id)[id].view + view: inspector[id].view }; }); diff --git a/devtools/client/shared/browser-loader.js b/devtools/client/shared/browser-loader.js index 0b1c1be0ea1b..b192756e18a1 100644 --- a/devtools/client/shared/browser-loader.js +++ b/devtools/client/shared/browser-loader.js @@ -6,8 +6,9 @@ var { classes: Cc, interfaces: Ci, utils: Cu } = Components; const loaders = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {}); -const { devtools, DevToolsLoader } = Cu.import("resource://devtools/shared/Loader.jsm", {}); +const { devtools } = Cu.import("resource://devtools/shared/Loader.jsm", {}); const { joinURI } = devtools.require("devtools/shared/path"); +const { Services } = devtools.require("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/AppConstants.jsm"); const BROWSER_BASED_DIRS = [ @@ -17,6 +18,40 @@ const BROWSER_BASED_DIRS = [ "resource://devtools/client/shared/redux" ]; +function clearCache() { + Services.obs.notifyObservers(null, "startupcache-invalidate", null); +} + +function hotReloadFile(window, require, loader, componentProxies, fileURI) { + dump("Hot reloading: " + fileURI + "\n"); + + if (fileURI.match(/\.js$/)) { + // Test for React proxy components + const proxy = componentProxies.get(fileURI); + if (proxy) { + // Remove the old module and re-require the new one; the require + // hook in the loader will take care of the rest + delete loader.modules[fileURI]; + clearCache(); + require(fileURI); + } + } else if (fileURI.match(/\.css$/)) { + const links = [...window.document.getElementsByTagNameNS("http://www.w3.org/1999/xhtml", "link")]; + links.forEach(link => { + if (link.href.indexOf(fileURI) === 0) { + const parentNode = link.parentNode; + const newLink = window.document.createElementNS("http://www.w3.org/1999/xhtml", "link"); + newLink.rel = "stylesheet"; + newLink.type = "text/css"; + newLink.href = fileURI + "?s=" + Math.random(); + + parentNode.insertBefore(newLink, link); + parentNode.removeChild(link); + } + }); + } +} + /* * Create a loader to be used in a browser environment. This evaluates * modules in their own environment, but sets window (the normal @@ -45,6 +80,8 @@ const BROWSER_BASED_DIRS = [ function BrowserLoader(baseURI, window) { const loaderOptions = devtools.require("@loader/options"); const dynamicPaths = {}; + const componentProxies = new Map(); + const hotReloadEnabled = Services.prefs.getBoolPref("devtools.loader.hotreload"); if(AppConstants.DEBUG || AppConstants.DEBUG_JS_MODULES) { dynamicPaths["devtools/client/shared/vendor/react"] = @@ -81,12 +118,47 @@ function BrowserLoader(baseURI, window) { } }; + if(hotReloadEnabled) { + opts.loadModuleHook = (module, require) => { + const { uri, exports } = module; + + if (exports.prototype && + exports.prototype.isReactComponent) { + const { createProxy, getForceUpdate } = + require("devtools/client/shared/vendor/react-proxy"); + const React = require("devtools/client/shared/vendor/react"); + + if (!componentProxies.get(uri)) { + const proxy = createProxy(exports); + componentProxies.set(uri, proxy); + module.exports = proxy.get(); + } + else { + const proxy = componentProxies.get(uri); + const instances = proxy.update(exports); + instances.forEach(getForceUpdate(React)); + module.exports = proxy.get(); + } + } + return exports; + } + } + + const mainModule = loaders.Module(baseURI, joinURI(baseURI, "main.js")); const mainLoader = loaders.Loader(opts); + const require = loaders.Require(mainLoader, mainModule); + + if(hotReloadEnabled) { + const watcher = devtools.require("devtools/client/shared/file-watcher"); + watcher.on("file-changed", (_, fileURI) => { + hotReloadFile(window, require, mainLoader, componentProxies, fileURI); + }); + } return { loader: mainLoader, - require: loaders.Require(mainLoader, mainModule) + require: require }; } diff --git a/devtools/client/shared/file-watcher-worker.js b/devtools/client/shared/file-watcher-worker.js new file mode 100644 index 000000000000..2f1c4575432c --- /dev/null +++ b/devtools/client/shared/file-watcher-worker.js @@ -0,0 +1,78 @@ +/* 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"; + +/* eslint-env worker */ +/* global OS */ +importScripts("resource://gre/modules/osfile.jsm"); + +const modifiedTimes = new Map(); + +function gatherFiles(path, fileRegex) { + let files = []; + const iterator = new OS.File.DirectoryIterator(path); + + try { + for (let child in iterator) { + // Don't descend into test directories. Saves us some time and + // there's no reason to. + if (child.isDir && !child.path.endsWith("/test")) { + files = files.concat(gatherFiles(child.path, fileRegex)); + } else if (child.path.match(fileRegex)) { + let info; + try { + info = OS.File.stat(child.path); + } catch (e) { + // Just ignore it. + continue; + } + + files.push(child.path); + modifiedTimes.set(child.path, info.lastModificationDate.getTime()); + } + } + } finally { + iterator.close(); + } + + return files; +} + +function scanFiles(files, onChangedFile) { + files.forEach(file => { + let info; + try { + info = OS.File.stat(file); + } catch (e) { + // Just ignore it. It was probably deleted. + return; + } + + const lastTime = modifiedTimes.get(file); + + if (info.lastModificationDate.getTime() > lastTime) { + modifiedTimes.set(file, info.lastModificationDate.getTime()); + onChangedFile(file); + } + }); +} + +onmessage = function(event) { + const { path, fileRegex } = event.data; + let info = OS.File.stat(path); + if (!info.isDir) { + throw new Error("watcher expects a directory as root path"); + } + + // We get a list of all the files upfront, which means we don't + // support adding new files. But you need to rebuild Firefox when + // adding a new file anyway. + const files = gatherFiles(path, fileRegex || /.*/); + + // Every second, scan for file changes by stat-ing each of them and + // comparing modification time. + setInterval(() => { + scanFiles(files, changedFile => postMessage(changedFile)); + }, 1000); +}; diff --git a/devtools/client/shared/file-watcher.js b/devtools/client/shared/file-watcher.js new file mode 100644 index 000000000000..f423a64fe6de --- /dev/null +++ b/devtools/client/shared/file-watcher.js @@ -0,0 +1,73 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { Ci, ChromeWorker } = require("chrome"); +const { Services } = require("resource://gre/modules/Services.jsm"); +const EventEmitter = require("devtools/shared/event-emitter"); + +const HOTRELOAD_PREF = "devtools.loader.hotreload"; + +function resolveResourceURI(uri) { + const handler = Services.io.getProtocolHandler("resource") + .QueryInterface(Ci.nsIResProtocolHandler); + return handler.resolveURI(Services.io.newURI(uri, null, null)); +} + +function watchFiles(path, onFileChanged) { + if (!path.startsWith("devtools/")) { + throw new Error("`watchFiles` expects a devtools path"); + } + + // We need to figure out a local path to watch. We start with + // whatever devtools points to. + let resolvedRootURI = resolveResourceURI("resource://devtools"); + if (resolvedRootURI.match(/\/obj\-.*/)) { + // Move from the built directory to the user's local files + resolvedRootURI = resolvedRootURI.replace(/\/obj\-.*/, "") + "/devtools"; + } + resolvedRootURI = resolvedRootURI.replace(/^file:\/\//, ""); + const localURI = resolvedRootURI + "/" + path.replace(/^devtools\//, ""); + + const watchWorker = new ChromeWorker( + "resource://devtools/client/shared/file-watcher-worker.js" + ); + + watchWorker.onmessage = event => { + // We need to turn a local path back into a resource URI (or + // chrome). This means that this system will only work when built + // files are symlinked, so that these URIs actually read from + // local sources. There might be a better way to do this. + const relativePath = event.data.replace(resolvedRootURI + "/", ""); + if (relativePath.startsWith("client/themes")) { + onFileChanged(relativePath.replace("client/themes", + "chrome://devtools/skin")); + } + onFileChanged("resource://devtools/" + relativePath); + }; + + watchWorker.postMessage({ path: localURI, fileRegex: /\.(js|css)$/ }); + return watchWorker; +} + +EventEmitter.decorate(module.exports); + +let watchWorker; +function onPrefChange() { + if (Services.prefs.getBoolPref(HOTRELOAD_PREF) && !watchWorker) { + watchWorker = watchFiles("devtools/client", changedFile => { + module.exports.emit("file-changed", changedFile); + }); + } + else if(watchWorker) { + watchWorker.terminate(); + watchWorker = null; + } +} + +Services.prefs.addObserver(HOTRELOAD_PREF, { + observe: onPrefChange +}, false); + +onPrefChange(); diff --git a/devtools/client/shared/moz.build b/devtools/client/shared/moz.build index 8c6b830fcda3..21f2dbbcf866 100644 --- a/devtools/client/shared/moz.build +++ b/devtools/client/shared/moz.build @@ -25,6 +25,8 @@ DevToolsModules( 'devices.js', 'DOMHelpers.jsm', 'doorhanger.js', + 'file-watcher-worker.js', + 'file-watcher.js', 'frame-script-utils.js', 'getjson.js', 'inplace-editor.js', diff --git a/devtools/client/shared/vendor/moz.build b/devtools/client/shared/vendor/moz.build index 7e2aec4aa424..6c4d5c00cbe3 100644 --- a/devtools/client/shared/vendor/moz.build +++ b/devtools/client/shared/vendor/moz.build @@ -6,11 +6,14 @@ modules = [] +# react-dev is used if either debug mode is enabled, +# so include it for both if CONFIG['DEBUG_JS_MODULES'] or CONFIG['MOZ_DEBUG']: modules += ['react-dev.js'] modules += [ 'react-dom.js', + 'react-proxy.js', 'react-redux.js', 'react.js', 'redux.js', diff --git a/devtools/client/shared/vendor/react-proxy.js b/devtools/client/shared/vendor/react-proxy.js new file mode 100644 index 000000000000..95346a0269bf --- /dev/null +++ b/devtools/client/shared/vendor/react-proxy.js @@ -0,0 +1,1909 @@ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define(factory); + else if(typeof exports === 'object') + exports["ReactProxy"] = factory(); + else + root["ReactProxy"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; +/******/ +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.loaded = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + function _interopRequire(obj) { return obj && obj.__esModule ? obj['default'] : obj; } + + var _createClassProxy = __webpack_require__(12); + + exports.createProxy = _interopRequire(_createClassProxy); + + var _reactDeepForceUpdate = __webpack_require__(39); + + exports.getForceUpdate = _interopRequire(_reactDeepForceUpdate); + +/***/ }, +/* 1 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`. + * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(1); + * // => false + */ + function isObject(value) { + // Avoid a V8 JIT bug in Chrome 19-20. + // See https://code.google.com/p/v8/issues/detail?id=2291 for more details. + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); + } + + module.exports = isObject; + + +/***/ }, +/* 2 */ +/***/ function(module, exports, __webpack_require__) { + + var getLength = __webpack_require__(30), + isLength = __webpack_require__(5); + + /** + * Checks if `value` is array-like. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is array-like, else `false`. + */ + function isArrayLike(value) { + return value != null && isLength(getLength(value)); + } + + module.exports = isArrayLike; + + +/***/ }, +/* 3 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Checks if `value` is object-like. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + */ + function isObjectLike(value) { + return !!value && typeof value == 'object'; + } + + module.exports = isObjectLike; + + +/***/ }, +/* 4 */ +/***/ function(module, exports, __webpack_require__) { + + var isNative = __webpack_require__(35); + + /** + * Gets the native function at `key` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. + */ + function getNative(object, key) { + var value = object == null ? undefined : object[key]; + return isNative(value) ? value : undefined; + } + + module.exports = getNative; + + +/***/ }, +/* 5 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer) + * of an array-like value. + */ + var MAX_SAFE_INTEGER = 9007199254740991; + + /** + * Checks if `value` is a valid array-like length. + * + * **Note:** This function is based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength). + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. + */ + function isLength(value) { + return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; + } + + module.exports = isLength; + + +/***/ }, +/* 6 */ +/***/ function(module, exports, __webpack_require__) { + + /** Used to detect unsigned integer values. */ + var reIsUint = /^\d+$/; + + /** + * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer) + * of an array-like value. + */ + var MAX_SAFE_INTEGER = 9007199254740991; + + /** + * Checks if `value` is a valid array-like index. + * + * @private + * @param {*} value The value to check. + * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. + * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. + */ + function isIndex(value, length) { + value = (typeof value == 'number' || reIsUint.test(value)) ? +value : -1; + length = length == null ? MAX_SAFE_INTEGER : length; + return value > -1 && value % 1 == 0 && value < length; + } + + module.exports = isIndex; + + +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { + + var isArrayLike = __webpack_require__(2), + isObjectLike = __webpack_require__(3); + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** Native method references. */ + var propertyIsEnumerable = objectProto.propertyIsEnumerable; + + /** + * Checks if `value` is classified as an `arguments` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isArguments(function() { return arguments; }()); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + function isArguments(value) { + return isObjectLike(value) && isArrayLike(value) && + hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee'); + } + + module.exports = isArguments; + + +/***/ }, +/* 8 */ +/***/ function(module, exports, __webpack_require__) { + + var getNative = __webpack_require__(4), + isLength = __webpack_require__(5), + isObjectLike = __webpack_require__(3); + + /** `Object#toString` result references. */ + var arrayTag = '[object Array]'; + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** + * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) + * of values. + */ + var objToString = objectProto.toString; + + /* Native method references for those with the same name as other `lodash` methods. */ + var nativeIsArray = getNative(Array, 'isArray'); + + /** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(function() { return arguments; }()); + * // => false + */ + var isArray = nativeIsArray || function(value) { + return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag; + }; + + module.exports = isArray; + + +/***/ }, +/* 9 */ +/***/ function(module, exports, __webpack_require__) { + + /** Used as the `TypeError` message for "Functions" methods. */ + var FUNC_ERROR_TEXT = 'Expected a function'; + + /* Native method references for those with the same name as other `lodash` methods. */ + var nativeMax = Math.max; + + /** + * Creates a function that invokes `func` with the `this` binding of the + * created function and arguments from `start` and beyond provided as an array. + * + * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/Web/JavaScript/Reference/Functions/rest_parameters). + * + * @static + * @memberOf _ + * @category Function + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @returns {Function} Returns the new function. + * @example + * + * var say = _.restParam(function(what, names) { + * return what + ' ' + _.initial(names).join(', ') + + * (_.size(names) > 1 ? ', & ' : '') + _.last(names); + * }); + * + * say('hello', 'fred', 'barney', 'pebbles'); + * // => 'hello fred, barney, & pebbles' + */ + function restParam(func, start) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0); + return function() { + var args = arguments, + index = -1, + length = nativeMax(args.length - start, 0), + rest = Array(length); + + while (++index < length) { + rest[index] = args[start + index]; + } + switch (start) { + case 0: return func.call(this, rest); + case 1: return func.call(this, args[0], rest); + case 2: return func.call(this, args[0], args[1], rest); + } + var otherArgs = Array(start + 1); + index = -1; + while (++index < start) { + otherArgs[index] = args[index]; + } + otherArgs[start] = rest; + return func.apply(this, otherArgs); + }; + } + + module.exports = restParam; + + +/***/ }, +/* 10 */ +/***/ function(module, exports, __webpack_require__) { + + var getNative = __webpack_require__(4), + isArrayLike = __webpack_require__(2), + isObject = __webpack_require__(1), + shimKeys = __webpack_require__(33); + + /* Native method references for those with the same name as other `lodash` methods. */ + var nativeKeys = getNative(Object, 'keys'); + + /** + * Creates an array of the own enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. See the + * [ES spec](http://ecma-international.org/ecma-262/6.0/#sec-object.keys) + * for more details. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keys(new Foo); + * // => ['a', 'b'] (iteration order is not guaranteed) + * + * _.keys('hi'); + * // => ['0', '1'] + */ + var keys = !nativeKeys ? shimKeys : function(object) { + var Ctor = object == null ? undefined : object.constructor; + if ((typeof Ctor == 'function' && Ctor.prototype === object) || + (typeof object != 'function' && isArrayLike(object))) { + return shimKeys(object); + } + return isObject(object) ? nativeKeys(object) : []; + }; + + module.exports = keys; + + +/***/ }, +/* 11 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of React source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * Original: + * https://github.com/facebook/react/blob/6508b1ad273a6f371e8d90ae676e5390199461b4/src/isomorphic/classic/class/ReactClass.js#L650-L713 + */ + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + exports['default'] = bindAutoBindMethods; + function bindAutoBindMethod(component, method) { + var boundMethod = method.bind(component); + + boundMethod.__reactBoundContext = component; + boundMethod.__reactBoundMethod = method; + boundMethod.__reactBoundArguments = null; + + var componentName = component.constructor.displayName, + _bind = boundMethod.bind; + + boundMethod.bind = function (newThis) { + var args = Array.prototype.slice.call(arguments, 1); + if (newThis !== component && newThis !== null) { + console.warn('bind(): React component methods may only be bound to the ' + 'component instance. See ' + componentName); + } else if (!args.length) { + console.warn('bind(): You are binding a component method to the component. ' + 'React does this for you automatically in a high-performance ' + 'way, so you can safely remove this call. See ' + componentName); + return boundMethod; + } + + var reboundMethod = _bind.apply(boundMethod, arguments); + reboundMethod.__reactBoundContext = component; + reboundMethod.__reactBoundMethod = method; + reboundMethod.__reactBoundArguments = args; + + return reboundMethod; + }; + + return boundMethod; + } + + function bindAutoBindMethods(component) { + for (var autoBindKey in component.__reactAutoBindMap) { + if (!component.__reactAutoBindMap.hasOwnProperty(autoBindKey)) { + return; + } + + // Tweak: skip methods that are already bound. + // This is to preserve method reference in case it is used + // as a subscription handler that needs to be detached later. + if (component.hasOwnProperty(autoBindKey) && component[autoBindKey].__reactBoundContext === component) { + continue; + } + + var method = component.__reactAutoBindMap[autoBindKey]; + component[autoBindKey] = bindAutoBindMethod(component, method); + } + } + + ; + module.exports = exports['default']; + +/***/ }, +/* 12 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + + var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + + exports['default'] = proxyClass; + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _createPrototypeProxy = __webpack_require__(13); + + var _createPrototypeProxy2 = _interopRequireDefault(_createPrototypeProxy); + + var _bindAutoBindMethods = __webpack_require__(11); + + var _bindAutoBindMethods2 = _interopRequireDefault(_bindAutoBindMethods); + + var _deleteUnknownAutoBindMethods = __webpack_require__(14); + + var _deleteUnknownAutoBindMethods2 = _interopRequireDefault(_deleteUnknownAutoBindMethods); + + var RESERVED_STATICS = ['length', 'name', 'arguments', 'caller', 'prototype', 'toString']; + + function isEqualDescriptor(a, b) { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + for (var key in a) { + if (a[key] !== b[key]) { + return false; + } + } + return true; + } + + function proxyClass(InitialClass) { + // Prevent double wrapping. + // Given a proxy class, return the existing proxy managing it. + if (Object.prototype.hasOwnProperty.call(InitialClass, '__reactPatchProxy')) { + return InitialClass.__reactPatchProxy; + } + + var prototypeProxy = (0, _createPrototypeProxy2['default'])(); + var CurrentClass = undefined; + + var staticDescriptors = {}; + function wasStaticModifiedByUser(key) { + // Compare the descriptor with the one we previously set ourselves. + var currentDescriptor = Object.getOwnPropertyDescriptor(ProxyClass, key); + return !isEqualDescriptor(staticDescriptors[key], currentDescriptor); + } + + var ProxyClass = undefined; + try { + // Create a proxy constructor with matching name + ProxyClass = new Function('getCurrentClass', 'return function ' + (InitialClass.name || 'ProxyClass') + '() {\n return getCurrentClass().apply(this, arguments);\n }')(function () { + return CurrentClass; + }); + } catch (err) { + // Some environments may forbid dynamic evaluation + ProxyClass = function () { + return CurrentClass.apply(this, arguments); + }; + } + + // Point proxy constructor to the proxy prototype + ProxyClass.prototype = prototypeProxy.get(); + + // Proxy toString() to the current constructor + ProxyClass.toString = function toString() { + return CurrentClass.toString(); + }; + + function update(_x) { + var _again = true; + + _function: while (_again) { + var NextClass = _x; + mountedInstances = undefined; + _again = false; + + if (typeof NextClass !== 'function') { + throw new Error('Expected a constructor.'); + } + + // Prevent proxy cycles + if (Object.prototype.hasOwnProperty.call(NextClass, '__reactPatchProxy')) { + _x = NextClass.__reactPatchProxy.__getCurrent(); + _again = true; + continue _function; + } + + // Save the next constructor so we call it + CurrentClass = NextClass; + + // Update the prototype proxy with new methods + var mountedInstances = prototypeProxy.update(NextClass.prototype); + + // Set up the constructor property so accessing the statics work + ProxyClass.prototype.constructor = ProxyClass; + + // Set up the same prototype for inherited statics + ProxyClass.__proto__ = NextClass.__proto__; + + // Copy static methods and properties + Object.getOwnPropertyNames(NextClass).forEach(function (key) { + if (RESERVED_STATICS.indexOf(key) > -1) { + return; + } + + var staticDescriptor = _extends({}, Object.getOwnPropertyDescriptor(NextClass, key), { + configurable: true + }); + + // Copy static unless user has redefined it at runtime + if (!wasStaticModifiedByUser(key)) { + Object.defineProperty(ProxyClass, key, staticDescriptor); + staticDescriptors[key] = staticDescriptor; + } + }); + + // Remove old static methods and properties + Object.getOwnPropertyNames(ProxyClass).forEach(function (key) { + if (RESERVED_STATICS.indexOf(key) > -1) { + return; + } + + // Skip statics that exist on the next class + if (NextClass.hasOwnProperty(key)) { + return; + } + + // Skip non-configurable statics + var descriptor = Object.getOwnPropertyDescriptor(ProxyClass, key); + if (descriptor && !descriptor.configurable) { + return; + } + + // Delete static unless user has redefined it at runtime + if (!wasStaticModifiedByUser(key)) { + delete ProxyClass[key]; + delete staticDescriptors[key]; + } + }); + + // Try to infer displayName + ProxyClass.displayName = NextClass.displayName || NextClass.name; + + // We might have added new methods that need to be auto-bound + mountedInstances.forEach(_bindAutoBindMethods2['default']); + mountedInstances.forEach(_deleteUnknownAutoBindMethods2['default']); + + // Let the user take care of redrawing + return mountedInstances; + } + }; + + function get() { + return ProxyClass; + } + + function getCurrent() { + return CurrentClass; + } + + update(InitialClass); + + var proxy = { get: get, update: update }; + + Object.defineProperty(proxy, '__getCurrent', { + configurable: false, + writable: false, + enumerable: false, + value: getCurrent + }); + + Object.defineProperty(ProxyClass, '__reactPatchProxy', { + configurable: false, + writable: false, + enumerable: false, + value: proxy + }); + + return proxy; + } + + module.exports = exports['default']; + +/***/ }, +/* 13 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + exports['default'] = createPrototypeProxy; + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + + var _lodashObjectAssign = __webpack_require__(36); + + var _lodashObjectAssign2 = _interopRequireDefault(_lodashObjectAssign); + + var _lodashArrayDifference = __webpack_require__(15); + + var _lodashArrayDifference2 = _interopRequireDefault(_lodashArrayDifference); + + function createPrototypeProxy() { + var proxy = {}; + var current = null; + var mountedInstances = []; + + /** + * Creates a proxied toString() method pointing to the current version's toString(). + */ + function proxyToString(name) { + // Wrap to always call the current version + return function toString() { + if (typeof current[name] === 'function') { + return current[name].toString(); + } else { + return ''; + } + }; + } + + /** + * Creates a proxied method that calls the current version, whenever available. + */ + function proxyMethod(name) { + // Wrap to always call the current version + var proxiedMethod = function proxiedMethod() { + if (typeof current[name] === 'function') { + return current[name].apply(this, arguments); + } + }; + + // Copy properties of the original function, if any + (0, _lodashObjectAssign2['default'])(proxiedMethod, current[name]); + proxiedMethod.toString = proxyToString(name); + + return proxiedMethod; + } + + /** + * Augments the original componentDidMount with instance tracking. + */ + function proxiedComponentDidMount() { + mountedInstances.push(this); + if (typeof current.componentDidMount === 'function') { + return current.componentDidMount.apply(this, arguments); + } + } + proxiedComponentDidMount.toString = proxyToString('componentDidMount'); + + /** + * Augments the original componentWillUnmount with instance tracking. + */ + function proxiedComponentWillUnmount() { + var index = mountedInstances.indexOf(this); + // Unless we're in a weird environment without componentDidMount + if (index !== -1) { + mountedInstances.splice(index, 1); + } + if (typeof current.componentWillUnmount === 'function') { + return current.componentWillUnmount.apply(this, arguments); + } + } + proxiedComponentWillUnmount.toString = proxyToString('componentWillUnmount'); + + /** + * Defines a property on the proxy. + */ + function defineProxyProperty(name, descriptor) { + Object.defineProperty(proxy, name, descriptor); + } + + /** + * Defines a property, attempting to keep the original descriptor configuration. + */ + function defineProxyPropertyWithValue(name, value) { + var _ref = Object.getOwnPropertyDescriptor(current, name) || {}; + + var _ref$enumerable = _ref.enumerable; + var enumerable = _ref$enumerable === undefined ? false : _ref$enumerable; + var _ref$writable = _ref.writable; + var writable = _ref$writable === undefined ? true : _ref$writable; + + defineProxyProperty(name, { + configurable: true, + enumerable: enumerable, + writable: writable, + value: value + }); + } + + /** + * Creates an auto-bind map mimicking the original map, but directed at proxy. + */ + function createAutoBindMap() { + if (!current.__reactAutoBindMap) { + return; + } + + var __reactAutoBindMap = {}; + for (var _name in current.__reactAutoBindMap) { + if (current.__reactAutoBindMap.hasOwnProperty(_name)) { + __reactAutoBindMap[_name] = proxy[_name]; + } + } + + return __reactAutoBindMap; + } + + /** + * Applies the updated prototype. + */ + function update(next) { + // Save current source of truth + current = next; + + // Find changed property names + var currentNames = Object.getOwnPropertyNames(current); + var previousName = Object.getOwnPropertyNames(proxy); + var addedNames = (0, _lodashArrayDifference2['default'])(currentNames, previousName); + var removedNames = (0, _lodashArrayDifference2['default'])(previousName, currentNames); + + // Remove properties and methods that are no longer there + removedNames.forEach(function (name) { + delete proxy[name]; + }); + + // Copy every descriptor + currentNames.forEach(function (name) { + var descriptor = Object.getOwnPropertyDescriptor(current, name); + if (typeof descriptor.value === 'function') { + // Functions require additional wrapping so they can be bound later + defineProxyPropertyWithValue(name, proxyMethod(name)); + } else { + // Other values can be copied directly + defineProxyProperty(name, descriptor); + } + }); + + // Track mounting and unmounting + defineProxyPropertyWithValue('componentDidMount', proxiedComponentDidMount); + defineProxyPropertyWithValue('componentWillUnmount', proxiedComponentWillUnmount); + defineProxyPropertyWithValue('__reactAutoBindMap', createAutoBindMap()); + + // Set up the prototype chain + proxy.__proto__ = next; + + return mountedInstances; + } + + /** + * Returns the up-to-date proxy prototype. + */ + function get() { + return proxy; + } + + return { + update: update, + get: get + }; + } + + ; + module.exports = exports['default']; + +/***/ }, +/* 14 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + Object.defineProperty(exports, '__esModule', { + value: true + }); + exports['default'] = deleteUnknownAutoBindMethods; + function shouldDeleteClassicInstanceMethod(component, name) { + if (component.__reactAutoBindMap.hasOwnProperty(name)) { + // It's a known autobound function, keep it + return false; + } + + if (component[name].__reactBoundArguments !== null) { + // It's a function bound to specific args, keep it + return false; + } + + // It's a cached bound method for a function + // that was deleted by user, so we delete it from component. + return true; + } + + function shouldDeleteModernInstanceMethod(component, name) { + var prototype = component.constructor.prototype; + + var prototypeDescriptor = Object.getOwnPropertyDescriptor(prototype, name); + + if (!prototypeDescriptor || !prototypeDescriptor.get) { + // This is definitely not an autobinding getter + return false; + } + + if (prototypeDescriptor.get().length !== component[name].length) { + // The length doesn't match, bail out + return false; + } + + // This seems like a method bound using an autobinding getter on the prototype + // Hopefully we won't run into too many false positives. + return true; + } + + function shouldDeleteInstanceMethod(component, name) { + var descriptor = Object.getOwnPropertyDescriptor(component, name); + if (typeof descriptor.value !== 'function') { + // Not a function, or something fancy: bail out + return; + } + + if (component.__reactAutoBindMap) { + // Classic + return shouldDeleteClassicInstanceMethod(component, name); + } else { + // Modern + return shouldDeleteModernInstanceMethod(component, name); + } + } + + /** + * Deletes autobound methods from the instance. + * + * For classic React classes, we only delete the methods that no longer exist in map. + * This means the user actually deleted them in code. + * + * For modern classes, we delete methods that exist on prototype with the same length, + * and which have getters on prototype, but are normal values on the instance. + * This is usually an indication that an autobinding decorator is being used, + * and the getter will re-generate the memoized handler on next access. + */ + + function deleteUnknownAutoBindMethods(component) { + var names = Object.getOwnPropertyNames(component); + + names.forEach(function (name) { + if (shouldDeleteInstanceMethod(component, name)) { + delete component[name]; + } + }); + } + + module.exports = exports['default']; + +/***/ }, +/* 15 */ +/***/ function(module, exports, __webpack_require__) { + + var baseDifference = __webpack_require__(21), + baseFlatten = __webpack_require__(22), + isArrayLike = __webpack_require__(2), + isObjectLike = __webpack_require__(3), + restParam = __webpack_require__(9); + + /** + * Creates an array of unique `array` values not included in the other + * provided arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) + * for equality comparisons. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to inspect. + * @param {...Array} [values] The arrays of values to exclude. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.difference([1, 2, 3], [4, 2]); + * // => [1, 3] + */ + var difference = restParam(function(array, values) { + return (isObjectLike(array) && isArrayLike(array)) + ? baseDifference(array, baseFlatten(values, false, true)) + : []; + }); + + module.exports = difference; + + +/***/ }, +/* 16 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) {var cachePush = __webpack_require__(27), + getNative = __webpack_require__(4); + + /** Native method references. */ + var Set = getNative(global, 'Set'); + + /* Native method references for those with the same name as other `lodash` methods. */ + var nativeCreate = getNative(Object, 'create'); + + /** + * + * Creates a cache object to store unique values. + * + * @private + * @param {Array} [values] The values to cache. + */ + function SetCache(values) { + var length = values ? values.length : 0; + + this.data = { 'hash': nativeCreate(null), 'set': new Set }; + while (length--) { + this.push(values[length]); + } + } + + // Add functions to the `Set` cache. + SetCache.prototype.push = cachePush; + + module.exports = SetCache; + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 17 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Appends the elements of `values` to `array`. + * + * @private + * @param {Array} array The array to modify. + * @param {Array} values The values to append. + * @returns {Array} Returns `array`. + */ + function arrayPush(array, values) { + var index = -1, + length = values.length, + offset = array.length; + + while (++index < length) { + array[offset + index] = values[index]; + } + return array; + } + + module.exports = arrayPush; + + +/***/ }, +/* 18 */ +/***/ function(module, exports, __webpack_require__) { + + var keys = __webpack_require__(10); + + /** + * A specialized version of `_.assign` for customizing assigned values without + * support for argument juggling, multiple sources, and `this` binding `customizer` + * functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {Function} customizer The function to customize assigned values. + * @returns {Object} Returns `object`. + */ + function assignWith(object, source, customizer) { + var index = -1, + props = keys(source), + length = props.length; + + while (++index < length) { + var key = props[index], + value = object[key], + result = customizer(value, source[key], key, object, source); + + if ((result === result ? (result !== value) : (value === value)) || + (value === undefined && !(key in object))) { + object[key] = result; + } + } + return object; + } + + module.exports = assignWith; + + +/***/ }, +/* 19 */ +/***/ function(module, exports, __webpack_require__) { + + var baseCopy = __webpack_require__(20), + keys = __webpack_require__(10); + + /** + * The base implementation of `_.assign` without support for argument juggling, + * multiple sources, and `customizer` functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @returns {Object} Returns `object`. + */ + function baseAssign(object, source) { + return source == null + ? object + : baseCopy(source, keys(source), object); + } + + module.exports = baseAssign; + + +/***/ }, +/* 20 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Copies properties of `source` to `object`. + * + * @private + * @param {Object} source The object to copy properties from. + * @param {Array} props The property names to copy. + * @param {Object} [object={}] The object to copy properties to. + * @returns {Object} Returns `object`. + */ + function baseCopy(source, props, object) { + object || (object = {}); + + var index = -1, + length = props.length; + + while (++index < length) { + var key = props[index]; + object[key] = source[key]; + } + return object; + } + + module.exports = baseCopy; + + +/***/ }, +/* 21 */ +/***/ function(module, exports, __webpack_require__) { + + var baseIndexOf = __webpack_require__(23), + cacheIndexOf = __webpack_require__(26), + createCache = __webpack_require__(29); + + /** Used as the size to enable large array optimizations. */ + var LARGE_ARRAY_SIZE = 200; + + /** + * The base implementation of `_.difference` which accepts a single array + * of values to exclude. + * + * @private + * @param {Array} array The array to inspect. + * @param {Array} values The values to exclude. + * @returns {Array} Returns the new array of filtered values. + */ + function baseDifference(array, values) { + var length = array ? array.length : 0, + result = []; + + if (!length) { + return result; + } + var index = -1, + indexOf = baseIndexOf, + isCommon = true, + cache = (isCommon && values.length >= LARGE_ARRAY_SIZE) ? createCache(values) : null, + valuesLength = values.length; + + if (cache) { + indexOf = cacheIndexOf; + isCommon = false; + values = cache; + } + outer: + while (++index < length) { + var value = array[index]; + + if (isCommon && value === value) { + var valuesIndex = valuesLength; + while (valuesIndex--) { + if (values[valuesIndex] === value) { + continue outer; + } + } + result.push(value); + } + else if (indexOf(values, value, 0) < 0) { + result.push(value); + } + } + return result; + } + + module.exports = baseDifference; + + +/***/ }, +/* 22 */ +/***/ function(module, exports, __webpack_require__) { + + var arrayPush = __webpack_require__(17), + isArguments = __webpack_require__(7), + isArray = __webpack_require__(8), + isArrayLike = __webpack_require__(2), + isObjectLike = __webpack_require__(3); + + /** + * The base implementation of `_.flatten` with added support for restricting + * flattening and specifying the start index. + * + * @private + * @param {Array} array The array to flatten. + * @param {boolean} [isDeep] Specify a deep flatten. + * @param {boolean} [isStrict] Restrict flattening to arrays-like objects. + * @param {Array} [result=[]] The initial result value. + * @returns {Array} Returns the new flattened array. + */ + function baseFlatten(array, isDeep, isStrict, result) { + result || (result = []); + + var index = -1, + length = array.length; + + while (++index < length) { + var value = array[index]; + if (isObjectLike(value) && isArrayLike(value) && + (isStrict || isArray(value) || isArguments(value))) { + if (isDeep) { + // Recursively flatten arrays (susceptible to call stack limits). + baseFlatten(value, isDeep, isStrict, result); + } else { + arrayPush(result, value); + } + } else if (!isStrict) { + result[result.length] = value; + } + } + return result; + } + + module.exports = baseFlatten; + + +/***/ }, +/* 23 */ +/***/ function(module, exports, __webpack_require__) { + + var indexOfNaN = __webpack_require__(31); + + /** + * The base implementation of `_.indexOf` without support for binary searches. + * + * @private + * @param {Array} array The array to search. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function baseIndexOf(array, value, fromIndex) { + if (value !== value) { + return indexOfNaN(array, fromIndex); + } + var index = fromIndex - 1, + length = array.length; + + while (++index < length) { + if (array[index] === value) { + return index; + } + } + return -1; + } + + module.exports = baseIndexOf; + + +/***/ }, +/* 24 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * The base implementation of `_.property` without support for deep paths. + * + * @private + * @param {string} key The key of the property to get. + * @returns {Function} Returns the new function. + */ + function baseProperty(key) { + return function(object) { + return object == null ? undefined : object[key]; + }; + } + + module.exports = baseProperty; + + +/***/ }, +/* 25 */ +/***/ function(module, exports, __webpack_require__) { + + var identity = __webpack_require__(38); + + /** + * A specialized version of `baseCallback` which only supports `this` binding + * and specifying the number of arguments to provide to `func`. + * + * @private + * @param {Function} func The function to bind. + * @param {*} thisArg The `this` binding of `func`. + * @param {number} [argCount] The number of arguments to provide to `func`. + * @returns {Function} Returns the callback. + */ + function bindCallback(func, thisArg, argCount) { + if (typeof func != 'function') { + return identity; + } + if (thisArg === undefined) { + return func; + } + switch (argCount) { + case 1: return function(value) { + return func.call(thisArg, value); + }; + case 3: return function(value, index, collection) { + return func.call(thisArg, value, index, collection); + }; + case 4: return function(accumulator, value, index, collection) { + return func.call(thisArg, accumulator, value, index, collection); + }; + case 5: return function(value, other, key, object, source) { + return func.call(thisArg, value, other, key, object, source); + }; + } + return function() { + return func.apply(thisArg, arguments); + }; + } + + module.exports = bindCallback; + + +/***/ }, +/* 26 */ +/***/ function(module, exports, __webpack_require__) { + + var isObject = __webpack_require__(1); + + /** + * Checks if `value` is in `cache` mimicking the return signature of + * `_.indexOf` by returning `0` if the value is found, else `-1`. + * + * @private + * @param {Object} cache The cache to search. + * @param {*} value The value to search for. + * @returns {number} Returns `0` if `value` is found, else `-1`. + */ + function cacheIndexOf(cache, value) { + var data = cache.data, + result = (typeof value == 'string' || isObject(value)) ? data.set.has(value) : data.hash[value]; + + return result ? 0 : -1; + } + + module.exports = cacheIndexOf; + + +/***/ }, +/* 27 */ +/***/ function(module, exports, __webpack_require__) { + + var isObject = __webpack_require__(1); + + /** + * Adds `value` to the cache. + * + * @private + * @name push + * @memberOf SetCache + * @param {*} value The value to cache. + */ + function cachePush(value) { + var data = this.data; + if (typeof value == 'string' || isObject(value)) { + data.set.add(value); + } else { + data.hash[value] = true; + } + } + + module.exports = cachePush; + + +/***/ }, +/* 28 */ +/***/ function(module, exports, __webpack_require__) { + + var bindCallback = __webpack_require__(25), + isIterateeCall = __webpack_require__(32), + restParam = __webpack_require__(9); + + /** + * Creates a `_.assign`, `_.defaults`, or `_.merge` function. + * + * @private + * @param {Function} assigner The function to assign values. + * @returns {Function} Returns the new assigner function. + */ + function createAssigner(assigner) { + return restParam(function(object, sources) { + var index = -1, + length = object == null ? 0 : sources.length, + customizer = length > 2 ? sources[length - 2] : undefined, + guard = length > 2 ? sources[2] : undefined, + thisArg = length > 1 ? sources[length - 1] : undefined; + + if (typeof customizer == 'function') { + customizer = bindCallback(customizer, thisArg, 5); + length -= 2; + } else { + customizer = typeof thisArg == 'function' ? thisArg : undefined; + length -= (customizer ? 1 : 0); + } + if (guard && isIterateeCall(sources[0], sources[1], guard)) { + customizer = length < 3 ? undefined : customizer; + length = 1; + } + while (++index < length) { + var source = sources[index]; + if (source) { + assigner(object, source, customizer); + } + } + return object; + }); + } + + module.exports = createAssigner; + + +/***/ }, +/* 29 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) {var SetCache = __webpack_require__(16), + getNative = __webpack_require__(4); + + /** Native method references. */ + var Set = getNative(global, 'Set'); + + /* Native method references for those with the same name as other `lodash` methods. */ + var nativeCreate = getNative(Object, 'create'); + + /** + * Creates a `Set` cache object to optimize linear searches of large arrays. + * + * @private + * @param {Array} [values] The values to cache. + * @returns {null|Object} Returns the new cache object if `Set` is supported, else `null`. + */ + function createCache(values) { + return (nativeCreate && Set) ? new SetCache(values) : null; + } + + module.exports = createCache; + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 30 */ +/***/ function(module, exports, __webpack_require__) { + + var baseProperty = __webpack_require__(24); + + /** + * Gets the "length" property value of `object`. + * + * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792) + * that affects Safari on at least iOS 8.1-8.3 ARM64. + * + * @private + * @param {Object} object The object to query. + * @returns {*} Returns the "length" value. + */ + var getLength = baseProperty('length'); + + module.exports = getLength; + + +/***/ }, +/* 31 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Gets the index at which the first occurrence of `NaN` is found in `array`. + * + * @private + * @param {Array} array The array to search. + * @param {number} fromIndex The index to search from. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {number} Returns the index of the matched `NaN`, else `-1`. + */ + function indexOfNaN(array, fromIndex, fromRight) { + var length = array.length, + index = fromIndex + (fromRight ? 0 : -1); + + while ((fromRight ? index-- : ++index < length)) { + var other = array[index]; + if (other !== other) { + return index; + } + } + return -1; + } + + module.exports = indexOfNaN; + + +/***/ }, +/* 32 */ +/***/ function(module, exports, __webpack_require__) { + + var isArrayLike = __webpack_require__(2), + isIndex = __webpack_require__(6), + isObject = __webpack_require__(1); + + /** + * Checks if the provided arguments are from an iteratee call. + * + * @private + * @param {*} value The potential iteratee value argument. + * @param {*} index The potential iteratee index or key argument. + * @param {*} object The potential iteratee object argument. + * @returns {boolean} Returns `true` if the arguments are from an iteratee call, else `false`. + */ + function isIterateeCall(value, index, object) { + if (!isObject(object)) { + return false; + } + var type = typeof index; + if (type == 'number' + ? (isArrayLike(object) && isIndex(index, object.length)) + : (type == 'string' && index in object)) { + var other = object[index]; + return value === value ? (value === other) : (other !== other); + } + return false; + } + + module.exports = isIterateeCall; + + +/***/ }, +/* 33 */ +/***/ function(module, exports, __webpack_require__) { + + var isArguments = __webpack_require__(7), + isArray = __webpack_require__(8), + isIndex = __webpack_require__(6), + isLength = __webpack_require__(5), + keysIn = __webpack_require__(37); + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** + * A fallback implementation of `Object.keys` which creates an array of the + * own enumerable property names of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function shimKeys(object) { + var props = keysIn(object), + propsLength = props.length, + length = propsLength && object.length; + + var allowIndexes = !!length && isLength(length) && + (isArray(object) || isArguments(object)); + + var index = -1, + result = []; + + while (++index < propsLength) { + var key = props[index]; + if ((allowIndexes && isIndex(key, length)) || hasOwnProperty.call(object, key)) { + result.push(key); + } + } + return result; + } + + module.exports = shimKeys; + + +/***/ }, +/* 34 */ +/***/ function(module, exports, __webpack_require__) { + + var isObject = __webpack_require__(1); + + /** `Object#toString` result references. */ + var funcTag = '[object Function]'; + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** + * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) + * of values. + */ + var objToString = objectProto.toString; + + /** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ + function isFunction(value) { + // The use of `Object#toString` avoids issues with the `typeof` operator + // in older versions of Chrome and Safari which return 'function' for regexes + // and Safari 8 which returns 'object' for typed array constructors. + return isObject(value) && objToString.call(value) == funcTag; + } + + module.exports = isFunction; + + +/***/ }, +/* 35 */ +/***/ function(module, exports, __webpack_require__) { + + var isFunction = __webpack_require__(34), + isObjectLike = __webpack_require__(3); + + /** Used to detect host constructors (Safari > 5). */ + var reIsHostCtor = /^\[object .+?Constructor\]$/; + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** Used to resolve the decompiled source of functions. */ + var fnToString = Function.prototype.toString; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** Used to detect if a method is native. */ + var reIsNative = RegExp('^' + + fnToString.call(hasOwnProperty).replace(/[\\^$.*+?()[\]{}|]/g, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' + ); + + /** + * Checks if `value` is a native function. + * + * @static + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, else `false`. + * @example + * + * _.isNative(Array.prototype.push); + * // => true + * + * _.isNative(_); + * // => false + */ + function isNative(value) { + if (value == null) { + return false; + } + if (isFunction(value)) { + return reIsNative.test(fnToString.call(value)); + } + return isObjectLike(value) && reIsHostCtor.test(value); + } + + module.exports = isNative; + + +/***/ }, +/* 36 */ +/***/ function(module, exports, __webpack_require__) { + + var assignWith = __webpack_require__(18), + baseAssign = __webpack_require__(19), + createAssigner = __webpack_require__(28); + + /** + * Assigns own enumerable properties of source object(s) to the destination + * object. Subsequent sources overwrite property assignments of previous sources. + * If `customizer` is provided it's invoked to produce the assigned values. + * The `customizer` is bound to `thisArg` and invoked with five arguments: + * (objectValue, sourceValue, key, object, source). + * + * **Note:** This method mutates `object` and is based on + * [`Object.assign`](http://ecma-international.org/ecma-262/6.0/#sec-object.assign). + * + * @static + * @memberOf _ + * @alias extend + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @param {Function} [customizer] The function to customize assigned values. + * @param {*} [thisArg] The `this` binding of `customizer`. + * @returns {Object} Returns `object`. + * @example + * + * _.assign({ 'user': 'barney' }, { 'age': 40 }, { 'user': 'fred' }); + * // => { 'user': 'fred', 'age': 40 } + * + * // using a customizer callback + * var defaults = _.partialRight(_.assign, function(value, other) { + * return _.isUndefined(value) ? other : value; + * }); + * + * defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' }); + * // => { 'user': 'barney', 'age': 36 } + */ + var assign = createAssigner(function(object, source, customizer) { + return customizer + ? assignWith(object, source, customizer) + : baseAssign(object, source); + }); + + module.exports = assign; + + +/***/ }, +/* 37 */ +/***/ function(module, exports, __webpack_require__) { + + var isArguments = __webpack_require__(7), + isArray = __webpack_require__(8), + isIndex = __webpack_require__(6), + isLength = __webpack_require__(5), + isObject = __webpack_require__(1); + + /** Used for native method references. */ + var objectProto = Object.prototype; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** + * Creates an array of the own and inherited enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keysIn(new Foo); + * // => ['a', 'b', 'c'] (iteration order is not guaranteed) + */ + function keysIn(object) { + if (object == null) { + return []; + } + if (!isObject(object)) { + object = Object(object); + } + var length = object.length; + length = (length && isLength(length) && + (isArray(object) || isArguments(object)) && length) || 0; + + var Ctor = object.constructor, + index = -1, + isProto = typeof Ctor == 'function' && Ctor.prototype === object, + result = Array(length), + skipIndexes = length > 0; + + while (++index < length) { + result[index] = (index + ''); + } + for (var key in object) { + if (!(skipIndexes && isIndex(key, length)) && + !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { + result.push(key); + } + } + return result; + } + + module.exports = keysIn; + + +/***/ }, +/* 38 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * This method returns the first argument provided to it. + * + * @static + * @memberOf _ + * @category Utility + * @param {*} value Any value. + * @returns {*} Returns `value`. + * @example + * + * var object = { 'user': 'fred' }; + * + * _.identity(object) === object; + * // => true + */ + function identity(value) { + return value; + } + + module.exports = identity; + + +/***/ }, +/* 39 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + exports.__esModule = true; + exports["default"] = getForceUpdate; + function traverseRenderedChildren(internalInstance, callback, argument) { + callback(internalInstance, argument); + + if (internalInstance._renderedComponent) { + traverseRenderedChildren(internalInstance._renderedComponent, callback, argument); + } else { + for (var key in internalInstance._renderedChildren) { + if (internalInstance._renderedChildren.hasOwnProperty(key)) { + traverseRenderedChildren(internalInstance._renderedChildren[key], callback, argument); + } + } + } + } + + function setPendingForceUpdate(internalInstance) { + if (internalInstance._pendingForceUpdate === false) { + internalInstance._pendingForceUpdate = true; + } + } + + function forceUpdateIfPending(internalInstance, React) { + if (internalInstance._pendingForceUpdate === true) { + var publicInstance = internalInstance._instance; + React.Component.prototype.forceUpdate.call(publicInstance); + } + } + + function getForceUpdate(React) { + return function (instance) { + var internalInstance = instance._reactInternalInstance; + traverseRenderedChildren(internalInstance, setPendingForceUpdate); + traverseRenderedChildren(internalInstance, forceUpdateIfPending, React); + }; + } + + module.exports = exports["default"]; + +/***/ } +/******/ ]) +}); diff --git a/devtools/client/storage/test/browser_storage_dynamic_updates.js b/devtools/client/storage/test/browser_storage_dynamic_updates.js index e72b1e67488e..1ce3f14a8628 100644 --- a/devtools/client/storage/test/browser_storage_dynamic_updates.js +++ b/devtools/client/storage/test/browser_storage_dynamic_updates.js @@ -197,8 +197,12 @@ add_task(function*() { yield findVariableViewProperties([{name: "ss2", value: "changed=ss2"}]); - // Clearing items - yield gWindow.clear(); + // Clearing items. Bug 1233497 makes it so that we can no longer yield + // CPOWs from Tasks. We work around this by calling clear via a ContentTask + // instead. + yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { + return Task.spawn(content.wrappedJSObject.clear); + }); yield gUI.once("store-objects-cleared"); diff --git a/devtools/client/storage/test/head.js b/devtools/client/storage/test/head.js index 0db62d2a4bd9..efc78fd3cc76 100644 --- a/devtools/client/storage/test/head.js +++ b/devtools/client/storage/test/head.js @@ -203,41 +203,44 @@ function forceCollections() { Cu.forceShrinkingGC(); } -/** - * Get all windows including frames recursively. - * - * @param {Window} [baseWindow] - * The base window at which to start looking for child windows - * (optional). - * @return {Set} - * A set of windows. - */ -function getAllWindows(baseWindow=gWindow) { - let windows = new Set(); - - let _getAllWindows = function(win) { - windows.add(win); - - for (let i = 0; i < win.length; i++) { - _getAllWindows(win[i]); - } - }; - _getAllWindows(baseWindow); - - return windows; -} - /** * Cleans up and finishes the test */ function* finishTests() { - for (let win of getAllWindows()) { - if (win.clear) { - console.log("Clearing cookies, localStorage and indexedDBs from " + - win.document.location); - yield win.clear(); + // Bug 1233497 makes it so that we can no longer yield CPOWs from Tasks. + // We work around this by calling clear() via a ContentTask instead. + yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { + /** + * Get all windows including frames recursively. + * + * @param {Window} [baseWindow] + * The base window at which to start looking for child windows + * (optional). + * @return {Set} + * A set of windows. + */ + function getAllWindows(baseWindow) { + let windows = new Set(); + + let _getAllWindows = function(win) { + windows.add(win); + + for (let i = 0; i < win.length; i++) { + _getAllWindows(win[i]); + } + }; + _getAllWindows(baseWindow); + + return windows; } - } + + let windows = getAllWindows(content.wrappedJSObject); + for (let win of windows) { + if (win.clear) { + yield Task.spawn(win.clear); + } + } + }); forceCollections(); finish(); diff --git a/devtools/client/themes/computed.css b/devtools/client/themes/computed.css index ad47fa215335..d3621215d022 100644 --- a/devtools/client/themes/computed.css +++ b/devtools/client/themes/computed.css @@ -3,21 +3,12 @@ * 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/. */ -/* From content */ - -* { - box-sizing: border-box; -} - -:root { - height: 100%; -} - -body { +#sidebar-panel-computedview { margin: 0; display : flex; flex-direction: column; height: 100%; + width: 100%; } #browser-style-checkbox { @@ -29,8 +20,8 @@ body { #propertyContainer { -moz-user-select: text; overflow: auto; - min-height: 0; - flex: 1; + height: 0px; + flex: auto; } .row-striped { @@ -43,18 +34,26 @@ body { } .property-view { - clear: both; padding: 2px 0 2px 17px; + display: flex; + flex-wrap: wrap; } -.property-view > * { +.property-name-container { + width: 202px; +} + +.property-value-container { + width: 168px; +} + +.property-name-container > *, +.property-value-container > * { display: inline-block; vertical-align: middle; } .property-name { - /* -12px is so the expander triangle isn't pushed up above the property */ - width: calc(100% - 12px); overflow-x: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -74,7 +73,6 @@ body { } .property-value { - width: 100%; overflow-x: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -88,16 +86,6 @@ body { padding-left: 8px; } -@media (min-width: 400px) { - .property-name { - width: 200px; - } - .property-value { - /* -212px is accounting for the 200px property-name and the 12px triangle */ - width: calc(100% - 212px); - } -} - .property-content { padding-left: 17px; } @@ -105,7 +93,7 @@ body { /* From skin */ .expander { visibility: hidden; - margin-left: -12px!important; + margin-left: -12px !important; } .expandable { diff --git a/devtools/client/themes/fonts.css b/devtools/client/themes/fonts.css index 7f847c66487d..cc9ffb1795fe 100644 --- a/devtools/client/themes/fonts.css +++ b/devtools/client/themes/fonts.css @@ -2,24 +2,19 @@ * 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/. */ -* { - box-sizing: border-box; -} - -:root { - height: 100%; -} - -body { +#sidebar-panel-fontinspector { display: flex; flex-direction: column; height: 100%; margin: 0; padding-bottom: 20px; + width: 100%; } -#root { +#font-container { overflow: auto; + flex: auto; + height: 0px; } #all-fonts { @@ -27,7 +22,7 @@ body { margin: 0; } -#showall { +#font-showall { border-radius: 0; border: 1px solid black; margin: 3px; @@ -37,12 +32,12 @@ body { right: 0; } -.dim > #root, +.dim > #font-container, .font:not(.has-code) .font-css-code, .font-is-local, .font-is-remote, .font.is-local .font-format-url, -#template { +#font-template { display: none; } @@ -68,7 +63,7 @@ body { overflow-x: auto; } -#preview-text-input { +#font-preview-text-input { font: inherit; margin-top: 1px; margin-bottom: 1px; diff --git a/devtools/client/themes/layout.css b/devtools/client/themes/layout.css index c6c8a94ab414..7d2c01c0ef57 100644 --- a/devtools/client/themes/layout.css +++ b/devtools/client/themes/layout.css @@ -2,22 +2,22 @@ * 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/ */ -.theme-sidebar { - box-sizing: border-box; +#sidebar-panel-layoutview { + display: block; } -body.theme-sidebar { +#layout-container { /* The view will grow bigger as the window gets resized, until 400px */ max-width: 400px; margin: 0px auto; padding: 0; - /* "Contain" the absolutely positioned #main element */ + /* "Contain" the absolutely positioned #layout-main element */ position: relative; } /* Header: contains the position and size of the element */ -#header { +#layout-header { box-sizing: border-box; width: 100%; padding: 4px 14px; @@ -25,24 +25,24 @@ body.theme-sidebar { vertical-align: top; } -#header:-moz-dir(rtl) { +#layout-header:-moz-dir(rtl) { -moz-box-direction: reverse; } -#header > span { +#layout-header > span { display: -moz-box; } -#element-size { +#layout-element-size { -moz-box-flex: 1; } -#element-size:-moz-dir(rtl) { +#layout-element-size:-moz-dir(rtl) { -moz-box-pack: end; } @media (max-height: 228px) { - #header { + #layout-header { padding-top: 0; padding-bottom: 0; margin-top: 10px; @@ -52,7 +52,7 @@ body.theme-sidebar { /* Main: contains the box-model regions */ -#main { +#layout-main { position: absolute; box-sizing: border-box; /* The regions are semi-transparent, so the white background is partly @@ -64,27 +64,27 @@ body.theme-sidebar { width: calc(100% - 2 * 14px); } -.margin, -.size { +.layout-margin, +.layout-size { color: var(--theme-highlight-blue); } /* Regions are 3 nested elements with wide borders and outlines */ -#content { +#layout-content { height: 25px; } -#margins, -#borders, -#padding { +#layout-margins, +#layout-borders, +#layout-padding { border-color: hsla(210,100%,85%,0.2); border-width: 25px; border-style: solid; outline: dotted 1px hsl(210,100%,85%); } -#margins { +#layout-margins { /* This opacity applies to all of the regions, since they are nested */ opacity: .8; } @@ -92,135 +92,135 @@ body.theme-sidebar { /* Respond to window size change by changing the size of the regions */ @media (max-height: 228px) { - #content { + #layout-content { height: 18px; } - #margins, - #borders, - #padding { + #layout-margins, + #layout-borders, + #layout-padding { border-width: 18px; } } /* Regions colors */ -#margins { +#layout-margins { border-color: #edff64; } -#borders { +#layout-borders { border-color: #444444; } -#padding { +#layout-padding { border-color: #6a5acd; } -#content { +#layout-content { background-color: #87ceeb; } /* Editable region sizes are contained in absolutely positioned

    */ -#main > p { +#layout-main > p { position: absolute; pointer-events: none; margin: 0; text-align: center; } -#main > p > span, -#main > p > input { +#layout-main > p > span, +#layout-main > p > input { vertical-align: middle; pointer-events: auto; } /* Coordinates for the region sizes */ -.top, -.bottom { +.layout-top, +.layout-bottom { width: calc(100% - 2px); text-align: center; } -.padding.top { +.layout-padding.layout-top { top: 55px; } -.padding.bottom { +.layout-padding.layout-bottom { bottom: 57px; } -.border.top { +.layout-border.layout-top { top: 30px; } -.border.bottom { +.layout-border.layout-bottom { bottom: 31px; } -.margin.top { +.layout-margin.layout-top { top: 5px; } -.margin.bottom { +.layout-margin.layout-bottom { bottom: 6px; } -.size, -.margin.left, -.margin.right, -.border.left, -.border.right, -.padding.left, -.padding.right { +.layout-size, +.layout-margin.layout-left, +.layout-margin.layout-right, +.layout-border.layout-left, +.layout-border.layout-right, +.layout-padding.layout-left, +.layout-padding.layout-right { top: 22px; line-height: 132px; } -.size { +.layout-size { width: calc(100% - 2px); } -.margin.right, -.margin.left, -.border.left, -.border.right, -.padding.right, -.padding.left { +.layout-margin.layout-right, +.layout-margin.layout-left, +.layout-border.layout-left, +.layout-border.layout-right, +.layout-padding.layout-right, +.layout-padding.layout-left { width: 25px; } -.padding.left { +.layout-padding.layout-left { left: 52px; } -.padding.right { +.layout-padding.layout-right { right: 51px; } -.border.left { +.layout-border.layout-left { left: 26px; } -.border.right { +.layout-border.layout-right { right: 26px; } -.margin.right { +.layout-margin.layout-right { right: 0; } -.margin.left { +.layout-margin.layout-left { left: 0; } -.rotate.left:not(.editing) { +.layout-rotate.layout-left:not(.layout-editing) { transform: rotate(-90deg); } -.rotate.right:not(.editing) { +.layout-rotate.layout-right:not(.layout-editing) { transform: rotate(90deg); } @@ -228,92 +228,92 @@ body.theme-sidebar { the regions smaller then */ @media (max-height: 228px) { - .padding.top { + .layout-padding.layout-top { top: 37px; } - .padding.bottom { + .layout-padding.layout-bottom { bottom: 38px; } - .border.top { + .layout-border.layout-top { top: 19px; } - .border.bottom { + .layout-border.layout-bottom { bottom: 20px; } - .margin.top { + .layout-margin.layout-top { top: 1px; } - .margin.bottom { + .layout-margin.layout-bottom { bottom: 2px; } - .size, - .margin.left, - .margin.right, - .border.left, - .border.right, - .padding.left, - .padding.right { + .layout-size, + .layout-margin.layout-left, + .layout-margin.layout-right, + .layout-border.layout-left, + .layout-border.layout-right, + .layout-padding.layout-left, + .layout-padding.layout-right { line-height: 80px; } - .margin.right, - .margin.left, - .border.left, - .border.right, - .padding.right, - .padding.left { + .layout-margin.layout-right, + .layout-margin.layout-left, + .layout-border.layout-left, + .layout-border.layout-right, + .layout-padding.layout-right, + .layout-padding.layout-left { width: 21px; } - .padding.left { + .layout-padding.layout-left { left: 35px; } - .padding.right { + .layout-padding.layout-right { right: 35px; } - .border.left { + .layout-border.layout-left { left: 16px; } - .border.right { + .layout-border.layout-right { right: 17px; } } /* Legend, displayed inside regions */ -.legend { +.layout-legend { position: absolute; margin: 5px 6px; z-index: 1; } -.legend[data-box="margin"] { +.layout-legend[data-box="margin"] { color: var(--theme-highlight-blue); } @media (max-height: 228px) { - .legend { + .layout-legend { margin: 2px 6px; } } /* Editable fields */ -.editable { +.layout-editable { border: 1px dashed transparent; -moz-user-select: text; } -.editable:hover { +.layout-editable:hover { border-bottom-color: hsl(0, 0%, 50%); } @@ -324,14 +324,14 @@ body.theme-sidebar { /* Make sure the content size doesn't appear as editable like the other sizes */ -.size > span { +.layout-size > span { cursor: default; } /* Hide all values when the view is inactive */ -body.inactive > #header > #element-position, -body.inactive > #header > #element-size, -body.inactive > #main > p { +#layout-container.inactive > #layout-header > #layout-element-position, +#layout-container.inactive > #layout-header > #layout-element-size, +#layout-container.inactive > #layout-main > p { visibility: hidden; } diff --git a/devtools/client/themes/markup.css b/devtools/client/themes/markup.css index b664b8dcc198..e3d26a39dd57 100644 --- a/devtools/client/themes/markup.css +++ b/devtools/client/themes/markup.css @@ -7,6 +7,212 @@ margin: 0; } +:root { + -moz-control-character-visibility: visible; +} + +body { + -moz-user-select: none; +} + +/* Force height and width (possibly overflowing) from inline elements. + * This allows long overflows of text or input fields to still be styled with + * the container, rather than the background disappearing when scrolling */ +#root { + float: left; + min-width: 100%; +} + +body.dragging .tag-line { + cursor: grabbing; +} + +#root-wrapper:after { + content: ""; + display: block; + clear: both; + position:relative; +} + +.html-editor { + display: none; + position: absolute; + z-index: 2; + + /* Use the same margin/padding trick used by .child tags to ensure that + * the editor covers up any content to the left (including expander arrows + * and hover effects). */ + margin-left: -1000em; + padding-left: 1000em; +} + +.html-editor-inner { + border: solid .1px; + flex: 1 1 auto; + + /* Keep the editor away from the markup view floating scrollbars */ + -moz-margin-end: 12px; +} + +.html-editor iframe { + height: 100%; + width: 100%; + border: none; + margin: 0; + padding: 0; +} + +.children { + list-style: none; + padding: 0; + margin: 0; +} + +/* Tags are organized in a UL/LI tree and indented thanks to a left padding. + * A very large padding is used in combination with a slightly smaller margin + * to make sure childs actually span from edge-to-edge. */ +.child { + margin-left: -1000em; + padding-left: 1001em; +} + +/* Normally this element takes space in the layout even if it's position: relative + * by adding height: 0 we let surrounding elements to fill the blank space */ +.child.dragging { + position: relative; + pointer-events: none; + opacity: 0.7; + z-index: 1; + height: 0; +} + +/* Indicates a tag-line in the markup-view as being an active drop target by + * drawing a horizontal line where the dragged element would be inserted if + * dropped here */ +.tag-line.drop-target::before, +.tag-line.drag-target::before { + content: ''; + position: absolute; + top: 0; + width: 100%; + /* Offset these by 1000px to make sure they cover the full width of the view */ + padding-left: 1000px; + left: -1000px; +} + +.tag-line.drag-target::before { + border-top: 2px solid var(--theme-content-color2); +} + +.tag-line.drop-target::before { + border-top: 2px solid var(--theme-contrast-background); +} + +/* In case the indicator is put on the closing .tag-line, the indentation level + * will become misleading, so we push it forward to match the indentation level */ +ul.children + .tag-line::before { + margin-left: 14px; +} + +.tag-line { + min-height: 1.4em; + line-height: 1.4em; + position: relative; +} + +.html-editor-container { + position: relative; + min-height: 200px; +} + +/* This extra element placed in each tag is positioned absolutely to cover the + * whole tag line and is used for background styling (when a selection is made + * or when the tag is flashing) */ +.tag-line .tag-state { + position: absolute; + left: -1000em; + right: 0; + height: 100%; + z-index: 0; +} + +.expander { + display: inline-block; + margin-left: -14px; + vertical-align: middle; + /* Make sure the expander still appears above the tag-state */ + position: relative; + z-index: 1; +} + +.child.collapsed .child { + display: none; +} + +.child > .tag-line:first-child .close { + display: none; +} + +.child.collapsed > .tag-line:first-child .close { + display: inline; +} + +.child.collapsed > .tag-line ~ .tag-line { + display: none; +} + +.child.collapsed .close { + display: inline; +} + +.closing-bracket { + pointer-events: none; +} + +.newattr { + display: inline-block; + width: 1em; + height: 1ex; + margin-right: -1em; + padding: 1px 0; +} + +.attr-value .link { + text-decoration: underline; +} + +.newattr:focus { + margin-right: 0; +} + +.flash-out { + transition: background .5s; +} + +.tag-line { + cursor: default; +} + +.markupview-events { + display: none; + cursor: pointer; +} + +.editor { + /* Make sure the editor still appears above the tag-state */ + position: relative; + z-index: 1; +} + +.editor.text { + display: inline-block; +} + +.editor.text pre, +.editor.comment pre { + font: inherit; +} + .more-nodes { padding-left: 16px; } diff --git a/devtools/client/themes/rules.css b/devtools/client/themes/rules.css index 6e171f5e1bba..b18772d9806d 100644 --- a/devtools/client/themes/rules.css +++ b/devtools/client/themes/rules.css @@ -13,27 +13,19 @@ --rule-filter-icon: url(images/magnifying-glass.png); } -* { - box-sizing: border-box; -} - -:root { - height: 100%; -} - -body { +#sidebar-panel-ruleview { margin: 0; display: flex; flex-direction: column; height: 100%; + width: 100%; } #ruleview-container { -moz-user-select: text; overflow: auto; - min-height: 0; - flex: 1; - height: 100%; + flex: auto; + height: 0px; } #ruleview-container.non-interactive { @@ -42,8 +34,7 @@ body { transition: visibility 0.25s; } -.devtools-toolbar { - width: 100%; +.devtools-sidebar-toolbar { display: flex; } diff --git a/devtools/server/tests/browser/browser_canvasframe_helper_01.js b/devtools/server/tests/browser/browser_canvasframe_helper_01.js index c66f4c55d6fa..ad91c8723f62 100644 --- a/devtools/server/tests/browser/browser_canvasframe_helper_01.js +++ b/devtools/server/tests/browser/browser_canvasframe_helper_01.js @@ -18,7 +18,8 @@ const { const TEST_URL = "data:text/html;charset=utf-8,CanvasFrameAnonymousContentHelper test"; add_task(function*() { - let doc = yield addTab(TEST_URL); + let browser = yield addTab(TEST_URL); + let doc = browser.contentDocument; let nodeBuilder = () => { let root = doc.createElement("div"); diff --git a/devtools/server/tests/browser/browser_canvasframe_helper_02.js b/devtools/server/tests/browser/browser_canvasframe_helper_02.js index e551253c6750..7b682e2406ac 100644 --- a/devtools/server/tests/browser/browser_canvasframe_helper_02.js +++ b/devtools/server/tests/browser/browser_canvasframe_helper_02.js @@ -18,7 +18,8 @@ const { } = require("devtools/server/actors/highlighters/utils/markup"); add_task(function*() { - let doc = yield addTab("about:preferences"); + let browser = yield addTab("about:preferences"); + let doc = browser.contentDocument; let nodeBuilder = () => { let root = doc.createElement("div"); diff --git a/devtools/server/tests/browser/browser_canvasframe_helper_03.js b/devtools/server/tests/browser/browser_canvasframe_helper_03.js index e87c8cd99f60..0c078a6ddc6b 100644 --- a/devtools/server/tests/browser/browser_canvasframe_helper_03.js +++ b/devtools/server/tests/browser/browser_canvasframe_helper_03.js @@ -19,7 +19,8 @@ const { const TEST_URL = "data:text/html;charset=utf-8,CanvasFrameAnonymousContentHelper test"; add_task(function*() { - let doc = yield addTab(TEST_URL); + let browser = yield addTab(TEST_URL); + let doc = browser.contentDocument; let nodeBuilder = () => { let root = doc.createElement("div"); diff --git a/devtools/server/tests/browser/browser_canvasframe_helper_04.js b/devtools/server/tests/browser/browser_canvasframe_helper_04.js index 9b87a99cb546..9bbed17e349a 100644 --- a/devtools/server/tests/browser/browser_canvasframe_helper_04.js +++ b/devtools/server/tests/browser/browser_canvasframe_helper_04.js @@ -22,7 +22,8 @@ const TEST_URL_1 = "data:text/html;charset=utf-8,CanvasFrameAnonymousContentHelp const TEST_URL_2 = "data:text/html;charset=utf-8,CanvasFrameAnonymousContentHelper test 2"; add_task(function*() { - let doc = yield addTab(TEST_URL_2); + let browser = yield addTab(TEST_URL_2); + let doc = browser.contentDocument; let nodeBuilder = () => { let root = doc.createElement("div"); diff --git a/devtools/server/tests/browser/browser_canvasframe_helper_05.js b/devtools/server/tests/browser/browser_canvasframe_helper_05.js index 26db5a276800..0473834a0223 100644 --- a/devtools/server/tests/browser/browser_canvasframe_helper_05.js +++ b/devtools/server/tests/browser/browser_canvasframe_helper_05.js @@ -20,7 +20,8 @@ const { const TEST_URL = "data:text/html;charset=utf-8,CanvasFrameAnonymousContentHelper test"; add_task(function*() { - let doc = yield addTab(TEST_URL); + let browser = yield addTab(TEST_URL); + let doc = browser.contentDocument; let nodeBuilder = () => { let root = doc.createElement("div"); diff --git a/devtools/server/tests/browser/browser_canvasframe_helper_06.js b/devtools/server/tests/browser/browser_canvasframe_helper_06.js index 435476b2b158..4b2ff7230e2b 100644 --- a/devtools/server/tests/browser/browser_canvasframe_helper_06.js +++ b/devtools/server/tests/browser/browser_canvasframe_helper_06.js @@ -20,7 +20,8 @@ const { const TEST_URL = "data:text/html;charset=utf-8,CanvasFrameAnonymousContentHelper test"; add_task(function*() { - let doc = yield addTab(TEST_URL); + let browser = yield addTab(TEST_URL); + let doc = browser.contentDocument; let nodeBuilder = () => { let root = doc.createElement("div"); diff --git a/devtools/server/tests/browser/browser_directorscript_actors.js b/devtools/server/tests/browser/browser_directorscript_actors.js index 5a950c1fbde2..3a6d8a205272 100644 --- a/devtools/server/tests/browser/browser_directorscript_actors.js +++ b/devtools/server/tests/browser/browser_directorscript_actors.js @@ -8,7 +8,8 @@ const {DirectorManagerFront} = require("devtools/server/actors/director-manager" const {DirectorRegistry} = require("devtools/server/actors/director-registry"); add_task(function*() { - let doc = yield addTab(MAIN_DOMAIN + "director-script-target.html"); + let browser = yield addTab(MAIN_DOMAIN + "director-script-target.html"); + let doc = browser.contentDocument; initDebuggerServer(); let client = new DebuggerClient(DebuggerServer.connectPipe()); diff --git a/devtools/server/tests/browser/browser_directorscript_actors_error_events.js b/devtools/server/tests/browser/browser_directorscript_actors_error_events.js index d20e00dd98db..51a3000e7845 100644 --- a/devtools/server/tests/browser/browser_directorscript_actors_error_events.js +++ b/devtools/server/tests/browser/browser_directorscript_actors_error_events.js @@ -8,7 +8,8 @@ const {DirectorManagerFront} = require("devtools/server/actors/director-manager" const {DirectorRegistry} = require("devtools/server/actors/director-registry"); add_task(function*() { - let doc = yield addTab(MAIN_DOMAIN + "director-script-target.html"); + let browser = yield addTab(MAIN_DOMAIN + "director-script-target.html"); + let doc = browser.contentDocument; initDebuggerServer(); let client = new DebuggerClient(DebuggerServer.connectPipe()); diff --git a/devtools/server/tests/browser/browser_directorscript_actors_exports.js b/devtools/server/tests/browser/browser_directorscript_actors_exports.js index a11e9ff6116f..624f2dbc0f24 100644 --- a/devtools/server/tests/browser/browser_directorscript_actors_exports.js +++ b/devtools/server/tests/browser/browser_directorscript_actors_exports.js @@ -10,7 +10,8 @@ const {DirectorRegistry} = require("devtools/server/actors/director-registry"); DirectorRegistry.clear(); add_task(function*() { - let doc = yield addTab(MAIN_DOMAIN + "director-script-target.html"); + let browser = yield addTab(MAIN_DOMAIN + "director-script-target.html"); + let doc = browser.contentDocument; initDebuggerServer(); let client = new DebuggerClient(DebuggerServer.connectPipe()); diff --git a/devtools/server/tests/browser/browser_markers-cycle-collection.js b/devtools/server/tests/browser/browser_markers-cycle-collection.js index 8a523f33273c..c421a983b3a9 100644 --- a/devtools/server/tests/browser/browser_markers-cycle-collection.js +++ b/devtools/server/tests/browser/browser_markers-cycle-collection.js @@ -12,7 +12,8 @@ add_task(function*() { // This test runs very slowly on linux32 debug EC2 instances. requestLongerTimeout(2); - let doc = yield addTab(MAIN_DOMAIN + "doc_force_cc.html"); + let browser = yield addTab(MAIN_DOMAIN + "doc_force_cc.html"); + let doc = browser.contentDocument; initDebuggerServer(); let client = new DebuggerClient(DebuggerServer.connectPipe()); diff --git a/devtools/server/tests/browser/browser_markers-docloading-01.js b/devtools/server/tests/browser/browser_markers-docloading-01.js index 054d80b93008..5d6c1bfb843e 100644 --- a/devtools/server/tests/browser/browser_markers-docloading-01.js +++ b/devtools/server/tests/browser/browser_markers-docloading-01.js @@ -9,7 +9,8 @@ const { TimelineFront } = require("devtools/server/actors/timeline"); const MARKER_NAMES = ["document::DOMContentLoaded", "document::Load"]; add_task(function*() { - let doc = yield addTab(MAIN_DOMAIN + "doc_innerHTML.html"); + let browser = yield addTab(MAIN_DOMAIN + "doc_innerHTML.html"); + let doc = browser.contentDocument; initDebuggerServer(); let client = new DebuggerClient(DebuggerServer.connectPipe()); diff --git a/devtools/server/tests/browser/browser_markers-docloading-02.js b/devtools/server/tests/browser/browser_markers-docloading-02.js index ec78ac0f99fe..b53d04c7edfc 100644 --- a/devtools/server/tests/browser/browser_markers-docloading-02.js +++ b/devtools/server/tests/browser/browser_markers-docloading-02.js @@ -9,7 +9,8 @@ const { TimelineFront } = require("devtools/server/actors/timeline"); const MARKER_NAMES = ["document::DOMContentLoaded", "document::Load"]; add_task(function*() { - let doc = yield addTab(MAIN_DOMAIN + "doc_innerHTML.html"); + let browser = yield addTab(MAIN_DOMAIN + "doc_innerHTML.html"); + let doc = browser.contentDocument; initDebuggerServer(); let client = new DebuggerClient(DebuggerServer.connectPipe()); diff --git a/devtools/server/tests/browser/browser_markers-docloading-03.js b/devtools/server/tests/browser/browser_markers-docloading-03.js index 9ec16c854ca1..0cc0de1092ab 100644 --- a/devtools/server/tests/browser/browser_markers-docloading-03.js +++ b/devtools/server/tests/browser/browser_markers-docloading-03.js @@ -9,7 +9,8 @@ const { TimelineFront } = require("devtools/server/actors/timeline"); const MARKER_NAMES = ["document::DOMContentLoaded", "document::Load"]; add_task(function*() { - let doc = yield addTab(MAIN_DOMAIN + "doc_innerHTML.html"); + let browser = yield addTab(MAIN_DOMAIN + "doc_innerHTML.html"); + let doc = browser.contentDocument; initDebuggerServer(); let client = new DebuggerClient(DebuggerServer.connectPipe()); diff --git a/devtools/server/tests/browser/browser_markers-gc.js b/devtools/server/tests/browser/browser_markers-gc.js index 14097bd303db..e2c525c58cff 100644 --- a/devtools/server/tests/browser/browser_markers-gc.js +++ b/devtools/server/tests/browser/browser_markers-gc.js @@ -9,7 +9,8 @@ const { PerformanceFront } = require("devtools/server/actors/performance"); const MARKER_NAME = "GarbageCollection"; add_task(function*() { - let doc = yield addTab(MAIN_DOMAIN + "doc_force_gc.html"); + let browser = yield addTab(MAIN_DOMAIN + "doc_force_gc.html"); + let doc = browser.contentDocument; initDebuggerServer(); let client = new DebuggerClient(DebuggerServer.connectPipe()); diff --git a/devtools/server/tests/browser/browser_markers-parse-html.js b/devtools/server/tests/browser/browser_markers-parse-html.js index 476f62c30191..ebbfb92e3899 100644 --- a/devtools/server/tests/browser/browser_markers-parse-html.js +++ b/devtools/server/tests/browser/browser_markers-parse-html.js @@ -9,7 +9,8 @@ const { PerformanceFront } = require("devtools/server/actors/performance"); const MARKER_NAME = "Parse HTML"; add_task(function*() { - let doc = yield addTab(MAIN_DOMAIN + "doc_innerHTML.html"); + let browser = yield addTab(MAIN_DOMAIN + "doc_innerHTML.html"); + let doc = browser.contentDocument; initDebuggerServer(); let client = new DebuggerClient(DebuggerServer.connectPipe()); diff --git a/devtools/server/tests/browser/browser_markers-styles.js b/devtools/server/tests/browser/browser_markers-styles.js index c447e679352b..d389d9f09096 100644 --- a/devtools/server/tests/browser/browser_markers-styles.js +++ b/devtools/server/tests/browser/browser_markers-styles.js @@ -9,7 +9,8 @@ const { PerformanceFront } = require("devtools/server/actors/performance"); const MARKER_NAME = "Styles"; add_task(function*() { - let doc = yield addTab(MAIN_DOMAIN + "doc_perf.html"); + let browser = yield addTab(MAIN_DOMAIN + "doc_perf.html"); + let doc = browser.contentDocument; initDebuggerServer(); let client = new DebuggerClient(DebuggerServer.connectPipe()); diff --git a/devtools/server/tests/browser/browser_markers-timestamp.js b/devtools/server/tests/browser/browser_markers-timestamp.js index a224198486a1..77cd4f7be0ed 100644 --- a/devtools/server/tests/browser/browser_markers-timestamp.js +++ b/devtools/server/tests/browser/browser_markers-timestamp.js @@ -10,7 +10,8 @@ const { consoleMethod, PMM_loadFrameScripts } = require("devtools/shared/perform const MARKER_NAME = "TimeStamp"; add_task(function*() { - let doc = yield addTab(MAIN_DOMAIN + "doc_perf.html"); + let browser = yield addTab(MAIN_DOMAIN + "doc_perf.html"); + let doc = browser.contentDocument; initDebuggerServer(); let client = new DebuggerClient(DebuggerServer.connectPipe()); diff --git a/devtools/server/tests/browser/browser_navigateEvents.js b/devtools/server/tests/browser/browser_navigateEvents.js index 06c95aa0f904..3f17c3ead70e 100644 --- a/devtools/server/tests/browser/browser_navigateEvents.js +++ b/devtools/server/tests/browser/browser_navigateEvents.js @@ -117,7 +117,8 @@ function getServerTabActor(callback) { function test() { // Open a test tab - addTab(URL1).then(function(doc) { + addTab(URL1).then(function(browser) { + let doc = browser.contentDocument; getServerTabActor(function (tabActor) { // In order to listen to internal will-navigate/navigate events events.on(tabActor, "will-navigate", function (data) { @@ -128,7 +129,6 @@ function test() { }); // Start listening for page load events - let browser = gBrowser.selectedBrowser; browser.addEventListener("DOMContentLoaded", onDOMContentLoaded, true); browser.addEventListener("load", onLoad, true); diff --git a/devtools/server/tests/browser/browser_perf-allocation-data.js b/devtools/server/tests/browser/browser_perf-allocation-data.js index e6b04893654d..67deac7e0953 100644 --- a/devtools/server/tests/browser/browser_perf-allocation-data.js +++ b/devtools/server/tests/browser/browser_perf-allocation-data.js @@ -8,7 +8,8 @@ const { PerformanceFront } = require("devtools/server/actors/performance"); add_task(function*() { - let doc = yield addTab(MAIN_DOMAIN + "doc_allocations.html"); + let browser = yield addTab(MAIN_DOMAIN + "doc_allocations.html"); + let doc = browser.contentDocument; initDebuggerServer(); let client = new DebuggerClient(DebuggerServer.connectPipe()); diff --git a/devtools/server/tests/browser/browser_perf-profiler-01.js b/devtools/server/tests/browser/browser_perf-profiler-01.js index 7a44a3fe8aeb..75af46688cb2 100644 --- a/devtools/server/tests/browser/browser_perf-profiler-01.js +++ b/devtools/server/tests/browser/browser_perf-profiler-01.js @@ -11,7 +11,8 @@ const { PerformanceFront } = require("devtools/server/actors/performance"); const { sendProfilerCommand, PMM_isProfilerActive, PMM_stopProfiler, PMM_loadFrameScripts } = require("devtools/shared/performance/process-communication"); add_task(function*() { - let doc = yield addTab(MAIN_DOMAIN + "doc_perf.html"); + let browser = yield addTab(MAIN_DOMAIN + "doc_perf.html"); + let doc = browser.contentDocument; initDebuggerServer(); let client = new DebuggerClient(DebuggerServer.connectPipe()); diff --git a/devtools/server/tests/browser/browser_perf-realtime-markers.js b/devtools/server/tests/browser/browser_perf-realtime-markers.js index 455bbae8ff21..8e8f8d77af9e 100644 --- a/devtools/server/tests/browser/browser_perf-realtime-markers.js +++ b/devtools/server/tests/browser/browser_perf-realtime-markers.js @@ -8,7 +8,8 @@ const { PerformanceFront } = require("devtools/server/actors/performance"); add_task(function*() { - let doc = yield addTab(MAIN_DOMAIN + "doc_perf.html"); + let browser = yield addTab(MAIN_DOMAIN + "doc_perf.html"); + let doc = browser.contentDocument; initDebuggerServer(); let client = new DebuggerClient(DebuggerServer.connectPipe()); diff --git a/devtools/server/tests/browser/browser_perf-recording-actor-01.js b/devtools/server/tests/browser/browser_perf-recording-actor-01.js index f42fdf9a66b8..64d60bb2cc5c 100644 --- a/devtools/server/tests/browser/browser_perf-recording-actor-01.js +++ b/devtools/server/tests/browser/browser_perf-recording-actor-01.js @@ -9,7 +9,8 @@ const { PerformanceFront } = require("devtools/server/actors/performance"); add_task(function*() { - let doc = yield addTab(MAIN_DOMAIN + "doc_perf.html"); + let browser = yield addTab(MAIN_DOMAIN + "doc_perf.html"); + let doc = browser.contentDocument; initDebuggerServer(); let client = new DebuggerClient(DebuggerServer.connectPipe()); diff --git a/devtools/server/tests/browser/browser_perf-recording-actor-02.js b/devtools/server/tests/browser/browser_perf-recording-actor-02.js index ae9e839a3d43..f626f0256c48 100644 --- a/devtools/server/tests/browser/browser_perf-recording-actor-02.js +++ b/devtools/server/tests/browser/browser_perf-recording-actor-02.js @@ -11,7 +11,8 @@ var config = { bufferSize: BUFFER_SIZE }; const { PerformanceFront } = require("devtools/server/actors/performance"); add_task(function*() { - let doc = yield addTab(MAIN_DOMAIN + "doc_perf.html"); + let browser = yield addTab(MAIN_DOMAIN + "doc_perf.html"); + let doc = browser.contentDocument; initDebuggerServer(); let client = new DebuggerClient(DebuggerServer.connectPipe()); diff --git a/devtools/server/tests/browser/browser_perf-samples-01.js b/devtools/server/tests/browser/browser_perf-samples-01.js index b1687ccde803..f9aa09ec63bc 100644 --- a/devtools/server/tests/browser/browser_perf-samples-01.js +++ b/devtools/server/tests/browser/browser_perf-samples-01.js @@ -11,7 +11,8 @@ const WAIT_TIME = 1000; // ms const { PerformanceFront } = require("devtools/server/actors/performance"); add_task(function*() { - let doc = yield addTab(MAIN_DOMAIN + "doc_perf.html"); + let browser = yield addTab(MAIN_DOMAIN + "doc_perf.html"); + let doc = browser.contentDocument; initDebuggerServer(); let client = new DebuggerClient(DebuggerServer.connectPipe()); diff --git a/devtools/server/tests/browser/browser_perf-samples-02.js b/devtools/server/tests/browser/browser_perf-samples-02.js index 354368868b95..4899932069c6 100644 --- a/devtools/server/tests/browser/browser_perf-samples-02.js +++ b/devtools/server/tests/browser/browser_perf-samples-02.js @@ -12,7 +12,8 @@ const WAIT_TIME = 1000; // ms const { PerformanceFront } = require("devtools/server/actors/performance"); add_task(function*() { - let doc = yield addTab(MAIN_DOMAIN + "doc_perf.html"); + let browser = yield addTab(MAIN_DOMAIN + "doc_perf.html"); + let doc = browser.contentDocument; initDebuggerServer(); let client = new DebuggerClient(DebuggerServer.connectPipe()); diff --git a/devtools/server/tests/browser/browser_storage_dynamic_windows.js b/devtools/server/tests/browser/browser_storage_dynamic_windows.js index 344564f8f346..ac5a0ce75e71 100644 --- a/devtools/server/tests/browser/browser_storage_dynamic_windows.js +++ b/devtools/server/tests/browser/browser_storage_dynamic_windows.js @@ -302,7 +302,8 @@ function testRemoveIframe() { } function test() { - addTab(MAIN_DOMAIN + "storage-dynamic-windows.html").then(function(doc) { + addTab(MAIN_DOMAIN + "storage-dynamic-windows.html").then(function(browser) { + let doc = browser.contentDocument; initDebuggerServer(); let createConnection = () => { diff --git a/devtools/server/tests/browser/browser_storage_listings.js b/devtools/server/tests/browser/browser_storage_listings.js index d19c206a1765..0c28ae2ec6b0 100644 --- a/devtools/server/tests/browser/browser_storage_listings.js +++ b/devtools/server/tests/browser/browser_storage_listings.js @@ -636,7 +636,8 @@ var testIDBEntries = Task.async(function*(index, hosts, indexedDBActor) { }); function test() { - addTab(MAIN_DOMAIN + "storage-listings.html").then(function(doc) { + addTab(MAIN_DOMAIN + "storage-listings.html").then(function(browser) { + let doc = browser.contentDocument; initDebuggerServer(); let createConnection = () => { diff --git a/devtools/server/tests/browser/browser_storage_updates.js b/devtools/server/tests/browser/browser_storage_updates.js index 25ed01195268..3f0df7690052 100644 --- a/devtools/server/tests/browser/browser_storage_updates.js +++ b/devtools/server/tests/browser/browser_storage_updates.js @@ -282,7 +282,8 @@ function* finishTests(client) { } add_task(function*() { - let doc = yield addTab(MAIN_DOMAIN + "storage-updates.html"); + let browser = yield addTab(MAIN_DOMAIN + "storage-updates.html"); + let doc = browser.contentDocument; initDebuggerServer(); diff --git a/devtools/server/tests/browser/browser_stylesheets_nested-iframes.js b/devtools/server/tests/browser/browser_stylesheets_nested-iframes.js index 229f412f46d5..6fd247c788b6 100644 --- a/devtools/server/tests/browser/browser_stylesheets_nested-iframes.js +++ b/devtools/server/tests/browser/browser_stylesheets_nested-iframes.js @@ -10,7 +10,8 @@ const {StyleSheetsFront} = require("devtools/server/actors/stylesheets"); add_task(function*() { - let doc = yield addTab(MAIN_DOMAIN + "stylesheets-nested-iframes.html"); + let browser = yield addTab(MAIN_DOMAIN + "stylesheets-nested-iframes.html"); + let doc = browser.contentDocument; info("Initialising the debugger server and client."); initDebuggerServer(); diff --git a/devtools/server/tests/browser/browser_timeline.js b/devtools/server/tests/browser/browser_timeline.js index 73509ff22f81..51a2c67b94dd 100644 --- a/devtools/server/tests/browser/browser_timeline.js +++ b/devtools/server/tests/browser/browser_timeline.js @@ -13,7 +13,8 @@ const {TimelineFront} = require("devtools/server/actors/timeline"); add_task(function*() { - let doc = yield addTab("data:text/html;charset=utf-8,mop"); + let browser = yield addTab("data:text/html;charset=utf-8,mop"); + let doc = browser.contentDocument; initDebuggerServer(); let client = new DebuggerClient(DebuggerServer.connectPipe()); diff --git a/devtools/server/tests/browser/browser_timeline_actors.js b/devtools/server/tests/browser/browser_timeline_actors.js index 34d5f9dfe4f1..0b5dc317709e 100644 --- a/devtools/server/tests/browser/browser_timeline_actors.js +++ b/devtools/server/tests/browser/browser_timeline_actors.js @@ -10,7 +10,8 @@ const {TimelineFront} = require("devtools/server/actors/timeline"); add_task(function*() { - let doc = yield addTab("data:text/html;charset=utf-8,mop"); + let browser = yield addTab("data:text/html;charset=utf-8,mop"); + let doc = browser.contentDocument; initDebuggerServer(); let client = new DebuggerClient(DebuggerServer.connectPipe()); diff --git a/devtools/server/tests/browser/browser_timeline_iframes.js b/devtools/server/tests/browser/browser_timeline_iframes.js index 23be278299fb..47a69acd1af0 100644 --- a/devtools/server/tests/browser/browser_timeline_iframes.js +++ b/devtools/server/tests/browser/browser_timeline_iframes.js @@ -10,7 +10,8 @@ const {TimelineFront} = require("devtools/server/actors/timeline"); add_task(function*() { - let doc = yield addTab(MAIN_DOMAIN + "timeline-iframe-parent.html"); + let browser = yield addTab(MAIN_DOMAIN + "timeline-iframe-parent.html"); + let doc = browser.contentDocument; initDebuggerServer(); let client = new DebuggerClient(DebuggerServer.connectPipe()); diff --git a/devtools/server/tests/browser/head.js b/devtools/server/tests/browser/head.js index 3c52770218af..81a5249f6878 100644 --- a/devtools/server/tests/browser/head.js +++ b/devtools/server/tests/browser/head.js @@ -25,7 +25,10 @@ waitForExplicitFinish(); /** * Add a new test tab in the browser and load the given url. * @param {String} url The url to be loaded in the new tab - * @return a promise that resolves to the document when the url is loaded + * @return a promise that resolves to the new browser that the document + * is loaded in. Note that we cannot return the document + * directly, since this would be a CPOW in the e10s case, + * and Promises cannot be resolved with CPOWs (see bug 1233497). */ var addTab = Task.async(function* (url) { info("Adding a new tab with URL: '" + url + "'"); @@ -42,7 +45,7 @@ var addTab = Task.async(function* (url) { waitForFocus(resolve, content, isBlank); }); - return tab.linkedBrowser.contentWindow.document; + return tab.linkedBrowser; }); function* initAnimationsFrontForUrl(url) { diff --git a/devtools/server/tests/unit/head_dbg.js b/devtools/server/tests/unit/head_dbg.js index ccd10c91e5e8..e07439951a56 100644 --- a/devtools/server/tests/unit/head_dbg.js +++ b/devtools/server/tests/unit/head_dbg.js @@ -372,13 +372,10 @@ function attachTestThread(aClient, aTitle, aCallback) { // thread, and then resume it. Pass |aCallback| the thread's response to // the 'resume' packet, a TabClient for the tab, and a ThreadClient for the // thread. -function attachTestTabAndResume(aClient, aTitle, aCallback = () => {}) { - return new Promise((resolve, reject) => { - attachTestThread(aClient, aTitle, function(aResponse, aTabClient, aThreadClient) { - aThreadClient.resume(function (aResponse) { - aCallback(aResponse, aTabClient, aThreadClient); - resolve([aResponse, aTabClient, aThreadClient]); - }); +function attachTestTabAndResume(aClient, aTitle, aCallback) { + attachTestThread(aClient, aTitle, function(aResponse, aTabClient, aThreadClient) { + aThreadClient.resume(function (aResponse) { + aCallback(aResponse, aTabClient, aThreadClient); }); }); } @@ -727,20 +724,6 @@ function stepIn(client, threadClient) { .then(() => paused); } -/** - * Resume JS execution for a step over and wait for the pause after the step - * has been taken. - * - * @param DebuggerClient client - * @param ThreadClient threadClient - * @returns Promise - */ -function stepOver(client, threadClient) { - dumpn("Stepping over."); - return threadClient.stepOver() - .then(() => waitForPause(client)); -} - /** * Get the list of `count` frames currently on stack, starting at the index * `first` for the specified thread. diff --git a/devtools/server/tests/unit/setBreakpoint-on-column-with-no-offsets-at-end-of-script.js b/devtools/server/tests/unit/setBreakpoint-on-column-with-no-offsets-at-end-of-script.js new file mode 100644 index 000000000000..48dabd5e043b --- /dev/null +++ b/devtools/server/tests/unit/setBreakpoint-on-column-with-no-offsets-at-end-of-script.js @@ -0,0 +1,5 @@ +"use strict"; + +function f() { + function g() { var a = 1; var b = 2; } g(); +} diff --git a/devtools/server/tests/unit/setBreakpoint-on-line-with-no-offsets-at-end-of-script.js b/devtools/server/tests/unit/setBreakpoint-on-line-with-no-offsets-at-end-of-script.js new file mode 100644 index 000000000000..52453c7b387b --- /dev/null +++ b/devtools/server/tests/unit/setBreakpoint-on-line-with-no-offsets-at-end-of-script.js @@ -0,0 +1,11 @@ +"use strict"; + +function f() { + function g() { + var a = 1; + var b = 2; + + } + + g(); +} diff --git a/devtools/server/tests/unit/test_breakpoint-13.js b/devtools/server/tests/unit/test_breakpoint-13.js index 69b3b2f16a05..307b058a4119 100644 --- a/devtools/server/tests/unit/test_breakpoint-13.js +++ b/devtools/server/tests/unit/test_breakpoint-13.js @@ -39,68 +39,69 @@ function test_simple_breakpoint() let source = gThreadClient.source(aPacket.frame.where.source); let location = { line: gDebuggee.line0 + 2 }; - source.setBreakpoint(location, Task.async(function*(aResponse, bpClient) { - const testCallbacks = [ - function(aPacket) { - // Check that the stepping worked. - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 5); - do_check_eq(aPacket.why.type, "resumeLimit"); - }, - function(aPacket) { + source.setBreakpoint(location, function (aResponse, bpClient) { + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + // Check that the stepping worked. + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 5); + do_check_eq(aPacket.why.type, "resumeLimit"); + + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { // Entered the foo function call frame. do_check_eq(aPacket.frame.where.line, location.line); do_check_neq(aPacket.why.type, "breakpoint"); do_check_eq(aPacket.why.type, "resumeLimit"); - }, - function(aPacket) { - // At the end of the foo function call frame. - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 3); - do_check_neq(aPacket.why.type, "breakpoint"); - do_check_eq(aPacket.why.type, "resumeLimit"); - }, - function(aPacket) { - // Check that the breakpoint wasn't the reason for this pause, but - // that the frame is about to be popped while stepping. - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 3); - do_check_neq(aPacket.why.type, "breakpoint"); - do_check_eq(aPacket.why.type, "resumeLimit"); - do_check_eq(aPacket.why.frameFinished.return.type, "undefined"); - }, - function(aPacket) { - // The foo function call frame was just popped from the stack. - do_check_eq(gDebuggee.a, 1); - do_check_eq(gDebuggee.b, undefined); - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 5); - do_check_eq(aPacket.why.type, "resumeLimit"); - do_check_eq(aPacket.poppedFrames.length, 1); - }, - function(aPacket) { - // Check that the debugger statement wasn't the reason for this pause. - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 6); - do_check_neq(aPacket.why.type, "debuggerStatement"); - do_check_eq(aPacket.why.type, "resumeLimit"); - }, - function(aPacket) { - // Check that the debugger statement wasn't the reason for this pause. - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 7); - do_check_neq(aPacket.why.type, "debuggerStatement"); - do_check_eq(aPacket.why.type, "resumeLimit"); - }, - ]; - for (let callback of testCallbacks) { - let waiter = waitForPause(gThreadClient); + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + // Check that the breakpoint wasn't the reason for this pause, but + // that the frame is about to be popped while stepping. + do_check_eq(aPacket.frame.where.line, location.line); + do_check_neq(aPacket.why.type, "breakpoint"); + do_check_eq(aPacket.why.type, "resumeLimit"); + do_check_eq(aPacket.why.frameFinished.return.type, "undefined"); + + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + // The foo function call frame was just popped from the stack. + do_check_eq(gDebuggee.a, 1); + do_check_eq(gDebuggee.b, undefined); + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 5); + do_check_eq(aPacket.why.type, "resumeLimit"); + do_check_eq(aPacket.poppedFrames.length, 1); + + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + // Check that the debugger statement wasn't the reason for this pause. + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 6); + do_check_neq(aPacket.why.type, "debuggerStatement"); + do_check_eq(aPacket.why.type, "resumeLimit"); + + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + // Check that the debugger statement wasn't the reason for this pause. + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 7); + do_check_neq(aPacket.why.type, "debuggerStatement"); + do_check_eq(aPacket.why.type, "resumeLimit"); + + // Remove the breakpoint and finish. + bpClient.remove(() => gThreadClient.resume(() => gClient.close(gCallback))); + + }); + // Step past the debugger statement. + gThreadClient.stepIn(); + }); + // Step into the debugger statement. + gThreadClient.stepIn(); + }); + // Get back to the frame above. + gThreadClient.stepIn(); + }); + // Step to the end of the function call frame. + gThreadClient.stepIn(); + }); + + // Step into the function call. gThreadClient.stepIn(); - let packet = yield waiter; - callback(packet); - } - - // Remove the breakpoint and finish. - let waiter = waitForPause(gThreadClient); + }); + // Step into the next line with the function call. gThreadClient.stepIn(); - yield waiter; - bpClient.remove(() => gThreadClient.resume(() => gClient.close(gCallback))); - })); + }); }); Cu.evalInSandbox("var line0 = Error().lineNumber;\n" + diff --git a/devtools/server/tests/unit/test_breakpoint-14.js b/devtools/server/tests/unit/test_breakpoint-14.js index b25e2802a9a4..163b357bb371 100644 --- a/devtools/server/tests/unit/test_breakpoint-14.js +++ b/devtools/server/tests/unit/test_breakpoint-14.js @@ -39,66 +39,67 @@ function test_simple_breakpoint() let source = gThreadClient.source(aPacket.frame.where.source); let location = { line: gDebuggee.line0 + 2 }; - source.setBreakpoint(location, Task.async(function*(aResponse, bpClient) { - const testCallbacks = [ - function(aPacket) { - // Check that the stepping worked. - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 5); - do_check_eq(aPacket.why.type, "resumeLimit"); - }, - function(aPacket) { + source.setBreakpoint(location, function (aResponse, bpClient) { + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + // Check that the stepping worked. + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 5); + do_check_eq(aPacket.why.type, "resumeLimit"); + + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { // Reached the breakpoint. do_check_eq(aPacket.frame.where.line, location.line); do_check_eq(aPacket.why.type, "breakpoint"); do_check_neq(aPacket.why.type, "resumeLimit"); - }, - function(aPacket) { - // Stepped to the closing brace of the function. - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 3); - do_check_eq(aPacket.why.type, "resumeLimit"); - }, - function(aPacket) { - // The frame is about to be popped while stepping. - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 3); - do_check_neq(aPacket.why.type, "breakpoint"); - do_check_eq(aPacket.why.type, "resumeLimit"); - do_check_eq(aPacket.why.frameFinished.return.type, "undefined"); - }, - function(aPacket) { - // The foo function call frame was just popped from the stack. - do_check_eq(gDebuggee.a, 1); - do_check_eq(gDebuggee.b, undefined); - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 5); - do_check_eq(aPacket.why.type, "resumeLimit"); - do_check_eq(aPacket.poppedFrames.length, 1); - }, - function(aPacket) { - // Check that the debugger statement wasn't the reason for this pause. - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 6); - do_check_neq(aPacket.why.type, "debuggerStatement"); - do_check_eq(aPacket.why.type, "resumeLimit"); - }, - function(aPacket) { - // Check that the debugger statement wasn't the reason for this pause. - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 7); - do_check_neq(aPacket.why.type, "debuggerStatement"); - do_check_eq(aPacket.why.type, "resumeLimit"); - }, - ]; - for (let callback of testCallbacks) { - let waiter = waitForPause(gThreadClient); + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + // The frame is about to be popped while stepping. + do_check_eq(aPacket.frame.where.line, location.line); + do_check_neq(aPacket.why.type, "breakpoint"); + do_check_eq(aPacket.why.type, "resumeLimit"); + do_check_eq(aPacket.why.frameFinished.return.type, "undefined"); + + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + // The foo function call frame was just popped from the stack. + do_check_eq(gDebuggee.a, 1); + do_check_eq(gDebuggee.b, undefined); + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 5); + do_check_eq(aPacket.why.type, "resumeLimit"); + do_check_eq(aPacket.poppedFrames.length, 1); + + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + // Check that the debugger statement wasn't the reason for this pause. + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 6); + do_check_neq(aPacket.why.type, "debuggerStatement"); + do_check_eq(aPacket.why.type, "resumeLimit"); + + gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + // Check that the debugger statement wasn't the reason for this pause. + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 7); + do_check_neq(aPacket.why.type, "debuggerStatement"); + do_check_eq(aPacket.why.type, "resumeLimit"); + + // Remove the breakpoint and finish. + bpClient.remove(() => gThreadClient.resume(() => gClient.close(gCallback))); + + }); + // Step past the debugger statement. + gThreadClient.stepOver(); + }); + // Step over the debugger statement. + gThreadClient.stepOver(); + }); + // Get back to the frame above. + gThreadClient.stepOver(); + }); + // Step to the end of the function call frame. + gThreadClient.stepOver(); + }); + // Step over the function call. gThreadClient.stepOver(); - let packet = yield waiter; - callback(packet); - } - - // Remove the breakpoint and finish. - let waiter = waitForPause(gThreadClient); + }); + // Step over to the next line with the function call. gThreadClient.stepOver(); - yield waiter; - bpClient.remove(() => gThreadClient.resume(() => gClient.close(gCallback))); - })); + }); }); Cu.evalInSandbox("var line0 = Error().lineNumber;\n" + diff --git a/devtools/server/tests/unit/test_get-executable-lines.js b/devtools/server/tests/unit/test_get-executable-lines.js index d3be7efef62c..2228fa5259f5 100644 --- a/devtools/server/tests/unit/test_get-executable-lines.js +++ b/devtools/server/tests/unit/test_get-executable-lines.js @@ -38,7 +38,7 @@ function test_executable_lines() { do_check_true(!error); let source = gThreadClient.source(sources[0]); source.getExecutableLines(function(lines){ - do_check_true(arrays_equal([2, 5, 7, 8, 10, 12, 14, 16], lines)); + do_check_true(arrays_equal([2, 5, 7, 8, 12, 14, 16], lines)); finishClient(gClient); }); }); diff --git a/devtools/server/tests/unit/test_setBreakpoint-on-line-with-no-offsets-at-end-of-script.js b/devtools/server/tests/unit/test_setBreakpoint-on-line-with-no-offsets-at-end-of-script.js new file mode 100644 index 000000000000..96ab3647526f --- /dev/null +++ b/devtools/server/tests/unit/test_setBreakpoint-on-line-with-no-offsets-at-end-of-script.js @@ -0,0 +1,53 @@ +"use strict"; + +var SOURCE_URL = getFileUrl("setBreakpoint-on-line-with-no-offsets-at-end-of-script.js"); + +function run_test() { + return Task.spawn(function* () { + do_test_pending(); + + DebuggerServer.registerModule("xpcshell-test/testactors"); + DebuggerServer.init(() => true); + + let global = createTestGlobal("test"); + DebuggerServer.addTestGlobal(global); + + let client = new DebuggerClient(DebuggerServer.connectPipe()); + yield connect(client); + + let { tabs } = yield listTabs(client); + let tab = findTab(tabs, "test"); + let [, tabClient] = yield attachTab(client, tab); + let [, threadClient] = yield attachThread(tabClient); + yield resume(threadClient); + + let promise = waitForNewSource(threadClient, SOURCE_URL); + loadSubScript(SOURCE_URL, global); + let { source } = yield promise; + let sourceClient = threadClient.source(source); + + let location = { line: 7 }; + let [packet, breakpointClient] = yield setBreakpoint(sourceClient, location); + do_check_false(packet.isPending); // NOTE: Change this when bug 1148356 lands + do_check_true("actualLocation" in packet); + let actualLocation = packet.actualLocation; + do_check_eq(actualLocation.line, 10); + + packet = yield executeOnNextTickAndWaitForPause(function () { + Cu.evalInSandbox("f()", global); + }, client); + do_check_eq(packet.type, "paused"); + let why = packet.why; + do_check_eq(why.type, "breakpoint"); + do_check_eq(why.actors.length, 1); + do_check_eq(why.actors[0], breakpointClient.actor); + let where = packet.frame.where; + do_check_eq(where.source.actor, source.actor); + do_check_eq(where.line, actualLocation.line); + + yield resume(threadClient); + yield close(client); + + do_test_finished(); + }); +} diff --git a/devtools/server/tests/unit/test_stepping-03.js b/devtools/server/tests/unit/test_stepping-03.js index 10706d63fdb3..6bb3ce1a40bc 100644 --- a/devtools/server/tests/unit/test_stepping-03.js +++ b/devtools/server/tests/unit/test_stepping-03.js @@ -38,7 +38,7 @@ function test_simple_stepping() gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { // Check the return value. do_check_eq(aPacket.type, "paused"); - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 5); + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 4); do_check_eq(aPacket.why.type, "resumeLimit"); // Check that stepping worked. do_check_eq(gDebuggee.a, 1); diff --git a/devtools/server/tests/unit/test_stepping-06.js b/devtools/server/tests/unit/test_stepping-06.js index 7acb05b31d3f..e2c58bcae851 100644 --- a/devtools/server/tests/unit/test_stepping-06.js +++ b/devtools/server/tests/unit/test_stepping-06.js @@ -43,7 +43,7 @@ function test_simple_stepping() gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { // Check that the return value is 10. do_check_eq(aPacket.type, "paused"); - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 5); + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 4); do_check_eq(aPacket.why.type, "resumeLimit"); do_check_eq(aPacket.why.frameFinished.return, 10); @@ -51,7 +51,7 @@ function test_simple_stepping() gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { // Check that the return value is undefined. do_check_eq(aPacket.type, "paused"); - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 8); + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 7); do_check_eq(aPacket.why.type, "resumeLimit"); do_check_eq(aPacket.why.frameFinished.return.type, "undefined"); diff --git a/devtools/server/tests/unit/test_stepping-07.js b/devtools/server/tests/unit/test_stepping-07.js deleted file mode 100644 index e09e59e3486c..000000000000 --- a/devtools/server/tests/unit/test_stepping-07.js +++ /dev/null @@ -1,92 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Check that stepping over an implicit return makes sense. Bug 1155966. - */ - -var gDebuggee; -var gClient; -var gCallback; - -function run_test() { - do_test_pending(); - run_test_with_server(DebuggerServer, function () { - run_test_with_server(WorkerDebuggerServer, do_test_finished); - }); -}; - -function run_test_with_server(aServer, aCallback) { - gCallback = aCallback; - initTestDebuggerServer(aServer); - gDebuggee = addTestGlobal("test-stepping", aServer); - gClient = new DebuggerClient(aServer.connectPipe()); - gClient.connect(testSteppingAndReturns); -} - -const testSteppingAndReturns = Task.async(function*() { - const [attachResponse, tabClient, threadClient] = yield attachTestTabAndResume(gClient, "test-stepping"); - ok(!attachResponse.error, "Should not get an error attaching"); - - dumpn("Evaluating test code and waiting for first debugger statement"); - const dbgStmt1 = yield executeOnNextTickAndWaitForPause(evaluateTestCode, gClient) - equal(dbgStmt1.frame.where.line, 3, - "Should be at debugger statement on line 3") - - dumpn("Testing stepping with implicit return"); - const step1 = yield stepOver(gClient, threadClient); - equal(step1.frame.where.line, 4, "Should step to line 4"); - const step2 = yield stepOver(gClient, threadClient); - equal(step2.frame.where.line, 7, - "Should step to line 7, the implicit return at the last line of the function"); - // This assertion doesn't pass yet. You would need to do *another* - // step at the end of this function to get the frameFinished. - // See bug 923975. - // - // ok(step2.why.frameFinished, "This should be the implicit function return"); - - dumpn("Continuing and waiting for second debugger statement"); - const dbgStmt2 = yield resumeAndWaitForPause(gClient, threadClient); - equal(dbgStmt2.frame.where.line, 12, - "Should be at debugger statement on line 3") - - dumpn("Testing stepping with explicit return"); - const step3 = yield stepOver(gClient, threadClient); - equal(step3.frame.where.line, 13, "Should step to line 13"); - const step4 = yield stepOver(gClient, threadClient); - equal(step4.frame.where.line, 15, "Should step out of the function from line 15"); - // This step is a bit funny, see bug 1013219 for details. - const step5 = yield stepOver(gClient, threadClient); - equal(step5.frame.where.line, 15, "Should step out of the function from line 15"); - ok(step5.why.frameFinished, "This should be the explicit function return"); - - finishClient(gClient, gCallback); -}); - -function evaluateTestCode() { - Cu.evalInSandbox( - ` // 1 - function implicitReturn() { // 2 - debugger; // 3 - if (this.someUndefinedProperty) { // 4 - yikes(); // 5 - } // 6 - } // 7 - // 8 - var yes = true; // 9 - function explicitReturn() { // 10 - if (yes) { // 11 - debugger; // 12 - return 1; // 13 - } // 14 - } // 15 - // 16 - implicitReturn(); // 17 - explicitReturn(); // 18 - `, // 19 - gDebuggee, - "1.8", - "test_stepping-07-test-code.js", - 1 - ); -} diff --git a/devtools/server/tests/unit/xpcshell.ini b/devtools/server/tests/unit/xpcshell.ini index d59a7560171f..5236cbe44eea 100644 --- a/devtools/server/tests/unit/xpcshell.ini +++ b/devtools/server/tests/unit/xpcshell.ini @@ -23,12 +23,14 @@ support-files = setBreakpoint-on-column-in-gcd-script.js setBreakpoint-on-column-with-no-offsets.js setBreakpoint-on-column-with-no-offsets-at-end-of-line.js + setBreakpoint-on-column-with-no-offsets-at-end-of-script.js setBreakpoint-on-column-with-no-offsets-in-gcd-script.js setBreakpoint-on-line.js setBreakpoint-on-line-in-gcd-script.js setBreakpoint-on-line-with-multiple-offsets.js setBreakpoint-on-line-with-multiple-statements.js setBreakpoint-on-line-with-no-offsets.js + setBreakpoint-on-line-with-no-offsets-at-end-of-script.js setBreakpoint-on-line-with-no-offsets-in-gcd-script.js [test_ScriptStore.js] @@ -203,7 +205,6 @@ reason = bug 820380 [test_stepping-04.js] [test_stepping-05.js] [test_stepping-06.js] -[test_stepping-07.js] [test_framebindings-01.js] [test_framebindings-02.js] [test_framebindings-03.js] @@ -261,6 +262,7 @@ reason = bug 1014071 [test_setBreakpoint-on-line-with-multiple-offsets.js] [test_setBreakpoint-on-line-with-multiple-statements.js] [test_setBreakpoint-on-line-with-no-offsets.js] +[test_setBreakpoint-on-line-with-no-offsets-at-end-of-script.js] [test_setBreakpoint-on-line-with-no-offsets-in-gcd-script.js] [test_safe-getter.js] [test_client_close.js] diff --git a/devtools/shared/Loader.jsm b/devtools/shared/Loader.jsm index db886660b231..87d1b7a95a31 100644 --- a/devtools/shared/Loader.jsm +++ b/devtools/shared/Loader.jsm @@ -71,7 +71,7 @@ XPCOMUtils.defineLazyGetter(loaderModules, "CSS", () => { return Cu.Sandbox(this, {wantGlobalProperties: ["CSS"]}).CSS; }); -var sharedGlobalBlacklist = ["sdk/indexed-db"]; +var sharedGlobalBlocklist = ["sdk/indexed-db"]; /** * Used when the tools should be loaded from the Firefox package itself. @@ -108,7 +108,7 @@ BuiltinProvider.prototype = { globals: this.globals, invisibleToDebugger: this.invisibleToDebugger, sharedGlobal: true, - sharedGlobalBlacklist: sharedGlobalBlacklist + sharedGlobalBlocklist, }); return promise.resolve(undefined); @@ -171,7 +171,7 @@ SrcdirProvider.prototype = { globals: this.globals, invisibleToDebugger: this.invisibleToDebugger, sharedGlobal: true, - sharedGlobalBlacklist: sharedGlobalBlacklist + sharedGlobalBlocklist, }); return this._writeManifest(srcDir).then(null, Cu.reportError); diff --git a/devtools/shared/client/main.js b/devtools/shared/client/main.js index 13e556e053e4..425b67ced47b 100644 --- a/devtools/shared/client/main.js +++ b/devtools/shared/client/main.js @@ -1100,12 +1100,14 @@ DebuggerClient.prototype = { request.emit("json-reply", packet); }; - this._pendingRequests.forEach((list, actor) => { + let pendingRequests = new Map(this._pendingRequests); + this._pendingRequests.clear(); + pendingRequests.forEach((list, actor) => { list.forEach(request => reject("pending", request, actor)); }); - this._pendingRequests.clear(); - this._activeRequests.forEach(reject.bind(null, "active")); + let activeRequests = new Map(this._activeRequests); this._activeRequests.clear(); + activeRequests.forEach(reject.bind(null, "active")); // The |_pools| array on the client-side currently is used only by // protocol.js to store active fronts, mirroring the actor pools found in diff --git a/dom/base/test/chrome/test_cpows.xul b/dom/base/test/chrome/test_cpows.xul index 0f10f509439a..acbbebb96fc0 100644 --- a/dom/base/test/chrome/test_cpows.xul +++ b/dom/base/test/chrome/test_cpows.xul @@ -16,6 +16,12 @@ SimpleTest.waitForExplicitFinish(); + const PREF_UNSAFE_FORBIDDEN = "dom.ipc.cpows.forbid-unsafe-from-browser"; + SpecialPowers.setBoolPref(PREF_UNSAFE_FORBIDDEN, false); + SimpleTest.registerCleanupFunction(() => { + SpecialPowers.clearUserPref(PREF_UNSAFE_FORBIDDEN); + }); + function done() { SimpleTest.finish(); } diff --git a/dom/ipc/tests/test_bug1086684.html b/dom/ipc/tests/test_bug1086684.html index f4483d9b5c9e..ebef81785c9a 100644 --- a/dom/ipc/tests/test_bug1086684.html +++ b/dom/ipc/tests/test_bug1086684.html @@ -31,8 +31,8 @@ let input = content.document.getElementById("f"); input.addEventListener("change", () => { MockFilePicker.cleanup(); - message.target.sendAsyncMessage("testBug1086684:childDone", { }, - { elt: input }); + let value = input.value; + message.target.sendAsyncMessage("testBug1086684:childDone", { value }); }); input.focus(); @@ -46,10 +46,10 @@ let test; function* testStructure(mm) { - let lastResult; + let value; function testDone(msg) { - test.next(msg.objects); + test.next(msg.data.value); } mm.addMessageListener("testBug1086684:childDone", testDone); @@ -58,12 +58,12 @@ let file = new File([blob], "helloworld.txt", { type: "text/plain" }); mm.sendAsyncMessage("testBug1086684:parentReady", { file }); - lastResult = yield; + value = yield; // Note that the "helloworld.txt" passed in above doesn't affect the // 'value' getter. Because we're mocking a file using a blob, we ask the // blob for its path, which is the empty string. - is(SpecialPowers.wrap(lastResult.elt).value, "", "got the right answer and didn't crash"); + is(value, "", "got the right answer and didn't crash"); SimpleTest.finish(); } diff --git a/dom/security/nsCSPContext.cpp b/dom/security/nsCSPContext.cpp index b53722f7bebe..b4a473cff8ec 100644 --- a/dom/security/nsCSPContext.cpp +++ b/dom/security/nsCSPContext.cpp @@ -915,7 +915,12 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource, NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr uploadChannel(do_QueryInterface(reportChannel)); - NS_ASSERTION(uploadChannel, "nsIUploadChannel is needed but not available to send CSP violation reports"); + if (!uploadChannel) { + // It's possible the URI provided can't be uploaded to, in which case + // we skip this one. We'll already have warned about a non-HTTP URI earlier. + continue; + } + rv = uploadChannel->SetUploadStream(sis, NS_LITERAL_CSTRING("application/json"), -1); NS_ENSURE_SUCCESS(rv, rv); diff --git a/extensions/cookie/nsCookiePermission.cpp b/extensions/cookie/nsCookiePermission.cpp index 5b50c58e2143..3d8e2ad3e3d8 100644 --- a/extensions/cookie/nsCookiePermission.cpp +++ b/extensions/cookie/nsCookiePermission.cpp @@ -45,7 +45,6 @@ static const uint32_t ACCEPT_FOR_N_DAYS = 3; static const bool kDefaultPolicy = true; static const char kCookiesLifetimePolicy[] = "network.cookie.lifetimePolicy"; static const char kCookiesLifetimeDays[] = "network.cookie.lifetime.days"; -static const char kCookiesAlwaysAcceptSession[] = "network.cookie.alwaysAcceptSessionCookies"; static const char kCookiesPrefsMigrated[] = "network.cookie.prefsMigrated"; // obsolete pref names for migration @@ -76,7 +75,6 @@ nsCookiePermission::Init() if (prefBranch) { prefBranch->AddObserver(kCookiesLifetimePolicy, this, false); prefBranch->AddObserver(kCookiesLifetimeDays, this, false); - prefBranch->AddObserver(kCookiesAlwaysAcceptSession, this, false); PrefChanged(prefBranch, nullptr); // migration code for original cookie prefs @@ -122,11 +120,6 @@ nsCookiePermission::PrefChanged(nsIPrefBranch *aPrefBranch, NS_SUCCEEDED(aPrefBranch->GetIntPref(kCookiesLifetimeDays, &val))) // save cookie lifetime in seconds instead of days mCookiesLifetimeSec = val * 24 * 60 * 60; - - bool bval; - if (PREF_CHANGED(kCookiesAlwaysAcceptSession) && - NS_SUCCEEDED(aPrefBranch->GetBoolPref(kCookiesAlwaysAcceptSession, &bval))) - mCookiesAlwaysAcceptSession = bval; } NS_IMETHODIMP diff --git a/extensions/cookie/nsCookiePermission.h b/extensions/cookie/nsCookiePermission.h index d64720812f4c..d683f206acf7 100644 --- a/extensions/cookie/nsCookiePermission.h +++ b/extensions/cookie/nsCookiePermission.h @@ -24,7 +24,6 @@ public: nsCookiePermission() : mCookiesLifetimeSec(INT64_MAX) , mCookiesLifetimePolicy(0) // ACCEPT_NORMALLY - , mCookiesAlwaysAcceptSession(false) {} bool Init(); @@ -40,7 +39,6 @@ private: int64_t mCookiesLifetimeSec; // lifetime limit specified in seconds uint8_t mCookiesLifetimePolicy; // pref for how long cookies are stored - bool mCookiesAlwaysAcceptSession; // don't prompt for session cookies }; // {EF565D0A-AB9A-4A13-9160-0644CDFD859A} diff --git a/js/ipc/JavaScriptParent.cpp b/js/ipc/JavaScriptParent.cpp index fa65efb57068..1cbf4dffdf10 100644 --- a/js/ipc/JavaScriptParent.cpp +++ b/js/ipc/JavaScriptParent.cpp @@ -7,6 +7,7 @@ #include "JavaScriptParent.h" #include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/ScriptSettings.h" #include "nsJSUtils.h" #include "jsfriendapi.h" #include "jswrapper.h" @@ -68,8 +69,11 @@ JavaScriptParent::allowMessage(JSContext* cx) return true; if (ForbidUnsafeBrowserCPOWs()) { - if (JSObject* global = JS::CurrentGlobalOrNull(cx)) { - if (!JS::AddonIdOfObject(global)) { + nsIGlobalObject* global = dom::GetIncumbentGlobal(); + JSObject* jsGlobal = global ? global->GetGlobalJSObject() : nullptr; + if (jsGlobal) { + JSAutoCompartment ac(cx, jsGlobal); + if (!JS::AddonIdOfObject(jsGlobal) && !xpc::CompartmentPrivate::Get(jsGlobal)->allowCPOWs) { JS_ReportError(cx, "unsafe CPOW usage forbidden"); return false; } diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index f7c0820360d2..3ea9abe339f9 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -346,16 +346,12 @@ BytecodeCompiler::prepareAndEmitTree(ParseNode** ppn) { if (!FoldConstants(cx, ppn, parser.ptr()) || !NameFunctions(cx, *ppn) || - !emitter->updateLocalsToFrameSlots()) + !emitter->updateLocalsToFrameSlots() || + !emitter->emitTree(*ppn)) { return false; } - emitter->setFunctionBodyEndPos((*ppn)->pn_pos); - - if (!emitter->emitTree(*ppn)) - return false; - return true; } @@ -832,7 +828,7 @@ frontend::CompileLazyFunction(JSContext* cx, Handle lazy, const cha MOZ_ASSERT(!options.forEval); BytecodeEmitter bce(/* parent = */ nullptr, &parser, pn->pn_funbox, script, lazy, /* insideEval = */ false, /* evalCaller = */ nullptr, - /* insideNonGlobalEval = */ false, pn->pn_pos, + /* insideNonGlobalEval = */ false, options.lineno, BytecodeEmitter::LazyFunction); if (!bce.init()) return false; diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 39c31e04b60a..607358517b20 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -143,27 +143,12 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent, insideEval(insideEval), insideNonGlobalEval(insideNonGlobalEval), insideModule(false), - emitterMode(emitterMode), - functionBodyEndPosSet(false) + emitterMode(emitterMode) { MOZ_ASSERT_IF(evalCaller, insideEval); MOZ_ASSERT_IF(emitterMode == LazyFunction, lazyScript); } -BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent, - Parser* parser, SharedContext* sc, - HandleScript script, Handle lazyScript, - bool insideEval, HandleScript evalCaller, - bool insideNonGlobalEval, TokenPos bodyPosition, - EmitterMode emitterMode) - : BytecodeEmitter(parent, parser, sc, script, lazyScript, insideEval, - evalCaller, insideNonGlobalEval, - parser->tokenStream.srcCoords.lineNum(bodyPosition.begin), - emitterMode) -{ - setFunctionBodyEndPos(bodyPosition); -} - bool BytecodeEmitter::init() { @@ -3604,13 +3589,9 @@ BytecodeEmitter::emitFunctionScript(ParseNode* body) switchToMain(); } - setFunctionBodyEndPos(body->pn_pos); if (!emitTree(body)) return false; - if (!updateSourceCoordNotes(body->pn_pos.end)) - return false; - if (sc->isFunctionBox()) { if (sc->asFunctionBox()->isGenerator()) { // If we fall off the end of a generator, do a final yield. @@ -3710,7 +3691,6 @@ BytecodeEmitter::emitModuleScript(ParseNode* body) // may walk the scope chain of currently compiling scripts. JSScript::linkToModuleFromEmitter(cx, script, modulebox); - setFunctionBodyEndPos(body->pn_pos); if (!emitTree(body)) return false; @@ -6429,9 +6409,10 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) script->bindings = funbox->bindings; + uint32_t lineNum = parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin); BytecodeEmitter bce2(this, parser, funbox, script, /* lazyScript = */ nullptr, insideEval, evalCaller, - insideNonGlobalEval, pn->pn_pos, emitterMode); + insideNonGlobalEval, lineNum, emitterMode); if (!bce2.init()) return false; @@ -6802,13 +6783,6 @@ BytecodeEmitter::emitReturn(ParseNode* pn) return false; } - // We know functionBodyEndPos is set because "return" is only - // valid in a function, and so we've passed through - // emitFunctionScript. - MOZ_ASSERT(functionBodyEndPosSet); - if (!updateSourceCoordNotes(functionBodyEndPos)) - return false; - /* * EmitNonLocalJumpFixup may add fixup bytecode to close open try * blocks having finally clauses and to exit intermingled let blocks. diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index be59b8530302..b7cacb6249da 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -234,11 +234,6 @@ struct BytecodeEmitter const EmitterMode emitterMode; - // The end location of a function body that is being emitted. - uint32_t functionBodyEndPos; - // Whether functionBodyEndPos was set. - bool functionBodyEndPosSet; - /* * Note that BytecodeEmitters are magic: they own the arena "top-of-stack" * space above their tempMark points. This means that you cannot alloc from @@ -249,14 +244,6 @@ struct BytecodeEmitter HandleScript script, Handle lazyScript, bool insideEval, HandleScript evalCaller, bool insideNonGlobalEval, uint32_t lineNum, EmitterMode emitterMode = Normal); - - // An alternate constructor that uses a TokenPos for the starting - // line and that sets functionBodyEndPos as well. - BytecodeEmitter(BytecodeEmitter* parent, Parser* parser, SharedContext* sc, - HandleScript script, Handle lazyScript, - bool insideEval, HandleScript evalCaller, - bool insideNonGlobalEval, TokenPos bodyPosition, EmitterMode emitterMode = Normal); - bool init(); bool updateLocalsToFrameSlots(); @@ -317,11 +304,6 @@ struct BytecodeEmitter unsigned currentLine() const { return current->currentLine; } unsigned lastColumn() const { return current->lastColumn; } - void setFunctionBodyEndPos(TokenPos pos) { - functionBodyEndPos = pos.end; - functionBodyEndPosSet = true; - } - bool reportError(ParseNode* pn, unsigned errorNumber, ...); bool reportStrictWarning(ParseNode* pn, unsigned errorNumber, ...); bool reportStrictModeError(ParseNode* pn, unsigned errorNumber, ...); diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 66875369a8e9..984b8cf720be 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -3188,8 +3188,6 @@ Parser::functionArgsAndBodyGeneric(InHandling inHandling, return false; } - handler.setEndPosition(body, pos().begin); - return finishFunctionDefinition(pn, funbox, body); } diff --git a/js/src/jit-test/tests/debug/Frame-onPop-23.js b/js/src/jit-test/tests/debug/Frame-onPop-23.js deleted file mode 100644 index d49e1d456c98..000000000000 --- a/js/src/jit-test/tests/debug/Frame-onPop-23.js +++ /dev/null @@ -1,34 +0,0 @@ -// Check that the line number reported at an onPop stop makes sense, -// even when it happens on an "artificial" instruction. - -var g = newGlobal(); - -// This bit of code arranges for the line number of the "artificial" -// instruction to be something nonsensical -- the middle of a loop -// which cannot be entered. -g.eval(`function f() { - debugger; // +0 - if(false) { // +1 - for(var b=0; b<0; b++) { // +2 - c = 2; // +3 - } // +4 - } // +5 -} // +6 -`); - -var dbg = Debugger(g); - -let debugLine; -let foundLine; - -dbg.onDebuggerStatement = function(frame) { - debugLine = frame.script.getOffsetLocation(frame.offset).lineNumber; - frame.onPop = function(c) { - foundLine = this.script.getOffsetLocation(this.offset).lineNumber; - }; -}; - -g.eval("f();\n"); - -// The stop should happen on the closing brace of the function. -assertEq(foundLine == debugLine + 6, true); diff --git a/js/src/jit-test/tests/debug/Frame-onStep-11.js b/js/src/jit-test/tests/debug/Frame-onStep-11.js index dae366165c40..21521ae52504 100644 --- a/js/src/jit-test/tests/debug/Frame-onStep-11.js +++ b/js/src/jit-test/tests/debug/Frame-onStep-11.js @@ -33,4 +33,4 @@ dbg.onDebuggerStatement = function(frame) { g.f(); -assertEq(foundLines, ",1,2,3,4,5,6,7,8,10,11"); +assertEq(foundLines, ",1,2,3,4,5,6,7,8,10"); diff --git a/js/src/jit-test/tests/debug/Frame-onStep-12.js b/js/src/jit-test/tests/debug/Frame-onStep-12.js index 4db38b313606..517ccbf1d97e 100644 --- a/js/src/jit-test/tests/debug/Frame-onStep-12.js +++ b/js/src/jit-test/tests/debug/Frame-onStep-12.js @@ -65,7 +65,7 @@ testOne("testTryFinally", } finally { // +6 } // +7 nothing(); // +8 - `, "1689"); + `, "168"); // The same but without a finally clause. testOne("testTryCatch", @@ -74,7 +74,7 @@ testOne("testTryCatch", } catch (e) { // +6 } // +7 nothing(); // +8 - `, "189"); + `, "18"); // Test the instructions at the end of a "catch". testOne("testCatchFinally", @@ -85,7 +85,7 @@ testOne("testCatchFinally", } finally { // +6 } // +7 nothing(); // +8 - `, "1689"); + `, "168"); // The same but without a finally clause. This relies on a // SpiderMonkey extension, because otherwise there's no way to see @@ -98,7 +98,7 @@ testOne("testCatch", } catch (e) { // +6 } // +7 nothing(); // +8 - `, "189"); + `, "18"); // Test the instruction at the end of a "finally" clause. testOne("testFinally", @@ -107,7 +107,7 @@ testOne("testFinally", ${bitOfCode} } // +6 nothing(); // +7 - `, "178"); + `, "17"); // Test the instruction at the end of a "then" clause. testOne("testThen", @@ -116,7 +116,7 @@ testOne("testThen", } else { // +6 } // +7 nothing(); // +8 - `, "189"); + `, "18"); // Test the instructions leaving a switch block. testOne("testSwitch", @@ -126,4 +126,4 @@ testOne("testSwitch", ${bitOfCode} } // +6 nothing(); // +7 - `, "178"); + `, "17"); diff --git a/js/src/jit-test/tests/debug/Frame-onStep-13.js b/js/src/jit-test/tests/debug/Frame-onStep-13.js deleted file mode 100644 index cc2c18c6d644..000000000000 --- a/js/src/jit-test/tests/debug/Frame-onStep-13.js +++ /dev/null @@ -1,29 +0,0 @@ -// Stepping over a not-taken "if" that is at the end of the function -// should move to the end of the function, not somewhere in the body -// of the "if". - -var g = newGlobal(); -g.eval(`function f() { // 1 - var a,c; // 2 - debugger; // 3 - if(false) { // 4 - for(var b=0; b<0; b++) { // 5 - c = 2; // 6 - } // 7 - } // 8 -} // 9 -`); - -var dbg = Debugger(g); -var badStep = false; - -dbg.onDebuggerStatement = function(frame) { - let debugLine = frame.script.getOffsetLocation(frame.offset).lineNumber; - assertEq(debugLine, 3); - frame.onStep = function() { - let foundLine = this.script.getOffsetLocation(this.offset).lineNumber; - assertEq(foundLine <= 4 || foundLine >= 8, true); - }; -}; - -g.eval("f();\n"); diff --git a/js/src/jit-test/tests/debug/Frame-onStep-16.js b/js/src/jit-test/tests/debug/Frame-onStep-16.js deleted file mode 100644 index b011da4288d6..000000000000 --- a/js/src/jit-test/tests/debug/Frame-onStep-16.js +++ /dev/null @@ -1,34 +0,0 @@ -// Stepping through a function with a return statement should pause on -// the closing brace of the function. - -var g = newGlobal(); -var dbg = Debugger(g); -var log; - -function test(fnStr) { - log = ''; - g.eval(fnStr); - - dbg.onDebuggerStatement = function(frame) { - frame.onStep = function() { - let {lineNumber, isEntryPoint} = frame.script.getOffsetLocation(frame.offset); - if (isEntryPoint) { - log += lineNumber + ' '; - } - }; - }; - - g.eval("f(23);"); -} - -test("function f(x) {\n" + // 1 - " debugger;\n" + // 2 - " return 23 + x;\n" + // 3 - "}\n"); // 4 -assertEq(log, '3 4 '); - -test("function f(x) {\n" + // 1 - " debugger;\n" + // 2 - " return;\n" + // 3 - "}\n"); // 4 -assertEq(log, '3 4 '); diff --git a/js/src/jit-test/tests/debug/Frame-onStep-lines-01.js b/js/src/jit-test/tests/debug/Frame-onStep-lines-01.js index 0c486971c7c8..b805f0efcbc4 100644 --- a/js/src/jit-test/tests/debug/Frame-onStep-lines-01.js +++ b/js/src/jit-test/tests/debug/Frame-onStep-lines-01.js @@ -48,7 +48,7 @@ assertEq(Object.keys(offsets).length, 2); // have no effect on this one. doSingleStep = false; g.eval('t(0, 0, 0)'); -assertEq(Object.keys(offsets).length, 7); +assertEq(Object.keys(offsets).length, 6); doSingleStep = true; // Single-step in an eval frame. This should reach every line but the diff --git a/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-01.js b/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-01.js index 620d5624415d..50c38dffe1cc 100644 --- a/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-01.js +++ b/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-01.js @@ -16,4 +16,4 @@ Debugger(global).onDebuggerStatement = function (frame) { global.log = ''; global.eval("function f(n) { for (var i = 0; i < n; ++i) log += '. '; log += '! '; } debugger;"); global.f(3); -assertEq(global.log, "25 32 44 . 39 32 44 . 39 32 44 . 39 32 57 ! 70 "); +assertEq(global.log, "25 32 44 . 39 32 44 . 39 32 44 . 39 32 57 ! 69 "); diff --git a/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-06.js b/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-06.js index 20b49add8805..fe9bdb621798 100644 --- a/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-06.js +++ b/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-06.js @@ -16,7 +16,7 @@ Debugger(global).onDebuggerStatement = function (frame) { global.log = ""; global.eval("function ppppp() { return 1; }"); // 1 2 3 4 -// 01234567890123456789012345678901234567890123456789 +// 0123456789012345678901234567890123456789012345678 global.eval("function f(){ 1 && ppppp(ppppp()) && new Error() } debugger;"); global.f(); @@ -24,5 +24,5 @@ global.f(); // 25 - Inner print() // 19 - Outer print() // 37 - new Error() -// 49 - Exit the function body -assertEq(global.log, "14 25 19 37 49 "); +// 48 - Exit the function body +assertEq(global.log, "14 25 19 37 48 "); diff --git a/js/src/jit-test/tests/debug/Script-startLine.js b/js/src/jit-test/tests/debug/Script-startLine.js index f5b2d108505c..4d3c49950102 100644 --- a/js/src/jit-test/tests/debug/Script-startLine.js +++ b/js/src/jit-test/tests/debug/Script-startLine.js @@ -55,9 +55,7 @@ g.eval("/* Any copyright is dedicated to the Public Domain.\n" + " eval(\"42;\");\n" + " function foo() {}\n" + " if (true) {\n" + - " foo();\n" + - // The "missing" newline here is a trick to make a newline - // source note come at the end. A real newline between the two - // closing braces causes a setline note instead. - " } }"); -test(g.secondCall, 8); + " foo();\n" + // <- this is +6 and must be within the extent + " }\n" + + "}"); +test(g.secondCall, 7); diff --git a/js/xpconnect/idl/xpccomponents.idl b/js/xpconnect/idl/xpccomponents.idl index bcb2302c4f3a..66cd7027ce57 100644 --- a/js/xpconnect/idl/xpccomponents.idl +++ b/js/xpconnect/idl/xpccomponents.idl @@ -123,7 +123,7 @@ interface ScheduledGCCallback : nsISupports /** * interface of Components.utils */ -[scriptable, uuid(3ce3a6f8-2b59-439c-a57e-74e7b122fb3c)] +[scriptable, uuid(86003fe3-ee9a-4620-91dc-eef8b1e58815)] interface nsIXPCComponents_Utils : nsISupports { @@ -444,6 +444,15 @@ interface nsIXPCComponents_Utils : nsISupports */ ACString getCrossProcessWrapperTag(in jsval obj); + /** + * If CPOWs are disabled for browser code via the + * dom.ipc.cpows.forbid-unsafe-from-browser preferences, then only + * add-ons can use CPOWs. This function allows a non-addon scope + * to opt into CPOWs. It's necessary for the implementation of + * RemoteAddonsParent.jsm. + */ + void permitCPOWsInScope(in jsval obj); + /* * To be called from JS only. This is for Gecko internal use only, and may * disappear at any moment. diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index fad59a1ff585..ec9a168e48e4 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -2860,6 +2860,17 @@ nsXPCComponents_Utils::GetCrossProcessWrapperTag(HandleValue obj, nsACString& ou return NS_OK; } +NS_IMETHODIMP +nsXPCComponents_Utils::PermitCPOWsInScope(HandleValue obj) +{ + if (!obj.isObject()) + return NS_ERROR_INVALID_ARG; + + JSObject* scopeObj = js::UncheckedUnwrap(&obj.toObject()); + CompartmentPrivate::Get(scopeObj)->allowCPOWs = true; + return NS_OK; +} + NS_IMETHODIMP nsXPCComponents_Utils::RecomputeWrappers(HandleValue vobj, JSContext* cx) { diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index de41067e4965..c246b9441b66 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -202,6 +202,7 @@ CompartmentPrivate::CompartmentPrivate(JSCompartment* c) , skipWriteToGlobalPrototype(false) , isWebExtensionContentScript(false) , waiveInterposition(false) + , allowCPOWs(false) , universalXPConnectEnabled(false) , forcePermissiveCOWs(false) , scriptability(c) diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 02e80dcfbcb3..e87c71ba2c60 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -3730,6 +3730,13 @@ public: // scope. bool waiveInterposition; + // If CPOWs are disabled for browser code via the + // dom.ipc.cpows.forbid-unsafe-from-browser preferences, then only + // add-ons can use CPOWs. This flag allows a non-addon scope + // to opt into CPOWs. It's necessary for the implementation of + // RemoteAddonsParent.jsm. + bool allowCPOWs; + // This is only ever set during mochitest runs when enablePrivilege is called. // It's intended as a temporary stopgap measure until we can finish ripping out // enablePrivilege. Once set, this value is never unset (i.e., it doesn't follow diff --git a/layout/base/AccessibleCaretManager.cpp b/layout/base/AccessibleCaretManager.cpp index faa1c09fe7fd..398b70e7f6a8 100644 --- a/layout/base/AccessibleCaretManager.cpp +++ b/layout/base/AccessibleCaretManager.cpp @@ -69,6 +69,8 @@ AccessibleCaretManager::sSelectionBarEnabled = false; /*static*/ bool AccessibleCaretManager::sCaretsExtendedVisibility = false; /*static*/ bool +AccessibleCaretManager::sCaretsScriptUpdates = false; +/*static*/ bool AccessibleCaretManager::sHapticFeedback = false; AccessibleCaretManager::AccessibleCaretManager(nsIPresShell* aPresShell) @@ -89,6 +91,8 @@ AccessibleCaretManager::AccessibleCaretManager(nsIPresShell* aPresShell) "layout.accessiblecaret.bar.enabled"); Preferences::AddBoolVarCache(&sCaretsExtendedVisibility, "layout.accessiblecaret.extendedvisibility"); + Preferences::AddBoolVarCache(&sCaretsScriptUpdates, + "layout.accessiblecaret.allow_script_change_updates"); Preferences::AddBoolVarCache(&sHapticFeedback, "layout.accessiblecaret.hapticfeedback"); addedPrefs = true; @@ -128,9 +132,8 @@ AccessibleCaretManager::OnSelectionChanged(nsIDOMDocument* aDoc, // Move the cursor by Javascript / or unknown internal. if (aReason == nsISelectionListener::NO_REASON) { - // Extended visibility won't make hidden carets visible. Visible carets will - // be updated or hidden as appropriate. - if (sCaretsExtendedVisibility && + // Update visible carets, if javascript changes are allowed. + if (sCaretsScriptUpdates && (mFirstCaret->IsLogicallyVisible() || mSecondCaret->IsLogicallyVisible())) { FlushLayout(); UpdateCarets(); diff --git a/layout/base/AccessibleCaretManager.h b/layout/base/AccessibleCaretManager.h index 61f98227e6cb..d9289f6be13a 100644 --- a/layout/base/AccessibleCaretManager.h +++ b/layout/base/AccessibleCaretManager.h @@ -251,12 +251,16 @@ protected: // selection bar is always disabled in cursor mode. static bool sSelectionBarEnabled; - // AccessibleCaret visibility preference. Used to avoid hiding caret during - // (NO_REASON) selection change notifications generated by keyboard IME, and to - // maintain a visible ActionBar while carets NotShown during scroll and while - // cursor is on an empty input. + // Android specific visibility extensions correct compatibility issues + // with caret-drag, tapping into empty inputs, and ActionBar visiblity + // during page scroll. static bool sCaretsExtendedVisibility; + // By default, javascript content selection changes closes AccessibleCarets and + // UI interactions. Optionally, we can try to maintain the active UI, keeping + // carets and ActionBar available. + static bool sCaretsScriptUpdates; + // AccessibleCaret pref for haptic feedback behaviour on longPress. static bool sHapticFeedback; }; diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index 2cb972c591d0..66957240a04f 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -924,11 +924,15 @@ pref("layout.accessiblecaret.enabled", false); // Android generates long tap (mouse) events. pref("layout.accessiblecaret.use_long_tap_injector", false); -// Android tries to maintain extended visibility of the AccessibleCarets -// during Selection change notifications generated by Javascript, -// or misc internal events. +// AccessibleCarets behaviour is extended to support Android specific +// requirements during caret-drag, tapping into empty inputs, and to +// hide carets while maintaining ActionBar visiblity during page scroll. pref("layout.accessiblecaret.extendedvisibility", true); +// Selection change notifications generated by Javascript changes +// update active AccessibleCarets / UI interactions. +pref("layout.accessiblecaret.allow_script_change_updates", true); + // Optionally provide haptic feedback on longPress selection events. pref("layout.accessiblecaret.hapticfeedback", true); diff --git a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/permissions/TestPermissions.java b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/permissions/TestPermissions.java index b95af6bd9b82..07fbab4933a4 100644 --- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/permissions/TestPermissions.java +++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/permissions/TestPermissions.java @@ -167,6 +167,62 @@ public class TestPermissions { verify(helper, never()).prompt(anyActivity(), any(String[].class)); } + @Test + public void testDoNotPromptBehavior() { + PermissionsHelper helper = mockDenyingHelper(); + Permissions.setPermissionHelper(helper); + + Permissions.from(mockActivity()) + .withPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE) + .doNotPrompt() + .andFallback(mock(Runnable.class)) + .run(mock(Runnable.class)); + + verify(helper, never()).prompt(anyActivity(), any(String[].class)); + } + + @Test(expected = IllegalStateException.class) + public void testThrowsExceptionIfNeedstoPromptWithNonActivityContext() { + Permissions.setPermissionHelper(mockDenyingHelper()); + + Permissions.from(mock(Context.class)) + .withPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE) + .andFallback(mock(Runnable.class)) + .run(mock(Runnable.class)); + } + + @Test + public void testDoNotPromptIfFalse() { + Activity activity = mockActivity(); + + PermissionsHelper helper = mockDenyingHelper(); + Permissions.setPermissionHelper(helper); + + Permissions.from(activity) + .withPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE) + .doNotPromptIf(false) + .andFallback(mock(Runnable.class)) + .run(mock(Runnable.class)); + + verify(helper).prompt(anyActivity(), any(String[].class)); + + Permissions.onRequestPermissionsResult(activity, new String[0], new int[0]); + } + + @Test + public void testDoNotPromptIfTrue() { + PermissionsHelper helper = mockDenyingHelper(); + Permissions.setPermissionHelper(helper); + + Permissions.from(mockActivity()) + .withPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE) + .doNotPromptIf(true) + .andFallback(mock(Runnable.class)) + .run(mock(Runnable.class)); + + verify(helper, never()).prompt(anyActivity(), any(String[].class)); + } + private Activity mockActivity() { return mock(Activity.class); } diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index c6eff239bda5..1163555e2377 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1858,7 +1858,6 @@ pref("network.cookie.cookieBehavior", 0); // Keep the old default of accep #endif pref("network.cookie.thirdparty.sessionOnly", false); pref("network.cookie.lifetimePolicy", 0); // 0-accept, 1-dontUse 2-acceptForSession, 3-acceptForNDays -pref("network.cookie.alwaysAcceptSessionCookies", false); pref("network.cookie.prefsMigrated", false); pref("network.cookie.lifetime.days", 90); // Ignored unless network.cookie.lifetimePolicy is 3. @@ -4938,10 +4937,13 @@ pref("layout.accessiblecaret.timeout_ms", 3000); // or long tap events does not fired by APZ. pref("layout.accessiblecaret.use_long_tap_injector", true); -// Selection change notifications generated by Javascript, or misc internal -// events hide AccessibleCarets by default. +// Use AccessibleCaret default behaviours. pref("layout.accessiblecaret.extendedvisibility", false); +// Selection change notifications generated by Javascript hide +// AccessibleCarets and close UI interaction by default. +pref("layout.accessiblecaret.allow_script_change_updates", false); + // Optionally provide haptic feedback on longPress selection events. pref("layout.accessiblecaret.hapticfeedback", false); diff --git a/security/manager/pki/resources/content/exceptionDialog.xul b/security/manager/pki/resources/content/exceptionDialog.xul index 61f3148911b8..6d417f360ccd 100644 --- a/security/manager/pki/resources/content/exceptionDialog.xul +++ b/security/manager/pki/resources/content/exceptionDialog.xul @@ -9,12 +9,12 @@