author | Ryan VanderMeulen <ryanvm@gmail.com> |
Wed, 17 Jun 2015 15:01:24 -0400 | |
changeset 249424 | a3f280b6f8d5c1a2894421f92a4e308769de5bea |
parent 249408 | 31d1aea7dc85d06787fc9063c5bc49f0c1e4f8f7 (current diff) |
parent 249423 | 06d8b833c6a2c277d8c69c81f250c05bb2005c71 (diff) |
child 249425 | b40af190753c5fe1298b2e4ea03b99fdc4b5d42e |
child 249502 | df807da6ed3c4ae92ebbd8d3e098cd61f0f7b076 |
child 249524 | 6876a553b8982a2d03ed252c6a56e811eb3c05fb |
push id | 61241 |
push user | ryanvm@gmail.com |
push date | Wed, 17 Jun 2015 19:03:38 +0000 |
treeherder | mozilla-inbound@b40af190753c [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 41.0a1 |
first release with | nightly linux32
a3f280b6f8d5
/
41.0a1
/
20150618030206
/
files
nightly linux64
a3f280b6f8d5
/
41.0a1
/
20150618030206
/
files
nightly mac
a3f280b6f8d5
/
41.0a1
/
20150618030206
/
files
nightly win32
a3f280b6f8d5
/
41.0a1
/
20150618030206
/
files
nightly win64
a3f280b6f8d5
/
41.0a1
/
20150618030206
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
41.0a1
/
20150618030206
/
pushlog to previous
nightly linux64
41.0a1
/
20150618030206
/
pushlog to previous
nightly mac
41.0a1
/
20150618030206
/
pushlog to previous
nightly win32
41.0a1
/
20150618030206
/
pushlog to previous
nightly win64
41.0a1
/
20150618030206
/
pushlog to previous
|
mobile/android/base/resources/drawable-hdpi/menu_pb.png | file | annotate | diff | comparison | revisions | |
mobile/android/base/resources/drawable-mdpi/menu_pb.png | file | annotate | diff | comparison | revisions | |
mobile/android/base/resources/drawable-xhdpi/menu_pb.png | file | annotate | diff | comparison | revisions | |
mobile/android/base/resources/drawable/menu_level.xml | file | annotate | diff | comparison | revisions |
--- a/b2g/components/test/unit/test_logcapture_gonk.js +++ b/b2g/components/test/unit/test_logcapture_gonk.js @@ -28,17 +28,27 @@ add_test(function test_readLogFile() { run_next_test(); }); add_test(function test_readProperties() { let propertiesLog = LogCapture.readProperties(); notEqual(propertiesLog, null, "Properties should not be null"); notEqual(propertiesLog, undefined, "Properties should not be undefined"); - equal(propertiesLog["ro.kernel.qemu"], "1", "QEMU property should be 1"); + + for (let propertyName in propertiesLog) { + equal(typeof(propertiesLog[propertyName]), "string", + "Property " + propertyName + " should be a string"); + } + + equal(propertiesLog["ro.product.locale.language"], "en", + "Locale language should be read correctly. See bug 1171577."); + + equal(propertiesLog["ro.product.locale.region"], "US", + "Locale region should be read correctly. See bug 1171577."); run_next_test(); }); add_test(function test_readAppIni() { let appIni = LogCapture.readLogFile("/system/b2g/application.ini"); verifyLog(appIni);
--- a/browser/components/loop/.eslintrc +++ b/browser/components/loop/.eslintrc @@ -46,17 +46,16 @@ "no-console": 0, // Leave as 0. We use console logging in content code. "no-empty": 0, // TODO: Remove (use default) "no-extra-bind": 0, // Leave as 0 "no-extra-boolean-cast": 0, // TODO: Remove (use default) "no-multi-spaces": 0, // TBD. "no-new": 0, // TODO: Remove (use default) "no-redeclare": 0, // TODO: Remove (use default) "no-return-assign": 0, // TODO: Remove (use default) - "no-shadow": 0, // TODO: Remove (use default) "no-underscore-dangle": 0, // Leave as 0. Commonly used for private variables. "no-unneeded-ternary": 2, "no-unused-expressions": 0, // TODO: Remove (use default) "no-unused-vars": 0, // TODO: Remove (use default) "no-use-before-define": 0, // TODO: Remove (use default) "quotes": [2, "double", "avoid-escape"], "strict": 0, // [2, "function"], // eslint-plugin-react rules. These are documented at
--- a/browser/components/loop/content/js/client.js +++ b/browser/components/loop/content/js/client.js @@ -101,19 +101,19 @@ loop.Client = (function($) { try { var postData = JSON.parse(responseText); var outgoingCallData = this._validate(postData, expectedPostCallProperties); cb(null, outgoingCallData); - } catch (err) { - console.log("Error requesting call info", err); - cb(err); + } catch (ex) { + console.log("Error requesting call info", ex); + cb(ex); } }.bind(this) ); } }; return Client; })(jQuery);
--- a/browser/components/loop/content/js/contacts.jsx +++ b/browser/components/loop/content/js/contacts.jsx @@ -480,19 +480,19 @@ loop.contacts = (function(_, mozL10n) { case "edit": this.props.startForm("contacts_edit", contact); break; case "remove": navigator.mozLoop.confirm({ message: mozL10n.get("confirm_delete_contact_alert"), okButton: mozL10n.get("confirm_delete_contact_remove_button"), cancelButton: mozL10n.get("confirm_delete_contact_cancel_button") - }, (err, result) => { - if (err) { - throw err; + }, (error, result) => { + if (error) { + throw error; } if (!result) { return; } navigator.mozLoop.contacts.remove(contact._guid, err => { if (err) {
--- a/browser/components/loop/content/js/panel.js +++ b/browser/components/loop/content/js/panel.js @@ -715,16 +715,22 @@ loop.panel = (function(_, mozL10n) { onDocumentVisible: function() { // We would use onDocumentHidden to null out the data ready for the next // opening. However, this seems to cause an awkward glitch in the display // when opening the panel, and it seems cleaner just to update the data // even if there's a small delay. this.props.mozLoop.getSelectedTabMetadata(function callback(metadata) { + // Bail out when the component is not mounted (anymore). + // This occurs during test runs. See bug 1174611 for more info. + if (!this.isMounted()) { + return; + } + var previewImage = metadata.favicon || ""; var description = metadata.title || metadata.description; var url = metadata.url; this.setState({ previewImage: previewImage, description: description, url: url });
--- a/browser/components/loop/content/js/panel.jsx +++ b/browser/components/loop/content/js/panel.jsx @@ -715,16 +715,22 @@ loop.panel = (function(_, mozL10n) { onDocumentVisible: function() { // We would use onDocumentHidden to null out the data ready for the next // opening. However, this seems to cause an awkward glitch in the display // when opening the panel, and it seems cleaner just to update the data // even if there's a small delay. this.props.mozLoop.getSelectedTabMetadata(function callback(metadata) { + // Bail out when the component is not mounted (anymore). + // This occurs during test runs. See bug 1174611 for more info. + if (!this.isMounted()) { + return; + } + var previewImage = metadata.favicon || ""; var description = metadata.title || metadata.description; var url = metadata.url; this.setState({ previewImage: previewImage, description: description, url: url });
--- a/browser/components/loop/content/js/roomStore.js +++ b/browser/components/loop/content/js/roomStore.js @@ -518,19 +518,19 @@ loop.store = loop.store || {}; setTimeout(function() { this.dispatchAction(new sharedActions.UpdateRoomContextDone()); }.bind(this), 0); return; } this.setStoreState({error: null}); this._mozLoop.rooms.update(actionData.roomToken, roomData, - function(err, data) { - var action = err ? - new sharedActions.UpdateRoomContextError({ error: err }) : + function(error, data) { + var action = error ? + new sharedActions.UpdateRoomContextError({ error: error }) : new sharedActions.UpdateRoomContextDone(); this.dispatchAction(action); }.bind(this)); }.bind(this)); }, /** * Handles the updateRoomContextDone action.
--- a/browser/components/loop/content/js/roomViews.jsx +++ b/browser/components/loop/content/js/roomViews.jsx @@ -87,19 +87,20 @@ loop.roomViews = (function(mozL10n) { this.props.dispatcher.dispatch(new sharedActions.AddSocialShareProvider()); }, handleProviderClick: function(event) { event.preventDefault(); var origin = event.currentTarget.dataset.provider; - var provider = this.props.socialShareProviders.filter(function(provider) { - return provider.origin == origin; - })[0]; + var provider = this.props.socialShareProviders + .filter(function(socialProvider) { + return socialProvider.origin == origin; + })[0]; this.props.dispatcher.dispatch(new sharedActions.ShareRoomUrl({ provider: provider, roomUrl: this.props.roomUrl, previews: [] })); }, @@ -299,22 +300,22 @@ loop.roomViews = (function(mozL10n) { newState.editMode = nextProps.editMode; // If we're switching to edit mode, fetch the metadata of the current tab. // But _only_ if there's no context currently attached to the room; the // checkbox will be disabled in that case. if (nextProps.editMode) { this.props.mozLoop.getSelectedTabMetadata(function(metadata) { var previewImage = metadata.favicon || ""; var description = metadata.title || metadata.description; - var url = metadata.url; + var metaUrl = metadata.url; this.setState({ availableContext: { previewImage: previewImage, description: description, - url: url + url: metaUrl } }); }.bind(this)); } } // When we receive an update for the `roomData` property, make sure that // the current form fields reflect reality. This is necessary, because the // form state is maintained in the components' state.
--- a/browser/components/loop/content/shared/js/activeRoomStore.js +++ b/browser/components/loop/content/shared/js/activeRoomStore.js @@ -315,17 +315,17 @@ loop.store.ActiveRoomStore = (function() .then(function(decryptedResult) { var realResult = JSON.parse(decryptedResult); roomInfoData.description = realResult.description; roomInfoData.urls = realResult.urls; roomInfoData.roomName = realResult.roomName; dispatcher.dispatch(roomInfoData); - }, function(err) { + }, function(error) { roomInfoData.roomInfoFailure = ROOM_INFO_FAILURES.DECRYPT_FAILED; dispatcher.dispatch(roomInfoData); }); }.bind(this)); }, /** * Handles the setupRoomInfo action. Sets up the initial room data and
--- a/browser/components/loop/content/shared/js/otSdkDriver.js +++ b/browser/components/loop/content/shared/js/otSdkDriver.js @@ -675,43 +675,43 @@ loop.OTSdkDriver = (function() { if (err) { console.error(err); return; } this._publisherChannel = channel; channel.on({ - close: function(event) { + close: function(e) { // XXX We probably want to dispatch and handle this somehow. console.log("Published data channel closed!"); } }); this._checkDataChannelsAvailable(); }.bind(this)); this.subscriber._.getDataChannel("text", {}, function(err, channel) { // Sends will queue until the channel is fully open. if (err) { console.error(err); return; } channel.on({ - message: function(event) { + message: function(ev) { try { this.dispatcher.dispatch( - new sharedActions.ReceivedTextChatMessage(JSON.parse(event.data))); + new sharedActions.ReceivedTextChatMessage(JSON.parse(ev.data))); } catch (ex) { console.error("Failed to process incoming chat message", ex); } }.bind(this), - close: function(event) { + close: function(e) { // XXX We probably want to dispatch and handle this somehow. console.log("Subscribed data channel closed!"); } }); this._subscriberChannel = channel; this._checkDataChannelsAvailable(); }.bind(this));
--- a/browser/components/loop/content/shared/js/views.jsx +++ b/browser/components/loop/content/shared/js/views.jsx @@ -386,24 +386,24 @@ loop.shared.views = (function(_, l10n) { var outgoing = this.getDOMNode().querySelector(".local"); // XXX move this into its StreamingVideo component? this.publisher = this.props.sdk.initPublisher( outgoing, this.getDefaultPublisherConfig({publishVideo: this.props.video.enabled})); // Suppress OT GuM custom dialog, see bug 1018875 this.listenTo(this.publisher, "accessDialogOpened accessDenied", - function(event) { - event.preventDefault(); + function(ev) { + ev.preventDefault(); }); - this.listenTo(this.publisher, "streamCreated", function(event) { + this.listenTo(this.publisher, "streamCreated", function(ev) { this.setState({ - audio: {enabled: event.stream.hasAudio}, - video: {enabled: event.stream.hasVideo} + audio: {enabled: ev.stream.hasAudio}, + video: {enabled: ev.stream.hasVideo} }); }.bind(this)); this.listenTo(this.publisher, "streamDestroyed", function() { this.setState({ audio: {enabled: false}, video: {enabled: false} });
--- a/browser/components/loop/modules/LoopContacts.jsm +++ b/browser/components/loop/modules/LoopContacts.jsm @@ -421,19 +421,19 @@ let LoopContactsInternal = Object.freeze */ remove: function(guid, callback) { this.get(guid, (err, contact) => { if (err) { callback(err); return; } - LoopStorage.getStore(kObjectStoreName, (err, store) => { - if (err) { - callback(err); + LoopStorage.getStore(kObjectStoreName, (error, store) => { + if (error) { + callback(error); return; } let request; try { request = store.delete(guid); } catch (ex) { callback(ex); @@ -679,19 +679,19 @@ let LoopContactsInternal = Object.freeze } if (!contact) { callback(new Error("Contact with " + kKeyPath + " '" + guid + "' could not be found")); return; } - LoopStorage.getStore(kObjectStoreName, (err, store) => { - if (err) { - callback(err); + LoopStorage.getStore(kObjectStoreName, (error, store) => { + if (error) { + callback(error); return; } let previous = extend({}, contact); // Update the contact with properties provided by `details`. extend(contact, details); details._date_lch = Date.now();
--- a/browser/components/loop/modules/MozLoopAPI.jsm +++ b/browser/components/loop/modules/MozLoopAPI.jsm @@ -119,16 +119,35 @@ const cloneValueInto = function(value, t MozLoopService.log.debug("Failed to clone value:", value); throw ex; } return clone; }; /** + * Guarded callback invocation that reports when a callback function doesn't + * exist anymore. + * + * @param {Function} callback Callback function to be invoked. + * @param {...mixed} args Rest param of callback function arguments. + */ +const invokeCallback = function(callback, ...args) { + if (typeof callback != "function") { + // We log an error, because it will have a stack trace attached which will + // be helpful whilst debugging. + MozLoopService.log.error.apply(MozLoopService.log, + [new Error("Callback function was lost!"), ...args]); + return; + } + + return callback.apply(null, args); +}; + +/** * Get the two-digit hexadecimal code for a byte * * @param {byte} charCode */ const toHexString = function(charCode) { return ("0" + charCode.toString(16)).slice(-2); }; @@ -429,17 +448,17 @@ function injectLoopAPI(targetWindow) { * `Error` object or `null`. The second argument will * be the result of the operation, if successfull. */ startImport: { enumerable: true, writable: true, value: function(options, callback) { LoopContacts.startImport(options, getChromeWindow(targetWindow), function(...results) { - callback(...[cloneValueInto(r, targetWindow) for (r of results)]); + invokeCallback(callback, ...[cloneValueInto(r, targetWindow) for (r of results)]); }); } }, /** * Returns translated strings associated with an element. Designed * for use with l10n.js * @@ -489,27 +508,27 @@ function injectLoopAPI(targetWindow) { let buttonFlags; if (options.okButton && options.cancelButton) { buttonFlags = (Ci.nsIPrompt.BUTTON_POS_0 * Ci.nsIPrompt.BUTTON_TITLE_IS_STRING) + (Ci.nsIPrompt.BUTTON_POS_1 * Ci.nsIPrompt.BUTTON_TITLE_IS_STRING); } else if (!options.okButton && !options.cancelButton) { buttonFlags = Services.prompt.STD_YES_NO_BUTTONS; } else { - callback(cloneValueInto(new Error("confirm: missing button options"), targetWindow)); + invokeCallback(callback, cloneValueInto(new Error("confirm: missing button options"), targetWindow)); } try { let chosenButton = Services.prompt.confirmEx(null, "", options.message, buttonFlags, options.okButton, options.cancelButton, null, null, {}); - callback(null, chosenButton == 0); + invokeCallback(callback, null, chosenButton == 0); } catch (ex) { - callback(cloneValueInto(ex, targetWindow)); + invokeCallback(callback, cloneValueInto(ex, targetWindow)); } } }, /** * Set any preference under "loop." * * @param {String} prefName The name of the pref without the preceding "loop." @@ -612,30 +631,23 @@ function injectLoopAPI(targetWindow) { * transmitted with the request. * @param {Function} callback Called when the request completes. */ hawkRequest: { enumerable: true, writable: true, value: function(sessionType, path, method, payloadObj, callback) { // XXX Should really return a DOM promise here. - let callbackIsFunction = (typeof callback == "function"); MozLoopService.hawkRequest(sessionType, path, method, payloadObj).then((response) => { - callback(null, response.body); + invokeCallback(callback, null, response.body); }, hawkError => { - // When the function was garbage collected due to async events, like - // closing a window, we want to circumvent a JS error. - if (callbackIsFunction && typeof callback != "function") { - MozLoopService.log.error("hawkRequest: callback function was lost.", hawkError); - return; - } // The hawkError.error property, while usually a string representing // an HTTP response status message, may also incorrectly be a native // error object that will cause the cloning function to fail. - callback(Cu.cloneInto({ + invokeCallback(callback, Cu.cloneInto({ error: (hawkError.error && typeof hawkError.error == "string") ? hawkError.error : "Unexpected exception", message: hawkError.message, code: hawkError.code, errno: hawkError.errno, }, targetWindow)); }).catch(Cu.reportError); } @@ -838,22 +850,22 @@ function injectLoopAPI(targetWindow) { .createInstance(Ci.nsIXMLHttpRequest); let url = `chrome://browser/content/loop/shared/sounds/${name}.ogg`; request.open("GET", url, true); request.responseType = "arraybuffer"; request.onload = () => { if (request.status < 200 || request.status >= 300) { let error = new Error(request.status + " " + request.statusText); - callback(cloneValueInto(error, targetWindow)); + invokeCallback(callback, cloneValueInto(error, targetWindow)); return; } let blob = new Blob([request.response], {type: "audio/ogg"}); - callback(null, cloneValueInto(blob, targetWindow)); + invokeCallback(callback, null, cloneValueInto(blob, targetWindow)); }; request.send(); } }, /** * Compose a URL pointing to the location of an avatar by email address. @@ -907,17 +919,17 @@ function injectLoopAPI(targetWindow) { win.LoopUI.getFavicon(function(err, favicon) { if (err) { MozLoopService.log.error("Error occurred whilst fetching favicon", err); // We don't return here intentionally to make sure the callback is // invoked at all times. We just report the error here. } pageData.favicon = favicon || null; - callback(cloneValueInto(pageData, targetWindow)); + invokeCallback(callback, cloneValueInto(pageData, targetWindow)); }); }); win.gBrowser.selectedBrowser.messageManager.sendAsyncMessage("PageMetadata:GetPageData"); } }, /** * Associates a session-id and a call-id with a window for debugging.
--- a/browser/components/loop/modules/MozLoopService.jsm +++ b/browser/components/loop/modules/MozLoopService.jsm @@ -352,18 +352,18 @@ let MozLoopServiceInternal = { * with this channel from the PushServer. * @returns {Promise} A promise that is resolved with no params on completion, or * rejected with an error code or string. */ createNotificationChannel: function(channelID, sessionType, serviceType, onNotification) { log.debug("createNotificationChannel", channelID, sessionType, serviceType); // Wrap the push notification registration callback in a Promise. return new Promise((resolve, reject) => { - let onRegistered = (error, pushURL, channelID) => { - log.debug("createNotificationChannel onRegistered:", error, pushURL, channelID); + let onRegistered = (error, pushURL, chID) => { + log.debug("createNotificationChannel onRegistered:", error, pushURL, chID); if (error) { reject(Error(error)); } else { resolve(this.registerWithLoopServer(sessionType, serviceType, pushURL)); } }; this.pushHandler.register(channelID, onRegistered, onNotification); @@ -506,36 +506,36 @@ let MozLoopServiceInternal = { } let error, pushURLs = this.pushURLs.get(sessionType), callsPushURL = pushURLs ? pushURLs.calls : null, roomsPushURL = pushURLs ? pushURLs.rooms : null; this.pushURLs.delete(sessionType); - let unregister = (sessionType, pushURL) => { + let unregister = (sessType, pushURL) => { if (!pushURL) { return Promise.resolve("no pushURL of this type to unregister"); } let unregisterURL = "/registration?simplePushURL=" + encodeURIComponent(pushURL); - return this.hawkRequestInternal(sessionType, unregisterURL, "DELETE").then( + return this.hawkRequestInternal(sessType, unregisterURL, "DELETE").then( () => { - log.debug("Successfully unregistered from server for sessionType = ", sessionType); - return "unregistered sessionType " + sessionType; + log.debug("Successfully unregistered from server for sessionType = ", sessType); + return "unregistered sessionType " + sessType; }, - error => { - if (error.code === 401) { + err => { + if (err.code === 401) { // Authorization failed, invalid token. This is fine since it may mean we already logged out. - log.debug("already unregistered - invalid token", sessionType); - return "already unregistered, sessionType = " + sessionType; + log.debug("already unregistered - invalid token", sessType); + return "already unregistered, sessionType = " + sessType; } log.error("Failed to unregister with the loop server. Error: ", error); - throw error; + throw err; }); }; return Promise.all([unregister(sessionType, callsPushURL), unregister(sessionType, roomsPushURL)]); }, /** * Performs a hawk based request to the loop server - there is no pre-registration @@ -880,20 +880,20 @@ let MozLoopServiceInternal = { window.addEventListener("socialFrameAttached", socialFrameChanged.bind(null, "Loop:ChatWindowAttached")); window.addEventListener("unload", socialFrameChanged.bind(null, "Loop:ChatWindowClosed")); const kSizeMap = { LoopChatEnabled: "loopChatEnabled", LoopChatMessageAppended: "loopChatMessageAppended" }; - function onChatEvent(event) { + function onChatEvent(ev) { // When the chat box or messages are shown, resize the panel or window // to be slightly higher to accomodate them. - let customSize = kSizeMap[event.type]; + let customSize = kSizeMap[ev.type]; if (customSize) { chatbox.setAttribute("customSize", customSize); chatbox.parentNode.setAttribute("customSize", customSize); } } window.addEventListener("LoopChatEnabled", onChatEvent); window.addEventListener("LoopChatMessageAppended", onChatEvent); @@ -904,18 +904,18 @@ let MozLoopServiceInternal = { .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID; let onPCLifecycleChange = (pc, winID, type) => { if (winID != ourID) { return; } // Chat Window Id, this is different that the internal winId - let windowId = window.location.hash.slice(1); - var context = this.conversationContexts.get(windowId); + let chatWindowId = window.location.hash.slice(1); + var context = this.conversationContexts.get(chatWindowId); var exists = pc.id.match(/session=(\S+)/); if (context && !exists) { // Not ideal but insert our data amidst existing data like this: // - 000 (id=00 url=http) // + 000 (session=000 call=000 id=00 url=http) var pair = pc.id.split("("); //) if (pair.length == 2) { pc.id = pair[0] + "(session=" + context.sessionId + @@ -937,26 +937,27 @@ let MozLoopServiceInternal = { let pc_static = new window.mozRTCPeerConnectionStatic(); pc_static.registerPeerConnectionLifecycleCallback(onPCLifecycleChange); UITour.notify("Loop:ChatWindowOpened"); }.bind(this), true); }; - let chatbox = Chat.open(null, origin, "", url, undefined, undefined, callback); - if (!chatbox) { + let chatboxInstance = Chat.open(null, origin, "", url, undefined, undefined, + callback); + if (!chatboxInstance) { return null; // It's common for unit tests to overload Chat.open. - } else if (chatbox.setAttribute) { + } else if (chatboxInstance.setAttribute) { // Set properties that influence visual appeara nce of the chatbox right // away to circumvent glitches. - chatbox.setAttribute("dark", true); - chatbox.setAttribute("customSize", "loopDefault"); - chatbox.parentNode.setAttribute("customSize", "loopDefault"); + chatboxInstance.setAttribute("dark", true); + chatboxInstance.setAttribute("customSize", "loopDefault"); + chatboxInstance.parentNode.setAttribute("customSize", "loopDefault"); } return windowId; }, /** * Fetch Firefox Accounts (FxA) OAuth parameters from the Loop Server. * * @return {Promise} resolved with the body of the hawk request for OAuth parameters. @@ -1227,20 +1228,20 @@ this.MozLoopService = { if (!this.getLoopPref("gettingStarted.resumeOnFirstJoin")) { return; } if (!room.participants) { return; } - // The particpant that joined isn't necessarily included in room.participants (depending on + // The participant that joined isn't necessarily included in room.participants (depending on // when the broadcast happens) so concatenate. - for (let participant of room.participants.concat(participant)) { - if (participant.owner) { + for (let roomParticipant of room.participants.concat(participant)) { + if (roomParticipant.owner) { isOwnerInRoom = true; } else { isOtherInRoom = true; } } if (!isOwnerInRoom || !isOtherInRoom) { return;
--- a/browser/components/loop/standalone/content/js/standaloneAppStore.js +++ b/browser/components/loop/standalone/content/js/standaloneAppStore.js @@ -16,17 +16,18 @@ loop.store.StandaloneAppStore = (functio var sharedUtils = loop.shared.utils; var CALL_REGEXP = /\/c\/([\w\-]+)$/; var ROOM_REGEXP = /\/([\w\-]+)$/; /** * Constructor * - * @param {Object} options Options for the store. Should contain the dispatcher. + * @param {Object} options Options for the store. Should contain the + * dispatcher. */ var StandaloneAppStore = function(options) { if (!options.dispatcher) { throw new Error("Missing option dispatcher"); } if (!options.sdk) { throw new Error("Missing option sdk"); } @@ -64,19 +65,19 @@ loop.store.StandaloneAppStore = (functio this.trigger("change"); }, _extractWindowDataFromPath: function(windowPath) { var match; var windowType = "home"; function extractId(path, regexp) { - var match = path.match(regexp); - if (match && match[1]) { - return match; + var pathMatch = path.match(regexp); + if (pathMatch && pathMatch[1]) { + return pathMatch; } return null; } if (windowPath) { match = extractId(windowPath, CALL_REGEXP); if (match) { @@ -136,18 +137,18 @@ loop.store.StandaloneAppStore = (functio } this.setStoreState({ windowType: windowType, isFirefox: sharedUtils.isFirefox(navigator.userAgent), unsupportedPlatform: unsupportedPlatform }); - // If we've not got a window ID, don't dispatch the action, as we don't need - // it. + // If we've not got a window ID, don't dispatch the action, as we don't + // need it. if (token) { this._dispatcher.dispatch(new loop.shared.actions.FetchServerData({ cryptoKey: this._extractCryptoKey(actionData.windowHash), token: token, windowType: windowType })); } }
--- a/browser/components/loop/test/desktop-local/conversationViews_test.js +++ b/browser/components/loop/test/desktop-local/conversationViews_test.js @@ -19,18 +19,18 @@ describe("loop.conversationViews", funct var WEBSOCKET_REASONS = loop.shared.utils.WEBSOCKET_REASONS; // XXX refactor to Just Work with "sandbox.stubComponent" or else // just pass in the sandbox and put somewhere generally usable function stubComponent(obj, component, mockTagName){ var reactClass = React.createClass({ render: function() { - var mockTagName = mockTagName || "div"; - return React.DOM[mockTagName](null, this.props.children); + var tagName = mockTagName || "div"; + return React.DOM[tagName](null, this.props.children); } }); return sandbox.stub(obj, component, reactClass); } beforeEach(function() { sandbox = sinon.sandbox.create(); sandbox.useFakeTimers(); @@ -265,17 +265,17 @@ describe("loop.conversationViews", funct sinon.assert.calledWithMatch(dispatcher.dispatch, sinon.match.hasOwn("name", "cancelCall")); }); }); describe("CallFailedView", function() { var fakeAudio; - var contact = {email: [{value: "test@test.tld"}]}; + var fakeContact = {email: [{value: "test@test.tld"}]}; function mountTestComponent(options) { options = options || {}; return TestUtils.renderIntoDocument( React.createElement(loop.conversationViews.CallFailedView, { dispatcher: dispatcher, contact: options.contact })); @@ -287,43 +287,43 @@ describe("loop.conversationViews", funct pause: sinon.spy(), removeAttribute: sinon.spy() }; sandbox.stub(window, "Audio").returns(fakeAudio); }); it("should dispatch a retryCall action when the retry button is pressed", function() { - view = mountTestComponent({contact: contact}); + view = mountTestComponent({contact: fakeContact}); var retryBtn = view.getDOMNode().querySelector(".btn-retry"); React.addons.TestUtils.Simulate.click(retryBtn); sinon.assert.calledOnce(dispatcher.dispatch); sinon.assert.calledWithMatch(dispatcher.dispatch, sinon.match.hasOwn("name", "retryCall")); }); it("should dispatch a cancelCall action when the cancel button is pressed", function() { - view = mountTestComponent({contact: contact}); + view = mountTestComponent({contact: fakeContact}); var cancelBtn = view.getDOMNode().querySelector(".btn-cancel"); React.addons.TestUtils.Simulate.click(cancelBtn); sinon.assert.calledOnce(dispatcher.dispatch); sinon.assert.calledWithMatch(dispatcher.dispatch, sinon.match.hasOwn("name", "cancelCall")); }); it("should dispatch a fetchRoomEmailLink action when the email button is pressed", function() { - view = mountTestComponent({contact: contact}); + view = mountTestComponent({contact: fakeContact}); var emailLinkBtn = view.getDOMNode().querySelector(".btn-email"); React.addons.TestUtils.Simulate.click(emailLinkBtn); sinon.assert.calledOnce(dispatcher.dispatch); sinon.assert.calledWithMatch(dispatcher.dispatch, sinon.match.hasOwn("name", "fetchRoomEmailLink")); @@ -346,127 +346,130 @@ describe("loop.conversationViews", funct sinon.assert.calledOnce(dispatcher.dispatch); sinon.assert.calledWithMatch(dispatcher.dispatch, sinon.match.hasOwn("roomName", "Mr Fake ContactName")); }); it("should disable the email link button once the action is dispatched", function() { - view = mountTestComponent({contact: contact}); + view = mountTestComponent({contact: fakeContact}); var emailLinkBtn = view.getDOMNode().querySelector(".btn-email"); React.addons.TestUtils.Simulate.click(emailLinkBtn); expect(view.getDOMNode().querySelector(".btn-email").disabled).eql(true); }); it("should compose an email once the email link is received", function() { var composeCallUrlEmail = sandbox.stub(sharedUtils, "composeCallUrlEmail"); - view = mountTestComponent({contact: contact}); + view = mountTestComponent({contact: fakeContact}); conversationStore.setStoreState({emailLink: "http://fake.invalid/"}); sinon.assert.calledOnce(composeCallUrlEmail); sinon.assert.calledWithExactly(composeCallUrlEmail, "http://fake.invalid/", "test@test.tld"); }); it("should close the conversation window once the email link is received", function() { - view = mountTestComponent({contact: contact}); + view = mountTestComponent({contact: fakeContact}); conversationStore.setStoreState({emailLink: "http://fake.invalid/"}); sinon.assert.calledOnce(fakeWindow.close); }); it("should display an error message in case email link retrieval failed", function() { - view = mountTestComponent({contact: contact}); + view = mountTestComponent({contact: fakeContact}); conversationStore.trigger("error:emailLink"); expect(view.getDOMNode().querySelector(".error")).not.eql(null); }); it("should allow retrying to get a call url if it failed previously", function() { - view = mountTestComponent({contact: contact}); + view = mountTestComponent({contact: fakeContact}); conversationStore.trigger("error:emailLink"); expect(view.getDOMNode().querySelector(".btn-email").disabled).eql(false); }); it("should play a failure sound, once", function() { - view = mountTestComponent({contact: contact}); + view = mountTestComponent({contact: fakeContact}); sinon.assert.calledOnce(navigator.mozLoop.getAudioBlob); sinon.assert.calledWithExactly(navigator.mozLoop.getAudioBlob, "failure", sinon.match.func); sinon.assert.calledOnce(fakeAudio.play); expect(fakeAudio.loop).to.equal(false); }); it("should show 'something went wrong' when the reason is WEBSOCKET_REASONS.MEDIA_FAIL", function () { conversationStore.setStoreState({callStateReason: WEBSOCKET_REASONS.MEDIA_FAIL}); - view = mountTestComponent({contact: contact}); + view = mountTestComponent({contact: fakeContact}); sinon.assert.calledWith(document.mozL10n.get, "generic_failure_title"); }); it("should show 'contact unavailable' when the reason is WEBSOCKET_REASONS.REJECT", function () { conversationStore.setStoreState({callStateReason: WEBSOCKET_REASONS.REJECT}); - view = mountTestComponent({contact: contact}); + view = mountTestComponent({contact: fakeContact}); sinon.assert.calledWithExactly(document.mozL10n.get, "contact_unavailable_title", - {contactName: loop.conversationViews._getContactDisplayName(contact)}); + {contactName: loop.conversationViews + ._getContactDisplayName(fakeContact)}); }); it("should show 'contact unavailable' when the reason is WEBSOCKET_REASONS.BUSY", function () { conversationStore.setStoreState({callStateReason: WEBSOCKET_REASONS.BUSY}); - view = mountTestComponent({contact: contact}); + view = mountTestComponent({contact: fakeContact}); sinon.assert.calledWithExactly(document.mozL10n.get, "contact_unavailable_title", - {contactName: loop.conversationViews._getContactDisplayName(contact)}); + {contactName: loop.conversationViews + ._getContactDisplayName(fakeContact)}); }); it("should show 'something went wrong' when the reason is 'setup'", function () { conversationStore.setStoreState({callStateReason: "setup"}); - view = mountTestComponent({contact: contact}); + view = mountTestComponent({contact: fakeContact}); sinon.assert.calledWithExactly(document.mozL10n.get, "generic_failure_title"); }); it("should show 'contact unavailable' when the reason is REST_ERRNOS.USER_UNAVAILABLE", function () { conversationStore.setStoreState({callStateReason: REST_ERRNOS.USER_UNAVAILABLE}); - view = mountTestComponent({contact: contact}); + view = mountTestComponent({contact: fakeContact}); sinon.assert.calledWithExactly(document.mozL10n.get, "contact_unavailable_title", - {contactName: loop.conversationViews._getContactDisplayName(contact)}); + {contactName: loop.conversationViews + ._getContactDisplayName(fakeContact)}); }); it("should show 'no media' when the reason is FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA", function () { conversationStore.setStoreState({callStateReason: FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA}); - view = mountTestComponent({contact: contact}); + view = mountTestComponent({contact: fakeContact}); sinon.assert.calledWithExactly(document.mozL10n.get, "no_media_failure_message"); }); it("should display a generic contact unavailable msg when the reason is" + " WEBSOCKET_REASONS.BUSY and no display name is available", function() { conversationStore.setStoreState({callStateReason: WEBSOCKET_REASONS.BUSY}); var phoneOnlyContact = { @@ -760,185 +763,185 @@ describe("loop.conversationViews", funct conversationStore.setStoreState({callState: CALL_STATES.TERMINATED}); TestUtils.findRenderedComponentWithType(view, loop.conversationViews.CallFailedView); }); }); describe("AcceptCallView", function() { - var view; + var callView; function mountTestComponent(extraProps) { var props = _.extend({dispatcher: dispatcher, mozLoop: fakeMozLoop}, extraProps); return TestUtils.renderIntoDocument( React.createElement(loop.conversationViews.AcceptCallView, props)); } afterEach(function() { - view = null; + callView = null; }); it("should start alerting on display", function() { - view = mountTestComponent({ + callView = mountTestComponent({ callType: CALL_TYPES.AUDIO_VIDEO, callerId: "fake@invalid.com" }); sinon.assert.calledOnce(fakeMozLoop.startAlerting); }); it("should stop alerting when removed from the display", function() { - view = mountTestComponent({ + callView = mountTestComponent({ callType: CALL_TYPES.AUDIO_VIDEO, callerId: "fake@invalid.com" }); - view.componentWillUnmount(); + callView.componentWillUnmount(); sinon.assert.calledOnce(fakeMozLoop.stopAlerting); }); describe("default answer mode", function() { it("should display video as primary answer mode", function() { - view = mountTestComponent({ + callView = mountTestComponent({ callType: CALL_TYPES.AUDIO_VIDEO, callerId: "fake@invalid.com" }); - var primaryBtn = view.getDOMNode() - .querySelector(".fx-embedded-btn-icon-video"); + var primaryBtn = callView.getDOMNode() + .querySelector(".fx-embedded-btn-icon-video"); expect(primaryBtn).not.to.eql(null); }); it("should display audio as primary answer mode", function() { - view = mountTestComponent({ + callView = mountTestComponent({ callType: CALL_TYPES.AUDIO_ONLY, callerId: "fake@invalid.com" }); - var primaryBtn = view.getDOMNode() - .querySelector(".fx-embedded-btn-icon-audio"); + var primaryBtn = callView.getDOMNode() + .querySelector(".fx-embedded-btn-icon-audio"); expect(primaryBtn).not.to.eql(null); }); it("should accept call with video", function() { - view = mountTestComponent({ + callView = mountTestComponent({ callType: CALL_TYPES.AUDIO_VIDEO, callerId: "fake@invalid.com" }); - var primaryBtn = view.getDOMNode() - .querySelector(".fx-embedded-btn-icon-video"); + var primaryBtn = callView.getDOMNode() + .querySelector(".fx-embedded-btn-icon-video"); React.addons.TestUtils.Simulate.click(primaryBtn); sinon.assert.calledOnce(dispatcher.dispatch); sinon.assert.calledWithExactly(dispatcher.dispatch, new sharedActions.AcceptCall({ callType: CALL_TYPES.AUDIO_VIDEO })); }); it("should accept call with audio", function() { - view = mountTestComponent({ + callView = mountTestComponent({ callType: CALL_TYPES.AUDIO_ONLY, callerId: "fake@invalid.com" }); - var primaryBtn = view.getDOMNode() - .querySelector(".fx-embedded-btn-icon-audio"); + var primaryBtn = callView.getDOMNode() + .querySelector(".fx-embedded-btn-icon-audio"); React.addons.TestUtils.Simulate.click(primaryBtn); sinon.assert.calledOnce(dispatcher.dispatch); sinon.assert.calledWithExactly(dispatcher.dispatch, new sharedActions.AcceptCall({ callType: CALL_TYPES.AUDIO_ONLY })); }); it("should accept call with video when clicking on secondary btn", function() { - view = mountTestComponent({ + callView = mountTestComponent({ callType: CALL_TYPES.AUDIO_ONLY, callerId: "fake@invalid.com" }); - var secondaryBtn = view.getDOMNode() + var secondaryBtn = callView.getDOMNode() .querySelector(".fx-embedded-btn-video-small"); React.addons.TestUtils.Simulate.click(secondaryBtn); sinon.assert.calledOnce(dispatcher.dispatch); sinon.assert.calledWithExactly(dispatcher.dispatch, new sharedActions.AcceptCall({ callType: CALL_TYPES.AUDIO_VIDEO })); }); it("should accept call with audio when clicking on secondary btn", function() { - view = mountTestComponent({ + callView = mountTestComponent({ callType: CALL_TYPES.AUDIO_VIDEO, callerId: "fake@invalid.com" }); - var secondaryBtn = view.getDOMNode() + var secondaryBtn = callView.getDOMNode() .querySelector(".fx-embedded-btn-audio-small"); React.addons.TestUtils.Simulate.click(secondaryBtn); sinon.assert.calledOnce(dispatcher.dispatch); sinon.assert.calledWithExactly(dispatcher.dispatch, new sharedActions.AcceptCall({ callType: CALL_TYPES.AUDIO_ONLY })); }); }); describe("click event on .btn-decline", function() { it("should dispatch a DeclineCall action", function() { - view = mountTestComponent({ + callView = mountTestComponent({ callType: CALL_TYPES.AUDIO_VIDEO, callerId: "fake@invalid.com" }); - var buttonDecline = view.getDOMNode().querySelector(".btn-decline"); + var buttonDecline = callView.getDOMNode().querySelector(".btn-decline"); TestUtils.Simulate.click(buttonDecline); sinon.assert.calledOnce(dispatcher.dispatch); sinon.assert.calledWithExactly(dispatcher.dispatch, new sharedActions.DeclineCall({blockCaller: false})); }); }); describe("click event on .btn-block", function() { it("should dispatch a DeclineCall action with blockCaller true", function() { - view = mountTestComponent({ + callView = mountTestComponent({ callType: CALL_TYPES.AUDIO_VIDEO, callerId: "fake@invalid.com" }); - var buttonBlock = view.getDOMNode().querySelector(".btn-block"); + var buttonBlock = callView.getDOMNode().querySelector(".btn-block"); TestUtils.Simulate.click(buttonBlock); sinon.assert.calledOnce(dispatcher.dispatch); sinon.assert.calledWithExactly(dispatcher.dispatch, new sharedActions.DeclineCall({blockCaller: true})); }); }); }); describe("GenericFailureView", function() { - var view, fakeAudio; + var callView, fakeAudio; function mountTestComponent(props) { return TestUtils.renderIntoDocument( React.createElement(loop.conversationViews.GenericFailureView, props)); } beforeEach(function() { fakeAudio = { @@ -946,48 +949,53 @@ describe("loop.conversationViews", funct pause: sinon.spy(), removeAttribute: sinon.spy() }; navigator.mozLoop.doNotDisturb = false; sandbox.stub(window, "Audio").returns(fakeAudio); }); it("should play a failure sound, once", function() { - view = mountTestComponent({cancelCall: function() {}}); + callView = mountTestComponent({cancelCall: function() {}}); sinon.assert.calledOnce(navigator.mozLoop.getAudioBlob); sinon.assert.calledWithExactly(navigator.mozLoop.getAudioBlob, "failure", sinon.match.func); sinon.assert.calledOnce(fakeAudio.play); expect(fakeAudio.loop).to.equal(false); }); it("should set the title to generic_failure_title", function() { - view = mountTestComponent({cancelCall: function() {}}); + callView = mountTestComponent({cancelCall: function() {}}); expect(fakeWindow.document.title).eql("generic_failure_title"); }); - it("should show 'no media' for FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA reason", function() { - view = mountTestComponent({ - cancelCall: function() {}, - failureReason: FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA - }); + it("should show 'no media' for FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA reason", + function() { + callView = mountTestComponent({ + cancelCall: function() {}, + failureReason: FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA + }); - expect(view.getDOMNode().querySelector("h2").textContent).eql("no_media_failure_message"); - }); + expect(callView.getDOMNode().querySelector("h2").textContent) + .eql("no_media_failure_message"); + }); it("should show 'no media' for FAILURE_DETAILS.NO_MEDIA reason", function() { - view = mountTestComponent({ + callView = mountTestComponent({ cancelCall: function() {}, failureReason: FAILURE_DETAILS.NO_MEDIA }); - expect(view.getDOMNode().querySelector("h2").textContent).eql("no_media_failure_message"); + expect(callView.getDOMNode().querySelector("h2").textContent) + .eql("no_media_failure_message"); }); - it("should show 'generic_failure_title' when no reason is specified", function() { - view = mountTestComponent({cancelCall: function() {}}); + it("should show 'generic_failure_title' when no reason is specified", + function() { + callView = mountTestComponent({cancelCall: function() {}}); - expect(view.getDOMNode().querySelector("h2").textContent).eql("generic_failure_title"); - }); + expect(callView.getDOMNode().querySelector("h2").textContent) + .eql("generic_failure_title"); + }); }); });
--- a/browser/components/loop/test/desktop-local/roomStore_test.js +++ b/browser/components/loop/test/desktop-local/roomStore_test.js @@ -544,41 +544,41 @@ describe("loop.store.RoomStore", functio store.getAllRooms(); expect(store.getStoreState().pendingInitialRetrieval).eql(false); }); }); describe("ActiveRoomStore substore", function() { - var store, activeRoomStore; + var fakeStore, activeRoomStore; beforeEach(function() { activeRoomStore = new loop.store.ActiveRoomStore(dispatcher, { mozLoop: fakeMozLoop, sdkDriver: {} }); - store = new loop.store.RoomStore(dispatcher, { + fakeStore = new loop.store.RoomStore(dispatcher, { mozLoop: fakeMozLoop, activeRoomStore: activeRoomStore }); }); it("should subscribe to substore changes", function() { var fakeServerData = {fake: true}; activeRoomStore.setStoreState({serverData: fakeServerData}); - expect(store.getStoreState().activeRoom.serverData) + expect(fakeStore.getStoreState().activeRoom.serverData) .eql(fakeServerData); }); it("should trigger a change event when the substore is updated", function(done) { - store.once("change:activeRoom", function() { + fakeStore.once("change:activeRoom", function() { done(); }); activeRoomStore.setStoreState({serverData: {}}); }); }); });
--- a/browser/components/loop/test/desktop-local/roomViews_test.js +++ b/browser/components/loop/test/desktop-local/roomViews_test.js @@ -434,40 +434,40 @@ describe("loop.roomViews", function () { var hangupBtn = view.getDOMNode().querySelector(".btn-hangup"); React.addons.TestUtils.Simulate.click(hangupBtn); sinon.assert.calledOnce(fakeWindow.close); }); describe("#componentWillUpdate", function() { - function expectActionDispatched(view) { + function expectActionDispatched(component) { sinon.assert.calledOnce(dispatcher.dispatch); sinon.assert.calledWithExactly(dispatcher.dispatch, sinon.match.instanceOf(sharedActions.SetupStreamElements)); } it("should dispatch a `SetupStreamElements` action when the MEDIA_WAIT state " + "is entered", function() { activeRoomStore.setStoreState({roomState: ROOM_STATES.READY}); - var view = mountTestComponent(); + var component = mountTestComponent(); activeRoomStore.setStoreState({roomState: ROOM_STATES.MEDIA_WAIT}); - expectActionDispatched(view); + expectActionDispatched(component); }); it("should dispatch a `SetupStreamElements` action on MEDIA_WAIT state is " + "re-entered", function() { activeRoomStore.setStoreState({roomState: ROOM_STATES.ENDED}); - var view = mountTestComponent(); + var component = mountTestComponent(); activeRoomStore.setStoreState({roomState: ROOM_STATES.MEDIA_WAIT}); - expectActionDispatched(view); + expectActionDispatched(component); }); }); describe("#render", function() { it("should set document.title to store.serverData.roomName", function() { mountTestComponent(); activeRoomStore.setStoreState({roomName: "fakeName"});
--- a/browser/components/loop/test/mochitest/browser_LoopContacts.js +++ b/browser/components/loop/test/mochitest/browser_LoopContacts.js @@ -84,19 +84,19 @@ const promiseLoadContacts = function() { return new Promise((resolve, reject) => { LoopContacts.removeAll(err => { if (err) { reject(err); return; } gExpectedAdds.push(...kContacts); - LoopContacts.addMany(kContacts, (err, contacts) => { - if (err) { - reject(err); + LoopContacts.addMany(kContacts, (error, contacts) => { + if (error) { + reject(error); return; } resolve(contacts); }); }); }); }; @@ -118,17 +118,17 @@ const compareContacts = function(contact }; // LoopContacts emits various events. Test if they work as expected here. let gExpectedAdds = []; let gExpectedRemovals = []; let gExpectedUpdates = []; const onContactAdded = function(e, contact) { - let expectedIds = gExpectedAdds.map(contact => contact.id); + let expectedIds = gExpectedAdds.map(contactEntry => contactEntry.id); let idx = expectedIds.indexOf(contact.id); Assert.ok(idx > -1, "Added contact should be expected"); let expected = gExpectedAdds[idx]; compareContacts(contact, expected); gExpectedAdds.splice(idx, 1); }; const onContactRemoved = function(e, contact) { @@ -164,34 +164,34 @@ add_task(function* () { info("Add a contact."); yield new Promise((resolve, reject) => { gExpectedAdds.push(kDanglingContact); LoopContacts.add(kDanglingContact, (err, contact) => { Assert.ok(!err, "There shouldn't be an error"); compareContacts(contact, kDanglingContact); info("Check if it's persisted."); - LoopContacts.get(contact._guid, (err, contact) => { - Assert.ok(!err, "There shouldn't be an error"); - compareContacts(contact, kDanglingContact); + LoopContacts.get(contact._guid, (error, contactEntry) => { + Assert.ok(!error, "There shouldn't be an error"); + compareContacts(contactEntry, kDanglingContact); resolve(); }); }); }); }); add_task(function* () { info("Test removing all contacts."); let contacts = yield promiseLoadContacts(); yield new Promise((resolve, reject) => { LoopContacts.removeAll(function(err) { Assert.ok(!err, "There shouldn't be an error"); - LoopContacts.getAll(function(err, found) { - Assert.ok(!err, "There shouldn't be an error"); + LoopContacts.getAll(function(error, found) { + Assert.ok(!error, "There shouldn't be an error"); Assert.equal(found.length, 0, "There shouldn't be any contacts left"); resolve(); }); }); }); }); // Test retrieving a contact. @@ -234,20 +234,20 @@ add_task(function* () { compareContacts(found[0], contact); } resolve(); }); }); info("Get all contacts."); yield new Promise((resolve, reject) => { - LoopContacts.getAll((err, contacts) => { + LoopContacts.getAll((err, allContacts) => { Assert.ok(!err, "There shouldn't be an error"); - for (let i = 0, l = contacts.length; i < l; ++i) { - compareContacts(contacts[i], kContacts[i]); + for (let i = 0, l = allContacts.length; i < l; ++i) { + compareContacts(allContacts[i], kContacts[i]); } resolve(); }); }); info("Get a non-existent contact."); return new Promise((resolve, reject) => { LoopContacts.get(1000, (err, contact) => { @@ -264,18 +264,18 @@ add_task(function* () { info("Remove a single contact."); yield new Promise((resolve, reject) => { let toRemove = contacts[2]._guid; gExpectedRemovals.push(toRemove); LoopContacts.remove(toRemove, err => { Assert.ok(!err, "There shouldn't be an error"); - LoopContacts.get(toRemove, (err, contact) => { - Assert.ok(!err, "There shouldn't be an error"); + LoopContacts.get(toRemove, (error, contact) => { + Assert.ok(!error, "There shouldn't be an error"); Assert.ok(!contact, "There shouldn't be a contact"); resolve(); }); }); }); info("Remove a non-existing contact."); yield new Promise((resolve, reject) => { @@ -288,19 +288,19 @@ add_task(function* () { info("Remove multiple contacts."); yield new Promise((resolve, reject) => { let toRemove = [contacts[0]._guid, contacts[1]._guid]; gExpectedRemovals.push(...toRemove); LoopContacts.removeMany(toRemove, err => { Assert.ok(!err, "There shouldn't be an error"); - LoopContacts.getAll((err, contacts) => { - Assert.ok(!err, "There shouldn't be an error"); - let ids = contacts.map(contact => contact._guid); + LoopContacts.getAll((error, allContacts) => { + Assert.ok(!error, "There shouldn't be an error"); + let ids = allContacts.map(contact => contact._guid); Assert.equal(ids.indexOf(toRemove[0]), -1, "Contact '" + toRemove[0] + "' shouldn't be there"); Assert.equal(ids.indexOf(toRemove[1]), -1, "Contact '" + toRemove[1] + "' shouldn't be there"); resolve(); }); }); }); @@ -318,18 +318,18 @@ add_task(function* () { _guid: contacts[2]._guid, bday: newBday }; gExpectedUpdates.push(contacts[2]._guid); LoopContacts.update(toUpdate, (err, result) => { Assert.ok(!err, "There shouldn't be an error"); Assert.equal(result, toUpdate._guid, "Result should be the same as the contact ID"); - LoopContacts.get(toUpdate._guid, (err, contact) => { - Assert.ok(!err, "There shouldn't be an error"); + LoopContacts.get(toUpdate._guid, (error, contact) => { + Assert.ok(!error, "There shouldn't be an error"); Assert.equal(contact.bday, newBday, "Birthday should be the same"); info("Check that all other properties were left intact."); contacts[2].bday = newBday; compareContacts(contact, contacts[2]); resolve(); }); }); }); @@ -356,18 +356,18 @@ add_task(function* () { info("Block contact."); yield new Promise((resolve, reject) => { let toBlock = contacts[1]._guid; gExpectedUpdates.push(toBlock); LoopContacts.block(toBlock, (err, result) => { Assert.ok(!err, "There shouldn't be an error"); Assert.equal(result, toBlock, "Result should be the same as the contact ID"); - LoopContacts.get(toBlock, (err, contact) => { - Assert.ok(!err, "There shouldn't be an error"); + LoopContacts.get(toBlock, (error, contact) => { + Assert.ok(!error, "There shouldn't be an error"); Assert.strictEqual(contact.blocked, true, "Blocked status should be set"); info("Check that all other properties were left intact."); delete contact.blocked; compareContacts(contact, contacts[1]); resolve(); }); }); }); @@ -385,18 +385,18 @@ add_task(function* () { info("Unblock a contact."); yield new Promise((resolve, reject) => { let toUnblock = contacts[1]._guid; gExpectedUpdates.push(toUnblock); LoopContacts.unblock(toUnblock, (err, result) => { Assert.ok(!err, "There shouldn't be an error"); Assert.equal(result, toUnblock, "Result should be the same as the contact ID"); - LoopContacts.get(toUnblock, (err, contact) => { - Assert.ok(!err, "There shouldn't be an error"); + LoopContacts.get(toUnblock, (error, contact) => { + Assert.ok(!error, "There shouldn't be an error"); Assert.strictEqual(contact.blocked, false, "Blocked status should be set"); info("Check that all other properties were left intact."); delete contact.blocked; compareContacts(contact, contacts[1]); resolve(); }); }); });
--- a/browser/components/loop/test/mochitest/browser_mozLoop_sharingListeners.js +++ b/browser/components/loop/test/mochitest/browser_mozLoop_sharingListeners.js @@ -32,20 +32,20 @@ function promiseWindowIdReceivedOnAdd(ha return new Promise(resolve => { handler.resolve = resolve; gMozLoopAPI.addBrowserSharingListener(handler.listener); }); } let createdTabs = []; -function promiseWindowIdReceivedNewTab(handlers = []) { +function promiseWindowIdReceivedNewTab(handlersParam = []) { let promiseHandlers = []; - handlers.forEach(handler => { + handlersParam.forEach(handler => { promiseHandlers.push(new Promise(resolve => { handler.resolve = resolve; })); }); let createdTab = gBrowser.selectedTab = gBrowser.addTab(); createdTabs.push(createdTab);
--- a/browser/components/loop/test/mochitest/head.js +++ b/browser/components/loop/test/mochitest/head.js @@ -179,18 +179,18 @@ function promiseDeletedOAuthParams(baseU xhr.addEventListener("load", () => resolve(xhr)); xhr.addEventListener("error", reject); xhr.send(); }); } function promiseObserverNotified(aTopic, aExpectedData = null) { return new Promise((resolve, reject) => { - Services.obs.addObserver(function onNotification(aSubject, aTopic, aData) { - Services.obs.removeObserver(onNotification, aTopic); + Services.obs.addObserver(function onNotification(aSubject, topic, aData) { + Services.obs.removeObserver(onNotification, topic); is(aData, aExpectedData, "observer data should match expected data"); resolve({subject: aSubject, data: aData}); }, aTopic, false); }); } /** * Get the last registration on the test server.
--- a/browser/components/loop/test/shared/conversationStore_test.js +++ b/browser/components/loop/test/shared/conversationStore_test.js @@ -725,24 +725,24 @@ describe("loop.store.ConversationStore", sinon.match.hasOwn("reason", "websocket-setup")); }); }); }); }); }); describe("#hangupCall", function() { - var wsMediaFailSpy, wsCloseSpy; + var wsMediaFailSpy, wsHangupSpy; beforeEach(function() { wsMediaFailSpy = sinon.spy(); - wsCloseSpy = sinon.spy(); + wsHangupSpy = sinon.spy(); store._websocket = { mediaFail: wsMediaFailSpy, - close: wsCloseSpy + close: wsHangupSpy }; store.setStoreState({callState: CALL_STATES.ONGOING}); store.setStoreState({windowId: "42"}); }); it("should disconnect the session", function() { store.hangupCall(new sharedActions.HangupCall()); @@ -753,17 +753,17 @@ describe("loop.store.ConversationStore", store.hangupCall(new sharedActions.HangupCall()); sinon.assert.calledOnce(wsMediaFailSpy); }); it("should ensure the websocket is closed", function() { store.hangupCall(new sharedActions.HangupCall()); - sinon.assert.calledOnce(wsCloseSpy); + sinon.assert.calledOnce(wsHangupSpy); }); it("should set the callState to finished", function() { store.hangupCall(new sharedActions.HangupCall()); expect(store.getStoreState("callState")).eql(CALL_STATES.FINISHED); }); @@ -772,24 +772,24 @@ describe("loop.store.ConversationStore", sinon.assert.calledOnce(fakeMozLoop.calls.clearCallInProgress); sinon.assert.calledWithExactly( fakeMozLoop.calls.clearCallInProgress, "42"); }); }); describe("#remotePeerDisconnected", function() { - var wsMediaFailSpy, wsCloseSpy; + var wsMediaFailSpy, wsDisconnectSpy; beforeEach(function() { wsMediaFailSpy = sinon.spy(); - wsCloseSpy = sinon.spy(); + wsDisconnectSpy = sinon.spy(); store._websocket = { mediaFail: wsMediaFailSpy, - close: wsCloseSpy + close: wsDisconnectSpy }; store.setStoreState({callState: CALL_STATES.ONGOING}); store.setStoreState({windowId: "42"}); }); it("should disconnect the session", function() { store.remotePeerDisconnected(new sharedActions.RemotePeerDisconnected({ peerHungup: true @@ -798,17 +798,17 @@ describe("loop.store.ConversationStore", sinon.assert.calledOnce(sdkDriver.disconnectSession); }); it("should ensure the websocket is closed", function() { store.remotePeerDisconnected(new sharedActions.RemotePeerDisconnected({ peerHungup: true })); - sinon.assert.calledOnce(wsCloseSpy); + sinon.assert.calledOnce(wsDisconnectSpy); }); it("should release mozLoop callsData", function() { store.remotePeerDisconnected(new sharedActions.RemotePeerDisconnected({ peerHungup: true })); sinon.assert.calledOnce(fakeMozLoop.calls.clearCallInProgress); @@ -1050,48 +1050,48 @@ describe("loop.store.ConversationStore", })); sinon.assert.calledOnce(trigger); sinon.assert.calledWithExactly(trigger, "error:emailLink"); }); }); describe("#windowUnload", function() { - var fakeWebsocket; + var fakeWs; beforeEach(function() { - fakeWebsocket = store._websocket = { + fakeWs = store._websocket = { close: sinon.stub(), decline: sinon.stub() }; store.setStoreState({windowId: 42}); }); it("should decline the connection on the websocket for incoming calls if the state is alerting", function() { store.setStoreState({ callState: CALL_STATES.ALERTING, outgoing: false }); store.windowUnload(); - sinon.assert.calledOnce(fakeWebsocket.decline); + sinon.assert.calledOnce(fakeWs.decline); }); it("should disconnect the sdk session", function() { store.windowUnload(); sinon.assert.calledOnce(sdkDriver.disconnectSession); }); it("should close the websocket", function() { store.windowUnload(); - sinon.assert.calledOnce(fakeWebsocket.close); + sinon.assert.calledOnce(fakeWs.close); }); it("should clear the call in progress for the backend", function() { store.windowUnload(); sinon.assert.calledOnce(fakeMozLoop.calls.clearCallInProgress); sinon.assert.calledWithExactly(fakeMozLoop.calls.clearCallInProgress, 42); });
--- a/browser/components/loop/test/shared/feedbackStore_test.js +++ b/browser/components/loop/test/shared/feedbackStore_test.js @@ -30,21 +30,22 @@ describe("loop.store.FeedbackStore", fun describe("#constructor", function() { it("should throw an error if feedbackClient is missing", function() { expect(function() { new loop.store.FeedbackStore(dispatcher); }).to.Throw(/feedbackClient/); }); it("should set the store to the INIT feedback state", function() { - var store = new loop.store.FeedbackStore(dispatcher, { + var fakeStore = new loop.store.FeedbackStore(dispatcher, { feedbackClient: feedbackClient }); - expect(store.getStoreState("feedbackState")).eql(FEEDBACK_STATES.INIT); + expect(fakeStore.getStoreState("feedbackState")) + .eql(FEEDBACK_STATES.INIT); }); }); describe("#requireFeedbackDetails", function() { it("should transition to DETAILS state", function() { store.requireFeedbackDetails(new sharedActions.RequireFeedbackDetails()); expect(store.getStoreState("feedbackState")).eql(FEEDBACK_STATES.DETAILS);
--- a/browser/components/loop/test/shared/feedbackViews_test.js +++ b/browser/components/loop/test/shared/feedbackViews_test.js @@ -26,40 +26,40 @@ describe("loop.shared.views.FeedbackView React.createElement(sharedViews.FeedbackView)); }); afterEach(function() { sandbox.restore(); }); // local test helpers - function clickHappyFace(comp) { - var happyFace = comp.getDOMNode().querySelector(".face-happy"); + function clickHappyFace(component) { + var happyFace = component.getDOMNode().querySelector(".face-happy"); TestUtils.Simulate.click(happyFace); } - function clickSadFace(comp) { - var sadFace = comp.getDOMNode().querySelector(".face-sad"); + function clickSadFace(component) { + var sadFace = component.getDOMNode().querySelector(".face-sad"); TestUtils.Simulate.click(sadFace); } - function fillSadFeedbackForm(comp, category, text) { + function fillSadFeedbackForm(component, category, text) { TestUtils.Simulate.change( - comp.getDOMNode().querySelector("[value='" + category + "']")); + component.getDOMNode().querySelector("[value='" + category + "']")); if (text) { TestUtils.Simulate.change( - comp.getDOMNode().querySelector("[name='description']"), { + component.getDOMNode().querySelector("[name='description']"), { target: {value: "fake reason"} }); } } - function submitSadFeedbackForm(comp, category, text) { - TestUtils.Simulate.submit(comp.getDOMNode().querySelector("form")); + function submitSadFeedbackForm(component, category, text) { + TestUtils.Simulate.submit(component.getDOMNode().querySelector("form")); } describe("Happy feedback", function() { it("should dispatch a SendFeedback action", function() { var dispatch = sandbox.stub(dispatcher, "dispatch"); clickHappyFace(comp);
--- a/browser/components/loop/test/shared/mixins_test.js +++ b/browser/components/loop/test/shared/mixins_test.js @@ -466,17 +466,17 @@ describe("loop.shared.mixins", function( sinon.assert.calledWithExactly(navigator.mozLoop.getAudioBlob, "failure", sinon.match.func); sinon.assert.calledOnce(fakeAudio.play); expect(fakeAudio.loop).to.equal(false); }); }); describe("loop.shared.mixins.RoomsAudioMixin", function() { - var view, fakeAudioMixin, TestComp, comp; + var view, fakeAudioMixin, comp; function createTestComponent(initialState) { var TestComp = React.createClass({ mixins: [loop.shared.mixins.RoomsAudioMixin], render: function() { return React.DOM.div(); },
--- a/browser/components/loop/test/shared/otSdkDriver_test.js +++ b/browser/components/loop/test/shared/otSdkDriver_test.js @@ -754,57 +754,57 @@ describe("loop.OTSdkDriver", function () sinon.assert.calledWith(driver._noteConnectionLengthIfNeeded, startTime, endTime); }); }); describe("streamCreated (publisher/local)", function() { - var fakeStream, fakeMockVideo; + var stream, fakeMockVideo; beforeEach(function() { driver._mockPublisherEl = document.createElement("div"); fakeMockVideo = document.createElement("video"); driver._mockPublisherEl.appendChild(fakeMockVideo); - fakeStream = { + stream = { hasVideo: true, videoType: "camera", videoDimensions: {width: 1, height: 2} }; }); it("should dispatch a VideoDimensionsChanged action", function() { - publisher.trigger("streamCreated", { stream: fakeStream }); + publisher.trigger("streamCreated", { stream: stream }); sinon.assert.called(dispatcher.dispatch); sinon.assert.calledWithExactly(dispatcher.dispatch, new sharedActions.VideoDimensionsChanged({ isLocal: true, videoType: "camera", dimensions: {width: 1, height: 2} })); }); it("should dispatch a LocalVideoEnabled action", function() { - publisher.trigger("streamCreated", { stream: fakeStream }); + publisher.trigger("streamCreated", { stream: stream }); sinon.assert.called(dispatcher.dispatch); sinon.assert.calledWithExactly(dispatcher.dispatch, new sharedActions.LocalVideoEnabled({ srcVideoObject: fakeMockVideo })); }); it("should dispatch a ConnectionStatus action", function() { driver._metrics.recvStreams = 1; driver._metrics.connections = 2; - publisher.trigger("streamCreated", {stream: fakeStream}); + publisher.trigger("streamCreated", {stream: stream}); sinon.assert.called(dispatcher.dispatch); sinon.assert.calledWithExactly(dispatcher.dispatch, new sharedActions.ConnectionStatus({ event: "Publisher.streamCreated", state: "sendrecv", connections: 2, recvStreams: 1, @@ -1013,91 +1013,91 @@ describe("loop.OTSdkDriver", function () connections: 2, recvStreams: 1, sendStreams: 0 })); }); }); describe("streamDestroyed: session/remote", function() { - var fakeStream; + var stream; beforeEach(function() { - fakeStream = { + stream = { videoType: "screen" }; }); it("should dispatch a ReceivingScreenShare action", function() { - session.trigger("streamDestroyed", { stream: fakeStream }); + session.trigger("streamDestroyed", { stream: stream }); sinon.assert.called(dispatcher.dispatch); sinon.assert.calledWithExactly(dispatcher.dispatch, new sharedActions.ReceivingScreenShare({ receiving: false })); }); it("should dispatch a ConnectionStatus action", function() { driver._metrics.connections = 2; driver._metrics.sendStreams = 1; driver._metrics.recvStreams = 1; - session.trigger("streamDestroyed", {stream: fakeStream}); + session.trigger("streamDestroyed", {stream: stream}); sinon.assert.called(dispatcher.dispatch); sinon.assert.calledWithExactly(dispatcher.dispatch, new sharedActions.ConnectionStatus({ event: "Session.streamDestroyed", state: "sending", connections: 2, recvStreams: 0, sendStreams: 1 })); }); it("should not dispatch an action if the videoType is camera", function() { - fakeStream.videoType = "camera"; + stream.videoType = "camera"; - session.trigger("streamDestroyed", { stream: fakeStream }); + session.trigger("streamDestroyed", { stream: stream }); sinon.assert.neverCalledWithMatch(dispatcher.dispatch, sinon.match.hasOwn("name", "receivingScreenShare")); }); }); describe("streamPropertyChanged", function() { - var fakeStream = { + var stream = { connection: { id: "fake" }, videoType: "screen", videoDimensions: { width: 320, height: 160 } }; it("should not dispatch a VideoDimensionsChanged action for other properties", function() { session.trigger("streamPropertyChanged", { - stream: fakeStream, + stream: stream, changedProperty: STREAM_PROPERTIES.HAS_AUDIO }); session.trigger("streamPropertyChanged", { - stream: fakeStream, + stream: stream, changedProperty: STREAM_PROPERTIES.HAS_VIDEO }); sinon.assert.notCalled(dispatcher.dispatch); }); it("should dispatch a VideoDimensionsChanged action", function() { session.connection = { id: "localUser" }; session.trigger("streamPropertyChanged", { - stream: fakeStream, + stream: stream, changedProperty: STREAM_PROPERTIES.VIDEO_DIMENSIONS }); sinon.assert.calledOnce(dispatcher.dispatch); sinon.assert.calledWithMatch(dispatcher.dispatch, sinon.match.hasOwn("name", "videoDimensionsChanged")); }); });
--- a/browser/components/loop/test/shared/views_test.js +++ b/browser/components/loop/test/shared/views_test.js @@ -499,50 +499,50 @@ describe("loop.shared.views", function() comp.stopPublishing(); // Note: Backbone.Events#stopListening calls off() on passed object. sinon.assert.calledOnce(fakePublisher.off); }); }); describe("#publishStream", function() { - var comp; + var component; beforeEach(function() { - comp = mountTestComponent({ + component = mountTestComponent({ sdk: fakeSDK, model: model, video: {enabled: false} }); - comp.startPublishing(); + component.startPublishing(); }); it("should start streaming local audio", function() { - comp.publishStream("audio", true); + component.publishStream("audio", true); sinon.assert.calledOnce(fakePublisher.publishAudio); sinon.assert.calledWithExactly(fakePublisher.publishAudio, true); }); it("should stop streaming local audio", function() { - comp.publishStream("audio", false); + component.publishStream("audio", false); sinon.assert.calledOnce(fakePublisher.publishAudio); sinon.assert.calledWithExactly(fakePublisher.publishAudio, false); }); it("should start streaming local video", function() { - comp.publishStream("video", true); + component.publishStream("video", true); sinon.assert.calledOnce(fakePublisher.publishVideo); sinon.assert.calledWithExactly(fakePublisher.publishVideo, true); }); it("should stop streaming local video", function() { - comp.publishStream("video", false); + component.publishStream("video", false); sinon.assert.calledOnce(fakePublisher.publishVideo); sinon.assert.calledWithExactly(fakePublisher.publishVideo, false); }); }); describe("Model events", function() {
--- a/browser/components/loop/test/standalone/standaloneMozLoop_test.js +++ b/browser/components/loop/test/standalone/standaloneMozLoop_test.js @@ -147,65 +147,68 @@ describe("loop.StandaloneMozLoop", funct JSON.stringify(fakeServerErrorDescription)); sinon.assert.calledWithMatch(callback, sinon.match(function(err) { return /HTTP 401 Unauthorized/.test(err.message); })); }); }); describe("#rooms.refreshMembership", function() { - var mozLoop, fakeServerErrorDescription; + var standaloneMozLoop, fakeServerErrDescription; beforeEach(function() { - mozLoop = new loop.StandaloneMozLoop({ + standaloneMozLoop = new loop.StandaloneMozLoop({ baseServerUrl: fakeBaseServerUrl }); - fakeServerErrorDescription = { + fakeServerErrDescription = { code: 401, errno: 101, error: "error", message: "invalid token", info: "error info" }; }); it("should POST to the server", function() { - mozLoop.rooms.refreshMembership("fakeToken", "fakeSessionToken", callback); + standaloneMozLoop.rooms.refreshMembership("fakeToken", "fakeSessionToken", + callback); expect(requests).to.have.length.of(1); expect(requests[0].url).eql(fakeBaseServerUrl + "/rooms/fakeToken"); expect(requests[0].method).eql("POST"); expect(requests[0].requestHeaders.Authorization) .eql("Basic " + btoa("fakeSessionToken")); var requestData = JSON.parse(requests[0].requestBody); expect(requestData.action).eql("refresh"); expect(requestData.sessionToken).eql("fakeSessionToken"); }); it("should call the callback with success parameters", function() { - mozLoop.rooms.refreshMembership("fakeToken", "fakeSessionToken", callback); + standaloneMozLoop.rooms.refreshMembership("fakeToken", "fakeSessionToken", + callback); var responseData = { expires: 20 }; requests[0].respond(200, {"Content-Type": "application/json"}, JSON.stringify(responseData)); sinon.assert.calledOnce(callback); sinon.assert.calledWithExactly(callback, null, responseData); }); it("should call the callback with failure parameters", function() { - mozLoop.rooms.refreshMembership("fakeToken", "fakeSessionToken", callback); + standaloneMozLoop.rooms.refreshMembership("fakeToken", "fakeSessionToken", + callback); requests[0].respond(401, {"Content-Type": "application/json"}, - JSON.stringify(fakeServerErrorDescription)); + JSON.stringify(fakeServerErrDescription)); sinon.assert.calledWithMatch(callback, sinon.match(function(err) { return /HTTP 401 Unauthorized/.test(err.message); })); }); }); describe("#rooms.leave", function() { it("should POST to the server", function() {
--- a/browser/components/loop/test/standalone/standaloneRoomViews_test.js +++ b/browser/components/loop/test/standalone/standaloneRoomViews_test.js @@ -610,18 +610,18 @@ describe("loop.standaloneRoomViews", fun activeRoomStore.setStoreState({roomState: ROOM_STATES.FAILED}); expect(view.getDOMNode().querySelector(".btn-info")) .not.eql(null); }); }); describe("Join button", function() { - function getJoinButton(view) { - return view.getDOMNode().querySelector(".btn-join"); + function getJoinButton(elem) { + return elem.getDOMNode().querySelector(".btn-join"); } it("should render the Join button when room isn't active", function() { activeRoomStore.setStoreState({roomState: ROOM_STATES.READY}); expect(getJoinButton(view)).not.eql(null); }); @@ -733,18 +733,18 @@ describe("loop.standaloneRoomViews", fun mediaConnected: true }); expect(view.getDOMNode().querySelector(".remote .avatar")).not.eql(null); }); }); describe("Leave button", function() { - function getLeaveButton(view) { - return view.getDOMNode().querySelector(".btn-hangup"); + function getLeaveButton(elem) { + return elem.getDOMNode().querySelector(".btn-hangup"); } it("should disable the Leave button when the room state is READY", function() { activeRoomStore.setStoreState({roomState: ROOM_STATES.READY}); expect(getLeaveButton(view).disabled).eql(true); });
--- a/browser/components/loop/test/standalone/webapp_test.js +++ b/browser/components/loop/test/standalone/webapp_test.js @@ -581,42 +581,42 @@ describe("loop.webapp", function() { expect(ocView.state.callStatus).eql("failure"); }); }); }); describe("FailedConversationView", function() { - var view, conversation, client, fakeAudio; + var view, fakeConversation, fakeClient, fakeAudio; beforeEach(function() { sandbox.stub(window, "XMLHttpRequest").returns(fakeAudioXHR); fakeAudio = { play: sinon.spy(), pause: sinon.spy(), removeAttribute: sinon.spy() }; sandbox.stub(window, "Audio").returns(fakeAudio); - client = new loop.StandaloneClient({ + fakeClient = new loop.StandaloneClient({ baseServerUrl: "http://fake.example.com" }); - conversation = new sharedModels.ConversationModel({}, { + fakeConversation = new sharedModels.ConversationModel({}, { sdk: {} }); - conversation.set("loopToken", "fakeToken"); + fakeConversation.set("loopToken", "fakeToken"); - sandbox.stub(client, "requestCallUrlInfo"); + sandbox.stub(fakeClient, "requestCallUrlInfo"); view = React.addons.TestUtils.renderIntoDocument( React.createElement( loop.webapp.FailedConversationView, { - conversation: conversation, - client: client, + conversation: fakeConversation, + client: fakeClient, notifications: notifications })); }); it("should play a failure sound, once", function() { fakeAudioXHR.onload(); sinon.assert.called(fakeAudioXHR.open); @@ -1115,35 +1115,35 @@ describe("loop.webapp", function() { sandbox.stub(client, "requestCallInfo"); conversation = new sharedModels.ConversationModel({}, { sdk: {}, pendingCallTimeout: 1000 }); }); describe("Setup call", function() { - var conversation, setupOutgoingCall, view, requestCallUrlInfo; + var fakeConversation, setupOutgoingCall, view, requestCallUrlInfo; beforeEach(function() { - conversation = new loop.webapp.FxOSConversationModel({ + fakeConversation = new loop.webapp.FxOSConversationModel({ loopToken: "fakeToken" }); - setupOutgoingCall = sandbox.stub(conversation, "setupOutgoingCall"); + setupOutgoingCall = sandbox.stub(fakeConversation, "setupOutgoingCall"); var standaloneClientStub = { requestCallUrlInfo: function(token, cb) { cb(null, {urlCreationDate: 0}); }, settings: {baseServerUrl: loop.webapp.baseServerUrl} }; view = React.addons.TestUtils.renderIntoDocument( React.createElement( loop.webapp.StartConversationView, { - conversation: conversation, + conversation: fakeConversation, notifications: notifications, client: standaloneClientStub })); // default to succeeding with a null local media object stubGetPermsAndCacheMedia.callsArgWith(1, {}); });
--- a/browser/components/loop/test/xpcshell/test_looppush_initialize.js +++ b/browser/components/loop/test/xpcshell/test_looppush_initialize.js @@ -64,19 +64,19 @@ add_test(function test_register_twice_sa Assert.equal(mockWebSocket.origin, kServerPushUrl, "Should have the origin url from preferences"); Assert.equal(mockWebSocket.protocol, "push-notification", "Should have the protocol set to push-notifications"); // Register again for the same channel MozLoopPushHandler.register( "chan-2", - function(err, url, id) { - Assert.equal(err, null, "Should return null for success"); - Assert.equal(id, "chan-2", "Should have channel id = chan-2"); + function(error, newUrl, newId) { + Assert.equal(error, null, "Should return null for success"); + Assert.equal(newId, "chan-2", "Should have channel id = chan-2"); run_next_test(); }, dummyCallback ); }, dummyCallback ); });
--- a/browser/components/loop/ui/ui-showcase.jsx +++ b/browser/components/loop/ui/ui-showcase.jsx @@ -95,19 +95,19 @@ /** * Every view that uses an activeRoomStore needs its own; if they shared * an active store, they'd interfere with each other. * * @param options * @returns {loop.store.ActiveRoomStore} */ function makeActiveRoomStore(options) { - var dispatcher = new loop.Dispatcher(); + var roomDispatcher = new loop.Dispatcher(); - var store = new loop.store.ActiveRoomStore(dispatcher, { + var store = new loop.store.ActiveRoomStore(roomDispatcher, { mozLoop: navigator.mozLoop, sdkDriver: mockSDK }); if (!("remoteVideoEnabled" in options)) { options.remoteVideoEnabled = true; } @@ -990,17 +990,17 @@ } } catch(err) { console.error(err); uncaughtError = err; } // Wait until all the FramedExamples have been fully loaded. setTimeout(function waitForQueuedFrames() { - if (window.queuedFrames.length != 0) { + if (window.queuedFrames.length !== 0) { setTimeout(waitForQueuedFrames, 500); return; } // Put the title back, in case views changed it. document.title = "Loop UI Components Showcase"; // This simulates the mocha layout for errors which means we can run // this alongside our other unit tests but use the same harness.
--- a/browser/devtools/animationinspector/components.js +++ b/browser/devtools/animationinspector/components.js @@ -77,16 +77,26 @@ PlayerMetaDataHeader.prototype = { let metaData = createNode({ parent: this.el, nodeType: "span", attributes: { "class": "meta-data" } }); + // Animation is running on compositor + this.compositorIcon = createNode({ + parent: metaData, + nodeType: "span", + attributes: { + "class": "compositor-icon", + "title": L10N.getStr("player.runningOnCompositorTooltip") + } + }); + // Animation duration. this.durationLabel = createNode({ parent: metaData, nodeType: "span", textContent: L10N.getStr("player.animationDurationLabel") }); this.durationValue = createNode({ @@ -132,16 +142,17 @@ PlayerMetaDataHeader.prototype = { destroy: function() { this.state = null; this.el.remove(); this.el = null; this.nameLabel = this.nameValue = null; this.durationLabel = this.durationValue = null; this.delayLabel = this.delayValue = null; this.iterationLabel = this.iterationValue = null; + this.compositorIcon = null; }, render: function(state) { // Update the name if needed. if (state.name !== this.state.name) { if (state.name) { // Animations (and transitions since bug 1122414) have names. this.nameLabel.textContent = L10N.getStr("player.animationNameLabel"); @@ -184,16 +195,26 @@ PlayerMetaDataHeader.prototype = { this.iterationValue.innerHTML = count; } else { // Hide the iteration elements if iteration is 1. this.iterationLabel.style.display = "none"; this.iterationValue.style.display = "none"; } } + // Show the Running on compositor icon if needed. + if (state.isRunningOnCompositor !== this.state.isRunningOnCompositor) { + if (state.isRunningOnCompositor) { + this.compositorIcon.style.display = "inline"; + } else { + // Hide the compositor icon + this.compositorIcon.style.display = "none"; + } + } + this.state = state; } }; /** * UI component responsible for displaying the playback rate drop-down in each * player widget, updating it when the state changes, and emitting events when * the user selects a new value.
--- a/browser/devtools/animationinspector/test/browser.ini +++ b/browser/devtools/animationinspector/test/browser.ini @@ -9,16 +9,17 @@ support-files = [browser_animation_empty_on_invalid_nodes.js] [browser_animation_iterationCount_hidden_by_default.js] [browser_animation_panel_exists.js] [browser_animation_participate_in_inspector_update.js] [browser_animation_play_pause_button.js] [browser_animation_playerFronts_are_refreshed.js] [browser_animation_playerWidgets_appear_on_panel_init.js] +[browser_animation_playerWidgets_compositor_icon.js] [browser_animation_playerWidgets_destroy.js] [browser_animation_playerWidgets_disables_on_finished.js] [browser_animation_playerWidgets_dont_show_time_after_duration.js] [browser_animation_playerWidgets_have_control_buttons.js] [browser_animation_playerWidgets_meta_data.js] [browser_animation_playerWidgets_scrubber_delayed.js] [browser_animation_playerWidgets_scrubber_enabled.js] [browser_animation_playerWidgets_scrubber_moves.js]
new file mode 100644 --- /dev/null +++ b/browser/devtools/animationinspector/test/browser_animation_playerWidgets_compositor_icon.js @@ -0,0 +1,24 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that player widgets show the right player meta-data for the +// isRunningOnCompositor property. + +add_task(function*() { + yield addTab(TEST_URL_ROOT + "doc_simple_animation.html"); + let {inspector, panel} = yield openAnimationInspector(); + + info("Select the simple animated node"); + yield selectNode(".animated", inspector); + + let compositorEl = panel.playerWidgets[0] + .el.querySelector(".compositor-icon"); + + ok(compositorEl, "The compositor-icon element exists"); + ok(isNodeVisible(compositorEl), + "The compositor icon is visible, since the animation is running on " + + "compositor thread"); +});
--- a/browser/devtools/debugger/test/browser_dbg_WorkerActor.attachThread.js +++ b/browser/devtools/debugger/test/browser_dbg_WorkerActor.attachThread.js @@ -38,16 +38,27 @@ function test() { let [, threadClient2] = yield attachThread(workerClient2); let sources2 = yield getSources(threadClient2); let sourceClient2 = threadClient2.source(findSource(sources2, EXAMPLE_URL + WORKER_URL)); let [, breakpointClient2] = yield setBreakpoint(sourceClient2, location); yield resume(threadClient2); + let packet = yield source(sourceClient1); + let text = (yield new Promise(function (resolve) { + let request = new XMLHttpRequest(); + request.open("GET", EXAMPLE_URL + WORKER_URL, true); + request.send(); + request.onload = function () { + resolve(request.responseText); + }; + })); + is(packet.source, text); + postMessageToWorkerInTab(tab, WORKER_URL, "ping"); yield Promise.all([ waitForPause(threadClient1).then((packet) => { is(packet.type, "paused"); let why = packet.why; is(why.type, "breakpoint"); is(why.actors.length, 1); is(why.actors[0], breakpointClient1.actor);
--- a/browser/devtools/debugger/test/head.js +++ b/browser/devtools/debugger/test/head.js @@ -929,20 +929,23 @@ function attachAddonActorForUrl(aClient, }); }); return deferred.promise; } function rdpInvoke(aClient, aMethod, ...args) { return promiseInvoke(aClient, aMethod, ...args) - .then(({error, message }) => { + .then((packet) => { + let { error, message } = packet; if (error) { throw new Error(error + ": " + message); } + + return packet; }); } function doResume(aPanel) { const threadClient = aPanel.panelWin.gThreadClient; return rdpInvoke(threadClient, threadClient.resume); } @@ -1180,25 +1183,16 @@ function findSource(sources, url) { for (let source of sources) { if (source.url === url) { return source; } } return null; } -function setBreakpoint(sourceClient, location) { - info("Setting breakpoint.\n"); - return new Promise(function (resolve) { - sourceClient.setBreakpoint(location, function (response, breakpointClient) { - resolve([response, breakpointClient]); - }); - }); -} - function waitForEvent(client, type, predicate) { return new Promise(function (resolve) { function listener(type, packet) { if (!predicate(packet)) { return; } client.removeListener(listener); resolve(packet); @@ -1213,8 +1207,18 @@ function waitForEvent(client, type, pred } }); } function waitForPause(threadClient) { info("Waiting for pause.\n"); return waitForEvent(threadClient, "paused"); } + +function setBreakpoint(sourceClient, location) { + info("Setting breakpoint.\n"); + return rdpInvoke(sourceClient, sourceClient.setBreakpoint, location); +} + +function source(sourceClient) { + info("Getting source.\n"); + return rdpInvoke(sourceClient, sourceClient.source); +}
--- a/browser/devtools/framework/toolbox-hosts.js +++ b/browser/devtools/framework/toolbox-hosts.js @@ -101,17 +101,22 @@ BottomHost.prototype = { * means that the toolbox won't be visible at all once minimized. */ minimize: function(height=0) { if (this.isMinimized) { return; } this.isMinimized = true; - let onTransitionEnd = () => { + let onTransitionEnd = event => { + if (event.propertyName !== "margin-bottom") { + // Ignore transitionend on unrelated properties. + return; + } + this.frame.removeEventListener("transitionend", onTransitionEnd); this.emit("minimized"); }; this.frame.addEventListener("transitionend", onTransitionEnd); this.frame.style.marginBottom = -this.frame.height + height + "px"; this._splitter.classList.add("disabled"); }, @@ -120,17 +125,22 @@ BottomHost.prototype = { * maximized to the height it previously had). */ maximize: function() { if (!this.isMinimized) { return; } this.isMinimized = false; - let onTransitionEnd = () => { + let onTransitionEnd = event => { + if (event.propertyName !== "margin-bottom") { + // Ignore transitionend on unrelated properties. + return; + } + this.frame.removeEventListener("transitionend", onTransitionEnd); this.emit("maximized"); }; this.frame.addEventListener("transitionend", onTransitionEnd); this.frame.style.marginBottom = "0"; this._splitter.classList.remove("disabled"); },
--- a/browser/devtools/netmonitor/netmonitor-controller.js +++ b/browser/devtools/netmonitor/netmonitor-controller.js @@ -708,39 +708,17 @@ NetworkEventsHandler.prototype = { * The long string grip containing the corresponding actor. * If you pass in a plain string (by accident or because you're lazy), * then a promise of the same string is simply returned. * @return object Promise * A promise that is resolved when the full string contents * are available, or rejected if something goes wrong. */ getString: function(aStringGrip) { - // Make sure this is a long string. - if (typeof aStringGrip != "object" || aStringGrip.type != "longString") { - return promise.resolve(aStringGrip); // Go home string, you're drunk. - } - // Fetch the long string only once. - if (aStringGrip._fullText) { - return aStringGrip._fullText.promise; - } - - let deferred = aStringGrip._fullText = promise.defer(); - let { actor, initial, length } = aStringGrip; - let longStringClient = this.webConsoleClient.longString(aStringGrip); - - longStringClient.substring(initial.length, length, aResponse => { - if (aResponse.error) { - Cu.reportError(aResponse.error + ": " + aResponse.message); - deferred.reject(aResponse); - return; - } - deferred.resolve(initial + aResponse.substring); - }); - - return deferred.promise; + return this.webConsoleClient.getString(aStringGrip); } }; /** * Localization convenience methods. */ let L10N = new ViewHelpers.L10N(NET_STRINGS_URI); let PKI_L10N = new ViewHelpers.L10N(PKI_STRINGS_URI);
--- a/browser/locales/en-US/chrome/browser/devtools/animationinspector.properties +++ b/browser/locales/en-US/chrome/browser/devtools/animationinspector.properties @@ -48,13 +48,18 @@ player.infiniteIterationCount=∞ player.timeLabel=%Ss # LOCALIZATION NOTE (player.playbackRateLabel): # This string is displayed in each animation player widget, as the label of # drop-down list items that can be used to change the rate at which the # animation runs (1x being the default, 2x being twice as fast). player.playbackRateLabel=%Sx +# LOCALIZATION NOTE (player.runningOnCompositorTooltip): +# This string is displayed as a tooltip for the icon that indicates that the +# animation is running on the compositor thread. +player.runningOnCompositorTooltip=This animation is running on compositor thread + # LOCALIZATION NOTE (timeline.timeGraduationLabel): # This string is displayed at the top of the animation panel, next to each time # graduation, to indicate what duration (in milliseconds) this graduation # corresponds to. timeline.timeGraduationLabel=%Sms
--- a/browser/themes/linux/jar.mn +++ b/browser/themes/linux/jar.mn @@ -267,16 +267,17 @@ browser.jar: * skin/classic/browser/devtools/dark-theme.css (../shared/devtools/dark-theme.css) * skin/classic/browser/devtools/light-theme.css (../shared/devtools/light-theme.css) skin/classic/browser/devtools/add.svg (../shared/devtools/images/add.svg) skin/classic/browser/devtools/filters.svg (../shared/devtools/filters.svg) skin/classic/browser/devtools/filter-swatch.svg (../shared/devtools/images/filter-swatch.svg) skin/classic/browser/devtools/pseudo-class.svg (../shared/devtools/images/pseudo-class.svg) skin/classic/browser/devtools/controls.png (../shared/devtools/images/controls.png) skin/classic/browser/devtools/controls@2x.png (../shared/devtools/images/controls@2x.png) + skin/classic/browser/devtools/animation-fast-track.svg (../shared/devtools/images/animation-fast-track.svg) skin/classic/browser/devtools/performance-icons.svg (../shared/devtools/images/performance-icons.svg) skin/classic/browser/devtools/newtab.png (../shared/devtools/images/newtab.png) skin/classic/browser/devtools/newtab@2x.png (../shared/devtools/images/newtab@2x.png) skin/classic/browser/devtools/newtab-inverted.png (../shared/devtools/images/newtab-inverted.png) skin/classic/browser/devtools/newtab-inverted@2x.png (../shared/devtools/images/newtab-inverted@2x.png) * skin/classic/browser/devtools/widgets.css (devtools/widgets.css) skin/classic/browser/devtools/power.svg (../shared/devtools/images/power.svg) skin/classic/browser/devtools/filetype-dir-close.svg (../shared/devtools/images/filetypes/dir-close.svg)
--- a/browser/themes/osx/jar.mn +++ b/browser/themes/osx/jar.mn @@ -369,16 +369,17 @@ browser.jar: * skin/classic/browser/devtools/dark-theme.css (../shared/devtools/dark-theme.css) * skin/classic/browser/devtools/light-theme.css (../shared/devtools/light-theme.css) skin/classic/browser/devtools/add.svg (../shared/devtools/images/add.svg) skin/classic/browser/devtools/filters.svg (../shared/devtools/filters.svg) skin/classic/browser/devtools/filter-swatch.svg (../shared/devtools/images/filter-swatch.svg) skin/classic/browser/devtools/pseudo-class.svg (../shared/devtools/images/pseudo-class.svg) skin/classic/browser/devtools/controls.png (../shared/devtools/images/controls.png) skin/classic/browser/devtools/controls@2x.png (../shared/devtools/images/controls@2x.png) + skin/classic/browser/devtools/animation-fast-track.svg (../shared/devtools/images/animation-fast-track.svg) skin/classic/browser/devtools/performance-icons.svg (../shared/devtools/images/performance-icons.svg) skin/classic/browser/devtools/newtab.png (../shared/devtools/images/newtab.png) skin/classic/browser/devtools/newtab@2x.png (../shared/devtools/images/newtab@2x.png) skin/classic/browser/devtools/newtab-inverted.png (../shared/devtools/images/newtab-inverted.png) skin/classic/browser/devtools/newtab-inverted@2x.png (../shared/devtools/images/newtab-inverted@2x.png) * skin/classic/browser/devtools/widgets.css (devtools/widgets.css) skin/classic/browser/devtools/power.svg (../shared/devtools/images/power.svg) skin/classic/browser/devtools/filetype-dir-close.svg (../shared/devtools/images/filetypes/dir-close.svg)
--- a/browser/themes/shared/devtools/animationinspector.css +++ b/browser/themes/shared/devtools/animationinspector.css @@ -307,16 +307,27 @@ body { .animation-title .meta-data { float: right; } .animation-title strong { margin: 0 .5em; } +.animation-title .meta-data .compositor-icon { + display: none; + background-image: url("animation-fast-track.svg"); + background-repeat: no-repeat; + padding-left: 12px; + /* Make sure the icon is positioned above the timeline range input so that + its tooltip appears on hover */ + z-index: 1; + position: relative; +} + /* Timeline wiget */ .timeline { height: 20px; width: 100%; display: flex; flex-direction: row; border-bottom: 1px solid var(--theme-splitter-color);
new file mode 100644 --- /dev/null +++ b/browser/themes/shared/devtools/images/animation-fast-track.svg @@ -0,0 +1,9 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<svg xmlns="http://www.w3.org/2000/svg" width="9" height="12"> + <g transform="matrix(1.0251088,0,0,0.85613344,-3.1546734,-888.94343)"> + <path d="m 5.1284819,1038.3667 6.4950901,0 -2.7147491,4.6651 2.9438561,0 -8.1148915,9.3081 1.6126718,-6.8973 -2.2701022,0 z" style="fill:#4cb0e1;"/> + </g> +</svg>
--- a/browser/themes/windows/jar.mn +++ b/browser/themes/windows/jar.mn @@ -359,16 +359,17 @@ browser.jar: * skin/classic/browser/devtools/dark-theme.css (../shared/devtools/dark-theme.css) * skin/classic/browser/devtools/light-theme.css (../shared/devtools/light-theme.css) skin/classic/browser/devtools/add.svg (../shared/devtools/images/add.svg) skin/classic/browser/devtools/filters.svg (../shared/devtools/filters.svg) skin/classic/browser/devtools/filter-swatch.svg (../shared/devtools/images/filter-swatch.svg) skin/classic/browser/devtools/pseudo-class.svg (../shared/devtools/images/pseudo-class.svg) skin/classic/browser/devtools/controls.png (../shared/devtools/images/controls.png) skin/classic/browser/devtools/controls@2x.png (../shared/devtools/images/controls@2x.png) + skin/classic/browser/devtools/animation-fast-track.svg (../shared/devtools/images/animation-fast-track.svg) skin/classic/browser/devtools/performance-icons.svg (../shared/devtools/images/performance-icons.svg) skin/classic/browser/devtools/newtab.png (../shared/devtools/images/newtab.png) skin/classic/browser/devtools/newtab@2x.png (../shared/devtools/images/newtab@2x.png) skin/classic/browser/devtools/newtab-inverted.png (../shared/devtools/images/newtab-inverted.png) skin/classic/browser/devtools/newtab-inverted@2x.png (../shared/devtools/images/newtab-inverted@2x.png) * skin/classic/browser/devtools/widgets.css (devtools/widgets.css) skin/classic/browser/devtools/power.svg (../shared/devtools/images/power.svg) skin/classic/browser/devtools/filetype-dir-close.svg (../shared/devtools/images/filetypes/dir-close.svg)
deleted file mode 100644 index af190aa7a01c32d430bcffbf82dd774027e9c2e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@<O00001
deleted file mode 100644 index aa79b84abfd94f69a3b30e82d181978cae140860..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@<O00001
deleted file mode 100644 index ce7edea6361a093f502fceee7acb14bf9d43b3d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@<O00001
deleted file mode 100644 --- a/mobile/android/base/resources/drawable/menu_level.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<level-list xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:gecko="http://schemas.android.com/apk/res-auto"> - - <item android:maxLevel="1"> - - <selector> - - <item gecko:state_private="true" android:drawable="@drawable/menu_pb"/> - <item android:drawable="@drawable/menu"/> - - </selector> - - </item> - - <item android:maxLevel="2" android:drawable="@android:color/transparent"/> - -</level-list>
--- a/mobile/android/base/resources/layout/browser_toolbar.xml +++ b/mobile/android/base/resources/layout/browser_toolbar.xml @@ -43,17 +43,17 @@ android:background="@drawable/shaped_button" android:visibility="gone"/> <org.mozilla.gecko.widget.ThemedImageView android:id="@+id/menu_icon" style="@style/UrlBar.ImageButton" android:layout_alignLeft="@id/menu" android:layout_alignRight="@id/menu" android:gravity="center_vertical" - android:src="@drawable/menu_level" + android:src="@drawable/menu" android:visibility="gone"/> <org.mozilla.gecko.toolbar.PhoneTabsButton android:id="@+id/tabs" style="@style/UrlBar.ImageButton" android:layout_width="64dip" android:layout_toLeftOf="@id/menu" android:layout_alignWithParentIfMissing="true" android:background="@drawable/shaped_button"/>
--- a/mobile/android/base/resources/layout/find_in_page_content.xml +++ b/mobile/android/base/resources/layout/find_in_page_content.xml @@ -23,18 +23,17 @@ <TextView android:id="@+id/find_status" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="@dimen/find_in_page_status_margin_right" android:textColor="@color/tabs_tray_icon_grey" android:visibility="gone"/> - <CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/find_matchcase" + <CheckedTextView android:id="@+id/find_matchcase" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="@dimen/find_in_page_matchcase_padding" android:checked="false" android:text="@string/find_matchcase" android:textColor="@drawable/find_matchcase_selector"/> <ImageButton android:id="@+id/find_prev"
--- a/mobile/android/base/resources/layout/fxaccount_status_error_preference.xml +++ b/mobile/android/base/resources/layout/fxaccount_status_error_preference.xml @@ -19,17 +19,17 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:minWidth="48dip" android:padding="10dip" /> </LinearLayout> <RelativeLayout - android:layout_width="wrap_content" + android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginBottom="6dip" android:layout_marginLeft="15dip" android:layout_marginRight="6dip" android:layout_marginTop="6dip" android:layout_weight="1" > <TextView
--- a/mobile/android/base/resources/layout/gecko_app.xml +++ b/mobile/android/base/resources/layout/gecko_app.xml @@ -58,17 +58,18 @@ </FrameLayout> <LinearLayout android:id="@+id/doorhanger_overlay" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/dark_transparent_overlay" android:visibility="gone" android:alpha="0" - android:layerType="hardware"/> + android:layerType="hardware" + android:orientation="horizontal" /> </RelativeLayout> <org.mozilla.gecko.FindInPageBar android:id="@+id/find_in_page" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" style="@style/FindBar"
--- a/mobile/android/base/resources/layout/home_remote_tabs_group.xml +++ b/mobile/android/base/resources/layout/home_remote_tabs_group.xml @@ -19,17 +19,17 @@ android:layout_width="50dp" android:layout_height="50dp" android:layout_marginLeft="5dp" android:layout_gravity="center_vertical" android:scaleType="center" tools:src="@drawable/sync_mobile"/> <LinearLayout - android:layout_width="match_parent" + android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <org.mozilla.gecko.widget.FadedSingleColorTextView android:id="@+id/client" style="@style/Widget.TwoLinePageRow.Title" android:layout_width="match_parent"
--- a/mobile/android/base/resources/layout/keyguard_widget.xml +++ b/mobile/android/base/resources/layout/keyguard_widget.xml @@ -16,17 +16,16 @@ android:background="@drawable/widget_button_left" android:layout_height="match_parent" android:src="@drawable/widget_icon"/> <LinearLayout android:id="@+id/new_tab_button" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" - android:layout_centerVertical="true" android:contentDescription="@string/new_tab" android:gravity="center" android:orientation="horizontal" android:background="@drawable/widget_button_right"> <TextView android:id="@+id/new_tab_button_label" android:layout_width="wrap_content" android:layout_height="wrap_content"
--- a/mobile/android/base/resources/layout/menu_popup.xml +++ b/mobile/android/base/resources/layout/menu_popup.xml @@ -4,13 +4,14 @@ - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/menu_panel" android:layout_width="@dimen/menu_popup_width" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:minWidth="@dimen/menu_popup_width" - android:background="@drawable/menu_popup_bg"> + android:background="@drawable/menu_popup_bg" + android:orientation="horizontal"> <!-- MenuPanel will be added here dynamically --> </LinearLayout>
--- a/mobile/android/base/resources/layout/notification_icon_text.xml +++ b/mobile/android/base/resources/layout/notification_icon_text.xml @@ -15,17 +15,17 @@ <ImageView android:id="@+id/notification_image" android:layout_width="25dp" android:layout_height="25dp" android:scaleType="fitCenter"/> <TextView android:id="@+id/notification_title" android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent.Title" - android:layout_width="match_parent" + android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:singleLine="true" android:ellipsize="marquee" android:fadingEdge="horizontal" android:paddingLeft="4dp"/> </LinearLayout>
--- a/mobile/android/base/resources/layout/notification_progress.xml +++ b/mobile/android/base/resources/layout/notification_progress.xml @@ -16,17 +16,17 @@ <ImageView android:id="@+id/notification_image" android:layout_width="25dp" android:layout_height="25dp" android:scaleType="fitCenter"/> <TextView android:id="@+id/notification_title" android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent.Title" - android:layout_width="match_parent" + android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:singleLine="true" android:ellipsize="marquee" android:fadingEdge="horizontal" android:paddingLeft="10dp"/> </LinearLayout> @@ -41,13 +41,12 @@ android:paddingLeft="3dp"/> <ProgressBar android:id="@+id/notification_progressbar" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="1dip" android:layout_marginBottom="1dip" android:layout_marginLeft="4dip" - android:layout_marginRight="10dip" - android:layout_centerHorizontal="true"/> + android:layout_marginRight="10dip" /> </LinearLayout> </LinearLayout>
--- a/mobile/android/base/resources/layout/notification_progress_text.xml +++ b/mobile/android/base/resources/layout/notification_progress_text.xml @@ -15,35 +15,34 @@ <ImageView android:id="@+id/notification_image" android:layout_width="25dp" android:layout_height="25dp" android:scaleType="fitCenter"/> <TextView android:id="@+id/notification_title" android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent.Title" - android:layout_width="match_parent" + android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:singleLine="true" android:ellipsize="marquee" android:fadingEdge="horizontal" android:paddingLeft="4dp"/> </LinearLayout> <ProgressBar android:id="@+id/notification_progressbar" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="16dip" android:layout_marginTop="1dip" android:layout_marginBottom="1dip" android:layout_marginLeft="10dip" - android:layout_marginRight="10dip" - android:layout_centerHorizontal="true" /> + android:layout_marginRight="10dip" /> <TextView android:id="@+id/notification_text" android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingLeft="4dp"/> </LinearLayout>
--- a/mobile/android/base/resources/layout/preference_rightalign_icon.xml +++ b/mobile/android/base/resources/layout/preference_rightalign_icon.xml @@ -8,17 +8,17 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:paddingRight="?android:attr/scrollbarSize"> <RelativeLayout - android:layout_width="match_parent" + android:layout_width="0dp" android:layout_height="wrap_content" android:gravity="right" android:layout_marginLeft="15dip" android:layout_marginRight="6dip" android:layout_marginTop="6dip" android:layout_marginBottom="6dip" android:paddingRight="6dip" android:layout_weight="1">
--- a/mobile/android/base/resources/layout/preference_search_tip.xml +++ b/mobile/android/base/resources/layout/preference_search_tip.xml @@ -6,17 +6,17 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingRight="?android:attr/scrollbarSize"> <TextView android:id="@+id/label_search_hint" android:layout_height="wrap_content" - android:layout_width="wrap_content" + android:layout_width="0dp" android:text="@string/pref_search_hint" android:layout_marginTop="5dip" android:layout_marginBottom="6dip" android:layout_marginLeft="15dip" android:layout_marginRight="6dip" android:paddingTop="8dp" android:paddingBottom="8dp" android:paddingRight="6dip"
--- a/mobile/android/base/resources/layout/search_suggestions_row.xml +++ b/mobile/android/base/resources/layout/search_suggestions_row.xml @@ -8,17 +8,17 @@ android:layout_height="wrap_content" android:background="@drawable/search_row_background" android:padding="@dimen/search_row_padding" android:descendantFocusability="blocksDescendants" android:orientation="horizontal"> <TextView android:id="@+id/auto_complete_row_text" - android:layout_width="wrap_content" + android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:textSize="@dimen/query_text_size"/> <ImageButton android:id="@+id/auto_complete_row_jump_button" android:layout_width="wrap_content" android:layout_height="wrap_content"
--- a/mobile/android/base/resources/layout/search_widget.xml +++ b/mobile/android/base/resources/layout/search_widget.xml @@ -41,17 +41,16 @@ android:textColor="@color/toolbar_icon_grey"/> </LinearLayout> <LinearLayout android:id="@+id/new_tab_button" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" - android:layout_centerVertical="true" android:contentDescription="@string/new_tab" android:gravity="center" android:orientation="horizontal" android:background="@drawable/widget_button_right"> <TextView android:id="@+id/new_tab_button_label" android:layout_width="wrap_content" android:layout_height="wrap_content"
--- a/mobile/android/base/resources/layout/site_identity.xml +++ b/mobile/android/base/resources/layout/site_identity.xml @@ -14,17 +14,17 @@ android:padding="@dimen/doorhanger_padding"> <ImageView android:id="@+id/larry" android:layout_width="@dimen/doorhanger_icon_size" android:layout_height="@dimen/doorhanger_icon_size" android:src="@drawable/larry" android:paddingRight="@dimen/doorhanger_padding"/> - <LinearLayout android:layout_width="match_parent" + <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:orientation="vertical" android:layout_weight="1.0"> <include layout="@layout/site_identity_unknown" /> <LinearLayout android:id="@+id/site_identity_known_container" android:layout_width="match_parent"
--- a/mobile/android/base/resources/layout/tab_history_item_row.xml +++ b/mobile/android/base/resources/layout/tab_history_item_row.xml @@ -38,17 +38,16 @@ android:layout_gravity="center_horizontal" android:background="@color/tab_history_timeline_separator" /> </LinearLayout> <org.mozilla.gecko.widget.FadedSingleColorTextView android:id="@+id/tab_history_title" style="@style/Widget.TwoLinePageRow.Title" - android:layout_centerVertical="true" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:paddingRight="@dimen/tab_history_title_margin_right" android:text="@+id/tab_history_title" android:textSize="@dimen/tab_history_title_text_size" android:textColor="@color/text_and_tabs_tray_grey" gecko:fadeWidth="@dimen/tab_history_title_fading_width"/>
--- a/mobile/android/base/resources/layout/web_app.xml +++ b/mobile/android/base/resources/layout/web_app.xml @@ -18,17 +18,16 @@ android:layout_height="wrap_content" android:singleLine="true"/> </LinearLayout> <RelativeLayout android:id="@+id/gecko_layout" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_weight="1" android:layout_below="@+id/webapp_titlebar"> <include layout="@layout/shared_ui_components"/> <RelativeLayout android:id="@+id/splashscreen" android:layout_width="match_parent" android:layout_height="match_parent" >
--- a/mobile/android/base/resources/menu/preferences_search_menu.xml +++ b/mobile/android/base/resources/menu/preferences_search_menu.xml @@ -1,9 +1,10 @@ <?xml version="1.0" encoding="utf-8"?> <!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> <!-- Stub to preserve IDs. --> <menu xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:id="@+id/restore_defaults" /> + <item android:id="@+id/restore_defaults" + android:title="" /> </menu>
--- a/mobile/android/base/toolbar/BrowserToolbar.java +++ b/mobile/android/base/toolbar/BrowserToolbar.java @@ -831,17 +831,16 @@ public abstract class BrowserToolbar ext } @Override public void setPrivateMode(boolean isPrivate) { super.setPrivateMode(isPrivate); tabsButton.setPrivateMode(isPrivate); menuButton.setPrivateMode(isPrivate); - menuIcon.setPrivateMode(isPrivate); urlEditLayout.setPrivateMode(isPrivate); } public void show() { setVisibility(View.VISIBLE); } public void hide() {
--- a/toolkit/devtools/DevToolsUtils.js +++ b/toolkit/devtools/DevToolsUtils.js @@ -440,146 +440,154 @@ exports.defineLazyGetter(this, "NetUtil" * - charset: the charset to use if the channel doesn't provide one * @returns Promise * A promise of the document at that URL, as a string. * * XXX: It may be better to use nsITraceableChannel to get to the sources * without relying on caching when we can (not for eval, etc.): * http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/ */ -exports.fetch = function fetch(aURL, aOptions={ loadFromCache: true, - policy: Ci.nsIContentPolicy.TYPE_OTHER, - window: null, - charset: null }) { - let deferred = promise.defer(); - let scheme; - let url = aURL.split(" -> ").pop(); - let charset; - let contentType; - try { - scheme = Services.io.extractScheme(url); - } catch (e) { - // In the xpcshell tests, the script url is the absolute path of the test - // file, which will make a malformed URI error be thrown. Add the file - // scheme prefix ourselves. - url = "file://" + url; - scheme = Services.io.extractScheme(url); - } +// Fetch is defined differently depending on whether we are on the main thread +// or a worker thread. +if (!this.isWorker) { + exports.fetch = function (aURL, aOptions={ loadFromCache: true, + policy: Ci.nsIContentPolicy.TYPE_OTHER, + window: null, + charset: null }) { + let deferred = promise.defer(); + let scheme; + let url = aURL.split(" -> ").pop(); + let charset; + let contentType; + + try { + scheme = Services.io.extractScheme(url); + } catch (e) { + // In the xpcshell tests, the script url is the absolute path of the test + // file, which will make a malformed URI error be thrown. Add the file + // scheme prefix ourselves. + url = "file://" + url; + scheme = Services.io.extractScheme(url); + } - switch (scheme) { - case "file": - case "chrome": - case "resource": - try { - NetUtil.asyncFetch({ - uri: url, - loadUsingSystemPrincipal: true - }, function onFetch(aStream, aStatus, aRequest) { - if (!components.isSuccessCode(aStatus)) { - deferred.reject(new Error("Request failed with status code = " - + aStatus - + " after NetUtil.asyncFetch for url = " - + url)); - return; - } + switch (scheme) { + case "file": + case "chrome": + case "resource": + try { + NetUtil.asyncFetch({ + uri: url, + loadUsingSystemPrincipal: true + }, function onFetch(aStream, aStatus, aRequest) { + if (!components.isSuccessCode(aStatus)) { + deferred.reject(new Error("Request failed with status code = " + + aStatus + + " after NetUtil.asyncFetch for url = " + + url)); + return; + } - let source = NetUtil.readInputStreamToString(aStream, aStream.available()); - contentType = aRequest.contentType; - deferred.resolve(source); - aStream.close(); - }); - } catch (ex) { - deferred.reject(ex); - } - break; + let source = NetUtil.readInputStreamToString(aStream, aStream.available()); + contentType = aRequest.contentType; + deferred.resolve(source); + aStream.close(); + }); + } catch (ex) { + deferred.reject(ex); + } + break; - default: - let channel; - try { - channel = Services.io.newChannel2(url, - null, - null, - null, // aLoadingNode - Services.scriptSecurityManager.getSystemPrincipal(), - null, // aTriggeringPrincipal - Ci.nsILoadInfo.SEC_NORMAL, - aOptions.policy); - } catch (e) { - if (e.name == "NS_ERROR_UNKNOWN_PROTOCOL") { + default: + let channel; + try { + channel = Services.io.newChannel2(url, + null, + null, + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + aOptions.policy); + } catch (e if e.name == "NS_ERROR_UNKNOWN_PROTOCOL") { // On Windows xpcshell tests, c:/foo/bar can pass as a valid URL, but // newChannel won't be able to handle it. url = "file:///" + url; channel = Services.io.newChannel2(url, null, null, null, // aLoadingNode Services.scriptSecurityManager.getSystemPrincipal(), null, // aTriggeringPrincipal Ci.nsILoadInfo.SEC_NORMAL, aOptions.policy); - } else { - throw e; - } - } - let chunks = []; - let streamListener = { - onStartRequest: function(aRequest, aContext, aStatusCode) { - if (!components.isSuccessCode(aStatusCode)) { - deferred.reject(new Error("Request failed with status code = " - + aStatusCode - + " in onStartRequest handler for url = " - + url)); - } - }, - onDataAvailable: function(aRequest, aContext, aStream, aOffset, aCount) { - chunks.push(NetUtil.readInputStreamToString(aStream, aCount)); - }, - onStopRequest: function(aRequest, aContext, aStatusCode) { - if (!components.isSuccessCode(aStatusCode)) { - deferred.reject(new Error("Request failed with status code = " - + aStatusCode - + " in onStopRequest handler for url = " - + url)); - return; - } - - charset = channel.contentCharset || aOptions.charset; - contentType = channel.contentType; - deferred.resolve(chunks.join("")); } - }; + let chunks = []; + let streamListener = { + onStartRequest: function(aRequest, aContext, aStatusCode) { + if (!components.isSuccessCode(aStatusCode)) { + deferred.reject(new Error("Request failed with status code = " + + aStatusCode + + " in onStartRequest handler for url = " + + url)); + } + }, + onDataAvailable: function(aRequest, aContext, aStream, aOffset, aCount) { + chunks.push(NetUtil.readInputStreamToString(aStream, aCount)); + }, + onStopRequest: function(aRequest, aContext, aStatusCode) { + if (!components.isSuccessCode(aStatusCode)) { + deferred.reject(new Error("Request failed with status code = " + + aStatusCode + + " in onStopRequest handler for url = " + + url)); + return; + } + + charset = channel.contentCharset || aOptions.charset; + contentType = channel.contentType; + deferred.resolve(chunks.join("")); + } + }; - if (aOptions.window) { - // Respect private browsing. - channel.loadGroup = aOptions.window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocumentLoader) - .loadGroup; - } - channel.loadFlags = aOptions.loadFromCache - ? channel.LOAD_FROM_CACHE - : channel.LOAD_BYPASS_CACHE; - try { - channel.asyncOpen(streamListener, null); - } catch(e) { - deferred.reject(new Error("Request failed for '" - + url - + "': " - + e.message)); - } - break; + if (aOptions.window) { + // Respect private browsing. + channel.loadGroup = aOptions.window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocumentLoader) + .loadGroup; + } + channel.loadFlags = aOptions.loadFromCache + ? channel.LOAD_FROM_CACHE + : channel.LOAD_BYPASS_CACHE; + try { + channel.asyncOpen(streamListener, null); + } catch(e) { + deferred.reject(new Error("Request failed for '" + + url + + "': " + + e.message)); + } + break; + } + + return deferred.promise.then(source => { + return { + content: convertToUnicode(source, charset), + contentType: contentType + }; + }); } - - return deferred.promise.then(source => { - return { - content: convertToUnicode(source, charset), - contentType: contentType - }; - }); +} else { + // Services is not available in worker threads, nor is there any other way + // to fetch a URL. We need to enlist the help from the main thread here, by + // issuing an rpc request, to fetch the URL on our behalf. + exports.fetch = function (url, options) { + return rpc("fetch", url, options); + } } /** * Convert a given string, encoded in a given character set, to unicode. * * @param string aString * A string. * @param string aCharset
--- a/toolkit/devtools/server/main.js +++ b/toolkit/devtools/server/main.js @@ -15,16 +15,17 @@ let Services = require("Services"); let { ActorPool, OriginalLocation, RegisteredActorFactory, ObservedActorFactory } = require("devtools/server/actors/common"); let { LocalDebuggerTransport, ChildDebuggerTransport, WorkerDebuggerTransport } = require("devtools/toolkit/transport/transport"); let DevToolsUtils = require("devtools/toolkit/DevToolsUtils"); let { dumpn, dumpv, dbg_assert } = DevToolsUtils; let EventEmitter = require("devtools/toolkit/event-emitter"); let Debugger = require("Debugger"); +let Promise = require("promise"); DevToolsUtils.defineLazyGetter(this, "DebuggerSocket", () => { let { DebuggerSocket } = require("devtools/toolkit/security/socket"); return DebuggerSocket; }); DevToolsUtils.defineLazyGetter(this, "Authentication", () => { return require("devtools/toolkit/security/auth"); }); @@ -755,18 +756,63 @@ var DebuggerServer = { events.on(aConnection, "closed", onClose); return deferred.promise; }, connectToWorker: function (aConnection, aDbg, aId, aOptions) { return new Promise((resolve, reject) => { - // Step 1: Initialize the worker debugger. - aDbg.initialize("resource://gre/modules/devtools/server/worker.js"); + // Step 1: Ensure the worker debugger is initialized. + if (!aDbg.isInitialized) { + aDbg.initialize("resource://gre/modules/devtools/server/worker.js"); + + // Create a listener for rpc requests from the worker debugger. Only do + // this once, when the worker debugger is first initialized, rather than + // for each connection. + let listener = { + onClose: () => { + aDbg.removeListener(listener); + }, + + onMessage: (message) => { + let packet = JSON.parse(message); + if (packet.type !== "rpc") { + return; + } + + Promise.resolve().then(() => { + let method = { + "fetch": DevToolsUtils.fetch, + }[packet.method]; + if (!method) { + throw Error("Unknown method: " + packet.method); + } + + return method.apply(undefined, packet.params); + }).then((value) => { + aDbg.postMessage(JSON.stringify({ + type: "rpc", + result: value, + error: null, + id: packet.id + })); + }, (reason) => { + aDbg.postMessage(JSON.stringify({ + type: "rpc", + result: null, + error: reason, + id: packet.id + })); + }); + } + }; + + aDbg.addListener(listener); + } // Step 2: Send a connect request to the worker debugger. aDbg.postMessage(JSON.stringify({ type: "connect", id: aId, options: aOptions }));
--- a/toolkit/devtools/server/worker.js +++ b/toolkit/devtools/server/worker.js @@ -1,32 +1,57 @@ "use strict" +// This function is used to do remote procedure calls from the worker to the +// main thread. It is exposed as a built-in global to every module by the +// worker loader. To make sure the worker loader can access it, it needs to be +// defined before loading the worker loader script below. +this.rpc = function (method, ...params) { + let id = nextId++; + + postMessage(JSON.stringify({ + type: "rpc", + method: method, + params: params, + id: id + })); + + let deferred = Promise.defer(); + rpcDeferreds[id] = deferred; + return deferred.promise; +}; + loadSubScript("resource://gre/modules/devtools/worker-loader.js"); +let Promise = worker.require("promise"); let { ActorPool } = worker.require("devtools/server/actors/common"); let { ThreadActor } = worker.require("devtools/server/actors/script"); let { TabSources } = worker.require("devtools/server/actors/utils/TabSources"); let makeDebugger = worker.require("devtools/server/actors/utils/make-debugger"); let { DebuggerServer } = worker.require("devtools/server/main"); DebuggerServer.init(); DebuggerServer.createRootActor = function () { throw new Error("Should never get here!"); }; let connections = Object.create(null); +let nextId = 0; +let rpcDeferreds = []; this.addEventListener("message", function (event) { let packet = JSON.parse(event.data); switch (packet.type) { case "connect": // Step 3: Create a connection to the parent. let connection = DebuggerServer.connectToParent(packet.id, this); - connections[packet.id] = connection; + connections[packet.id] = { + connection : connection, + rpcs: [] + }; // Step 4: Create a thread actor for the connection to the parent. let pool = new ActorPool(connection); connection.addActorPool(pool); let sources = null; let actor = new ThreadActor({ @@ -55,12 +80,21 @@ this.addEventListener("message", functi // This will cause a packet to be sent over the connection to the parent. // Because this connection uses WorkerDebuggerTransport internally, this // packet will be sent using WorkerDebuggerGlobalScope.postMessage, causing // an onMessage event to be fired on the WorkerDebugger in the main thread. actor.onAttach({}); break; case "disconnect": - connections[packet.id].close(); + connections[packet.id].connection.close(); + break; + + case "rpc": + let deferred = rpcDeferreds[packet.id]; + delete rpcDeferreds[packet.id]; + if (packet.error) { + deferred.reject(packet.error); + } + deferred.resolve(packet.result); break; }; });
--- a/toolkit/devtools/webconsole/client.js +++ b/toolkit/devtools/webconsole/client.js @@ -4,16 +4,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; const {Cc, Ci, Cu} = require("chrome"); const DevToolsUtils = require("devtools/toolkit/DevToolsUtils"); const EventEmitter = require("devtools/toolkit/event-emitter"); +const promise = require("promise"); loader.lazyImporter(this, "LongStringClient", "resource://gre/modules/devtools/dbg-client.jsm"); /** * A WebConsoleClient is used as a front end for the WebConsoleActor that is * created on the server, hiding implementation details. * * @param object aDebuggerClient @@ -609,10 +610,50 @@ WebConsoleClient.prototype = { this.pendingEvaluationResults.clear(); this.pendingEvaluationResults = null; this.clearNetworkRequests(); this._networkRequests = null; }, clearNetworkRequests: function () { this._networkRequests.clear(); + }, + + /** + * Fetches the full text of a LongString. + * + * @param object | string stringGrip + * The long string grip containing the corresponding actor. + * If you pass in a plain string (by accident or because you're lazy), + * then a promise of the same string is simply returned. + * @return object Promise + * A promise that is resolved when the full string contents + * are available, or rejected if something goes wrong. + */ + getString: function(stringGrip) { + // Make sure this is a long string. + if (typeof stringGrip != "object" || stringGrip.type != "longString") { + return promise.resolve(stringGrip); // Go home string, you're drunk. + } + + // Fetch the long string only once. + if (stringGrip._fullText) { + return stringGrip._fullText.promise; + } + + let deferred = stringGrip._fullText = promise.defer(); + let { actor, initial, length } = stringGrip; + let longStringClient = this.longString(stringGrip); + + longStringClient.substring(initial.length, length, aResponse => { + if (aResponse.error) { + DevToolsUtils.reportException("getString", + aResponse.error + ": " + aResponse.message); + + deferred.reject(aResponse); + return; + } + deferred.resolve(initial + aResponse.substring); + }); + + return deferred.promise; } };
--- a/toolkit/devtools/worker-loader.js +++ b/toolkit/devtools/worker-loader.js @@ -363,16 +363,17 @@ let loader = { // main thread or a worker thread. On the main thread, we use the Components // object to implement them. On worker threads, we use the APIs provided by // the worker debugger. let { Debugger, createSandbox, dump, + rpc, loadSubScript, reportError, setImmediate, xpcInspector } = (function () { if (typeof Components === "object") { // Main thread let { Constructor: CC, @@ -400,16 +401,18 @@ let { invisibleToDebugger: true, sandboxName: name, sandboxPrototype: prototype, wantComponents: false, wantXrays: false }); }; + let rpc = undefined; + let subScriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1']. getService(Ci.mozIJSSubScriptLoader); let loadSubScript = function (url, sandbox) { subScriptLoader.loadSubScript(url, sandbox, "UTF-8"); }; let reportError = Cu.reportError; @@ -422,16 +425,17 @@ let { let xpcInspector = Cc["@mozilla.org/jsinspector;1"]. getService(Ci.nsIJSInspector); return { Debugger, createSandbox, dump, + rpc, loadSubScript, reportError, setImmediate, xpcInspector }; } else { // Worker thread let requestors = []; @@ -454,16 +458,17 @@ let { return requestors.length; } }; return { Debugger: this.Debugger, createSandbox: this.createSandbox, dump: this.dump, + rpc: this.rpc, loadSubScript: this.loadSubScript, reportError: this.reportError, setImmediate: this.setImmediate, xpcInspector: xpcInspector }; } }).call(this); @@ -472,16 +477,17 @@ let { this.worker = new WorkerDebuggerLoader({ createSandbox: createSandbox, globals: { "isWorker": true, "dump": dump, "loader": loader, "reportError": reportError, + "rpc": rpc, "setImmediate": setImmediate }, loadSubScript: loadSubScript, modules: { "Debugger": Debugger, "PromiseDebugging": PromiseDebugging, "Services": Object.create(null), "chrome": chrome,