diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
index 101065afc3a2..85e20ffbad92 100644
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1662,7 +1662,7 @@ pref("loop.enabled", true);
pref("loop.server", "https://loop.services.mozilla.com/v0");
pref("loop.seenToS", "unseen");
pref("loop.gettingStarted.seen", false);
-pref("loop.gettingStarted.url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/hello/start");
+pref("loop.gettingStarted.url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/hello/start/");
pref("loop.gettingStarted.resumeOnFirstJoin", false);
pref("loop.learnMoreUrl", "https://www.firefox.com/hello/");
pref("loop.legal.ToS_url", "https://www.mozilla.org/about/legal/terms/firefox-hello/");
diff --git a/browser/base/content/browser-fxaccounts.js b/browser/base/content/browser-fxaccounts.js
index 5873206bc890..537705d85fb7 100644
--- a/browser/base/content/browser-fxaccounts.js
+++ b/browser/base/content/browser-fxaccounts.js
@@ -2,19 +2,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/.
-XPCOMUtils.defineLazyGetter(this, "FxAccountsCommon", function () {
- return Cu.import("resource://gre/modules/FxAccountsCommon.js", {});
-});
-
-XPCOMUtils.defineLazyModuleGetter(this, "fxaMigrator",
- "resource://services-sync/FxaMigrator.jsm");
-
-const PREF_SYNC_START_DOORHANGER = "services.sync.ui.showSyncStartDoorhanger";
-const DOORHANGER_ACTIVATE_DELAY_MS = 5000;
-const SYNC_MIGRATION_NOTIFICATION_TITLE = "fxa-migration";
-
let gFxAccounts = {
+ PREF_SYNC_START_DOORHANGER: "services.sync.ui.showSyncStartDoorhanger",
+ DOORHANGER_ACTIVATE_DELAY_MS: 5000,
+ SYNC_MIGRATION_NOTIFICATION_TITLE: "fxa-migration",
+
_initialized: false,
_inCustomizationMode: false,
@@ -34,8 +27,8 @@ let gFxAccounts = {
"weave:service:login:error",
"weave:service:setup-complete",
"fxa-migration:state-changed",
- FxAccountsCommon.ONVERIFIED_NOTIFICATION,
- FxAccountsCommon.ONLOGOUT_NOTIFICATION
+ this.FxAccountsCommon.ONVERIFIED_NOTIFICATION,
+ this.FxAccountsCommon.ONLOGOUT_NOTIFICATION
];
},
@@ -108,8 +101,8 @@ let gFxAccounts = {
observe: function (subject, topic, data) {
switch (topic) {
- case FxAccountsCommon.ONVERIFIED_NOTIFICATION:
- Services.prefs.setBoolPref(PREF_SYNC_START_DOORHANGER, true);
+ case this.FxAccountsCommon.ONVERIFIED_NOTIFICATION:
+ Services.prefs.setBoolPref(this.PREF_SYNC_START_DOORHANGER, true);
break;
case "weave:service:sync:start":
this.onSyncStart();
@@ -131,11 +124,11 @@ let gFxAccounts = {
let showDoorhanger = false;
try {
- showDoorhanger = Services.prefs.getBoolPref(PREF_SYNC_START_DOORHANGER);
+ showDoorhanger = Services.prefs.getBoolPref(this.PREF_SYNC_START_DOORHANGER);
} catch (e) { /* The pref might not exist. */ }
if (showDoorhanger) {
- Services.prefs.clearUserPref(PREF_SYNC_START_DOORHANGER);
+ Services.prefs.clearUserPref(this.PREF_SYNC_START_DOORHANGER);
this.showSyncStartedDoorhanger();
}
},
@@ -155,7 +148,7 @@ let gFxAccounts = {
// a short delay. Without this delay the doorhanger would not show up
// or with a too small delay show up while we're still animating the
// window.
- setTimeout(() => this.onSyncStart(), DOORHANGER_ACTIVATE_DELAY_MS);
+ setTimeout(() => this.onSyncStart(), this.DOORHANGER_ACTIVATE_DELAY_MS);
} else {
this._inCustomizationMode = event.type == "customizationstarting";
this.updateAppMenuItem();
@@ -251,12 +244,12 @@ let gFxAccounts = {
let status = null;
let label = null;
switch (this._migrationInfo.state) {
- case fxaMigrator.STATE_USER_FXA:
+ case this.fxaMigrator.STATE_USER_FXA:
status = "migrate-signup";
label = this.strings.formatStringFromName("needUserShort",
[this.button.getAttribute("fxabrandname")], 1);
break;
- case fxaMigrator.STATE_USER_FXA_VERIFIED:
+ case this.fxaMigrator.STATE_USER_FXA_VERIFIED:
status = "migrate-verify";
label = this.strings.formatStringFromName("needVerifiedUserShort",
[this._migrationInfo.email],
@@ -270,7 +263,7 @@ let gFxAccounts = {
updateMigrationNotification: Task.async(function* () {
if (!this._migrationInfo) {
- Weave.Notifications.removeAll(SYNC_MIGRATION_NOTIFICATION_TITLE);
+ Weave.Notifications.removeAll(this.SYNC_MIGRATION_NOTIFICATION_TITLE);
return;
}
if (gBrowser.currentURI.spec.split("?")[0] == "about:accounts") {
@@ -280,7 +273,7 @@ let gFxAccounts = {
}
let note = null;
switch (this._migrationInfo.state) {
- case fxaMigrator.STATE_USER_FXA: {
+ case this.fxaMigrator.STATE_USER_FXA: {
let msg = this.strings.GetStringFromName("needUserLong");
let upgradeLabel =
this.strings.GetStringFromName("upgradeToFxA.label");
@@ -289,13 +282,13 @@ let gFxAccounts = {
note = new Weave.Notification(
undefined, msg, undefined, Weave.Notifications.PRIORITY_WARNING, [
new Weave.NotificationButton(upgradeLabel, upgradeAccessKey, () => {
- fxaMigrator.createFxAccount(window);
+ this.fxaMigrator.createFxAccount(window);
}),
]
);
break;
}
- case fxaMigrator.STATE_USER_FXA_VERIFIED: {
+ case this.fxaMigrator.STATE_USER_FXA_VERIFIED: {
let msg =
this.strings.formatStringFromName("needVerifiedUserLong",
[this._migrationInfo.email], 1);
@@ -306,14 +299,14 @@ let gFxAccounts = {
note = new Weave.Notification(
undefined, msg, undefined, Weave.Notifications.PRIORITY_INFO, [
new Weave.NotificationButton(resendLabel, resendAccessKey, () => {
- fxaMigrator.resendVerificationMail();
+ this.fxaMigrator.resendVerificationMail();
}),
]
);
break;
}
}
- note.title = SYNC_MIGRATION_NOTIFICATION_TITLE;
+ note.title = this.SYNC_MIGRATION_NOTIFICATION_TITLE;
Weave.Notifications.replaceTitle(note);
}),
@@ -328,7 +321,7 @@ let gFxAccounts = {
this.openSignInAgainPage("menupanel");
break;
case "migrate-signup":
- fxaMigrator.createFxAccount(window);
+ this.fxaMigrator.createFxAccount(window);
break;
case "migrate-verify":
// Instead of using the migrator module directly here the UX calls for
@@ -374,3 +367,10 @@ let gFxAccounts = {
this.openAccountsPage("reauth", { entryPoint: entryPoint });
},
};
+
+XPCOMUtils.defineLazyGetter(gFxAccounts, "FxAccountsCommon", function () {
+ return Cu.import("resource://gre/modules/FxAccountsCommon.js", {});
+});
+
+XPCOMUtils.defineLazyModuleGetter(gFxAccounts, "fxaMigrator",
+ "resource://services-sync/FxaMigrator.jsm");
diff --git a/browser/base/content/test/general/browser_fxa_migrate.js b/browser/base/content/test/general/browser_fxa_migrate.js
index d631a4ea53db..778bad5ab136 100644
--- a/browser/base/content/test/general/browser_fxa_migrate.js
+++ b/browser/base/content/test/general/browser_fxa_migrate.js
@@ -11,7 +11,7 @@ add_task(function* test() {
// Fake the state where we need an FxA user.
let buttonPromise = promiseButtonMutation();
Services.obs.notifyObservers(null, STATE_CHANGED_TOPIC,
- fxaMigrator.STATE_USER_FXA);
+ imports.fxaMigrator.STATE_USER_FXA);
let buttonState = yield buttonPromise;
assertButtonState(buttonState, "migrate-signup", true);
Assert.ok(Weave.Notifications.notifications.some(n => {
@@ -24,7 +24,7 @@ add_task(function* test() {
createInstance(Ci.nsISupportsString);
email.data = "foo@example.com";
Services.obs.notifyObservers(email, STATE_CHANGED_TOPIC,
- fxaMigrator.STATE_USER_FXA_VERIFIED);
+ imports.fxaMigrator.STATE_USER_FXA_VERIFIED);
buttonState = yield buttonPromise;
assertButtonState(buttonState, "migrate-verify", true,
"foo@example.com not verified");
diff --git a/browser/components/loop/content/js/roomViews.js b/browser/components/loop/content/js/roomViews.js
index 5327d09582e2..f5d078e5c3df 100644
--- a/browser/components/loop/content/js/roomViews.js
+++ b/browser/components/loop/content/js/roomViews.js
@@ -300,10 +300,16 @@ loop.roomViews = (function(mozL10n) {
);
}
case ROOM_STATES.ENDED: {
- return sharedViews.FeedbackView({
- feedbackStore: this.props.feedbackStore,
- onAfterFeedbackReceived: this.closeWindow}
- );
+ if (this.state.used)
+ return sharedViews.FeedbackView({
+ feedbackStore: this.props.feedbackStore,
+ onAfterFeedbackReceived: this.closeWindow}
+ );
+
+ // In case the room was not used (no one was here), we
+ // bypass the feedback form.
+ this.closeWindow();
+ return null;
}
default: {
return (
diff --git a/browser/components/loop/content/js/roomViews.jsx b/browser/components/loop/content/js/roomViews.jsx
index 7d23fbe62819..8d4e1ddec230 100644
--- a/browser/components/loop/content/js/roomViews.jsx
+++ b/browser/components/loop/content/js/roomViews.jsx
@@ -300,10 +300,16 @@ loop.roomViews = (function(mozL10n) {
/>;
}
case ROOM_STATES.ENDED: {
- return ;
+ if (this.state.used)
+ return ;
+
+ // In case the room was not used (no one was here), we
+ // bypass the feedback form.
+ this.closeWindow();
+ return null;
}
default: {
return (
diff --git a/browser/components/loop/content/shared/js/activeRoomStore.js b/browser/components/loop/content/shared/js/activeRoomStore.js
index 05713606a4cd..cdcc5f88cfcd 100644
--- a/browser/components/loop/content/shared/js/activeRoomStore.js
+++ b/browser/components/loop/content/shared/js/activeRoomStore.js
@@ -67,7 +67,12 @@ loop.store.ActiveRoomStore = (function() {
roomState: ROOM_STATES.INIT,
audioMuted: false,
videoMuted: false,
- failureReason: undefined
+ failureReason: undefined,
+ // Tracks if the room has been used during this
+ // session. 'Used' means at least one call has been placed
+ // with it. Entering and leaving the room without seeing
+ // anyone is not considered as 'used'
+ used: false
};
},
@@ -361,7 +366,10 @@ loop.store.ActiveRoomStore = (function() {
* Handles recording when a remote peer has connected to the servers.
*/
remotePeerConnected: function() {
- this.setStoreState({roomState: ROOM_STATES.HAS_PARTICIPANTS});
+ this.setStoreState({
+ roomState: ROOM_STATES.HAS_PARTICIPANTS,
+ used: true
+ });
// We've connected with a third-party, therefore stop displaying the ToS etc.
this._mozLoop.setLoopPref("seenToS", "seen");
diff --git a/browser/components/loop/standalone/content/js/standaloneRoomViews.js b/browser/components/loop/standalone/content/js/standaloneRoomViews.js
index 27e8441d9691..fdfa50c66f0b 100644
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.js
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.js
@@ -115,14 +115,20 @@ loop.standaloneRoomViews = (function(mozL10n) {
);
}
case ROOM_STATES.ENDED: {
- return (
- React.DOM.div({className: "ended-conversation"},
- sharedViews.FeedbackView({
- feedbackStore: this.props.feedbackStore,
- onAfterFeedbackReceived: this.onFeedbackSent}
+ if (this.props.roomUsed)
+ return (
+ React.DOM.div({className: "ended-conversation"},
+ sharedViews.FeedbackView({
+ feedbackStore: this.props.feedbackStore,
+ onAfterFeedbackReceived: this.onFeedbackSent}
+ )
)
- )
- );
+ );
+
+ // In case the room was not used (no one was here), we
+ // bypass the feedback form.
+ this.onFeedbackSent();
+ return null;
}
case ROOM_STATES.FAILED: {
return (
@@ -362,7 +368,8 @@ loop.standaloneRoomViews = (function(mozL10n) {
joinRoom: this.joinRoom,
helper: this.props.helper,
activeRoomStore: this.props.activeRoomStore,
- feedbackStore: this.props.feedbackStore}),
+ feedbackStore: this.props.feedbackStore,
+ roomUsed: this.state.used}),
React.DOM.div({className: "video-layout-wrapper"},
React.DOM.div({className: "conversation room-conversation"},
React.DOM.h2({className: "room-name"}, this.state.roomName),
diff --git a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
index f4f93c86a26d..894305a7ba5b 100644
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
@@ -115,14 +115,20 @@ loop.standaloneRoomViews = (function(mozL10n) {
);
}
case ROOM_STATES.ENDED: {
- return (
-
-
-
- );
+ if (this.props.roomUsed)
+ return (
+
+
+
+ );
+
+ // In case the room was not used (no one was here), we
+ // bypass the feedback form.
+ this.onFeedbackSent();
+ return null;
}
case ROOM_STATES.FAILED: {
return (
@@ -362,7 +368,8 @@ loop.standaloneRoomViews = (function(mozL10n) {
joinRoom={this.joinRoom}
helper={this.props.helper}
activeRoomStore={this.props.activeRoomStore}
- feedbackStore={this.props.feedbackStore} />
+ feedbackStore={this.props.feedbackStore}
+ roomUsed={this.state.used} />
{this.state.roomName}
diff --git a/browser/components/loop/test/desktop-local/roomViews_test.js b/browser/components/loop/test/desktop-local/roomViews_test.js
index 23c6c2dd72e9..f69c429340ab 100644
--- a/browser/components/loop/test/desktop-local/roomViews_test.js
+++ b/browser/components/loop/test/desktop-local/roomViews_test.js
@@ -64,6 +64,7 @@ describe("loop.roomViews", function () {
audioMuted: false,
videoMuted: false,
failureReason: undefined,
+ used: false,
foo: "bar"
});
});
@@ -355,13 +356,28 @@ describe("loop.roomViews", function () {
it("should render the FeedbackView if roomState is `ENDED`",
function() {
- activeRoomStore.setStoreState({roomState: ROOM_STATES.ENDED});
+ activeRoomStore.setStoreState({
+ roomState: ROOM_STATES.ENDED,
+ used: true
+ });
view = mountTestComponent();
TestUtils.findRenderedComponentWithType(view,
loop.shared.views.FeedbackView);
});
+
+ it("should NOT render the FeedbackView if the room has not been used",
+ function() {
+ activeRoomStore.setStoreState({
+ roomState: ROOM_STATES.ENDED,
+ used: false
+ });
+
+ view = mountTestComponent();
+
+ expect(view.getDOMNode()).eql(null);
+ });
});
describe("Mute", function() {
diff --git a/browser/components/loop/test/standalone/standaloneRoomViews_test.js b/browser/components/loop/test/standalone/standaloneRoomViews_test.js
index 3d40c90b7819..b9e0a2901f51 100644
--- a/browser/components/loop/test/standalone/standaloneRoomViews_test.js
+++ b/browser/components/loop/test/standalone/standaloneRoomViews_test.js
@@ -300,7 +300,10 @@ describe("loop.standaloneRoomViews", function() {
describe("Feedback", function() {
beforeEach(function() {
- activeRoomStore.setStoreState({roomState: ROOM_STATES.ENDED});
+ activeRoomStore.setStoreState({
+ roomState: ROOM_STATES.ENDED,
+ used: true
+ });
});
it("should display a feedback form when the user leaves the room",
@@ -318,6 +321,13 @@ describe("loop.standaloneRoomViews", function() {
sinon.assert.calledOnce(dispatch);
sinon.assert.calledWithExactly(dispatch, new sharedActions.FeedbackComplete());
});
+
+ it("should NOT display a feedback form if the room has not been used",
+ function() {
+ activeRoomStore.setStoreState({used: false});
+ expect(view.getDOMNode().querySelector(".faces")).eql(null);
+ });
+
});
describe("Mute", function() {
diff --git a/browser/components/preferences/in-content/search.xul b/browser/components/preferences/in-content/search.xul
index f733b086c1ed..e13c22c7ea21 100644
--- a/browser/components/preferences/in-content/search.xul
+++ b/browser/components/preferences/in-content/search.xul
@@ -15,6 +15,8 @@
+
+
-
+
diff --git a/browser/components/search/content/search.xml b/browser/components/search/content/search.xml
index d1d4b5892dd4..78388bbcbf55 100644
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -645,6 +645,8 @@
this.destroy();
]]>
+ false
+
-
+
+
-
+
+
+
+
diff --git a/browser/components/search/test/browser.ini b/browser/components/search/test/browser.ini
index e3a34afb3359..e63669e29870 100644
--- a/browser/components/search/test/browser.ini
+++ b/browser/components/search/test/browser.ini
@@ -37,3 +37,5 @@ skip-if = e10s # Bug ?????? - Test uses load event and checks event.target.
skip-if = e10s # Bug ?????? - some issue with progress listeners [JavaScript Error: "req.originalURI is null" {file: "chrome://mochitests/content/browser/browser/components/search/test/browser_bing_behavior.js" line: 127}]
[browser_abouthome_behavior.js]
skip-if = e10s || true # Bug ??????, Bug 1100301 - leaks windows until shutdown when --run-by-dir
+[browser_searchbar_openpopup.js]
+skip-if = os == "linux" || e10s # Linux has different focus behaviours and e10s seems to have timing issues.
diff --git a/browser/components/search/test/browser_searchbar_openpopup.js b/browser/components/search/test/browser_searchbar_openpopup.js
new file mode 100644
index 000000000000..06fa3f773a3b
--- /dev/null
+++ b/browser/components/search/test/browser_searchbar_openpopup.js
@@ -0,0 +1,297 @@
+// Tests that the suggestion popup appears at the right times in response to
+// focus and clicks.
+
+const searchbar = document.getElementById("searchbar");
+const searchIcon = document.getAnonymousElementByAttribute(searchbar, "anonid", "searchbar-search-button");
+const textbox = searchbar._textbox;
+const searchPopup = document.getElementById("PopupSearchAutoComplete");
+
+function promiseNewEngine(basename) {
+ return new Promise((resolve, reject) => {
+ info("Waiting for engine to be added: " + basename);
+ Services.search.init({
+ onInitComplete: function() {
+ let url = getRootDirectory(gTestPath) + basename;
+ let current = Services.search.currentEngine;
+ Services.search.addEngine(url, Ci.nsISearchEngine.TYPE_MOZSEARCH, "", false, {
+ onSuccess: function (engine) {
+ info("Search engine added: " + basename);
+ Services.search.currentEngine = engine;
+ registerCleanupFunction(() => {
+ Services.search.currentEngine = current;
+ Services.search.removeEngine(engine);
+ info("Search engine removed: " + basename);
+ });
+ resolve(engine);
+ },
+ onError: function (errCode) {
+ ok(false, "addEngine failed with error code " + errCode);
+ reject();
+ }
+ });
+ }
+ });
+ });
+}
+
+add_task(function* init() {
+ yield promiseNewEngine("testEngine.xml");
+});
+
+// Adds a task that shouldn't show the search suggestions popup.
+function add_no_popup_task(task) {
+ add_task(function*() {
+ let sawPopup = false;
+ function listener() {
+ sawPopup = true;
+ }
+
+ info("Entering test " + task.name);
+ searchPopup.addEventListener("popupshowing", listener, false);
+ yield Task.spawn(task);
+ searchPopup.removeEventListener("popupshowing", listener, false);
+ ok(!sawPopup, "Shouldn't have seen the suggestions popup");
+ info("Leaving test " + task.name);
+ });
+}
+
+// Simulates the full set of events for a context click
+function context_click(target) {
+ for (let event of ["mousedown", "contextmenu", "mouseup"])
+ EventUtils.synthesizeMouseAtCenter(target, { type: event, button: 2 });
+}
+
+// Right clicking the icon should not open the popup.
+add_no_popup_task(function* open_icon_context() {
+ gURLBar.focus();
+ let toolbarPopup = document.getElementById("toolbar-context-menu");
+
+ let promise = promiseEvent(toolbarPopup, "popupshown");
+ context_click(searchIcon);
+ yield promise;
+
+ promise = promiseEvent(toolbarPopup, "popuphidden");
+ toolbarPopup.hidePopup();
+ yield promise;
+});
+
+// With no text in the search box left clicking the icon should open the popup.
+add_task(function* open_empty() {
+ gURLBar.focus();
+
+ let promise = promiseEvent(searchPopup, "popupshown");
+ info("Clicking icon");
+ EventUtils.synthesizeMouseAtCenter(searchIcon, {});
+ yield promise;
+ is(searchPopup.getAttribute("showonlysettings"), "true", "Should only show the settings");
+
+ promise = promiseEvent(searchPopup, "popuphidden");
+ info("Hiding popup");
+ searchPopup.hidePopup();
+ yield promise;
+});
+
+// With no text in the search box left clicking it should not open the popup.
+add_no_popup_task(function* click_doesnt_open_popup() {
+ gURLBar.focus();
+
+ EventUtils.synthesizeMouseAtCenter(textbox, {});
+ is(Services.focus.focusedElement, textbox.inputField, "Should have focused the search bar");
+ is(textbox.selectionStart, 0, "Should have selected all of the text");
+ is(textbox.selectionEnd, 0, "Should have selected all of the text");
+});
+
+// Left clicking in a non-empty search box when unfocused should focus it and open the popup.
+add_task(function* click_opens_popup() {
+ gURLBar.focus();
+ textbox.value = "foo";
+
+ let promise = promiseEvent(searchPopup, "popupshown");
+ EventUtils.synthesizeMouseAtCenter(textbox, {});
+ yield promise;
+ isnot(searchPopup.getAttribute("showonlysettings"), "true", "Should show the full popup");
+
+ is(Services.focus.focusedElement, textbox.inputField, "Should have focused the search bar");
+ is(textbox.selectionStart, 0, "Should have selected all of the text");
+ is(textbox.selectionEnd, 3, "Should have selected all of the text");
+
+ promise = promiseEvent(searchPopup, "popuphidden");
+ searchPopup.hidePopup();
+ yield promise;
+
+ textbox.value = "";
+});
+
+// Right clicking in a non-empty search box when unfocused should open the edit context menu.
+add_no_popup_task(function* right_click_doesnt_open_popup() {
+ gURLBar.focus();
+ textbox.value = "foo";
+
+ let contextPopup = document.getAnonymousElementByAttribute(textbox.inputField.parentNode, "anonid", "input-box-contextmenu");
+ let promise = promiseEvent(contextPopup, "popupshown");
+ context_click(textbox);
+ yield promise;
+
+ is(Services.focus.focusedElement, textbox.inputField, "Should have focused the search bar");
+ is(textbox.selectionStart, 0, "Should have selected all of the text");
+ is(textbox.selectionEnd, 3, "Should have selected all of the text");
+
+ promise = promiseEvent(contextPopup, "popuphidden");
+ contextPopup.hidePopup();
+ yield promise;
+
+ textbox.value = "";
+});
+
+// Moving focus away from the search box should close the popup
+add_task(function* focus_change_closes_popup() {
+ gURLBar.focus();
+ textbox.value = "foo";
+
+ let promise = promiseEvent(searchPopup, "popupshown");
+ EventUtils.synthesizeMouseAtCenter(textbox, {});
+ yield promise;
+ isnot(searchPopup.getAttribute("showonlysettings"), "true", "Should show the full popup");
+
+ is(Services.focus.focusedElement, textbox.inputField, "Should have focused the search bar");
+ is(textbox.selectionStart, 0, "Should have selected all of the text");
+ is(textbox.selectionEnd, 3, "Should have selected all of the text");
+
+ promise = promiseEvent(searchPopup, "popuphidden");
+ let promise2 = promiseEvent(searchbar, "blur");
+ EventUtils.synthesizeKey("VK_TAB", { shiftKey: true });
+ yield promise;
+ yield promise2;
+
+ textbox.value = "";
+});
+
+// Pressing escape should close the popup.
+add_task(function* escape_closes_popup() {
+ gURLBar.focus();
+ textbox.value = "foo";
+
+ let promise = promiseEvent(searchPopup, "popupshown");
+ EventUtils.synthesizeMouseAtCenter(textbox, {});
+ yield promise;
+ isnot(searchPopup.getAttribute("showonlysettings"), "true", "Should show the full popup");
+
+ is(Services.focus.focusedElement, textbox.inputField, "Should have focused the search bar");
+ is(textbox.selectionStart, 0, "Should have selected all of the text");
+ is(textbox.selectionEnd, 3, "Should have selected all of the text");
+
+ promise = promiseEvent(searchPopup, "popuphidden");
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ yield promise;
+
+ textbox.value = "";
+});
+
+// Tabbing to the search box should open the popup if it contains text.
+add_task(function* tab_opens_popup() {
+ gURLBar.focus();
+ textbox.value = "foo";
+
+ let promise = promiseEvent(searchPopup, "popupshown");
+ EventUtils.synthesizeKey("VK_TAB", {});
+ yield promise;
+ isnot(searchPopup.getAttribute("showonlysettings"), "true", "Should show the full popup");
+
+ is(Services.focus.focusedElement, textbox.inputField, "Should have focused the search bar");
+ is(textbox.selectionStart, 0, "Should have selected all of the text");
+ is(textbox.selectionEnd, 3, "Should have selected all of the text");
+
+ promise = promiseEvent(searchPopup, "popuphidden");
+ searchPopup.hidePopup();
+ yield promise;
+
+ textbox.value = "";
+});
+
+// Tabbing to the search box should not open the popup if it doesn't contain text.
+add_no_popup_task(function* tab_doesnt_open_popup() {
+ gURLBar.focus();
+ textbox.value = "foo";
+
+ EventUtils.synthesizeKey("VK_TAB", {});
+
+ is(Services.focus.focusedElement, textbox.inputField, "Should have focused the search bar");
+ is(textbox.selectionStart, 0, "Should have selected all of the text");
+ is(textbox.selectionEnd, 3, "Should have selected all of the text");
+
+ textbox.value = "";
+});
+
+// Switching back to the window when the search box has focus from mouse should not open the popup.
+add_task(function* refocus_window_doesnt_open_popup_mouse() {
+ gURLBar.focus();
+ textbox.value = "foo";
+
+ let promise = promiseEvent(searchPopup, "popupshown");
+ EventUtils.synthesizeMouseAtCenter(searchbar, {});
+ yield promise;
+ isnot(searchPopup.getAttribute("showonlysettings"), "true", "Should show the full popup");
+
+ is(Services.focus.focusedElement, textbox.inputField, "Should have focused the search bar");
+ is(textbox.selectionStart, 0, "Should have selected all of the text");
+ is(textbox.selectionEnd, 3, "Should have selected all of the text");
+
+ promise = promiseEvent(searchPopup, "popuphidden");
+ let newWin = OpenBrowserWindow();
+ yield new Promise(resolve => waitForFocus(resolve, newWin));
+ yield promise;
+
+ function listener() {
+ ok(false, "Should not have shown the popup.");
+ }
+ searchPopup.addEventListener("popupshowing", listener, false);
+
+ promise = promiseEvent(searchbar, "focus");
+ newWin.close();
+ yield promise;
+
+ // Wait a few ticks to allow any focus handlers to show the popup if they are going to.
+ yield new Promise(resolve => executeSoon(resolve));
+ yield new Promise(resolve => executeSoon(resolve));
+ yield new Promise(resolve => executeSoon(resolve));
+
+ searchPopup.removeEventListener("popupshowing", listener, false);
+ textbox.value = "";
+});
+
+// Switching back to the window when the search box has focus from keyboard should not open the popup.
+add_task(function* refocus_window_doesnt_open_popup_keyboard() {
+ gURLBar.focus();
+ textbox.value = "foo";
+
+ let promise = promiseEvent(searchPopup, "popupshown");
+ EventUtils.synthesizeKey("VK_TAB", {});
+ yield promise;
+ isnot(searchPopup.getAttribute("showonlysettings"), "true", "Should show the full popup");
+
+ is(Services.focus.focusedElement, textbox.inputField, "Should have focused the search bar");
+ is(textbox.selectionStart, 0, "Should have selected all of the text");
+ is(textbox.selectionEnd, 3, "Should have selected all of the text");
+
+ promise = promiseEvent(searchPopup, "popuphidden");
+ let newWin = OpenBrowserWindow();
+ yield new Promise(resolve => waitForFocus(resolve, newWin));
+ yield promise;
+
+ function listener() {
+ ok(false, "Should not have shown the popup.");
+ }
+ searchPopup.addEventListener("popupshowing", listener, false);
+
+ promise = promiseEvent(searchbar, "focus");
+ newWin.close();
+ yield promise;
+
+ // Wait a few ticks to allow any focus handlers to show the popup if they are going to.
+ yield new Promise(resolve => executeSoon(resolve));
+ yield new Promise(resolve => executeSoon(resolve));
+ yield new Promise(resolve => executeSoon(resolve));
+
+ searchPopup.removeEventListener("popupshowing", listener, false);
+ textbox.value = "";
+});
diff --git a/browser/components/search/test/head.js b/browser/components/search/test/head.js
index c2b76460af25..709f0a0f800b 100644
--- a/browser/components/search/test/head.js
+++ b/browser/components/search/test/head.js
@@ -91,7 +91,7 @@ function waitForPopupShown(aPopupId, aCallback) {
registerCleanupFunction(removePopupShownListener);
}
-function* promiseEvent(aTarget, aEventName, aPreventDefault) {
+function promiseEvent(aTarget, aEventName, aPreventDefault) {
let deferred = Promise.defer();
aTarget.addEventListener(aEventName, function onEvent(aEvent) {
aTarget.removeEventListener(aEventName, onEvent, true);
diff --git a/browser/devtools/framework/sidebar.js b/browser/devtools/framework/sidebar.js
index 8def92c101a6..15314cf2faed 100644
--- a/browser/devtools/framework/sidebar.js
+++ b/browser/devtools/framework/sidebar.js
@@ -7,6 +7,7 @@
const {Cu} = require("chrome");
Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
var {Promise: promise} = require("resource://gre/modules/Promise.jsm");
var EventEmitter = require("devtools/toolkit/event-emitter");
@@ -118,12 +119,17 @@ ToolSidebar.prototype = {
/**
* Remove an existing tab.
*/
- removeTab: function(id) {
+ removeTab: Task.async(function*(id) {
let tab = this._tabbox.tabs.querySelector("tab#sidebar-tab-" + id);
if (!tab) {
return;
}
+ let win = this.getWindowForTab(id);
+ if ("destroy" in win) {
+ yield win.destroy();
+ }
+
tab.remove();
let panel = this.getTab(id);
@@ -134,7 +140,7 @@ ToolSidebar.prototype = {
this._tabs.delete(id);
this.emit("tab-unregistered", id);
- },
+ }),
/**
* Select a specific tab.
@@ -241,7 +247,7 @@ ToolSidebar.prototype = {
/**
* Clean-up.
*/
- destroy: function ToolSidebar_destroy() {
+ destroy: Task.async(function*() {
if (this._destroyed) {
return promise.resolve(null);
}
@@ -252,7 +258,12 @@ ToolSidebar.prototype = {
this._tabbox.tabpanels.removeEventListener("select", this, true);
while (this._tabbox.tabpanels.hasChildNodes()) {
- this._tabbox.tabpanels.removeChild(this._tabbox.tabpanels.firstChild);
+ let panel = this._tabbox.tabpanels.firstChild;
+ let win = panel.firstChild.contentWindow;
+ if ("destroy" in win) {
+ yield win.destroy();
+ }
+ panel.remove();
}
while (this._tabbox.tabs.hasChildNodes()) {
@@ -271,5 +282,5 @@ ToolSidebar.prototype = {
this._toolPanel = null;
return promise.resolve(null);
- },
+ }),
}
diff --git a/browser/devtools/framework/toolbox.js b/browser/devtools/framework/toolbox.js
index 2f98ee3a40cc..0a1e6f3951f6 100644
--- a/browser/devtools/framework/toolbox.js
+++ b/browser/devtools/framework/toolbox.js
@@ -1578,9 +1578,12 @@ Toolbox.prototype = {
* @return {promise} to be resolved when the host is destroyed.
*/
destroyHost: function() {
- this.doc.removeEventListener("keypress",
- this._splitConsoleOnKeypress, false);
- this.doc.removeEventListener("focus", this._onFocus, true);
+ // The host iframe's contentDocument may already be gone.
+ if (this.doc) {
+ this.doc.removeEventListener("keypress",
+ this._splitConsoleOnKeypress, false);
+ this.doc.removeEventListener("focus", this._onFocus, true);
+ }
return this._host.destroy();
},
@@ -1645,18 +1648,17 @@ Toolbox.prototype = {
// We need to grab a reference to win before this._host is destroyed.
let win = this.frame.ownerGlobal;
- // Remove the host UI
- outstanding.push(this.destroyHost());
-
if (this._requisition) {
this._requisition.destroy();
}
this._telemetry.toolClosed("toolbox");
this._telemetry.destroy();
- // Finish all outstanding tasks (successfully or not) before destroying the
+ // Finish all outstanding tasks (which means finish destroying panels and
+ // then destroying the host, successfully or not) before destroying the
// target.
- this._destroyer = promise.all(outstanding).then(null, console.error).then(() => {
+ this._destroyer = promise.all(outstanding)
+ .then(() => this.destroyHost()).then(null, console.error).then(() => {
// Targets need to be notified that the toolbox is being torn down.
// This is done after other destruction tasks since it may tear down
// fronts and the debugger transport which earlier destroy methods may
diff --git a/browser/devtools/inspector/inspector-panel.js b/browser/devtools/inspector/inspector-panel.js
index 17def2919814..c2f3af807de4 100644
--- a/browser/devtools/inspector/inspector-panel.js
+++ b/browser/devtools/inspector/inspector-panel.js
@@ -549,7 +549,7 @@ InspectorPanel.prototype = {
this._toolbox.off("select", this.updateDebuggerPausedWarning);
this.sidebar.off("select", this._setDefaultSidebar);
- this.sidebar.destroy();
+ let sidebarDestroyer = this.sidebar.destroy();
this.sidebar = null;
this.nodemenu.removeEventListener("popupshowing", this._setupNodeMenu, true);
@@ -561,7 +561,7 @@ InspectorPanel.prototype = {
this.selection.off("before-new-node", this.onBeforeNewSelection);
this.selection.off("before-new-node-front", this.onBeforeNewSelection);
this.selection.off("detached-front", this.onDetached);
- this._panelDestroyer = this._destroyMarkup();
+ let markupDestroyer = this._destroyMarkup();
this.panelWin.inspector = null;
this.target = null;
this.panelDoc = null;
@@ -572,6 +572,11 @@ InspectorPanel.prototype = {
this.nodemenu = null;
this._toolbox = null;
+ this._panelDestroyer = promise.all([
+ sidebarDestroyer,
+ markupDestroyer
+ ]);
+
return this._panelDestroyer;
},
diff --git a/browser/devtools/inspector/test/browser_inspector_breadcrumbs_highlight_hover.js b/browser/devtools/inspector/test/browser_inspector_breadcrumbs_highlight_hover.js
index f4ba5735e954..255aa9d35fef 100644
--- a/browser/devtools/inspector/test/browser_inspector_breadcrumbs_highlight_hover.js
+++ b/browser/devtools/inspector/test/browser_inspector_breadcrumbs_highlight_hover.js
@@ -35,6 +35,4 @@ add_task(function*() {
highlightedNode = yield getHighlitNode(toolbox);
is(highlightedNode, getNode("span"), "The highlighter highlights the right node");
-
- gBrowser.removeCurrentTab();
});
diff --git a/browser/devtools/inspector/test/browser_inspector_highlight_after_transition.js b/browser/devtools/inspector/test/browser_inspector_highlight_after_transition.js
index ef495804b1a2..49c18b2580c8 100644
--- a/browser/devtools/inspector/test/browser_inspector_highlight_after_transition.js
+++ b/browser/devtools/inspector/test/browser_inspector_highlight_after_transition.js
@@ -17,8 +17,6 @@ add_task(function*() {
let {inspector} = yield openInspector();
yield checkDivHeight(inspector);
-
- gBrowser.removeCurrentTab();
});
function* checkDivHeight(inspector) {
diff --git a/browser/devtools/inspector/test/browser_inspector_highlighter-01.js b/browser/devtools/inspector/test/browser_inspector_highlighter-01.js
index 44965a7e0b9e..90f4d0cfa158 100644
--- a/browser/devtools/inspector/test/browser_inspector_highlighter-01.js
+++ b/browser/devtools/inspector/test/browser_inspector_highlighter-01.js
@@ -28,6 +28,4 @@ add_task(function*() {
let node = yield getHighlitNode(toolbox);
is(node, getNode("h1"), "The highlighter highlights the right node");
-
- gBrowser.removeCurrentTab();
});
diff --git a/browser/devtools/inspector/test/browser_inspector_highlighter-04.js b/browser/devtools/inspector/test/browser_inspector_highlighter-04.js
index 0b3e5f7b192f..af0a59e40863 100644
--- a/browser/devtools/inspector/test/browser_inspector_highlighter-04.js
+++ b/browser/devtools/inspector/test/browser_inspector_highlighter-04.js
@@ -44,6 +44,4 @@ add_task(function*() {
info("Hide the box-model highlighter");
yield toolbox.highlighter.hideBoxModel();
-
- gBrowser.removeCurrentTab();
});
diff --git a/browser/devtools/inspector/test/browser_inspector_highlighter-by-type.js b/browser/devtools/inspector/test/browser_inspector_highlighter-by-type.js
index 4a58a51c6769..62a29e851632 100644
--- a/browser/devtools/inspector/test/browser_inspector_highlighter-by-type.js
+++ b/browser/devtools/inspector/test/browser_inspector_highlighter-by-type.js
@@ -16,8 +16,6 @@ add_task(function*() {
yield manyInstancesOfCustomHighlighters(inspector);
yield showHideMethodsAreAvailable(inspector);
yield unknownHighlighterTypeShouldntBeAccepted(inspector);
-
- gBrowser.removeCurrentTab();
});
function* onlyOneInstanceOfMainHighlighter({inspector}) {
diff --git a/browser/devtools/inspector/test/browser_inspector_highlighter-csstransform_01.js b/browser/devtools/inspector/test/browser_inspector_highlighter-csstransform_01.js
index b65943439824..835e9c0183be 100644
--- a/browser/devtools/inspector/test/browser_inspector_highlighter-csstransform_01.js
+++ b/browser/devtools/inspector/test/browser_inspector_highlighter-csstransform_01.js
@@ -26,8 +26,6 @@ add_task(function*() {
yield linesLinkThePolygons(highlighter, inspector);
yield highlighter.finalize();
-
- gBrowser.removeCurrentTab();
});
function* isHiddenByDefault(highlighterFront, inspector) {
diff --git a/browser/devtools/inspector/test/browser_inspector_highlighter-csstransform_02.js b/browser/devtools/inspector/test/browser_inspector_highlighter-csstransform_02.js
index a3f58e03f2b1..4c4e05419391 100644
--- a/browser/devtools/inspector/test/browser_inspector_highlighter-csstransform_02.js
+++ b/browser/devtools/inspector/test/browser_inspector_highlighter-csstransform_02.js
@@ -53,8 +53,6 @@ add_task(function*() {
info("Hiding the transform highlighter");
yield highlighter.hide();
yield highlighter.finalize();
-
- gBrowser.removeCurrentTab();
});
function* getAttribute(nodeID, name, {actorID}) {
diff --git a/browser/devtools/inspector/test/browser_inspector_highlighter-options.js b/browser/devtools/inspector/test/browser_inspector_highlighter-options.js
index 1195e03344e9..dbb8be654994 100644
--- a/browser/devtools/inspector/test/browser_inspector_highlighter-options.js
+++ b/browser/devtools/inspector/test/browser_inspector_highlighter-options.js
@@ -149,8 +149,6 @@ add_task(function*() {
info("Hide the box-model highlighter");
yield toolbox.highlighter.hideBoxModel();
}
-
- gBrowser.removeCurrentTab();
});
function* getAttribute(nodeID, name, toolbox) {
diff --git a/browser/devtools/inspector/test/browser_inspector_highlighter-rect_01.js b/browser/devtools/inspector/test/browser_inspector_highlighter-rect_01.js
index ae5eff336232..ccb74406a3b9 100644
--- a/browser/devtools/inspector/test/browser_inspector_highlighter-rect_01.js
+++ b/browser/devtools/inspector/test/browser_inspector_highlighter-rect_01.js
@@ -105,7 +105,6 @@ add_task(function*() {
yield highlighter.hide();
yield highlighter.finalize();
- gBrowser.removeCurrentTab();
});
function* getAttribute(highlighter, name) {
diff --git a/browser/devtools/inspector/test/browser_inspector_highlighter-rect_02.js b/browser/devtools/inspector/test/browser_inspector_highlighter-rect_02.js
index 01b79019e812..c5eab8faf1be 100644
--- a/browser/devtools/inspector/test/browser_inspector_highlighter-rect_02.js
+++ b/browser/devtools/inspector/test/browser_inspector_highlighter-rect_02.js
@@ -33,7 +33,6 @@ add_task(function*() {
yield highlighter.hide();
yield highlighter.finalize();
- gBrowser.removeCurrentTab();
});
function* getAttribute(highlighter, name) {
diff --git a/browser/devtools/inspector/test/browser_inspector_highlighter-selector_01.js b/browser/devtools/inspector/test/browser_inspector_highlighter-selector_01.js
index 39d8ed47acb6..30640dffd339 100644
--- a/browser/devtools/inspector/test/browser_inspector_highlighter-selector_01.js
+++ b/browser/devtools/inspector/test/browser_inspector_highlighter-selector_01.js
@@ -60,5 +60,4 @@ add_task(function*() {
}
yield highlighter.finalize();
- gBrowser.removeCurrentTab();
});
diff --git a/browser/devtools/inspector/test/browser_inspector_highlighter-selector_02.js b/browser/devtools/inspector/test/browser_inspector_highlighter-selector_02.js
index 22e7600df52c..be8d4c05e615 100644
--- a/browser/devtools/inspector/test/browser_inspector_highlighter-selector_02.js
+++ b/browser/devtools/inspector/test/browser_inspector_highlighter-selector_02.js
@@ -58,5 +58,4 @@ add_task(function*() {
}
yield highlighter.finalize();
- gBrowser.removeCurrentTab();
});
diff --git a/browser/devtools/inspector/test/browser_inspector_highlighter-zoom.js b/browser/devtools/inspector/test/browser_inspector_highlighter-zoom.js
index 61ce279b94ca..f5a4fcca2c7f 100644
--- a/browser/devtools/inspector/test/browser_inspector_highlighter-zoom.js
+++ b/browser/devtools/inspector/test/browser_inspector_highlighter-zoom.js
@@ -47,8 +47,6 @@ add_task(function*() {
let style = yield getRootNodeStyle(toolbox);
is(style, expected, "The style attribute of the root element is correct");
}
-
- gBrowser.removeCurrentTab();
});
function* hoverElement(selector, inspector) {
diff --git a/browser/devtools/inspector/test/browser_inspector_infobar_01.js b/browser/devtools/inspector/test/browser_inspector_infobar_01.js
index 3df9aac0bccf..e25a413167b0 100644
--- a/browser/devtools/inspector/test/browser_inspector_infobar_01.js
+++ b/browser/devtools/inspector/test/browser_inspector_infobar_01.js
@@ -50,8 +50,6 @@ add_task(function*() {
for (let currTest of testData) {
yield testPosition(currTest, inspector);
}
-
- gBrowser.removeCurrentTab();
});
function* testPosition(test, inspector) {
diff --git a/browser/devtools/inspector/test/browser_inspector_select-docshell.js b/browser/devtools/inspector/test/browser_inspector_select-docshell.js
index c384ed51bbce..58ca50943e3f 100644
--- a/browser/devtools/inspector/test/browser_inspector_select-docshell.js
+++ b/browser/devtools/inspector/test/browser_inspector_select-docshell.js
@@ -64,7 +64,6 @@ add_task(function*() {
yield newRoot;
Services.prefs.clearUserPref("devtools.command-button-frames.enabled");
- gBrowser.removeCurrentTab();
});
function assertMarkupViewIsLoaded(inspector) {
diff --git a/browser/devtools/inspector/test/head.js b/browser/devtools/inspector/test/head.js
index 057f931d485f..7359bd4d7dcf 100644
--- a/browser/devtools/inspector/test/head.js
+++ b/browser/devtools/inspector/test/head.js
@@ -32,15 +32,11 @@ let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers.js", this);
gDevTools.testing = true;
-SimpleTest.registerCleanupFunction(() => {
+registerCleanupFunction(() => {
gDevTools.testing = false;
});
-SimpleTest.registerCleanupFunction(() => {
- console.error("Here we are\n");
- let {DebuggerServer} = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
- console.error("DebuggerServer open connections: " + Object.getOwnPropertyNames(DebuggerServer._connections).length);
-
+registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.dump.emit");
Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
});
@@ -58,7 +54,6 @@ registerCleanupFunction(function*() {
while (gBrowser.tabs.length > 1) {
gBrowser.removeCurrentTab();
}
-
});
/**
diff --git a/browser/devtools/netmonitor/netmonitor-controller.js b/browser/devtools/netmonitor/netmonitor-controller.js
index ed84b8da5f00..aaaba31deb11 100644
--- a/browser/devtools/netmonitor/netmonitor-controller.js
+++ b/browser/devtools/netmonitor/netmonitor-controller.js
@@ -391,6 +391,16 @@ let NetMonitorController = {
!this._target.isApp);
},
+ /**
+ * Getter that tells if the server includes the transferred (compressed /
+ * encoded) response size.
+ * @type boolean
+ */
+ get supportsTransferredResponseSize() {
+ return this.webConsoleClient &&
+ this.webConsoleClient.traits.transferredResponseSize;
+ },
+
/**
* Getter that tells if the server can do network performance statistics.
* @type boolean
@@ -590,6 +600,7 @@ NetworkEventsHandler.prototype = {
case "responseContent":
NetMonitorView.RequestsMenu.updateRequest(aPacket.from, {
contentSize: aPacket.contentSize,
+ transferredSize: aPacket.transferredSize,
mimeType: aPacket.mimeType
});
this.webConsoleClient.getResponseContent(actor, this._onResponseContent);
diff --git a/browser/devtools/netmonitor/netmonitor-view.js b/browser/devtools/netmonitor/netmonitor-view.js
index 55b4c24624ad..c3c3f7dc4884 100644
--- a/browser/devtools/netmonitor/netmonitor-view.js
+++ b/browser/devtools/netmonitor/netmonitor-view.js
@@ -416,6 +416,11 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
$("#requests-menu-network-summary-button").hidden = true;
$("#requests-menu-network-summary-label").hidden = true;
}
+
+ if (!NetMonitorController.supportsTransferredResponseSize) {
+ $("#requests-menu-transferred-header-box").hidden = true;
+ $("#requests-menu-item-template .requests-menu-transferred").hidden = true;
+ }
},
/**
@@ -799,8 +804,8 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
* Sorts all network requests in this container by a specified detail.
*
* @param string aType
- * Either "status", "method", "file", "domain", "type", "size" or
- * "waterfall".
+ * Either "status", "method", "file", "domain", "type", "transferred",
+ * "size" or "waterfall".
*/
sortBy: function(aType = "waterfall") {
let target = $("#requests-menu-" + aType + "-button");
@@ -861,6 +866,13 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
this.sortContents((a, b) => !this._byType(a, b));
}
break;
+ case "transferred":
+ if (direction == "ascending") {
+ this.sortContents(this._byTransferred);
+ } else {
+ this.sortContents((a, b) => !this._byTransferred(a, b));
+ }
+ break;
case "size":
if (direction == "ascending") {
this.sortContents(this._bySize);
@@ -993,8 +1005,13 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
: firstType > secondType;
},
- _bySize: function({ attachment: first }, { attachment: second })
- first.contentSize > second.contentSize,
+ _byTransferred: function({ attachment: first }, { attachment: second }) {
+ return first.transferredSize > second.transferredSize;
+ },
+
+ _bySize: function({ attachment: first }, { attachment: second }) {
+ return first.contentSize > second.contentSize;
+ },
/**
* Refreshes the status displayed in this container's footer, providing
@@ -1159,6 +1176,10 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
requestItem.attachment.contentSize = value;
this.updateMenuView(requestItem, key, value);
break;
+ case "transferredSize":
+ requestItem.attachment.transferredSize = value;
+ this.updateMenuView(requestItem, key, value);
+ break;
case "mimeType":
requestItem.attachment.mimeType = value;
this.updateMenuView(requestItem, key, value);
@@ -1307,6 +1328,20 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
node.setAttribute("tooltiptext", text);
break;
}
+ case "transferredSize": {
+ let text;
+ if (aValue === null) {
+ text = L10N.getStr("networkMenu.sizeUnavailable");
+ } else {
+ let kb = aValue / 1024;
+ let size = L10N.numberWithDecimals(kb, CONTENT_SIZE_DECIMALS);
+ text = L10N.getFormatStr("networkMenu.sizeKB", size);
+ }
+ let node = $(".requests-menu-transferred", target);
+ node.setAttribute("value", text);
+ node.setAttribute("tooltiptext", text);
+ break;
+ }
case "mimeType": {
let type = this._getAbbreviatedMimeType(aValue);
let node = $(".requests-menu-type", target);
diff --git a/browser/devtools/netmonitor/netmonitor.xul b/browser/devtools/netmonitor/netmonitor.xul
index c16a7a2265a8..0cadbccd8ada 100644
--- a/browser/devtools/netmonitor/netmonitor.xul
+++ b/browser/devtools/netmonitor/netmonitor.xul
@@ -101,6 +101,16 @@
flex="1">
+
+
+
@@ -174,6 +184,8 @@
crop="end"/>
+
{
let requestItem = RequestsMenu.getItemAtIndex(0);
+ is(requestItem.attachment.transferredSize, "12",
+ "The transferredSize attachment has an incorrect value.");
is(requestItem.attachment.contentSize, "12",
"The contentSize attachment has an incorrect value.");
is(requestItem.attachment.mimeType, "text/plain; charset=utf-8",
@@ -164,6 +168,7 @@ function test() {
verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, {
type: "plain",
fullMimeType: "text/plain; charset=utf-8",
+ transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
});
});
@@ -183,6 +188,7 @@ function test() {
verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, {
type: "plain",
fullMimeType: "text/plain; charset=utf-8",
+ transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
});
});
diff --git a/browser/devtools/netmonitor/test/browser_net_sort-01.js b/browser/devtools/netmonitor/test/browser_net_sort-01.js
index 4f919fbdf3c8..ed8f2b8ae3b6 100644
--- a/browser/devtools/netmonitor/test/browser_net_sort-01.js
+++ b/browser/devtools/netmonitor/test/browser_net_sort-01.js
@@ -202,6 +202,7 @@ function test() {
statusText: "Switching Protocols",
type: "plain",
fullMimeType: "text/plain; charset=utf-8",
+ transferred: L10N.getStr("networkMenu.sizeUnavailable"),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0),
time: true
});
@@ -211,6 +212,7 @@ function test() {
statusText: "Created",
type: "plain",
fullMimeType: "text/plain; charset=utf-8",
+ transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
time: true
});
@@ -220,6 +222,7 @@ function test() {
statusText: "See Other",
type: "plain",
fullMimeType: "text/plain; charset=utf-8",
+ transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0),
time: true
});
@@ -229,6 +232,7 @@ function test() {
statusText: "Not Found",
type: "plain",
fullMimeType: "text/plain; charset=utf-8",
+ transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
time: true
});
@@ -238,6 +242,7 @@ function test() {
statusText: "Not Implemented",
type: "plain",
fullMimeType: "text/plain; charset=utf-8",
+ transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
time: true
});
diff --git a/browser/devtools/netmonitor/test/browser_net_sort-02.js b/browser/devtools/netmonitor/test/browser_net_sort-02.js
index ffcdfe13d9eb..7a6becf68e85 100644
--- a/browser/devtools/netmonitor/test/browser_net_sort-02.js
+++ b/browser/devtools/netmonitor/test/browser_net_sort-02.js
@@ -102,6 +102,24 @@ function test() {
testHeaders("type", "ascending");
return testContents([0, 1, 2, 3, 4]);
})
+ .then(() => {
+ info("Testing transferred sort, ascending.");
+ EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-transferred-button"));
+ testHeaders("transferred", "ascending");
+ return testContents([0, 1, 2, 3, 4]);
+ })
+ .then(() => {
+ info("Testing transferred sort, descending.");
+ EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-transferred-button"));
+ testHeaders("transferred", "descending");
+ return testContents([4, 3, 2, 1, 0]);
+ })
+ .then(() => {
+ info("Testing transferred sort, ascending. Checking sort loops correctly.");
+ EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-transferred-button"));
+ testHeaders("transferred", "ascending");
+ return testContents([0, 1, 2, 3, 4]);
+ })
.then(() => {
info("Testing size sort, ascending.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-size-button"));
@@ -199,6 +217,7 @@ function test() {
statusText: "Meh",
type: "1",
fullMimeType: "text/1",
+ transferred: L10N.getStr("networkMenu.sizeUnavailable"),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0),
time: true
});
@@ -209,6 +228,7 @@ function test() {
statusText: "Meh",
type: "2",
fullMimeType: "text/2",
+ transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
time: true
});
@@ -219,6 +239,7 @@ function test() {
statusText: "Meh",
type: "3",
fullMimeType: "text/3",
+ transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
time: true
});
@@ -229,6 +250,7 @@ function test() {
statusText: "Meh",
type: "4",
fullMimeType: "text/4",
+ transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
time: true
});
@@ -239,6 +261,7 @@ function test() {
statusText: "Meh",
type: "5",
fullMimeType: "text/5",
+ transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
time: true
});
diff --git a/browser/devtools/netmonitor/test/browser_net_sort-03.js b/browser/devtools/netmonitor/test/browser_net_sort-03.js
index 0cffd484d5ef..d4c1de33b720 100644
--- a/browser/devtools/netmonitor/test/browser_net_sort-03.js
+++ b/browser/devtools/netmonitor/test/browser_net_sort-03.js
@@ -130,6 +130,7 @@ function test() {
statusText: "Meh",
type: "1",
fullMimeType: "text/1",
+ transferred: L10N.getStr("networkMenu.sizeUnavailable"),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0),
time: true
});
@@ -142,6 +143,7 @@ function test() {
statusText: "Meh",
type: "2",
fullMimeType: "text/2",
+ transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
time: true
});
@@ -154,6 +156,7 @@ function test() {
statusText: "Meh",
type: "3",
fullMimeType: "text/3",
+ transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
time: true
});
@@ -166,6 +169,7 @@ function test() {
statusText: "Meh",
type: "4",
fullMimeType: "text/4",
+ transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
time: true
});
@@ -178,6 +182,7 @@ function test() {
statusText: "Meh",
type: "5",
fullMimeType: "text/5",
+ transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
time: true
});
diff --git a/browser/devtools/netmonitor/test/browser_net_timeline_ticks.js b/browser/devtools/netmonitor/test/browser_net_timeline_ticks.js
index 62fe15188317..c6fa32439e77 100644
--- a/browser/devtools/netmonitor/test/browser_net_timeline_ticks.js
+++ b/browser/devtools/netmonitor/test/browser_net_timeline_ticks.js
@@ -12,6 +12,12 @@ function test() {
let { document, L10N, NetMonitorView } = aMonitor.panelWin;
let { RequestsMenu } = NetMonitorView;
+ // Disable transferred size column support for this test.
+ // Without this, the waterfall only has enough room for one division, which
+ // would remove most of the value of this test.
+ document.querySelector("#requests-menu-transferred-header-box").hidden = true;
+ document.querySelector("#requests-menu-item-template .requests-menu-transferred").hidden = true;
+
RequestsMenu.lazyUpdate = false;
ok(document.querySelector("#requests-menu-waterfall-label"),
diff --git a/browser/devtools/netmonitor/test/head.js b/browser/devtools/netmonitor/test/head.js
index c2f34b151f47..8ae7d1b816ef 100644
--- a/browser/devtools/netmonitor/test/head.js
+++ b/browser/devtools/netmonitor/test/head.js
@@ -264,7 +264,7 @@ function verifyRequestItemTarget(aRequestItem, aMethod, aUrl, aData = {}) {
info("Widget index of item: " + widgetIndex);
info("Visible index of item: " + visibleIndex);
- let { fuzzyUrl, status, statusText, type, fullMimeType, size, time } = aData;
+ let { fuzzyUrl, status, statusText, type, fullMimeType, transferred, size, time } = aData;
let { attachment, target } = aRequestItem
let uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL);
@@ -319,6 +319,14 @@ function verifyRequestItemTarget(aRequestItem, aMethod, aUrl, aData = {}) {
is(value, type, "The displayed type is incorrect.");
is(tooltip, fullMimeType, "The tooltip type is incorrect.");
}
+ if (transferred !== undefined) {
+ let value = target.querySelector(".requests-menu-transferred").getAttribute("value");
+ let tooltip = target.querySelector(".requests-menu-transferred").getAttribute("tooltiptext");
+ info("Displayed transferred size: " + value);
+ info("Tooltip transferred size: " + tooltip);
+ is(value, transferred, "The displayed transferred size is incorrect.");
+ is(tooltip, transferred, "The tooltip transferred size is incorrect.");
+ }
if (size !== undefined) {
let value = target.querySelector(".requests-menu-size").getAttribute("value");
let tooltip = target.querySelector(".requests-menu-size").getAttribute("tooltiptext");
diff --git a/browser/devtools/timeline/test/browser_timeline_aaa_run_first_leaktest.js b/browser/devtools/timeline/test/browser_timeline_aaa_run_first_leaktest.js
index 8ad25d4c3d0c..33ecca920278 100644
--- a/browser/devtools/timeline/test/browser_timeline_aaa_run_first_leaktest.js
+++ b/browser/devtools/timeline/test/browser_timeline_aaa_run_first_leaktest.js
@@ -6,7 +6,7 @@
* You can also use this initialization format as a template for other tests.
*/
-let test = Task.async(function*() {
+add_task(function*() {
let { target, panel } = yield initTimelinePanel(SIMPLE_URL);
ok(target, "Should have a target available.");
@@ -15,7 +15,4 @@ let test = Task.async(function*() {
ok(panel.panelWin.gToolbox, "Should have a toolbox reference on the panel window.");
ok(panel.panelWin.gTarget, "Should have a target reference on the panel window.");
ok(panel.panelWin.gFront, "Should have a front reference on the panel window.");
-
- yield teardown(panel);
- finish();
});
diff --git a/browser/devtools/timeline/test/browser_timeline_blueprint.js b/browser/devtools/timeline/test/browser_timeline_blueprint.js
index 6964acda7bf0..ecb530f2fb42 100644
--- a/browser/devtools/timeline/test/browser_timeline_blueprint.js
+++ b/browser/devtools/timeline/test/browser_timeline_blueprint.js
@@ -5,7 +5,7 @@
* Tests if the timeline blueprint has a correct structure.
*/
-function test() {
+add_task(function*() {
let { TIMELINE_BLUEPRINT } = devtools.require("devtools/timeline/global");
ok(TIMELINE_BLUEPRINT,
@@ -24,6 +24,4 @@ function test() {
ok("label" in value,
"Each entry in the timeline blueprint contains a `label` key.");
}
-
- finish();
-}
+});
diff --git a/browser/devtools/timeline/test/browser_timeline_overview-initial-selection-01.js b/browser/devtools/timeline/test/browser_timeline_overview-initial-selection-01.js
index 23c26f85502c..d2f9b226a6ac 100644
--- a/browser/devtools/timeline/test/browser_timeline_overview-initial-selection-01.js
+++ b/browser/devtools/timeline/test/browser_timeline_overview-initial-selection-01.js
@@ -6,7 +6,7 @@
* and there is data available.
*/
-let test = Task.async(function*() {
+add_task(function*() {
let { target, panel } = yield initTimelinePanel(SIMPLE_URL);
let { $, EVENTS, TimelineView, TimelineController } = panel.panelWin;
let { OVERVIEW_INITIAL_SELECTION_RATIO: selectionRatio } = panel.panelWin;
@@ -41,7 +41,4 @@ let test = Task.async(function*() {
is((selection.end - selection.start) | 0,
(selectionRatio * TimelineView.markersOverview.width) | 0,
"The initial selection end is correct.");
-
- yield teardown(panel);
- finish();
});
diff --git a/browser/devtools/timeline/test/browser_timeline_overview-initial-selection-02.js b/browser/devtools/timeline/test/browser_timeline_overview-initial-selection-02.js
index e10a1d058d7f..8a7675c0f49f 100644
--- a/browser/devtools/timeline/test/browser_timeline_overview-initial-selection-02.js
+++ b/browser/devtools/timeline/test/browser_timeline_overview-initial-selection-02.js
@@ -6,7 +6,7 @@
* and there is no data available.
*/
-let test = Task.async(function*() {
+add_task(function*() {
let { target, panel } = yield initTimelinePanel(SIMPLE_URL);
let { $, EVENTS, TimelineView, TimelineController } = panel.panelWin;
let { OVERVIEW_INITIAL_SELECTION_RATIO: selectionRatio } = panel.panelWin;
@@ -32,7 +32,4 @@ let test = Task.async(function*() {
"The initial selection start is correct.");
is(selection.end, null,
"The initial selection end is correct.");
-
- yield teardown(panel);
- finish();
});
diff --git a/browser/devtools/timeline/test/browser_timeline_overview-update.js b/browser/devtools/timeline/test/browser_timeline_overview-update.js
index 6a85e51f723b..ffecffb24f54 100644
--- a/browser/devtools/timeline/test/browser_timeline_overview-update.js
+++ b/browser/devtools/timeline/test/browser_timeline_overview-update.js
@@ -5,7 +5,7 @@
* Tests if the markers and memory overviews are continuously updated.
*/
-let test = Task.async(function*() {
+add_task(function*() {
let { target, panel } = yield initTimelinePanel("about:blank");
let { $, EVENTS, TimelineView, TimelineController } = panel.panelWin;
@@ -68,7 +68,4 @@ let test = Task.async(function*() {
"The selection should now be enabled for the memory overview.");
is(TimelineView.memoryOverview.hasSelection(), false,
"The memory overview should not have a selection after recording.");
-
- yield teardown(panel);
- finish();
});
diff --git a/browser/devtools/timeline/test/browser_timeline_panels.js b/browser/devtools/timeline/test/browser_timeline_panels.js
index c1f483018e4f..f7f0de0865cb 100644
--- a/browser/devtools/timeline/test/browser_timeline_panels.js
+++ b/browser/devtools/timeline/test/browser_timeline_panels.js
@@ -6,7 +6,7 @@
* recording starts and stops.
*/
-let test = Task.async(function*() {
+add_task(function*() {
let { target, panel } = yield initTimelinePanel(SIMPLE_URL);
let { $, EVENTS } = panel.panelWin;
@@ -36,7 +36,4 @@ let test = Task.async(function*() {
"The record button should be unchecked again.");
is($("#timeline-pane").selectedPanel, $("#timeline-waterfall-container"),
"A waterfall view is now displayed.");
-
- yield teardown(panel);
- finish();
});
diff --git a/browser/devtools/timeline/test/browser_timeline_recording-without-memory.js b/browser/devtools/timeline/test/browser_timeline_recording-without-memory.js
index 65c715977bdf..5b8b0e0ea26f 100644
--- a/browser/devtools/timeline/test/browser_timeline_recording-without-memory.js
+++ b/browser/devtools/timeline/test/browser_timeline_recording-without-memory.js
@@ -5,7 +5,7 @@
* Tests if the timeline actor isn't unnecessarily asked to record memory.
*/
-let test = Task.async(function*() {
+add_task(function*() {
let { target, panel } = yield initTimelinePanel(SIMPLE_URL);
let { $, EVENTS, TimelineView, TimelineController } = panel.panelWin;
@@ -30,7 +30,4 @@ let test = Task.async(function*() {
"There are some markers available.");
is(memory.length, 0,
"There are no memory measurements available.");
-
- yield teardown(panel);
- finish();
});
diff --git a/browser/devtools/timeline/test/browser_timeline_recording.js b/browser/devtools/timeline/test/browser_timeline_recording.js
index 04a4c482386b..e1ae526d2807 100644
--- a/browser/devtools/timeline/test/browser_timeline_recording.js
+++ b/browser/devtools/timeline/test/browser_timeline_recording.js
@@ -5,7 +5,7 @@
* Tests if the timeline can properly start and stop a recording.
*/
-let test = Task.async(function*() {
+add_task(function*() {
let { target, panel } = yield initTimelinePanel(SIMPLE_URL);
let { $, gFront, TimelineController } = panel.panelWin;
@@ -35,6 +35,5 @@ let test = Task.async(function*() {
TimelineController.getInterval().startTime,
"Some time has passed since the recording started.");
- yield teardown(panel);
- finish();
+ yield TimelineController.toggleRecording();
});
diff --git a/browser/devtools/timeline/test/browser_timeline_waterfall-background.js b/browser/devtools/timeline/test/browser_timeline_waterfall-background.js
index 1d80e542f0ea..47c1cfba1705 100644
--- a/browser/devtools/timeline/test/browser_timeline_waterfall-background.js
+++ b/browser/devtools/timeline/test/browser_timeline_waterfall-background.js
@@ -6,7 +6,7 @@
* the container bounds.
*/
-let test = Task.async(function*() {
+add_task(function*() {
let { target, panel } = yield initTimelinePanel(SIMPLE_URL);
let { $, EVENTS, TimelineView, TimelineController } = panel.panelWin;
@@ -41,7 +41,4 @@ let test = Task.async(function*() {
"The canvas width is correct.");
is(TimelineView.waterfall._canvas.height, 1,
"The canvas height is correct.");
-
- yield teardown(panel);
- finish();
});
diff --git a/browser/devtools/timeline/test/browser_timeline_waterfall-generic.js b/browser/devtools/timeline/test/browser_timeline_waterfall-generic.js
index ec9f1070ba4e..ceff2bd1f42e 100644
--- a/browser/devtools/timeline/test/browser_timeline_waterfall-generic.js
+++ b/browser/devtools/timeline/test/browser_timeline_waterfall-generic.js
@@ -5,7 +5,7 @@
* Tests if the waterfall is properly built after finishing a recording.
*/
-let test = Task.async(function*() {
+add_task(function*() {
let { target, panel } = yield initTimelinePanel(SIMPLE_URL);
let { $, $$, EVENTS, TimelineController } = panel.panelWin;
@@ -62,7 +62,4 @@ let test = Task.async(function*() {
"Some marker waterfall nodes should have been created.");
ok($$(".waterfall-marker-item:not(spacer) > .waterfall-marker-bar").length,
"Some marker color bars should have been created inside the waterfall.");
-
- yield teardown(panel);
- finish();
});
diff --git a/browser/devtools/timeline/test/browser_timeline_waterfall-sidebar.js b/browser/devtools/timeline/test/browser_timeline_waterfall-sidebar.js
index 061251452562..b6b4f5dbe83f 100644
--- a/browser/devtools/timeline/test/browser_timeline_waterfall-sidebar.js
+++ b/browser/devtools/timeline/test/browser_timeline_waterfall-sidebar.js
@@ -5,7 +5,7 @@
* Tests if the sidebar is properly updated when a marker is selected.
*/
-let test = Task.async(function*() {
+add_task(function*() {
let { target, panel } = yield initTimelinePanel(SIMPLE_URL);
let { $, $$, EVENTS, TimelineController, TimelineView } = panel.panelWin;
let { L10N } = devtools.require("devtools/timeline/global");
@@ -53,7 +53,4 @@ let test = Task.async(function*() {
is(toMs(m.end), printedEndTime, "sidebar end time is valid");
is(toMs(m.end - m.start), printedDuration, "sidebar duration is valid");
}
-
- yield teardown(panel);
- finish();
});
diff --git a/browser/devtools/timeline/test/browser_timeline_waterfall-styles.js b/browser/devtools/timeline/test/browser_timeline_waterfall-styles.js
index 40e700d6a5c9..9e1ae1f0bb9b 100644
--- a/browser/devtools/timeline/test/browser_timeline_waterfall-styles.js
+++ b/browser/devtools/timeline/test/browser_timeline_waterfall-styles.js
@@ -17,7 +17,7 @@ var gRGB_TO_HSL = {
"rgb(153, 153, 153)": "hsl(0,0%,60%)",
};
-let test = Task.async(function*() {
+add_task(function*() {
let { target, panel } = yield initTimelinePanel(SIMPLE_URL);
let { TIMELINE_BLUEPRINT } = devtools.require("devtools/timeline/global");
let { $, $$, EVENTS, TimelineController } = panel.panelWin;
@@ -85,7 +85,4 @@ let test = Task.async(function*() {
ok(bar.style.transform.match(/^translateX\(.*px\)$/),
"The bar appears to have proper translations.");
}
-
- yield teardown(panel);
- finish();
});
diff --git a/browser/devtools/timeline/test/head.js b/browser/devtools/timeline/test/head.js
index cd3c7f0f00eb..3ac9624e26b0 100644
--- a/browser/devtools/timeline/test/head.js
+++ b/browser/devtools/timeline/test/head.js
@@ -36,6 +36,16 @@ registerCleanupFunction(() => {
Services.prefs.setBoolPref("devtools.timeline.enabled", gToolEnabled);
});
+// Close the toolbox and all opened tabs automatically.
+registerCleanupFunction(function*() {
+ let target = TargetFactory.forTab(gBrowser.selectedTab);
+ yield gDevTools.closeToolbox(target);
+
+ while (gBrowser.tabs.length > 1) {
+ gBrowser.removeCurrentTab();
+ }
+});
+
function addTab(url) {
info("Adding tab: " + url);
@@ -52,22 +62,6 @@ function addTab(url) {
return deferred.promise;
}
-function removeTab(tab) {
- info("Removing tab.");
-
- let deferred = promise.defer();
- let tabContainer = gBrowser.tabContainer;
-
- tabContainer.addEventListener("TabClose", function onClose(aEvent) {
- tabContainer.removeEventListener("TabClose", onClose, false);
- info("Tab removed and finished closing.");
- deferred.resolve();
- }, false);
-
- gBrowser.removeTab(tab);
- return deferred.promise;
-}
-
/**
* Spawns a new tab and starts up a toolbox with the timeline panel
* automatically selected.
@@ -93,25 +87,6 @@ function* initTimelinePanel(url) {
return { target, panel };
}
-/**
- * Closes a tab and destroys the toolbox holding a timeline panel.
- *
- * Must be used within a task.
- *
- * @param object panel
- * The timeline panel, created by the toolbox.
- * @return object
- * A promise resolved once the timeline, toolbox and debuggee tab
- * are destroyed.
- */
-function* teardown(panel) {
- info("Destroying the specified timeline.");
-
- let tab = panel.target.tab;
- yield panel._toolbox.destroy();
- yield removeTab(tab);
-}
-
/**
* Waits until a predicate returns true.
*
diff --git a/browser/devtools/webaudioeditor/test/browser_audionode-actor-bypass.js b/browser/devtools/webaudioeditor/test/browser_audionode-actor-bypass.js
index 4c520b6d7b13..6b1606b1e426 100644
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-bypass.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-bypass.js
@@ -5,7 +5,7 @@
* Test AudioNode#bypass(), AudioNode#isBypassed()
*/
-function spawnTest () {
+add_task(function*() {
let { target, front } = yield initBackend(SIMPLE_CONTEXT_URL);
let [_, [destNode, oscNode, gainNode]] = yield Promise.all([
front.setup({ reload: true }),
@@ -25,5 +25,4 @@ function spawnTest () {
is((yield gainNode.isBypassed()), false, "Node back to being unbypassed.");
yield removeTab(target.tab);
- finish();
-}
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_audionode-actor-connectnode-disconnect.js b/browser/devtools/webaudioeditor/test/browser_audionode-actor-connectnode-disconnect.js
index b3da4e9dbe0d..f0e437d8662f 100644
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-connectnode-disconnect.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-connectnode-disconnect.js
@@ -6,7 +6,7 @@
* Uses the editor front as the actors do not retain connect state.
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
@@ -28,7 +28,6 @@ function spawnTest() {
]);
ok(true, "Oscillator disconnected, event emitted.");
-
info("Reconnecting oscillator...");
osc.connectNode(gain);
yield Promise.all([
@@ -37,8 +36,5 @@ function spawnTest() {
]);
ok(true, "Oscillator reconnected.");
-
- yield teardown(panel);
- finish();
-}
-
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_audionode-actor-connectparam.js b/browser/devtools/webaudioeditor/test/browser_audionode-actor-connectparam.js
index 2603299d17b8..df1f6e0b3e8c 100644
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-connectparam.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-connectparam.js
@@ -6,7 +6,7 @@
* Uses the editor front as the actors do not retain connect state.
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
@@ -29,7 +29,5 @@ function spawnTest() {
]);
ok(true, "Oscillator connect to Gain's Gain AudioParam, event emitted.");
- yield teardown(panel);
- finish();
-}
-
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-param-flags.js b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-param-flags.js
index e60a697e9e3d..cfa8cff2a212 100644
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-param-flags.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-param-flags.js
@@ -5,7 +5,7 @@
* Test AudioNode#getParamFlags()
*/
-function spawnTest () {
+add_task(function*() {
let { target, front } = yield initBackend(SIMPLE_NODES_URL);
let [_, nodes] = yield Promise.all([
front.setup({ reload: true }),
@@ -45,5 +45,4 @@ function spawnTest () {
}
yield removeTab(target.tab);
- finish();
-}
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-01.js b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-01.js
index 9c88ff0b54fe..a4efc6d61aaa 100644
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-01.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-01.js
@@ -5,7 +5,7 @@
* Test AudioNode#getParams()
*/
-function spawnTest () {
+add_task(function*() {
let { target, front } = yield initBackend(SIMPLE_NODES_URL);
let [_, nodes] = yield Promise.all([
front.setup({ reload: true }),
@@ -42,5 +42,4 @@ function spawnTest () {
});
yield removeTab(target.tab);
- finish();
-}
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-02.js b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-02.js
index c426cab925ef..2afb718242c7 100644
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-02.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-02.js
@@ -6,7 +6,7 @@
* from the AudioNode actors.
*/
-function spawnTest() {
+add_task(function*() {
let { target, front } = yield initBackend(SIMPLE_NODES_URL);
let [_, nodes] = yield Promise.all([
front.setup({ reload: true }),
@@ -26,8 +26,7 @@ function spawnTest() {
});
yield removeTab(target.tab);
- finish();
-}
+});
function compare (actual, expected, type) {
actual.forEach(({ value, param }) => {
diff --git a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-set-param.js b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-set-param.js
index da6de234e2e8..4679159aa51d 100644
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-set-param.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-set-param.js
@@ -5,7 +5,7 @@
* Test AudioNode#getParam() / AudioNode#setParam()
*/
-function spawnTest () {
+add_task(function*() {
let { target, front } = yield initBackend(SIMPLE_CONTEXT_URL);
let [_, [destNode, oscNode, gainNode]] = yield Promise.all([
front.setup({ reload: true }),
@@ -44,5 +44,4 @@ function spawnTest () {
}
yield removeTab(target.tab);
- finish();
-}
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-type.js b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-type.js
index b4ec5e5f6b93..94d31a6c398b 100644
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-type.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-type.js
@@ -5,7 +5,7 @@
* Test AudioNode#getType()
*/
-function spawnTest () {
+add_task(function*() {
let { target, front } = yield initBackend(SIMPLE_NODES_URL);
let [_, nodes] = yield Promise.all([
front.setup({ reload: true }),
@@ -25,5 +25,4 @@ function spawnTest () {
});
yield removeTab(target.tab);
- finish();
-}
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_audionode-actor-is-source.js b/browser/devtools/webaudioeditor/test/browser_audionode-actor-is-source.js
index bc99540ef9b1..13523fb36232 100644
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-is-source.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-is-source.js
@@ -5,7 +5,7 @@
* Test AudioNode#isSource()
*/
-function spawnTest () {
+add_task(function*() {
let { target, front } = yield initBackend(SIMPLE_NODES_URL);
let [_, nodes] = yield Promise.all([
front.setup({ reload: true }),
@@ -24,5 +24,4 @@ function spawnTest () {
});
yield removeTab(target.tab);
- finish();
-}
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_destroy-node-01.js b/browser/devtools/webaudioeditor/test/browser_wa_destroy-node-01.js
index d8b27e945e94..b44ab9e557a2 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_destroy-node-01.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_destroy-node-01.js
@@ -9,7 +9,7 @@
* All done in one test since this test takes a few seconds to clear GC.
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(DESTROY_NODES_URL);
let { panelWin } = panel;
let { gFront, $, $$, gAudioNodes } = panelWin;
@@ -56,7 +56,5 @@ function spawnTest() {
ok(isVisible($("#web-audio-editor-details-pane-empty")),
"InspectorView empty message should show if the currently selected node gets collected.");
- yield teardown(panel);
- finish();
-}
-
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_first-run.js b/browser/devtools/webaudioeditor/test/browser_wa_first-run.js
index cdf8f84b1fd1..5229b0923b83 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_first-run.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_first-run.js
@@ -12,7 +12,7 @@ thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Connection closed");
* Tests that the reloading/onContentLoaded hooks work.
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
let { gFront, $ } = panel.panelWin;
@@ -46,6 +46,5 @@ function spawnTest() {
is($("#content").hidden, false,
"The tool's content should not be hidden anymore.");
- yield teardown(panel);
- finish();
-}
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_graph-click.js b/browser/devtools/webaudioeditor/test/browser_wa_graph-click.js
index 822c5dd2447c..d14d3e027e85 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-click.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-click.js
@@ -6,7 +6,7 @@
* the correct node in the InspectorView
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
let panelWin = panel.panelWin;
let { gFront, $, $$, InspectorView } = panelWin;
@@ -46,6 +46,5 @@ function spawnTest() {
ok(InspectorView.isVisible(),
"InspectorView still visible after several nodes have been clicked.");
- yield teardown(panel);
- finish();
-}
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_graph-markers.js b/browser/devtools/webaudioeditor/test/browser_wa_graph-markers.js
index d1264043e972..684561161838 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-markers.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-markers.js
@@ -5,7 +5,7 @@
* Tests that the SVG marker styling is updated when devtools theme changes.
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
let { panelWin } = panel;
let { gFront, $, $$, MARKER_STYLING } = panelWin;
@@ -47,9 +47,8 @@ function spawnTest() {
is(getFill($("#arrowhead")), MARKER_STYLING.light,
"marker styling switches back to light once again.");
- yield teardown(panel);
- finish();
-}
+ yield teardown(target);
+});
/**
* Returns a hex value found in styling for an element. So parses
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_graph-render-01.js b/browser/devtools/webaudioeditor/test/browser_wa_graph-render-01.js
index 290022b1e91b..fdbe1ce5dbe0 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-render-01.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-render-01.js
@@ -7,7 +7,7 @@
let connectCount = 0;
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
@@ -37,9 +37,8 @@ function spawnTest() {
gAudioNodes.off("connect", onConnectNode);
- yield teardown(panel);
- finish();
-}
+ yield teardown(target);
+});
function onConnectNode () {
++connectCount;
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_graph-render-02.js b/browser/devtools/webaudioeditor/test/browser_wa_graph-render-02.js
index a52f63fd874e..825ab76b95da 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-render-02.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-render-02.js
@@ -5,7 +5,7 @@
* Tests more edge rendering for complex graphs.
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
let { panelWin } = panel;
let { gFront, $, $$ } = panelWin;
@@ -45,7 +45,5 @@ function spawnTest() {
"found edge for " + msg);
});
- yield teardown(panel);
- finish();
-}
-
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_graph-render-03.js b/browser/devtools/webaudioeditor/test/browser_wa_graph-render-03.js
index 2573ddfed2e0..4486483fac61 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-render-03.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-render-03.js
@@ -5,7 +5,7 @@
* Tests to ensure that selected nodes stay selected on graph redraw.
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS } = panelWin;
@@ -31,7 +31,5 @@ function spawnTest() {
ok(findGraphNode(panelWin, gain.actorID).classList.contains("selected"),
"Node still selected after rerender.");
- yield teardown(panel);
- finish();
-}
-
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_graph-render-04.js b/browser/devtools/webaudioeditor/test/browser_wa_graph-render-04.js
index 1292b84851e5..84db904a9a28 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-render-04.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-render-04.js
@@ -5,7 +5,7 @@
* Tests audio param connection rendering.
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(CONNECT_MULTI_PARAM_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS } = panelWin;
@@ -34,7 +34,5 @@ function spawnTest() {
ok(edge.classList.contains("param-connection"), "edge is classified as a param-connection");
});
- yield teardown(panel);
- finish();
-}
-
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_graph-render-05.js b/browser/devtools/webaudioeditor/test/browser_wa_graph-render-05.js
index 253a9383ef41..b1ddd78b304f 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-render-05.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-render-05.js
@@ -5,7 +5,7 @@
* Tests to ensure that param connections trigger graph redraws
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS } = panelWin;
@@ -25,7 +25,5 @@ function spawnTest() {
yield waitForGraphRendered(panelWin, 3, 1, 1);
ok(true, "Graph re-rendered upon param connection");
- yield teardown(panel);
- finish();
-}
-
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_graph-selected.js b/browser/devtools/webaudioeditor/test/browser_wa_graph-selected.js
index 51edb953182d..4ee7e04feaaa 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-selected.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-selected.js
@@ -5,7 +5,7 @@
* Tests that SVG nodes and edges were created for the Graph View.
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS } = panelWin;
@@ -46,7 +46,5 @@ function spawnTest() {
ok(findGraphNode(panelWin, gainId).classList.contains("selected"),
"Newly selected node now has class 'selected'.");
- yield teardown(panel);
- finish();
-}
-
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_graph-zoom.js b/browser/devtools/webaudioeditor/test/browser_wa_graph-zoom.js
index 0ec876a783f6..d06cc15491da 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-zoom.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-zoom.js
@@ -5,7 +5,7 @@
* Tests that the graph's scale and position is reset on a page reload.
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS, ContextView } = panelWin;
@@ -39,7 +39,5 @@ function spawnTest() {
is(ContextView.getCurrentTranslation()[0], 20, "After refresh, x-translation is 20.");
is(ContextView.getCurrentTranslation()[1], 20, "After refresh, y-translation is 20.");
- yield teardown(panel);
- finish();
-}
-
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_inspector-toggle.js b/browser/devtools/webaudioeditor/test/browser_wa_inspector-toggle.js
index d133e00d9135..606af9f2e5a8 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_inspector-toggle.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_inspector-toggle.js
@@ -6,7 +6,7 @@
* the inspector panel as intended.
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
@@ -61,6 +61,5 @@ function spawnTest() {
is($("#web-audio-inspector-title").value, "Oscillator",
"Inspector title updates when loading node while open.");
- yield teardown(panel);
- finish();
-}
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_inspector.js b/browser/devtools/webaudioeditor/test/browser_wa_inspector.js
index 085d17513687..616be4062f41 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_inspector.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_inspector.js
@@ -6,7 +6,7 @@
* loads the correct node inside the inspector.
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
@@ -55,6 +55,5 @@ function spawnTest() {
is($("#web-audio-inspector-title").value, "Gain",
"Inspector title updates when a new node is selected.");
- yield teardown(panel);
- finish();
-}
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_navigate.js b/browser/devtools/webaudioeditor/test/browser_wa_navigate.js
index a57c5ec725d9..d913ff1cfa5b 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_navigate.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_navigate.js
@@ -6,7 +6,7 @@
* the audio graph if both pages have an AudioContext.
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
let { panelWin } = panel;
let { gFront, $ } = panelWin;
@@ -40,6 +40,5 @@ function spawnTest() {
ise(nodes, 14, "after navigation, should have 14 nodes");
ise(edges, 0, "after navigation, should have 0 edges.");
- yield teardown(panel);
- finish();
-}
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_properties-view-edit-01.js b/browser/devtools/webaudioeditor/test/browser_wa_properties-view-edit-01.js
index 9b0495dfcc32..673eb7611a97 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_properties-view-edit-01.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_properties-view-edit-01.js
@@ -5,7 +5,7 @@
* Tests that properties are updated when modifying the VariablesView.
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
@@ -52,9 +52,8 @@ function spawnTest() {
yield setAndCheck(0, "gain", "0.1", 0.1, "sets float as float");
yield setAndCheck(0, "gain", ".2", 0.2, "sets float without leading zero as float");
- yield teardown(panel);
- finish();
-}
+ yield teardown(target);
+});
function setAndCheckVariable (panelWin, gVars) {
return Task.async(function (varNum, prop, value, expected, desc) {
@@ -64,4 +63,3 @@ function setAndCheckVariable (panelWin, gVars) {
checkVariableView(gVars, varNum, props, desc);
});
}
-
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_properties-view-edit-02.js b/browser/devtools/webaudioeditor/test/browser_wa_properties-view-edit-02.js
index 322ae56542e3..58f31fa6fa33 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_properties-view-edit-02.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_properties-view-edit-02.js
@@ -5,7 +5,7 @@
* Tests that properties are not updated when modifying the VariablesView.
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
@@ -40,6 +40,5 @@ function spawnTest() {
checkVariableView(gVars, 0, {bufferSize: 4096}, "check that unwritable variable is not updated");
- yield teardown(panel);
- finish();
-}
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_properties-view-media-nodes.js b/browser/devtools/webaudioeditor/test/browser_wa_properties-view-media-nodes.js
index f647e5b0b3db..afbba4d663e1 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_properties-view-media-nodes.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_properties-view-media-nodes.js
@@ -34,7 +34,7 @@ function waitForDeviceClosed() {
return deferred.promise;
}
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(MEDIA_NODES_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
@@ -66,9 +66,7 @@ function spawnTest() {
// Reset permissions on getUserMedia
Services.prefs.setBoolPref(MEDIA_PERMISSION, mediaPermissionPref);
- yield teardown(panel);
+ yield teardown(target);
yield waitForDeviceClosed();
-
- finish();
-}
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_properties-view-params-objects.js b/browser/devtools/webaudioeditor/test/browser_wa_properties-view-params-objects.js
index 7fb6ec1bdc37..e13efe52841f 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_properties-view-params-objects.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_properties-view-params-objects.js
@@ -6,7 +6,7 @@
* like AudioBuffer and Float32Array in properties of AudioNodes.
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(BUFFER_AND_ARRAY_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
@@ -42,6 +42,5 @@ function spawnTest() {
state = aVar.target.querySelector(".theme-twisty").hasAttribute("invisible");
ok(state, "AudioBuffer property should not have a dropdown.");
- yield teardown(panel);
- finish();
-}
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_properties-view-params.js b/browser/devtools/webaudioeditor/test/browser_wa_properties-view-params.js
index d699a7ec2f27..14605cb04002 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_properties-view-params.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_properties-view-params.js
@@ -6,7 +6,7 @@
* correctly, with default values and correct types.
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(SIMPLE_NODES_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
@@ -34,6 +34,5 @@ function spawnTest() {
checkVariableView(gVars, 0, NODE_DEFAULT_VALUES[types[i]], types[i]);
}
- yield teardown(panel);
- finish();
-}
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_properties-view.js b/browser/devtools/webaudioeditor/test/browser_wa_properties-view.js
index 262797fd885e..b86bf5498e9e 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_properties-view.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_properties-view.js
@@ -5,7 +5,7 @@
* Tests that params view shows params when they exist, and are hidden otherwise.
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
@@ -38,6 +38,5 @@ function spawnTest() {
ok(isVisible($("#properties-tabpanel-content-empty")),
"Empty message shown when no AudioParams exist.");
- yield teardown(panel);
- finish();
-}
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_reset-01.js b/browser/devtools/webaudioeditor/test/browser_wa_reset-01.js
index a9d305392d9d..3208227acb96 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_reset-01.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_reset-01.js
@@ -13,7 +13,7 @@ thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Connection closed");
* event and reshow the tools after reloading.
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
let { gFront, $ } = panel.panelWin;
@@ -61,6 +61,5 @@ function spawnTest() {
is($("#content").hidden, false,
"The tool's content should reappear without closing and reopening the toolbox.");
- yield teardown(panel);
- finish();
-}
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_reset-02.js b/browser/devtools/webaudioeditor/test/browser_wa_reset-02.js
index 81943268f034..478b9f5db605 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_reset-02.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_reset-02.js
@@ -6,7 +6,7 @@
* the graph.
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
let { panelWin } = panel;
let { gFront, $ } = panelWin;
@@ -33,6 +33,5 @@ function spawnTest() {
ise(nodes, 3, "after reload, should only be 3 nodes.");
ise(edges, 2, "after reload, should only be 2 edges.");
- yield teardown(panel);
- finish();
-}
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_reset-03.js b/browser/devtools/webaudioeditor/test/browser_wa_reset-03.js
index bbb358373e87..60c9572d1106 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_reset-03.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_reset-03.js
@@ -6,7 +6,7 @@
* the inspector and selected node.
*/
-function spawnTest() {
+add_task(function*() {
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
let { panelWin } = panel;
let { gFront, $, InspectorView } = panelWin;
@@ -44,6 +44,5 @@ function spawnTest() {
"InspectorView visible after selecting a node after a reset.");
is(InspectorView.getCurrentAudioNode().id, nodeIds[2], "InspectorView has correct node set upon clicking graph node after a reset.");
- yield teardown(panel);
- finish();
-}
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_wa_reset-04.js b/browser/devtools/webaudioeditor/test/browser_wa_reset-04.js
index efbf9ef9a146..3bef5775dbee 100644
--- a/browser/devtools/webaudioeditor/test/browser_wa_reset-04.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_reset-04.js
@@ -12,7 +12,7 @@ thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Connection closed");
* Tests that switching to an iframe works fine.
*/
-function spawnTest() {
+add_task(function*() {
Services.prefs.setBoolPref("devtools.command-button-frames.enabled", true);
let { target, panel, toolbox } = yield initWebAudioEditor(IFRAME_CONTEXT_URL);
@@ -58,6 +58,5 @@ function spawnTest() {
is($("#content").hidden, false,
"The tool's content should appear after reload.");
- yield teardown(panel);
- finish();
-}
+ yield teardown(target);
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_webaudio-actor-connect-param.js b/browser/devtools/webaudioeditor/test/browser_webaudio-actor-connect-param.js
index ad1a5c9cb6ff..a436401dc4a8 100644
--- a/browser/devtools/webaudioeditor/test/browser_webaudio-actor-connect-param.js
+++ b/browser/devtools/webaudioeditor/test/browser_webaudio-actor-connect-param.js
@@ -5,7 +5,7 @@
* Test the `connect-param` event on the web audio actor.
*/
-function spawnTest () {
+add_task(function*() {
let { target, front } = yield initBackend(CONNECT_PARAM_URL);
let [, , [destNode, carrierNode, modNode, gainNode], , connectParam] = yield Promise.all([
front.setup({ reload: true }),
@@ -22,5 +22,4 @@ function spawnTest () {
is(connectParam.param, "gain", "`connect-param` has correct parameter name for `param`");
yield removeTab(target.tab);
- finish();
-}
+});
diff --git a/browser/devtools/webaudioeditor/test/browser_webaudio-actor-destroy-node.js b/browser/devtools/webaudioeditor/test/browser_webaudio-actor-destroy-node.js
index 9e5feea293d9..60140dbde17f 100644
--- a/browser/devtools/webaudioeditor/test/browser_webaudio-actor-destroy-node.js
+++ b/browser/devtools/webaudioeditor/test/browser_webaudio-actor-destroy-node.js
@@ -5,7 +5,7 @@
* Test `destroy-node` event on WebAudioActor.
*/
-function spawnTest () {
+add_task(function*() {
let { target, front } = yield initBackend(DESTROY_NODES_URL);
let waitUntilDestroyed = getN(front, "destroy-node", 10);
@@ -29,8 +29,7 @@ function spawnTest () {
});
yield removeTab(target.tab);
- finish();
-}
+});
function actorIsInList (list, actor) {
for (let i = 0; i < list.length; i++) {
diff --git a/browser/devtools/webaudioeditor/test/browser_webaudio-actor-simple.js b/browser/devtools/webaudioeditor/test/browser_webaudio-actor-simple.js
index 1ef29a9f999f..949e19f0f8dd 100644
--- a/browser/devtools/webaudioeditor/test/browser_webaudio-actor-simple.js
+++ b/browser/devtools/webaudioeditor/test/browser_webaudio-actor-simple.js
@@ -5,7 +5,7 @@
* Test basic communication of Web Audio actor
*/
-function spawnTest () {
+add_task(function*() {
let { target, front } = yield initBackend(SIMPLE_CONTEXT_URL);
let [_, __, [destNode, oscNode, gainNode], [connect1, connect2]] = yield Promise.all([
front.setup({ reload: true }),
@@ -31,5 +31,4 @@ function spawnTest () {
is(dest.actorID, destNode.actorID, "WebAudioActor:connect-node returns correct actor with ID on dest (gain->dest)");
yield removeTab(target.tab);
- finish();
-}
+});
diff --git a/browser/devtools/webaudioeditor/test/head.js b/browser/devtools/webaudioeditor/test/head.js
index 25dbda604406..730679822a01 100644
--- a/browser/devtools/webaudioeditor/test/head.js
+++ b/browser/devtools/webaudioeditor/test/head.js
@@ -84,11 +84,6 @@ function removeTab(aTab, aWindow) {
return deferred.promise;
}
-function handleError(aError) {
- ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
- finish();
-}
-
function once(aTarget, aEventName, aUseCapture = false) {
info("Waiting for event: '" + aEventName + "' on " + aTarget + ".");
@@ -121,10 +116,10 @@ function navigate(aTarget, aUrl, aWaitForTargetEvent = "navigate") {
return once(aTarget, aWaitForTargetEvent);
}
-function test () {
- Task.spawn(spawnTest).then(finish, handleError);
-}
-
+/**
+ * Adds a new tab, and instantiate a WebAudiFront object.
+ * This requires calling removeTab before the test ends.
+ */
function initBackend(aUrl) {
info("Initializing a web audio editor front.");
@@ -144,6 +139,11 @@ function initBackend(aUrl) {
});
}
+/**
+ * Adds a new tab, and open the toolbox for that tab, selecting the audio editor
+ * panel.
+ * This requires calling teardown before the test ends.
+ */
function initWebAudioEditor(aUrl) {
info("Initializing a web audio editor pane.");
@@ -160,18 +160,16 @@ function initWebAudioEditor(aUrl) {
});
}
-function teardown(aPanel) {
+/**
+ * Close the toolbox, destroying all panels, and remove the added test tabs.
+ */
+function teardown(aTarget) {
info("Destroying the web audio editor.");
- return Promise.all([
- once(aPanel, "destroyed"),
- removeTab(aPanel.target.tab)
- ]).then(() => {
- let gBrowser = window.gBrowser;
+ return gDevTools.closeToolbox(aTarget).then(() => {
while (gBrowser.tabs.length > 1) {
gBrowser.removeCurrentTab();
}
- gBrowser = null;
});
}
diff --git a/browser/devtools/webconsole/test/browser_webconsole_scratchpad_panel_link.js b/browser/devtools/webconsole/test/browser_webconsole_scratchpad_panel_link.js
index 0d1db73c5828..c0899d868862 100644
--- a/browser/devtools/webconsole/test/browser_webconsole_scratchpad_panel_link.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_scratchpad_panel_link.js
@@ -10,62 +10,54 @@ let { isTargetSupported } = Tools.scratchpad;
Tools.scratchpad.isTargetSupported = () => true;
-
-function test()
-{
+add_task(function*() {
waitForExplicitFinish();
+ yield loadTab(TEST_URI);
- loadTab(TEST_URI).then(() => {
- info("Opening toolbox with Scratchpad panel");
+ info("Opening toolbox with Scratchpad panel");
- let target = TargetFactory.forTab(gBrowser.selectedTab);
- gDevTools.showToolbox(target, "scratchpad", "window").then(runTests);
+ let target = TargetFactory.forTab(gBrowser.selectedTab);
+ let toolbox = yield gDevTools.showToolbox(target, "scratchpad", "window");
+
+ let scratchpadPanel = toolbox.getPanel("scratchpad");
+ let { scratchpad } = scratchpadPanel;
+ is(toolbox.getCurrentPanel(), scratchpadPanel,
+ "Scratchpad is currently selected panel");
+
+ info("Switching to webconsole panel");
+
+ let webconsolePanel = yield toolbox.selectTool("webconsole");
+ let { hud } = webconsolePanel;
+ is(toolbox.getCurrentPanel(), webconsolePanel,
+ "Webconsole is currently selected panel");
+
+ info("console.log()ing from Scratchpad");
+
+ scratchpad.setText("console.log('foobar-from-scratchpad')");
+ scratchpad.run();
+ let messages = yield waitForMessages({
+ webconsole: hud,
+ messages: [{ text: "foobar-from-scratchpad" }]
});
-}
-function runTests(aToolbox)
-{
- Task.spawn(function*() {
- let scratchpadPanel = aToolbox.getPanel("scratchpad");
- let { scratchpad } = scratchpadPanel;
- is(aToolbox.getCurrentPanel(), scratchpadPanel,
- "Scratchpad is currently selected panel");
+ info("Clicking link to switch to and focus Scratchpad");
- info("Switching to webconsole panel");
+ let [matched] = [...messages[0].matched];
+ ok(matched, "Found logged message from Scratchpad");
+ let anchor = matched.querySelector("a.message-location");
- let webconsolePanel = yield aToolbox.selectTool("webconsole");
- let { hud } = webconsolePanel;
- is(aToolbox.getCurrentPanel(), webconsolePanel,
- "Webconsole is currently selected panel");
+ toolbox.on("scratchpad-selected", function selected() {
+ toolbox.off("scratchpad-selected", selected);
- info("console.log()ing from Scratchpad");
+ is(toolbox.getCurrentPanel(), scratchpadPanel,
+ "Clicking link switches to Scratchpad panel");
- scratchpad.setText("console.log('foobar-from-scratchpad')");
- scratchpad.run();
- let messages = yield waitForMessages({
- webconsole: hud,
- messages: [{ text: "foobar-from-scratchpad" }]
- });
+ is(Services.ww.activeWindow, toolbox.frame.ownerGlobal,
+ "Scratchpad's toolbox is focused");
- info("Clicking link to switch to and focus Scratchpad");
-
- let [matched] = [...messages[0].matched];
- ok(matched, "Found logged message from Scratchpad");
- let anchor = matched.querySelector("a.message-location");
-
- aToolbox.on("scratchpad-selected", function selected() {
- aToolbox.off("scratchpad-selected", selected);
-
- is(aToolbox.getCurrentPanel(), scratchpadPanel,
- "Clicking link switches to Scratchpad panel");
-
- is(Services.ww.activeWindow, aToolbox.frame.ownerGlobal,
- "Scratchpad's toolbox is focused");
-
- Tools.scratchpad.isTargetSupported = isTargetSupported;
- finish();
- });
-
- EventUtils.synthesizeMouse(anchor, 2, 2, {}, hud.iframeWindow);
+ Tools.scratchpad.isTargetSupported = isTargetSupported;
+ finish();
});
-}
+
+ EventUtils.synthesizeMouse(anchor, 2, 2, {}, hud.iframeWindow);
+});
diff --git a/browser/devtools/webconsole/test/head.js b/browser/devtools/webconsole/test/head.js
index e842f3ad93de..faae30b19a8d 100644
--- a/browser/devtools/webconsole/test/head.js
+++ b/browser/devtools/webconsole/test/head.js
@@ -319,8 +319,7 @@ let finishTest = Task.async(function* () {
finish();
});
-function tearDown()
-{
+registerCleanupFunction(function*() {
gDevTools.testing = false;
dumpConsoles();
@@ -330,14 +329,12 @@ function tearDown()
}
let target = TargetFactory.forTab(gBrowser.selectedTab);
- gDevTools.closeToolbox(target);
+ yield gDevTools.closeToolbox(target);
while (gBrowser.tabs.length > 1) {
gBrowser.removeCurrentTab();
}
-}
-
-registerCleanupFunction(tearDown);
+});
waitForExplicitFinish();
diff --git a/browser/devtools/webide/webide-prefs.js b/browser/devtools/webide/webide-prefs.js
index 349f16fd4ca1..73a674449e90 100644
--- a/browser/devtools/webide/webide-prefs.js
+++ b/browser/devtools/webide/webide-prefs.js
@@ -6,11 +6,7 @@
pref("devtools.webide.showProjectEditor", true);
pref("devtools.webide.templatesURL", "https://code.cdn.mozilla.net/templates/list.json");
pref("devtools.webide.autoinstallADBHelper", true);
-#ifdef MOZ_DEV_EDITION
pref("devtools.webide.autoinstallFxdtAdapters", true);
-#else
-pref("devtools.webide.autoinstallFxdtAdapters", false);
-#endif
pref("devtools.webide.autoConnectRuntime", true);
pref("devtools.webide.restoreLastProject", true);
pref("devtools.webide.enableLocalRuntime", false);
diff --git a/browser/locales/en-US/chrome/browser/devtools/netmonitor.dtd b/browser/locales/en-US/chrome/browser/devtools/netmonitor.dtd
index 66a740b83a03..4a558e0d9b9f 100644
--- a/browser/locales/en-US/chrome/browser/devtools/netmonitor.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/netmonitor.dtd
@@ -42,8 +42,14 @@
- in the network table toolbar, above the "type" column. -->
+
+
+
+ - in the network table toolbar, above the "size" column, which is the
+ - uncompressed / decoded size. -->
+
diff --git a/browser/themes/windows/jar.mn b/browser/themes/windows/jar.mn
index ef1711a6b7f4..853995c6f666 100644
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -88,6 +88,7 @@ browser.jar:
skin/classic/browser/search-engine-placeholder.png (../shared/search/search-engine-placeholder.png)
skin/classic/browser/badge-add-engine.png (../shared/search/badge-add-engine.png)
skin/classic/browser/search-indicator-badge-add.png (../shared/search/search-indicator-badge-add.png)
+ skin/classic/browser/search-history-icon.svg (../shared/search/history-icon.svg)
skin/classic/browser/Secure24.png
skin/classic/browser/setDesktopBackground.css
skin/classic/browser/slowStartup-16.png
@@ -540,6 +541,7 @@ browser.jar:
skin/classic/aero/browser/search-engine-placeholder.png (../shared/search/search-engine-placeholder.png)
skin/classic/aero/browser/badge-add-engine.png (../shared/search/badge-add-engine.png)
skin/classic/aero/browser/search-indicator-badge-add.png (../shared/search/search-indicator-badge-add.png)
+ skin/classic/aero/browser/search-history-icon.svg (../shared/search/history-icon.svg)
skin/classic/aero/browser/Secure24.png (Secure24-aero.png)
skin/classic/aero/browser/setDesktopBackground.css
skin/classic/aero/browser/slowStartup-16.png
diff --git a/browser/themes/windows/preferences/search.css b/browser/themes/windows/preferences/search.css
index 729948c9bcb2..f66507a501bc 100644
--- a/browser/themes/windows/preferences/search.css
+++ b/browser/themes/windows/preferences/search.css
@@ -6,6 +6,12 @@
height: 16px;
}
+/* Reserve space so that localized labels can wrap without hiding the
+ 'add more engines' link at the bottom. See bug 1112688. */
+#oneClickSearchProvidersGroup {
+ margin-bottom: 3em;
+}
+
#engineList {
margin: .5em 6px;
}
diff --git a/browser/themes/windows/searchbar.css b/browser/themes/windows/searchbar.css
index ff8961e96e8e..c6073ea0ed8d 100644
--- a/browser/themes/windows/searchbar.css
+++ b/browser/themes/windows/searchbar.css
@@ -242,10 +242,27 @@ searchbar[oneoffui] .searchbar-engine-button {
}
.search-panel-tree > .autocomplete-treebody::-moz-tree-cell {
- -moz-padding-start: 15px;
border-top: none !important;
}
+.search-panel-tree > .autocomplete-treebody::-moz-tree-cell-text {
+ -moz-padding-start: 4px;
+}
+
+.search-panel-tree > .autocomplete-treebody::-moz-tree-image {
+ -moz-padding-start: 5px;
+ width: 14px;
+ height: 14px;
+}
+
+.search-panel-tree > .autocomplete-treebody::-moz-tree-image(fromhistory) {
+ list-style-image: url("chrome://browser/skin/search-history-icon.svg#search-history-icon");
+}
+
+.search-panel-tree > .autocomplete-treebody::-moz-tree-image(fromhistory, selected) {
+ list-style-image: url("chrome://browser/skin/search-history-icon.svg#search-history-icon-active");
+}
+
searchbar[oneoffui] .searchbar-engine-image {
-moz-margin-start: -1px;
}
diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java
index 5f7bc160381a..e9f9abb0d6be 100644
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -258,171 +258,6 @@ public class BrowserApp extends GeckoApp
private TilesRecorder mTilesRecorder;
- private DragHelper mDragHelper;
-
- private class DragHelper implements OuterLayout.DragCallback {
- private int[] mToolbarLocation = new int[2]; // to avoid creation every time we need to check for toolbar location.
- // When dragging horizontally, the area of mainlayout between left drag bound and right drag bound can
- // be dragged. A touch on the right of that area will automatically close the view.
- private int mStatusBarHeight;
-
- public DragHelper() {
- // If a layout round happens from the root, the offset placed by viewdraghelper gets forgotten and
- // main layout gets replaced to offset 0.
- ((MainLayout) mMainLayout).setLayoutInterceptor(new LayoutInterceptor() {
- @Override
- public void onLayout() {
- if (mRootLayout.isMoving()) {
- mRootLayout.restoreTargetViewPosition();
- }
- }
- });
- }
-
- @Override
- public void onDragProgress(float progress) {
- mBrowserToolbar.setToolBarButtonsAlpha(1.0f - progress);
- mTabsPanel.translateInRange(progress);
- }
-
- @Override
- public View getViewToDrag() {
- return mMainLayout;
- }
-
- /**
- * Since pressing the tabs button slides the main layout, whereas draghelper changes its offset, here we
- * restore the position of mainlayout as if it was opened by pressing the button. This allows the closing
- * mechanism to work.
- */
- @Override
- public void startDrag(boolean wasOpen) {
- if (wasOpen) {
- mTabsPanel.setHWLayerEnabled(true);
- mMainLayout.offsetTopAndBottom(getDragRange());
- mMainLayout.scrollTo(0, 0);
- } else {
- prepareTabsToShow();
- mBrowserToolbar.hideVirtualKeyboard();
- }
- mBrowserToolbar.setContextMenuEnabled(false);
- }
-
- @Override
- public void stopDrag(boolean stoppingToOpen) {
- if (stoppingToOpen) {
- mTabsPanel.setHWLayerEnabled(false);
- mMainLayout.offsetTopAndBottom(-getDragRange());
- mMainLayout.scrollTo(0, -getDragRange());
- } else {
- mTabsPanel.hideImmediately();
- mTabsPanel.setHWLayerEnabled(false);
- }
- // Re-enabling context menu only while stopping to close.
- if (stoppingToOpen) {
- mBrowserToolbar.setContextMenuEnabled(false);
- } else {
- mBrowserToolbar.setContextMenuEnabled(true);
- }
- }
-
- @Override
- public int getDragRange() {
- return mTabsPanel.getVerticalPanelHeight();
- }
-
- @Override
- public int getOrderedChildIndex(int index) {
- // See ViewDragHelper's findTopChildUnder method. ViewDragHelper looks for the topmost view in z order
- // to understand what needs to be dragged. Here we are tampering Toast's index in case it's hidden,
- // otherwise draghelper would try to drag it.
- int mainLayoutIndex = mRootLayout.indexOfChild(mMainLayout);
- if (index > mainLayoutIndex && (mToast == null || !mToast.isVisible())) {
- return mainLayoutIndex;
- } else {
- return index;
- }
- }
-
- @Override
- public boolean canDrag(MotionEvent event) {
- // if no current tab is active.
- if (Tabs.getInstance().getSelectedTab() == null) {
- return false;
- }
-
- // currently disabled for tablets.
- if (HardwareUtils.isTablet()) {
- return false;
- }
-
- // not enabled in editing mode.
- if (mBrowserToolbar.isEditing()) {
- return false;
- }
-
- return isInToolbarBounds((int) event.getRawY());
- }
-
- @Override
- public boolean canInterceptEventWhileOpen(MotionEvent event) {
- if (event.getActionMasked() != MotionEvent.ACTION_DOWN) {
- return false;
- }
-
- // Need to check if are intercepting a touch on main layout since we might hit a visible toast.
- if (mRootLayout.findTopChildUnder(event) == mMainLayout &&
- isInToolbarBounds((int) event.getRawY())) {
- return true;
- }
- return false;
- }
-
- private boolean isInToolbarBounds(int y) {
- mBrowserToolbar.getLocationOnScreen(mToolbarLocation);
- final int upperLimit = mToolbarLocation[1] + mBrowserToolbar.getMeasuredHeight();
- final int lowerLimit = mToolbarLocation[1];
- return (y > lowerLimit && y < upperLimit);
- }
-
- public void prepareTabsToShow() {
- if (ensureTabsPanelExists()) {
- // If we've just inflated the tabs panel, only show it once the current
- // layout pass is done to avoid displayed temporary UI states during
- // relayout.
- final ViewTreeObserver vto = mTabsPanel.getViewTreeObserver();
- if (vto.isAlive()) {
- vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- mTabsPanel.getViewTreeObserver().removeGlobalOnLayoutListener(this);
- prepareTabsToShow();
- }
- });
- }
- } else {
- mTabsPanel.prepareToDrag();
- }
- }
-
- public int getLowerLimit() {
- return getStatusBarHeight();
- }
-
- private int getStatusBarHeight() {
- if (mStatusBarHeight != 0) {
- return mStatusBarHeight;
- }
- final int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
- if (resourceId > 0) {
- mStatusBarHeight = getResources().getDimensionPixelSize(resourceId);
- return mStatusBarHeight;
- }
- Log.e(LOGTAG, "Unable to find statusbar height");
- return 0;
- }
- }
-
@Override
public View onCreateView(final String name, final Context context, final AttributeSet attrs) {
final View view;
@@ -813,9 +648,6 @@ public class BrowserApp extends GeckoApp
}
});
- mDragHelper = new DragHelper();
- mRootLayout.setDraggableCallback(mDragHelper);
-
// Set the maximum bits-per-pixel the favicon system cares about.
IconDirectoryEntry.setMaxBPP(GeckoAppShell.getScreenDepth());
@@ -1532,7 +1364,6 @@ public class BrowserApp extends GeckoApp
invalidateOptionsMenu();
if (mTabsPanel != null) {
- mRootLayout.reset();
updateSideBarState();
mTabsPanel.refresh();
}
@@ -1556,10 +1387,6 @@ public class BrowserApp extends GeckoApp
});
}
- private boolean isSideBar() {
- return (HardwareUtils.isTablet() && getOrientation() == Configuration.ORIENTATION_LANDSCAPE);
- }
-
private void updateSideBarState() {
if (NewTabletUI.isEnabled(this)) {
return;
@@ -1568,7 +1395,7 @@ public class BrowserApp extends GeckoApp
if (mMainLayoutAnimator != null)
mMainLayoutAnimator.stop();
- boolean isSideBar = isSideBar();
+ boolean isSideBar = (HardwareUtils.isTablet() && getOrientation() == Configuration.ORIENTATION_LANDSCAPE);
final int sidebarWidth = getResources().getDimensionPixelSize(R.dimen.tabs_sidebar_width);
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mTabsPanel.getLayoutParams();
@@ -1580,7 +1407,6 @@ public class BrowserApp extends GeckoApp
mMainLayout.scrollTo(mainLayoutScrollX, 0);
mTabsPanel.setIsSideBar(isSideBar);
- mRootLayout.updateDragHelperParameters();
}
@Override
@@ -1891,7 +1717,7 @@ public class BrowserApp extends GeckoApp
@Override
public void onGlobalLayout() {
mTabsPanel.getViewTreeObserver().removeGlobalOnLayoutListener(this);
- showTabs(panel);
+ mTabsPanel.show(panel);
}
});
}
@@ -1980,13 +1806,10 @@ public class BrowserApp extends GeckoApp
if (!areTabsShown()) {
mTabsPanel.setVisibility(View.INVISIBLE);
mTabsPanel.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
- mRootLayout.setClosed();
- mBrowserToolbar.setContextMenuEnabled(true);
} else {
// Cancel editing mode to return to page content when the TabsPanel closes. We cancel
// it here because there are graphical glitches if it's canceled while it's visible.
mBrowserToolbar.cancelEdit();
- mRootLayout.setOpen();
}
mTabsPanel.finishTabsAnimation();
diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java
index 7712322b9264..3a86ac7e479c 100644
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -160,9 +160,8 @@ public abstract class GeckoApp
// after a version upgrade.
private static final int CLEANUP_DEFERRAL_SECONDS = 15;
- protected OuterLayout mRootLayout;
+ protected RelativeLayout mRootLayout;
protected RelativeLayout mMainLayout;
-
protected RelativeLayout mGeckoLayout;
private View mCameraView;
private OrientationEventListener mCameraOrientationEventListener;
@@ -1278,7 +1277,7 @@ public abstract class GeckoApp
setContentView(getLayout());
// Set up Gecko layout.
- mRootLayout = (OuterLayout) findViewById(R.id.root_layout);
+ mRootLayout = (RelativeLayout) findViewById(R.id.root_layout);
mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout);
mMainLayout = (RelativeLayout) findViewById(R.id.main_layout);
@@ -2404,24 +2403,11 @@ public abstract class GeckoApp
public static class MainLayout extends RelativeLayout {
private TouchEventInterceptor mTouchEventInterceptor;
private MotionEventInterceptor mMotionEventInterceptor;
- private LayoutInterceptor mLayoutInterceptor;
public MainLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- if (mLayoutInterceptor != null) {
- mLayoutInterceptor.onLayout();
- }
- }
-
- public void setLayoutInterceptor(LayoutInterceptor interceptor) {
- mLayoutInterceptor = interceptor;
- }
-
public void setTouchEventInterceptor(TouchEventInterceptor interceptor) {
mTouchEventInterceptor = interceptor;
}
diff --git a/mobile/android/base/GeckoInputConnection.java b/mobile/android/base/GeckoInputConnection.java
index 26bc262f3567..691bcc24f3d5 100644
--- a/mobile/android/base/GeckoInputConnection.java
+++ b/mobile/android/base/GeckoInputConnection.java
@@ -622,9 +622,12 @@ class GeckoInputConnection
outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
else if (mIMEModeHint.equalsIgnoreCase("titlecase"))
outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_WORDS;
+ else if (mIMETypeHint.equalsIgnoreCase("text") &&
+ !mIMEModeHint.equalsIgnoreCase("autocapitalized"))
+ outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_NORMAL;
else if (!mIMEModeHint.equalsIgnoreCase("lowercase"))
outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
- // auto-capitalized mode is the default
+ // auto-capitalized mode is the default for types other than text
}
if (mIMEActionHint.equalsIgnoreCase("go"))
diff --git a/mobile/android/base/LayoutInterceptor.java b/mobile/android/base/LayoutInterceptor.java
deleted file mode 100644
index be80ea57521f..000000000000
--- a/mobile/android/base/LayoutInterceptor.java
+++ /dev/null
@@ -1,11 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-
-package org.mozilla.gecko;
-
-public interface LayoutInterceptor {
- public void onLayout();
-}
diff --git a/mobile/android/base/OuterLayout.java b/mobile/android/base/OuterLayout.java
deleted file mode 100644
index d8b0e8d302b1..000000000000
--- a/mobile/android/base/OuterLayout.java
+++ /dev/null
@@ -1,254 +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/. */
-package org.mozilla.gecko;
-
-import android.content.Context;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.widget.ViewDragHelper;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.RelativeLayout;
-
-/* Outerlayout is the container layout of all the main views. It allows mainlayout to be dragged while targeting
- the toolbar and it's responsible for handling the dragprocess. It relies on ViewDragHelper to ease the drag process.
- */
-public class OuterLayout extends RelativeLayout {
- private final double AUTO_OPEN_SPEED_LIMIT = 800.0;
- private ViewDragHelper mDragHelper;
- private int mDraggingBorder;
- private int mDragRange;
- private boolean mIsOpen = false;
- private int mDraggingState = ViewDragHelper.STATE_IDLE;
- private DragCallback mDragCallback;
-
- public static interface DragCallback {
- public void startDrag(boolean wasOpen);
- public void stopDrag(boolean stoppingToOpen);
- public int getDragRange();
- public int getOrderedChildIndex(int index);
- public boolean canDrag(MotionEvent event);
- public boolean canInterceptEventWhileOpen(MotionEvent event);
- public void onDragProgress(float progress);
- public View getViewToDrag();
- public int getLowerLimit();
- }
-
- private class DragHelperCallback extends ViewDragHelper.Callback {
- @Override
- public void onViewDragStateChanged(int newState) {
- if (newState == mDraggingState) { // no change
- return;
- }
-
- // if the view stopped moving.
- if ((mDraggingState == ViewDragHelper.STATE_DRAGGING || mDraggingState == ViewDragHelper.STATE_SETTLING) &&
- newState == ViewDragHelper.STATE_IDLE) {
-
- final float rangeToCheck = mDragRange;
- final float lowerLimit = mDragCallback.getLowerLimit();
- if (mDraggingBorder == lowerLimit) {
- mIsOpen = false;
- mDragCallback.onDragProgress(0);
- } else if (mDraggingBorder == rangeToCheck) {
- mIsOpen = true;
- mDragCallback.onDragProgress(1);
- }
- mDragCallback.stopDrag(mIsOpen);
- }
-
- // The view was previuosly moving.
- if (newState == ViewDragHelper.STATE_DRAGGING && !isMoving()) {
- mDragCallback.startDrag(mIsOpen);
- updateRanges();
- }
-
- mDraggingState = newState;
- }
-
- @Override
- public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
- mDraggingBorder = top;
- final float progress = Math.min(1, ((float) top) / mDragRange);
- mDragCallback.onDragProgress(progress);
- }
-
- @Override
- public int getViewVerticalDragRange(View child) {
- return mDragRange;
- }
-
- @Override
- public int getOrderedChildIndex(int index) {
- return mDragCallback.getOrderedChildIndex(index);
- }
-
- @Override
- public boolean tryCaptureView(View view, int i) {
- return (view.getId() == mDragCallback.getViewToDrag().getId());
- }
-
- @Override
- public int clampViewPositionVertical(View child, int top, int dy) {
- return top;
- }
-
- @Override
- public void onViewReleased(View releasedChild, float xvel, float yvel) {
- final float rangeToCheck = mDragRange;
- final float speedToCheck = yvel;
-
- if (mDraggingBorder == mDragCallback.getLowerLimit()) {
- return;
- }
-
- if (mDraggingBorder == rangeToCheck) {
- return;
- }
-
- boolean settleToOpen = false;
- // Speed has priority over position.
- if (speedToCheck > AUTO_OPEN_SPEED_LIMIT) {
- settleToOpen = true;
- } else if (speedToCheck < -AUTO_OPEN_SPEED_LIMIT) {
- settleToOpen = false;
- } else if (mDraggingBorder > rangeToCheck / 2) {
- settleToOpen = true;
- } else if (mDraggingBorder < rangeToCheck / 2) {
- settleToOpen = false;
- }
-
- final int settleDestX;
- final int settleDestY;
- if (settleToOpen) {
- settleDestX = 0;
- settleDestY = mDragRange;
- } else {
- settleDestX = 0;
- settleDestY = mDragCallback.getLowerLimit();
- }
-
- if(mDragHelper.settleCapturedViewAt(settleDestX, settleDestY)) {
- ViewCompat.postInvalidateOnAnimation(OuterLayout.this);
- }
- }
- }
-
- public OuterLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- private void updateRanges() {
- // Need to wait for the tabs to show in order to fetch the right sizes.
- mDragRange = mDragCallback.getDragRange() + mDragCallback.getLowerLimit();
- }
-
- private void updateOrientation() {
- mDragHelper.setEdgeTrackingEnabled(0);
- }
-
- @Override
- protected void onFinishInflate() {
- mDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback());
- mIsOpen = false;
- super.onFinishInflate();
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- if (mDragCallback.canDrag(event)) {
- if (mDragHelper.shouldInterceptTouchEvent(event)) {
- return true;
- }
- }
-
- // Because while open the target layout is translated and draghelper does not catch it.
- if (mIsOpen && mDragCallback.canInterceptEventWhileOpen(event)) {
- return true;
- }
- return false;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- // touch events can be passed to the helper if we target the toolbar or we are already dragging.
- if (mDragCallback.canDrag(event) || mDraggingState == ViewDragHelper.STATE_DRAGGING) {
- mDragHelper.processTouchEvent(event);
- }
- return true;
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- // The first time fennec is started, tabs might not have been created while we drag. In that case we need
- // an arbitrary range to start dragging that will be updated as soon as the tabs are created.
-
- if (mDragRange == 0) {
- mDragRange = h / 2;
- }
- super.onSizeChanged(w, h, oldw, oldh);
- }
-
- @Override
- public void computeScroll() { // needed for automatic settling.
- if (mDragHelper.continueSettling(true)) {
- ViewCompat.postInvalidateOnAnimation(this);
- }
- }
-
- /**
- * To be called when closing the tabs from outside (i.e. when touching the main layout).
- */
- public void setClosed() {
- mIsOpen = false;
- mDragHelper.abort();
- }
-
- /**
- * To be called when opening the tabs from outside (i.e. when clicking on the tabs button).
- */
- public void setOpen() {
- mIsOpen = true;
- mDragHelper.abort();
- }
-
- public void setDraggableCallback(DragCallback dragCallback) {
- mDragCallback = dragCallback;
- updateOrientation();
- }
-
- // If a change happens while we are dragging, we abort the dragging and set to open state.
- public void reset() {
- updateOrientation();
- if (isMoving()) {
- mDragHelper.abort();
- if (mDragCallback != null) {
- mDragCallback.stopDrag(false);
- mDragCallback.onDragProgress(0f);
- }
- }
- }
-
- public void updateDragHelperParameters() {
- mDragRange = mDragCallback.getDragRange() + mDragCallback.getLowerLimit();
- updateOrientation();
- }
-
- public boolean isMoving() {
- return (mDraggingState == ViewDragHelper.STATE_DRAGGING ||
- mDraggingState == ViewDragHelper.STATE_SETTLING);
- }
-
- public boolean isOpen() {
- return mIsOpen;
- }
-
- public View findTopChildUnder(MotionEvent event) {
- return mDragHelper.findTopChildUnder((int) event.getX(), (int) event.getY());
- }
-
- public void restoreTargetViewPosition() {
- mDragCallback.getViewToDrag().offsetTopAndBottom(mDraggingBorder);
- }
-}
diff --git a/mobile/android/base/Tab.java b/mobile/android/base/Tab.java
index e4538b582a90..f438d3dbbfea 100644
--- a/mobile/android/base/Tab.java
+++ b/mobile/android/base/Tab.java
@@ -53,7 +53,6 @@ public class Tab {
private boolean mHasFeeds;
private boolean mHasOpenSearch;
private final SiteIdentity mSiteIdentity;
- private boolean mReaderEnabled;
private BitmapDrawable mThumbnail;
private final int mParentId;
private final boolean mExternal;
@@ -262,10 +261,6 @@ public class Tab {
return mSiteIdentity;
}
- public boolean getReaderEnabled() {
- return mReaderEnabled;
- }
-
public boolean isBookmark() {
return mBookmark;
}
@@ -474,11 +469,6 @@ public class Tab {
mSiteIdentity.update(identityData);
}
- public void setReaderEnabled(boolean readerEnabled) {
- mReaderEnabled = readerEnabled;
- Tabs.getInstance().notifyListeners(this, Tabs.TabEvents.MENU_UPDATED);
- }
-
void updateBookmark() {
if (getURL() == null) {
return;
@@ -529,7 +519,7 @@ public class Tab {
public void toggleReaderMode() {
if (AboutPages.isAboutReader(mUrl)) {
Tabs.getInstance().loadUrl(ReaderModeUtils.getUrlFromAboutReader(mUrl));
- } else if (mReaderEnabled) {
+ } else {
mEnteringReaderMode = true;
Tabs.getInstance().loadUrl(ReaderModeUtils.getAboutReaderForUrl(mUrl, mId));
}
@@ -614,7 +604,6 @@ public class Tab {
setHasFeeds(false);
setHasOpenSearch(false);
updateIdentityData(null);
- setReaderEnabled(false);
setZoomConstraints(new ZoomConstraints(true));
setHasTouchListeners(false);
setBackgroundColor(DEFAULT_BACKGROUND_COLOR);
@@ -632,7 +621,6 @@ public class Tab {
setLoadProgress(LOAD_PROGRESS_START);
setState((!restoring && shouldShowProgress(url)) ? STATE_LOADING : STATE_SUCCESS);
updateIdentityData(null);
- setReaderEnabled(false);
}
void handleDocumentStop(boolean success) {
diff --git a/mobile/android/base/Tabs.java b/mobile/android/base/Tabs.java
index 311650259c76..3f8e17d5892c 100644
--- a/mobile/android/base/Tabs.java
+++ b/mobile/android/base/Tabs.java
@@ -92,7 +92,6 @@ public class Tabs implements GeckoEventListener {
"Tab:Select",
"Content:LocationChange",
"Content:SecurityChange",
- "Content:ReaderEnabled",
"Content:StateChange",
"Content:LoadError",
"Content:PageShow",
@@ -455,9 +454,6 @@ public class Tabs implements GeckoEventListener {
} else if (event.equals("Content:SecurityChange")) {
tab.updateIdentityData(message.getJSONObject("identity"));
notifyListeners(tab, TabEvents.SECURITY_CHANGE);
- } else if (event.equals("Content:ReaderEnabled")) {
- tab.setReaderEnabled(true);
- notifyListeners(tab, TabEvents.READER_ENABLED);
} else if (event.equals("Content:StateChange")) {
int state = message.getInt("state");
if ((state & GeckoAppShell.WPL_STATE_IS_NETWORK) != 0) {
@@ -577,7 +573,6 @@ public class Tabs implements GeckoEventListener {
PAGE_SHOW,
LINK_FEED,
SECURITY_CHANGE,
- READER_ENABLED,
DESKTOP_MODE_CHANGE,
VIEWPORT_CHANGE,
RECORDING_CHANGE,
diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build
index e55e53be7c17..f57ec594d3ca 100644
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -326,7 +326,6 @@ gbjar.sources += [
'InputMethods.java',
'IntentHelper.java',
'JavaAddonManager.java',
- 'LayoutInterceptor.java',
'LocaleManager.java',
'Locales.java',
'lwt/LightweightTheme.java',
@@ -350,7 +349,6 @@ gbjar.sources += [
'NotificationService.java',
'NSSBridge.java',
'OrderedBroadcastHelper.java',
- 'OuterLayout.java',
'preferences/AlignRightLinkPreference.java',
'preferences/AndroidImport.java',
'preferences/AndroidImportPreference.java',
diff --git a/mobile/android/base/resources/layout/gecko_app.xml b/mobile/android/base/resources/layout/gecko_app.xml
index 49edbacae7d8..8f5e552ad229 100644
--- a/mobile/android/base/resources/layout/gecko_app.xml
+++ b/mobile/android/base/resources/layout/gecko_app.xml
@@ -3,7 +3,7 @@
- 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/. -->
-
-
+
diff --git a/mobile/android/base/resources/values/dimens.xml b/mobile/android/base/resources/values/dimens.xml
index dbe8444789ac..f389fbf074cc 100644
--- a/mobile/android/base/resources/values/dimens.xml
+++ b/mobile/android/base/resources/values/dimens.xml
@@ -188,8 +188,6 @@
2dp2dp
- 256dp
-
5dip12dip
diff --git a/mobile/android/base/strings.xml.in b/mobile/android/base/strings.xml.in
index 1a5a0eea6e1a..b05bc8fd45af 100644
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -8,9 +8,7 @@
#includesubst @BRANDPATH@
#includesubst @STRINGSPATH@
#includesubst @SYNCSTRINGSPATH@
-#ifdef MOZ_ANDROID_SEARCH_ACTIVITY
#includesubst @SEARCHSTRINGSPATH@
-#endif
@@ -33,9 +31,7 @@
@MOZ_ANDROID_SHARED_FXACCOUNT_TYPE@@ANDROID_PACKAGE_NAME@
-#ifdef MOZ_ANDROID_SEARCH_ACTIVITY
#include ../search/strings/search_strings.xml.in
-#endif
#include ../services/strings.xml.in
&no_space_to_start_error;
diff --git a/mobile/android/base/tabs/TabsPanel.java b/mobile/android/base/tabs/TabsPanel.java
index 950dd1009c09..6977b4674a2a 100644
--- a/mobile/android/base/tabs/TabsPanel.java
+++ b/mobile/android/base/tabs/TabsPanel.java
@@ -14,8 +14,6 @@ import org.mozilla.gecko.R;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.animation.PropertyAnimator;
-import org.mozilla.gecko.Tab;
-import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.animation.ViewHelper;
import org.mozilla.gecko.lwt.LightweightTheme;
import org.mozilla.gecko.lwt.LightweightThemeDrawable;
@@ -395,40 +393,15 @@ public class TabsPanel extends LinearLayout
}
public void show(Panel panelToShow) {
- final boolean showAnimation = !mVisible;
- prepareToShow(panelToShow);
- if (isSideBar()) {
- if (showAnimation) {
- dispatchLayoutChange(getWidth(), getHeight());
- }
- } else {
- int height = getVerticalPanelHeight();
- dispatchLayoutChange(getWidth(), height);
- }
- }
-
- public void prepareToDrag() {
- Tab selectedTab = Tabs.getInstance().getSelectedTab();
- if (selectedTab != null && selectedTab.isPrivate()) {
- prepareToShow(TabsPanel.Panel.PRIVATE_TABS);
- } else {
- prepareToShow(TabsPanel.Panel.NORMAL_TABS);
- }
- if (mIsSideBar) {
- prepareSidebarAnimation(getWidth());
- }
- }
-
- public void prepareToShow(Panel panelToShow) {
- if (!isShown()) {
+ if (!isShown())
setVisibility(View.VISIBLE);
- }
if (mPanel != null) {
// Hide the old panel.
mPanel.hide();
}
+ final boolean showAnimation = !mVisible;
mVisible = true;
mCurrentPanel = panelToShow;
@@ -458,20 +431,20 @@ public class TabsPanel extends LinearLayout
if (!HardwareUtils.hasMenuButton()) {
mMenuButton.setVisibility(View.VISIBLE);
mMenuButton.setEnabled(true);
+ mPopupMenu.setAnchor(mMenuButton);
} else {
mPopupMenu.setAnchor(mAddTab);
}
- }
- public void hideImmediately() {
- mVisible = false;
- setVisibility(View.INVISIBLE);
- }
-
- public int getVerticalPanelHeight() {
- final int actionBarHeight = mContext.getResources().getDimensionPixelSize(R.dimen.browser_toolbar_height);
- final int height = actionBarHeight + getTabContainerHeight(mTabsContainer);
- return height;
+ if (isSideBar()) {
+ if (showAnimation)
+ dispatchLayoutChange(getWidth(), getHeight());
+ } else {
+ int actionBarHeight = mContext.getResources().getDimensionPixelSize(R.dimen.browser_toolbar_height);
+ int height = actionBarHeight + getTabContainerHeight(mTabsContainer);
+ dispatchLayoutChange(getWidth(), height);
+ }
+ mHeaderVisible = true;
}
public void hide() {
@@ -515,28 +488,6 @@ public class TabsPanel extends LinearLayout
return mCurrentPanel;
}
- public void setHWLayerEnabled(boolean enabled) {
- if (Versions.preHC) {
- return;
- }
- if (enabled) {
- mHeader.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- mTabsContainer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- } else {
- mHeader.setLayerType(View.LAYER_TYPE_NONE, null);
- mTabsContainer.setLayerType(View.LAYER_TYPE_NONE, null);
- }
- }
-
- public void prepareSidebarAnimation(int tabsPanelWidth) {
- if (mVisible) {
- ViewHelper.setTranslationX(mHeader, -tabsPanelWidth);
- ViewHelper.setTranslationX(mTabsContainer, -tabsPanelWidth);
- // The footer view is only present on the sidebar, v11+.
- ViewHelper.setTranslationX(mFooter, -tabsPanelWidth);
- }
- }
-
public void prepareTabsAnimation(PropertyAnimator animator) {
// Not worth doing this on pre-Honeycomb without proper
// hardware accelerated animations.
@@ -546,7 +497,13 @@ public class TabsPanel extends LinearLayout
if (mIsSideBar) {
final int tabsPanelWidth = getWidth();
- prepareSidebarAnimation(tabsPanelWidth);
+ if (mVisible) {
+ ViewHelper.setTranslationX(mHeader, -tabsPanelWidth);
+ ViewHelper.setTranslationX(mTabsContainer, -tabsPanelWidth);
+
+ // The footer view is only present on the sidebar, v11+.
+ ViewHelper.setTranslationX(mFooter, -tabsPanelWidth);
+ }
final int translationX = (mVisible ? 0 : -tabsPanelWidth);
animator.attach(mTabsContainer, PropertyAnimator.Property.TRANSLATION_X, translationX);
animator.attach(mHeader, PropertyAnimator.Property.TRANSLATION_X, translationX);
@@ -566,25 +523,8 @@ public class TabsPanel extends LinearLayout
animator.attach(mHeader, PropertyAnimator.Property.TRANSLATION_Y, translationY);
}
- setHWLayerEnabled(true);
- }
-
- public void translateInRange(float progress) {
- final Resources resources = getContext().getResources();
- if (!mIsSideBar) {
- final int toolbarHeight = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height);
- final int translationY = (int) - ((1 - progress) * toolbarHeight);
- ViewHelper.setTranslationY(mHeader, translationY);
- ViewHelper.setTranslationY(mTabsContainer, translationY);
- mTabsContainer.setAlpha(progress);
- } else {
- final int tabsPanelWidth = getWidth();
- prepareSidebarAnimation(tabsPanelWidth);
- final int translationX = (int) - ((1 - progress) * tabsPanelWidth);
- ViewHelper.setTranslationX(mHeader, translationX);
- ViewHelper.setTranslationX(mTabsContainer, translationX);
- ViewHelper.setTranslationX(mFooter, translationX);
- }
+ mHeader.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mTabsContainer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
public void finishTabsAnimation() {
@@ -592,9 +532,10 @@ public class TabsPanel extends LinearLayout
return;
}
- setHWLayerEnabled(false);
+ mHeader.setLayerType(View.LAYER_TYPE_NONE, null);
+ mTabsContainer.setLayerType(View.LAYER_TYPE_NONE, null);
- // If the tray is now hidden, call hide() on current panel and unset it as the current panel
+ // If the tabs panel is now hidden, call hide() on current panel and unset it as the current panel
// to avoid hide() being called again when the layout is opened next.
if (!mVisible && mPanel != null) {
mPanel.hide();
diff --git a/mobile/android/base/toolbar/BrowserToolbar.java b/mobile/android/base/toolbar/BrowserToolbar.java
index 2a5e9d92827b..3adfb5d09e35 100644
--- a/mobile/android/base/toolbar/BrowserToolbar.java
+++ b/mobile/android/base/toolbar/BrowserToolbar.java
@@ -141,7 +141,6 @@ public abstract class BrowserToolbar extends ThemedRelativeLayout
private final int shadowSize;
private final ToolbarPrefs prefs;
- private boolean contextMenuEnabled = true;
public abstract boolean isAnimating();
@@ -245,8 +244,8 @@ public abstract class BrowserToolbar extends ThemedRelativeLayout
setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
- // We don't the context menu while editing or while dragging
- if (isEditing() || !contextMenuEnabled) {
+ // We don't the context menu while editing
+ if (isEditing()) {
return;
}
@@ -571,19 +570,16 @@ public abstract class BrowserToolbar extends ThemedRelativeLayout
menuButton.setNextFocusDownId(nextId);
}
- public void hideVirtualKeyboard() {
- InputMethodManager imm =
- (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.hideSoftInputFromWindow(tabsButton.getWindowToken(), 0);
- }
-
private void toggleTabs() {
if (activity.areTabsShown()) {
if (activity.hasTabsSideBar())
activity.hideTabs();
} else {
+ // hide the virtual keyboard
+ InputMethodManager imm =
+ (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(tabsButton.getWindowToken(), 0);
- hideVirtualKeyboard();
Tab tab = Tabs.getInstance().getSelectedTab();
if (tab != null) {
if (!tab.isPrivate())
@@ -678,13 +674,6 @@ public abstract class BrowserToolbar extends ThemedRelativeLayout
}
}
- public void setToolBarButtonsAlpha(float alpha) {
- ViewHelper.setAlpha(tabsCounter, alpha);
- if (hasSoftMenuButton && !HardwareUtils.isTablet()) {
- ViewHelper.setAlpha(menuIcon, alpha);
- }
- }
-
public void onEditSuggestion(String suggestion) {
if (!isEditing()) {
return;
@@ -960,10 +949,6 @@ public abstract class BrowserToolbar extends ThemedRelativeLayout
return drawable;
}
- public void setContextMenuEnabled(boolean enabled) {
- contextMenuEnabled = enabled;
- }
-
public static class TabEditingState {
// The edited text from the most recent time this tab was unselected.
protected String lastEditingText;
diff --git a/mobile/android/base/toolbar/BrowserToolbarNewTablet.java b/mobile/android/base/toolbar/BrowserToolbarNewTablet.java
index 5ccaae9df98d..942b3b729323 100644
--- a/mobile/android/base/toolbar/BrowserToolbarNewTablet.java
+++ b/mobile/android/base/toolbar/BrowserToolbarNewTablet.java
@@ -172,12 +172,6 @@ class BrowserToolbarNewTablet extends BrowserToolbarTabletBase {
// Do nothing.
}
- @Override
- public void setToolBarButtonsAlpha(float alpha) {
- // Do nothing;
- }
-
-
@Override
public void startEditing(final String url, final PropertyAnimator animator) {
// We already know the forward button state - no need to store it here.
diff --git a/mobile/android/base/util/StringUtils.java b/mobile/android/base/util/StringUtils.java
index d072402418b4..bc1783dfd389 100644
--- a/mobile/android/base/util/StringUtils.java
+++ b/mobile/android/base/util/StringUtils.java
@@ -8,6 +8,12 @@ package org.mozilla.gecko.util;
import android.net.Uri;
import android.text.TextUtils;
+import org.mozilla.gecko.AppConstants.Versions;
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
public class StringUtils {
private static final String LOGTAG = "GeckoStringUtils";
@@ -187,4 +193,51 @@ public class StringUtils {
public static String encodeUserEnteredUrl(String url) {
return Uri.fromParts("user-entered", url, null).toString();
}
+
+ /**
+ * Compatibility layer for API < 11.
+ *
+ * Returns a set of the unique names of all query parameters. Iterating
+ * over the set will return the names in order of their first occurrence.
+ *
+ * @param uri
+ * @throws UnsupportedOperationException if this isn't a hierarchical URI
+ *
+ * @return a set of decoded names
+ */
+ public static Set getQueryParameterNames(Uri uri) {
+ if (Versions.feature11Plus) {
+ return uri.getQueryParameterNames();
+ }
+
+ // Logic below copied from Uri.java included with Android 5.0.0.
+ if (uri.isOpaque()) {
+ throw new UnsupportedOperationException("This isn't a hierarchical URI.");
+ }
+
+ String query = uri.getEncodedQuery();
+ if (query == null) {
+ return Collections.emptySet();
+ }
+
+ Set names = new LinkedHashSet();
+ int start = 0;
+ do {
+ int next = query.indexOf('&', start);
+ int end = (next == -1) ? query.length() : next;
+
+ int separator = query.indexOf('=', start);
+ if (separator > end || separator == -1) {
+ separator = end;
+ }
+
+ String name = query.substring(start, separator);
+ names.add(Uri.decode(name));
+
+ // Move start to end of name.
+ start = end + 1;
+ } while (start < query.length());
+
+ return Collections.unmodifiableSet(names);
+ }
}
diff --git a/mobile/android/base/widget/ButtonToast.java b/mobile/android/base/widget/ButtonToast.java
index 2ff3fb6e0432..e402040eee99 100644
--- a/mobile/android/base/widget/ButtonToast.java
+++ b/mobile/android/base/widget/ButtonToast.java
@@ -173,8 +173,4 @@ public class ButtonToast {
hide(false, ReasonHidden.TIMEOUT);
}
};
-
- public boolean isVisible() {
- return (mView.getVisibility() == View.VISIBLE);
- }
}
diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js
index fcf35f8f673c..006bf55e37c9 100644
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -4299,11 +4299,6 @@ Tab.prototype = {
this.savedArticle = article;
Reader.updatePageAction(this);
-
- Messaging.sendRequest({
- type: "Content:ReaderEnabled",
- tabID: this.id
- });
}).catch(e => Cu.reportError("Error parsing document from tab: " + e));
}
}
diff --git a/mobile/android/modules/DownloadNotifications.jsm b/mobile/android/modules/DownloadNotifications.jsm
index e2745ac26b66..69680f1c99cb 100644
--- a/mobile/android/modules/DownloadNotifications.jsm
+++ b/mobile/android/modules/DownloadNotifications.jsm
@@ -154,7 +154,7 @@ var DownloadNotifications = {
try {
file.launch();
} catch (ex) {
- this.showInAboutDownloads(id, download);
+ this.showInAboutDownloads(download);
}
} else {
ConfirmCancelPrompt.show(download);
diff --git a/mobile/android/search/java/org/mozilla/search/providers/SearchEngine.java b/mobile/android/search/java/org/mozilla/search/providers/SearchEngine.java
index dca6285f4d10..fdc32f3df94f 100644
--- a/mobile/android/search/java/org/mozilla/search/providers/SearchEngine.java
+++ b/mobile/android/search/java/org/mozilla/search/providers/SearchEngine.java
@@ -8,6 +8,7 @@ import android.net.Uri;
import android.util.Log;
import android.util.Xml;
+import org.mozilla.gecko.util.StringUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -234,7 +235,7 @@ public class SearchEngine {
*/
public String queryForResultsUrl(String url) {
final Uri resultsUri = getResultsUri();
- final Set names = resultsUri.getQueryParameterNames();
+ final Set names = StringUtils.getQueryParameterNames(resultsUri);
for (String name : names) {
if (resultsUri.getQueryParameter(name).matches(OS_PARAM_USER_DEFINED)) {
return Uri.parse(url).getQueryParameter(name);
diff --git a/toolkit/components/satchel/nsFormAutoCompleteResult.jsm b/toolkit/components/satchel/nsFormAutoCompleteResult.jsm
index 414e642ade3d..17b52adbc3dc 100644
--- a/toolkit/components/satchel/nsFormAutoCompleteResult.jsm
+++ b/toolkit/components/satchel/nsFormAutoCompleteResult.jsm
@@ -130,6 +130,11 @@ FormAutoCompleteResult.prototype = {
*/
getStyleAt: function(index) {
this._checkIndexBounds(index);
+
+ if (this._formHistResult && index < this._formHistResult.matchCount) {
+ return "fromhistory";
+ }
+
if (!this._comments[index]) {
return null; // not a category label, so no special styling
}
diff --git a/toolkit/content/widgets/textbox.xml b/toolkit/content/widgets/textbox.xml
index 4d4d9c25ce12..b489d879390d 100644
--- a/toolkit/content/widgets/textbox.xml
+++ b/toolkit/content/widgets/textbox.xml
@@ -241,6 +241,9 @@
#ifndef XP_WIN
+ // Only care about context clicks on the input field itself
+ if (event.target != this.inputField)
+ return;
if (!event.button) // context menu opened via keyboard shortcut
return;
this._maybeSelectAll();
diff --git a/toolkit/devtools/server/actors/animation.js b/toolkit/devtools/server/actors/animation.js
index 5b1b63439675..93c19a4abf7c 100644
--- a/toolkit/devtools/server/actors/animation.js
+++ b/toolkit/devtools/server/actors/animation.js
@@ -24,10 +24,16 @@
* /dom/webidl/Animation*.webidl
*/
+const {Cu} = require("chrome");
+const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
+const {setInterval, clearInterval} = require("sdk/timers");
const {ActorClass, Actor,
FrontClass, Front,
Arg, method, RetVal} = require("devtools/server/protocol");
const {NodeActor} = require("devtools/server/actors/inspector");
+const EventEmitter = require("devtools/toolkit/event-emitter");
+
+const PLAYER_DEFAULT_AUTO_REFRESH_TIMEOUT = 500; // ms
/**
* The AnimationPlayerActor provides information about a given animation: its
@@ -179,8 +185,13 @@ let AnimationPlayerActor = ActorClass({
});
let AnimationPlayerFront = FrontClass(AnimationPlayerActor, {
+ AUTO_REFRESH_EVENT: "updated-state",
+
initialize: function(conn, form, detail, ctx) {
+ EventEmitter.decorate(this);
Front.prototype.initialize.call(this, conn, form, detail, ctx);
+
+ this.state = {};
},
form: function(form, detail) {
@@ -189,9 +200,11 @@ let AnimationPlayerFront = FrontClass(AnimationPlayerActor, {
return;
}
this._form = form;
+ this.state = this.initialState;
},
destroy: function() {
+ this.stopAutoRefresh();
Front.prototype.destroy.call(this);
},
@@ -209,7 +222,75 @@ let AnimationPlayerFront = FrontClass(AnimationPlayerActor, {
iterationCount: this._form.iterationCount,
isRunningOnCompositor: this._form.isRunningOnCompositor
}
- }
+ },
+
+ // About auto-refresh:
+ //
+ // The AnimationPlayerFront is capable of automatically refreshing its state
+ // by calling the getCurrentState method at regular intervals. This allows
+ // consumers to update their knowledge of the player's currentTime, playState,
+ // ... dynamically.
+ //
+ // Calling startAutoRefresh will start the automatic refreshing of the state,
+ // and calling stopAutoRefresh will stop it.
+ // Once the automatic refresh has been started, the AnimationPlayerFront emits
+ // "updated-state" events everytime the state changes.
+ //
+ // Note that given the time-related nature of animations, the actual state
+ // changes a lot more often than "updated-state" events are emitted. This is
+ // to avoid making many protocol requests.
+
+ /**
+ * Start auto-refreshing this player's state.
+ * @param {Number} interval Optional auto-refresh timer interval to override
+ * the default value.
+ */
+ startAutoRefresh: function(interval=PLAYER_DEFAULT_AUTO_REFRESH_TIMEOUT) {
+ if (this.autoRefreshTimer) {
+ return;
+ }
+
+ this.autoRefreshTimer = setInterval(this.refreshState.bind(this), interval);
+ },
+
+ /**
+ * Stop auto-refreshing this player's state.
+ */
+ stopAutoRefresh: function() {
+ if (!this.autoRefreshTimer) {
+ return;
+ }
+
+ clearInterval(this.autoRefreshTimer);
+ this.autoRefreshTimer = null;
+ },
+
+ /**
+ * Called automatically when auto-refresh is on. Doesn't return anything, but
+ * emits the "updated-state" event.
+ */
+ refreshState: Task.async(function*() {
+ let data = yield this.getCurrentState();
+
+ // By the time the new state is received, auto-refresh might be stopped.
+ if (!this.autoRefreshTimer) {
+ return;
+ }
+
+ // Check if something has changed
+ let hasChanged = false;
+ for (let key in data) {
+ if (this.state[key] !== data[key]) {
+ hasChanged = true;
+ break;
+ }
+ }
+
+ if (hasChanged) {
+ this.state = data;
+ this.emit(this.AUTO_REFRESH_EVENT, this.state);
+ }
+ })
});
/**
diff --git a/toolkit/devtools/server/actors/utils/automation-timeline.js b/toolkit/devtools/server/actors/utils/automation-timeline.js
new file mode 100644
index 000000000000..b3f41f3bf7f5
--- /dev/null
+++ b/toolkit/devtools/server/actors/utils/automation-timeline.js
@@ -0,0 +1,353 @@
+/**
+ * web-audio-automation-timeline - 1.0.2
+ * https://github.com/jsantell/web-audio-automation-timeline
+ * MIT License, copyright (c) 2014 Jordan Santell
+ */
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.Timeline=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 1
+ if (t >= start + duration || (ratio = Math.max((t - start) / duration, 0)) >= 1) {
+ return curve[curveLength - 1];
+ }
+
+ return curve[~~(curveLength * ratio)];
+};
+
+exports.exponentialApproach = function (t0, v0, v1, timeConstant, t) {
+ return v1 + (v0 - v1) * Math.exp(-(t - t0) / timeConstant);
+};
+
+// Since we are going to accumulate error by adding 0.01 multiple times
+// in a loop, we want to fuzz the equality check in `getValueAtTime`
+exports.fuzzyEqual = function (lhs, rhs) {
+ return Math.abs(lhs - rhs) < EPSILON;
+};
+
+},{}],4:[function(require,module,exports){
+var TimelineEvent = require("./event").TimelineEvent;
+var F = require("./formulas");
+
+exports.Timeline = Timeline;
+
+function Timeline (defaultValue) {
+ this.events = [];
+
+ this._value = defaultValue || 0;
+
+ // This is the value of this AudioParam we computed at the last call.
+ this._computedValue = defaultValue || 0;
+ // This is the value of this AudioParam at the last tick of the previous event.
+ this._lastComputedValue = defaultValue || 0;
+}
+
+Timeline.prototype.getEventCount = function () {
+ return this.events.length;
+};
+
+Timeline.prototype.value = function () {
+ return this._value;
+};
+
+Timeline.prototype.setValue = function (value) {
+ if (this.events.length === 0) {
+ this._computedValue = this._lastComputedValue = this._value = value;
+ }
+};
+
+Timeline.prototype.getValue = function () {
+ if (this.events.length) {
+ throw new Error("Can only call `getValue` when there are 0 events.");
+ }
+
+ return this._value;
+};
+
+Timeline.prototype.getValueAtTime = function (time) {
+ this._computedValue = this._getValueAtTimeHelper(time);
+ return this._computedValue;
+};
+
+Timeline.prototype._getValueAtTimeHelper = function (time) {
+ var bailOut = false;
+ var previous = null;
+ var next = null;
+ var events = this.events;
+ var e;
+
+ for (var i = 0; !bailOut && i < events.length; i++) {
+ if (F.fuzzyEqual(time, events[i].time)) {
+ this._lastComputedValue = this._computedValue;
+
+ // Find the last event with the same time as `time`
+ do {
+ ++i;
+ } while (i < events.length && F.fuzzyEqual(time, events[i].time));
+
+ e = events[i - 1];
+
+ // `setTargetAtTime` can be handled no matter what their next event is (if they have one)
+ if (e.type === "setTargetAtTime") {
+ return e.exponentialApproach(this._lastComputedValue, time);
+ }
+
+ // `setValueCurveAtTime` events can be handled no matter what their next event node is
+ // (if they have one)
+ if (e.type === "setValueCurveAtTime") {
+ return e.extractValueFromCurve(time);
+ }
+
+ // For other event types
+ return e.value;
+ }
+ previous = next;
+ next = events[i];
+
+ if (time < events[i].time) {
+ bailOut = true;
+ }
+ }
+
+ // Handle the case where the time is past all of the events
+ if (!bailOut) {
+ previous = next;
+ next = null;
+ }
+
+ // Just return the default value if we did not find anything
+ if (!previous && !next) {
+ return this._value;
+ }
+
+ // If the requested time is before all of the existing events
+ if (!previous) {
+ return this._value;
+ }
+
+ // `setTargetAtTime` can be handled no matter what their next event is (if they have one)
+ if (previous.type === "setTargetAtTime") {
+ return previous.exponentialApproach(this._lastComputedValue, time);
+ }
+
+ // `setValueCurveAtTime` events can be handled no matter what their next event node is
+ // (if they have one)
+ if (previous.type === "setValueCurveAtTime") {
+ return previous.extractValueFromCurve(time);
+ }
+
+ if (!next) {
+ if (~["setValueAtTime", "linearRampToValueAtTime", "exponentialRampToValueAtTime"].indexOf(previous.type)) {
+ return previous.value;
+ }
+ if (previous.type === "setValueCurveAtTime") {
+ return previous.extractValueFromCurve(time);
+ }
+ if (previous.type === "setTargetAtTime") {
+ throw new Error("unreached");
+ }
+ throw new Error("unreached");
+ }
+
+ // Finally handle the case where we have both a previous and a next event
+ // First handle the case where our range ends up in a ramp event
+ if (next.type === "linearRampToValueAtTime") {
+ return previous.linearInterpolate(next, time);
+ } else if (next.type === "exponentialRampToValueAtTime") {
+ return previous.exponentialInterpolate(next, time);
+ }
+
+ // Now handle all other cases
+ if (~["setValueAtTime", "linearRampToValueAtTime", "exponentialRampToValueAtTime"].indexOf(previous.type)) {
+ // If the next event type is neither linear or exponential ramp,
+ // the value is constant.
+ return previous.value;
+ }
+ if (previous.type === "setValueCurveAtTime") {
+ return previous.extractValueFromCurve(time);
+ }
+ if (previous.type === "setTargetAtTime") {
+ throw new Error("unreached");
+ }
+ throw new Error("unreached");
+};
+
+Timeline.prototype._insertEvent = function (ev) {
+ var events = this.events;
+
+ if (ev.type === "setValueCurveAtTime") {
+ if (!ev.value || !ev.value.length) {
+ throw new Error("NS_ERROR_DOM_SYNTAX_ERR");
+ }
+ }
+
+ if (ev.type === "setTargetAtTime") {
+ if (F.fuzzyEqual(ev.constant, 0)) {
+ throw new Error("NS_ERROR_DOM_SYNTAX_ERR");
+ }
+ }
+
+ // Make sure that non-curve events don't fall within the duration of a
+ // curve event.
+ for (var i = 0; i < events.length; i++) {
+ if (events[i].type === "setValueCurveAtTime" &&
+ events[i].time <= ev.time &&
+ (events[i].time + events[i].duration) >= ev.time) {
+ throw new Error("NS_ERROR_DOM_SYNTAX_ERR");
+ }
+ }
+
+ // Make sure that curve events don't fall in a range which includes other
+ // events.
+ if (ev.type === "setValueCurveAtTime") {
+ for (var i = 0; i < events.length; i++) {
+ if (events[i].time > ev.time &&
+ events[i].time < (ev.time + ev.duration)) {
+ throw new Error("NS_ERROR_DOM_SYNTAX_ERR");
+ }
+ }
+ }
+
+ // Make sure that invalid values are not used for exponential curves
+ if (ev.type === "exponentialRampToValueAtTime") {
+ if (ev.value <= 0) throw new Error("NS_ERROR_DOM_SYNTAX_ERR");
+ var prev = this._getPreviousEvent(ev.time);
+ if (prev) {
+ if (prev.value <= 0) {
+ throw new Error("NS_ERROR_DOM_SYNTAX_ERR");
+ }
+ } else {
+ if (this._value <= 0) {
+ throw new Error("NS_ERROR_DOM_SYNTAX_ERR");
+ }
+ }
+ }
+
+ for (var i = 0; i < events.length; i++) {
+ if (ev.time === events[i].time) {
+ if (ev.type === events[i].type) {
+ // If times and types are equal, replace the event;
+ events[i] = ev;
+ } else {
+ // Otherwise, place the element after the last event of another type
+ do { i++; }
+ while (i < events.length && ev.type !== events[i].type && ev.time === events[i].time);
+ events.splice(i, 0, ev);
+ }
+ return;
+ }
+ // Otherwise, place the event right after the latest existing event
+ if (ev.time < events[i].time) {
+ events.splice(i, 0, ev);
+ return;
+ }
+ }
+
+ // If we couldn't find a place for the event, just append it to the list
+ this.events.push(ev);
+};
+
+Timeline.prototype._getPreviousEvent = function (time) {
+ var previous = null, next = null;
+ var bailOut = false;
+ var events = this.events;
+
+ for (var i = 0; !bailOut && i < events.length; i++) {
+ if (time === events[i]) {
+ do { ++i; }
+ while (i < events.length && time === events[i].time);
+ return events[i - 1];
+ }
+ previous = next;
+ next = events[i];
+ if (time < events[i].time) {
+ bailOut = true;
+ }
+ }
+
+ // Handle the case where the time is past all the events
+ if (!bailOut) {
+ previous = next;
+ }
+
+ return previous;
+};
+
+Timeline.prototype.setValueAtTime = function (value, startTime) {
+ this._insertEvent(new TimelineEvent("setValueAtTime", value, startTime));
+};
+
+Timeline.prototype.linearRampToValueAtTime = function (value, endTime) {
+ this._insertEvent(new TimelineEvent("linearRampToValueAtTime", value, endTime));
+};
+
+Timeline.prototype.exponentialRampToValueAtTime = function (value, endTime) {
+ this._insertEvent(new TimelineEvent("exponentialRampToValueAtTime", value, endTime));
+};
+
+Timeline.prototype.setTargetAtTime = function (value, startTime, timeConstant) {
+ this._insertEvent(new TimelineEvent("setTargetAtTime", value, startTime, timeConstant));
+};
+
+Timeline.prototype.setValueCurveAtTime = function (value, startTime, duration) {
+ this._insertEvent(new TimelineEvent("setValueCurveAtTime", value, startTime, null, duration));
+};
+
+Timeline.prototype.cancelScheduledValues = function (time) {
+ for (var i = 0; i < this.events.length; i++) {
+ if (this.events[i].time >= time) {
+ this.events = this.events.slice(0, i);
+ break;
+ }
+ }
+};
+
+Timeline.prototype.cancelAllEvents = function () {
+ this.events.length = 0;
+};
+
+},{"./event":2,"./formulas":3}]},{},[1])(1)
+});
\ No newline at end of file
diff --git a/toolkit/devtools/server/actors/webconsole.js b/toolkit/devtools/server/actors/webconsole.js
index b52f6acb5159..e4d8e7fd35b2 100644
--- a/toolkit/devtools/server/actors/webconsole.js
+++ b/toolkit/devtools/server/actors/webconsole.js
@@ -83,6 +83,7 @@ function WebConsoleActor(aConnection, aParentActor)
this.traits = {
customNetworkRequest: !this._parentIsContentActor,
+ transferredResponseSize: true
};
}
@@ -1974,6 +1975,7 @@ NetworkEventActor.prototype =
updateType: "responseContent",
mimeType: aContent.mimeType,
contentSize: aContent.text.length,
+ transferredSize: aContent.transferredSize,
discardResponseBody: aDiscardedResponseBody,
};
diff --git a/toolkit/devtools/server/moz.build b/toolkit/devtools/server/moz.build
index 492cc616fedb..f6c017b3fa49 100644
--- a/toolkit/devtools/server/moz.build
+++ b/toolkit/devtools/server/moz.build
@@ -70,6 +70,7 @@ EXTRA_JS_MODULES.devtools.server.actors += [
]
EXTRA_JS_MODULES.devtools.server.actors.utils += [
+ 'actors/utils/automation-timeline.js',
'actors/utils/make-debugger.js',
'actors/utils/map-uri-to-addon-id.js',
'actors/utils/ScriptStore.js'
diff --git a/toolkit/devtools/server/tests/browser/browser.ini b/toolkit/devtools/server/tests/browser/browser.ini
index 080d536dd1aa..64f125acc668 100644
--- a/toolkit/devtools/server/tests/browser/browser.ini
+++ b/toolkit/devtools/server/tests/browser/browser.ini
@@ -18,6 +18,7 @@ support-files =
[browser_animation_actors_02.js]
[browser_animation_actors_03.js]
[browser_animation_actors_04.js]
+[browser_animation_actors_05.js]
[browser_navigateEvents.js]
[browser_storage_dynamic_windows.js]
[browser_storage_listings.js]
diff --git a/toolkit/devtools/server/tests/browser/browser_animation_actors_05.js b/toolkit/devtools/server/tests/browser/browser_animation_actors_05.js
new file mode 100644
index 000000000000..59f91a6e185c
--- /dev/null
+++ b/toolkit/devtools/server/tests/browser/browser_animation_actors_05.js
@@ -0,0 +1,57 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Check that AnimationPlayers can auto-refresh their states.
+
+const {AnimationsFront} = require("devtools/server/actors/animation");
+const {InspectorFront} = require("devtools/server/actors/inspector");
+
+add_task(function*() {
+ let doc = yield addTab(MAIN_DOMAIN + "animation.html");
+
+ initDebuggerServer();
+ let client = new DebuggerClient(DebuggerServer.connectPipe());
+ let form = yield connectDebuggerClient(client);
+ let inspector = InspectorFront(client, form);
+ let walker = yield inspector.getWalker();
+ let front = AnimationsFront(client, form);
+
+ let node = yield walker.querySelector(walker.rootNode, ".simple-animation");
+ let [player] = yield front.getAnimationPlayersForNode(node);
+
+ ok(player.startAutoRefresh, "The startAutoRefresh function is available");
+ ok(player.stopAutoRefresh, "The stopAutoRefresh function is available");
+ ok(player.state, "The current state is stored on the player");
+
+ info("Subscribe to the refresh event, start the auto-refresh and wait for " +
+ "a few events to be received");
+
+ player.startAutoRefresh();
+
+ let onAllEventsReceived = new Promise(resolve => {
+ let expected = 5;
+ let previousState = player.initialState;
+ let onNewState = (e, state) => {
+ ok(state.currentTime !== previousState.currentTime,
+ "The time has changed since the last update");
+ expected --;
+ previousState = state;
+ if (expected === 0) {
+ player.off(player.AUTO_REFRESH_EVENT, onNewState);
+ resolve();
+ }
+ };
+ player.on(player.AUTO_REFRESH_EVENT, onNewState);
+ });
+
+ yield onAllEventsReceived;
+
+ info("Stop the auto-refresh");
+ player.stopAutoRefresh();
+
+ yield closeDebuggerClient(client);
+ gBrowser.removeCurrentTab();
+});
diff --git a/toolkit/devtools/webconsole/network-monitor.js b/toolkit/devtools/webconsole/network-monitor.js
index 4ff49f31ec78..c93381d1903f 100644
--- a/toolkit/devtools/webconsole/network-monitor.js
+++ b/toolkit/devtools/webconsole/network-monitor.js
@@ -4,7 +4,7 @@
"use strict";
-const {Cc, Ci, Cu} = require("chrome");
+const {Cc, Ci, Cu, Cr} = require("chrome");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@@ -57,13 +57,33 @@ function NetworkResponseListener(aOwner, aHttpActivity)
this.receivedData = "";
this.httpActivity = aHttpActivity;
this.bodySize = 0;
+ let channel = this.httpActivity.channel;
+ this._wrappedNotificationCallbacks = channel.notificationCallbacks;
+ channel.notificationCallbacks = this;
}
exports.NetworkResponseListener = NetworkResponseListener;
NetworkResponseListener.prototype = {
QueryInterface:
XPCOMUtils.generateQI([Ci.nsIStreamListener, Ci.nsIInputStreamCallback,
- Ci.nsIRequestObserver, Ci.nsISupports]),
+ Ci.nsIRequestObserver, Ci.nsIInterfaceRequestor,
+ Ci.nsISupports]),
+
+ // nsIInterfaceRequestor implementation
+
+ /**
+ * This object implements nsIProgressEventSink, but also needs to forward
+ * interface requests to the notification callbacks of other objects.
+ */
+ getInterface(iid) {
+ if (iid.equals(Ci.nsIProgressEventSink)) {
+ return this;
+ }
+ if (this._wrappedNotificationCallbacks) {
+ return this._wrappedNotificationCallbacks.getInterface(iid);
+ }
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ },
/**
* This NetworkResponseListener tracks the NetworkMonitor.openResponses object
@@ -72,6 +92,12 @@ NetworkResponseListener.prototype = {
*/
_foundOpenResponse: false,
+ /**
+ * If the channel already had notificationCallbacks, hold them here internally
+ * so that we can forward getInterface requests to that object.
+ */
+ _wrappedNotificationCallbacks: null,
+
/**
* The response listener owner.
*/
@@ -94,10 +120,15 @@ NetworkResponseListener.prototype = {
receivedData: null,
/**
- * The network response body size.
+ * The uncompressed, decoded response body size.
*/
bodySize: null,
+ /**
+ * Response body size on the wire, potentially compressed / encoded.
+ */
+ transferredSize: null,
+
/**
* The nsIRequest we are started for.
*/
@@ -177,6 +208,18 @@ NetworkResponseListener.prototype = {
this.sink.outputStream.close();
},
+ // nsIProgressEventSink implementation
+
+ /**
+ * Handle progress event as data is transferred. This is used to record the
+ * size on the wire, which may be compressed / encoded.
+ */
+ onProgress: function(request, context, progress, progressMax) {
+ this.transferredSize = progress;
+ },
+
+ onStatus: function () {},
+
/**
* Find the open response object associated to the current request. The
* NetworkMonitor._httpResponseExaminer() method saves the response headers in
@@ -259,6 +302,7 @@ NetworkResponseListener.prototype = {
};
response.size = response.text.length;
+ response.transferredSize = this.transferredSize;
try {
response.mimeType = this.request.contentType;
@@ -279,6 +323,7 @@ NetworkResponseListener.prototype = {
this.httpActivity.owner.
addResponseContent(response, this.httpActivity.discardResponseBody);
+ this._wrappedNotificationCallbacks = null;
this.httpActivity.channel = null;
this.httpActivity.owner = null;
this.httpActivity = null;