diff --git a/accessible/tests/mochitest/states/a11y.ini b/accessible/tests/mochitest/states/a11y.ini index 631ae38288b..2f1793aa3b6 100644 --- a/accessible/tests/mochitest/states/a11y.ini +++ b/accessible/tests/mochitest/states/a11y.ini @@ -15,6 +15,7 @@ support-files = [test_controls.xul] [test_doc.html] [test_doc_busy.html] +skip-if = os == 'mac' && os_version == '10.6' [test_docarticle.html] [test_editablebody.html] [test_expandable.xul] diff --git a/addon-sdk/source/lib/sdk/tabs/helpers.js b/addon-sdk/source/lib/sdk/tabs/helpers.js index 78c0e36856e..fab18cd4e11 100644 --- a/addon-sdk/source/lib/sdk/tabs/helpers.js +++ b/addon-sdk/source/lib/sdk/tabs/helpers.js @@ -27,6 +27,6 @@ exports.getTabForWindow = getTabForWindow; exports.getTabForRawTab = modelFor; function getTabForBrowser(browser) { - return modelFor(getRawTabForBrowser(browser)); + return modelFor(getRawTabForBrowser(browser)) || null; } exports.getTabForBrowser = getTabForBrowser; diff --git a/addon-sdk/source/lib/sdk/tabs/tab-fennec.js b/addon-sdk/source/lib/sdk/tabs/tab-fennec.js index b1ca765020c..515f6e10279 100644 --- a/addon-sdk/source/lib/sdk/tabs/tab-fennec.js +++ b/addon-sdk/source/lib/sdk/tabs/tab-fennec.js @@ -9,7 +9,7 @@ const { tabNS, rawTabNS } = require('./namespace'); const { EventTarget } = require('../event/target'); const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL, getTabContentWindow, getTabForBrowser, setTabURL, getOwnerWindow, - getTabContentDocument, getTabContentType, getTabId } = require('./utils'); + getTabContentDocument, getTabContentType, getTabId, isTab } = require('./utils'); const { emit } = require('../event/core'); const { isPrivate } = require('../private-browsing/utils'); const { isWindowPrivate } = require('../window/utils'); @@ -17,6 +17,7 @@ const { when: unload } = require('../system/unload'); const { BLANK } = require('../content/thumbnail'); const { viewFor } = require('../view/core'); const { EVENTS } = require('./events'); +const { modelFor } = require('../model/core'); const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec'; diff --git a/b2g/config/aries/sources.xml b/b2g/config/aries/sources.xml index a805f001d9e..afb129ea52d 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 4e502fc0511..9d7e8cbb979 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 3679cd6fdd1..d7c8ad124b6 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 f28c4373817..4f46e5fdd5d 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 b307636f7e7..e69765a0a8c 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 b2e93c50fc5..5af9b486c42 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 3679cd6fdd1..d7c8ad124b6 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 7c520725d66..f4eff463bfc 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 c1d39702d27..882693d64b2 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,9 +1,9 @@ { "git": { - "git_revision": "05380df3158fa39e1dde1687c0bf11a71f8c6868", + "git_revision": "e7d268074ee3c9eeb191c2205c0e35992fb3915d", "remote": "https://git.mozilla.org/releases/gaia.git", "branch": "" }, - "revision": "fc4f5dfe591a45cb03ae221489f41b56f38c883a", + "revision": "f7c817ea239637d874d7099f2ccaafc6c3766389", "repo_path": "integration/gaia-central" } diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index 9c2fb778f3c..291f4727982 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 df3b1b475ba..e749b95bc4f 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 94457602abb..549d1f34942 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -178,6 +178,9 @@ pref("app.update.badge", true); #else pref("app.update.badge", false); #endif +// Give the user x seconds to reboot before showing a badge on the hamburger +// button. default=4 days +pref("app.update.badgeWaitTime", 345600); // If set to true, the Update Service will apply updates in the background // when it finishes downloading them. diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 60c1a4498cf..e85458e1b54 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -2363,11 +2363,19 @@ function BrowserViewSourceOfDocument(aArgsOrDocument) { let inTab = Services.prefs.getBoolPref("view_source.tab"); if (inTab) { let viewSourceURL = `view-source:${args.URL}`; - let tab = gBrowser.loadOneTab(viewSourceURL, { + let tabBrowser = gBrowser; + // In the case of sidebars and chat windows, gBrowser is defined but null, + // because no #content element exists. For these cases, we need to find + // the most recent browser window. + if (!tabBrowser) { + let browserWindow = RecentWindow.getMostRecentBrowserWindow(); + tabBrowser = browserWindow.gBrowser; + } + let tab = tabBrowser.loadOneTab(viewSourceURL, { relatedToCurrent: true, inBackground: false }); - args.viewSourceBrowser = gBrowser.getBrowserForTab(tab); + args.viewSourceBrowser = tabBrowser.getBrowserForTab(tab); top.gViewSourceUtils.viewSourceInBrowser(args); } else { top.gViewSourceUtils.viewSource(args); @@ -2561,18 +2569,27 @@ function PageProxyClickHandler(aEvent) // Setup the hamburger button badges for updates, if enabled. let gMenuButtonUpdateBadge = { enabled: false, + badgeWaitTime: 0, + timer: null, init: function () { try { this.enabled = Services.prefs.getBoolPref("app.update.badge"); } catch (e) {} if (this.enabled) { + try { + this.badgeWaitTime = Services.prefs.getIntPref("app.update.badgeWaitTime"); + } catch (e) { + this.badgeWaitTime = 345600; // 4 days + } PanelUI.menuButton.classList.add("badged-button"); Services.obs.addObserver(this, "update-staged", false); } }, uninit: function () { + if (this.timer) + this.timer.cancel(); if (this.enabled) { Services.obs.removeObserver(this, "update-staged"); PanelUI.panel.removeEventListener("popupshowing", this, true); @@ -2616,23 +2633,14 @@ let gMenuButtonUpdateBadge = { case STATE_APPLIED_SVC: case STATE_PENDING: case STATE_PENDING_SVC: - // If the update is successfully applied, or if the updater has fallen back - // to non-staged updates, add a badge to the hamburger menu to indicate an - // update will be applied once the browser restarts. - PanelUI.menuButton.setAttribute("update-status", "succeeded"); - - let brandBundle = document.getElementById("bundle_brand"); - let brandShortName = brandBundle.getString("brandShortName"); - stringId = "appmenu.restartNeeded.description"; - updateButtonText = gNavigatorBundle.getFormattedString(stringId, - [brandShortName]); - - updateButton.setAttribute("label", updateButtonText); - updateButton.setAttribute("update-status", "succeeded"); - updateButton.hidden = false; - - PanelUI.panel.addEventListener("popupshowing", this, true); - + if (this.timer) { + return; + } + // Give the user badgeWaitTime seconds to react before prompting. + this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + this.timer.initWithCallback(this, this.badgeWaitTime * 1000, + this.timer.TYPE_ONE_SHOT); + // The timer callback will call uninit() when it completes. break; case STATE_FAILED: // Background update has failed, let's show the UI responsible for @@ -2649,13 +2657,29 @@ let gMenuButtonUpdateBadge = { PanelUI.panel.addEventListener("popupshowing", this, true); + this.uninit(); break; - case STATE_DOWNLOADING: - // We've fallen back to downloading the full update because the partial - // update failed to get staged in the background. Therefore we need to keep - // our observer. - return; } + }, + + notify: function () { + // If the update is successfully applied, or if the updater has fallen back + // to non-staged updates, add a badge to the hamburger menu to indicate an + // update will be applied once the browser restarts. + PanelUI.menuButton.setAttribute("update-status", "succeeded"); + + let brandBundle = document.getElementById("bundle_brand"); + let brandShortName = brandBundle.getString("brandShortName"); + stringId = "appmenu.restartNeeded.description"; + updateButtonText = gNavigatorBundle.getFormattedString(stringId, + [brandShortName]); + + let updateButton = document.getElementById("PanelUI-update-status"); + updateButton.setAttribute("label", updateButtonText); + updateButton.setAttribute("update-status", "succeeded"); + updateButton.hidden = false; + + PanelUI.panel.addEventListener("popupshowing", this, true); this.uninit(); }, diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js index 18cb420b0d4..71081326ca3 100644 --- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -1007,11 +1007,19 @@ nsContextMenu.prototype = { let inTab = Services.prefs.getBoolPref("view_source.tab"); if (inTab) { - let tab = gBrowser.loadOneTab("about:blank", { + let tabBrowser = gBrowser; + // In the case of sidebars and chat windows, gBrowser is defined but null, + // because no #content element exists. For these cases, we need to find + // the most recent browser window. + if (!tabBrowser) { + let browserWindow = RecentWindow.getMostRecentBrowserWindow(); + tabBrowser = browserWindow.gBrowser; + } + let tab = tabBrowser.loadOneTab("about:blank", { relatedToCurrent: true, inBackground: false }); - let viewSourceBrowser = gBrowser.getBrowserForTab(tab); + let viewSourceBrowser = tabBrowser.getBrowserForTab(tab); if (aContext == "selection") { top.gViewSourceUtils .viewSourceFromSelectionInBrowser(reference, viewSourceBrowser); diff --git a/browser/components/loop/content/js/conversation.js b/browser/components/loop/content/js/conversation.js index 86a14d30cf2..a763664f9cc 100644 --- a/browser/components/loop/content/js/conversation.js +++ b/browser/components/loop/content/js/conversation.js @@ -163,7 +163,7 @@ loop.conversation = (function(mozL10n) { dispatcher: dispatcher, mozLoop: navigator.mozLoop}), document.querySelector("#main")); - document.body.setAttribute("dir", mozL10n.getDirection()); + document.body.setAttribute("dir", "rtl");//mozL10n.getDirection()); document.body.setAttribute("platform", loop.shared.utils.getPlatform()); dispatcher.dispatch(new sharedActions.GetWindowData({ diff --git a/browser/components/loop/content/js/conversation.jsx b/browser/components/loop/content/js/conversation.jsx index d24272c0432..b09120a1525 100644 --- a/browser/components/loop/content/js/conversation.jsx +++ b/browser/components/loop/content/js/conversation.jsx @@ -163,7 +163,7 @@ loop.conversation = (function(mozL10n) { dispatcher={dispatcher} mozLoop={navigator.mozLoop} />, document.querySelector("#main")); - document.body.setAttribute("dir", mozL10n.getDirection()); + document.body.setAttribute("dir", "rtl");//mozL10n.getDirection()); document.body.setAttribute("platform", loop.shared.utils.getPlatform()); dispatcher.dispatch(new sharedActions.GetWindowData({ diff --git a/browser/components/loop/content/js/roomStore.js b/browser/components/loop/content/js/roomStore.js index a6f9519599f..9c55bd04050 100644 --- a/browser/components/loop/content/js/roomStore.js +++ b/browser/components/loop/content/js/roomStore.js @@ -91,6 +91,7 @@ loop.store = loop.store || {}; "openRoom", "shareRoomUrl", "updateRoomContext", + "updateRoomContextDone", "updateRoomContextError", "updateRoomList" ], @@ -115,7 +116,8 @@ loop.store = loop.store || {}; error: null, pendingCreation: false, pendingInitialRetrieval: false, - rooms: [] + rooms: [], + savingContext: false }; }, @@ -473,6 +475,7 @@ loop.store = loop.store || {}; * @param {sharedActions.UpdateRoomContext} actionData */ updateRoomContext: function(actionData) { + this.setStoreState({ savingContext: true }); this._mozLoop.rooms.get(actionData.roomToken, function(err, room) { if (err) { this.dispatchAction(new sharedActions.UpdateRoomContextError({ @@ -520,28 +523,38 @@ loop.store = loop.store || {}; // When no properties have been set on the roomData object, there's nothing // to save. if (!Object.getOwnPropertyNames(roomData).length) { + this.dispatchAction(new sharedActions.UpdateRoomContextDone()); return; } this.setStoreState({error: null}); this._mozLoop.rooms.update(actionData.roomToken, roomData, function(err, data) { - if (err) { - this.dispatchAction(new sharedActions.UpdateRoomContextError({ - error: err - })); - } + var action = err ? + new sharedActions.UpdateRoomContextError({ error: err }) : + new sharedActions.UpdateRoomContextDone(); + this.dispatchAction(action); }.bind(this)); }.bind(this)); }, + /** + * Handles the updateRoomContextDone action. + */ + updateRoomContextDone: function() { + this.setStoreState({ savingContext: false }); + }, + /** * Updating the context data attached to a room error. * * @param {sharedActions.UpdateRoomContextError} actionData */ updateRoomContextError: function(actionData) { - this.setStoreState({error: actionData.error}); + this.setStoreState({ + error: actionData.error, + savingContext: false + }); } }); })(document.mozL10n || navigator.mozL10n); diff --git a/browser/components/loop/content/js/roomViews.js b/browser/components/loop/content/js/roomViews.js index d7bc1efba02..258ee2943ba 100644 --- a/browser/components/loop/content/js/roomViews.js +++ b/browser/components/loop/content/js/roomViews.js @@ -29,6 +29,8 @@ loop.roomViews = (function(mozL10n) { this._onActiveRoomStateChanged); this.listenTo(this.props.roomStore, "change:error", this._onRoomError); + this.listenTo(this.props.roomStore, "change:savingContext", + this._onRoomSavingContext); }, componentWillUnmount: function() { @@ -53,11 +55,21 @@ loop.roomViews = (function(mozL10n) { } }, + _onRoomSavingContext: function() { + // Only update the state if we're mounted, to avoid the problem where + // stopListening doesn't nuke the active listeners during a event + // processing. + if (this.isMounted()) { + this.setState({savingContext: this.props.roomStore.getStoreState("savingContext")}); + } + }, + getInitialState: function() { var storeState = this.props.roomStore.getStoreState("activeRoom"); return _.extend({ // Used by the UI showcase. - roomState: this.props.roomState || storeState.roomState + roomState: this.props.roomState || storeState.roomState, + savingContext: false }, storeState); } }; @@ -174,6 +186,7 @@ loop.roomViews = (function(mozL10n) { mozLoop: React.PropTypes.object.isRequired, // This data is supplied by the activeRoomStore. roomData: React.PropTypes.object.isRequired, + savingContext: React.PropTypes.bool, show: React.PropTypes.bool.isRequired, showContext: React.PropTypes.bool.isRequired }, @@ -243,7 +256,11 @@ loop.roomViews = (function(mozL10n) { mozL10n.get("context_add_some_label") ) ), - React.createElement("div", {className: "btn-group call-action-group"}, + React.createElement("div", {className: cx({ + "btn-group": true, + "call-action-group": true, + hide: this.state.editMode + })}, React.createElement("button", {className: "btn btn-info btn-email", onClick: this.handleEmailButtonClick}, mozL10n.get("email_link_button") @@ -270,6 +287,7 @@ loop.roomViews = (function(mozL10n) { dispatcher: this.props.dispatcher, editMode: this.state.editMode, error: this.props.error, + savingContext: this.props.savingContext, mozLoop: this.props.mozLoop, onEditModeChange: this.handleEditModeChange, roomData: this.props.roomData, @@ -290,6 +308,7 @@ loop.roomViews = (function(mozL10n) { onEditModeChange: React.PropTypes.func, // This data is supplied by the activeRoomStore. roomData: React.PropTypes.object.isRequired, + savingContext: React.PropTypes.bool.isRequired, show: React.PropTypes.bool.isRequired }, @@ -379,6 +398,18 @@ loop.roomViews = (function(mozL10n) { this.setState({ show: false }); }, + handleContextClick: function(event) { + event.stopPropagation(); + event.preventDefault(); + + var url = this._getURL(); + if (!url || !url.location) { + return; + } + + this.props.mozLoop.openURL(url.location); + }, + handleEditClick: function(event) { event.preventDefault(); @@ -397,13 +428,13 @@ loop.roomViews = (function(mozL10n) { newRoomURL: context.url, newRoomDescription: context.description, newRoomThumbnail: context.previewImage - }, this.handleFormSubmit); + }); } else { this.setState({ newRoomURL: "", newRoomDescription: "", newRoomThumbnail: "" - }, this.handleFormSubmit); + }); } }, @@ -469,7 +500,6 @@ loop.roomViews = (function(mozL10n) { var thumbnail = url && url.thumbnail || ""; var urlDescription = url && url.description || ""; var location = url && url.location || ""; - var checkboxLabel = null; var locationData = null; if (location) { locationData = checkboxLabel = sharedUtils.formatURL(location); @@ -483,43 +513,50 @@ loop.roomViews = (function(mozL10n) { var cx = React.addons.classSet; if (this.state.editMode) { + var availableContext = this.state.availableContext; + // The checkbox shows as checked when there's already context data + // attached to this room. + var checked = !!urlDescription; + var checkboxLabel = urlDescription || (availableContext && availableContext.url ? + availableContext.description : ""); + return ( - React.createElement("div", {className: "room-context"}, - React.createElement("div", {className: "room-context-content"}, - React.createElement("p", {className: cx({"error": !!this.props.error, - "error-display-area": true})}, - mozL10n.get("rooms_change_failed_label") - ), - React.createElement("div", {className: "room-context-label"}, mozL10n.get("context_inroom_label")), - React.createElement(sharedViews.Checkbox, { - checked: !!url, - disabled: !!url || !checkboxLabel, - label: mozL10n.get("context_edit_activate_label", { - title: checkboxLabel ? checkboxLabel.hostname : "" - }), - onChange: this.handleCheckboxChange, - value: location}), - React.createElement("form", {onSubmit: this.handleFormSubmit}, - React.createElement("textarea", {rows: "2", type: "text", className: "room-context-name", - onBlur: this.handleFormSubmit, - onKeyDown: this.handleTextareaKeyDown, - placeholder: mozL10n.get("context_edit_name_placeholder"), - valueLink: this.linkState("newRoomName")}), - React.createElement("input", {type: "text", className: "room-context-url", - onBlur: this.handleFormSubmit, - onKeyDown: this.handleTextareaKeyDown, - placeholder: "https://", - valueLink: this.linkState("newRoomURL")}), - React.createElement("textarea", {rows: "4", type: "text", className: "room-context-comments", - onBlur: this.handleFormSubmit, - onKeyDown: this.handleTextareaKeyDown, - placeholder: mozL10n.get("context_edit_comments_placeholder"), - valueLink: this.linkState("newRoomDescription")}) - ), - React.createElement("button", {className: "room-context-btn-close", - onClick: this.handleCloseClick, - title: mozL10n.get("cancel_button")}) - ) + React.createElement("div", {className: "room-context editMode"}, + React.createElement("p", {className: cx({"error": !!this.props.error, + "error-display-area": true})}, + mozL10n.get("rooms_change_failed_label") + ), + React.createElement("div", {className: "room-context-label"}, mozL10n.get("context_inroom_label")), + React.createElement(sharedViews.Checkbox, { + additionalClass: cx({ hide: !checkboxLabel }), + checked: checked, + disabled: checked, + label: checkboxLabel, + onChange: this.handleCheckboxChange, + value: location}), + React.createElement("form", {onSubmit: this.handleFormSubmit}, + React.createElement("input", {type: "text", className: "room-context-name", + onKeyDown: this.handleTextareaKeyDown, + placeholder: mozL10n.get("context_edit_name_placeholder"), + valueLink: this.linkState("newRoomName")}), + React.createElement("input", {type: "text", className: "room-context-url", + onKeyDown: this.handleTextareaKeyDown, + placeholder: "https://", + disabled: availableContext && availableContext.url === this.state.newRoomURL, + valueLink: this.linkState("newRoomURL")}), + React.createElement("textarea", {rows: "3", type: "text", className: "room-context-comments", + onKeyDown: this.handleTextareaKeyDown, + placeholder: mozL10n.get("context_edit_comments_placeholder"), + valueLink: this.linkState("newRoomDescription")}) + ), + React.createElement("button", {className: "btn btn-info", + disabled: this.props.savingContext, + onClick: this.handleFormSubmit}, + mozL10n.get("context_save_label") + ), + React.createElement("button", {className: "room-context-btn-close", + onClick: this.handleCloseClick, + title: mozL10n.get("cancel_button")}) ) ); } @@ -530,25 +567,23 @@ loop.roomViews = (function(mozL10n) { return ( React.createElement("div", {className: "room-context"}, - React.createElement("img", {className: "room-context-thumbnail", src: thumbnail}), - React.createElement("div", {className: "room-context-content"}, - React.createElement("div", {className: "room-context-label"}, mozL10n.get("context_inroom_label")), + React.createElement("div", {className: "room-context-label"}, mozL10n.get("context_inroom_label")), + React.createElement("div", {className: "room-context-content", + onClick: this.handleContextClick}, + React.createElement("img", {className: "room-context-thumbnail", src: thumbnail}), React.createElement("div", {className: "room-context-description", - title: urlDescription}, this._truncate(urlDescription)), - React.createElement("a", {className: "room-context-url", - href: location, - target: "_blank", - title: locationData.location}, locationData.hostname), - this.props.roomData.roomDescription ? - React.createElement("div", {className: "room-context-comment"}, this.props.roomData.roomDescription) : - null, - React.createElement("button", {className: "room-context-btn-close", - onClick: this.handleCloseClick, - title: mozL10n.get("context_hide_tooltip")}), - React.createElement("button", {className: "room-context-btn-edit", - onClick: this.handleEditClick, - title: mozL10n.get("context_edit_tooltip")}) - ) + title: urlDescription}, + this._truncate(urlDescription), + React.createElement("a", {className: "room-context-url", + title: locationData.location}, locationData.hostname) + ) + ), + React.createElement("button", {className: "room-context-btn-close", + onClick: this.handleCloseClick, + title: mozL10n.get("context_hide_tooltip")}), + React.createElement("button", {className: "room-context-btn-edit", + onClick: this.handleEditClick, + title: mozL10n.get("context_edit_tooltip")}) ) ); } @@ -671,6 +706,7 @@ loop.roomViews = (function(mozL10n) { error: this.state.error, mozLoop: this.props.mozLoop, roomData: roomData, + savingContext: this.state.savingContext, show: shouldRenderInvitationOverlay, showContext: shouldRenderContextView, socialShareButtonAvailable: this.state.socialShareButtonAvailable, @@ -696,6 +732,7 @@ loop.roomViews = (function(mozL10n) { React.createElement(DesktopRoomContextView, { dispatcher: this.props.dispatcher, error: this.state.error, + savingContext: this.state.savingContext, mozLoop: this.props.mozLoop, roomData: roomData, show: !shouldRenderInvitationOverlay && shouldRenderContextView}) diff --git a/browser/components/loop/content/js/roomViews.jsx b/browser/components/loop/content/js/roomViews.jsx index 2677133388e..19d4900c79d 100644 --- a/browser/components/loop/content/js/roomViews.jsx +++ b/browser/components/loop/content/js/roomViews.jsx @@ -29,6 +29,8 @@ loop.roomViews = (function(mozL10n) { this._onActiveRoomStateChanged); this.listenTo(this.props.roomStore, "change:error", this._onRoomError); + this.listenTo(this.props.roomStore, "change:savingContext", + this._onRoomSavingContext); }, componentWillUnmount: function() { @@ -53,11 +55,21 @@ loop.roomViews = (function(mozL10n) { } }, + _onRoomSavingContext: function() { + // Only update the state if we're mounted, to avoid the problem where + // stopListening doesn't nuke the active listeners during a event + // processing. + if (this.isMounted()) { + this.setState({savingContext: this.props.roomStore.getStoreState("savingContext")}); + } + }, + getInitialState: function() { var storeState = this.props.roomStore.getStoreState("activeRoom"); return _.extend({ // Used by the UI showcase. - roomState: this.props.roomState || storeState.roomState + roomState: this.props.roomState || storeState.roomState, + savingContext: false }, storeState); } }; @@ -174,6 +186,7 @@ loop.roomViews = (function(mozL10n) { mozLoop: React.PropTypes.object.isRequired, // This data is supplied by the activeRoomStore. roomData: React.PropTypes.object.isRequired, + savingContext: React.PropTypes.bool, show: React.PropTypes.bool.isRequired, showContext: React.PropTypes.bool.isRequired }, @@ -243,7 +256,11 @@ loop.roomViews = (function(mozL10n) { {mozL10n.get("context_add_some_label")} -
+