merge fx-team to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2014-12-04 16:59:18 +01:00
Родитель d928f41d37 a68fa06fb7
Коммит cde42d544d
33 изменённых файлов: 507 добавлений и 1256 удалений

Просмотреть файл

@ -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, {

Просмотреть файл

@ -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, {

Просмотреть файл

@ -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);
},

Просмотреть файл

@ -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);
},

Просмотреть файл

@ -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() {

Просмотреть файл

@ -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() {

Просмотреть файл

@ -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;
})();

Просмотреть файл

@ -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")
)
)
);

Просмотреть файл

@ -128,6 +128,10 @@ loop.standaloneRoomViews = (function(mozL10n) {
<p className="failed-room-message">
{this._getFailureString()}
</p>
<button className="btn btn-join btn-info"
onClick={this.props.joinRoom}>
{mozL10n.get("retry_call_button")}
</button>
</div>
);
}

Просмотреть файл

@ -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);

Просмотреть файл

@ -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,
});

Просмотреть файл

@ -41,7 +41,6 @@
<!-- App scripts -->
<script src="../../content/shared/js/utils.js"></script>
<script src="../../content/shared/js/feedbackApiClient.js"></script>
<script src="../../content/shared/js/conversationStore.js"></script>
<script src="../../content/shared/js/models.js"></script>
<script src="../../content/shared/js/mixins.js"></script>
<script src="../../content/shared/js/views.js"></script>
@ -51,6 +50,7 @@
<script src="../../content/shared/js/dispatcher.js"></script>
<script src="../../content/shared/js/otSdkDriver.js"></script>
<script src="../../content/shared/js/store.js"></script>
<script src="../../content/shared/js/conversationStore.js"></script>
<script src="../../content/shared/js/roomStore.js"></script>
<script src="../../content/shared/js/activeRoomStore.js"></script>
<script src="../../content/shared/js/feedbackStore.js"></script>

Просмотреть файл

@ -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();

Просмотреть файл

@ -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",

Просмотреть файл

@ -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() {

Просмотреть файл

@ -8333,9 +8333,6 @@ Parser<ParseHandler>::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<ParseHandler>::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<FullParseHandler>;

Просмотреть файл

@ -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)

Просмотреть файл

@ -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.

Просмотреть файл

@ -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 *)

Просмотреть файл

@ -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);

Просмотреть файл

@ -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)
{

Просмотреть файл

@ -707,7 +707,16 @@ struct JSRuntime : public JS::shadow::Runtime,
private:
mozilla::Atomic<uint32_t, mozilla::Relaxed> interrupt_;
mozilla::Atomic<uint32_t, mozilla::Relaxed> 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;

Просмотреть файл

@ -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");
}

Просмотреть файл

@ -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() {}

Просмотреть файл

@ -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);

Просмотреть файл

@ -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;

Просмотреть файл

@ -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);
}

Просмотреть файл

@ -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);

Просмотреть файл

@ -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<String> 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.
//

Просмотреть файл

@ -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",

Просмотреть файл

@ -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);
}
}

Просмотреть файл

@ -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.");
}

Просмотреть файл

@ -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)