diff --git a/b2g/components/SystemAppProxy.jsm b/b2g/components/SystemAppProxy.jsm index 74abffddd31..c17b78c8ec5 100644 --- a/b2g/components/SystemAppProxy.jsm +++ b/b2g/components/SystemAppProxy.jsm @@ -99,9 +99,41 @@ let SystemAppProxy = { }, dispatchKeyboardEvent: function systemApp_dispatchKeyboardEvent(type, details) { - let content = this._frame ? this._frame.contentWindow : null; - let e = new content.KeyboardEvent(type, details); - content.dispatchEvent(e); + try { + let content = this._frame ? this._frame.contentWindow : null; + if (!content) { + throw new Error("no content window"); + } + + // If we don't already have a TextInputProcessor, create one now + if (!this.TIP) { + this.TIP = Cc["@mozilla.org/text-input-processor;1"] + .createInstance(Ci.nsITextInputProcessor); + if (!this.TIP) { + throw new Error("failed to create textInputProcessor"); + } + } + + if (!this.TIP.beginInputTransactionForTests(content)) { + this.TIP = null; + throw new Error("beginInputTransaction failed"); + } + + let e = new content.KeyboardEvent("", { key: details.key, }); + + if (type === 'keydown') { + this.TIP.keydown(e); + } + else if (type === 'keyup') { + this.TIP.keyup(e); + } + else { + throw new Error("unexpected event type: " + type); + } + } + catch(e) { + dump("dispatchKeyboardEvent: " + e + "\n"); + } }, // Listen for dom events on the system app diff --git a/b2g/config/aries/sources.xml b/b2g/config/aries/sources.xml index 2c0f6aebb44..3d1636ed348 100644 --- a/b2g/config/aries/sources.xml +++ b/b2g/config/aries/sources.xml @@ -15,7 +15,7 @@ - + @@ -23,7 +23,7 @@ - + diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index 8b8f5a6e342..e8be8f5d0a6 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + @@ -23,7 +23,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 590bd8d1ba1..a54a82efe91 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 9dc19054d65..e57a2e9d2bd 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,10 +17,10 @@ - + - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index c1640d8fca1..39c58c28064 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + @@ -23,7 +23,7 @@ - + diff --git a/b2g/config/emulator-l/sources.xml b/b2g/config/emulator-l/sources.xml index ab94c4e174e..fdb05600149 100644 --- a/b2g/config/emulator-l/sources.xml +++ b/b2g/config/emulator-l/sources.xml @@ -15,7 +15,7 @@ - + @@ -23,7 +23,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 590bd8d1ba1..a54a82efe91 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index 5ccb5ffb8bd..389c82d6737 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + @@ -23,7 +23,7 @@ - + diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 40a1a8f7687..91ea65071dc 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,9 +1,9 @@ { "git": { - "git_revision": "c7875bbc8b32e7b95cc55c9690b03b140905d84d", + "git_revision": "6fef72357971934c8774578044ea7a442be3a75d", "remote": "https://git.mozilla.org/releases/gaia.git", "branch": "" }, - "revision": "9db39036600a1c999f93038fa0dd09a48321518e", + "revision": "0fa143d87b0b96ddd6196c2915e4b53cceb427db", "repo_path": "integration/gaia-central" } diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index 3cd7d87502f..368edbdc153 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,10 +17,10 @@ - + - + diff --git a/b2g/config/nexus-5-l/sources.xml b/b2g/config/nexus-5-l/sources.xml index a88bf0c841f..94d145ea036 100644 --- a/b2g/config/nexus-5-l/sources.xml +++ b/b2g/config/nexus-5-l/sources.xml @@ -15,7 +15,7 @@ - + @@ -23,7 +23,7 @@ - + diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 4a8773d5e26..7ec82c6a838 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1601,11 +1601,12 @@ pref("devtools.webconsole.persistlog", false); pref("devtools.webconsole.timestampMessages", false); // The number of lines that are displayed in the web console for the Net, -// CSS, JS and Web Developer categories. -pref("devtools.hud.loglimit.network", 200); -pref("devtools.hud.loglimit.cssparser", 200); -pref("devtools.hud.loglimit.exception", 200); -pref("devtools.hud.loglimit.console", 200); +// CSS, JS and Web Developer categories. These defaults should be kept in sync +// with DEFAULT_LOG_LIMIT in the webconsole frontend. +pref("devtools.hud.loglimit.network", 1000); +pref("devtools.hud.loglimit.cssparser", 1000); +pref("devtools.hud.loglimit.exception", 1000); +pref("devtools.hud.loglimit.console", 1000); // By how many times eyedropper will magnify pixels pref("devtools.eyedropper.zoom", 6); diff --git a/browser/base/content/tabbrowser.css b/browser/base/content/tabbrowser.css index 33bf5f72619..e485b3cb84f 100644 --- a/browser/base/content/tabbrowser.css +++ b/browser/base/content/tabbrowser.css @@ -19,10 +19,22 @@ } .tab-close-button[pinned], -.tabbrowser-tabs[closebuttons="activetab"] > * > * > * > .tab-close-button:not([visuallyselected="true"]) { +.tabbrowser-tabs[closebuttons="activetab"] > * > * > * > .tab-close-button:not([visuallyselected="true"]), +.tab-icon-image:not([src]):not([pinned]):not([crashed]), +.tab-icon-image[busy], +.tab-throbber:not([busy]), +.tab-icon-sound:not([soundplaying]):not([muted]), +.tab-icon-sound[pinned], +.tab-icon-overlay { display: none; } +.tab-icon-overlay[soundplaying][pinned], +.tab-icon-overlay[muted][pinned], +.tab-icon-overlay[crashed] { + display: -moz-box; +} + .tab-label[pinned] { width: 0; margin-left: 0 !important; @@ -51,13 +63,6 @@ tabpanels { } } -.tab-icon-image:not([src]):not([pinned]):not([crashed]), -.tab-throbber:not([busy]), -.tab-icon-image[busy], -.tab-icon-overlay[busy] { - display: none; -} - .closing-tabs-spacer { pointer-events: none; } diff --git a/browser/base/content/test/plugins/browser.ini b/browser/base/content/test/plugins/browser.ini index 23a0f8ddb24..f2939895dbc 100644 --- a/browser/base/content/test/plugins/browser.ini +++ b/browser/base/content/test/plugins/browser.ini @@ -67,6 +67,7 @@ skip-if = !crashreporter [browser_plugins_added_dynamically.js] [browser_pluginnotification.js] [browser_plugin_infolink.js] +skip-if = (os == 'win' && os_version == "6.2" && e10s) # Win8 permafail in subsequent tests [browser_plugin_reloading.js] [browser_blocklist_content.js] skip-if = !e10s diff --git a/browser/base/content/test/social/browser.ini b/browser/base/content/test/social/browser.ini index 159888bda68..09e4a70e900 100644 --- a/browser/base/content/test/social/browser.ini +++ b/browser/base/content/test/social/browser.ini @@ -40,6 +40,7 @@ skip-if = e10s && debug # e10s/Linux/Debug Leaking docshells (bug 1150147) [browser_social_chatwindowfocus.js] skip-if = e10s # tab crash on data url used in this test [browser_social_contextmenu.js] +skip-if = (os == 'linux' && e10s) # Bug 1072669 context menu relies on target element [browser_social_errorPage.js] [browser_social_flyout.js] [browser_social_isVisible.js] diff --git a/browser/components/loop/.eslintrc b/browser/components/loop/.eslintrc index 34a728480d0..8a408c45c64 100644 --- a/browser/components/loop/.eslintrc +++ b/browser/components/loop/.eslintrc @@ -44,7 +44,7 @@ "curly": [2, "all"], "dot-location": [2, "property"], "eol-last": 2, - "eqeqeq": 0, // TBD. Might need to be separate for content & chrome + "eqeqeq": [2, "smart"], "key-spacing": [2, {"beforeColon": false, "afterColon": true }], "linebreak-style": [2, "unix"], "new-cap": 0, // TODO: set to 2 diff --git a/browser/components/loop/.eslintrc-gecko b/browser/components/loop/.eslintrc-gecko index 085532ba768..6dae86ac9ae 100644 --- a/browser/components/loop/.eslintrc-gecko +++ b/browser/components/loop/.eslintrc-gecko @@ -56,8 +56,9 @@ "Assert": false, }, "rules": { - "arrow-parens": 0, // TBD + "arrow-parens": 0, // TBD "arrow-spacing": 2, + "eqeqeq": 0, // TBD "generator-star-spacing": [2, "after"], // We should fix the errors and enable this (set to 2) "no-var": 0, diff --git a/browser/components/loop/content/js/client.js b/browser/components/loop/content/js/client.js index 4463146a920..be7b780f153 100644 --- a/browser/components/loop/content/js/client.js +++ b/browser/components/loop/content/js/client.js @@ -53,7 +53,7 @@ loop.Client = (function() { } }); - if (properties.length == 1) { + if (properties.length === 1) { return data[properties[0]]; } diff --git a/browser/components/loop/content/js/contacts.js b/browser/components/loop/content/js/contacts.js index 336663168e8..6addc665887 100644 --- a/browser/components/loop/content/js/contacts.js +++ b/browser/components/loop/content/js/contacts.js @@ -271,7 +271,7 @@ loop.contacts = (function(_, mozL10n) { canEdit: function() { // We cannot modify imported contacts. For the moment, the check for // determining whether the contact is imported is based on its category. - return this.props.contact.category[0] != "google"; + return this.props.contact.category[0] !== "google"; }, render: function() { @@ -417,7 +417,7 @@ loop.contacts = (function(_, mozL10n) { let profile = this.props.mozLoop.userProfile; let currUid = this._userProfile ? this._userProfile.uid : null; let newUid = profile ? profile.uid : null; - if (currUid != newUid) { + if (currUid !== newUid) { // On profile change (login, logout), reload all contacts. this._userProfile = profile; // The following will do a forceUpdate() for us. @@ -750,7 +750,7 @@ loop.contacts = (function(_, mozL10n) { return ( React.createElement("div", {className: contentAreaClasses}, - React.createElement("header", null, this.props.mode == "add" + React.createElement("header", null, this.props.mode === "add" ? mozL10n.get("add_contact_title") : mozL10n.get("edit_contact_title")), React.createElement("div", {className: cx({"form-content-container": true})}, @@ -779,7 +779,7 @@ loop.contacts = (function(_, mozL10n) { caption: mozL10n.get("cancel_button"), onClick: this.handleCancelButtonClick}), React.createElement(Button, {additionalClass: "button-accept", - caption: this.props.mode == "add" + caption: this.props.mode === "add" ? mozL10n.get("add_contact_button") : mozL10n.get("edit_contact_done_button"), onClick: this.handleAcceptButtonClick}) diff --git a/browser/components/loop/content/js/contacts.jsx b/browser/components/loop/content/js/contacts.jsx index 6a2fb93656a..797788ff3b0 100644 --- a/browser/components/loop/content/js/contacts.jsx +++ b/browser/components/loop/content/js/contacts.jsx @@ -271,7 +271,7 @@ loop.contacts = (function(_, mozL10n) { canEdit: function() { // We cannot modify imported contacts. For the moment, the check for // determining whether the contact is imported is based on its category. - return this.props.contact.category[0] != "google"; + return this.props.contact.category[0] !== "google"; }, render: function() { @@ -417,7 +417,7 @@ loop.contacts = (function(_, mozL10n) { let profile = this.props.mozLoop.userProfile; let currUid = this._userProfile ? this._userProfile.uid : null; let newUid = profile ? profile.uid : null; - if (currUid != newUid) { + if (currUid !== newUid) { // On profile change (login, logout), reload all contacts. this._userProfile = profile; // The following will do a forceUpdate() for us. @@ -750,7 +750,7 @@ loop.contacts = (function(_, mozL10n) { return (
-
{this.props.mode == "add" +
{this.props.mode === "add" ? mozL10n.get("add_contact_title") : mozL10n.get("edit_contact_title")}
@@ -779,7 +779,7 @@ loop.contacts = (function(_, mozL10n) { caption={mozL10n.get("cancel_button")} onClick={this.handleCancelButtonClick} />
); diff --git a/browser/components/loop/content/shared/js/utils.js b/browser/components/loop/content/shared/js/utils.js index 3b622121c28..88257881671 100644 --- a/browser/components/loop/content/shared/js/utils.js +++ b/browser/components/loop/content/shared/js/utils.js @@ -687,7 +687,7 @@ var inChrome = typeof Components != "undefined" && "utils" in Components; var prop; for (var i = 0, lA = propsA.length; i < lA; ++i) { prop = propsA[i]; - if (propsB.indexOf(prop) == -1) { + if (propsB.indexOf(prop) === -1) { diff.removed.push(prop); } else if (a[prop] !== b[prop]) { diff.updated.push(prop); @@ -696,7 +696,7 @@ var inChrome = typeof Components != "undefined" && "utils" in Components; for (var j = 0, lB = propsB.length; j < lB; ++j) { prop = propsB[j]; - if (propsA.indexOf(prop) == -1) { + if (propsA.indexOf(prop) === -1) { diff.added.push(prop); } } diff --git a/browser/components/loop/content/shared/js/validate.js b/browser/components/loop/content/shared/js/validate.js index cb7b5c9fbc1..3f88060abae 100644 --- a/browser/components/loop/content/shared/js/validate.js +++ b/browser/components/loop/content/shared/js/validate.js @@ -115,7 +115,7 @@ loop.validate = (function() { try { return typeof Type === "undefined" || // skip checking Type === null && value === null || // null type - value.constructor == Type || // native type + value.constructor === Type || // native type Type.prototype.isPrototypeOf(value) || // custom type typeName(value) === typeName(Type); // type string eq. } catch (e) { diff --git a/browser/components/loop/content/shared/js/views.js b/browser/components/loop/content/shared/js/views.js index c0ba7fe031e..6c14b424f59 100644 --- a/browser/components/loop/content/shared/js/views.js +++ b/browser/components/loop/content/shared/js/views.js @@ -813,18 +813,25 @@ loop.shared.views = (function(_, mozL10n) { "shared/img/icons-16x16.svg#globe"; } + var wrapperClasses = React.addons.classSet({ + "context-wrapper": true, + "clicks-allowed": this.props.allowClick + }); + return ( React.createElement("div", {className: "context-content"}, this.renderContextTitle(), - React.createElement("div", {className: "context-wrapper"}, + React.createElement("a", {className: wrapperClasses, + href: this.props.allowClick ? this.props.url : null, + onClick: this.handleLinkClick, + rel: "noreferrer", + target: "_blank"}, React.createElement("img", {className: "context-preview", src: thumbnail}), - React.createElement("span", {className: "context-description"}, + React.createElement("span", {className: "context-info"}, this.props.description, - React.createElement("a", {className: "context-url", - href: this.props.allowClick ? this.props.url : null, - onClick: this.handleLinkClick, - rel: "noreferrer", - target: "_blank"}, hostname) + React.createElement("span", {className: "context-url"}, + hostname + ) ) ) ) @@ -988,7 +995,7 @@ loop.shared.views = (function(_, mozL10n) { componentWillReceiveProps: function(nextProps) { // This is all for the ui-showcase's benefit. - if (this.props.matchMedia != nextProps.matchMedia) { + if (this.props.matchMedia !== nextProps.matchMedia) { this.updateLocalMediaState(null, nextProps.matchMedia); } }, @@ -1003,7 +1010,7 @@ loop.shared.views = (function(_, mozL10n) { updateLocalMediaState: function(event, matchMedia) { var newState = this.isLocalMediaAbsolutelyPositioned(matchMedia); - if (this.state.localMediaAboslutelyPositioned != newState) { + if (this.state.localMediaAboslutelyPositioned !== newState) { this.setState({ localMediaAboslutelyPositioned: newState }); diff --git a/browser/components/loop/content/shared/js/views.jsx b/browser/components/loop/content/shared/js/views.jsx index efec9f4ddfb..497df94b548 100644 --- a/browser/components/loop/content/shared/js/views.jsx +++ b/browser/components/loop/content/shared/js/views.jsx @@ -813,20 +813,27 @@ loop.shared.views = (function(_, mozL10n) { "shared/img/icons-16x16.svg#globe"; } + var wrapperClasses = React.addons.classSet({ + "context-wrapper": true, + "clicks-allowed": this.props.allowClick + }); + return (
{this.renderContextTitle()} - +
); } @@ -988,7 +995,7 @@ loop.shared.views = (function(_, mozL10n) { componentWillReceiveProps: function(nextProps) { // This is all for the ui-showcase's benefit. - if (this.props.matchMedia != nextProps.matchMedia) { + if (this.props.matchMedia !== nextProps.matchMedia) { this.updateLocalMediaState(null, nextProps.matchMedia); } }, @@ -1003,7 +1010,7 @@ loop.shared.views = (function(_, mozL10n) { updateLocalMediaState: function(event, matchMedia) { var newState = this.isLocalMediaAbsolutelyPositioned(matchMedia); - if (this.state.localMediaAboslutelyPositioned != newState) { + if (this.state.localMediaAboslutelyPositioned !== newState) { this.setState({ localMediaAboslutelyPositioned: newState }); diff --git a/browser/components/loop/standalone/content/js/standaloneMetricsStore.js b/browser/components/loop/standalone/content/js/standaloneMetricsStore.js index bae0ca6a8a8..614750a80e7 100644 --- a/browser/components/loop/standalone/content/js/standaloneMetricsStore.js +++ b/browser/components/loop/standalone/content/js/standaloneMetricsStore.js @@ -50,7 +50,8 @@ loop.store.StandaloneMetricsStore = (function() { "mediaConnected", "recordClick", "remotePeerConnected", - "retryAfterRoomFailure" + "retryAfterRoomFailure", + "windowUnload" ], /** @@ -257,6 +258,17 @@ loop.store.StandaloneMetricsStore = (function() { var muteState = muted ? "mute" : "unmute"; this._storeEvent(METRICS_GA_CATEGORY.general, muteType, muteState); + }, + + /** + * Called when the window is unloaded, either by code, or by the user + * explicitly closing it. Expected to do any necessary housekeeping, such + * as shutting down the call cleanly and adding any relevant telemetry data. + */ + windowUnload: function() { + if (this.activeRoomStore) { + this.stopListening(this.activeRoomStore); + } } }); diff --git a/browser/components/loop/standalone/content/js/standaloneRoomViews.js b/browser/components/loop/standalone/content/js/standaloneRoomViews.js index 7802b2aac1f..b1537c5e571 100644 --- a/browser/components/loop/standalone/content/js/standaloneRoomViews.js +++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.js @@ -90,6 +90,29 @@ loop.standaloneRoomViews = (function(mozL10n) { roomUsed: React.PropTypes.bool.isRequired }, + componentDidMount: function() { + // Watch for messages from the waiting-tile iframe + window.addEventListener("message", this.recordTileClick); + }, + + componentWillUnmount: function() { + window.removeEventListener("message", this.recordTileClick); + }, + + recordTileClick: function(event) { + if (event.data === "tile-click") { + this.props.dispatcher.dispatch(new sharedActions.RecordClick({ + linkInfo: "Tiles iframe click" + })); + } + }, + + recordTilesSupport: function() { + this.props.dispatcher.dispatch(new sharedActions.RecordClick({ + linkInfo: "Tiles support link click" + })); + }, + _renderCallToActionLink: function() { if (this.props.isFirefox) { return ( @@ -155,7 +178,7 @@ loop.standaloneRoomViews = (function(mozL10n) { React.createElement("p", {className: "room-waiting-area"}, mozL10n.get("rooms_read_while_wait_offer"), React.createElement("a", {href: loop.config.tilesSupportUrl, - onClick: this.recordClick, + onClick: this.recordTilesSupport, rel: "noreferrer", target: "_blank"}, React.createElement("i", {className: "room-waiting-help"}) @@ -414,6 +437,8 @@ loop.standaloneRoomViews = (function(mozL10n) { case ROOM_STATES.FAILED: case ROOM_STATES.CLOSING: + case ROOM_STATES.FULL: + case ROOM_STATES.ENDED: // the other person has shown up, so we don't want to show an avatar return true; @@ -519,6 +544,7 @@ loop.standaloneRoomViews = (function(mozL10n) { return { StandaloneRoomFooter: StandaloneRoomFooter, StandaloneRoomHeader: StandaloneRoomHeader, + StandaloneRoomInfoArea: StandaloneRoomInfoArea, StandaloneRoomView: StandaloneRoomView }; })(navigator.mozL10n); diff --git a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx index ebc5fed9866..48398a9ce60 100644 --- a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx +++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx @@ -90,6 +90,29 @@ loop.standaloneRoomViews = (function(mozL10n) { roomUsed: React.PropTypes.bool.isRequired }, + componentDidMount: function() { + // Watch for messages from the waiting-tile iframe + window.addEventListener("message", this.recordTileClick); + }, + + componentWillUnmount: function() { + window.removeEventListener("message", this.recordTileClick); + }, + + recordTileClick: function(event) { + if (event.data === "tile-click") { + this.props.dispatcher.dispatch(new sharedActions.RecordClick({ + linkInfo: "Tiles iframe click" + })); + } + }, + + recordTilesSupport: function() { + this.props.dispatcher.dispatch(new sharedActions.RecordClick({ + linkInfo: "Tiles support link click" + })); + }, + _renderCallToActionLink: function() { if (this.props.isFirefox) { return ( @@ -155,7 +178,7 @@ loop.standaloneRoomViews = (function(mozL10n) {

{mozL10n.get("rooms_read_while_wait_offer")} @@ -414,6 +437,8 @@ loop.standaloneRoomViews = (function(mozL10n) { case ROOM_STATES.FAILED: case ROOM_STATES.CLOSING: + case ROOM_STATES.FULL: + case ROOM_STATES.ENDED: // the other person has shown up, so we don't want to show an avatar return true; @@ -519,6 +544,7 @@ loop.standaloneRoomViews = (function(mozL10n) { return { StandaloneRoomFooter: StandaloneRoomFooter, StandaloneRoomHeader: StandaloneRoomHeader, + StandaloneRoomInfoArea: StandaloneRoomInfoArea, StandaloneRoomView: StandaloneRoomView }; })(navigator.mozL10n); diff --git a/browser/components/loop/standalone/content/js/webapp.js b/browser/components/loop/standalone/content/js/webapp.js index 13789b527ae..54d801fe589 100644 --- a/browser/components/loop/standalone/content/js/webapp.js +++ b/browser/components/loop/standalone/content/js/webapp.js @@ -93,7 +93,7 @@ loop.webapp = (function($, _, OT, mozL10n) { render: function() { if (this.props.isFirefox) { - return React.createElement("div", null); + return null; } return ( React.createElement("div", {className: "promote-firefox"}, @@ -649,8 +649,8 @@ loop.webapp = (function($, _, OT, mozL10n) { React.PropTypes.instanceOf(FxOSConversationModel) ]).isRequired, dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired, - notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection) - .isRequired, + isFirefox: React.PropTypes.bool.isRequired, + notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection).isRequired, sdk: React.PropTypes.object.isRequired }, @@ -738,7 +738,7 @@ loop.webapp = (function($, _, OT, mozL10n) { } case "expired": { return ( - React.createElement(CallUrlExpiredView, null) + React.createElement(CallUrlExpiredView, {isFirefox: this.props.isFirefox}) ); } default: { @@ -986,6 +986,7 @@ loop.webapp = (function($, _, OT, mozL10n) { client: this.props.client, conversation: this.props.conversation, dispatcher: this.props.dispatcher, + isFirefox: this.state.isFirefox, notifications: this.props.notifications, sdk: this.props.sdk}) ); diff --git a/browser/components/loop/standalone/content/js/webapp.jsx b/browser/components/loop/standalone/content/js/webapp.jsx index f24fd4c1a9b..242aaed2799 100644 --- a/browser/components/loop/standalone/content/js/webapp.jsx +++ b/browser/components/loop/standalone/content/js/webapp.jsx @@ -93,7 +93,7 @@ loop.webapp = (function($, _, OT, mozL10n) { render: function() { if (this.props.isFirefox) { - return

; + return null; } return (
@@ -649,8 +649,8 @@ loop.webapp = (function($, _, OT, mozL10n) { React.PropTypes.instanceOf(FxOSConversationModel) ]).isRequired, dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired, - notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection) - .isRequired, + isFirefox: React.PropTypes.bool.isRequired, + notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection).isRequired, sdk: React.PropTypes.object.isRequired }, @@ -738,7 +738,7 @@ loop.webapp = (function($, _, OT, mozL10n) { } case "expired": { return ( - + ); } default: { @@ -986,6 +986,7 @@ loop.webapp = (function($, _, OT, mozL10n) { client={this.props.client} conversation={this.props.conversation} dispatcher={this.props.dispatcher} + isFirefox={this.state.isFirefox} notifications={this.props.notifications} sdk={this.props.sdk} /> ); diff --git a/browser/components/loop/test/desktop-local/client_test.js b/browser/components/loop/test/desktop-local/client_test.js index ac5b7c0cba6..c8dde7732ab 100644 --- a/browser/components/loop/test/desktop-local/client_test.js +++ b/browser/components/loop/test/desktop-local/client_test.js @@ -112,7 +112,7 @@ describe("loop.Client", function() { sinon.assert.calledOnce(callback); sinon.assert.calledWithExactly(callback, sinon.match(function(err) { - return err.code == 400 && err.message == "invalid token"; + return err.code === 400 && err.message === "invalid token"; })); }); diff --git a/browser/components/loop/test/desktop-local/contacts_test.js b/browser/components/loop/test/desktop-local/contacts_test.js index ef59f303f11..930d474ee24 100644 --- a/browser/components/loop/test/desktop-local/contacts_test.js +++ b/browser/components/loop/test/desktop-local/contacts_test.js @@ -87,21 +87,21 @@ describe("loop.contacts", function() { navigator.mozLoop = { getStrings: function(entityName) { var textContentValue = "fakeText"; - if (entityName == "add_contact_title") { + if (entityName === "add_contact_title") { textContentValue = fakeAddContactTitleText; - } else if (entityName == "add_contact_button") { + } else if (entityName === "add_contact_button") { textContentValue = fakeAddContactButtonText; - } else if (entityName == "edit_contact_title") { + } else if (entityName === "edit_contact_title") { textContentValue = fakeEditContactButtonText; - } else if (entityName == "edit_contact_done_button") { + } else if (entityName === "edit_contact_done_button") { textContentValue = fakeDoneButtonText; } return JSON.stringify({textContent: textContentValue}); }, getLoopPref: function(pref) { - if (pref == "contacts.gravatars.promo") { + if (pref === "contacts.gravatars.promo") { return true; - } else if (pref == "contacts.gravatars.show") { + } else if (pref === "contacts.gravatars.show") { return false; } return ""; @@ -170,9 +170,9 @@ describe("loop.contacts", function() { it("should not show the gravatars promo box when the 'contacts.gravatars.promo' pref is set", function() { sandbox.stub(navigator.mozLoop, "getLoopPref", function(pref) { - if (pref == "contacts.gravatars.promo") { + if (pref === "contacts.gravatars.promo") { return false; - } else if (pref == "contacts.gravatars.show") { + } else if (pref === "contacts.gravatars.show") { return true; } return ""; diff --git a/browser/components/loop/test/desktop-local/conversation_test.js b/browser/components/loop/test/desktop-local/conversation_test.js index 2b66566e9d3..d38cb8c1d5f 100644 --- a/browser/components/loop/test/desktop-local/conversation_test.js +++ b/browser/components/loop/test/desktop-local/conversation_test.js @@ -26,7 +26,7 @@ describe("loop.conversation", function() { }, setLoopPref: setLoopPrefStub, getLoopPref: function(prefName) { - if (prefName == "debug.sdk") { + if (prefName === "debug.sdk") { return false; } diff --git a/browser/components/loop/test/desktop-local/roomViews_test.js b/browser/components/loop/test/desktop-local/roomViews_test.js index a059c2398ee..083e93d4b74 100644 --- a/browser/components/loop/test/desktop-local/roomViews_test.js +++ b/browser/components/loop/test/desktop-local/roomViews_test.js @@ -295,7 +295,7 @@ describe("loop.roomViews", function () { beforeEach(function() { sandbox.stub(dispatcher, "dispatch"); fakeMozLoop.getLoopPref = function(prefName) { - if (prefName == "contextInConversations.enabled") { + if (prefName === "contextInConversations.enabled") { return true; } return "test"; diff --git a/browser/components/loop/test/index.html b/browser/components/loop/test/index.html index 80c2d297024..4bb1a2783bd 100644 --- a/browser/components/loop/test/index.html +++ b/browser/components/loop/test/index.html @@ -14,6 +14,7 @@
  • Local tests
  • Standalone tests
  • Code Coverage
  • +
  • UI Showcase
  • diff --git a/browser/components/loop/test/karma/stubs.js b/browser/components/loop/test/karma/head.js similarity index 55% rename from browser/components/loop/test/karma/stubs.js rename to browser/components/loop/test/karma/head.js index 4209e631088..14f04408f71 100644 --- a/browser/components/loop/test/karma/stubs.js +++ b/browser/components/loop/test/karma/head.js @@ -5,4 +5,7 @@ // DOMContentLoaded proved to lead to race conditions. sinon.stub(document, "addEventListener"); -console.log("[stubs.js] addEventListener stubbed to prevent race conditions"); +console.log("[head.js] addEventListener stubbed to prevent race conditions"); + +document.body.appendChild(document.createElement("div")).id = "fixtures"; +console.log("[head.js] div#fixtures added to attach DOM elements"); diff --git a/browser/components/loop/test/karma/karma.coverage.desktop.js b/browser/components/loop/test/karma/karma.coverage.desktop.js index aa57903c354..1e85ad0d699 100644 --- a/browser/components/loop/test/karma/karma.coverage.desktop.js +++ b/browser/components/loop/test/karma/karma.coverage.desktop.js @@ -16,7 +16,7 @@ module.exports = function(config) { "content/shared/libs/lodash-3.9.3.js", "content/shared/libs/backbone-1.2.1.js", "test/shared/vendor/*.js", - "test/karma/stubs.js", // Stub out DOM event listener due to races. + "test/karma/head.js", // Stub out DOM event listener due to races. "content/shared/js/utils.js", "content/shared/js/models.js", "content/shared/js/mixins.js", diff --git a/browser/components/loop/test/karma/karma.coverage.shared_standalone.js b/browser/components/loop/test/karma/karma.coverage.shared_standalone.js index 841e80db7bb..fafa4081f48 100644 --- a/browser/components/loop/test/karma/karma.coverage.shared_standalone.js +++ b/browser/components/loop/test/karma/karma.coverage.shared_standalone.js @@ -17,6 +17,7 @@ module.exports = function(config) { "content/shared/libs/react-0.12.2.js", "content/shared/libs/sdk.js", "test/shared/vendor/*.js", + "test/karma/head.js", // Add test fixture container "content/shared/js/utils.js", "content/shared/js/store.js", "content/shared/js/models.js", diff --git a/browser/components/loop/test/shared/textChatView_test.js b/browser/components/loop/test/shared/textChatView_test.js index 40a529c8a29..400f5ee2126 100644 --- a/browser/components/loop/test/shared/textChatView_test.js +++ b/browser/components/loop/test/shared/textChatView_test.js @@ -10,6 +10,7 @@ describe("loop.shared.views.TextChatView", function () { var TestUtils = React.addons.TestUtils; var CHAT_MESSAGE_TYPES = loop.store.CHAT_MESSAGE_TYPES; var CHAT_CONTENT_TYPES = loop.store.CHAT_CONTENT_TYPES; + var fixtures = document.querySelector("#fixtures"); var dispatcher, fakeSdkDriver, sandbox, store, fakeClock; @@ -35,6 +36,7 @@ describe("loop.shared.views.TextChatView", function () { afterEach(function() { sandbox.restore(); + React.unmountComponentAtNode(fixtures); }); describe("TextChatEntriesView", function() { @@ -52,6 +54,18 @@ describe("loop.shared.views.TextChatView", function () { _.extend(basicProps, extraProps))); } + function mountAsRealComponent(extraProps, container) { + var basicProps = { + dispatcher: dispatcher, + messageList: [], + useDesktopPaths: false + }; + + return React.render( + React.createElement(loop.shared.views.chat.TextChatEntriesView, + _.extend(basicProps, extraProps)), container); + } + beforeEach(function() { store.setStoreState({ textChatEnabled: true }); }); @@ -208,6 +222,115 @@ describe("loop.shared.views.TextChatView", function () { expect(node.querySelectorAll(".text-chat-entry-timestamp").length) .to.eql(1); }); + + describe("Scrolling", function() { + beforeEach(function() { + sandbox.stub(window, "requestAnimationFrame", function(callback) { + callback(); + }); + + // We're using scrolling, so we need to mount as a real one. + view = mountAsRealComponent({}, fixtures); + sandbox.stub(view, "play"); + + // We need some basic styling to ensure scrolling. + view.getDOMNode().style.overflow = "scroll"; + view.getDOMNode().style["max-height"] = "4ch"; + }); + + it("should scroll when a text message is added", function() { + var messageList = [{ + type: CHAT_MESSAGE_TYPES.RECEIVED, + contentType: CHAT_CONTENT_TYPES.TEXT, + message: "Hello!", + receivedTimestamp: "2015-06-25T17:53:55.357Z" + }]; + + view.setProps({ messageList: messageList }); + + node = view.getDOMNode(); + + expect(node.scrollTop).eql(node.scrollHeight - node.clientHeight); + }); + + it("should not scroll when a context tile is added", function() { + var messageList = [{ + type: CHAT_MESSAGE_TYPES.SPECIAL, + contentType: CHAT_CONTENT_TYPES.CONTEXT, + message: "Awesome!", + extraData: { + location: "http://invalid.com" + } + }]; + + view.setProps({ messageList: messageList }); + + node = view.getDOMNode(); + + expect(node.scrollTop).eql(0); + }); + + it("should scroll when a message is received after a context tile", function() { + // The context tile. + var messageList = [{ + type: CHAT_MESSAGE_TYPES.SPECIAL, + contentType: CHAT_CONTENT_TYPES.CONTEXT, + message: "Awesome!", + extraData: { + location: "http://invalid.com" + } + }]; + + view.setProps({ messageList: messageList }); + + // Now add a message. Don't use the same list as this is a shared object, + // that messes with React. + var messageList1 = [ + messageList[0], { + type: CHAT_MESSAGE_TYPES.RECEIVED, + contentType: CHAT_CONTENT_TYPES.TEXT, + message: "Hello!", + receivedTimestamp: "2015-06-25T17:53:55.357Z" + } + ]; + + view.setProps({ messageList: messageList1 }); + + node = view.getDOMNode(); + + expect(node.scrollTop).eql(node.scrollHeight - node.clientHeight); + + }); + + it("should not scroll when receiving a message and the scroll is not at the bottom", function() { + node = view.getDOMNode(); + + var messageList = [{ + type: CHAT_MESSAGE_TYPES.RECEIVED, + contentType: CHAT_CONTENT_TYPES.TEXT, + message: "Hello!", + receivedTimestamp: "2015-06-25T17:53:55.357Z" + }]; + + view.setProps({ messageList: messageList }); + + node.scrollTop = 0; + + // Don't use the same list as this is a shared object, that messes with React. + var messageList1 = [ + messageList[0], { + type: CHAT_MESSAGE_TYPES.RECEIVED, + contentType: CHAT_CONTENT_TYPES.TEXT, + message: "Hello!", + receivedTimestamp: "2015-06-25T17:53:55.357Z" + } + ]; + + view.setProps({ messageList: messageList1 }); + + expect(node.scrollTop).eql(0); + }); + }); }); describe("TextChatEntry", function() { @@ -285,6 +408,10 @@ describe("loop.shared.views.TextChatView", function () { // Fake server to catch all XHR requests. fakeServer = sinon.fakeServer.create(); store.setStoreState({ textChatEnabled: true }); + + sandbox.stub(navigator.mozL10n, "get", function(string) { + return string; + }); }); afterEach(function() { @@ -482,5 +609,34 @@ describe("loop.shared.views.TextChatView", function () { sinon.assert.notCalled(dispatcher.dispatch); }); + + it("should show a placeholder when no messages have been sent", function() { + view = mountTestComponent(); + + store.receivedTextChatMessage({ + contentType: CHAT_CONTENT_TYPES.TEXT, + message: "Foo", + sentTimestamp: "1970-01-01T00:03:00.000Z", + receivedTimestamp: "1970-01-01T00:03:00.000Z" + }); + + var textBox = view.getDOMNode().querySelector(".text-chat-box input"); + + expect(textBox.placeholder).contain("placeholder"); + }); + + it("should not show a placeholder when messages have been sent", function() { + view = mountTestComponent(); + + store.sendTextChatMessage({ + contentType: CHAT_CONTENT_TYPES.TEXT, + message: "Foo", + sentTimestamp: "2015-06-25T17:53:55.357Z" + }); + + var textBox = view.getDOMNode().querySelector(".text-chat-box input"); + + expect(textBox.placeholder).not.contain("placeholder"); + }); }); }); diff --git a/browser/components/loop/test/shared/views_test.js b/browser/components/loop/test/shared/views_test.js index 67d282e9e29..d0b2952bca2 100644 --- a/browser/components/loop/test/shared/views_test.js +++ b/browser/components/loop/test/shared/views_test.js @@ -850,6 +850,28 @@ describe("loop.shared.views", function() { React.createElement(sharedViews.ContextUrlView, props)); } + it("should set a clicks-allowed class if clicks are allowed", function() { + view = mountTestComponent({ + allowClick: true, + url: "http://wonderful.invalid" + }); + + var wrapper = view.getDOMNode().querySelector(".context-wrapper"); + + expect(wrapper.classList.contains("clicks-allowed")).eql(true); + }); + + it("should not set a clicks-allowed class if clicks are not allowed", function() { + view = mountTestComponent({ + allowClick: false, + url: "http://wonderful.invalid" + }); + + var wrapper = view.getDOMNode().querySelector(".context-wrapper"); + + expect(wrapper.classList.contains("clicks-allowed")).eql(false); + }); + it("should display nothing if the url is invalid", function() { view = mountTestComponent({ url: "fjrTykyw" @@ -900,7 +922,7 @@ describe("loop.shared.views", function() { url: "http://wonderful.invalid" }); - expect(view.getDOMNode().querySelector(".context-url").href) + expect(view.getDOMNode().querySelector(".context-wrapper").href) .eql("http://wonderful.invalid/"); }); @@ -910,7 +932,7 @@ describe("loop.shared.views", function() { url: "http://wonderful.invalid" }); - var linkNode = view.getDOMNode().querySelector(".context-url"); + var linkNode = view.getDOMNode().querySelector(".context-wrapper"); TestUtils.Simulate.click(linkNode); @@ -920,6 +942,19 @@ describe("loop.shared.views", function() { linkInfo: "Shared URL" })); }); + + it("should not dispatch an action if clicks are not allowed", function() { + view = mountTestComponent({ + allowClick: false, + url: "http://wonderful.invalid" + }); + + var linkNode = view.getDOMNode().querySelector(".context-wrapper"); + + TestUtils.Simulate.click(linkNode); + + sinon.assert.notCalled(dispatcher.dispatch); + }); }); describe("MediaView", function() { diff --git a/browser/components/loop/test/standalone/index.html b/browser/components/loop/test/standalone/index.html index 08ffebc438c..396c60200b6 100644 --- a/browser/components/loop/test/standalone/index.html +++ b/browser/components/loop/test/standalone/index.html @@ -84,8 +84,8 @@ }); describe("Unexpected Warnings Check", function() { - it("should long only the warnings we expect", function() { - chai.expect(caughtWarnings.length).to.eql(10); + it("should log only the warnings we expect", function() { + chai.expect(caughtWarnings.length).to.eql(0); }); }); diff --git a/browser/components/loop/test/standalone/standaloneMetricsStore_test.js b/browser/components/loop/test/standalone/standaloneMetricsStore_test.js index 6b86bfb6f98..e256dbdb7c5 100644 --- a/browser/components/loop/test/standalone/standaloneMetricsStore_test.js +++ b/browser/components/loop/test/standalone/standaloneMetricsStore_test.js @@ -195,5 +195,22 @@ describe("loop.store.StandaloneMetricsStore", function() { "send", "event", METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.audioMute, "mute"); }); + + describe("Event listeners", function() { + it("should call windowUnload when action is dispatched", function() { + sandbox.stub(store, "windowUnload"); + + dispatcher.dispatch(new sharedActions.WindowUnload()); + sinon.assert.calledOnce(store.windowUnload); + }); + + it("should stop listening to activeRoomStore", function() { + var stopListeningStub = sandbox.stub(store, "stopListening"); + store.windowUnload(); + + sinon.assert.calledOnce(stopListeningStub); + sinon.assert.calledWithExactly(stopListeningStub, store.activeRoomStore); + }); + }); }); }); diff --git a/browser/components/loop/test/standalone/standaloneRoomViews_test.js b/browser/components/loop/test/standalone/standaloneRoomViews_test.js index 3f9b8d5c04f..279f54e91f8 100644 --- a/browser/components/loop/test/standalone/standaloneRoomViews_test.js +++ b/browser/components/loop/test/standalone/standaloneRoomViews_test.js @@ -14,6 +14,7 @@ describe("loop.standaloneRoomViews", function() { var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES; var sharedActions = loop.shared.actions; var sharedUtils = loop.shared.utils; + var fixtures = document.querySelector("#fixtures"); var sandbox, dispatcher, activeRoomStore, dispatch; var fakeWindow; @@ -60,6 +61,7 @@ describe("loop.standaloneRoomViews", function() { afterEach(function() { loop.shared.mixins.setRootObject(window); sandbox.restore(); + React.unmountComponentAtNode(fixtures); }); describe("StandaloneRoomHeader", function() { @@ -84,6 +86,39 @@ describe("loop.standaloneRoomViews", function() { }); }); + describe("StandaloneRoomInfoArea in fixture", function() { + it("should dispatch a RecordClick action when the tile is clicked", function(done) { + // Point the iframe to a page that will auto-"click" + loop.config.tilesIframeUrl = "data:text/html,"; + + // Render the iframe into the fixture to cause it to load + React.render( + React.createElement( + loop.standaloneRoomViews.StandaloneRoomInfoArea, { + activeRoomStore: activeRoomStore, + dispatcher: dispatcher, + isFirefox: true, + joinRoom: sandbox.stub(), + roomState: ROOM_STATES.JOINED, + roomUsed: false + }), fixtures); + + // Wait for the iframe to load and trigger a message that should also + // cause the RecordClick action + window.addEventListener("message", function onMessage() { + window.removeEventListener("message", onMessage); + + sinon.assert.calledOnce(dispatcher.dispatch); + sinon.assert.calledWithExactly(dispatcher.dispatch, + new sharedActions.RecordClick({ + linkInfo: "Tiles iframe click" + })); + + done(); + }); + }); + }); + describe("StandaloneRoomView", function() { function mountTestComponent() { return TestUtils.renderIntoDocument( @@ -181,20 +216,6 @@ describe("loop.standaloneRoomViews", function() { .not.eql(null); }); - it("should display a waiting room message and tile iframe on JOINED", - function() { - var DUMMY_TILE_URL = "http://tile/"; - loop.config.tilesIframeUrl = DUMMY_TILE_URL; - activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED}); - - expect(view.getDOMNode().querySelector(".room-waiting-area")) - .not.eql(null); - - var tile = view.getDOMNode().querySelector(".room-waiting-tile"); - expect(tile).not.eql(null); - expect(tile.src).eql(DUMMY_TILE_URL); - }); - it("should display an empty room message on SESSION_CONNECTED", function() { activeRoomStore.setStoreState({roomState: ROOM_STATES.SESSION_CONNECTED}); @@ -212,6 +233,33 @@ describe("loop.standaloneRoomViews", function() { }); }); + describe("Empty room tile offer", function() { + it("should display a waiting room message and tile iframe on JOINED", function() { + var DUMMY_TILE_URL = "http://tile/"; + loop.config.tilesIframeUrl = DUMMY_TILE_URL; + activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED}); + + expect(view.getDOMNode().querySelector(".room-waiting-area")).not.eql(null); + + var tile = view.getDOMNode().querySelector(".room-waiting-tile"); + expect(tile).not.eql(null); + expect(tile.src).eql(DUMMY_TILE_URL); + }); + + it("should dispatch a RecordClick action when the tile support link is clicked", function() { + activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED}); + + TestUtils.Simulate.click(view.getDOMNode().querySelector(".room-waiting-area a")); + + sinon.assert.calledOnce(dispatcher.dispatch); + sinon.assert.calledWithExactly(dispatcher.dispatch, + new sharedActions.RecordClick({ + linkInfo: "Tiles support link click" + })); + }); + + }); + describe("Prompt media message", function() { it("should display a prompt for user media on MEDIA_WAIT", function() { diff --git a/browser/components/loop/test/standalone/webapp_test.js b/browser/components/loop/test/standalone/webapp_test.js index 263e79814c5..3528e7bb3ba 100644 --- a/browser/components/loop/test/standalone/webapp_test.js +++ b/browser/components/loop/test/standalone/webapp_test.js @@ -17,6 +17,7 @@ describe("loop.webapp", function() { stubGetPermsAndCacheMedia, fakeAudioXHR, dispatcher, + mozL10nGet, WEBSOCKET_REASONS = loop.shared.utils.WEBSOCKET_REASONS; beforeEach(function() { @@ -27,6 +28,10 @@ describe("loop.webapp", function() { stubGetPermsAndCacheMedia = sandbox.stub( loop.standaloneMedia._MultiplexGum.prototype, "getPermsAndCacheMedia"); + mozL10nGet = sandbox.stub(navigator.mozL10n, "get", function(x) { + return "translated:" + x; + }); + fakeAudioXHR = { open: sinon.spy(), send: function() {}, @@ -114,6 +119,7 @@ describe("loop.webapp", function() { ocView = mountTestComponent({ client: client, conversation: conversation, + isFirefox: true, notifications: notifications, sdk: { on: sandbox.stub() @@ -677,8 +683,7 @@ describe("loop.webapp", function() { it("should display the UnsupportedDeviceView for `unsupportedDevice` window type", function() { - standaloneAppStore.setStoreState({windowType: "unsupportedDevice"}); - + standaloneAppStore.setStoreState({windowType: "unsupportedDevice", unsupportedPlatform: "ios"}); var webappRootView = mountTestComponent(); TestUtils.findRenderedComponentWithType(webappRootView, @@ -687,7 +692,7 @@ describe("loop.webapp", function() { it("should display the UnsupportedBrowserView for `unsupportedBrowser` window type", function() { - standaloneAppStore.setStoreState({windowType: "unsupportedBrowser"}); + standaloneAppStore.setStoreState({windowType: "unsupportedBrowser", isFirefox: false}); var webappRootView = mountTestComponent(); @@ -697,7 +702,7 @@ describe("loop.webapp", function() { it("should display the OutgoingConversationView for `outgoing` window type", function() { - standaloneAppStore.setStoreState({windowType: "outgoing"}); + standaloneAppStore.setStoreState({windowType: "outgoing", isFirefox: true}); var webappRootView = mountTestComponent(); @@ -707,7 +712,7 @@ describe("loop.webapp", function() { it("should display the StandaloneRoomView for `room` window type", function() { - standaloneAppStore.setStoreState({windowType: "room"}); + standaloneAppStore.setStoreState({windowType: "room", isFirefox: true}); var webappRootView = mountTestComponent(); @@ -716,7 +721,7 @@ describe("loop.webapp", function() { }); it("should display the HomeView for `home` window type", function() { - standaloneAppStore.setStoreState({windowType: "home"}); + standaloneAppStore.setStoreState({windowType: "home", isFirefox: true}); var webappRootView = mountTestComponent(); @@ -1090,19 +1095,19 @@ describe("loop.webapp", function() { var comp = TestUtils.renderIntoDocument( React.createElement(loop.webapp.PromoteFirefoxView, { isFirefox: true - })); + })); - expect(comp.getDOMNode().querySelectorAll("h3").length).eql(0); + expect(comp.getDOMNode()).eql(null); }); it("should render when not using Firefox", function() { var comp = TestUtils.renderIntoDocument( - React.createElement( - loop.webapp.PromoteFirefoxView, { + React.createElement(loop.webapp.PromoteFirefoxView, { isFirefox: false - })); + })); - expect(comp.getDOMNode().querySelectorAll("h3").length).eql(1); + sinon.assert.calledWith(mozL10nGet, "promote_firefox_hello_heading"); + sinon.assert.calledWith(mozL10nGet, "get_firefox_button"); }); }); }); diff --git a/browser/components/loop/ui/ui-showcase.js b/browser/components/loop/ui/ui-showcase.js index fe235800525..5eeb933dca0 100644 --- a/browser/components/loop/ui/ui-showcase.js +++ b/browser/components/loop/ui/ui-showcase.js @@ -387,7 +387,7 @@ roomOwner: "fake", roomUrl: "http://showcase", urls: [{ - description: "A wonderful page!", + description: "1171925 - Clicking the title or favicon for context (in the conversation/standalone windows) should appear to be part of the link and open the webpage", location: "http://wonderful.invalid" // use the fallback thumbnail }] @@ -1541,7 +1541,7 @@ // This simulates the mocha layout for errors which means we can run // this alongside our other unit tests but use the same harness. - var expectedWarningsCount = 18; + var expectedWarningsCount = 16; var warningsMismatch = caughtWarnings.length !== expectedWarningsCount; if (uncaughtError || warningsMismatch) { $("#results").append("
    " + diff --git a/browser/components/loop/ui/ui-showcase.jsx b/browser/components/loop/ui/ui-showcase.jsx index dbece783126..be6e7880a0a 100644 --- a/browser/components/loop/ui/ui-showcase.jsx +++ b/browser/components/loop/ui/ui-showcase.jsx @@ -387,7 +387,7 @@ roomOwner: "fake", roomUrl: "http://showcase", urls: [{ - description: "A wonderful page!", + description: "1171925 - Clicking the title or favicon for context (in the conversation/standalone windows) should appear to be part of the link and open the webpage", location: "http://wonderful.invalid" // use the fallback thumbnail }] @@ -1541,7 +1541,7 @@ // This simulates the mocha layout for errors which means we can run // this alongside our other unit tests but use the same harness. - var expectedWarningsCount = 18; + var expectedWarningsCount = 16; var warningsMismatch = caughtWarnings.length !== expectedWarningsCount; if (uncaughtError || warningsMismatch) { $("#results").append("
    " + diff --git a/browser/devtools/responsivedesign/responsivedesign-child.js b/browser/devtools/responsivedesign/responsivedesign-child.js index a20d4e665e5..3d273520e1a 100644 --- a/browser/devtools/responsivedesign/responsivedesign-child.js +++ b/browser/devtools/responsivedesign/responsivedesign-child.js @@ -113,7 +113,10 @@ function screenshot() { } let WebProgressListener = { - onLocationChange: function onLocationChange(aWebProgress) { + onLocationChange(webProgress, request, URI, flags) { + if (flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) { + return; + } makeScrollbarsFloating(); }, QueryInterface: function QueryInterface(aIID) { diff --git a/browser/devtools/shared/widgets/spectrum.css b/browser/devtools/shared/widgets/spectrum.css index 7cb23c1e5b7..bb943f5235d 100644 --- a/browser/devtools/shared/widgets/spectrum.css +++ b/browser/devtools/shared/widgets/spectrum.css @@ -33,7 +33,7 @@ background-position: -48px center; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { #eyedropper-button { background-image: url("chrome://browser/skin/devtools/command-eyedropper@2x.png"); } diff --git a/browser/devtools/sourceeditor/codemirror/mozilla.css b/browser/devtools/sourceeditor/codemirror/mozilla.css index c90a693289d..455d8aee177 100644 --- a/browser/devtools/sourceeditor/codemirror/mozilla.css +++ b/browser/devtools/sourceeditor/codemirror/mozilla.css @@ -42,7 +42,7 @@ position: relative; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .breakpoint { background-image: url("chrome://browser/skin/devtools/editor-breakpoint@2x.png"); } @@ -56,7 +56,7 @@ background-image: url("chrome://browser/skin/devtools/editor-debug-location.png"); } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .debugLocation { background-image: url("chrome://browser/skin/devtools/editor-debug-location@2x.png"); } @@ -68,7 +68,7 @@ url("chrome://browser/skin/devtools/editor-breakpoint.png"); } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .breakpoint.debugLocation { background-image: url("chrome://browser/skin/devtools/editor-debug-location@2x.png"), diff --git a/browser/devtools/webconsole/webconsole.js b/browser/devtools/webconsole/webconsole.js index 6e8c9a725f8..57b0cb8dfbd 100644 --- a/browser/devtools/webconsole/webconsole.js +++ b/browser/devtools/webconsole/webconsole.js @@ -65,7 +65,7 @@ const SEARCH_DELAY = 200; // The number of lines that are displayed in the console output by default, for // each category. The user can change this number by adjusting the hidden // "devtools.hud.loglimit.{network,cssparser,exception,console}" preferences. -const DEFAULT_LOG_LIMIT = 200; +const DEFAULT_LOG_LIMIT = 1000; // The various categories of messages. We start numbering at zero so we can // use these as indexes into the MESSAGE_PREFERENCE_KEYS matrix below. diff --git a/browser/themes/linux/browser.css b/browser/themes/linux/browser.css index e8c50c352db..5b9d993c66a 100644 --- a/browser/themes/linux/browser.css +++ b/browser/themes/linux/browser.css @@ -1189,12 +1189,10 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action- } #urlbar-reload-button:not([disabled]):hover { - background-image: radial-gradient(circle closest-side, hsla(200,100%,70%,.2), transparent); -moz-image-region: rect(14px, 14px, 28px, 0); } #urlbar-reload-button:not([disabled]):hover:active { - background-image: radial-gradient(circle closest-side, hsla(200,100%,60%,.1), transparent); -moz-image-region: rect(28px, 14px, 42px, 0); } @@ -1207,12 +1205,10 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action- } #urlbar-go-button:hover { - background-image: radial-gradient(circle closest-side, hsla(110,70%,50%,.2), transparent); -moz-image-region: rect(14px, 42px, 28px, 28px); } #urlbar-go-button:hover:active { - background-image: radial-gradient(circle closest-side, hsla(110,70%,50%,.1), transparent); -moz-image-region: rect(28px, 42px, 42px, 28px); } @@ -1225,12 +1221,10 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action- } #urlbar-stop-button:not([disabled]):hover { - background-image: radial-gradient(circle closest-side, hsla(5,100%,75%,.3), transparent); -moz-image-region: rect(14px, 28px, 28px, 14px); } #urlbar-stop-button:hover:active { - background-image: radial-gradient(circle closest-side, hsla(5,100%,75%,.1), transparent); -moz-image-region: rect(28px, 28px, 42px, 14px); } diff --git a/browser/themes/osx/browser.css b/browser/themes/osx/browser.css index 349613d7c54..5207c706a87 100644 --- a/browser/themes/osx/browser.css +++ b/browser/themes/osx/browser.css @@ -1716,7 +1716,6 @@ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-ba .urlbar-history-dropmarker[open="true"], .urlbar-history-dropmarker:hover:active { -moz-image-region: var(--urlbar-dropmarker-active-region); - background-image: radial-gradient(circle closest-side, hsla(205,100%,70%,.3), transparent); } @media (min-resolution: 2dppx) { @@ -1743,11 +1742,6 @@ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-ba padding: 0 3px; } -.urlbar-icon[open="true"], -.urlbar-icon:hover:active { - background-image: radial-gradient(circle closest-side, hsla(205,100%,70%,.3), transparent); -} - #urlbar-search-footer { border-top: 1px solid hsla(210,4%,10%,.14); background-color: hsla(210,4%,10%,.07); @@ -1920,10 +1914,6 @@ richlistitem[type~="action"][actiontype="switchtab"][selected="true"] > .ac-url- list-style-image: url("chrome://browser/skin/reload-stop-go.png"); } -#urlbar > toolbarbutton:not([disabled]):hover:active { - background-image: radial-gradient(circle closest-side, hsla(205,100%,70%,.3), transparent); -} - #urlbar-go-button { -moz-image-region: rect(0, 42px, 14px, 28px); } diff --git a/browser/themes/shared/devtools/animationinspector.css b/browser/themes/shared/devtools/animationinspector.css index 40efab741a0..a7e17707455 100644 --- a/browser/themes/shared/devtools/animationinspector.css +++ b/browser/themes/shared/devtools/animationinspector.css @@ -99,7 +99,7 @@ body { background-image: url("debugger-play.png"); } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { #element-picker::before { background-image: url("chrome://browser/skin/devtools/command-pick@2x.png"); background-size: 64px; @@ -362,7 +362,7 @@ body { background-image: url(rewind.png); } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .timeline .toggle::before { background-image: url(debugger-pause@2x.png); } diff --git a/browser/themes/shared/devtools/canvasdebugger.css b/browser/themes/shared/devtools/canvasdebugger.css index 43298e77b15..08cee84d7bf 100644 --- a/browser/themes/shared/devtools/canvasdebugger.css +++ b/browser/themes/shared/devtools/canvasdebugger.css @@ -145,7 +145,7 @@ list-style-image: url(debugger-step-out.png); } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { #resume { list-style-image: url(debugger-play@2x.png); -moz-image-region: rect(0px,64px,32px,32px); @@ -250,7 +250,7 @@ background-size: 12px; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .selected .call-item-gutter { background-image: url("editor-debug-location@2x.png"); } diff --git a/browser/themes/shared/devtools/commandline.inc.css b/browser/themes/shared/devtools/commandline.inc.css index 927ac7fc0a7..09573571270 100644 --- a/browser/themes/shared/devtools/commandline.inc.css +++ b/browser/themes/shared/devtools/commandline.inc.css @@ -82,7 +82,7 @@ -moz-image-region: rect(0px, 64px, 16px, 48px); } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { #developer-toolbar-toolbox-button { list-style-image: url("chrome://browser/skin/devtools/toggle-tools@2x.png"); -moz-image-region: rect(0px, 32px, 32px, 0px); @@ -111,7 +111,7 @@ opacity: 0.6; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { #developer-toolbar-closebutton { list-style-image: url("chrome://browser/skin/devtools/close@2x.png"); } @@ -199,7 +199,7 @@ html|*#gcli-output-frame { background-position: -16px center; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .gclitoolbar-input-node::before { background-image: url("chrome://browser/skin/devtools/commandline-icon@2x.png"); } diff --git a/browser/themes/shared/devtools/computedview.css b/browser/themes/shared/devtools/computedview.css index f770681c008..a4eff4318a8 100644 --- a/browser/themes/shared/devtools/computedview.css +++ b/browser/themes/shared/devtools/computedview.css @@ -61,7 +61,7 @@ body { background-size: 5px 8px; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .property-value, .other-property-value { background-image: url(arrow-e@2x.png); } diff --git a/browser/themes/shared/devtools/dark-theme.css b/browser/themes/shared/devtools/dark-theme.css index ffde5dc1866..aa60aa25ac5 100644 --- a/browser/themes/shared/devtools/dark-theme.css +++ b/browser/themes/shared/devtools/dark-theme.css @@ -334,7 +334,7 @@ div.CodeMirror span.eval-text { background-position: -42px 0; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .theme-twisty, .theme-checkbox { background-image: url("chrome://browser/skin/devtools/controls@2x.png"); } @@ -372,7 +372,7 @@ div.CodeMirror span.eval-text { margin-left: -4px; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .theme-tooltip-panel .panel-arrow[side="top"], .theme-tooltip-panel .panel-arrow[side="bottom"] { list-style-image: url("chrome://browser/skin/devtools/tooltip/arrow-vertical-dark@2x.png"); diff --git a/browser/themes/shared/devtools/debugger.css b/browser/themes/shared/devtools/debugger.css index 523961adea7..502272c6573 100644 --- a/browser/themes/shared/devtools/debugger.css +++ b/browser/themes/shared/devtools/debugger.css @@ -66,7 +66,7 @@ list-style-image: url(debugger-blackbox.png); } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { #black-box { list-style-image: url(debugger-blackbox@2x.png); } @@ -76,7 +76,7 @@ list-style-image: url(debugger-prettyprint.png); } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { #pretty-print { list-style-image: url(debugger-prettyprint@2x.png); } @@ -86,7 +86,7 @@ list-style-image: url(debugger-toggleBreakpoints.png); } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { #toggle-breakpoints { list-style-image: url(debugger-toggleBreakpoints@2x.png); } @@ -104,7 +104,7 @@ -moz-image-region: rect(0px,32px,16px,16px); } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { #sources-toolbar .devtools-toolbarbutton:not([label]) { -moz-image-region: rect(0px,32px,32px,0px); } @@ -138,7 +138,7 @@ -moz-margin-end: 5px; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { #black-boxed-message-button > .button-box > .button-icon { background-image: url(debugger-blackbox@2x.png); } @@ -226,7 +226,7 @@ -moz-image-region: rect(0px,32px,16px,16px); } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { #trace { list-style-image: url(tracer-icon@2x.png); -moz-image-region: rect(0px,32px,32px,0px); @@ -328,7 +328,7 @@ margin: 2px; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .dbg-expression-arrow { background-image: url(commandline-icon@2x.png); } @@ -564,7 +564,7 @@ list-style-image: url(debugger-play.png); } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { #resume { list-style-image: url(debugger-pause@2x.png); -moz-image-region: rect(0px,32px,32px,0px); @@ -596,7 +596,7 @@ list-style-image: url(debugger-step-out.png); } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { #step-over { list-style-image: url(debugger-step-over@2x.png); } @@ -626,7 +626,7 @@ -moz-image-region: rect(0px,32px,16px,16px); } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { #instruments-pane-toggle { list-style-image: url(debugger-collapse@2x.png); -moz-image-region: rect(0px,32px,32px,0px); diff --git a/browser/themes/shared/devtools/inspector.css b/browser/themes/shared/devtools/inspector.css index b937e0a20ee..96f5fb7c0f2 100644 --- a/browser/themes/shared/devtools/inspector.css +++ b/browser/themes/shared/devtools/inspector.css @@ -60,7 +60,7 @@ -moz-image-region: rect(0px,32px,16px,16px); } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { #inspector-pane-toggle { list-style-image: url(debugger-collapse@2x.png); -moz-image-region: rect(0px,32px,32px,0px); diff --git a/browser/themes/shared/devtools/light-theme.css b/browser/themes/shared/devtools/light-theme.css index 9390453f4b5..a7cbb12251b 100644 --- a/browser/themes/shared/devtools/light-theme.css +++ b/browser/themes/shared/devtools/light-theme.css @@ -343,7 +343,7 @@ div.CodeMirror span.eval-text { background-position: -14px 0; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .theme-twisty, .theme-checkbox { background-image: url("chrome://browser/skin/devtools/controls@2x.png"); } @@ -381,7 +381,7 @@ div.CodeMirror span.eval-text { margin-left: -4px; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .theme-tooltip-panel .panel-arrow[side="top"], .theme-tooltip-panel .panel-arrow[side="bottom"] { list-style-image: url("chrome://browser/skin/devtools/tooltip/arrow-vertical-light@2x.png"); diff --git a/browser/themes/shared/devtools/netmonitor.inc.css b/browser/themes/shared/devtools/netmonitor.inc.css index 34dc6b4f59f..9949d23584d 100644 --- a/browser/themes/shared/devtools/netmonitor.inc.css +++ b/browser/themes/shared/devtools/netmonitor.inc.css @@ -416,7 +416,7 @@ box.requests-menu-status[code^="5"] { -moz-image-region: rect(0px,32px,16px,16px); } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { #details-pane-toggle { list-style-image: url("chrome://browser/skin/devtools/debugger-collapse@2x.png"); -moz-image-region: rect(0px,32px,32px,0px); @@ -556,7 +556,7 @@ box.requests-menu-status[code^="5"] { height: 12px; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .security-warning-icon { background-image: url(alerticon-warning@2x.png); } diff --git a/browser/themes/shared/devtools/responsivedesign.inc.css b/browser/themes/shared/devtools/responsivedesign.inc.css index 95397cd388b..7707693ca1c 100644 --- a/browser/themes/shared/devtools/responsivedesign.inc.css +++ b/browser/themes/shared/devtools/responsivedesign.inc.css @@ -155,7 +155,7 @@ list-style-image: url("chrome://browser/skin/devtools/responsiveui-rotate.png"); } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .devtools-responsiveui-close { list-style-image: url("chrome://browser/skin/devtools/close@2x.png"); } @@ -174,7 +174,7 @@ -moz-image-region: rect(0px,32px,16px,16px); } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .devtools-responsiveui-touch { list-style-image: url("chrome://browser/skin/devtools/responsiveui-touch@2x.png"); -moz-image-region: rect(0px,32px,32px,0px); @@ -189,7 +189,7 @@ list-style-image: url("chrome://browser/skin/devtools/responsiveui-screenshot.png"); } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .devtools-responsiveui-screenshot { list-style-image: url("chrome://browser/skin/devtools/responsiveui-screenshot@2x.png"); } @@ -264,12 +264,14 @@ } .devtools-responsiveui-home-button { + -moz-user-focus: ignore; width: 40px; height: 30px; list-style-image: url("chrome://browser/skin/devtools/responsiveui-home.png"); } .devtools-responsiveui-sleep-button { + -moz-user-focus: ignore; -moz-appearance: none; /* compensate browserStack top padding */ margin-top: -67px; @@ -297,6 +299,7 @@ .devtools-responsiveui-volume-up-button, .devtools-responsiveui-volume-down-button { + -moz-user-focus: ignore; -moz-appearance: none; border: 1px solid red; min-width: 8px; @@ -321,7 +324,7 @@ border-bottom-left-radius: 12px; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .devtools-responsiveui-resizebarV { background-image: url("chrome://browser/skin/devtools/responsive-vertical-resizer@2x.png"); } diff --git a/browser/themes/shared/devtools/ruleview.css b/browser/themes/shared/devtools/ruleview.css index c2057304f36..801a87a9901 100644 --- a/browser/themes/shared/devtools/ruleview.css +++ b/browser/themes/shared/devtools/ruleview.css @@ -111,7 +111,7 @@ } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .ruleview-warning { background-image: url(alerticon-warning@2x.png); } @@ -198,7 +198,7 @@ background-size: 1em; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .ruleview-bezierswatch { background: url("chrome://browser/skin/devtools/cubic-bezier-swatch@2x.png"); background-size: 1em; diff --git a/browser/themes/shared/devtools/shadereditor.css b/browser/themes/shared/devtools/shadereditor.css index 9ba9ff86f3d..4626a3853cb 100644 --- a/browser/themes/shared/devtools/shadereditor.css +++ b/browser/themes/shared/devtools/shadereditor.css @@ -60,7 +60,7 @@ border: 0; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .side-menu-widget-item-checkbox .checkbox-check { background-image: url(itemToggle@2x.png); } diff --git a/browser/themes/shared/devtools/styleeditor.css b/browser/themes/shared/devtools/styleeditor.css index a4a300fe061..0cc2fca4861 100644 --- a/browser/themes/shared/devtools/styleeditor.css +++ b/browser/themes/shared/devtools/styleeditor.css @@ -115,7 +115,7 @@ height: 40px; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .stylesheet-enabled { background-image: url(itemToggle@2x.png); } diff --git a/browser/themes/shared/devtools/toolbars.inc.css b/browser/themes/shared/devtools/toolbars.inc.css index 2d7aa072f16..c2f45534809 100644 --- a/browser/themes/shared/devtools/toolbars.inc.css +++ b/browser/themes/shared/devtools/toolbars.inc.css @@ -317,7 +317,7 @@ opacity: 0.5; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .devtools-button::before { background-size: 32px; } @@ -438,7 +438,7 @@ -moz-image-region: rect(0, 32px, 16px, 16px); } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .theme-dark .devtools-searchinput { background-image: url(magnifying-glass@2x.png); } @@ -768,7 +768,7 @@ background-image: url("chrome://browser/skin/devtools/command-rulers.png"); } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { #command-button-paintflashing > image { background-image: url("chrome://browser/skin/devtools/command-paintflashing@2x.png"); } diff --git a/browser/themes/shared/devtools/webaudioeditor.css b/browser/themes/shared/devtools/webaudioeditor.css index 0d666382c70..3be55bd5447 100644 --- a/browser/themes/shared/devtools/webaudioeditor.css +++ b/browser/themes/shared/devtools/webaudioeditor.css @@ -187,7 +187,7 @@ text { -moz-box-flex: 1; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { #inspector-pane-toggle { list-style-image: url(debugger-collapse@2x.png); -moz-image-region: rect(0px,32px,32px,0px); diff --git a/browser/themes/shared/devtools/webconsole.inc.css b/browser/themes/shared/devtools/webconsole.inc.css index 16b70853d8b..a066d626492 100644 --- a/browser/themes/shared/devtools/webconsole.inc.css +++ b/browser/themes/shared/devtools/webconsole.inc.css @@ -360,7 +360,7 @@ a { background-size: 16px 16px; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .jsterm-input-node { background-image: -moz-image-rect(url('chrome://browser/skin/devtools/commandline-icon@2x.png'), 0, 64, 32, 32); } diff --git a/browser/themes/shared/devtools/widgets.inc.css b/browser/themes/shared/devtools/widgets.inc.css index 2961f8170dd..a84a4d304cc 100644 --- a/browser/themes/shared/devtools/widgets.inc.css +++ b/browser/themes/shared/devtools/widgets.inc.css @@ -111,7 +111,7 @@ padding: 0; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .scrollbutton-up > .toolbarbutton-icon, .scrollbutton-down > .toolbarbutton-icon { background-image: url("breadcrumbs-scrollbutton@2x.png"); @@ -641,7 +641,7 @@ height: 16px; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .variable-or-property-non-writable-icon { background-image: url("chrome://browser/skin/devtools/vview-lock@2x.png"); } @@ -741,7 +741,7 @@ height: 16px; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .variables-view-delete { background-image: url("chrome://browser/skin/devtools/vview-delete@2x.png"); } @@ -767,7 +767,7 @@ cursor: pointer; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .variables-view-edit { background-image: url("chrome://browser/skin/devtools/vview-edit@2x.png"); } @@ -793,7 +793,7 @@ cursor: pointer; } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .variables-view-open-inspector { background-image: url("chrome://browser/skin/devtools/vview-open-inspector@2x.png"); } @@ -1424,7 +1424,7 @@ } } -@media (min-resolution: 1.25dppx) { +@media (min-resolution: 1.1dppx) { .tree-widget-item:before { background-image: url("chrome://browser/skin/devtools/controls@2x.png"); } diff --git a/browser/themes/shared/tabs.inc.css b/browser/themes/shared/tabs.inc.css index a91e01ea899..8e2c709090a 100644 --- a/browser/themes/shared/tabs.inc.css +++ b/browser/themes/shared/tabs.inc.css @@ -95,7 +95,6 @@ height: 16px; margin-top: -12px; -moz-margin-start: -16px; - display: none; position: relative; } @@ -103,35 +102,29 @@ list-style-image: url("chrome://browser/skin/tabbrowser/crashed.svg"); } -.tab-icon-overlay[crashed], -.tab-icon-overlay[soundplaying][pinned], -.tab-icon-overlay[muted][pinned]:not([crashed]) { - display: -moz-box; -} - -.tab-icon-overlay[soundplaying][pinned], -.tab-icon-overlay[muted][pinned]:not([crashed]) { +.tab-icon-overlay[soundplaying], +.tab-icon-overlay[muted]:not([crashed]) { border-radius: 8px; } -.tab-icon-overlay[soundplaying][pinned]:hover, -.tab-icon-overlay[muted][pinned]:not([crashed]):hover { +.tab-icon-overlay[soundplaying]:hover, +.tab-icon-overlay[muted]:not([crashed]):hover { background-color: white; } -.tab-icon-overlay[soundplaying][pinned] { +.tab-icon-overlay[soundplaying] { list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio"); } -.tab-icon-overlay[muted][pinned]:not([crashed]) { +.tab-icon-overlay[muted]:not([crashed]) { list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted"); } -#TabsToolbar[brighttext] .tab-icon-overlay[soundplaying][pinned]:not(:hover) { +#TabsToolbar[brighttext] .tab-icon-overlay[soundplaying]:not(:hover) { list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-white"); } -#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned]:not([crashed]):not(:hover) { +#TabsToolbar[brighttext] .tab-icon-overlay[muted]:not([crashed]):not(:hover) { list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-white-muted"); } @@ -161,11 +154,6 @@ padding: 0; } -.tab-icon-sound:not([soundplaying]):not([muted]), -.tab-icon-sound[pinned] { - display: none; -} - .tab-icon-sound[soundplaying] { list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-backgroundTab"); } diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css index 325bc7cc990..1f278e6246c 100644 --- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -1383,15 +1383,6 @@ html|*.urlbar-input:-moz-lwtheme::-moz-placeholder, padding: 2px 2px; } -.urlbar-icon:hover { - background-image: radial-gradient(circle closest-side, hsla(200,100%,70%,.3), transparent); -} - -.urlbar-icon[open="true"], -.urlbar-icon:hover:active { - background-image: radial-gradient(circle closest-side, hsla(200,100%,70%,.1), transparent); -} - #urlbar-search-footer { border-top: 1px solid hsla(210,4%,10%,.14); background-color: hsla(210,4%,10%,.07); @@ -1478,13 +1469,11 @@ html|*.urlbar-input:-moz-lwtheme::-moz-placeholder, } .urlbar-history-dropmarker:hover { - background-image: radial-gradient(circle closest-side, hsla(205,100%,70%,.3), transparent); -moz-image-region: var(--urlbar-dropmarker-hover-region); } .urlbar-history-dropmarker:hover:active, .urlbar-history-dropmarker[open="true"] { - background-image: radial-gradient(circle closest-side, hsla(205,100%,70%,.1), transparent); -moz-image-region: var(--urlbar-dropmarker-active-region); } @@ -1669,12 +1658,10 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action- } #urlbar-reload-button:not([disabled]):hover { - background-image: radial-gradient(circle closest-side, hsla(200,100%,70%,.2), transparent); -moz-image-region: rect(14px, 14px, 28px, 0); } #urlbar-reload-button:not([disabled]):hover:active { - background-image: radial-gradient(circle closest-side, hsla(200,100%,60%,.1), transparent); -moz-image-region: rect(28px, 14px, 42px, 0); } @@ -1687,12 +1674,10 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action- } #urlbar-go-button:hover { - background-image: radial-gradient(circle closest-side, hsla(110,70%,50%,.2), transparent); -moz-image-region: rect(14px, 42px, 28px, 28px); } #urlbar-go-button:hover:active { - background-image: radial-gradient(circle closest-side, hsla(110,70%,50%,.1), transparent); -moz-image-region: rect(28px, 42px, 42px, 28px); } @@ -1705,12 +1690,10 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action- } #urlbar-stop-button:not([disabled]):hover { - background-image: radial-gradient(circle closest-side, hsla(5,100%,75%,.3), transparent); -moz-image-region: rect(14px, 28px, 28px, 14px); } #urlbar-stop-button:hover:active { - background-image: radial-gradient(circle closest-side, hsla(5,100%,75%,.1), transparent); -moz-image-region: rect(28px, 28px, 42px, 14px); } diff --git a/dom/apps/Webapps.jsm b/dom/apps/Webapps.jsm index 3432f88bdd3..b22224af8ea 100644 --- a/dom/apps/Webapps.jsm +++ b/dom/apps/Webapps.jsm @@ -738,7 +738,11 @@ this.DOMApplicationRegistry = { loadAndUpdateApps: function() { return Task.spawn(function*() { - let runUpdate = AppsUtils.isFirstRun(Services.prefs); + let runUpdate = false; + try { + runUpdate = AppsUtils.isFirstRun(Services.prefs); + } catch(e) {} + let loadAppPermission = Services.prefs.getBoolPref("dom.apps.reset-permissions"); yield this.loadCurrentRegistry(); @@ -801,7 +805,7 @@ this.DOMApplicationRegistry = { // installPreinstalledApp() removes the ones failing to install. this._saveApps(); - Services.prefs.setBoolPref("dom.apps.reset-permissions", true); + Services.prefs.setBoolPref("dom.apps.reset-permissions", true); } // DataStores must be initialized at startup. @@ -4335,27 +4339,6 @@ this.DOMApplicationRegistry = { }); }, - getAll: function(aCallback) { - debug("getAll"); - let apps = []; - let tmp = []; - - for (let id in this.webapps) { - let app = AppsUtils.cloneAppObject(this.webapps[id]); - if (!this._isLaunchable(app)) - continue; - - apps.push(app); - tmp.push({ id: id }); - } - - this._readManifests(tmp).then((aResult) => { - for (let i = 0; i < aResult.length; i++) - apps[i].manifest = aResult[i].manifest; - aCallback(apps); - }); - }, - /* Check if |data| is actually a receipt */ isReceipt: function(data) { try { diff --git a/mobile/android/base/IntentHelper.java b/mobile/android/base/IntentHelper.java index 7cde1e460d4..1c95224e2a0 100644 --- a/mobile/android/base/IntentHelper.java +++ b/mobile/android/base/IntentHelper.java @@ -6,8 +6,11 @@ package org.mozilla.gecko; import org.mozilla.gecko.util.ActivityResultHandler; +import org.mozilla.gecko.util.EventCallback; import org.mozilla.gecko.util.GeckoEventListener; import org.mozilla.gecko.util.JSONUtils; +import org.mozilla.gecko.util.NativeEventListener; +import org.mozilla.gecko.util.NativeJSObject; import org.mozilla.gecko.util.WebActivityMapper; import org.json.JSONArray; @@ -26,16 +29,21 @@ import java.net.URLEncoder; import java.util.Arrays; import java.util.List; -public final class IntentHelper implements GeckoEventListener { +public final class IntentHelper implements GeckoEventListener, + NativeEventListener { + private static final String LOGTAG = "GeckoIntentHelper"; private static final String[] EVENTS = { "Intent:GetHandlers", "Intent:Open", "Intent:OpenForResult", - "Intent:OpenNoHandler", "WebActivity:Open" }; + private static final String[] NATIVE_EVENTS = { + "Intent:OpenNoHandler", + }; + // via http://developer.android.com/distribute/tools/promote/linking.html private static String MARKET_INTENT_URI_PACKAGE_PREFIX = "market://details?id="; private static String EXTRA_BROWSER_FALLBACK_URL = "browser_fallback_url"; @@ -49,7 +57,8 @@ public final class IntentHelper implements GeckoEventListener { private IntentHelper(Activity activity) { this.activity = activity; - EventDispatcher.getInstance().registerGeckoThreadListener(this, EVENTS); + EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener) this, EVENTS); + EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener) this, NATIVE_EVENTS); } public static IntentHelper init(Activity activity) { @@ -64,11 +73,19 @@ public final class IntentHelper implements GeckoEventListener { public static void destroy() { if (instance != null) { - EventDispatcher.getInstance().unregisterGeckoThreadListener(instance, EVENTS); + EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener) instance, EVENTS); + EventDispatcher.getInstance().unregisterGeckoThreadListener((NativeEventListener) instance, NATIVE_EVENTS); instance = null; } } + @Override + public void handleMessage(final String event, final NativeJSObject message, final EventCallback callback) { + if (event.equals("Intent:OpenNoHandler")) { + openNoHandler(message, callback); + } + } + @Override public void handleMessage(String event, JSONObject message) { try { @@ -78,8 +95,6 @@ public final class IntentHelper implements GeckoEventListener { open(message); } else if (event.equals("Intent:OpenForResult")) { openForResult(message); - } else if (event.equals("Intent:OpenNoHandler")) { - openNoHandler(message); } else if (event.equals("WebActivity:Open")) { openWebActivity(message); } @@ -132,13 +147,17 @@ public final class IntentHelper implements GeckoEventListener { * and we can bring the user directly to the application page in an app market. If a package is * not specified and there is a fallback url in the intent extras, we open that url. If neither * is present, we alert the user that we were unable to open the link. + * + * @param msg A message with the uri with no handlers as the value for the "uri" key + * @param callback A callback that will be called with success & no params if Java loads a page, or with error and + * the uri to load if Java does not load a page */ - private void openNoHandler(final JSONObject msg) { - final String uri = msg.optString("uri"); + private void openNoHandler(final NativeJSObject msg, final EventCallback callback) { + final String uri = msg.getString("uri"); if (TextUtils.isEmpty(uri)) { - openUnknownProtocolErrorPage(""); Log.w(LOGTAG, "Received empty URL - loading about:neterror"); + callback.sendError(getUnknownProtocolErrorPageUri("")); return; } @@ -147,14 +166,16 @@ public final class IntentHelper implements GeckoEventListener { // TODO (bug 1173626): This will not handle android-app uris on non 5.1 devices. intent = Intent.parseUri(uri, 0); } catch (final URISyntaxException e) { + String errorUri; try { - openUnknownProtocolErrorPage(URLEncoder.encode(uri, "UTF-8")); + errorUri = getUnknownProtocolErrorPageUri(URLEncoder.encode(uri, "UTF-8")); } catch (final UnsupportedEncodingException encodingE) { - openUnknownProtocolErrorPage(""); + errorUri = getUnknownProtocolErrorPageUri(""); } // Don't log the exception to prevent leaking URIs. Log.w(LOGTAG, "Unable to parse Intent URI - loading about:neterror"); + callback.sendError(errorUri); return; } @@ -174,28 +195,31 @@ public final class IntentHelper implements GeckoEventListener { final Intent marketIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(marketUri)); marketIntent.addCategory(Intent.CATEGORY_BROWSABLE); marketIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + // (Bug 1192436) We don't know if marketIntent matches any Activities (e.g. non-Play + // Store devices). If it doesn't, clicking the link will cause no action to occur. activity.startActivity(marketIntent); + callback.sendSuccess(null); } else if (intent.hasExtra(EXTRA_BROWSER_FALLBACK_URL)) { final String fallbackUrl = intent.getStringExtra(EXTRA_BROWSER_FALLBACK_URL); - Tabs.getInstance().loadUrl(fallbackUrl); + callback.sendError(fallbackUrl); } else { - openUnknownProtocolErrorPage(intent.getData().toString()); // Don't log the URI to prevent leaking it. Log.w(LOGTAG, "Unable to open URI, default case - loading about:neterror"); + callback.sendError(getUnknownProtocolErrorPageUri(intent.getData().toString())); } } /** - * Opens about:neterror with the unknownProtocolFound text. + * Returns an about:neterror uri with the unknownProtocolFound text as a parameter. * @param encodedUri The encoded uri. While the page does not open correctly without specifying * a uri parameter, it happily accepts the empty String so this argument may * be the empty String. */ - private void openUnknownProtocolErrorPage(final String encodedUri) { - final String errorUri = UNKNOWN_PROTOCOL_URI_PREFIX + encodedUri; - Tabs.getInstance().loadUrl(errorUri); + private String getUnknownProtocolErrorPageUri(final String encodedUri) { + return UNKNOWN_PROTOCOL_URI_PREFIX + encodedUri; } private void openWebActivity(JSONObject message) throws JSONException { diff --git a/mobile/android/base/RestrictedProfiles.java b/mobile/android/base/RestrictedProfiles.java index 27ec658972f..ef2212397bd 100644 --- a/mobile/android/base/RestrictedProfiles.java +++ b/mobile/android/base/RestrictedProfiles.java @@ -41,7 +41,7 @@ public class RestrictedProfiles { return configuration; } - if (isGuestProfile()) { + if (isGuestProfile(context)) { return new GuestProfileConfiguration(); } else if(isRestrictedProfile(context)) { return new RestrictedProfileConfiguration(context); @@ -50,8 +50,13 @@ public class RestrictedProfiles { } } - private static boolean isGuestProfile() { - return GeckoAppShell.getGeckoInterface().getProfile().inGuestMode(); + private static boolean isGuestProfile(Context context) { + GeckoAppShell.GeckoInterface geckoInterface = GeckoAppShell.getGeckoInterface(); + if (geckoInterface != null) { + return geckoInterface.getProfile().inGuestMode(); + } + + return GeckoProfile.get(context).inGuestMode(); } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) diff --git a/mobile/android/base/locales/en-US/android_strings.dtd b/mobile/android/base/locales/en-US/android_strings.dtd index b15834309c0..d3a8c548d3c 100644 --- a/mobile/android/base/locales/en-US/android_strings.dtd +++ b/mobile/android/base/locales/en-US/android_strings.dtd @@ -564,14 +564,10 @@ just addresses the organization to follow, e.g. "This site is run by " --> - - - - - - - - + + + + diff --git a/mobile/android/base/resources/drawable-hdpi/firstrun_background_coffee.png b/mobile/android/base/resources/drawable-hdpi/firstrun_background_coffee.png new file mode 100644 index 00000000000..6e4f718c7f9 Binary files /dev/null and b/mobile/android/base/resources/drawable-hdpi/firstrun_background_coffee.png differ diff --git a/mobile/android/base/resources/drawable-large-hdpi-v11/firstrun_background_coffee.png b/mobile/android/base/resources/drawable-large-hdpi-v11/firstrun_background_coffee.png new file mode 100644 index 00000000000..593e4d56c56 Binary files /dev/null and b/mobile/android/base/resources/drawable-large-hdpi-v11/firstrun_background_coffee.png differ diff --git a/mobile/android/base/resources/drawable-large-xhdpi-v11/firstrun_background_coffee.png b/mobile/android/base/resources/drawable-large-xhdpi-v11/firstrun_background_coffee.png new file mode 100644 index 00000000000..97211617cd7 Binary files /dev/null and b/mobile/android/base/resources/drawable-large-xhdpi-v11/firstrun_background_coffee.png differ diff --git a/mobile/android/base/resources/drawable-large-xxhdpi-v11/firstrun_background_coffee.png b/mobile/android/base/resources/drawable-large-xxhdpi-v11/firstrun_background_coffee.png new file mode 100644 index 00000000000..2067432c0bb Binary files /dev/null and b/mobile/android/base/resources/drawable-large-xxhdpi-v11/firstrun_background_coffee.png differ diff --git a/mobile/android/base/resources/drawable-xhdpi/firstrun_background_coffee.png b/mobile/android/base/resources/drawable-xhdpi/firstrun_background_coffee.png new file mode 100644 index 00000000000..97534e00041 Binary files /dev/null and b/mobile/android/base/resources/drawable-xhdpi/firstrun_background_coffee.png differ diff --git a/mobile/android/base/resources/drawable-xxhdpi/firstrun_background_coffee.png b/mobile/android/base/resources/drawable-xxhdpi/firstrun_background_coffee.png new file mode 100644 index 00000000000..dcae6ae7272 Binary files /dev/null and b/mobile/android/base/resources/drawable-xxhdpi/firstrun_background_coffee.png differ diff --git a/mobile/android/base/resources/layout-mdpi/firstrun_welcome_fragment.xml b/mobile/android/base/resources/layout-mdpi/firstrun_welcome_fragment.xml deleted file mode 100644 index a412d3b195a..00000000000 --- a/mobile/android/base/resources/layout-mdpi/firstrun_welcome_fragment.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - - - - - - -