diff --git a/browser/components/loop/content/js/conversation.js b/browser/components/loop/content/js/conversation.js index baa089052a82..fe0d3d3c9bf4 100644 --- a/browser/components/loop/content/js/conversation.js +++ b/browser/components/loop/content/js/conversation.js @@ -656,9 +656,9 @@ loop.conversation = (function(mozL10n) { dispatcher: dispatcher, mozLoop: navigator.mozLoop }); - var conversationStore = new loop.store.ConversationStore({}, { + var conversationStore = new loop.store.ConversationStore(dispatcher, { client: client, - dispatcher: dispatcher, + mozLoop: navigator.mozLoop, sdkDriver: sdkDriver }); var activeRoomStore = new loop.store.ActiveRoomStore(dispatcher, { diff --git a/browser/components/loop/content/js/conversation.jsx b/browser/components/loop/content/js/conversation.jsx index c77966e31b83..5ce05242f0ed 100644 --- a/browser/components/loop/content/js/conversation.jsx +++ b/browser/components/loop/content/js/conversation.jsx @@ -656,9 +656,9 @@ loop.conversation = (function(mozL10n) { dispatcher: dispatcher, mozLoop: navigator.mozLoop }); - var conversationStore = new loop.store.ConversationStore({}, { + var conversationStore = new loop.store.ConversationStore(dispatcher, { client: client, - dispatcher: dispatcher, + mozLoop: navigator.mozLoop, sdkDriver: sdkDriver }); var activeRoomStore = new loop.store.ActiveRoomStore(dispatcher, { diff --git a/browser/components/loop/content/js/conversationViews.js b/browser/components/loop/content/js/conversationViews.js index 6aa4603e81de..b0bae810e0a2 100644 --- a/browser/components/loop/content/js/conversationViews.js +++ b/browser/components/loop/content/js/conversationViews.js @@ -224,7 +224,7 @@ loop.conversationViews = (function(mozL10n) { }, _onEmailLinkReceived: function() { - var emailLink = this.props.store.get("emailLink"); + var emailLink = this.props.store.getStoreState("emailLink"); var contactEmail = _getPreferredEmail(this.props.contact).value; sharedUtils.composeCallUrlEmail(emailLink, contactEmail); window.close(); @@ -428,7 +428,10 @@ loop.conversationViews = (function(mozL10n) { * the different views that need displaying. */ var OutgoingConversationView = React.createClass({displayName: 'OutgoingConversationView', - mixins: [sharedMixins.AudioMixin], + mixins: [ + sharedMixins.AudioMixin, + Backbone.Events + ], propTypes: { dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired, @@ -438,12 +441,18 @@ loop.conversationViews = (function(mozL10n) { }, getInitialState: function() { - return this.props.store.attributes; + return this.props.store.getStoreState(); }, componentWillMount: function() { - this.props.store.on("change", function() { - this.setState(this.props.store.attributes); + this.listenTo(this.props.store, "change", function() { + this.setState(this.props.store.getStoreState()); + }, this); + }, + + componentWillUnmount: function() { + this.stopListening(this.props.store, "change", function() { + this.setState(this.props.store.getStoreState()); }, this); }, diff --git a/browser/components/loop/content/js/conversationViews.jsx b/browser/components/loop/content/js/conversationViews.jsx index 5de7c233f617..b1b552f74e4a 100644 --- a/browser/components/loop/content/js/conversationViews.jsx +++ b/browser/components/loop/content/js/conversationViews.jsx @@ -224,7 +224,7 @@ loop.conversationViews = (function(mozL10n) { }, _onEmailLinkReceived: function() { - var emailLink = this.props.store.get("emailLink"); + var emailLink = this.props.store.getStoreState("emailLink"); var contactEmail = _getPreferredEmail(this.props.contact).value; sharedUtils.composeCallUrlEmail(emailLink, contactEmail); window.close(); @@ -428,7 +428,10 @@ loop.conversationViews = (function(mozL10n) { * the different views that need displaying. */ var OutgoingConversationView = React.createClass({ - mixins: [sharedMixins.AudioMixin], + mixins: [ + sharedMixins.AudioMixin, + Backbone.Events + ], propTypes: { dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired, @@ -438,12 +441,18 @@ loop.conversationViews = (function(mozL10n) { }, getInitialState: function() { - return this.props.store.attributes; + return this.props.store.getStoreState(); }, componentWillMount: function() { - this.props.store.on("change", function() { - this.setState(this.props.store.attributes); + this.listenTo(this.props.store, "change", function() { + this.setState(this.props.store.getStoreState()); + }, this); + }, + + componentWillUnmount: function() { + this.stopListening(this.props.store, "change", function() { + this.setState(this.props.store.getStoreState()); }, this); }, diff --git a/browser/components/loop/content/js/roomViews.js b/browser/components/loop/content/js/roomViews.js index d2dfa98a6e0c..65cf5f137237 100644 --- a/browser/components/loop/content/js/roomViews.js +++ b/browser/components/loop/content/js/roomViews.js @@ -162,15 +162,20 @@ loop.roomViews = (function(mozL10n) { */ window.addEventListener('orientationchange', this.updateVideoContainer); window.addEventListener('resize', this.updateVideoContainer); + }, + componentWillUpdate: function(nextProps, nextState) { // The SDK needs to know about the configuration and the elements to use // for display. So the best way seems to pass the information here - ideally // the sdk wouldn't need to know this, but we can't change that. - this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({ - publisherConfig: this._getPublisherConfig(), - getLocalElementFunc: this._getElement.bind(this, ".local"), - getRemoteElementFunc: this._getElement.bind(this, ".remote") - })); + if (this.state.roomState !== ROOM_STATES.MEDIA_WAIT && + nextState.roomState === ROOM_STATES.MEDIA_WAIT) { + this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({ + publisherConfig: this._getPublisherConfig(), + getLocalElementFunc: this._getElement.bind(this, ".local"), + getRemoteElementFunc: this._getElement.bind(this, ".remote") + })); + } }, _getPublisherConfig: function() { diff --git a/browser/components/loop/content/js/roomViews.jsx b/browser/components/loop/content/js/roomViews.jsx index 9aa6e08e2420..f306a408459b 100644 --- a/browser/components/loop/content/js/roomViews.jsx +++ b/browser/components/loop/content/js/roomViews.jsx @@ -162,15 +162,20 @@ loop.roomViews = (function(mozL10n) { */ window.addEventListener('orientationchange', this.updateVideoContainer); window.addEventListener('resize', this.updateVideoContainer); + }, + componentWillUpdate: function(nextProps, nextState) { // The SDK needs to know about the configuration and the elements to use // for display. So the best way seems to pass the information here - ideally // the sdk wouldn't need to know this, but we can't change that. - this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({ - publisherConfig: this._getPublisherConfig(), - getLocalElementFunc: this._getElement.bind(this, ".local"), - getRemoteElementFunc: this._getElement.bind(this, ".remote") - })); + if (this.state.roomState !== ROOM_STATES.MEDIA_WAIT && + nextState.roomState === ROOM_STATES.MEDIA_WAIT) { + this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({ + publisherConfig: this._getPublisherConfig(), + getLocalElementFunc: this._getElement.bind(this, ".local"), + getRemoteElementFunc: this._getElement.bind(this, ".remote") + })); + } }, _getPublisherConfig: function() { diff --git a/browser/components/loop/content/shared/js/conversationStore.js b/browser/components/loop/content/shared/js/conversationStore.js index f07b3ae3a6e6..c5eb044e97d4 100644 --- a/browser/components/loop/content/shared/js/conversationStore.js +++ b/browser/components/loop/content/shared/js/conversationStore.js @@ -7,7 +7,7 @@ var loop = loop || {}; loop.store = loop.store || {}; -loop.store.ConversationStore = (function() { +(function() { var sharedActions = loop.shared.actions; var CALL_TYPES = loop.shared.utils.CALL_TYPES; @@ -53,81 +53,84 @@ loop.store.ConversationStore = (function() { TERMINATED: "cs-terminated" }; - // XXX this needs to migrate to use loop.store.createStore - var ConversationStore = Backbone.Model.extend({ - defaults: { - // The id of the window. Currently used for getting the window id. - windowId: undefined, - // The current state of the call - callState: CALL_STATES.INIT, - // The reason if a call was terminated - callStateReason: undefined, - // The error information, if there was a failure - error: undefined, - // True if the call is outgoing, false if not, undefined if unknown - outgoing: undefined, - // The contact being called for outgoing calls - contact: undefined, - // The call type for the call. - // XXX Don't hard-code, this comes from the data in bug 1072323 - callType: CALL_TYPES.AUDIO_VIDEO, + /** + * Conversation store. + * + * @param {loop.Dispatcher} dispatcher The dispatcher for dispatching actions + * and registering to consume actions. + * @param {Object} options Options object: + * - {client} client The client object. + * - {mozLoop} mozLoop The MozLoop API object. + * - {loop.OTSdkDriver} loop.OTSdkDriver The SDK Driver + */ + loop.store.ConversationStore = loop.store.createStore({ + // Further actions are registered in setupWindowData when + // we know what window type this is. + actions: [ + "setupWindowData" + ], - // Call Connection information - // The call id from the loop-server - callId: undefined, - // The caller id of the contacting side - callerId: undefined, - // The connection progress url to connect the websocket - progressURL: undefined, - // The websocket token that allows connection to the progress url - websocketToken: undefined, - // SDK API key - apiKey: undefined, - // SDK session ID - sessionId: undefined, - // SDK session token - sessionToken: undefined, - // If the audio is muted - audioMuted: false, - // If the video is muted - videoMuted: false + getInitialStoreState: function() { + return { + // The id of the window. Currently used for getting the window id. + windowId: undefined, + // The current state of the call + callState: CALL_STATES.INIT, + // The reason if a call was terminated + callStateReason: undefined, + // True if the call is outgoing, false if not, undefined if unknown + outgoing: undefined, + // The contact being called for outgoing calls + contact: undefined, + // The call type for the call. + // XXX Don't hard-code, this comes from the data in bug 1072323 + callType: CALL_TYPES.AUDIO_VIDEO, + // A link for emailing once obtained from the server + emailLink: undefined, + + // Call Connection information + // The call id from the loop-server + callId: undefined, + // The caller id of the contacting side + callerId: undefined, + // The connection progress url to connect the websocket + progressURL: undefined, + // The websocket token that allows connection to the progress url + websocketToken: undefined, + // SDK API key + apiKey: undefined, + // SDK session ID + sessionId: undefined, + // SDK session token + sessionToken: undefined, + // If the audio is muted + audioMuted: false, + // If the video is muted + videoMuted: false + }; }, /** - * Constructor + * Handles initialisation of the store. * - * Options: - * - {loop.Dispatcher} dispatcher The dispatcher for dispatching actions and - * registering to consume actions. - * - {Object} client A client object for communicating with the server. - * - * @param {Object} attributes Attributes object. * @param {Object} options Options object. */ - initialize: function(attributes, options) { + initialize: function(options) { options = options || {}; - if (!options.dispatcher) { - throw new Error("Missing option dispatcher"); - } if (!options.client) { throw new Error("Missing option client"); } if (!options.sdkDriver) { throw new Error("Missing option sdkDriver"); } + if (!options.mozLoop) { + throw new Error("Missing option mozLoop"); + } this.client = options.client; - this.dispatcher = options.dispatcher; this.sdkDriver = options.sdkDriver; - - // XXX Further actions are registered in setupWindowData when - // we know what window type this is. At some stage, we might want to - // consider store mixins or some alternative which means the stores - // would only be created when we want them. - this.dispatcher.register(this, [ - "setupWindowData" - ]); + this.mozLoop = options.mozLoop; }, /** @@ -138,7 +141,7 @@ loop.store.ConversationStore = (function() { */ connectionFailure: function(actionData) { this._endSession(); - this.set({ + this.setStoreState({ callState: CALL_STATES.TERMINATED, callStateReason: actionData.reason }); @@ -151,34 +154,35 @@ loop.store.ConversationStore = (function() { * @param {sharedActions.ConnectionProgress} actionData The action data. */ connectionProgress: function(actionData) { - var callState = this.get("callState"); + var state = this.getStoreState(); switch(actionData.wsState) { case WS_STATES.INIT: { - if (callState === CALL_STATES.GATHER) { - this.set({callState: CALL_STATES.CONNECTING}); + if (state.callState === CALL_STATES.GATHER) { + this.setStoreState({callState: CALL_STATES.CONNECTING}); } break; } case WS_STATES.ALERTING: { - this.set({callState: CALL_STATES.ALERTING}); + this.setStoreState({callState: CALL_STATES.ALERTING}); break; } case WS_STATES.CONNECTING: { this.sdkDriver.connectSession({ - apiKey: this.get("apiKey"), - sessionId: this.get("sessionId"), - sessionToken: this.get("sessionToken") + apiKey: state.apiKey, + sessionId: state.sessionId, + sessionToken: state.sessionToken }); - navigator.mozLoop.addConversationContext(this.get("windowId"), - this.get("sessionId"), - this.get("callId")); - this.set({callState: CALL_STATES.ONGOING}); + this.mozLoop.addConversationContext( + state.windowId, + state.sessionId, + state.callId); + this.setStoreState({callState: CALL_STATES.ONGOING}); break; } case WS_STATES.HALF_CONNECTED: case WS_STATES.CONNECTED: { - this.set({callState: CALL_STATES.ONGOING}); + this.setStoreState({callState: CALL_STATES.ONGOING}); break; } default: { @@ -209,7 +213,7 @@ loop.store.ConversationStore = (function() { "fetchEmailLink" ]); - this.set({ + this.setStoreState({ contact: actionData.contact, outgoing: windowType === "outgoing", windowId: actionData.windowId, @@ -218,7 +222,7 @@ loop.store.ConversationStore = (function() { videoMuted: actionData.callType === CALL_TYPES.AUDIO_ONLY }); - if (this.get("outgoing")) { + if (this.getStoreState("outgoing")) { this._setupOutgoingCall(); } // XXX Else, other types aren't supported yet. }, @@ -231,7 +235,7 @@ loop.store.ConversationStore = (function() { * @param {sharedActions.ConnectCall} actionData The action data. */ connectCall: function(actionData) { - this.set(actionData.sessionData); + this.setStoreState(actionData.sessionData); this._connectWebSocket(); }, @@ -245,7 +249,7 @@ loop.store.ConversationStore = (function() { } this._endSession(); - this.set({callState: CALL_STATES.FINISHED}); + this.setStoreState({callState: CALL_STATES.FINISHED}); }, /** @@ -259,9 +263,9 @@ loop.store.ConversationStore = (function() { // If the peer hungup, we end normally, otherwise // we treat this as a call failure. if (actionData.peerHungup) { - this.set({callState: CALL_STATES.FINISHED}); + this.setStoreState({callState: CALL_STATES.FINISHED}); } else { - this.set({ + this.setStoreState({ callState: CALL_STATES.TERMINATED, callStateReason: "peerNetworkDisconnected" }); @@ -272,7 +276,7 @@ loop.store.ConversationStore = (function() { * Cancels a call */ cancelCall: function() { - var callState = this.get("callState"); + var callState = this.getStoreState("callState"); if (this._websocket && (callState === CALL_STATES.CONNECTING || callState === CALL_STATES.ALERTING)) { @@ -281,21 +285,21 @@ loop.store.ConversationStore = (function() { } this._endSession(); - this.set({callState: CALL_STATES.CLOSE}); + this.setStoreState({callState: CALL_STATES.CLOSE}); }, /** * Retries a call */ retryCall: function() { - var callState = this.get("callState"); + var callState = this.getStoreState("callState"); if (callState !== CALL_STATES.TERMINATED) { console.error("Unexpected retry in state", callState); return; } - this.set({callState: CALL_STATES.GATHER}); - if (this.get("outgoing")) { + this.setStoreState({callState: CALL_STATES.GATHER}); + if (this.getStoreState("outgoing")) { this._setupOutgoingCall(); } }, @@ -313,8 +317,9 @@ loop.store.ConversationStore = (function() { * @param {sharedActions.setMute} actionData The mute state for the stream type. */ setMute: function(actionData) { - var muteType = actionData.type + "Muted"; - this.set(muteType, !actionData.enabled); + var newState = {}; + newState[actionData.type + "Muted"] = !actionData.enabled; + this.setStoreState(newState); }, /** @@ -329,7 +334,7 @@ loop.store.ConversationStore = (function() { this.trigger("error:emailLink"); return; } - this.set("emailLink", callUrlData.callUrl); + this.setStoreState({"emailLink": callUrlData.callUrl}); }.bind(this)); }, @@ -339,9 +344,9 @@ loop.store.ConversationStore = (function() { */ _setupOutgoingCall: function() { var contactAddresses = []; - var contact = this.get("contact"); + var contact = this.getStoreState("contact"); - navigator.mozLoop.calls.setCallInProgress(this.get("windowId")); + this.mozLoop.calls.setCallInProgress(this.getStoreState("windowId")); function appendContactValues(property, strip) { if (contact.hasOwnProperty(property)) { @@ -362,7 +367,7 @@ loop.store.ConversationStore = (function() { appendContactValues("tel", true); this.client.setupOutgoingCall(contactAddresses, - this.get("callType"), + this.getStoreState("callType"), function(err, result) { if (err) { console.error("Failed to get outgoing call data", err); @@ -385,9 +390,9 @@ loop.store.ConversationStore = (function() { */ _connectWebSocket: function() { this._websocket = new loop.CallConnectionWebSocket({ - url: this.get("progressURL"), - callId: this.get("callId"), - websocketToken: this.get("websocketToken") + url: this.getStoreState("progressURL"), + callId: this.getStoreState("callId"), + websocketToken: this.getStoreState("websocketToken") }); this._websocket.promiseConnect().then( @@ -422,7 +427,8 @@ loop.store.ConversationStore = (function() { delete this._websocket; } - navigator.mozLoop.calls.clearCallInProgress(this.get("windowId")); + this.mozLoop.calls.clearCallInProgress( + this.getStoreState("windowId")); }, /** @@ -450,6 +456,4 @@ loop.store.ConversationStore = (function() { this.dispatcher.dispatch(action); } }); - - return ConversationStore; })(); diff --git a/browser/components/loop/standalone/content/js/standaloneRoomViews.js b/browser/components/loop/standalone/content/js/standaloneRoomViews.js index d6a8238dcd5d..4440af4560bd 100644 --- a/browser/components/loop/standalone/content/js/standaloneRoomViews.js +++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.js @@ -127,6 +127,10 @@ loop.standaloneRoomViews = (function(mozL10n) { React.DOM.div({className: "room-inner-info-area"}, React.DOM.p({className: "failed-room-message"}, this._getFailureString() + ), + React.DOM.button({className: "btn btn-join btn-info", + onClick: this.props.joinRoom}, + mozL10n.get("retry_call_button") ) ) ); diff --git a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx index d128d0774095..8f235874c558 100644 --- a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx +++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx @@ -128,6 +128,10 @@ loop.standaloneRoomViews = (function(mozL10n) {

{this._getFailureString()}

+ ); } diff --git a/browser/components/loop/test/desktop-local/conversationViews_test.js b/browser/components/loop/test/desktop-local/conversationViews_test.js index 60a2dde9d453..2b8ba185bd20 100644 --- a/browser/components/loop/test/desktop-local/conversationViews_test.js +++ b/browser/components/loop/test/desktop-local/conversationViews_test.js @@ -8,6 +8,7 @@ describe("loop.conversationViews", function () { var sharedUtils = loop.shared.utils; var sandbox, oldTitle, view, dispatcher, contact, fakeAudioXHR; + var fakeMozLoop; var CALL_STATES = loop.store.CALL_STATES; @@ -43,7 +44,7 @@ describe("loop.conversationViews", function () { onload: null }; - navigator.mozLoop = { + fakeMozLoop = navigator.mozLoop = { getLoopPref: sinon.stub().returns("http://fakeurl"), composeEmail: sinon.spy(), get appVersionInfo() { @@ -242,9 +243,9 @@ describe("loop.conversationViews", function () { } beforeEach(function() { - store = new loop.store.ConversationStore({}, { - dispatcher: dispatcher, + store = new loop.store.ConversationStore(dispatcher, { client: {}, + mozLoop: navigator.mozLoop, sdkDriver: {} }); fakeAudio = { @@ -306,7 +307,7 @@ describe("loop.conversationViews", function () { it("should compose an email once the email link is received", function() { var composeCallUrlEmail = sandbox.stub(sharedUtils, "composeCallUrlEmail"); view = mountTestComponent(); - store.set("emailLink", "http://fake.invalid/"); + store.setStoreState({emailLink: "http://fake.invalid/"}); sinon.assert.calledOnce(composeCallUrlEmail); sinon.assert.calledWithExactly(composeCallUrlEmail, @@ -318,7 +319,7 @@ describe("loop.conversationViews", function () { sandbox.stub(window, "close"); view = mountTestComponent(); - store.set("emailLink", "http://fake.invalid/"); + store.setStoreState({emailLink: "http://fake.invalid/"}); sinon.assert.calledOnce(window.close); }); @@ -457,9 +458,9 @@ describe("loop.conversationViews", function () { } beforeEach(function() { - store = new loop.store.ConversationStore({}, { - dispatcher: dispatcher, + store = new loop.store.ConversationStore(dispatcher, { client: {}, + mozLoop: fakeMozLoop, sdkDriver: {} }); feedbackStore = new loop.store.FeedbackStore(dispatcher, { @@ -469,7 +470,7 @@ describe("loop.conversationViews", function () { it("should render the CallFailedView when the call state is 'terminated'", function() { - store.set({callState: CALL_STATES.TERMINATED}); + store.setStoreState({callState: CALL_STATES.TERMINATED}); view = mountTestComponent(); @@ -479,7 +480,7 @@ describe("loop.conversationViews", function () { it("should render the PendingConversationView when the call state is 'gather'", function() { - store.set({ + store.setStoreState({ callState: CALL_STATES.GATHER, contact: contact }); @@ -492,7 +493,7 @@ describe("loop.conversationViews", function () { it("should render the OngoingConversationView when the call state is 'ongoing'", function() { - store.set({callState: CALL_STATES.ONGOING}); + store.setStoreState({callState: CALL_STATES.ONGOING}); view = mountTestComponent(); @@ -502,7 +503,7 @@ describe("loop.conversationViews", function () { it("should render the FeedbackView when the call state is 'finished'", function() { - store.set({callState: CALL_STATES.FINISHED}); + store.setStoreState({callState: CALL_STATES.FINISHED}); view = mountTestComponent(); @@ -519,7 +520,7 @@ describe("loop.conversationViews", function () { }; sandbox.stub(window, "Audio").returns(fakeAudio); - store.set({callState: CALL_STATES.FINISHED}); + store.setStoreState({callState: CALL_STATES.FINISHED}); view = mountTestComponent(); @@ -528,7 +529,7 @@ describe("loop.conversationViews", function () { it("should update the rendered views when the state is changed.", function() { - store.set({ + store.setStoreState({ callState: CALL_STATES.GATHER, contact: contact }); @@ -538,7 +539,7 @@ describe("loop.conversationViews", function () { TestUtils.findRenderedComponentWithType(view, loop.conversationViews.PendingConversationView); - store.set({callState: CALL_STATES.TERMINATED}); + store.setStoreState({callState: CALL_STATES.TERMINATED}); TestUtils.findRenderedComponentWithType(view, loop.conversationViews.CallFailedView); diff --git a/browser/components/loop/test/desktop-local/conversation_test.js b/browser/components/loop/test/desktop-local/conversation_test.js index 524a657afc25..a150e664cd62 100644 --- a/browser/components/loop/test/desktop-local/conversation_test.js +++ b/browser/components/loop/test/desktop-local/conversation_test.js @@ -160,20 +160,22 @@ describe("loop.conversation", function() { sdk: {} }); dispatcher = new loop.Dispatcher(); - conversationStore = new loop.store.ConversationStore({ - contact: { - name: [ "Mr Smith" ], - email: [{ - type: "home", - value: "fakeEmail", - pref: true - }] - } - }, { - client: client, - dispatcher: dispatcher, - sdkDriver: {} - }); + conversationStore = new loop.store.ConversationStore( + dispatcher, { + client: client, + mozLoop: navigator.mozLoop, + sdkDriver: {} + }); + + conversationStore.setStoreState({contact: { + name: [ "Mr Smith" ], + email: [{ + type: "home", + value: "fakeEmail", + pref: true + }] + }}); + roomStore = new loop.store.RoomStore(dispatcher, { mozLoop: navigator.mozLoop, }); diff --git a/browser/components/loop/test/desktop-local/index.html b/browser/components/loop/test/desktop-local/index.html index f3a2039bcdf0..0aecad67b92a 100644 --- a/browser/components/loop/test/desktop-local/index.html +++ b/browser/components/loop/test/desktop-local/index.html @@ -41,7 +41,6 @@ - @@ -51,6 +50,7 @@ + diff --git a/browser/components/loop/test/desktop-local/roomViews_test.js b/browser/components/loop/test/desktop-local/roomViews_test.js index 11a6706fa4bb..e4d0e00ca17e 100644 --- a/browser/components/loop/test/desktop-local/roomViews_test.js +++ b/browser/components/loop/test/desktop-local/roomViews_test.js @@ -206,15 +206,6 @@ describe("loop.roomViews", function () { })); } - it("should dispatch a setupStreamElements action when the view is created", - function() { - view = mountTestComponent(); - - sinon.assert.calledOnce(dispatcher.dispatch); - sinon.assert.calledWithMatch(dispatcher.dispatch, - sinon.match.hasOwn("name", "setupStreamElements")); - }); - it("should dispatch a setMute action when the audio mute button is pressed", function() { view = mountTestComponent(); @@ -271,6 +262,44 @@ describe("loop.roomViews", function () { expect(muteBtn.classList.contains("muted")).eql(true); }); + describe("#componentWillUpdate", function() { + function expectActionDispatched(view) { + sinon.assert.calledOnce(dispatcher.dispatch); + sinon.assert.calledWithExactly(dispatcher.dispatch, + sinon.match.instanceOf(sharedActions.SetupStreamElements)); + sinon.assert.calledWithExactly(dispatcher.dispatch, + sinon.match(function(value) { + return value.getLocalElementFunc() === + view.getDOMNode().querySelector(".local"); + })); + sinon.assert.calledWithExactly(dispatcher.dispatch, + sinon.match(function(value) { + return value.getRemoteElementFunc() === + view.getDOMNode().querySelector(".remote"); + })); + } + + it("should dispatch a `SetupStreamElements` action when the MEDIA_WAIT state " + + "is entered", function() { + activeRoomStore.setStoreState({roomState: ROOM_STATES.READY}); + var view = mountTestComponent(); + + activeRoomStore.setStoreState({roomState: ROOM_STATES.MEDIA_WAIT}); + + expectActionDispatched(view); + }); + + it("should dispatch a `SetupStreamElements` action on MEDIA_WAIT state is " + + "re-entered", function() { + activeRoomStore.setStoreState({roomState: ROOM_STATES.ENDED}); + var view = mountTestComponent(); + + activeRoomStore.setStoreState({roomState: ROOM_STATES.MEDIA_WAIT}); + + expectActionDispatched(view); + }); + }); + describe("#render", function() { it("should set document.title to store.serverData.roomName", function() { mountTestComponent(); diff --git a/browser/components/loop/test/shared/conversationStore_test.js b/browser/components/loop/test/shared/conversationStore_test.js index 2ca796a9bcc2..d75d3fc7e8bc 100644 --- a/browser/components/loop/test/shared/conversationStore_test.js +++ b/browser/components/loop/test/shared/conversationStore_test.js @@ -11,7 +11,7 @@ describe("loop.store.ConversationStore", function () { var sharedActions = loop.shared.actions; var sharedUtils = loop.shared.utils; var sandbox, dispatcher, client, store, fakeSessionData, sdkDriver; - var contact; + var contact, fakeMozLoop; var connectPromise, resolveConnectPromise, rejectConnectPromise; var wsCancelSpy, wsCloseSpy, wsMediaUpSpy, fakeWebsocket; @@ -36,7 +36,7 @@ describe("loop.store.ConversationStore", function () { }] }; - navigator.mozLoop = { + fakeMozLoop = { getLoopPref: sandbox.stub(), addConversationContext: sandbox.stub(), calls: { @@ -65,9 +65,9 @@ describe("loop.store.ConversationStore", function () { mediaUp: wsMediaUpSpy }; - store = new loop.store.ConversationStore({}, { + store = new loop.store.ConversationStore(dispatcher, { client: client, - dispatcher: dispatcher, + mozLoop: fakeMozLoop, sdkDriver: sdkDriver }); fakeSessionData = { @@ -99,19 +99,9 @@ describe("loop.store.ConversationStore", function () { }); describe("#initialize", function() { - it("should throw an error if the dispatcher is missing", function() { - expect(function() { - new loop.store.ConversationStore({}, { - client: client, - sdkDriver: sdkDriver - }); - }).to.Throw(/dispatcher/); - }); - it("should throw an error if the client is missing", function() { expect(function() { - new loop.store.ConversationStore({}, { - dispatcher: dispatcher, + new loop.store.ConversationStore(dispatcher, { sdkDriver: sdkDriver }); }).to.Throw(/client/); @@ -119,18 +109,26 @@ describe("loop.store.ConversationStore", function () { it("should throw an error if the sdkDriver is missing", function() { expect(function() { - new loop.store.ConversationStore({}, { - client: client, - dispatcher: dispatcher + new loop.store.ConversationStore(dispatcher, { + client: client }); }).to.Throw(/sdkDriver/); }); + + it("should throw an error if mozLoop is missing", function() { + expect(function() { + new loop.store.ConversationStore(dispatcher, { + sdkDriver: sdkDriver, + client: client + }); + }).to.Throw(/mozLoop/); + }); }); describe("#connectionFailure", function() { beforeEach(function() { store._websocket = fakeWebsocket; - store.set({windowId: "42"}); + store.setStoreState({windowId: "42"}); }); it("should disconnect the session", function() { @@ -148,71 +146,71 @@ describe("loop.store.ConversationStore", function () { }); it("should set the state to 'terminated'", function() { - store.set({callState: CALL_STATES.ALERTING}); + store.setStoreState({callState: CALL_STATES.ALERTING}); store.connectionFailure( new sharedActions.ConnectionFailure({reason: "fake"})); - expect(store.get("callState")).eql(CALL_STATES.TERMINATED); - expect(store.get("callStateReason")).eql("fake"); + expect(store.getStoreState("callState")).eql(CALL_STATES.TERMINATED); + expect(store.getStoreState("callStateReason")).eql("fake"); }); it("should release mozLoop callsData", function() { store.connectionFailure( new sharedActions.ConnectionFailure({reason: "fake"})); - sinon.assert.calledOnce(navigator.mozLoop.calls.clearCallInProgress); + sinon.assert.calledOnce(fakeMozLoop.calls.clearCallInProgress); sinon.assert.calledWithExactly( - navigator.mozLoop.calls.clearCallInProgress, "42"); + fakeMozLoop.calls.clearCallInProgress, "42"); }); }); describe("#connectionProgress", function() { describe("progress: init", function() { it("should change the state from 'gather' to 'connecting'", function() { - store.set({callState: CALL_STATES.GATHER}); + store.setStoreState({callState: CALL_STATES.GATHER}); store.connectionProgress( new sharedActions.ConnectionProgress({wsState: WS_STATES.INIT})); - expect(store.get("callState")).eql(CALL_STATES.CONNECTING); + expect(store.getStoreState("callState")).eql(CALL_STATES.CONNECTING); }); }); describe("progress: alerting", function() { it("should change the state from 'gather' to 'alerting'", function() { - store.set({callState: CALL_STATES.GATHER}); + store.setStoreState({callState: CALL_STATES.GATHER}); store.connectionProgress( new sharedActions.ConnectionProgress({wsState: WS_STATES.ALERTING})); - expect(store.get("callState")).eql(CALL_STATES.ALERTING); + expect(store.getStoreState("callState")).eql(CALL_STATES.ALERTING); }); it("should change the state from 'init' to 'alerting'", function() { - store.set({callState: CALL_STATES.INIT}); + store.setStoreState({callState: CALL_STATES.INIT}); store.connectionProgress( new sharedActions.ConnectionProgress({wsState: WS_STATES.ALERTING})); - expect(store.get("callState")).eql(CALL_STATES.ALERTING); + expect(store.getStoreState("callState")).eql(CALL_STATES.ALERTING); }); }); describe("progress: connecting", function() { beforeEach(function() { - store.set({callState: CALL_STATES.ALERTING}); + store.setStoreState({callState: CALL_STATES.ALERTING}); }); it("should change the state to 'ongoing'", function() { store.connectionProgress( new sharedActions.ConnectionProgress({wsState: WS_STATES.CONNECTING})); - expect(store.get("callState")).eql(CALL_STATES.ONGOING); + expect(store.getStoreState("callState")).eql(CALL_STATES.ONGOING); }); it("should connect the session", function() { - store.set(fakeSessionData); + store.setStoreState(fakeSessionData); store.connectionProgress( new sharedActions.ConnectionProgress({wsState: WS_STATES.CONNECTING})); @@ -226,13 +224,13 @@ describe("loop.store.ConversationStore", function () { }); it("should call mozLoop.addConversationContext", function() { - store.set(fakeSessionData); + store.setStoreState(fakeSessionData); store.connectionProgress( new sharedActions.ConnectionProgress({wsState: WS_STATES.CONNECTING})); - sinon.assert.calledOnce(navigator.mozLoop.addConversationContext); - sinon.assert.calledWithExactly(navigator.mozLoop.addConversationContext, + sinon.assert.calledOnce(fakeMozLoop.addConversationContext); + sinon.assert.calledWithExactly(fakeMozLoop.addConversationContext, "28", "321456", "142536"); }); }); @@ -242,7 +240,7 @@ describe("loop.store.ConversationStore", function () { var fakeSetupWindowData; beforeEach(function() { - store.set({callState: CALL_STATES.INIT}); + store.setStoreState({callState: CALL_STATES.INIT}); fakeSetupWindowData = { windowId: "123456", type: "outgoing", @@ -255,23 +253,24 @@ describe("loop.store.ConversationStore", function () { dispatcher.dispatch( new sharedActions.SetupWindowData(fakeSetupWindowData)); - expect(store.get("callState")).eql(CALL_STATES.GATHER); + expect(store.getStoreState("callState")).eql(CALL_STATES.GATHER); }); it("should save the basic call information", function() { dispatcher.dispatch( new sharedActions.SetupWindowData(fakeSetupWindowData)); - expect(store.get("windowId")).eql("123456"); - expect(store.get("outgoing")).eql(true); + expect(store.getStoreState("windowId")).eql("123456"); + expect(store.getStoreState("outgoing")).eql(true); }); it("should save the basic information from the mozLoop api", function() { dispatcher.dispatch( new sharedActions.SetupWindowData(fakeSetupWindowData)); - expect(store.get("contact")).eql(contact); - expect(store.get("callType")).eql(sharedUtils.CALL_TYPES.AUDIO_VIDEO); + expect(store.getStoreState("contact")).eql(contact); + expect(store.getStoreState("callType")) + .eql(sharedUtils.CALL_TYPES.AUDIO_VIDEO); }); describe("outgoing calls", function() { @@ -402,12 +401,12 @@ describe("loop.store.ConversationStore", function () { store.connectCall( new sharedActions.ConnectCall({sessionData: fakeSessionData})); - expect(store.get("apiKey")).eql("fakeKey"); - expect(store.get("callId")).eql("142536"); - expect(store.get("sessionId")).eql("321456"); - expect(store.get("sessionToken")).eql("341256"); - expect(store.get("websocketToken")).eql("543216"); - expect(store.get("progressURL")).eql("fakeURL"); + expect(store.getStoreState("apiKey")).eql("fakeKey"); + expect(store.getStoreState("callId")).eql("142536"); + expect(store.getStoreState("sessionId")).eql("321456"); + expect(store.getStoreState("sessionToken")).eql("341256"); + expect(store.getStoreState("websocketToken")).eql("543216"); + expect(store.getStoreState("progressURL")).eql("fakeURL"); }); it("should initialize the websocket", function() { @@ -488,8 +487,8 @@ describe("loop.store.ConversationStore", function () { mediaFail: wsMediaFailSpy, close: wsCloseSpy }; - store.set({callState: CALL_STATES.ONGOING}); - store.set({windowId: "42"}); + store.setStoreState({callState: CALL_STATES.ONGOING}); + store.setStoreState({windowId: "42"}); }); it("should disconnect the session", function() { @@ -513,15 +512,15 @@ describe("loop.store.ConversationStore", function () { it("should set the callState to finished", function() { store.hangupCall(new sharedActions.HangupCall()); - expect(store.get("callState")).eql(CALL_STATES.FINISHED); + expect(store.getStoreState("callState")).eql(CALL_STATES.FINISHED); }); it("should release mozLoop callsData", function() { store.hangupCall(new sharedActions.HangupCall()); - sinon.assert.calledOnce(navigator.mozLoop.calls.clearCallInProgress); + sinon.assert.calledOnce(fakeMozLoop.calls.clearCallInProgress); sinon.assert.calledWithExactly( - navigator.mozLoop.calls.clearCallInProgress, "42"); + fakeMozLoop.calls.clearCallInProgress, "42"); }); }); @@ -535,8 +534,8 @@ describe("loop.store.ConversationStore", function () { mediaFail: wsMediaFailSpy, close: wsCloseSpy }; - store.set({callState: CALL_STATES.ONGOING}); - store.set({windowId: "42"}); + store.setStoreState({callState: CALL_STATES.ONGOING}); + store.setStoreState({windowId: "42"}); }); it("should disconnect the session", function() { @@ -560,9 +559,9 @@ describe("loop.store.ConversationStore", function () { peerHungup: true })); - sinon.assert.calledOnce(navigator.mozLoop.calls.clearCallInProgress); + sinon.assert.calledOnce(fakeMozLoop.calls.clearCallInProgress); sinon.assert.calledWithExactly( - navigator.mozLoop.calls.clearCallInProgress, "42"); + fakeMozLoop.calls.clearCallInProgress, "42"); }); it("should set the callState to finished if the peer hungup", function() { @@ -570,7 +569,7 @@ describe("loop.store.ConversationStore", function () { peerHungup: true })); - expect(store.get("callState")).eql(CALL_STATES.FINISHED); + expect(store.getStoreState("callState")).eql(CALL_STATES.FINISHED); }); it("should set the callState to terminated if the peer was disconnected" + @@ -579,7 +578,7 @@ describe("loop.store.ConversationStore", function () { peerHungup: false })); - expect(store.get("callState")).eql(CALL_STATES.TERMINATED); + expect(store.getStoreState("callState")).eql(CALL_STATES.TERMINATED); }); it("should set the reason to peerNetworkDisconnected if the peer was" + @@ -588,7 +587,8 @@ describe("loop.store.ConversationStore", function () { peerHungup: false })); - expect(store.get("callStateReason")).eql("peerNetworkDisconnected"); + expect(store.getStoreState("callStateReason")) + .eql("peerNetworkDisconnected"); }); }); @@ -596,8 +596,8 @@ describe("loop.store.ConversationStore", function () { beforeEach(function() { store._websocket = fakeWebsocket; - store.set({callState: CALL_STATES.CONNECTING}); - store.set({windowId: "42"}); + store.setStoreState({callState: CALL_STATES.CONNECTING}); + store.setStoreState({windowId: "42"}); }); it("should disconnect the session", function() { @@ -621,37 +621,38 @@ describe("loop.store.ConversationStore", function () { it("should set the state to close if the call is connecting", function() { store.cancelCall(new sharedActions.CancelCall()); - expect(store.get("callState")).eql(CALL_STATES.CLOSE); + expect(store.getStoreState("callState")).eql(CALL_STATES.CLOSE); }); it("should set the state to close if the call has terminated already", function() { - store.set({callState: CALL_STATES.TERMINATED}); + store.setStoreState({callState: CALL_STATES.TERMINATED}); store.cancelCall(new sharedActions.CancelCall()); - expect(store.get("callState")).eql(CALL_STATES.CLOSE); + expect(store.getStoreState("callState")).eql(CALL_STATES.CLOSE); }); it("should release mozLoop callsData", function() { store.cancelCall(new sharedActions.CancelCall()); - sinon.assert.calledOnce(navigator.mozLoop.calls.clearCallInProgress); + sinon.assert.calledOnce(fakeMozLoop.calls.clearCallInProgress); sinon.assert.calledWithExactly( - navigator.mozLoop.calls.clearCallInProgress, "42"); + fakeMozLoop.calls.clearCallInProgress, "42"); }); }); describe("#retryCall", function() { it("should set the state to gather", function() { - store.set({callState: CALL_STATES.TERMINATED}); + store.setStoreState({callState: CALL_STATES.TERMINATED}); store.retryCall(new sharedActions.RetryCall()); - expect(store.get("callState")).eql(CALL_STATES.GATHER); + expect(store.getStoreState("callState")) + .eql(CALL_STATES.GATHER); }); it("should request the outgoing call data", function() { - store.set({ + store.setStoreState({ callState: CALL_STATES.TERMINATED, outgoing: true, callType: sharedUtils.CALL_TYPES.AUDIO_VIDEO, @@ -678,25 +679,25 @@ describe("loop.store.ConversationStore", function () { describe("#setMute", function() { it("should save the mute state for the audio stream", function() { - store.set({"audioMuted": false}); + store.setStoreState({"audioMuted": false}); dispatcher.dispatch(new sharedActions.SetMute({ type: "audio", enabled: true })); - expect(store.get("audioMuted")).eql(false); + expect(store.getStoreState("audioMuted")).eql(false); }); it("should save the mute state for the video stream", function() { - store.set({"videoMuted": true}); + store.setStoreState({"videoMuted": true}); dispatcher.dispatch(new sharedActions.SetMute({ type: "video", enabled: false })); - expect(store.get("videoMuted")).eql(true); + expect(store.getStoreState("videoMuted")).eql(true); }); }); @@ -715,7 +716,7 @@ describe("loop.store.ConversationStore", function () { }; store.fetchEmailLink(new sharedActions.FetchEmailLink()); - expect(store.get("emailLink")).eql("http://fake.invalid/"); + expect(store.getStoreState("emailLink")).eql("http://fake.invalid/"); }); it("should trigger an error:emailLink event in case of failure", diff --git a/browser/components/loop/test/standalone/standaloneRoomViews_test.js b/browser/components/loop/test/standalone/standaloneRoomViews_test.js index fbdbc653eee4..d93b0ceb7ca2 100644 --- a/browser/components/loop/test/standalone/standaloneRoomViews_test.js +++ b/browser/components/loop/test/standalone/standaloneRoomViews_test.js @@ -203,6 +203,14 @@ describe("loop.standaloneRoomViews", function() { expect(view.getDOMNode().querySelector(".failed-room-message")) .not.eql(null); }); + + it("should display a retry button", + function() { + activeRoomStore.setStoreState({roomState: ROOM_STATES.FAILED}); + + expect(view.getDOMNode().querySelector(".btn-info")) + .not.eql(null); + }); }); describe("Join button", function() { diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index c3b02f5dfa2a..09d4b057aee2 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -8333,9 +8333,6 @@ Parser::accumulateTelemetry() JSContext* cx = context->maybeJSContext(); if (!cx) return; - JSAccumulateTelemetryDataCallback cb = cx->runtime()->telemetryCallback; - if (!cb) - return; const char* filename = getFilename(); if (!filename) return; @@ -8361,17 +8358,17 @@ Parser::accumulateTelemetry() // Call back into Firefox's Telemetry reporter. if (sawDeprecatedForEach) - (*cb)(JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, DeprecatedForEach); + cx->runtime()->addTelemetry(JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, DeprecatedForEach); if (sawDeprecatedDestructuringForIn) - (*cb)(JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, DeprecatedDestructuringForIn); + cx->runtime()->addTelemetry(JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, DeprecatedDestructuringForIn); if (sawDeprecatedLegacyGenerator) - (*cb)(JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, DeprecatedLegacyGenerator); + cx->runtime()->addTelemetry(JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, DeprecatedLegacyGenerator); if (sawDeprecatedExpressionClosure) - (*cb)(JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, DeprecatedExpressionClosure); + cx->runtime()->addTelemetry(JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, DeprecatedExpressionClosure); if (sawDeprecatedLetBlock) - (*cb)(JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, DeprecatedLetBlock); + cx->runtime()->addTelemetry(JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, DeprecatedLetBlock); if (sawDeprecatedLetExpression) - (*cb)(JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, DeprecatedLetExpression); + cx->runtime()->addTelemetry(JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, DeprecatedLetExpression); } template class Parser; diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index 56f8153ef623..da56309c4e40 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -749,28 +749,26 @@ Statistics::endGC() for (int i = 0; i < PHASE_LIMIT; i++) phaseTotals[i] += phaseTimes[i]; - if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) { - int64_t total, longest; - gcDuration(&total, &longest); + int64_t total, longest; + gcDuration(&total, &longest); - int64_t sccTotal, sccLongest; - sccDurations(&sccTotal, &sccLongest); + int64_t sccTotal, sccLongest; + sccDurations(&sccTotal, &sccLongest); - (*cb)(JS_TELEMETRY_GC_IS_COMPARTMENTAL, !zoneStats.isCollectingAllZones()); - (*cb)(JS_TELEMETRY_GC_MS, t(total)); - (*cb)(JS_TELEMETRY_GC_MAX_PAUSE_MS, t(longest)); - (*cb)(JS_TELEMETRY_GC_MARK_MS, t(phaseTimes[PHASE_MARK])); - (*cb)(JS_TELEMETRY_GC_SWEEP_MS, t(phaseTimes[PHASE_SWEEP])); - (*cb)(JS_TELEMETRY_GC_MARK_ROOTS_MS, t(phaseTimes[PHASE_MARK_ROOTS])); - (*cb)(JS_TELEMETRY_GC_MARK_GRAY_MS, t(phaseTimes[PHASE_SWEEP_MARK_GRAY])); - (*cb)(JS_TELEMETRY_GC_NON_INCREMENTAL, !!nonincrementalReason); - (*cb)(JS_TELEMETRY_GC_INCREMENTAL_DISABLED, !runtime->gc.isIncrementalGCAllowed()); - (*cb)(JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS, t(sccTotal)); - (*cb)(JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS, t(sccLongest)); - - double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC); - (*cb)(JS_TELEMETRY_GC_MMU_50, mmu50 * 100); - } + runtime->addTelemetry(JS_TELEMETRY_GC_IS_COMPARTMENTAL, !zoneStats.isCollectingAllZones()); + runtime->addTelemetry(JS_TELEMETRY_GC_MS, t(total)); + runtime->addTelemetry(JS_TELEMETRY_GC_MAX_PAUSE_MS, t(longest)); + runtime->addTelemetry(JS_TELEMETRY_GC_MARK_MS, t(phaseTimes[PHASE_MARK])); + runtime->addTelemetry(JS_TELEMETRY_GC_SWEEP_MS, t(phaseTimes[PHASE_SWEEP])); + runtime->addTelemetry(JS_TELEMETRY_GC_MARK_ROOTS_MS, t(phaseTimes[PHASE_MARK_ROOTS])); + runtime->addTelemetry(JS_TELEMETRY_GC_MARK_GRAY_MS, t(phaseTimes[PHASE_SWEEP_MARK_GRAY])); + runtime->addTelemetry(JS_TELEMETRY_GC_NON_INCREMENTAL, !!nonincrementalReason); + runtime->addTelemetry(JS_TELEMETRY_GC_INCREMENTAL_DISABLED, !runtime->gc.isIncrementalGCAllowed()); + runtime->addTelemetry(JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS, t(sccTotal)); + runtime->addTelemetry(JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS, t(sccLongest)); + + double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC); + runtime->addTelemetry(JS_TELEMETRY_GC_MMU_50, mmu50 * 100); if (fp) printStats(); @@ -795,8 +793,7 @@ Statistics::beginSlice(const ZoneGCStats &zoneStats, JSGCInvocationKind gckind, if (!slices.append(data)) CrashAtUnhandlableOOM("Failed to allocate statistics slice."); - if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) - (*cb)(JS_TELEMETRY_GC_REASON, reason); + runtime->addTelemetry(JS_TELEMETRY_GC_REASON, reason); // Slice callbacks should only fire for the outermost level if (++gcDepth == 1) { @@ -813,10 +810,8 @@ Statistics::endSlice() slices.back().end = PRMJ_Now(); slices.back().endFaults = GetPageFaultCount(); - if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) { - (*cb)(JS_TELEMETRY_GC_SLICE_MS, t(slices.back().end - slices.back().start)); - (*cb)(JS_TELEMETRY_GC_RESET, !!slices.back().resetReason); - } + runtime->addTelemetry(JS_TELEMETRY_GC_SLICE_MS, t(slices.back().end - slices.back().start)); + runtime->addTelemetry(JS_TELEMETRY_GC_RESET, !!slices.back().resetReason); bool last = runtime->gc.state() == gc::NO_INCREMENTAL; if (last) diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index c6ea97105fe6..7bb75548c911 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -20,6 +20,7 @@ #include "jsfun.h" #include "jsnum.h" #include "jsobj.h" +#include "jsprf.h" #include "jsscript.h" #include "jstypes.h" #include "jsutil.h" @@ -713,8 +714,31 @@ ErrorReport::init(JSContext *cx, HandleValue exn) if (exn.isObject()) { exnObject = &exn.toObject(); reportp = js_ErrorFromException(cx, exnObject); - } + JSCompartment *comp = exnObject->compartment(); + JSAddonId *addonId = comp->addonId; + if (addonId) { + UniqueChars addonIdChars(JS_EncodeString(cx, addonId)); + + const char *filename = nullptr; + + if (reportp && reportp->filename) { + filename = strrchr(reportp->filename, '/'); + if (filename) + filename++; + } + if (!filename) { + filename = "FILE_NOT_FOUND"; + } + char histogramKey[64]; + JS_snprintf(histogramKey, sizeof(histogramKey), + "%s %s %u", + addonIdChars.get(), + filename, + (reportp ? reportp->lineno : 0) ); + cx->runtime()->addTelemetry(JS_TELEMETRY_ADDON_EXCEPTIONS, 1, histogramKey); + } + } // Be careful not to invoke ToString if we've already successfully extracted // an error report, since the exception might be wrapped in a security // wrapper, and ToString-ing it might throw. diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 230676e8718f..9d9d45adfb1a 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -681,7 +681,7 @@ js::StringToLinearStringSlow(JSContext *cx, JSString *str) JS_FRIEND_API(void) JS_SetAccumulateTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallback callback) { - rt->telemetryCallback = callback; + rt->setTelemetryCallback(rt, callback); } JS_FRIEND_API(JSObject *) diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 78112e6e3dca..e9a7a0ff67e9 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -110,11 +110,12 @@ enum { JS_TELEMETRY_GC_NON_INCREMENTAL, JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS, JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS, - JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT + JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, + JS_TELEMETRY_ADDON_EXCEPTIONS }; typedef void -(* JSAccumulateTelemetryDataCallback)(int id, uint32_t sample); +(*JSAccumulateTelemetryDataCallback)(int id, uint32_t sample, const char *key); extern JS_FRIEND_API(void) JS_SetAccumulateTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallback callback); diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index fa1167bb3864..e79390adf035 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -139,6 +139,7 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime) parentRuntime(parentRuntime), interrupt_(false), interruptPar_(false), + telemetryCallback(nullptr), handlingSignal(false), interruptCallback(nullptr), exclusiveAccessLock(nullptr), @@ -196,7 +197,6 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime) DOMcallbacks(nullptr), destroyPrincipals(nullptr), structuredCloneCallbacks(nullptr), - telemetryCallback(nullptr), errorReporter(nullptr), linkedAsmJSModules(nullptr), propertyRemovals(0), @@ -458,6 +458,19 @@ JSRuntime::~JSRuntime() #endif } +void +JSRuntime::addTelemetry(int id, uint32_t sample, const char *key) +{ + if (telemetryCallback) + (*telemetryCallback)(id, sample, key); +} + +void +JSRuntime::setTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallback callback) +{ + rt->telemetryCallback = callback; +} + void NewObjectCache::clearNurseryObjects(JSRuntime *rt) { diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 55c040be182a..cb62d03d600b 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -707,7 +707,16 @@ struct JSRuntime : public JS::shadow::Runtime, private: mozilla::Atomic interrupt_; mozilla::Atomic interruptPar_; + + /* Call this to accumulate telemetry data. */ + JSAccumulateTelemetryDataCallback telemetryCallback; public: + // Accumulates data for Firefox telemetry. |id| is the ID of a JS_TELEMETRY_* + // histogram. |key| provides an additional key to identify the histogram. + // |sample| is the data to add to the histogram. + void addTelemetry(int id, uint32_t sample, const char *key = nullptr); + + void setTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallback callback); enum InterruptMode { RequestInterruptUrgent, @@ -1090,9 +1099,6 @@ struct JSRuntime : public JS::shadow::Runtime, /* Structured data callbacks are runtime-wide. */ const JSStructuredCloneCallbacks *structuredCloneCallbacks; - /* Call this to accumulate telemetry data. */ - JSAccumulateTelemetryDataCallback telemetryCallback; - /* Optional error reporter. */ JSErrorReporter errorReporter; diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index e85a181e1be2..7f917322a0fb 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -2963,7 +2963,7 @@ DiagnosticMemoryCallback(void *ptr, size_t size) #endif static void -AccumulateTelemetryCallback(int id, uint32_t sample) +AccumulateTelemetryCallback(int id, uint32_t sample, const char *key) { switch (id) { case JS_TELEMETRY_GC_REASON: @@ -3015,6 +3015,9 @@ AccumulateTelemetryCallback(int id, uint32_t sample) MOZ_ASSERT(sample <= 5); Telemetry::Accumulate(Telemetry::JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, sample); break; + case JS_TELEMETRY_ADDON_EXCEPTIONS: + Telemetry::Accumulate(Telemetry::JS_TELEMETRY_ADDON_EXCEPTIONS, nsDependentCString(key), sample); + break; default: MOZ_ASSERT_UNREACHABLE("Unexpected JS_TELEMETRY id"); } diff --git a/mobile/android/base/db/BrowserContract.java b/mobile/android/base/db/BrowserContract.java index f5d40b003da8..4f9922933115 100644 --- a/mobile/android/base/db/BrowserContract.java +++ b/mobile/android/base/db/BrowserContract.java @@ -157,7 +157,6 @@ public class BrowserContract { public static final String TAGS_FOLDER_GUID = "tags"; public static final String TOOLBAR_FOLDER_GUID = "toolbar"; public static final String UNFILED_FOLDER_GUID = "unfiled"; - public static final String READING_LIST_FOLDER_GUID = "readinglist"; public static final String FAKE_DESKTOP_FOLDER_GUID = "desktop"; public static final String PINNED_FOLDER_GUID = "pinned"; @@ -342,53 +341,6 @@ public class BrowserContract { new String[] { _ID, DATASET_ID, URL, TITLE, DESCRIPTION, IMAGE_URL, FILTER }; } - /* - * Contains names and schema definitions for tables and views - * no longer being used by current ContentProviders. These values are used - * to make incremental updates to the schema during a database upgrade. Will be - * removed with bug 947018. - */ - static final class Obsolete { - public static final String TABLE_IMAGES = "images"; - public static final String VIEW_BOOKMARKS_WITH_IMAGES = "bookmarks_with_images"; - public static final String VIEW_HISTORY_WITH_IMAGES = "history_with_images"; - public static final String VIEW_COMBINED_WITH_IMAGES = "combined_with_images"; - - public static final class Images implements CommonColumns, SyncColumns { - private Images() {} - - public static final String URL = "url_key"; - public static final String FAVICON_URL = "favicon_url"; - public static final String FAVICON = "favicon"; - public static final String THUMBNAIL = "thumbnail"; - public static final String _ID = "_id"; - public static final String GUID = "guid"; - public static final String DATE_CREATED = "created"; - public static final String DATE_MODIFIED = "modified"; - public static final String IS_DELETED = "deleted"; - } - - public static final class Combined { - private Combined() {} - - public static final String THUMBNAIL = "thumbnail"; - public static final String DISPLAY = "display"; - - public static final int DISPLAY_NORMAL = 0; - public static final int DISPLAY_READER = 1; - } - - static final String TABLE_BOOKMARKS_JOIN_IMAGES = Bookmarks.TABLE_NAME + " LEFT OUTER JOIN " + - Obsolete.TABLE_IMAGES + " ON " + Bookmarks.TABLE_NAME + "." + Bookmarks.URL + " = " + - Obsolete.TABLE_IMAGES + "." + Obsolete.Images.URL; - - static final String TABLE_HISTORY_JOIN_IMAGES = History.TABLE_NAME + " LEFT OUTER JOIN " + - Obsolete.TABLE_IMAGES + " ON " + Bookmarks.TABLE_NAME + "." + History.URL + " = " + - Obsolete.TABLE_IMAGES + "." + Obsolete.Images.URL; - - static final String FAVICON_DB = "favicon_urls.db"; - } - @RobocopTarget public static final class ReadingListItems implements CommonColumns, URLColumns, SyncColumns { private ReadingListItems() {} diff --git a/mobile/android/base/db/BrowserDatabaseHelper.java b/mobile/android/base/db/BrowserDatabaseHelper.java index e77c32d7deed..7505fca68d9e 100644 --- a/mobile/android/base/db/BrowserDatabaseHelper.java +++ b/mobile/android/base/db/BrowserDatabaseHelper.java @@ -11,14 +11,12 @@ import java.util.List; import org.mozilla.gecko.R; import org.mozilla.gecko.db.BrowserContract.Bookmarks; import org.mozilla.gecko.db.BrowserContract.Combined; -import org.mozilla.gecko.db.BrowserContract.FaviconColumns; import org.mozilla.gecko.db.BrowserContract.Favicons; import org.mozilla.gecko.db.BrowserContract.History; -import org.mozilla.gecko.db.BrowserContract.Obsolete; import org.mozilla.gecko.db.BrowserContract.ReadingListItems; import org.mozilla.gecko.db.BrowserContract.SearchHistory; import org.mozilla.gecko.db.BrowserContract.Thumbnails; -import org.mozilla.gecko.sync.Utils; +import static org.mozilla.gecko.db.DBUtils.qualifyColumn; import android.content.ContentValues; import android.content.Context; @@ -61,7 +59,6 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { static final String TABLE_BOOKMARKS_TMP = TABLE_BOOKMARKS + "_tmp"; static final String TABLE_HISTORY_TMP = TABLE_HISTORY + "_tmp"; - static final String TABLE_IMAGES_TMP = Obsolete.TABLE_IMAGES + "_tmp"; private static final String[] mobileIdColumns = new String[] { Bookmarks._ID }; private static final String[] mobileIdSelectionArgs = new String[] { Bookmarks.MOBILE_FOLDER_GUID }; @@ -74,37 +71,6 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { private void createBookmarksTable(SQLiteDatabase db) { debug("Creating " + TABLE_BOOKMARKS + " table"); - db.execSQL("CREATE TABLE " + TABLE_BOOKMARKS + "(" + - Bookmarks._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - Bookmarks.TITLE + " TEXT," + - Bookmarks.URL + " TEXT," + - Bookmarks.TYPE + " INTEGER NOT NULL DEFAULT " + Bookmarks.TYPE_BOOKMARK + "," + - Bookmarks.PARENT + " INTEGER," + - Bookmarks.POSITION + " INTEGER NOT NULL," + - Bookmarks.KEYWORD + " TEXT," + - Bookmarks.DESCRIPTION + " TEXT," + - Bookmarks.TAGS + " TEXT," + - Bookmarks.DATE_CREATED + " INTEGER," + - Bookmarks.DATE_MODIFIED + " INTEGER," + - Bookmarks.GUID + " TEXT NOT NULL," + - Bookmarks.IS_DELETED + " INTEGER NOT NULL DEFAULT 0, " + - "FOREIGN KEY (" + Bookmarks.PARENT + ") REFERENCES " + - TABLE_BOOKMARKS + "(" + Bookmarks._ID + ")" + - ");"); - - db.execSQL("CREATE INDEX bookmarks_url_index ON " + TABLE_BOOKMARKS + "(" - + Bookmarks.URL + ")"); - db.execSQL("CREATE INDEX bookmarks_type_deleted_index ON " + TABLE_BOOKMARKS + "(" - + Bookmarks.TYPE + ", " + Bookmarks.IS_DELETED + ")"); - db.execSQL("CREATE UNIQUE INDEX bookmarks_guid_index ON " + TABLE_BOOKMARKS + "(" - + Bookmarks.GUID + ")"); - db.execSQL("CREATE INDEX bookmarks_modified_index ON " + TABLE_BOOKMARKS + "(" - + Bookmarks.DATE_MODIFIED + ")"); - } - - private void createBookmarksTableOn13(SQLiteDatabase db) { - debug("Creating " + TABLE_BOOKMARKS + " table"); - db.execSQL("CREATE TABLE " + TABLE_BOOKMARKS + "(" + Bookmarks._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + Bookmarks.TITLE + " TEXT," + @@ -135,30 +101,6 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { } private void createHistoryTable(SQLiteDatabase db) { - debug("Creating " + TABLE_HISTORY + " table"); - db.execSQL("CREATE TABLE " + TABLE_HISTORY + "(" + - History._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - History.TITLE + " TEXT," + - History.URL + " TEXT NOT NULL," + - History.VISITS + " INTEGER NOT NULL DEFAULT 0," + - History.DATE_LAST_VISITED + " INTEGER," + - History.DATE_CREATED + " INTEGER," + - History.DATE_MODIFIED + " INTEGER," + - History.GUID + " TEXT NOT NULL," + - History.IS_DELETED + " INTEGER NOT NULL DEFAULT 0" + - ");"); - - db.execSQL("CREATE INDEX history_url_index ON " + TABLE_HISTORY + "(" - + History.URL + ")"); - db.execSQL("CREATE UNIQUE INDEX history_guid_index ON " + TABLE_HISTORY + "(" - + History.GUID + ")"); - db.execSQL("CREATE INDEX history_modified_index ON " + TABLE_HISTORY + "(" - + History.DATE_MODIFIED + ")"); - db.execSQL("CREATE INDEX history_visited_index ON " + TABLE_HISTORY + "(" - + History.DATE_LAST_VISITED + ")"); - } - - private void createHistoryTableOn13(SQLiteDatabase db) { debug("Creating " + TABLE_HISTORY + " table"); db.execSQL("CREATE TABLE " + TABLE_HISTORY + "(" + History._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + @@ -173,36 +115,14 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { History.IS_DELETED + " INTEGER NOT NULL DEFAULT 0" + ");"); - db.execSQL("CREATE INDEX history_url_index ON " + TABLE_HISTORY + "(" - + History.URL + ")"); - db.execSQL("CREATE UNIQUE INDEX history_guid_index ON " + TABLE_HISTORY + "(" - + History.GUID + ")"); - db.execSQL("CREATE INDEX history_modified_index ON " + TABLE_HISTORY + "(" - + History.DATE_MODIFIED + ")"); - db.execSQL("CREATE INDEX history_visited_index ON " + TABLE_HISTORY + "(" - + History.DATE_LAST_VISITED + ")"); - } - - private void createImagesTable(SQLiteDatabase db) { - debug("Creating " + Obsolete.TABLE_IMAGES + " table"); - db.execSQL("CREATE TABLE " + Obsolete.TABLE_IMAGES + " (" + - Obsolete.Images._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - Obsolete.Images.URL + " TEXT UNIQUE NOT NULL," + - Obsolete.Images.FAVICON + " BLOB," + - Obsolete.Images.FAVICON_URL + " TEXT," + - Obsolete.Images.THUMBNAIL + " BLOB," + - Obsolete.Images.DATE_CREATED + " INTEGER," + - Obsolete.Images.DATE_MODIFIED + " INTEGER," + - Obsolete.Images.GUID + " TEXT NOT NULL," + - Obsolete.Images.IS_DELETED + " INTEGER NOT NULL DEFAULT 0" + - ");"); - - db.execSQL("CREATE INDEX images_url_index ON " + Obsolete.TABLE_IMAGES + "(" - + Obsolete.Images.URL + ")"); - db.execSQL("CREATE UNIQUE INDEX images_guid_index ON " + Obsolete.TABLE_IMAGES + "(" - + Obsolete.Images.GUID + ")"); - db.execSQL("CREATE INDEX images_modified_index ON " + Obsolete.TABLE_IMAGES + "(" - + Obsolete.Images.DATE_MODIFIED + ")"); + db.execSQL("CREATE INDEX history_url_index ON " + TABLE_HISTORY + '(' + + History.URL + ')'); + db.execSQL("CREATE UNIQUE INDEX history_guid_index ON " + TABLE_HISTORY + '(' + + History.GUID + ')'); + db.execSQL("CREATE INDEX history_modified_index ON " + TABLE_HISTORY + '(' + + History.DATE_MODIFIED + ')'); + db.execSQL("CREATE INDEX history_visited_index ON " + TABLE_HISTORY + '(' + + History.DATE_LAST_VISITED + ')'); } private void createFaviconsTable(SQLiteDatabase db) { @@ -233,15 +153,6 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { + Thumbnails.URL + ")"); } - private void createBookmarksWithImagesView(SQLiteDatabase db) { - debug("Creating " + Obsolete.VIEW_BOOKMARKS_WITH_IMAGES + " view"); - - db.execSQL("CREATE VIEW IF NOT EXISTS " + Obsolete.VIEW_BOOKMARKS_WITH_IMAGES + " AS " + - "SELECT " + qualifyColumn(TABLE_BOOKMARKS, "*") + - ", " + Obsolete.Images.FAVICON + ", " + Obsolete.Images.THUMBNAIL + " FROM " + - Obsolete.TABLE_BOOKMARKS_JOIN_IMAGES); - } - private void createBookmarksWithFaviconsView(SQLiteDatabase db) { debug("Creating " + VIEW_BOOKMARKS_WITH_FAVICONS + " view"); @@ -252,15 +163,6 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { " FROM " + TABLE_BOOKMARKS_JOIN_FAVICONS); } - private void createHistoryWithImagesView(SQLiteDatabase db) { - debug("Creating " + Obsolete.VIEW_HISTORY_WITH_IMAGES + " view"); - - db.execSQL("CREATE VIEW IF NOT EXISTS " + Obsolete.VIEW_HISTORY_WITH_IMAGES + " AS " + - "SELECT " + qualifyColumn(TABLE_HISTORY, "*") + - ", " + Obsolete.Images.FAVICON + ", " + Obsolete.Images.THUMBNAIL + " FROM " + - Obsolete.TABLE_HISTORY_JOIN_IMAGES); - } - private void createHistoryWithFaviconsView(SQLiteDatabase db) { debug("Creating " + VIEW_HISTORY_WITH_FAVICONS + " view"); @@ -271,363 +173,6 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { " FROM " + TABLE_HISTORY_JOIN_FAVICONS); } - private void createCombinedWithImagesView(SQLiteDatabase db) { - debug("Creating " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " view"); - - db.execSQL("CREATE VIEW IF NOT EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " AS" + - " SELECT " + Combined.BOOKMARK_ID + ", " + - Combined.HISTORY_ID + ", " + - // We need to return an _id column because CursorAdapter requires it for its - // default implementation for the getItemId() method. However, since - // we're not using this feature in the parts of the UI using this view, - // we can just use 0 for all rows. - "0 AS " + Combined._ID + ", " + - Combined.URL + ", " + - Combined.TITLE + ", " + - Combined.VISITS + ", " + - Combined.DATE_LAST_VISITED + ", " + - qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.FAVICON) + " AS " + Combined.FAVICON + ", " + - qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.THUMBNAIL) + " AS " + Obsolete.Combined.THUMBNAIL + - " FROM (" + - // Bookmarks without history. - " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " + - "-1 AS " + Combined.HISTORY_ID + ", " + - "-1 AS " + Combined.VISITS + ", " + - "-1 AS " + Combined.DATE_LAST_VISITED + - " FROM " + TABLE_BOOKMARKS + - " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + - " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" + - " UNION ALL" + - // History with and without bookmark. - " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + - qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " + - // Prioritize bookmark titles over history titles, since the user may have - // customized the title for a bookmark. - "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " + - qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " + - qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " + - qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " + - qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + - " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS + - " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) + - " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " + - qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ")" + - ") LEFT OUTER JOIN " + Obsolete.TABLE_IMAGES + - " ON " + Combined.URL + " = " + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.URL)); - } - - private void createCombinedWithImagesViewOn9(SQLiteDatabase db) { - debug("Creating " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " view"); - - db.execSQL("CREATE VIEW IF NOT EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " AS" + - " SELECT " + Combined.BOOKMARK_ID + ", " + - Combined.HISTORY_ID + ", " + - // We need to return an _id column because CursorAdapter requires it for its - // default implementation for the getItemId() method. However, since - // we're not using this feature in the parts of the UI using this view, - // we can just use 0 for all rows. - "0 AS " + Combined._ID + ", " + - Combined.URL + ", " + - Combined.TITLE + ", " + - Combined.VISITS + ", " + - Obsolete.Combined.DISPLAY + ", " + - Combined.DATE_LAST_VISITED + ", " + - qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.FAVICON) + " AS " + Combined.FAVICON + ", " + - qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.THUMBNAIL) + " AS " + Obsolete.Combined.THUMBNAIL + - " FROM (" + - // Bookmarks without history. - " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " + - "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + - Bookmarks.FIXED_READING_LIST_ID + " THEN " + Obsolete.Combined.DISPLAY_READER + " ELSE " + - Obsolete.Combined.DISPLAY_NORMAL + " END AS " + Obsolete.Combined.DISPLAY + ", " + - "-1 AS " + Combined.HISTORY_ID + ", " + - "-1 AS " + Combined.VISITS + ", " + - "-1 AS " + Combined.DATE_LAST_VISITED + - " FROM " + TABLE_BOOKMARKS + - " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + - " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" + - " UNION ALL" + - // History with and without bookmark. - " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + - qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " + - // Prioritize bookmark titles over history titles, since the user may have - // customized the title for a bookmark. - "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " + - qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " + - "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + - Bookmarks.FIXED_READING_LIST_ID + " THEN " + Obsolete.Combined.DISPLAY_READER + " ELSE " + - Obsolete.Combined.DISPLAY_NORMAL + " END AS " + Obsolete.Combined.DISPLAY + ", " + - qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " + - qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " + - qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + - " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS + - " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) + - " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " + - qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ")" + - ") LEFT OUTER JOIN " + Obsolete.TABLE_IMAGES + - " ON " + Combined.URL + " = " + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.URL)); - } - - private void createCombinedWithImagesViewOn10(SQLiteDatabase db) { - debug("Creating " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " view"); - - db.execSQL("CREATE VIEW IF NOT EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " AS" + - " SELECT " + Combined.BOOKMARK_ID + ", " + - Combined.HISTORY_ID + ", " + - // We need to return an _id column because CursorAdapter requires it for its - // default implementation for the getItemId() method. However, since - // we're not using this feature in the parts of the UI using this view, - // we can just use 0 for all rows. - "0 AS " + Combined._ID + ", " + - Combined.URL + ", " + - Combined.TITLE + ", " + - Combined.VISITS + ", " + - Obsolete.Combined.DISPLAY + ", " + - Combined.DATE_LAST_VISITED + ", " + - qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.FAVICON) + " AS " + Combined.FAVICON + ", " + - qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.THUMBNAIL) + " AS " + Obsolete.Combined.THUMBNAIL + - " FROM (" + - // Bookmarks without history. - " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " + - "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + - Bookmarks.FIXED_READING_LIST_ID + " THEN " + Obsolete.Combined.DISPLAY_READER + " ELSE " + - Obsolete.Combined.DISPLAY_NORMAL + " END AS " + Obsolete.Combined.DISPLAY + ", " + - "-1 AS " + Combined.HISTORY_ID + ", " + - "-1 AS " + Combined.VISITS + ", " + - "-1 AS " + Combined.DATE_LAST_VISITED + - " FROM " + TABLE_BOOKMARKS + - " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + - " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" + - " UNION ALL" + - // History with and without bookmark. - " SELECT " + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " ELSE NULL END AS " + Combined.BOOKMARK_ID + ", " + - qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " + - // Prioritize bookmark titles over history titles, since the user may have - // customized the title for a bookmark. - "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " + - qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " + - "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + - Bookmarks.FIXED_READING_LIST_ID + " THEN " + Obsolete.Combined.DISPLAY_READER + " ELSE " + - Obsolete.Combined.DISPLAY_NORMAL + " END AS " + Obsolete.Combined.DISPLAY + ", " + - qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " + - qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " + - qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + - " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS + - " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) + - " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " + - qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ")" + - ") LEFT OUTER JOIN " + Obsolete.TABLE_IMAGES + - " ON " + Combined.URL + " = " + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.URL)); - } - - private void createCombinedWithImagesViewOn11(SQLiteDatabase db) { - debug("Creating " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " view"); - - db.execSQL("CREATE VIEW IF NOT EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " AS" + - " SELECT " + Combined.BOOKMARK_ID + ", " + - Combined.HISTORY_ID + ", " + - // We need to return an _id column because CursorAdapter requires it for its - // default implementation for the getItemId() method. However, since - // we're not using this feature in the parts of the UI using this view, - // we can just use 0 for all rows. - "0 AS " + Combined._ID + ", " + - Combined.URL + ", " + - Combined.TITLE + ", " + - Combined.VISITS + ", " + - Obsolete.Combined.DISPLAY + ", " + - Combined.DATE_LAST_VISITED + ", " + - qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.FAVICON) + " AS " + Combined.FAVICON + ", " + - qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.THUMBNAIL) + " AS " + Obsolete.Combined.THUMBNAIL + - " FROM (" + - // Bookmarks without history. - " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " + - "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + - Bookmarks.FIXED_READING_LIST_ID + " THEN " + Obsolete.Combined.DISPLAY_READER + " ELSE " + - Obsolete.Combined.DISPLAY_NORMAL + " END AS " + Obsolete.Combined.DISPLAY + ", " + - "-1 AS " + Combined.HISTORY_ID + ", " + - "-1 AS " + Combined.VISITS + ", " + - "-1 AS " + Combined.DATE_LAST_VISITED + - " FROM " + TABLE_BOOKMARKS + - " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + - " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" + - " UNION ALL" + - // History with and without bookmark. - " SELECT " + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " ELSE NULL END AS " + Combined.BOOKMARK_ID + ", " + - qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " + - // Prioritize bookmark titles over history titles, since the user may have - // customized the title for a bookmark. - "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " + - qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " + - qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " + - qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " + - qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + - " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS + - " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) + - " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " + - qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ") " + - ") LEFT OUTER JOIN " + Obsolete.TABLE_IMAGES + - " ON " + Combined.URL + " = " + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.URL)); - } - - private void createCombinedViewOn12(SQLiteDatabase db) { - debug("Creating " + VIEW_COMBINED + " view"); - - db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_COMBINED + " AS" + - " SELECT " + Combined.BOOKMARK_ID + ", " + - Combined.HISTORY_ID + ", " + - // We need to return an _id column because CursorAdapter requires it for its - // default implementation for the getItemId() method. However, since - // we're not using this feature in the parts of the UI using this view, - // we can just use 0 for all rows. - "0 AS " + Combined._ID + ", " + - Combined.URL + ", " + - Combined.TITLE + ", " + - Combined.VISITS + ", " + - Obsolete.Combined.DISPLAY + ", " + - Combined.DATE_LAST_VISITED + - " FROM (" + - // Bookmarks without history. - " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " + - "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + - Bookmarks.FIXED_READING_LIST_ID + " THEN " + Obsolete.Combined.DISPLAY_READER + " ELSE " + - Obsolete.Combined.DISPLAY_NORMAL + " END AS " + Obsolete.Combined.DISPLAY + ", " + - "-1 AS " + Combined.HISTORY_ID + ", " + - "-1 AS " + Combined.VISITS + ", " + - "-1 AS " + Combined.DATE_LAST_VISITED + - " FROM " + TABLE_BOOKMARKS + - " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + - " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" + - " UNION ALL" + - // History with and without bookmark. - " SELECT " + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " ELSE NULL END AS " + Combined.BOOKMARK_ID + ", " + - qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " + - // Prioritize bookmark titles over history titles, since the user may have - // customized the title for a bookmark. - "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " + - qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " + - qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " + - qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " + - qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + - " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS + - " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) + - " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " + - qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ") " + - ")"); - - debug("Creating " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " view"); - - db.execSQL("CREATE VIEW IF NOT EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES + " AS" + - " SELECT *, " + - qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.FAVICON) + " AS " + Combined.FAVICON + ", " + - qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.THUMBNAIL) + " AS " + Obsolete.Combined.THUMBNAIL + - " FROM " + VIEW_COMBINED + " LEFT OUTER JOIN " + Obsolete.TABLE_IMAGES + - " ON " + Combined.URL + " = " + qualifyColumn(Obsolete.TABLE_IMAGES, Obsolete.Images.URL)); - } - - private void createCombinedViewOn13(SQLiteDatabase db) { - debug("Creating " + VIEW_COMBINED + " view"); - - db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_COMBINED + " AS" + - " SELECT " + Combined.BOOKMARK_ID + ", " + - Combined.HISTORY_ID + ", " + - // We need to return an _id column because CursorAdapter requires it for its - // default implementation for the getItemId() method. However, since - // we're not using this feature in the parts of the UI using this view, - // we can just use 0 for all rows. - "0 AS " + Combined._ID + ", " + - Combined.URL + ", " + - Combined.TITLE + ", " + - Combined.VISITS + ", " + - Obsolete.Combined.DISPLAY + ", " + - Combined.DATE_LAST_VISITED + ", " + - Combined.FAVICON_ID + - " FROM (" + - // Bookmarks without history. - " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " + - "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + - Bookmarks.FIXED_READING_LIST_ID + " THEN " + Obsolete.Combined.DISPLAY_READER + " ELSE " + - Obsolete.Combined.DISPLAY_NORMAL + " END AS " + Obsolete.Combined.DISPLAY + ", " + - "-1 AS " + Combined.HISTORY_ID + ", " + - "-1 AS " + Combined.VISITS + ", " + - "-1 AS " + Combined.DATE_LAST_VISITED + ", " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.FAVICON_ID) + " AS " + Combined.FAVICON_ID + - " FROM " + TABLE_BOOKMARKS + - " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + - " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" + - " UNION ALL" + - // History with and without bookmark. - " SELECT " + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " ELSE NULL END AS " + Combined.BOOKMARK_ID + ", " + - qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " + - // Prioritize bookmark titles over history titles, since the user may have - // customized the title for a bookmark. - "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " + - qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " + - // Only use DISPLAY_READER if the matching bookmark entry inside reading - // list folder is not marked as deleted. - "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN CASE " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + Bookmarks.FIXED_READING_LIST_ID + - " THEN " + Obsolete.Combined.DISPLAY_READER + " ELSE " + Obsolete.Combined.DISPLAY_NORMAL + " END ELSE " + - Obsolete.Combined.DISPLAY_NORMAL + " END AS " + Obsolete.Combined.DISPLAY + ", " + - qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " + - qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " + - qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED + ", " + - qualifyColumn(TABLE_HISTORY, History.FAVICON_ID) + " AS " + Combined.FAVICON_ID + - " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS + - " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) + - " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " + - qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " + - qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ") " + - ")"); - - debug("Creating " + VIEW_COMBINED_WITH_FAVICONS + " view"); - - db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_COMBINED_WITH_FAVICONS + " AS" + - " SELECT " + qualifyColumn(VIEW_COMBINED, "*") + ", " + - qualifyColumn(TABLE_FAVICONS, Favicons.URL) + " AS " + Combined.FAVICON_URL + ", " + - qualifyColumn(TABLE_FAVICONS, Favicons.DATA) + " AS " + Combined.FAVICON + - " FROM " + VIEW_COMBINED + " LEFT OUTER JOIN " + TABLE_FAVICONS + - " ON " + Combined.FAVICON_ID + " = " + qualifyColumn(TABLE_FAVICONS, Favicons._ID)); - } - private void createCombinedViewOn19(SQLiteDatabase db) { /* The v19 combined view removes the redundant subquery from the v16 @@ -725,6 +270,7 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { qualifyColumn(TABLE_FAVICONS, Favicons.DATA) + " AS " + Combined.FAVICON + " FROM " + VIEW_COMBINED + " LEFT OUTER JOIN " + TABLE_FAVICONS + " ON " + Combined.FAVICON_ID + " = " + qualifyColumn(TABLE_FAVICONS, Favicons._ID)); + } @Override @@ -735,8 +281,8 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { table.onCreate(db); } - createBookmarksTableOn13(db); - createHistoryTableOn13(db); + createBookmarksTable(db); + createHistoryTable(db); createFaviconsTable(db); createThumbnailsTable(db); @@ -777,7 +323,7 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { ReadingListItems.IS_DELETED + " TINYINT DEFAULT 0, " + ReadingListItems.GUID + " TEXT UNIQUE NOT NULL, " + ReadingListItems.DATE_MODIFIED + " INTEGER NOT NULL, " + - ReadingListItems.DATE_CREATED + " INTEGER NOT NULL, " + + ReadingListItems.DATE_CREATED + " INTEGER NOT NULL, " + ReadingListItems.LENGTH + " INTEGER DEFAULT 0 ); "); db.execSQL("CREATE INDEX reading_list_url ON " + TABLE_READING_LIST + "(" @@ -797,10 +343,8 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { R.string.bookmarks_folder_tags, 3); createOrUpdateSpecialFolder(db, Bookmarks.UNFILED_FOLDER_GUID, R.string.bookmarks_folder_unfiled, 4); - createOrUpdateSpecialFolder(db, Bookmarks.READING_LIST_FOLDER_GUID, - R.string.bookmarks_folder_reading_list, 5); createOrUpdateSpecialFolder(db, Bookmarks.PINNED_FOLDER_GUID, - R.string.bookmarks_folder_pinned, 6); + R.string.bookmarks_folder_pinned, 5); } private void createOrUpdateSpecialFolder(SQLiteDatabase db, @@ -810,12 +354,11 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { values.put(Bookmarks.TYPE, Bookmarks.TYPE_FOLDER); values.put(Bookmarks.POSITION, position); - if (guid.equals(Bookmarks.PLACES_FOLDER_GUID)) + if (guid.equals(Bookmarks.PLACES_FOLDER_GUID)) { values.put(Bookmarks._ID, Bookmarks.FIXED_ROOT_ID); - else if (guid.equals(Bookmarks.READING_LIST_FOLDER_GUID)) - values.put(Bookmarks._ID, Bookmarks.FIXED_READING_LIST_ID); - else if (guid.equals(Bookmarks.PINNED_FOLDER_GUID)) + } else if (guid.equals(Bookmarks.PINNED_FOLDER_GUID)) { values.put(Bookmarks._ID, Bookmarks.FIXED_PINNED_LIST_ID); + } // Set the parent to 0, which sync assumes is the root values.put(Bookmarks.PARENT, Bookmarks.FIXED_ROOT_ID); @@ -841,8 +384,9 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { private boolean isSpecialFolder(ContentValues values) { String guid = values.getAsString(Bookmarks.GUID); - if (guid == null) + if (guid == null) { return false; + } return guid.equals(Bookmarks.MOBILE_FOLDER_GUID) || guid.equals(Bookmarks.MENU_FOLDER_GUID) || @@ -958,7 +502,6 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { " RENAME TO " + TABLE_BOOKMARKS_TMP); debug("Dropping views and indexes related to " + TABLE_BOOKMARKS); - db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_BOOKMARKS_WITH_IMAGES); db.execSQL("DROP INDEX IF EXISTS bookmarks_url_index"); db.execSQL("DROP INDEX IF EXISTS bookmarks_type_deleted_index"); @@ -966,7 +509,6 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { db.execSQL("DROP INDEX IF EXISTS bookmarks_modified_index"); createBookmarksTable(db); - createBookmarksWithImagesView(db); createOrUpdateSpecialFolder(db, Bookmarks.PLACES_FOLDER_GUID, R.string.bookmarks_folder_places, 0); @@ -981,15 +523,16 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { db.execSQL("DROP TABLE IF EXISTS " + TABLE_BOOKMARKS_TMP); } - + /** + * Migrate a history table from some old version to the newest one by creating the new table and + * copying all the data over. + */ private void migrateHistoryTable(SQLiteDatabase db) { debug("Renaming history table to " + TABLE_HISTORY_TMP); db.execSQL("ALTER TABLE " + TABLE_HISTORY + " RENAME TO " + TABLE_HISTORY_TMP); debug("Dropping views and indexes related to " + TABLE_HISTORY); - db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_HISTORY_WITH_IMAGES); - db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); db.execSQL("DROP INDEX IF EXISTS history_url_index"); db.execSQL("DROP INDEX IF EXISTS history_guid_index"); @@ -997,8 +540,6 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { db.execSQL("DROP INDEX IF EXISTS history_visited_index"); createHistoryTable(db); - createHistoryWithImagesView(db); - createCombinedWithImagesView(db); db.execSQL("INSERT INTO " + TABLE_HISTORY + " SELECT * FROM " + TABLE_HISTORY_TMP); @@ -1006,86 +547,16 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { db.execSQL("DROP TABLE IF EXISTS " + TABLE_HISTORY_TMP); } - private void migrateImagesTable(SQLiteDatabase db) { - debug("Renaming images table to " + TABLE_IMAGES_TMP); - db.execSQL("ALTER TABLE " + Obsolete.TABLE_IMAGES + - " RENAME TO " + TABLE_IMAGES_TMP); - - debug("Dropping views and indexes related to " + Obsolete.TABLE_IMAGES); - db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_HISTORY_WITH_IMAGES); - db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); - - db.execSQL("DROP INDEX IF EXISTS images_url_index"); - db.execSQL("DROP INDEX IF EXISTS images_guid_index"); - db.execSQL("DROP INDEX IF EXISTS images_modified_index"); - - createImagesTable(db); - createHistoryWithImagesView(db); - createCombinedWithImagesView(db); - - db.execSQL("INSERT INTO " + Obsolete.TABLE_IMAGES + " SELECT * FROM " + TABLE_IMAGES_TMP); - - debug("Dropping images temporary table"); - db.execSQL("DROP TABLE IF EXISTS " + TABLE_IMAGES_TMP); - } - - private void upgradeDatabaseFrom1to2(SQLiteDatabase db) { - migrateBookmarksTable(db); - } - - private void upgradeDatabaseFrom2to3(SQLiteDatabase db) { - debug("Dropping view: " + Obsolete.VIEW_BOOKMARKS_WITH_IMAGES); - db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_BOOKMARKS_WITH_IMAGES); - - createBookmarksWithImagesView(db); - - debug("Dropping view: " + Obsolete.VIEW_HISTORY_WITH_IMAGES); - db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_HISTORY_WITH_IMAGES); - - createHistoryWithImagesView(db); - } - private void upgradeDatabaseFrom3to4(SQLiteDatabase db) { migrateBookmarksTable(db, new BookmarkMigrator3to4()); } - private void upgradeDatabaseFrom4to5(SQLiteDatabase db) { - createCombinedWithImagesView(db); - } - - private void upgradeDatabaseFrom5to6(SQLiteDatabase db) { - debug("Dropping view: " + Obsolete.VIEW_COMBINED_WITH_IMAGES); - db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); - - createCombinedWithImagesView(db); - } - private void upgradeDatabaseFrom6to7(SQLiteDatabase db) { debug("Removing history visits with NULL GUIDs"); db.execSQL("DELETE FROM " + TABLE_HISTORY + " WHERE " + History.GUID + " IS NULL"); - debug("Update images with NULL GUIDs"); - String[] columns = new String[] { Obsolete.Images._ID }; - Cursor cursor = null; - try { - cursor = db.query(Obsolete.TABLE_IMAGES, columns, Obsolete.Images.GUID + " IS NULL", null, null ,null, null, null); - ContentValues values = new ContentValues(); - if (cursor.moveToFirst()) { - do { - values.put(Obsolete.Images.GUID, Utils.generateGuid()); - db.update(Obsolete.TABLE_IMAGES, values, Obsolete.Images._ID + " = ?", new String[] { - cursor.getString(cursor.getColumnIndexOrThrow(Obsolete.Images._ID)) - }); - } while (cursor.moveToNext()); - } - } finally { - if (cursor != null) - cursor.close(); - } - migrateBookmarksTable(db); migrateHistoryTable(db); - migrateImagesTable(db); } private void upgradeDatabaseFrom7to8(SQLiteDatabase db) { @@ -1122,157 +593,38 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { db.execSQL("DROP TABLE " + TABLE_DUPES); } - private void upgradeDatabaseFrom8to9(SQLiteDatabase db) { - createOrUpdateSpecialFolder(db, Bookmarks.READING_LIST_FOLDER_GUID, - R.string.bookmarks_folder_reading_list, 5); - - debug("Dropping view: " + Obsolete.VIEW_COMBINED_WITH_IMAGES); - db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); - - createCombinedWithImagesViewOn9(db); - } - - private void upgradeDatabaseFrom9to10(SQLiteDatabase db) { - debug("Dropping view: " + Obsolete.VIEW_COMBINED_WITH_IMAGES); - db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); - - createCombinedWithImagesViewOn10(db); - } - private void upgradeDatabaseFrom10to11(SQLiteDatabase db) { - debug("Dropping view: " + Obsolete.VIEW_COMBINED_WITH_IMAGES); - db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); - db.execSQL("CREATE INDEX bookmarks_type_deleted_index ON " + TABLE_BOOKMARKS + "(" + Bookmarks.TYPE + ", " + Bookmarks.IS_DELETED + ")"); - - createCombinedWithImagesViewOn11(db); - } - - private void upgradeDatabaseFrom11to12(SQLiteDatabase db) { - debug("Dropping view: " + Obsolete.VIEW_COMBINED_WITH_IMAGES); - db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); - - createCombinedViewOn12(db); } private void upgradeDatabaseFrom12to13(SQLiteDatabase db) { - // Update images table with favicon URLs - SQLiteDatabase faviconsDb = null; - Cursor c = null; - try { - final String FAVICON_TABLE = "favicon_urls"; - final String FAVICON_URL = "favicon_url"; - final String FAVICON_PAGE = "page_url"; - - String dbPath = mContext.getDatabasePath(Obsolete.FAVICON_DB).getPath(); - faviconsDb = SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READONLY); - String[] columns = new String[] { FAVICON_URL, FAVICON_PAGE }; - c = faviconsDb.query(FAVICON_TABLE, columns, null, null, null, null, null, null); - int faviconIndex = c.getColumnIndexOrThrow(FAVICON_URL); - int pageIndex = c.getColumnIndexOrThrow(FAVICON_PAGE); - while (c.moveToNext()) { - ContentValues values = new ContentValues(1); - String faviconUrl = c.getString(faviconIndex); - String pageUrl = c.getString(pageIndex); - values.put(FAVICON_URL, faviconUrl); - db.update(Obsolete.TABLE_IMAGES, values, Obsolete.Images.URL + " = ?", new String[] { pageUrl }); - } - } catch (SQLException e) { - // If we can't read from the database for some reason, we won't - // be able to import the favicon URLs. This isn't a fatal - // error, so continue the upgrade. - Log.e(LOGTAG, "Exception importing from " + Obsolete.FAVICON_DB, e); - } finally { - if (c != null) - c.close(); - if (faviconsDb != null) - faviconsDb.close(); - } - createFaviconsTable(db); - // Import favicons into the favicons table - db.execSQL("ALTER TABLE " + TABLE_HISTORY - + " ADD COLUMN " + History.FAVICON_ID + " INTEGER"); - db.execSQL("ALTER TABLE " + TABLE_BOOKMARKS - + " ADD COLUMN " + Bookmarks.FAVICON_ID + " INTEGER"); - + // Add favicon_id column to the history/bookmarks tables. We wrap this in a try-catch + // because the column *may* already exist at this point (depending on how many upgrade + // steps have been performed in this operation). In which case these queries will throw, + // but we don't care. try { - c = db.query(Obsolete.TABLE_IMAGES, - new String[] { - Obsolete.Images.URL, - Obsolete.Images.FAVICON_URL, - Obsolete.Images.FAVICON, - Obsolete.Images.DATE_MODIFIED, - Obsolete.Images.DATE_CREATED - }, - Obsolete.Images.FAVICON + " IS NOT NULL", - null, null, null, null); - - while (c.moveToNext()) { - long faviconId = -1; - int faviconUrlIndex = c.getColumnIndexOrThrow(Obsolete.Images.FAVICON_URL); - String faviconUrl = null; - if (!c.isNull(faviconUrlIndex)) { - faviconUrl = c.getString(faviconUrlIndex); - Cursor c2 = null; - try { - c2 = db.query(TABLE_FAVICONS, - new String[] { Favicons._ID }, - Favicons.URL + " = ?", - new String[] { faviconUrl }, - null, null, null); - if (c2.moveToFirst()) { - faviconId = c2.getLong(c2.getColumnIndexOrThrow(Favicons._ID)); - } - } finally { - if (c2 != null) - c2.close(); - } - } - - if (faviconId == -1) { - ContentValues values = new ContentValues(4); - values.put(Favicons.URL, faviconUrl); - values.put(Favicons.DATA, c.getBlob(c.getColumnIndexOrThrow(Obsolete.Images.FAVICON))); - values.put(Favicons.DATE_MODIFIED, c.getLong(c.getColumnIndexOrThrow(Obsolete.Images.DATE_MODIFIED))); - values.put(Favicons.DATE_CREATED, c.getLong(c.getColumnIndexOrThrow(Obsolete.Images.DATE_CREATED))); - faviconId = db.insert(TABLE_FAVICONS, null, values); - } - - ContentValues values = new ContentValues(1); - values.put(FaviconColumns.FAVICON_ID, faviconId); - db.update(TABLE_HISTORY, values, History.URL + " = ?", - new String[] { c.getString(c.getColumnIndexOrThrow(Obsolete.Images.URL)) }); - db.update(TABLE_BOOKMARKS, values, Bookmarks.URL + " = ?", - new String[] { c.getString(c.getColumnIndexOrThrow(Obsolete.Images.URL)) }); - } - } finally { - if (c != null) - c.close(); + db.execSQL("ALTER TABLE " + TABLE_HISTORY + + " ADD COLUMN " + History.FAVICON_ID + " INTEGER"); + db.execSQL("ALTER TABLE " + TABLE_BOOKMARKS + + " ADD COLUMN " + Bookmarks.FAVICON_ID + " INTEGER"); + } catch (SQLException e) { + // Don't care. + debug("Exception adding favicon_id column. We're probably fine." + e); } createThumbnailsTable(db); - // Import thumbnails into the thumbnails table - db.execSQL("INSERT INTO " + TABLE_THUMBNAILS + " (" - + Thumbnails.URL + ", " - + Thumbnails.DATA + ") " - + "SELECT " + Obsolete.Images.URL + ", " + Obsolete.Images.THUMBNAIL - + " FROM " + Obsolete.TABLE_IMAGES - + " WHERE " + Obsolete.Images.THUMBNAIL + " IS NOT NULL"); - - db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_BOOKMARKS_WITH_IMAGES); - db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_HISTORY_WITH_IMAGES); - db.execSQL("DROP VIEW IF EXISTS " + Obsolete.VIEW_COMBINED_WITH_IMAGES); - db.execSQL("DROP VIEW IF EXISTS " + VIEW_COMBINED); + db.execSQL("DROP VIEW IF EXISTS bookmarks_with_images"); + db.execSQL("DROP VIEW IF EXISTS history_with_images"); + db.execSQL("DROP VIEW IF EXISTS combined_with_images"); createBookmarksWithFaviconsView(db); createHistoryWithFaviconsView(db); - createCombinedViewOn13(db); - db.execSQL("DROP TABLE IF EXISTS " + Obsolete.TABLE_IMAGES); + db.execSQL("DROP TABLE IF EXISTS images"); } private void upgradeDatabaseFrom13to14(SQLiteDatabase db) { @@ -1425,26 +777,10 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { // database schema version. for (int v = oldVersion + 1; v <= newVersion; v++) { switch(v) { - case 2: - upgradeDatabaseFrom1to2(db); - break; - - case 3: - upgradeDatabaseFrom2to3(db); - break; - case 4: upgradeDatabaseFrom3to4(db); break; - case 5: - upgradeDatabaseFrom4to5(db); - break; - - case 6: - upgradeDatabaseFrom5to6(db); - break; - case 7: upgradeDatabaseFrom6to7(db); break; @@ -1453,22 +789,10 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { upgradeDatabaseFrom7to8(db); break; - case 9: - upgradeDatabaseFrom8to9(db); - break; - - case 10: - upgradeDatabaseFrom9to10(db); - break; - case 11: upgradeDatabaseFrom10to11(db); break; - case 12: - upgradeDatabaseFrom11to12(db); - break; - case 13: upgradeDatabaseFrom12to13(db); break; @@ -1507,14 +831,12 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { table.onUpgrade(db, oldVersion, newVersion); } - // If an upgrade after 12->13 fails, the entire upgrade is rolled - // back, but we can't undo the deletion of favicon_urls.db if we - // delete this in step 13; therefore, we wait until all steps are - // complete before removing it. - if (oldVersion < 13 && newVersion >= 13 - && mContext.getDatabasePath(Obsolete.FAVICON_DB).exists() - && !mContext.deleteDatabase(Obsolete.FAVICON_DB)) { - throw new SQLException("Could not delete " + Obsolete.FAVICON_DB); + // Delete the obsolete favicon database after all other upgrades complete. + // This can probably equivalently be moved into upgradeDatabaseFrom12to13. + if (oldVersion < 13 && newVersion >= 13) { + if (mContext.getDatabasePath("favicon_urls.db").exists()) { + mContext.deleteDatabase("favicon_urls.db"); + } } } @@ -1559,10 +881,6 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper { } } - private static final String qualifyColumn(String table, String column) { - return DBUtils.qualifyColumn(table, column); - } - // Calculate these once, at initialization. isLoggable is too expensive to // have in-line in each log call. private static final boolean logDebug = Log.isLoggable(LOGTAG, Log.DEBUG); diff --git a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java index 7bb4e2b65a5e..fd3c35da0131 100644 --- a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java +++ b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java @@ -37,7 +37,6 @@ public class AndroidBrowserBookmarksDataAccessor extends AndroidBrowserRepositor private static final String GUID_SHOULD_TRACK = BrowserContract.SyncColumns.GUID + " NOT IN ('" + BrowserContract.Bookmarks.TAGS_FOLDER_GUID + "', '" + BrowserContract.Bookmarks.PLACES_FOLDER_GUID + "', '" + - BrowserContract.Bookmarks.READING_LIST_FOLDER_GUID + "', '" + BrowserContract.Bookmarks.PINNED_FOLDER_GUID + "')"; private static final String EXCLUDE_SPECIAL_GUIDS_WHERE_CLAUSE; diff --git a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java index 0127b06ccb0d..fb79901a144c 100644 --- a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java +++ b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java @@ -203,8 +203,6 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo */ public static boolean forbiddenGUID(final String recordGUID) { return recordGUID == null || - // Temporarily exclude reading list items (Bug 762118; re-enable in Bug 762109.) - BrowserContract.Bookmarks.READING_LIST_FOLDER_GUID.equals(recordGUID) || BrowserContract.Bookmarks.PINNED_FOLDER_GUID.equals(recordGUID) || BrowserContract.Bookmarks.PLACES_FOLDER_GUID.equals(recordGUID) || BrowserContract.Bookmarks.TAGS_FOLDER_GUID.equals(recordGUID); @@ -221,8 +219,6 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo */ public static boolean forbiddenParent(final String parentGUID) { return parentGUID == null || - // Temporarily exclude reading list items (Bug 762118; re-enable in Bug 762109.) - BrowserContract.Bookmarks.READING_LIST_FOLDER_GUID.equals(parentGUID) || BrowserContract.Bookmarks.PINNED_FOLDER_GUID.equals(parentGUID); } diff --git a/mobile/android/base/tests/testBrowserProvider.java b/mobile/android/base/tests/testBrowserProvider.java index 610eb6f44075..2d3d7061d440 100644 --- a/mobile/android/base/tests/testBrowserProvider.java +++ b/mobile/android/base/tests/testBrowserProvider.java @@ -40,7 +40,7 @@ public class testBrowserProvider extends ContentProviderTest { } private void ensureEmptyDatabase() throws Exception { - Cursor c = null; + Cursor c; String guid = BrowserContract.Bookmarks.GUID; @@ -50,18 +50,16 @@ public class testBrowserProvider extends ContentProviderTest { guid + " != ? AND " + guid + " != ? AND " + guid + " != ? AND " + - guid + " != ? AND " + guid + " != ?", new String[] { BrowserContract.Bookmarks.PLACES_FOLDER_GUID, BrowserContract.Bookmarks.MOBILE_FOLDER_GUID, BrowserContract.Bookmarks.MENU_FOLDER_GUID, BrowserContract.Bookmarks.TAGS_FOLDER_GUID, BrowserContract.Bookmarks.TOOLBAR_FOLDER_GUID, - BrowserContract.Bookmarks.UNFILED_FOLDER_GUID, - BrowserContract.Bookmarks.READING_LIST_FOLDER_GUID }); + BrowserContract.Bookmarks.UNFILED_FOLDER_GUID }); c = mProvider.query(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), null, null, null, null); - assertCountIsAndClose(c, 7, "All non-special bookmarks and folders were deleted"); + assertCountIsAndClose(c, 6, "All non-special bookmarks and folders were deleted"); mProvider.delete(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"), null, null); c = mProvider.query(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), null, null, null, null); diff --git a/mobile/android/tests/background/junit3/src/db/TestBookmarks.java b/mobile/android/tests/background/junit3/src/db/TestBookmarks.java index f3a0dec165d6..783aea1ff848 100644 --- a/mobile/android/tests/background/junit3/src/db/TestBookmarks.java +++ b/mobile/android/tests/background/junit3/src/db/TestBookmarks.java @@ -50,7 +50,7 @@ public class TestBookmarks extends AndroidSyncTestCase { protected static final String LOG_TAG = "BookmarksTest"; /** - * Trivial test that forbidden records (reading list prior to Bug 762109, pinned items…) + * Trivial test that forbidden records such as pinned items * will be ignored if processed. */ public void testForbiddenItemsAreIgnored() { @@ -58,32 +58,25 @@ public class TestBookmarks extends AndroidSyncTestCase { final long now = System.currentTimeMillis(); final String bookmarksCollection = "bookmarks"; - final BookmarkRecord toRead = new BookmarkRecord("daaaaaaaaaaa", "bookmarks", now - 1, false); final BookmarkRecord pinned = new BookmarkRecord("pinpinpinpin", "bookmarks", now - 1, false); final BookmarkRecord normal = new BookmarkRecord("baaaaaaaaaaa", "bookmarks", now - 2, false); - final BookmarkRecord readingList = new BookmarkRecord(Bookmarks.READING_LIST_FOLDER_GUID, - bookmarksCollection, now - 3, false); final BookmarkRecord pinnedItems = new BookmarkRecord(Bookmarks.PINNED_FOLDER_GUID, bookmarksCollection, now - 4, false); - toRead.type = normal.type = pinned.type = "bookmark"; - readingList.type = "folder"; + normal.type = "bookmark"; + pinned.type = "bookmark"; pinnedItems.type = "folder"; - toRead.parentID = Bookmarks.READING_LIST_FOLDER_GUID; pinned.parentID = Bookmarks.PINNED_FOLDER_GUID; normal.parentID = Bookmarks.TOOLBAR_FOLDER_GUID; - readingList.parentID = Bookmarks.PLACES_FOLDER_GUID; pinnedItems.parentID = Bookmarks.PLACES_FOLDER_GUID; inBegunSession(repo, new SimpleSuccessBeginDelegate() { @Override public void onBeginSucceeded(RepositorySession session) { - assertTrue(((AndroidBrowserBookmarksRepositorySession) session).shouldIgnore(toRead)); assertTrue(((AndroidBrowserBookmarksRepositorySession) session).shouldIgnore(pinned)); - assertTrue(((AndroidBrowserBookmarksRepositorySession) session).shouldIgnore(readingList)); assertTrue(((AndroidBrowserBookmarksRepositorySession) session).shouldIgnore(pinnedItems)); assertFalse(((AndroidBrowserBookmarksRepositorySession) session).shouldIgnore(normal)); finishAndNotify(session); @@ -115,24 +108,6 @@ public class TestBookmarks extends AndroidSyncTestCase { assertFalse(guids.contains("dapinneditem")); } - /** - * Trivial test that reading list records will be skipped if present in the DB. - */ - public void testReadingListIsNotRetrieved() { - final AndroidBrowserBookmarksRepository repo = new AndroidBrowserBookmarksRepository(); - - // Ensure that it exists. - setUpFennecReadingListRecord(); - - // It's there in the DB… - final ArrayList roots = fetchChildrenDirect(BrowserContract.Bookmarks.FIXED_ROOT_ID); - Logger.info(LOG_TAG, "Roots: " + roots); - assertTrue(roots.contains(Bookmarks.READING_LIST_FOLDER_GUID)); - - // … but not when we fetch. - assertFalse(fetchGUIDs(repo).contains(Bookmarks.READING_LIST_FOLDER_GUID)); - } - public void testRetrieveFolderHasAccurateChildren() { AndroidBrowserBookmarksRepository repo = new AndroidBrowserBookmarksRepository(); @@ -864,17 +839,6 @@ public class TestBookmarks extends AndroidSyncTestCase { return values; } - protected ContentValues fennecReadingListRecord() { - final ContentValues values = specialFolder(); - final String title = getApplicationContext().getResources().getString(R.string.bookmarks_folder_reading_list); - - values.put(BrowserContract.SyncColumns.GUID, Bookmarks.READING_LIST_FOLDER_GUID); - values.put(Bookmarks._ID, Bookmarks.FIXED_READING_LIST_ID); - values.put(Bookmarks.PARENT, Bookmarks.FIXED_ROOT_ID); - values.put(Bookmarks.TITLE, title); - return values; - } - protected long setUpFennecMobileRecordWithoutTitle() { ContentResolver cr = getApplicationContext().getContentResolver(); ContentValues values = fennecMobileRecordWithoutTitle(); @@ -897,10 +861,6 @@ public class TestBookmarks extends AndroidSyncTestCase { insertRow(fennecPinnedChildItemRecord()); } - protected void setUpFennecReadingListRecord() { - insertRow(fennecReadingListRecord()); - } - // // Fennec fake layer. // diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 257a9a754ada..28ffc944d0ed 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -4509,6 +4509,12 @@ "n_values": 100, "description": "Security UI Telemetry" }, + "JS_TELEMETRY_ADDON_EXCEPTIONS" : { + "expires_in_version" : "never", + "kind": "count", + "keyed" : true, + "description" : "Exceptions thrown by add-ons" + }, "SEARCH_COUNTS": { "expires_in_version": "never", "kind": "count", diff --git a/toolkit/devtools/server/actors/script.js b/toolkit/devtools/server/actors/script.js index cf3ecb722ca2..1a95a77f8d8c 100644 --- a/toolkit/devtools/server/actors/script.js +++ b/toolkit/devtools/server/actors/script.js @@ -107,7 +107,7 @@ BreakpointStore.prototype = { * @returns Object aBreakpoint * The new or existing breakpoint. */ - addBreakpoint: function (aBreakpoint) { + addBreakpoint: function (aBreakpoint, aActor) { let { source: { actor }, line, column } = aBreakpoint; if (column != null) { @@ -119,7 +119,7 @@ BreakpointStore.prototype = { } if (!this._breakpoints[actor][line][column]) { - this._breakpoints[actor][line][column] = aBreakpoint; + this._breakpoints[actor][line][column] = aActor; this._size++; } return this._breakpoints[actor][line][column]; @@ -130,7 +130,7 @@ BreakpointStore.prototype = { } if (!this._wholeLineBreakpoints[actor][line]) { - this._wholeLineBreakpoints[actor][line] = aBreakpoint; + this._wholeLineBreakpoints[actor][line] = aActor; this._size++; } return this._wholeLineBreakpoints[actor][line]; @@ -178,53 +178,6 @@ BreakpointStore.prototype = { } }, - /** - * Move the breakpoint to the new location. - * - * @param Object aBreakpoint - * The breakpoint being moved. See `addBreakpoint` for a description of - * its expected properties. - * @param Object aNewLocation - * The location to move the breakpoint to. Properties: - * - line - * - column (optional; omission implies whole line breakpoint) - */ - moveBreakpoint: function (aBreakpoint, aNewLocation) { - const existingBreakpoint = this.getBreakpoint(aBreakpoint); - this.removeBreakpoint(existingBreakpoint); - - const { line, column } = aNewLocation; - existingBreakpoint.line = line; - existingBreakpoint.column = column; - this.addBreakpoint(existingBreakpoint); - }, - - /** - * Get a breakpoint from the breakpoint store. Will throw an error if the - * breakpoint is not found. - * - * @param Object aLocation - * The location of the breakpoint you are retrieving. It is an object - * with the following properties: - * - source - * - line - * - column (optional) - */ - getBreakpoint: function (aLocation) { - let { source: { actor, url }, line, column } = aLocation; - dbg_assert(actor != null); - dbg_assert(line != null); - - var foundBreakpoint = this.hasBreakpoint(aLocation); - if (foundBreakpoint == null) { - throw new Error("No breakpoint at = " + (url || actor) - + ", line = " + line - + ", column = " + column); - } - - return foundBreakpoint; - }, - /** * Checks if the breakpoint store has a requested breakpoint. * @@ -236,16 +189,16 @@ BreakpointStore.prototype = { * - column (optional) * @returns The stored breakpoint if it exists, null otherwise. */ - hasBreakpoint: function (aLocation) { + getBreakpoint: function (aLocation) { let { source: { actor }, line, column } = aLocation; dbg_assert(actor != null); dbg_assert(line != null); - for (let bp of this.findBreakpoints(aLocation)) { + for (let actor of this.findBreakpoints(aLocation)) { // We will get whole line breakpoints before individual columns, so just // return the first one and if they didn't specify a column then they will // get the whole line breakpoint, and otherwise we will find the correct // one. - return bp; + return actor; } return null; @@ -275,7 +228,7 @@ BreakpointStore.prototype = { for (let actor of this._iterActors(actor)) { for (let line of this._iterLines(actor, aSearchParams.line)) { // Always yield whole line breakpoints first. See comment in - // |BreakpointStore.prototype.hasBreakpoint|. + // |BreakpointStore.prototype.getBreakpoint|. if (aSearchParams.column == null && this._wholeLineBreakpoints[actor] && this._wholeLineBreakpoints[actor][line]) { @@ -1330,18 +1283,16 @@ ThreadActor.prototype = { for (let line = 0, n = offsets.length; line < n; line++) { if (offsets[line]) { let location = { line: line }; - let resp = sourceActor.createAndStoreBreakpoint(location); + let resp = sourceActor._setBreakpoint(location); dbg_assert(!resp.actualLocation, "No actualLocation should be returned"); if (resp.error) { reportError(new Error("Unable to set breakpoint on event listener")); return; } - let bp = this.breakpointStore.getBreakpoint({ + let bpActor = this.breakpointStore.getBreakpoint({ source: sourceActor.form(), line: location.line }); - let bpActor = bp.actor; - dbg_assert(bp, "Breakpoint must exist"); dbg_assert(bpActor, "Breakpoint actor must be created"); this._hiddenBreakpoints.set(bpActor.actorID, bpActor); break; @@ -1496,10 +1447,8 @@ ThreadActor.prototype = { * caches won't hold on to the Debugger.Script objects leaking memory. */ disableAllBreakpoints: function () { - for (let bp of this.breakpointStore.findBreakpoints()) { - if (bp.actor) { - bp.actor.removeScripts(); - } + for (let bpActor of this.breakpointStore.findBreakpoints()) { + bpActor.removeScripts(); } }, @@ -2153,11 +2102,11 @@ ThreadActor.prototype = { let endLine = aScript.startLine + aScript.lineCount - 1; let source = this.sources.source({ source: aScript.source }); - for (let bp of this.breakpointStore.findBreakpoints(source.form())) { + for (let bpActor of this.breakpointStore.findBreakpoints({ source: source.form() })) { // Limit the search to the line numbers contained in the new script. - if (bp.line >= aScript.startLine - && bp.line <= endLine) { - source._setBreakpoint(bp, aScript); + if (bpActor.location.line >= aScript.startLine + && bpActor.location.line <= endLine) { + source._setBreakpoint(bpActor.location, aScript); } } @@ -2841,7 +2790,7 @@ SourceActor.prototype = { _createBreakpoint: function(loc, originalLoc, condition) { return resolve(null).then(() => { - return this.createAndStoreBreakpoint({ + return this._setBreakpoint({ line: loc.line, column: loc.column, condition: condition @@ -2904,23 +2853,6 @@ SourceActor.prototype = { }); }, - /** - * Create a breakpoint at the specified location and store it in the - * cache. Takes ownership of `aRequest`. This is the - * generated location if this source is sourcemapped. - * Used by the XPCShell test harness to set breakpoints in a script before - * it has loaded. - * - * @param Object aRequest - * An object of the form { line[, column, condition] }. The - * location is in the generated source, if sourcemapped. - */ - createAndStoreBreakpoint: function (aRequest) { - let bp = update({}, aRequest, { source: this.form() }); - this.breakpointStore.addBreakpoint(bp); - return this._setBreakpoint(aRequest); - }, - /** Get or create the BreakpointActor for the breakpoint at the given location. * * NB: This will override a pre-existing BreakpointActor's condition with @@ -2932,22 +2864,20 @@ SourceActor.prototype = { * @returns BreakpointActor */ _getOrCreateBreakpointActor: function (location) { - let actor; - const storedBp = this.breakpointStore.getBreakpoint(location); - - if (storedBp.actor) { - actor = storedBp.actor; - actor.condition = location.condition; + let actor = this.breakpointStore.getBreakpoint(location); + if (!actor) { + actor = new BreakpointActor(this.threadActor, { + sourceActor: this, + line: location.line, + column: location.column, + condition: location.condition + }); + this.threadActor.threadLifetimePool.addActor(actor); + this.breakpointStore.addBreakpoint(location, actor); return actor; } - storedBp.actor = actor = new BreakpointActor(this.threadActor, { - sourceActor: this, - line: location.line, - column: location.column, - condition: location.condition - }); - this.threadActor.threadLifetimePool.addActor(actor); + actor.condition = location.condition; return actor; }, @@ -3112,12 +3042,12 @@ SourceActor.prototype = { // above is redundant and must be destroyed. If we do not have an existing // actor, we need to update the breakpoint store with the new location. - const existingBreakpoint = this.breakpointStore.hasBreakpoint(actualLocation); - if (existingBreakpoint && existingBreakpoint.actor) { + let existingActor = this.breakpointStore.getBreakpoint(actualLocation); + if (existingActor) { actor.onDelete(); this.breakpointStore.removeBreakpoint(location); return { - actor: existingBreakpoint.actor.actorID, + actor: existingActor.actorID, actualLocation }; } else { @@ -3126,7 +3056,8 @@ SourceActor.prototype = { sourceActor: this, line: actualLocation.line }; - this.breakpointStore.moveBreakpoint(location, actualLocation); + this.breakpointStore.removeBreakpoint(location); + this.breakpointStore.addBreakpoint(actualLocation, actor); } } diff --git a/toolkit/devtools/server/tests/unit/test_breakpointstore.js b/toolkit/devtools/server/tests/unit/test_breakpointstore.js index 1e4ed102e17a..40eeb7e5000b 100644 --- a/toolkit/devtools/server/tests/unit/test_breakpointstore.js +++ b/toolkit/devtools/server/tests/unit/test_breakpointstore.js @@ -11,15 +11,14 @@ function run_test() Cu.import("resource://gre/modules/jsdebugger.jsm"); addDebuggerToGlobal(this); - test_has_breakpoint(); + test_get_breakpoint(); test_add_breakpoint(); test_remove_breakpoint(); test_find_breakpoints(); test_duplicate_breakpoints(); - test_move_breakpoint(); } -function test_has_breakpoint() { +function test_get_breakpoint() { let bpStore = new BreakpointStore(); let location = { source: { actor: 'actor1' }, @@ -32,27 +31,27 @@ function test_has_breakpoint() { }; // Shouldn't have breakpoint - do_check_eq(null, bpStore.hasBreakpoint(location), + do_check_eq(null, bpStore.getBreakpoint(location), "Breakpoint not added and shouldn't exist."); - bpStore.addBreakpoint(location); - do_check_true(!!bpStore.hasBreakpoint(location), + bpStore.addBreakpoint(location, {}); + do_check_true(!!bpStore.getBreakpoint(location), "Breakpoint added but not found in Breakpoint Store."); bpStore.removeBreakpoint(location); - do_check_eq(null, bpStore.hasBreakpoint(location), + do_check_eq(null, bpStore.getBreakpoint(location), "Breakpoint removed but still exists."); // Same checks for breakpoint with a column - do_check_eq(null, bpStore.hasBreakpoint(columnLocation), + do_check_eq(null, bpStore.getBreakpoint(columnLocation), "Breakpoint with column not added and shouldn't exist."); - bpStore.addBreakpoint(columnLocation); - do_check_true(!!bpStore.hasBreakpoint(columnLocation), + bpStore.addBreakpoint(columnLocation, {}); + do_check_true(!!bpStore.getBreakpoint(columnLocation), "Breakpoint with column added but not found in Breakpoint Store."); bpStore.removeBreakpoint(columnLocation); - do_check_eq(null, bpStore.hasBreakpoint(columnLocation), + do_check_eq(null, bpStore.getBreakpoint(columnLocation), "Breakpoint with column removed but still exists in Breakpoint Store."); } @@ -64,8 +63,8 @@ function test_add_breakpoint() { line: 10, column: 9 }; - bpStore.addBreakpoint(location); - do_check_true(!!bpStore.hasBreakpoint(location), + bpStore.addBreakpoint(location, {}); + do_check_true(!!bpStore.getBreakpoint(location), "We should have the column breakpoint we just added"); // Breakpoint without column (whole line breakpoint) @@ -73,8 +72,8 @@ function test_add_breakpoint() { source: { actor: 'actor2' }, line: 103 }; - bpStore.addBreakpoint(location); - do_check_true(!!bpStore.hasBreakpoint(location), + bpStore.addBreakpoint(location, {}); + do_check_true(!!bpStore.getBreakpoint(location), "We should have the whole line breakpoint we just added"); } @@ -86,9 +85,9 @@ function test_remove_breakpoint() { line: 10, column: 9 }; - bpStore.addBreakpoint(location); + bpStore.addBreakpoint(location, {}); bpStore.removeBreakpoint(location); - do_check_eq(bpStore.hasBreakpoint(location), null, + do_check_eq(bpStore.getBreakpoint(location), null, "We should not have the column breakpoint anymore"); // Breakpoint without column (whole line breakpoint) @@ -96,9 +95,9 @@ function test_remove_breakpoint() { source: { actor: 'actor2' }, line: 103 }; - bpStore.addBreakpoint(location); + bpStore.addBreakpoint(location, {}); bpStore.removeBreakpoint(location); - do_check_eq(bpStore.hasBreakpoint(location), null, + do_check_eq(bpStore.getBreakpoint(location), null, "We should not have the whole line breakpoint anymore"); } @@ -117,7 +116,7 @@ function test_find_breakpoints() { let bpStore = new BreakpointStore(); for (let bp of bps) { - bpStore.addBreakpoint(bp); + bpStore.addBreakpoint(bp, bp); } // All breakpoints @@ -166,8 +165,8 @@ function test_duplicate_breakpoints() { line: 10, column: 9 }; - bpStore.addBreakpoint(location); - bpStore.addBreakpoint(location); + bpStore.addBreakpoint(location, {}); + bpStore.addBreakpoint(location, {}); do_check_eq(bpStore.size, 1, "We should have only 1 column breakpoint"); bpStore.removeBreakpoint(location); @@ -176,36 +175,8 @@ function test_duplicate_breakpoints() { source: { actor: "foo-actor" }, line: 15 }; - bpStore.addBreakpoint(location); - bpStore.addBreakpoint(location); + bpStore.addBreakpoint(location, {}); + bpStore.addBreakpoint(location, {}); do_check_eq(bpStore.size, 1, "We should have only 1 whole line breakpoint"); bpStore.removeBreakpoint(location); } - -function test_move_breakpoint() { - let bpStore = new BreakpointStore(); - - let oldLocation = { - source: { actor: "foo-actor" }, - line: 10 - }; - - let newLocation = { - source: { actor: "foo-actor" }, - line: 12 - }; - - bpStore.addBreakpoint(oldLocation); - bpStore.moveBreakpoint(oldLocation, newLocation); - - equal(bpStore.size, 1, "Moving a breakpoint maintains the correct size."); - - let bp = bpStore.getBreakpoint(newLocation); - ok(bp, "We should be able to get a breakpoint at the new location."); - equal(bp.line, newLocation.line, - "We should get the moved line."); - - equal(bpStore.hasBreakpoint({ source: { actor: "foo-actor" }, line: 10 }), - null, - "And we shouldn't be able to get any BP at the old location."); -} diff --git a/uriloader/exthandler/tests/mochitest/handlerApps.js b/uriloader/exthandler/tests/mochitest/handlerApps.js index cbfbcca6acb7..379f04120ba9 100644 --- a/uriloader/exthandler/tests/mochitest/handlerApps.js +++ b/uriloader/exthandler/tests/mochitest/handlerApps.js @@ -37,7 +37,7 @@ function test() { getInterface(SpecialPowers.Ci.nsIWebNavigation). QueryInterface(SpecialPowers.Ci.nsIDocShell); - webHandler.launchWithURI(uri, windowContext); + webHandler.launchWithURI(uri, windowContext); // if we get this far without an exception, we've at least partly passed // (remaining check in handlerApp.xhtml)