mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-24 03:19:06 +00:00
Merge fx-team to m-c a=merge
This commit is contained in:
commit
052b085df2
@ -1804,3 +1804,5 @@ pref("print.enable_e10s_testing", false);
|
||||
#else
|
||||
pref("print.enable_e10s_testing", true);
|
||||
#endif
|
||||
|
||||
pref("browser.defaultbrowser.notificationbar", false);
|
||||
|
@ -121,7 +121,7 @@ skip-if = e10s
|
||||
[browser_search_favicon.js]
|
||||
skip-if = os == "linux" || e10s # Bug 1073339 - Investigate autocomplete test unreliability on Linux/e10s
|
||||
[browser_alltabslistener.js]
|
||||
skip-if = os == "linux" || e10s # Linux: Intermittent failures, bug 951680; e10s: Bug ?????? - notifications don't work correctly.
|
||||
skip-if = os == "linux" || e10s # Linux: Intermittent failures, bug 951680; e10s: Bug 1093594 - notifications for tabs come in in the wrong order / unexpectedly
|
||||
[browser_autocomplete_a11y_label.js]
|
||||
skip-if = e10s # Bug ????? - no e10s switch-to-tab support yet
|
||||
[browser_backButtonFitts.js]
|
||||
@ -158,7 +158,7 @@ skip-if = e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
|
||||
[browser_bug423833.js]
|
||||
skip-if = true # bug 428712
|
||||
[browser_bug424101.js]
|
||||
skip-if = e10s # Bug ?????? - test directly manipulates content
|
||||
skip-if = e10s # Bug 1093155 - tries to use context menu from browser-chrome and gets in a mess when in e10s mode
|
||||
[browser_bug427559.js]
|
||||
skip-if = e10s # Bug ?????? - "content window is focused - Got [object ChromeWindow], expected [object XrayWrapper [object Window]]"
|
||||
[browser_bug431826.js]
|
||||
@ -202,7 +202,7 @@ skip-if = buildapp == 'mulet' || e10s # Bug 1093206 - need to re-enable tests re
|
||||
skip-if = e10s # Bug ?????? - test doesn't wait for document to be created before it checks it
|
||||
[browser_bug550565.js]
|
||||
[browser_bug553455.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1066070 - I don't think either popup notifications nor addon install stuff works?
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1066070 - I don't think either popup notifications nor addon install stuff works on mulet? ; for e10s, indefinite waiting halfway through the test, tracked in bug 1093586
|
||||
[browser_bug555224.js]
|
||||
skip-if = e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
|
||||
[browser_bug555767.js]
|
||||
@ -272,7 +272,7 @@ skip-if = e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
|
||||
[browser_bug724239.js]
|
||||
skip-if = e10s # Bug 1077738
|
||||
[browser_bug734076.js]
|
||||
skip-if = e10s # Bug ?????? - test directly manipulates content
|
||||
skip-if = e10s # Bug 1093155 - tries to use context menu from browser-chrome and gets in a mess when in e10s mode
|
||||
[browser_bug735471.js]
|
||||
[browser_bug749738.js]
|
||||
skip-if = e10s # Bug 921935 - focusmanager issues with e10s
|
||||
@ -294,9 +294,9 @@ skip-if = e10s # Bug 1093373 - relies on browser.sessionHistory
|
||||
[browser_bug902156.js]
|
||||
skip-if = e10s
|
||||
[browser_bug906190.js]
|
||||
skip-if = buildapp == "mulet" || e10s # Bug ?????? - test directly manipulates content (strange - gets an element from a child which it tries to treat as a string, but that fails)
|
||||
skip-if = buildapp == "mulet" || e10s # Bug 1093642 - test manipulates content and relies on content focus
|
||||
[browser_bug970746.js]
|
||||
skip-if = e10s # Bug ?????? - test directly manipulates content (directly gets elements from the content)
|
||||
skip-if = e10s # Bug 1093155 - tries to use context menu from browser-chrome and gets in a mess when in e10s mode
|
||||
[browser_bug1015721.js]
|
||||
skip-if = os == 'win' || e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
|
||||
[browser_bug1064280_changeUrlInPinnedTab.js]
|
||||
@ -363,7 +363,7 @@ skip-if = e10s # Bug ?????? - test directly manipulates content ("cannot ipc non
|
||||
[browser_notification_tab_switching.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug ?????? - uncaught exception - Error: cannot ipc non-cpow object at chrome://mochitests/content/browser/browser/base/content/test/general/browser_notification_tab_switching.js:32
|
||||
[browser_offlineQuotaNotification.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug ?????? - test directly manipulates content (gBrowser.selectedBrowser.contentWindow.applicationCache.oncached = function() {...})
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1093603 - test breaks with PopupNotifications.panel.firstElementChild is null
|
||||
[browser_overflowScroll.js]
|
||||
[browser_pageInfo.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 866413 - PageInfo doesn't work in e10s
|
||||
@ -377,7 +377,7 @@ skip-if = asan # Disabled because it takes a long time (see test for more inform
|
||||
|
||||
[browser_pinnedTabs.js]
|
||||
[browser_plainTextLinks.js]
|
||||
skip-if = e10s # Bug ?????? - test directly manipulates content (creates and fetches elements directly from content document)
|
||||
skip-if = e10s # Bug 1093155 - tries to use context menu from browser-chrome and gets in a mess when in e10s mode
|
||||
[browser_popupUI.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug ?????? - test directly manipulates content (tries to get a popup element directly from content)
|
||||
[browser_popup_blocker.js]
|
||||
@ -445,7 +445,7 @@ skip-if = e10s # Bug ?????? - test needs to be updated for e10s (captures a stac
|
||||
skip-if = e10s # Bug ?????? - test directly manipulates content (tries to get/set attributes directly on content docshell)
|
||||
[browser_tabs_owner.js]
|
||||
[browser_trackingUI.js]
|
||||
skip-if = e10s
|
||||
skip-if = e10s # Bug 1093155 - tries to use context menu from browser-chrome and gets in a mess when in e10s mode
|
||||
support-files =
|
||||
trackingPage.html
|
||||
benignPage.html
|
||||
|
@ -18,7 +18,7 @@ loop.conversation = (function(mozL10n) {
|
||||
|
||||
var OutgoingConversationView = loop.conversationViews.OutgoingConversationView;
|
||||
var CallIdentifierView = loop.conversationViews.CallIdentifierView;
|
||||
var EmptyRoomView = loop.roomViews.EmptyRoomView;
|
||||
var DesktopRoomView = loop.roomViews.DesktopRoomView;
|
||||
|
||||
var IncomingCallView = React.createClass({displayName: 'IncomingCallView',
|
||||
mixins: [sharedMixins.DropdownMenuMixin],
|
||||
@ -576,7 +576,7 @@ loop.conversation = (function(mozL10n) {
|
||||
));
|
||||
}
|
||||
case "room": {
|
||||
return (EmptyRoomView({
|
||||
return (DesktopRoomView({
|
||||
mozLoop: navigator.mozLoop,
|
||||
localRoomStore: this.props.localRoomStore}
|
||||
));
|
||||
|
@ -18,7 +18,7 @@ loop.conversation = (function(mozL10n) {
|
||||
|
||||
var OutgoingConversationView = loop.conversationViews.OutgoingConversationView;
|
||||
var CallIdentifierView = loop.conversationViews.CallIdentifierView;
|
||||
var EmptyRoomView = loop.roomViews.EmptyRoomView;
|
||||
var DesktopRoomView = loop.roomViews.DesktopRoomView;
|
||||
|
||||
var IncomingCallView = React.createClass({
|
||||
mixins: [sharedMixins.DropdownMenuMixin],
|
||||
@ -576,7 +576,7 @@ loop.conversation = (function(mozL10n) {
|
||||
/>);
|
||||
}
|
||||
case "room": {
|
||||
return (<EmptyRoomView
|
||||
return (<DesktopRoomView
|
||||
mozLoop={navigator.mozLoop}
|
||||
localRoomStore={this.props.localRoomStore}
|
||||
/>);
|
||||
|
@ -10,24 +10,8 @@ var loop = loop || {};
|
||||
loop.roomViews = (function(mozL10n) {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Root object, by default set to window.
|
||||
* @type {DOMWindow|Object}
|
||||
*/
|
||||
var rootObject = window;
|
||||
|
||||
/**
|
||||
* Sets a new root object. This is useful for testing native DOM events so we
|
||||
* can fake them.
|
||||
*
|
||||
* @param {Object}
|
||||
*/
|
||||
function setRootObject(obj) {
|
||||
rootObject = obj;
|
||||
}
|
||||
|
||||
var EmptyRoomView = React.createClass({displayName: 'EmptyRoomView',
|
||||
mixins: [Backbone.Events],
|
||||
var DesktopRoomView = React.createClass({displayName: 'DesktopRoomView',
|
||||
mixins: [Backbone.Events, loop.shared.mixins.DocumentTitleMixin],
|
||||
|
||||
propTypes: {
|
||||
mozLoop:
|
||||
@ -45,28 +29,6 @@ loop.roomViews = (function(mozL10n) {
|
||||
this._onLocalRoomStoreChanged);
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
// XXXremoveMe (just the conditional itself) in patch 2 for bug 1074686,
|
||||
// once the addCallback stuff lands
|
||||
if (this.props.mozLoop.rooms && this.props.mozLoop.rooms.addCallback) {
|
||||
this.props.mozLoop.rooms.addCallback(
|
||||
this.state.localRoomId,
|
||||
"RoomCreationError", this.onCreationError);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Attached to the "RoomCreationError" with mozLoop.rooms.addCallback,
|
||||
* which is fired mozLoop.rooms.createRoom from the panel encounters an
|
||||
* error while attempting to create the room for this view.
|
||||
*
|
||||
* @param {Error} err - JS Error object with info about the problem
|
||||
*/
|
||||
onCreationError: function(err) {
|
||||
// XXX put up a user friendly error instead of this
|
||||
rootObject.console.error("EmptyRoomView creation error: ", err);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles a "change" event on the localRoomStore, and updates this.state
|
||||
* to match the store.
|
||||
@ -79,20 +41,11 @@ loop.roomViews = (function(mozL10n) {
|
||||
|
||||
componentWillUnmount: function() {
|
||||
this.stopListening(this.props.localRoomStore);
|
||||
|
||||
// XXXremoveMe (just the conditional itself) in patch 2 for bug 1074686,
|
||||
// once the addCallback stuff lands
|
||||
if (this.props.mozLoop.rooms && this.props.mozLoop.rooms.removeCallback) {
|
||||
this.props.mozLoop.rooms.removeCallback(
|
||||
this.state.localRoomId,
|
||||
"RoomCreationError", this.onCreationError);
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// XXX switch this to use the document title mixin once bug 1081079 lands
|
||||
if (this.state.serverData && this.state.serverData.roomName) {
|
||||
rootObject.document.title = this.state.serverData.roomName;
|
||||
this.setTitle(this.state.serverData.roomName);
|
||||
}
|
||||
|
||||
return (
|
||||
@ -102,8 +55,7 @@ loop.roomViews = (function(mozL10n) {
|
||||
});
|
||||
|
||||
return {
|
||||
setRootObject: setRootObject,
|
||||
EmptyRoomView: EmptyRoomView
|
||||
DesktopRoomView: DesktopRoomView
|
||||
};
|
||||
|
||||
})(document.mozL10n || navigator.mozL10n);;
|
||||
|
@ -10,24 +10,8 @@ var loop = loop || {};
|
||||
loop.roomViews = (function(mozL10n) {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Root object, by default set to window.
|
||||
* @type {DOMWindow|Object}
|
||||
*/
|
||||
var rootObject = window;
|
||||
|
||||
/**
|
||||
* Sets a new root object. This is useful for testing native DOM events so we
|
||||
* can fake them.
|
||||
*
|
||||
* @param {Object}
|
||||
*/
|
||||
function setRootObject(obj) {
|
||||
rootObject = obj;
|
||||
}
|
||||
|
||||
var EmptyRoomView = React.createClass({
|
||||
mixins: [Backbone.Events],
|
||||
var DesktopRoomView = React.createClass({
|
||||
mixins: [Backbone.Events, loop.shared.mixins.DocumentTitleMixin],
|
||||
|
||||
propTypes: {
|
||||
mozLoop:
|
||||
@ -45,28 +29,6 @@ loop.roomViews = (function(mozL10n) {
|
||||
this._onLocalRoomStoreChanged);
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
// XXXremoveMe (just the conditional itself) in patch 2 for bug 1074686,
|
||||
// once the addCallback stuff lands
|
||||
if (this.props.mozLoop.rooms && this.props.mozLoop.rooms.addCallback) {
|
||||
this.props.mozLoop.rooms.addCallback(
|
||||
this.state.localRoomId,
|
||||
"RoomCreationError", this.onCreationError);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Attached to the "RoomCreationError" with mozLoop.rooms.addCallback,
|
||||
* which is fired mozLoop.rooms.createRoom from the panel encounters an
|
||||
* error while attempting to create the room for this view.
|
||||
*
|
||||
* @param {Error} err - JS Error object with info about the problem
|
||||
*/
|
||||
onCreationError: function(err) {
|
||||
// XXX put up a user friendly error instead of this
|
||||
rootObject.console.error("EmptyRoomView creation error: ", err);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles a "change" event on the localRoomStore, and updates this.state
|
||||
* to match the store.
|
||||
@ -79,20 +41,11 @@ loop.roomViews = (function(mozL10n) {
|
||||
|
||||
componentWillUnmount: function() {
|
||||
this.stopListening(this.props.localRoomStore);
|
||||
|
||||
// XXXremoveMe (just the conditional itself) in patch 2 for bug 1074686,
|
||||
// once the addCallback stuff lands
|
||||
if (this.props.mozLoop.rooms && this.props.mozLoop.rooms.removeCallback) {
|
||||
this.props.mozLoop.rooms.removeCallback(
|
||||
this.state.localRoomId,
|
||||
"RoomCreationError", this.onCreationError);
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// XXX switch this to use the document title mixin once bug 1081079 lands
|
||||
if (this.state.serverData && this.state.serverData.roomName) {
|
||||
rootObject.document.title = this.state.serverData.roomName;
|
||||
this.setTitle(this.state.serverData.roomName);
|
||||
}
|
||||
|
||||
return (
|
||||
@ -102,8 +55,7 @@ loop.roomViews = (function(mozL10n) {
|
||||
});
|
||||
|
||||
return {
|
||||
setRootObject: setRootObject,
|
||||
EmptyRoomView: EmptyRoomView
|
||||
DesktopRoomView: DesktopRoomView
|
||||
};
|
||||
|
||||
})(document.mozL10n || navigator.mozL10n);;
|
||||
|
@ -167,7 +167,7 @@ body {
|
||||
|
||||
.room-list {
|
||||
max-height: 335px; /* XXX better computation needed */
|
||||
overflow: scroll;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.room-list > .room-entry {
|
||||
|
@ -57,6 +57,17 @@ loop.shared.mixins = (function() {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Document title mixin.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
var DocumentTitleMixin = {
|
||||
setTitle: function(newTitle) {
|
||||
rootObject.document.title = newTitle;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Dropdown menu mixin.
|
||||
* @type {Object}
|
||||
@ -184,6 +195,7 @@ loop.shared.mixins = (function() {
|
||||
DropdownMenuMixin: DropdownMenuMixin,
|
||||
DocumentVisibilityMixin: DocumentVisibilityMixin,
|
||||
DocumentLocationMixin: DocumentLocationMixin,
|
||||
DocumentTitleMixin: DocumentTitleMixin,
|
||||
UrlHashChangeMixin: UrlHashChangeMixin
|
||||
};
|
||||
})();
|
||||
|
@ -201,13 +201,13 @@ describe("loop.conversation", function() {
|
||||
loop.conversation.IncomingConversationView);
|
||||
});
|
||||
|
||||
it("should display the EmptyRoomView for rooms", function() {
|
||||
it("should display the RoomView for rooms", function() {
|
||||
conversationAppStore.setStoreState({windowType: "room"});
|
||||
|
||||
ccView = mountTestComponent();
|
||||
|
||||
TestUtils.findRenderedComponentWithType(ccView,
|
||||
loop.roomViews.EmptyRoomView);
|
||||
loop.roomViews.DesktopRoomView);
|
||||
});
|
||||
|
||||
it("should display the GenericFailureView for failures", function() {
|
||||
|
@ -18,7 +18,7 @@ describe("loop.roomViews", function () {
|
||||
removeCallback: fakeRemoveCallback } };
|
||||
|
||||
fakeWindow = { document: {} };
|
||||
loop.roomViews.setRootObject(fakeWindow);
|
||||
loop.shared.mixins.setRootObject(fakeWindow);
|
||||
|
||||
store = new loop.store.LocalRoomStore({
|
||||
dispatcher: { register: function() {} },
|
||||
@ -29,54 +29,18 @@ describe("loop.roomViews", function () {
|
||||
|
||||
afterEach(function() {
|
||||
sinon.sandbox.restore();
|
||||
loop.roomViews.setRootObject(window);
|
||||
loop.shared.mixins.setRootObject(window);
|
||||
});
|
||||
|
||||
describe("EmptyRoomView", function() {
|
||||
describe("DesktopRoomView", function() {
|
||||
function mountTestComponent() {
|
||||
return TestUtils.renderIntoDocument(
|
||||
new loop.roomViews.EmptyRoomView({
|
||||
new loop.roomViews.DesktopRoomView({
|
||||
mozLoop: fakeMozLoop,
|
||||
localRoomStore: store
|
||||
}));
|
||||
}
|
||||
|
||||
describe("#componentDidMount", function() {
|
||||
it("should add #onCreationError using mozLoop.rooms.addCallback",
|
||||
function() {
|
||||
|
||||
var testComponent = mountTestComponent();
|
||||
|
||||
sinon.assert.calledOnce(fakeMozLoop.rooms.addCallback);
|
||||
sinon.assert.calledWithExactly(fakeMozLoop.rooms.addCallback,
|
||||
fakeRoomId, "RoomCreationError", testComponent.onCreationError);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#componentWillUnmount", function () {
|
||||
it("should remove #onCreationError using mozLoop.rooms.addCallback",
|
||||
function () {
|
||||
var testComponent = mountTestComponent();
|
||||
|
||||
testComponent.componentWillUnmount();
|
||||
|
||||
sinon.assert.calledOnce(fakeMozLoop.rooms.removeCallback);
|
||||
sinon.assert.calledWithExactly(fakeMozLoop.rooms.removeCallback,
|
||||
fakeRoomId, "RoomCreationError", testComponent.onCreationError);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#onCreationError", function() {
|
||||
it("should log an error using console.error", function() {
|
||||
fakeWindow.console = { error: sandbox.stub() };
|
||||
var testComponent = mountTestComponent();
|
||||
|
||||
testComponent.onCreationError(new Error("fake error"));
|
||||
|
||||
sinon.assert.calledOnce(fakeWindow.console.error);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#render", function() {
|
||||
it("should set document.title to store.serverData.roomName",
|
||||
function() {
|
||||
@ -87,7 +51,7 @@ describe("loop.roomViews", function () {
|
||||
mountTestComponent();
|
||||
|
||||
expect(fakeWindow.document.title).to.equal(fakeRoomName);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -21,7 +21,7 @@ describe("loop.shared.mixins", function() {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe("loop.webapp.UrlHashChangeMixin", function() {
|
||||
describe("loop.shared.mixins.UrlHashChangeMixin", function() {
|
||||
function createTestComponent(onUrlHashChange) {
|
||||
var TestComp = React.createClass({
|
||||
mixins: [loop.shared.mixins.UrlHashChangeMixin],
|
||||
@ -61,7 +61,7 @@ describe("loop.shared.mixins", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("loop.webapp.DocumentLocationMixin", function() {
|
||||
describe("loop.shared.mixins.DocumentLocationMixin", function() {
|
||||
var reloadStub, TestComp;
|
||||
|
||||
beforeEach(function() {
|
||||
@ -90,7 +90,33 @@ describe("loop.shared.mixins", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("loop.panel.DocumentVisibilityMixin", function() {
|
||||
describe("loop.shared.mixins.DocumentTitleMixin", function() {
|
||||
var TestComp, rootObject;
|
||||
|
||||
beforeEach(function() {
|
||||
rootObject = {
|
||||
document: {}
|
||||
};
|
||||
sharedMixins.setRootObject(rootObject);
|
||||
|
||||
TestComp = React.createClass({
|
||||
mixins: [loop.shared.mixins.DocumentTitleMixin],
|
||||
render: function() {
|
||||
return React.DOM.div();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should set window.document.title", function() {
|
||||
var comp = TestUtils.renderIntoDocument(TestComp());
|
||||
|
||||
comp.setTitle("It's a Fake!");
|
||||
|
||||
expect(rootObject.document.title).eql("It's a Fake!");
|
||||
});
|
||||
});
|
||||
|
||||
describe("loop.shared.mixins.DocumentVisibilityMixin", function() {
|
||||
var comp, TestComp, onDocumentVisibleStub, onDocumentHiddenStub;
|
||||
|
||||
beforeEach(function() {
|
||||
|
@ -502,9 +502,6 @@ BrowserGlue.prototype = {
|
||||
#endif
|
||||
os.addObserver(this, "browser-search-engine-modified", false);
|
||||
os.addObserver(this, "browser-search-service", false);
|
||||
#ifdef NIGHTLY_BUILD
|
||||
Services.prefs.addObserver(POLARIS_ENABLED, this, false);
|
||||
#endif
|
||||
},
|
||||
|
||||
// cleanup (called on application shutdown)
|
||||
@ -601,6 +598,10 @@ BrowserGlue.prototype = {
|
||||
|
||||
LoginManagerParent.init();
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
Services.prefs.addObserver(POLARIS_ENABLED, this, false);
|
||||
#endif
|
||||
|
||||
Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
|
||||
},
|
||||
|
||||
@ -2285,28 +2286,23 @@ let DefaultBrowserCheck = {
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
this.closePrompt();
|
||||
},
|
||||
|
||||
_createPopup: function(win, bundle) {
|
||||
_createPopup: function(win, notNowStrings, neverStrings) {
|
||||
let doc = win.document;
|
||||
let popup = doc.createElement("menupopup");
|
||||
popup.id = this.OPTIONPOPUP;
|
||||
|
||||
let notNowItem = doc.createElement("menuitem");
|
||||
notNowItem.id = "defaultBrowserNotNow";
|
||||
let label = bundle.getString("setDefaultBrowserNotNow.label");
|
||||
notNowItem.setAttribute("label", label);
|
||||
let accesskey = bundle.getString("setDefaultBrowserNotNow.accesskey");
|
||||
notNowItem.setAttribute("accesskey", accesskey);
|
||||
notNowItem.setAttribute("label", notNowStrings.label);
|
||||
notNowItem.setAttribute("accesskey", notNowStrings.accesskey);
|
||||
popup.appendChild(notNowItem);
|
||||
|
||||
let neverItem = doc.createElement("menuitem");
|
||||
neverItem.id = "defaultBrowserNever";
|
||||
label = bundle.getString("setDefaultBrowserNever.label");
|
||||
neverItem.setAttribute("label", label);
|
||||
accesskey = bundle.getString("setDefaultBrowserNever.accesskey");
|
||||
neverItem.setAttribute("accesskey", accesskey);
|
||||
neverItem.setAttribute("label", neverStrings.label);
|
||||
neverItem.setAttribute("accesskey", neverStrings.accesskey);
|
||||
popup.appendChild(neverItem);
|
||||
|
||||
popup.addEventListener("command", this);
|
||||
@ -2332,40 +2328,72 @@ let DefaultBrowserCheck = {
|
||||
let promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage2",
|
||||
[brandShortName]);
|
||||
|
||||
let confirmMessage = shellBundle.getFormattedString("setDefaultBrowserConfirm.label",
|
||||
[brandShortName]);
|
||||
let confirmKey = shellBundle.getString("setDefaultBrowserConfirm.accesskey");
|
||||
let yesButton = shellBundle.getFormattedString("setDefaultBrowserConfirm.label",
|
||||
[brandShortName]);
|
||||
|
||||
let optionsMessage = shellBundle.getString("setDefaultBrowserOptions.label");
|
||||
let optionsKey = shellBundle.getString("setDefaultBrowserOptions.accesskey");
|
||||
let notNowButton = shellBundle.getString("setDefaultBrowserNotNow.label");
|
||||
let notNowButtonKey = shellBundle.getString("setDefaultBrowserNotNow.accesskey");
|
||||
|
||||
let selectedBrowser = win.gBrowser.selectedBrowser;
|
||||
let notificationBox = win.document.getElementById("high-priority-global-notificationbox");
|
||||
let neverLabel = shellBundle.getString("setDefaultBrowserNever.label");
|
||||
let neverKey = shellBundle.getString("setDefaultBrowserNever.accesskey");
|
||||
|
||||
this._createPopup(win, shellBundle);
|
||||
let useNotificationBar = Services.prefs.getBoolPref("browser.defaultbrowser.notificationbar");
|
||||
if (useNotificationBar) {
|
||||
let optionsMessage = shellBundle.getString("setDefaultBrowserOptions.label");
|
||||
let optionsKey = shellBundle.getString("setDefaultBrowserOptions.accesskey");
|
||||
|
||||
let buttons = [
|
||||
{
|
||||
label: confirmMessage,
|
||||
accessKey: confirmKey,
|
||||
callback: this.setAsDefault.bind(this)
|
||||
},
|
||||
{
|
||||
label: optionsMessage,
|
||||
accessKey: optionsKey,
|
||||
popup: this.OPTIONPOPUP
|
||||
let yesButtonKey = shellBundle.getString("setDefaultBrowserConfirm.accesskey");
|
||||
|
||||
let notificationBox = win.document.getElementById("high-priority-global-notificationbox");
|
||||
|
||||
this._createPopup(win, {
|
||||
label: notNowButton,
|
||||
accesskey: notNowButtonKey
|
||||
}, {
|
||||
label: neverLabel,
|
||||
accesskey: neverKey
|
||||
});
|
||||
|
||||
let buttons = [
|
||||
{
|
||||
label: yesButton,
|
||||
accessKey: yesButtonKey,
|
||||
callback: () => {
|
||||
this.setAsDefault();
|
||||
this.closePrompt();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: optionsMessage,
|
||||
accessKey: optionsKey,
|
||||
popup: this.OPTIONPOPUP
|
||||
}
|
||||
];
|
||||
|
||||
let iconPixels = win.devicePixelRatio > 1 ? "32" : "16";
|
||||
let iconURL = "chrome://branding/content/icon" + iconPixels + ".png";
|
||||
const priority = notificationBox.PRIORITY_WARNING_HIGH;
|
||||
let callback = this._onNotificationEvent.bind(this);
|
||||
this._notification = notificationBox.appendNotification(promptMessage, "default-browser",
|
||||
iconURL, priority, buttons,
|
||||
callback);
|
||||
} else {
|
||||
// Modal prompt
|
||||
let promptTitle = shellBundle.getString("setDefaultBrowserTitle");
|
||||
|
||||
let ps = Services.prompt;
|
||||
let dontAsk = { value: false };
|
||||
let buttonFlags = (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0) +
|
||||
(ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_1) +
|
||||
ps.BUTTON_POS_0_DEFAULT;
|
||||
let rv = ps.confirmEx(win, promptTitle, promptMessage, buttonFlags,
|
||||
yesButton, notNowButton, null, neverLabel, dontAsk);
|
||||
if (rv == 0) {
|
||||
this.setAsDefault();
|
||||
} else if (dontAsk.value) {
|
||||
ShellService.shouldCheckDefaultBrowser = false;
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
let iconPixels = win.devicePixelRatio > 1 ? "32" : "16";
|
||||
let iconURL = "chrome://branding/content/icon" + iconPixels + ".png";
|
||||
const priority = notificationBox.PRIORITY_WARNING_HIGH;
|
||||
let callback = this._onNotificationEvent.bind(this);
|
||||
this._notification = notificationBox.appendNotification(promptMessage, "default-browser",
|
||||
iconURL, priority, buttons,
|
||||
callback);
|
||||
this._notification.persistence = -1;
|
||||
}
|
||||
},
|
||||
|
||||
_onNotificationEvent: function(eventType) {
|
||||
|
@ -80,7 +80,7 @@
|
||||
preference="privacy.trackingprotection.enabled"
|
||||
accesskey="&trackingProtection.accesskey;"
|
||||
label="&trackingProtection.label;" />
|
||||
<image id="trackingProtectionImage" src="chrome://browser/skin/bad-content-blocked-16.png"/>
|
||||
<image id="trackingProtectionImage"/>
|
||||
</hbox>
|
||||
<label id="trackingProtectionLearnMore"
|
||||
class="text-link"
|
||||
|
@ -268,7 +268,7 @@ Toolbox.prototype = {
|
||||
let domReady = () => {
|
||||
this.isReady = true;
|
||||
|
||||
this._listFrames();
|
||||
let framesPromise = this._listFrames();
|
||||
|
||||
this.closeButton = this.doc.getElementById("toolbox-close");
|
||||
this.closeButton.addEventListener("command", this.destroy, true);
|
||||
@ -309,7 +309,8 @@ Toolbox.prototype = {
|
||||
|
||||
promise.all([
|
||||
splitConsolePromise,
|
||||
buttonsPromise
|
||||
buttonsPromise,
|
||||
framesPromise
|
||||
]).then(() => {
|
||||
this.emit("ready");
|
||||
deferred.resolve();
|
||||
@ -1229,13 +1230,13 @@ Toolbox.prototype = {
|
||||
if (!this._target.form || !this._target.form.actor) {
|
||||
// We are not targetting a regular TabActor
|
||||
// it can be either an addon or browser toolbox actor
|
||||
return;
|
||||
return promise.resolve();
|
||||
}
|
||||
let packet = {
|
||||
to: this._target.form.actor,
|
||||
type: "listFrames"
|
||||
};
|
||||
this._target.client.request(packet, resp => {
|
||||
return this._target.client.request(packet, resp => {
|
||||
this._updateFrames(null, { frames: resp.frames });
|
||||
});
|
||||
},
|
||||
|
@ -70,9 +70,9 @@
|
||||
list-style-image: url("chrome://browser/skin/devedition/search.svg#search-icon-mac-inverted");
|
||||
}
|
||||
|
||||
/* Don't use default colors when in full screen */
|
||||
#main-window:not([customizing]) #navigator-toolbox[inFullscreen] > #TabsToolbar:not(:-moz-lwtheme) {
|
||||
-moz-appearance: none;
|
||||
/* Don't use the default background for tabs toolbar */
|
||||
#TabsToolbar {
|
||||
-moz-appearance: none !important;
|
||||
}
|
||||
|
||||
/* Tab styling - make sure to use an inverted icon for the selected tab
|
||||
|
@ -295,6 +295,18 @@ description > html|a {
|
||||
-moz-margin-start: 33px;
|
||||
}
|
||||
|
||||
#trackingProtectionImage {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
list-style-image: url(chrome://browser/skin/bad-content-blocked-16.png);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
#trackingProtectionImage {
|
||||
list-style-image: url(chrome://browser/skin/bad-content-blocked-16@2x.png);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sub-dialog
|
||||
*/
|
||||
|
@ -3479,7 +3479,7 @@ nsDOMWindowUtils::LoadSheet(nsIURI *aSheetURI, uint32_t aSheetType)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::LoadSheetFromURIString(const nsACString& aSheetURI, uint32_t aSheetType)
|
||||
nsDOMWindowUtils::LoadSheetUsingURIString(const nsACString& aSheetURI, uint32_t aSheetType)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
||||
|
||||
@ -3531,6 +3531,18 @@ nsDOMWindowUtils::RemoveSheet(nsIURI *aSheetURI, uint32_t aSheetType)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::RemoveSheetUsingURIString(const nsACString& aSheetURI, uint32_t aSheetType)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(uri), aSheetURI);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return RemoveSheet(uri, aSheetType);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::GetIsHandlingUserInput(bool* aHandlingUserInput)
|
||||
{
|
||||
|
@ -51,7 +51,7 @@ interface nsITranslationNodeList;
|
||||
interface nsIJSRAIIHelper;
|
||||
interface nsIContentPermissionRequest;
|
||||
|
||||
[scriptable, uuid(f7e4d5da-4dd0-455a-b448-d0224c17fd10)]
|
||||
[scriptable, uuid(e293355b-ae7f-4ef7-9237-452bcf3e9e6b)]
|
||||
interface nsIDOMWindowUtils : nsISupports {
|
||||
|
||||
/**
|
||||
@ -1601,7 +1601,7 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
/**
|
||||
* Same as the above method but allows passing the URI as a string.
|
||||
*/
|
||||
void loadSheetFromURIString(in ACString sheetURI, in unsigned long type);
|
||||
void loadSheetUsingURIString(in ACString sheetURI, in unsigned long type);
|
||||
|
||||
/**
|
||||
* Adds a style sheet to the list of additional style sheets of the document.
|
||||
@ -1618,6 +1618,11 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
*/
|
||||
void removeSheet(in nsIURI sheetURI, in unsigned long type);
|
||||
|
||||
/**
|
||||
* Same as the above method but allows passing the URI as a string.
|
||||
*/
|
||||
void removeSheetUsingURIString(in ACString sheetURI, in unsigned long type);
|
||||
|
||||
/**
|
||||
* Returns true if a user input is being handled.
|
||||
*
|
||||
|
@ -1326,10 +1326,14 @@ public class BrowserApp extends GeckoApp
|
||||
}
|
||||
|
||||
private void updateSideBarState() {
|
||||
if (NewTabletUI.isEnabled(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mMainLayoutAnimator != null)
|
||||
mMainLayoutAnimator.stop();
|
||||
|
||||
boolean isSideBar = !NewTabletUI.isEnabled(this) && (HardwareUtils.isTablet() && getOrientation() == Configuration.ORIENTATION_LANDSCAPE);
|
||||
boolean isSideBar = (HardwareUtils.isTablet() && getOrientation() == Configuration.ORIENTATION_LANDSCAPE);
|
||||
final int sidebarWidth = getResources().getDimensionPixelSize(R.dimen.tabs_sidebar_width);
|
||||
|
||||
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mTabsPanel.getLayoutParams();
|
||||
@ -1729,6 +1733,10 @@ public class BrowserApp extends GeckoApp
|
||||
if (!areTabsShown()) {
|
||||
mTabsPanel.setVisibility(View.INVISIBLE);
|
||||
mTabsPanel.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
|
||||
} else {
|
||||
// Cancel editing mode to return to page content when the TabsPanel closes. We cancel
|
||||
// it here because there are graphical glitches if it's canceled while it's visible.
|
||||
mBrowserToolbar.cancelEdit();
|
||||
}
|
||||
|
||||
mTabsPanel.finishTabsAnimation();
|
||||
|
@ -7,12 +7,15 @@ package org.mozilla.gecko;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.preferences.GeckoPreferences;
|
||||
import org.mozilla.gecko.util.HardwareUtils;
|
||||
|
||||
public class NewTabletUI {
|
||||
// This value should be in sync with preferences_display.xml.
|
||||
private static final boolean DEFAULT = false;
|
||||
// This value should be in sync with preferences_display.xml. On non-release
|
||||
// builds, the preference UI will be hidden and the (unused) default
|
||||
// preference UI value will still be 'true'.
|
||||
private static final boolean DEFAULT = !AppConstants.RELEASE_BUILD;
|
||||
|
||||
private static Boolean sNewTabletUI;
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<org.mozilla.gecko.tabs.TabsLayoutItemView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:gecko="http://schemas.android.com/apk/res-auto"
|
||||
style="@style/TabsItem"
|
||||
android:focusable="true"
|
||||
android:id="@+id/info"
|
||||
@ -21,21 +22,27 @@
|
||||
android:paddingRight="@dimen/new_tablet_tab_highlight_stroke_width"
|
||||
android:paddingBottom="@dimen/new_tablet_tab_highlight_stroke_width">
|
||||
|
||||
<TextView android:id="@+id/title"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0"
|
||||
style="@style/TabLayoutItemTextAppearance"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/new_tablet_tab_item_title"
|
||||
android:singleLine="true"
|
||||
android:duplicateParentState="true"/>
|
||||
<org.mozilla.gecko.widget.FadedTextView android:id="@+id/title"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0"
|
||||
style="@style/TabLayoutItemTextAppearance"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/new_tablet_tab_item_title"
|
||||
android:singleLine="true"
|
||||
android:duplicateParentState="true"
|
||||
gecko:fadeWidth="15dp"
|
||||
android:paddingRight="5dp"/>
|
||||
|
||||
|
||||
<!-- Use of baselineAlignBottom only supported from API 11+ - if this needs to work on lower API versions
|
||||
we'll need to override getBaseLine() and return image height, but we assume this won't happen -->
|
||||
<ImageButton android:id="@+id/close"
|
||||
style="@style/TabsItemClose"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="center"
|
||||
android:baselineAlignBottom="true"
|
||||
android:background="@android:color/transparent"
|
||||
android:contentDescription="@string/close_tab"
|
||||
android:src="@drawable/new_tablet_tab_item_close_button"
|
@ -8,4 +8,5 @@
|
||||
<item type="layout" name="tabs_layout_item_view">@layout/tabs_item_row</item>
|
||||
<item type="layout" name="new_tablet_browser_toolbar">@null</item>
|
||||
<item type="layout" name="new_tablet_tab_strip">@null</item>
|
||||
<item type="layout" name="new_tablet_tabs_item_cell">@null</item>
|
||||
</resources>
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
<CheckBoxPreference android:key="android.not_a_preference.new_tablet_ui"
|
||||
android:title="@string/new_tablet_pref"
|
||||
android:defaultValue="false" />
|
||||
android:defaultValue="true" />
|
||||
|
||||
<PreferenceCategory android:title="@string/pref_category_advanced">
|
||||
|
||||
|
@ -82,9 +82,7 @@ public class TabStrip extends ThemedLinearLayout {
|
||||
switch (msg) {
|
||||
case RESTORED:
|
||||
case ADDED:
|
||||
// Refresh the list to make sure the new tab is
|
||||
// added in the right position.
|
||||
tabStripView.refreshTabs();
|
||||
tabStripView.addTab(tab);
|
||||
break;
|
||||
|
||||
case CLOSED:
|
||||
|
@ -11,11 +11,16 @@ import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.ViewTreeObserver.OnPreDrawListener;
|
||||
|
||||
import com.nineoldandroids.animation.Animator;
|
||||
import com.nineoldandroids.animation.AnimatorSet;
|
||||
import com.nineoldandroids.animation.ObjectAnimator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ -27,6 +32,10 @@ import org.mozilla.gecko.widget.TwoWayView;
|
||||
public class TabStripView extends TwoWayView {
|
||||
private static final String LOGTAG = "GeckoTabStrip";
|
||||
|
||||
private static final int ANIM_TIME_MS = 200;
|
||||
private static final AccelerateDecelerateInterpolator ANIM_INTERPOLATOR =
|
||||
new AccelerateDecelerateInterpolator();
|
||||
|
||||
private final TabStripAdapter adapter;
|
||||
private final Drawable divider;
|
||||
|
||||
@ -71,14 +80,110 @@ public class TabStripView extends TwoWayView {
|
||||
setItemChecked(selected, true);
|
||||
}
|
||||
|
||||
private void updateSelectedPosition() {
|
||||
private void updateSelectedPosition(boolean ensureVisible) {
|
||||
final int selected = getPositionForSelectedTab();
|
||||
if (selected != -1) {
|
||||
updateSelectedStyle(selected);
|
||||
ensurePositionIsVisible(selected);
|
||||
|
||||
if (ensureVisible) {
|
||||
ensurePositionIsVisible(selected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void animateRemoveTab(Tab removedTab) {
|
||||
final int removedPosition = adapter.getPositionForTab(removedTab);
|
||||
|
||||
final View removedView = getViewForTab(removedTab);
|
||||
|
||||
// The removed position might not have a matching child view
|
||||
// when it's not within the visible range of positions in the strip.
|
||||
if (removedView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't animate the removed child view (it just disappears)
|
||||
// but we still need its size of animate all affected children
|
||||
// within the visible viewport.
|
||||
final int removedSize = removedView.getWidth() + getItemMargin();
|
||||
|
||||
getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
|
||||
final int firstPosition = getFirstVisiblePosition();
|
||||
final List<Animator> childAnimators = new ArrayList<Animator>();
|
||||
|
||||
final int childCount = getChildCount();
|
||||
for (int i = removedPosition - firstPosition; i < childCount; i++) {
|
||||
final View child = getChildAt(i);
|
||||
|
||||
// TODO: optimize with Valueresolver
|
||||
final ObjectAnimator animator =
|
||||
ObjectAnimator.ofFloat(child, "translationX", removedSize, 0);
|
||||
childAnimators.add(animator);
|
||||
}
|
||||
|
||||
final AnimatorSet animatorSet = new AnimatorSet();
|
||||
animatorSet.playTogether(childAnimators);
|
||||
animatorSet.setDuration(ANIM_TIME_MS);
|
||||
animatorSet.setInterpolator(ANIM_INTERPOLATOR);
|
||||
animatorSet.start();
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void animateNewTab(Tab newTab) {
|
||||
final int newPosition = adapter.getPositionForTab(newTab);
|
||||
if (newPosition < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
|
||||
final int firstPosition = getFirstVisiblePosition();
|
||||
|
||||
final View newChild = getChildAt(newPosition - firstPosition);
|
||||
if (newChild == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final List<Animator> childAnimators = new ArrayList<Animator>();
|
||||
childAnimators.add(
|
||||
ObjectAnimator.ofFloat(newChild, "translationY", newChild.getHeight(), 0));
|
||||
|
||||
// This will momentaneously add a gap on the right side
|
||||
// because TwoWayView doesn't provide APIs to control
|
||||
// view recycling programatically to handle these transitory
|
||||
// states in the container during animations.
|
||||
|
||||
final int tabSize = newChild.getWidth();
|
||||
final int newIndex = newPosition - firstPosition;
|
||||
final int childCount = getChildCount();
|
||||
for (int i = newIndex + 1; i < childCount; i++) {
|
||||
final View child = getChildAt(i);
|
||||
|
||||
childAnimators.add(
|
||||
ObjectAnimator.ofFloat(child, "translationX", -tabSize, 0));
|
||||
}
|
||||
|
||||
final AnimatorSet animatorSet = new AnimatorSet();
|
||||
animatorSet.playTogether(childAnimators);
|
||||
animatorSet.setDuration(ANIM_TIME_MS);
|
||||
animatorSet.setInterpolator(ANIM_INTERPOLATOR);
|
||||
animatorSet.start();
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void ensurePositionIsVisible(final int position) {
|
||||
getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
|
||||
@Override
|
||||
@ -111,16 +216,24 @@ public class TabStripView extends TwoWayView {
|
||||
}
|
||||
|
||||
adapter.refresh(tabs);
|
||||
updateSelectedPosition();
|
||||
updateSelectedPosition(true);
|
||||
}
|
||||
|
||||
void clearTabs() {
|
||||
adapter.clear();
|
||||
}
|
||||
|
||||
void addTab(Tab tab) {
|
||||
// Refresh the list to make sure the new tab is
|
||||
// added in the right position.
|
||||
refreshTabs();
|
||||
animateNewTab(tab);
|
||||
}
|
||||
|
||||
void removeTab(Tab tab) {
|
||||
animateRemoveTab(tab);
|
||||
adapter.removeTab(tab);
|
||||
updateSelectedPosition();
|
||||
updateSelectedPosition(false);
|
||||
}
|
||||
|
||||
void selectTab(Tab tab) {
|
||||
@ -128,7 +241,7 @@ public class TabStripView extends TwoWayView {
|
||||
isPrivate = tab.isPrivate();
|
||||
refreshTabs();
|
||||
} else {
|
||||
updateSelectedPosition();
|
||||
updateSelectedPosition(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,13 +9,15 @@ import org.mozilla.gecko.Tab;
|
||||
import org.mozilla.gecko.widget.TabThumbnailWrapper;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.TouchDelegate;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.Checkable;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ImageView.ScaleType;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
@ -85,6 +87,22 @@ public class TabsLayoutItemView extends LinearLayout
|
||||
mThumbnail = (ImageView) findViewById(R.id.thumbnail);
|
||||
mCloseButton = (ImageButton) findViewById(R.id.close);
|
||||
mThumbnailWrapper = (TabThumbnailWrapper) findViewById(R.id.wrapper);
|
||||
|
||||
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
|
||||
final Rect r = new Rect();
|
||||
mCloseButton.getHitRect(r);
|
||||
r.left -= 25;
|
||||
r.bottom += 25;
|
||||
|
||||
setTouchDelegate(new TouchDelegate(r, mCloseButton));
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void assignValues(Tab tab) {
|
||||
|
@ -315,6 +315,10 @@ public abstract class BrowserToolbar extends ThemedRelativeLayout
|
||||
tabsButton.setOnClickListener(new Button.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// Clear focus so a back press with the tabs
|
||||
// panel open does not go to the editing field.
|
||||
urlEditLayout.clearFocus();
|
||||
|
||||
toggleTabs();
|
||||
}
|
||||
});
|
||||
@ -341,6 +345,8 @@ public abstract class BrowserToolbar extends ThemedRelativeLayout
|
||||
menuButton.setOnClickListener(new Button.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
// Drop the soft keyboard.
|
||||
urlEditLayout.clearFocus();
|
||||
activity.openOptionsMenu();
|
||||
}
|
||||
});
|
||||
|
@ -99,6 +99,20 @@ let Bookmarks = Object.freeze({
|
||||
*/
|
||||
DEFAULT_INDEX: -1,
|
||||
|
||||
/**
|
||||
* Special GUIDs associated with bookmark roots.
|
||||
* It's guaranteed that the roots will always have these guids.
|
||||
*/
|
||||
|
||||
rootGuid: "root________",
|
||||
menuGuid: "menu________",
|
||||
toolbarGuid: "toolbar_____",
|
||||
unfiledGuid: "unfiled_____",
|
||||
|
||||
// With bug 424160, tags will stop being bookmarks, thus this root will
|
||||
// be removed. Do not rely on this, rather use the tagging service API.
|
||||
tagsGuid: "tags________",
|
||||
|
||||
/**
|
||||
* Inserts a bookmark-item into the bookmarks tree.
|
||||
*
|
||||
@ -437,9 +451,9 @@ let Bookmarks = Object.freeze({
|
||||
let rows = yield db.executeCached(
|
||||
`WITH RECURSIVE
|
||||
descendants(did) AS (
|
||||
SELECT id FROM moz_bookmarks
|
||||
WHERE parent IN (SELECT folder_id FROM moz_bookmarks_roots
|
||||
WHERE root_name IN ("toolbar", "menu", "unfiled"))
|
||||
SELECT b.id FROM moz_bookmarks b
|
||||
JOIN moz_bookmarks p ON b.parent = p.id
|
||||
WHERE p.guid IN ( :toolbarGuid, :menuGuid, :unfiledGuid )
|
||||
UNION ALL
|
||||
SELECT id FROM moz_bookmarks
|
||||
JOIN descendants ON parent = did
|
||||
@ -452,21 +466,23 @@ let Bookmarks = Object.freeze({
|
||||
JOIN moz_bookmarks p ON p.id = b.parent
|
||||
LEFT JOIN moz_places h ON b.fk = h.id
|
||||
WHERE b.id IN descendants
|
||||
`);
|
||||
`, { menuGuid: this.menuGuid, toolbarGuid: this.toolbarGuid,
|
||||
unfiledGuid: this.unfiledGuid });
|
||||
let items = rowsToItemsArray(rows);
|
||||
|
||||
yield db.executeCached(
|
||||
`WITH RECURSIVE
|
||||
descendants(did) AS (
|
||||
SELECT id FROM moz_bookmarks
|
||||
WHERE parent IN (SELECT folder_id FROM moz_bookmarks_roots
|
||||
WHERE root_name IN ("toolbar", "menu", "unfiled"))
|
||||
SELECT b.id FROM moz_bookmarks b
|
||||
JOIN moz_bookmarks p ON b.parent = p.id
|
||||
WHERE p.guid IN ( :toolbarGuid, :menuGuid, :unfiledGuid )
|
||||
UNION ALL
|
||||
SELECT id FROM moz_bookmarks
|
||||
JOIN descendants ON parent = did
|
||||
)
|
||||
DELETE FROM moz_bookmarks WHERE id IN descendants
|
||||
`);
|
||||
`, { menuGuid: this.menuGuid, toolbarGuid: this.toolbarGuid,
|
||||
unfiledGuid: this.unfiledGuid });
|
||||
|
||||
// Clenup orphans.
|
||||
yield removeOrphanAnnotations(db);
|
||||
@ -477,9 +493,11 @@ let Bookmarks = Object.freeze({
|
||||
// Update roots' lastModified.
|
||||
yield db.executeCached(
|
||||
`UPDATE moz_bookmarks SET lastModified = :time
|
||||
WHERE id IN (SELECT folder_id FROM moz_bookmarks_roots
|
||||
WHERE root_name IN ("places", "toolbar", "menu", "unfiled"));
|
||||
`, { time: toPRTime(new Date()) });
|
||||
WHERE id IN (SELECT id FROM moz_bookmarks
|
||||
WHERE guid IN ( :rootGuid, :toolbarGuid, :menuGuid, :unfiledGuid ))
|
||||
`, { time: toPRTime(new Date()), rootGuid: this.rootGuid,
|
||||
menuGuid: this.menuGuid, toolbarGuid: this.toolbarGuid,
|
||||
unfiledGuid: this.unfiledGuid });
|
||||
|
||||
let urls = [for (item of items) if (item.url) item.url];
|
||||
updateFrecency(db, urls).then(null, Cu.reportError);
|
||||
@ -506,7 +524,7 @@ let Bookmarks = Object.freeze({
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}.bind(this));
|
||||
}),
|
||||
|
||||
/**
|
||||
|
@ -257,7 +257,7 @@ NS_IMPL_ISUPPORTS(
|
||||
|
||||
nsresult
|
||||
CreateRoot(nsCOMPtr<mozIStorageConnection>& aDBConn,
|
||||
const nsCString& aRootName,
|
||||
const nsCString& aRootName, const nsCString& aGuid,
|
||||
const nsXPIDLString& titleString)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
@ -277,7 +277,7 @@ CreateRoot(nsCOMPtr<mozIStorageConnection>& aDBConn,
|
||||
"INSERT INTO moz_bookmarks "
|
||||
"(type, position, title, dateAdded, lastModified, guid, parent) "
|
||||
"VALUES (:item_type, :item_position, :item_title,"
|
||||
":date_added, :last_modified, GENERATE_GUID(),"
|
||||
":date_added, :last_modified, :guid,"
|
||||
"IFNULL((SELECT id FROM moz_bookmarks WHERE parent = 0), 0))"
|
||||
), getter_AddRefs(stmt));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
@ -294,6 +294,8 @@ CreateRoot(nsCOMPtr<mozIStorageConnection>& aDBConn,
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("last_modified"), timestamp);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aGuid);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
rv = stmt->Execute();
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
@ -301,18 +303,15 @@ CreateRoot(nsCOMPtr<mozIStorageConnection>& aDBConn,
|
||||
nsCOMPtr<mozIStorageStatement> newRootStmt;
|
||||
rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"INSERT INTO moz_bookmarks_roots (root_name, folder_id) "
|
||||
"VALUES (:root_name, "
|
||||
"(SELECT id from moz_bookmarks WHERE "
|
||||
" position = :item_position AND "
|
||||
" parent = IFNULL((SELECT MIN(folder_id) FROM moz_bookmarks_roots), 0)))"
|
||||
"VALUES (:root_name, (SELECT id from moz_bookmarks WHERE guid = :guid))"
|
||||
), getter_AddRefs(newRootStmt));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = newRootStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("root_name"),
|
||||
aRootName);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
rv = newRootStmt->BindInt32ByName(NS_LITERAL_CSTRING("item_position"),
|
||||
itemPosition);
|
||||
rv = newRootStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"),
|
||||
aGuid);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
rv = newRootStmt->Execute();
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
@ -638,43 +637,12 @@ Database::InitSchema(bool* aDatabaseMigrated)
|
||||
if (currentSchemaVersion < DATABASE_SCHEMA_VERSION) {
|
||||
*aDatabaseMigrated = true;
|
||||
|
||||
if (currentSchemaVersion < 6) {
|
||||
// These are early Firefox 3.0 alpha versions that are not supported
|
||||
if (currentSchemaVersion < 11) {
|
||||
// These are versions older than Firefox 4 that are not supported
|
||||
// anymore. In this case it's safer to just replace the database.
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
}
|
||||
|
||||
// Firefox 3.0 uses schema version 6.
|
||||
|
||||
if (currentSchemaVersion < 7) {
|
||||
rv = MigrateV7Up();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (currentSchemaVersion < 8) {
|
||||
rv = MigrateV8Up();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Firefox 3.5 uses schema version 8.
|
||||
|
||||
if (currentSchemaVersion < 9) {
|
||||
rv = MigrateV9Up();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (currentSchemaVersion < 10) {
|
||||
rv = MigrateV10Up();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Firefox 3.6 uses schema version 10.
|
||||
|
||||
if (currentSchemaVersion < 11) {
|
||||
rv = MigrateV11Up();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Firefox 4 uses schema version 11.
|
||||
|
||||
// Firefox 8 uses schema version 12.
|
||||
@ -750,8 +718,16 @@ Database::InitSchema(bool* aDatabaseMigrated)
|
||||
rv = MigrateV24Up();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Firefox 34 uses schema version 24.
|
||||
|
||||
if (currentSchemaVersion < 25) {
|
||||
rv = MigrateV25Up();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Firefox 36 uses schema version 25.
|
||||
|
||||
// Schema Upgrades must add migration code here.
|
||||
|
||||
rv = UpdateBookmarkRootTitles();
|
||||
@ -875,42 +851,43 @@ Database::CreateBookmarkRoots()
|
||||
|
||||
nsXPIDLString rootTitle;
|
||||
// The first root's title is an empty string.
|
||||
rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("places"), rootTitle);
|
||||
rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("places"),
|
||||
NS_LITERAL_CSTRING("root________"), rootTitle);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// Fetch the internationalized folder name from the string bundle.
|
||||
rv = bundle->GetStringFromName(MOZ_UTF16("BookmarksMenuFolderTitle"),
|
||||
getter_Copies(rootTitle));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("menu"), rootTitle);
|
||||
rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("menu"),
|
||||
NS_LITERAL_CSTRING("menu________"), rootTitle);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = bundle->GetStringFromName(MOZ_UTF16("BookmarksToolbarFolderTitle"),
|
||||
getter_Copies(rootTitle));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("toolbar"), rootTitle);
|
||||
rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("toolbar"),
|
||||
NS_LITERAL_CSTRING("toolbar_____"), rootTitle);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = bundle->GetStringFromName(MOZ_UTF16("TagsFolderTitle"),
|
||||
getter_Copies(rootTitle));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("tags"), rootTitle);
|
||||
rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("tags"),
|
||||
NS_LITERAL_CSTRING("tags________"), rootTitle);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = bundle->GetStringFromName(MOZ_UTF16("UnsortedBookmarksFolderTitle"),
|
||||
getter_Copies(rootTitle));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("unfiled"), rootTitle);
|
||||
rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("unfiled"),
|
||||
NS_LITERAL_CSTRING("unfiled_____"), rootTitle);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
#if DEBUG
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT "
|
||||
"(SELECT COUNT(*) FROM moz_bookmarks), "
|
||||
"(SELECT COUNT(*) FROM moz_bookmarks_roots), "
|
||||
"(SELECT SUM(position) FROM moz_bookmarks WHERE "
|
||||
"id IN (SELECT folder_id FROM moz_bookmarks_roots))"
|
||||
"SELECT count(*), sum(position) FROM moz_bookmarks"
|
||||
), getter_AddRefs(stmt));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
@ -918,16 +895,9 @@ Database::CreateBookmarkRoots()
|
||||
rv = stmt->ExecuteStep(&hasResult);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
MOZ_ASSERT(hasResult);
|
||||
int32_t bookmarkCount = 0;
|
||||
rv = stmt->GetInt32(0, &bookmarkCount);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
int32_t rootCount = 0;
|
||||
rv = stmt->GetInt32(1, &rootCount);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
int32_t positionSum = 0;
|
||||
rv = stmt->GetInt32(2, &positionSum);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
MOZ_ASSERT(bookmarkCount == 5 && rootCount == 5 && positionSum == 6);
|
||||
int32_t bookmarkCount = stmt->AsInt32(0);
|
||||
int32_t positionSum = stmt->AsInt32(1);
|
||||
MOZ_ASSERT(bookmarkCount == 5 && positionSum == 6);
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
@ -999,8 +969,7 @@ Database::UpdateBookmarkRootTitles()
|
||||
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt;
|
||||
rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
|
||||
"UPDATE moz_bookmarks SET title = :new_title WHERE id = "
|
||||
"(SELECT folder_id FROM moz_bookmarks_roots WHERE root_name = :root_name)"
|
||||
"UPDATE moz_bookmarks SET title = :new_title WHERE guid = :guid"
|
||||
), getter_AddRefs(stmt));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
@ -1008,13 +977,18 @@ Database::UpdateBookmarkRootTitles()
|
||||
rv = stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
const char *rootNames[] = { "menu", "toolbar", "tags", "unfiled" };
|
||||
const char *titleStringIDs[] = {
|
||||
"BookmarksMenuFolderTitle", "BookmarksToolbarFolderTitle",
|
||||
"TagsFolderTitle", "UnsortedBookmarksFolderTitle"
|
||||
};
|
||||
const char *rootGuids[] = { "menu________"
|
||||
, "toolbar_____"
|
||||
, "tags________"
|
||||
, "unfiled_____"
|
||||
};
|
||||
const char *titleStringIDs[] = { "BookmarksMenuFolderTitle"
|
||||
, "BookmarksToolbarFolderTitle"
|
||||
, "TagsFolderTitle"
|
||||
, "UnsortedBookmarksFolderTitle"
|
||||
};
|
||||
|
||||
for (uint32_t i = 0; i < ArrayLength(rootNames); ++i) {
|
||||
for (uint32_t i = 0; i < ArrayLength(rootGuids); ++i) {
|
||||
nsXPIDLString title;
|
||||
rv = bundle->GetStringFromName(NS_ConvertASCIItoUTF16(titleStringIDs[i]).get(),
|
||||
getter_Copies(title));
|
||||
@ -1023,8 +997,8 @@ Database::UpdateBookmarkRootTitles()
|
||||
nsCOMPtr<mozIStorageBindingParams> params;
|
||||
rv = paramsArray->NewBindingParams(getter_AddRefs(params));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("root_name"),
|
||||
nsDependentCString(rootNames[i]));
|
||||
rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"),
|
||||
nsDependentCString(rootGuids[i]));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("new_title"),
|
||||
NS_ConvertUTF16toUTF8(title));
|
||||
@ -1042,528 +1016,6 @@ Database::UpdateBookmarkRootTitles()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Database::CheckAndUpdateGUIDs()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// First, import any bookmark guids already set by Sync.
|
||||
nsCOMPtr<mozIStorageStatement> updateStmt;
|
||||
nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"UPDATE moz_bookmarks "
|
||||
"SET guid = :guid "
|
||||
"WHERE id = :item_id "
|
||||
), getter_AddRefs(updateStmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT item_id, content "
|
||||
"FROM moz_items_annos "
|
||||
"JOIN moz_anno_attributes "
|
||||
"WHERE name = :anno_name "
|
||||
), getter_AddRefs(stmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"),
|
||||
SYNCGUID_ANNO);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool hasResult;
|
||||
while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
|
||||
int64_t itemId;
|
||||
rv = stmt->GetInt64(0, &itemId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsAutoCString guid;
|
||||
rv = stmt->GetUTF8String(1, guid);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// If we have an invalid guid, we don't need to do any more work.
|
||||
if (!IsValidGUID(guid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mozStorageStatementScoper updateScoper(updateStmt);
|
||||
rv = updateStmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), itemId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = updateStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), guid);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = updateStmt->Execute();
|
||||
if (rv == NS_ERROR_STORAGE_CONSTRAINT) {
|
||||
// We just tried to insert a duplicate guid. Ignore this error, and we
|
||||
// will generate a new one next.
|
||||
continue;
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Now, remove all the bookmark guid annotations that we just imported.
|
||||
rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"DELETE FROM moz_items_annos "
|
||||
"WHERE anno_attribute_id = ( "
|
||||
"SELECT id "
|
||||
"FROM moz_anno_attributes "
|
||||
"WHERE name = :anno_name "
|
||||
") "
|
||||
), getter_AddRefs(stmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"),
|
||||
SYNCGUID_ANNO);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = stmt->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Next, generate guids for any bookmark that does not already have one.
|
||||
rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"UPDATE moz_bookmarks "
|
||||
"SET guid = GENERATE_GUID() "
|
||||
"WHERE guid IS NULL "
|
||||
), getter_AddRefs(stmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = stmt->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Now, import any history guids already set by Sync.
|
||||
rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"UPDATE moz_places "
|
||||
"SET guid = :guid "
|
||||
"WHERE id = :place_id "
|
||||
), getter_AddRefs(updateStmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT place_id, content "
|
||||
"FROM moz_annos "
|
||||
"JOIN moz_anno_attributes "
|
||||
"WHERE name = :anno_name "
|
||||
), getter_AddRefs(stmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"),
|
||||
SYNCGUID_ANNO);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
|
||||
int64_t placeId;
|
||||
rv = stmt->GetInt64(0, &placeId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsAutoCString guid;
|
||||
rv = stmt->GetUTF8String(1, guid);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// If we have an invalid guid, we don't need to do any more work.
|
||||
if (!IsValidGUID(guid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mozStorageStatementScoper updateScoper(updateStmt);
|
||||
rv = updateStmt->BindInt64ByName(NS_LITERAL_CSTRING("place_id"), placeId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = updateStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), guid);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = updateStmt->Execute();
|
||||
if (rv == NS_ERROR_STORAGE_CONSTRAINT) {
|
||||
// We just tried to insert a duplicate guid. Ignore this error, and we
|
||||
// will generate a new one next.
|
||||
continue;
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Now, remove all the place guid annotations that we just imported.
|
||||
rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"DELETE FROM moz_annos "
|
||||
"WHERE anno_attribute_id = ( "
|
||||
"SELECT id "
|
||||
"FROM moz_anno_attributes "
|
||||
"WHERE name = :anno_name "
|
||||
") "
|
||||
), getter_AddRefs(stmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"),
|
||||
SYNCGUID_ANNO);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = stmt->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Finally, generate guids for any places that do not already have one.
|
||||
rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"UPDATE moz_places "
|
||||
"SET guid = GENERATE_GUID() "
|
||||
"WHERE guid IS NULL "
|
||||
), getter_AddRefs(stmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = stmt->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Database::MigrateV7Up()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Some old v6 databases come from alpha versions that missed indices.
|
||||
// Just bail out and replace the database in such a case.
|
||||
bool URLUniqueIndexExists = false;
|
||||
nsresult rv = mMainConn->IndexExists(NS_LITERAL_CSTRING(
|
||||
"moz_places_url_uniqueindex"
|
||||
), &URLUniqueIndexExists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!URLUniqueIndexExists) {
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
}
|
||||
|
||||
// We need an index on lastModified to catch quickly last modified bookmark
|
||||
// title for tag container's children. This will be useful for Sync, too.
|
||||
bool lastModIndexExists = false;
|
||||
rv = mMainConn->IndexExists(
|
||||
NS_LITERAL_CSTRING("moz_bookmarks_itemlastmodifiedindex"),
|
||||
&lastModIndexExists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!lastModIndexExists) {
|
||||
rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PLACELASTMODIFIED);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// We need to do a one-time change of the moz_historyvisits.pageindex
|
||||
// to speed up finding last visit date when joinin with moz_places.
|
||||
// See bug 392399 for more details.
|
||||
bool pageIndexExists = false;
|
||||
rv = mMainConn->IndexExists(
|
||||
NS_LITERAL_CSTRING("moz_historyvisits_pageindex"), &pageIndexExists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (pageIndexExists) {
|
||||
// drop old index
|
||||
rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"DROP INDEX IF EXISTS moz_historyvisits_pageindex"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// create the new multi-column index
|
||||
rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_HISTORYVISITS_PLACEDATE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// for existing profiles, we may not have a frecency column
|
||||
nsCOMPtr<mozIStorageStatement> hasFrecencyStatement;
|
||||
rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT frecency FROM moz_places"),
|
||||
getter_AddRefs(hasFrecencyStatement));
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
// Add frecency column to moz_places, default to -1 so that all the
|
||||
// frecencies are invalid
|
||||
rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"ALTER TABLE moz_places ADD frecency INTEGER DEFAULT -1 NOT NULL"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// create index for the frecency column
|
||||
// XXX multi column index with typed, and visit_count?
|
||||
rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_FRECENCY);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Invalidate all frecencies, since they need recalculation.
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt = GetAsyncStatement(
|
||||
"UPDATE moz_places SET frecency = ( "
|
||||
"CASE "
|
||||
"WHEN url BETWEEN 'place:' AND 'place;' "
|
||||
"THEN 0 "
|
||||
"ELSE -1 "
|
||||
"END "
|
||||
") "
|
||||
);
|
||||
NS_ENSURE_STATE(stmt);
|
||||
nsCOMPtr<mozIStoragePendingStatement> ps;
|
||||
(void)stmt->ExecuteAsync(nullptr, getter_AddRefs(ps));
|
||||
}
|
||||
|
||||
// Temporary migration code for bug 396300
|
||||
nsCOMPtr<mozIStorageStatement> moveUnfiledBookmarks;
|
||||
rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"UPDATE moz_bookmarks "
|
||||
"SET parent = ("
|
||||
"SELECT folder_id "
|
||||
"FROM moz_bookmarks_roots "
|
||||
"WHERE root_name = :root_name "
|
||||
") "
|
||||
"WHERE type = :item_type "
|
||||
"AND parent = ("
|
||||
"SELECT folder_id "
|
||||
"FROM moz_bookmarks_roots "
|
||||
"WHERE root_name = :parent_name "
|
||||
")"),
|
||||
getter_AddRefs(moveUnfiledBookmarks));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = moveUnfiledBookmarks->BindUTF8StringByName(
|
||||
NS_LITERAL_CSTRING("root_name"), NS_LITERAL_CSTRING("unfiled")
|
||||
);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = moveUnfiledBookmarks->BindInt32ByName(
|
||||
NS_LITERAL_CSTRING("item_type"), nsINavBookmarksService::TYPE_BOOKMARK
|
||||
);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = moveUnfiledBookmarks->BindUTF8StringByName(
|
||||
NS_LITERAL_CSTRING("parent_name"), NS_LITERAL_CSTRING("places")
|
||||
);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = moveUnfiledBookmarks->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Create a statement to test for trigger creation
|
||||
nsCOMPtr<mozIStorageStatement> triggerDetection;
|
||||
rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT name "
|
||||
"FROM sqlite_master "
|
||||
"WHERE type = 'trigger' "
|
||||
"AND name = :trigger_name"),
|
||||
getter_AddRefs(triggerDetection));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Check for existence
|
||||
bool triggerExists;
|
||||
rv = triggerDetection->BindUTF8StringByName(
|
||||
NS_LITERAL_CSTRING("trigger_name"),
|
||||
NS_LITERAL_CSTRING("moz_historyvisits_afterinsert_v1_trigger")
|
||||
);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = triggerDetection->ExecuteStep(&triggerExists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = triggerDetection->Reset();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// We need to create two triggers on moz_historyvists to maintain the
|
||||
// accuracy of moz_places.visit_count. For this to work, we must ensure that
|
||||
// all moz_places.visit_count values are correct.
|
||||
// See bug 416313 for details.
|
||||
if (!triggerExists) {
|
||||
// First, we do a one-time reset of all the moz_places.visit_count values.
|
||||
rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"UPDATE moz_places SET visit_count = "
|
||||
"(SELECT count(*) FROM moz_historyvisits "
|
||||
"WHERE place_id = moz_places.id "
|
||||
"AND visit_type NOT IN ") +
|
||||
nsPrintfCString("(0,%d,%d,%d) ",
|
||||
nsINavHistoryService::TRANSITION_EMBED,
|
||||
nsINavHistoryService::TRANSITION_FRAMED_LINK,
|
||||
nsINavHistoryService::TRANSITION_DOWNLOAD) +
|
||||
NS_LITERAL_CSTRING(")"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// We used to create two triggers here, but we no longer need that with
|
||||
// schema version eight and greater. We've removed their creation here as
|
||||
// a result.
|
||||
}
|
||||
|
||||
// Check for existence
|
||||
rv = triggerDetection->BindUTF8StringByName(
|
||||
NS_LITERAL_CSTRING("trigger_name"),
|
||||
NS_LITERAL_CSTRING("moz_bookmarks_beforedelete_v1_trigger")
|
||||
);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = triggerDetection->ExecuteStep(&triggerExists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = triggerDetection->Reset();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// We need to create one trigger on moz_bookmarks to remove unused keywords.
|
||||
// See bug 421180 for details.
|
||||
if (!triggerExists) {
|
||||
// First, remove any existing dangling keywords
|
||||
rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"DELETE FROM moz_keywords "
|
||||
"WHERE id IN ("
|
||||
"SELECT k.id "
|
||||
"FROM moz_keywords k "
|
||||
"LEFT OUTER JOIN moz_bookmarks b "
|
||||
"ON b.keyword_id = k.id "
|
||||
"WHERE b.id IS NULL"
|
||||
")"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Add the moz_inputhistory table, if missing.
|
||||
bool tableExists = false;
|
||||
rv = mMainConn->TableExists(NS_LITERAL_CSTRING("moz_inputhistory"),
|
||||
&tableExists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!tableExists) {
|
||||
rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_INPUTHISTORY);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
Database::MigrateV8Up()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"DROP TRIGGER IF EXISTS moz_historyvisits_afterinsert_v1_trigger"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"DROP TRIGGER IF EXISTS moz_historyvisits_afterdelete_v1_trigger"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
||||
// bug #381795 - remove unused indexes
|
||||
rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"DROP INDEX IF EXISTS moz_places_titleindex"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"DROP INDEX IF EXISTS moz_annos_item_idindex"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"DROP INDEX IF EXISTS moz_annos_place_idindex"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Do a one-time re-creation of the moz_annos indexes (bug 415201)
|
||||
bool oldIndexExists = false;
|
||||
rv = mMainConn->IndexExists(NS_LITERAL_CSTRING("moz_annos_attributesindex"), &oldIndexExists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (oldIndexExists) {
|
||||
// drop old uri annos index
|
||||
rv = mMainConn->ExecuteSimpleSQL(
|
||||
NS_LITERAL_CSTRING("DROP INDEX moz_annos_attributesindex"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// create new uri annos index
|
||||
rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_ANNOS_PLACEATTRIBUTE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// drop old item annos index
|
||||
rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"DROP INDEX IF EXISTS moz_items_annos_attributesindex"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// create new item annos index
|
||||
rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_ITEMSANNOS_PLACEATTRIBUTE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
Database::MigrateV9Up()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
// Added in Bug 488966. The last_visit_date column caches the last
|
||||
// visit date, this enhances SELECT performances when we
|
||||
// need to sort visits by visit date.
|
||||
// The cached value is synced by triggers on every added or removed visit.
|
||||
// See nsPlacesTriggers.h for details on the triggers.
|
||||
bool oldIndexExists = false;
|
||||
nsresult rv = mMainConn->IndexExists(
|
||||
NS_LITERAL_CSTRING("moz_places_lastvisitdateindex"), &oldIndexExists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!oldIndexExists) {
|
||||
// Add last_visit_date column to moz_places.
|
||||
rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"ALTER TABLE moz_places ADD last_visit_date INTEGER"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_LASTVISITDATE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Now let's sync the column contents with real visit dates.
|
||||
// This query can be really slow due to disk access, since it will basically
|
||||
// dupe the table contents in the journal file, and then write them down
|
||||
// in the database.
|
||||
rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"UPDATE moz_places SET last_visit_date = "
|
||||
"(SELECT MAX(visit_date) "
|
||||
"FROM moz_historyvisits "
|
||||
"WHERE place_id = moz_places.id)"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
Database::MigrateV10Up()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
// LastModified is set to the same value as dateAdded on item creation.
|
||||
// This way we can use lastModified index to sort.
|
||||
nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"UPDATE moz_bookmarks SET lastModified = dateAdded "
|
||||
"WHERE lastModified IS NULL"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
Database::MigrateV11Up()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
// Temp tables are going away.
|
||||
// For triggers correctness, every time we pass through this migration
|
||||
// step, we must ensure correctness of visit_count values.
|
||||
nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"UPDATE moz_places SET visit_count = "
|
||||
"(SELECT count(*) FROM moz_historyvisits "
|
||||
"WHERE place_id = moz_places.id "
|
||||
"AND visit_type NOT IN ") +
|
||||
nsPrintfCString("(0,%d,%d,%d) ",
|
||||
nsINavHistoryService::TRANSITION_EMBED,
|
||||
nsINavHistoryService::TRANSITION_FRAMED_LINK,
|
||||
nsINavHistoryService::TRANSITION_DOWNLOAD) +
|
||||
NS_LITERAL_CSTRING(")")
|
||||
);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// For existing profiles, we may not have a moz_bookmarks.guid column
|
||||
nsCOMPtr<mozIStorageStatement> hasGuidStatement;
|
||||
rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT guid FROM moz_bookmarks"),
|
||||
getter_AddRefs(hasGuidStatement));
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
// moz_bookmarks grew a guid column. Add the column, but do not populate it
|
||||
// with anything just yet. We will do that soon.
|
||||
rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"ALTER TABLE moz_bookmarks "
|
||||
"ADD COLUMN guid TEXT"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_GUID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// moz_places grew a guid column. Add the column, but do not populate it
|
||||
// with anything just yet. We will do that soon.
|
||||
rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"ALTER TABLE moz_places "
|
||||
"ADD COLUMN guid TEXT"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_GUID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// We need to update our guids before we do any real database work.
|
||||
rv = CheckAndUpdateGUIDs();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Database::MigrateV13Up()
|
||||
{
|
||||
@ -1961,6 +1413,60 @@ Database::MigrateV24Up()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Database::MigrateV25Up()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Change bookmark roots GUIDs to constant values.
|
||||
|
||||
// If moz_bookmarks_roots doesn't exist anymore, it's because we finally have
|
||||
// been able to remove it. In such a case, we already assigned constant GUIDs
|
||||
// to the roots and we can skip this migration.
|
||||
{
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT root_name FROM moz_bookmarks_roots"
|
||||
), getter_AddRefs(stmt));
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"UPDATE moz_bookmarks SET guid = :guid "
|
||||
"WHERE id = (SELECT folder_id FROM moz_bookmarks_roots WHERE root_name = :name) "
|
||||
), getter_AddRefs(stmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
const char *rootNames[] = { "places", "menu", "toolbar", "tags", "unfiled" };
|
||||
const char *rootGuids[] = { "root________"
|
||||
, "menu________"
|
||||
, "toolbar_____"
|
||||
, "tags________"
|
||||
, "unfiled_____"
|
||||
};
|
||||
|
||||
for (uint32_t i = 0; i < ArrayLength(rootNames); ++i) {
|
||||
// Since this is using the synchronous API, we cannot use
|
||||
// a BindingParamsArray.
|
||||
mozStorageStatementScoper scoper(stmt);
|
||||
|
||||
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("name"),
|
||||
nsDependentCString(rootNames[i]));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"),
|
||||
nsDependentCString(rootGuids[i]));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = stmt->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
Database::Shutdown()
|
||||
{
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
// This is the schema version. Update it at any schema change and add a
|
||||
// corresponding migrateVxx method below.
|
||||
#define DATABASE_SCHEMA_VERSION 24
|
||||
#define DATABASE_SCHEMA_VERSION 25
|
||||
|
||||
// Fired after Places inited.
|
||||
#define TOPIC_PLACES_INIT_COMPLETE "places-init-complete"
|
||||
@ -260,11 +260,6 @@ protected:
|
||||
/**
|
||||
* Helpers used by schema upgrades.
|
||||
*/
|
||||
nsresult MigrateV7Up();
|
||||
nsresult MigrateV8Up();
|
||||
nsresult MigrateV9Up();
|
||||
nsresult MigrateV10Up();
|
||||
nsresult MigrateV11Up();
|
||||
nsresult MigrateV13Up();
|
||||
nsresult MigrateV14Up();
|
||||
nsresult MigrateV15Up();
|
||||
@ -277,9 +272,9 @@ protected:
|
||||
nsresult MigrateV22Up();
|
||||
nsresult MigrateV23Up();
|
||||
nsresult MigrateV24Up();
|
||||
nsresult MigrateV25Up();
|
||||
|
||||
nsresult UpdateBookmarkRootTitles();
|
||||
nsresult CheckAndUpdateGUIDs();
|
||||
|
||||
private:
|
||||
~Database();
|
||||
|
@ -502,9 +502,8 @@ this.PlacesBackups = {
|
||||
* * children: array of child items in a folder
|
||||
*/
|
||||
getBookmarksTree: Task.async(function* () {
|
||||
let rootGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.placesRootId);
|
||||
let startTime = Date.now();
|
||||
let root = yield PlacesUtils.promiseBookmarksTree(rootGuid, {
|
||||
let root = yield PlacesUtils.promiseBookmarksTree(PlacesUtils.bookmarks.rootGuid, {
|
||||
excludeItemsCallback: aItem => {
|
||||
return aItem.annos &&
|
||||
aItem.annos.find(a => a.name == PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO);
|
||||
|
@ -334,7 +334,7 @@ this.PlacesDBUtils = {
|
||||
)`);
|
||||
cleanupStatements.push(deleteOrphanAnnos);
|
||||
|
||||
// MOZ_BOOKMARKS_ROOTS
|
||||
// Bookmarks roots
|
||||
// C.1 fix missing Places root
|
||||
// Bug 477739 shows a case where the root could be wrongly removed
|
||||
// due to an endianness issue. We try to fix broken roots here.
|
||||
@ -346,17 +346,21 @@ this.PlacesDBUtils = {
|
||||
let createPlacesRoot = DBConn.createAsyncStatement(
|
||||
`INSERT INTO moz_bookmarks (id, type, fk, parent, position, title,
|
||||
guid)
|
||||
VALUES (:places_root, 2, NULL, 0, 0, :title, GENERATE_GUID())`);
|
||||
VALUES (:places_root, 2, NULL, 0, 0, :title, :guid)`);
|
||||
createPlacesRoot.params["places_root"] = PlacesUtils.placesRootId;
|
||||
createPlacesRoot.params["title"] = "";
|
||||
createPlacesRoot.params["guid"] = PlacesUtils.bookmarks.rootGuid;
|
||||
cleanupStatements.push(createPlacesRoot);
|
||||
|
||||
// Now ensure that other roots are children of Places root.
|
||||
let fixPlacesRootChildren = DBConn.createAsyncStatement(
|
||||
`UPDATE moz_bookmarks SET parent = :places_root WHERE id IN
|
||||
(SELECT folder_id FROM moz_bookmarks_roots
|
||||
WHERE folder_id <> :places_root)`);
|
||||
`UPDATE moz_bookmarks SET parent = :places_root WHERE guid IN
|
||||
( :menuGuid, :toolbarGuid, :unfiledGuid, :tagsGuid )`);
|
||||
fixPlacesRootChildren.params["places_root"] = PlacesUtils.placesRootId;
|
||||
fixPlacesRootChildren.params["menuGuid"] = PlacesUtils.bookmarks.menuGuid;
|
||||
fixPlacesRootChildren.params["toolbarGuid"] = PlacesUtils.bookmarks.toolbarGuid;
|
||||
fixPlacesRootChildren.params["unfiledGuid"] = PlacesUtils.bookmarks.unfiledGuid;
|
||||
fixPlacesRootChildren.params["tagsGuid"] = PlacesUtils.bookmarks.tagsGuid;
|
||||
cleanupStatements.push(fixPlacesRootChildren);
|
||||
}
|
||||
selectPlacesRoot.finalize();
|
||||
@ -400,20 +404,25 @@ this.PlacesDBUtils = {
|
||||
// D.1 remove items without a valid place
|
||||
// if fk IS NULL we fix them in D.7
|
||||
let deleteNoPlaceItems = DBConn.createAsyncStatement(
|
||||
`DELETE FROM moz_bookmarks WHERE id NOT IN (
|
||||
SELECT folder_id FROM moz_bookmarks_roots /* skip roots */
|
||||
`DELETE FROM moz_bookmarks WHERE guid NOT IN (
|
||||
:rootGuid, :menuGuid, :toolbarGuid, :unfiledGuid, :tagsGuid /* skip roots */
|
||||
) AND id IN (
|
||||
SELECT b.id FROM moz_bookmarks b
|
||||
WHERE fk NOT NULL AND b.type = :bookmark_type
|
||||
AND NOT EXISTS (SELECT url FROM moz_places WHERE id = b.fk LIMIT 1)
|
||||
)`);
|
||||
deleteNoPlaceItems.params["bookmark_type"] = PlacesUtils.bookmarks.TYPE_BOOKMARK;
|
||||
deleteNoPlaceItems.params["rootGuid"] = PlacesUtils.bookmarks.rootGuid;
|
||||
deleteNoPlaceItems.params["menuGuid"] = PlacesUtils.bookmarks.menuGuid;
|
||||
deleteNoPlaceItems.params["toolbarGuid"] = PlacesUtils.bookmarks.toolbarGuid;
|
||||
deleteNoPlaceItems.params["unfiledGuid"] = PlacesUtils.bookmarks.unfiledGuid;
|
||||
deleteNoPlaceItems.params["tagsGuid"] = PlacesUtils.bookmarks.tagsGuid;
|
||||
cleanupStatements.push(deleteNoPlaceItems);
|
||||
|
||||
// D.2 remove items that are not uri bookmarks from tag containers
|
||||
let deleteBogusTagChildren = DBConn.createAsyncStatement(
|
||||
`DELETE FROM moz_bookmarks WHERE id NOT IN (
|
||||
SELECT folder_id FROM moz_bookmarks_roots /* skip roots */
|
||||
`DELETE FROM moz_bookmarks WHERE guid NOT IN (
|
||||
:rootGuid, :menuGuid, :toolbarGuid, :unfiledGuid, :tagsGuid /* skip roots */
|
||||
) AND id IN (
|
||||
SELECT b.id FROM moz_bookmarks b
|
||||
WHERE b.parent IN
|
||||
@ -422,12 +431,17 @@ this.PlacesDBUtils = {
|
||||
)`);
|
||||
deleteBogusTagChildren.params["tags_folder"] = PlacesUtils.tagsFolderId;
|
||||
deleteBogusTagChildren.params["bookmark_type"] = PlacesUtils.bookmarks.TYPE_BOOKMARK;
|
||||
deleteBogusTagChildren.params["rootGuid"] = PlacesUtils.bookmarks.rootGuid;
|
||||
deleteBogusTagChildren.params["menuGuid"] = PlacesUtils.bookmarks.menuGuid;
|
||||
deleteBogusTagChildren.params["toolbarGuid"] = PlacesUtils.bookmarks.toolbarGuid;
|
||||
deleteBogusTagChildren.params["unfiledGuid"] = PlacesUtils.bookmarks.unfiledGuid;
|
||||
deleteBogusTagChildren.params["tagsGuid"] = PlacesUtils.bookmarks.tagsGuid;
|
||||
cleanupStatements.push(deleteBogusTagChildren);
|
||||
|
||||
// D.3 remove empty tags
|
||||
let deleteEmptyTags = DBConn.createAsyncStatement(
|
||||
`DELETE FROM moz_bookmarks WHERE id NOT IN (
|
||||
SELECT folder_id FROM moz_bookmarks_roots /* skip roots */
|
||||
`DELETE FROM moz_bookmarks WHERE guid NOT IN (
|
||||
:rootGuid, :menuGuid, :toolbarGuid, :unfiledGuid, :tagsGuid /* skip roots */
|
||||
) AND id IN (
|
||||
SELECT b.id FROM moz_bookmarks b
|
||||
WHERE b.id IN
|
||||
@ -436,31 +450,45 @@ this.PlacesDBUtils = {
|
||||
(SELECT id from moz_bookmarks WHERE parent = b.id LIMIT 1)
|
||||
)`);
|
||||
deleteEmptyTags.params["tags_folder"] = PlacesUtils.tagsFolderId;
|
||||
deleteEmptyTags.params["rootGuid"] = PlacesUtils.bookmarks.rootGuid;
|
||||
deleteEmptyTags.params["menuGuid"] = PlacesUtils.bookmarks.menuGuid;
|
||||
deleteEmptyTags.params["toolbarGuid"] = PlacesUtils.bookmarks.toolbarGuid;
|
||||
deleteEmptyTags.params["unfiledGuid"] = PlacesUtils.bookmarks.unfiledGuid;
|
||||
deleteEmptyTags.params["tagsGuid"] = PlacesUtils.bookmarks.tagsGuid;
|
||||
cleanupStatements.push(deleteEmptyTags);
|
||||
|
||||
// D.4 move orphan items to unsorted folder
|
||||
let fixOrphanItems = DBConn.createAsyncStatement(
|
||||
`UPDATE moz_bookmarks SET parent = :unsorted_folder WHERE id NOT IN (
|
||||
SELECT folder_id FROM moz_bookmarks_roots /* skip roots */
|
||||
`UPDATE moz_bookmarks SET parent = :unsorted_folder WHERE guid NOT IN (
|
||||
:rootGuid, :menuGuid, :toolbarGuid, :unfiledGuid, :tagsGuid /* skip roots */
|
||||
) AND id IN (
|
||||
SELECT b.id FROM moz_bookmarks b
|
||||
WHERE b.parent <> 0 /* exclude Places root */
|
||||
AND NOT EXISTS
|
||||
WHERE NOT EXISTS
|
||||
(SELECT id FROM moz_bookmarks WHERE id = b.parent LIMIT 1)
|
||||
)`);
|
||||
fixOrphanItems.params["unsorted_folder"] = PlacesUtils.unfiledBookmarksFolderId;
|
||||
fixOrphanItems.params["rootGuid"] = PlacesUtils.bookmarks.rootGuid;
|
||||
fixOrphanItems.params["menuGuid"] = PlacesUtils.bookmarks.menuGuid;
|
||||
fixOrphanItems.params["toolbarGuid"] = PlacesUtils.bookmarks.toolbarGuid;
|
||||
fixOrphanItems.params["unfiledGuid"] = PlacesUtils.bookmarks.unfiledGuid;
|
||||
fixOrphanItems.params["tagsGuid"] = PlacesUtils.bookmarks.tagsGuid;
|
||||
cleanupStatements.push(fixOrphanItems);
|
||||
|
||||
// D.5 fix wrong keywords
|
||||
let fixInvalidKeywords = DBConn.createAsyncStatement(
|
||||
`UPDATE moz_bookmarks SET keyword_id = NULL WHERE id NOT IN (
|
||||
SELECT folder_id FROM moz_bookmarks_roots /* skip roots */
|
||||
`UPDATE moz_bookmarks SET keyword_id = NULL WHERE guid NOT IN (
|
||||
:rootGuid, :menuGuid, :toolbarGuid, :unfiledGuid, :tagsGuid /* skip roots */
|
||||
) AND id IN (
|
||||
SELECT id FROM moz_bookmarks b
|
||||
WHERE keyword_id NOT NULL
|
||||
AND NOT EXISTS
|
||||
(SELECT id FROM moz_keywords WHERE id = b.keyword_id LIMIT 1)
|
||||
)`);
|
||||
fixInvalidKeywords.params["rootGuid"] = PlacesUtils.bookmarks.rootGuid;
|
||||
fixInvalidKeywords.params["menuGuid"] = PlacesUtils.bookmarks.menuGuid;
|
||||
fixInvalidKeywords.params["toolbarGuid"] = PlacesUtils.bookmarks.toolbarGuid;
|
||||
fixInvalidKeywords.params["unfiledGuid"] = PlacesUtils.bookmarks.unfiledGuid;
|
||||
fixInvalidKeywords.params["tagsGuid"] = PlacesUtils.bookmarks.tagsGuid;
|
||||
cleanupStatements.push(fixInvalidKeywords);
|
||||
|
||||
// D.6 fix wrong item types
|
||||
@ -468,8 +496,8 @@ this.PlacesDBUtils = {
|
||||
// If they have a valid fk convert them to bookmarks. Later in D.9 we
|
||||
// will move eventual children to unsorted bookmarks.
|
||||
let fixBookmarksAsFolders = DBConn.createAsyncStatement(
|
||||
`UPDATE moz_bookmarks SET type = :bookmark_type WHERE id NOT IN (
|
||||
SELECT folder_id FROM moz_bookmarks_roots /* skip roots */
|
||||
`UPDATE moz_bookmarks SET type = :bookmark_type WHERE guid NOT IN (
|
||||
:rootGuid, :menuGuid, :toolbarGuid, :unfiledGuid, :tagsGuid /* skip roots */
|
||||
) AND id IN (
|
||||
SELECT id FROM moz_bookmarks b
|
||||
WHERE type IN (:folder_type, :separator_type)
|
||||
@ -478,14 +506,19 @@ this.PlacesDBUtils = {
|
||||
fixBookmarksAsFolders.params["bookmark_type"] = PlacesUtils.bookmarks.TYPE_BOOKMARK;
|
||||
fixBookmarksAsFolders.params["folder_type"] = PlacesUtils.bookmarks.TYPE_FOLDER;
|
||||
fixBookmarksAsFolders.params["separator_type"] = PlacesUtils.bookmarks.TYPE_SEPARATOR;
|
||||
fixBookmarksAsFolders.params["rootGuid"] = PlacesUtils.bookmarks.rootGuid;
|
||||
fixBookmarksAsFolders.params["menuGuid"] = PlacesUtils.bookmarks.menuGuid;
|
||||
fixBookmarksAsFolders.params["toolbarGuid"] = PlacesUtils.bookmarks.toolbarGuid;
|
||||
fixBookmarksAsFolders.params["unfiledGuid"] = PlacesUtils.bookmarks.unfiledGuid;
|
||||
fixBookmarksAsFolders.params["tagsGuid"] = PlacesUtils.bookmarks.tagsGuid;
|
||||
cleanupStatements.push(fixBookmarksAsFolders);
|
||||
|
||||
// D.7 fix wrong item types
|
||||
// Bookmarks should have an fk, if they don't have any, convert them to
|
||||
// folders.
|
||||
let fixFoldersAsBookmarks = DBConn.createAsyncStatement(
|
||||
`UPDATE moz_bookmarks SET type = :folder_type WHERE id NOT IN (
|
||||
SELECT folder_id FROM moz_bookmarks_roots /* skip roots */
|
||||
`UPDATE moz_bookmarks SET type = :folder_type WHERE guid NOT IN (
|
||||
:rootGuid, :menuGuid, :toolbarGuid, :unfiledGuid, :tagsGuid /* skip roots */
|
||||
) AND id IN (
|
||||
SELECT id FROM moz_bookmarks b
|
||||
WHERE type = :bookmark_type
|
||||
@ -493,14 +526,19 @@ this.PlacesDBUtils = {
|
||||
)`);
|
||||
fixFoldersAsBookmarks.params["bookmark_type"] = PlacesUtils.bookmarks.TYPE_BOOKMARK;
|
||||
fixFoldersAsBookmarks.params["folder_type"] = PlacesUtils.bookmarks.TYPE_FOLDER;
|
||||
fixFoldersAsBookmarks.params["rootGuid"] = PlacesUtils.bookmarks.rootGuid;
|
||||
fixFoldersAsBookmarks.params["menuGuid"] = PlacesUtils.bookmarks.menuGuid;
|
||||
fixFoldersAsBookmarks.params["toolbarGuid"] = PlacesUtils.bookmarks.toolbarGuid;
|
||||
fixFoldersAsBookmarks.params["unfiledGuid"] = PlacesUtils.bookmarks.unfiledGuid;
|
||||
fixFoldersAsBookmarks.params["tagsGuid"] = PlacesUtils.bookmarks.tagsGuid;
|
||||
cleanupStatements.push(fixFoldersAsBookmarks);
|
||||
|
||||
// D.9 fix wrong parents
|
||||
// Items cannot have separators or other bookmarks
|
||||
// as parent, if they have bad parent move them to unsorted bookmarks.
|
||||
let fixInvalidParents = DBConn.createAsyncStatement(
|
||||
`UPDATE moz_bookmarks SET parent = :unsorted_folder WHERE id NOT IN (
|
||||
SELECT folder_id FROM moz_bookmarks_roots /* skip roots */
|
||||
`UPDATE moz_bookmarks SET parent = :unsorted_folder WHERE guid NOT IN (
|
||||
:rootGuid, :menuGuid, :toolbarGuid, :unfiledGuid, :tagsGuid /* skip roots */
|
||||
) AND id IN (
|
||||
SELECT id FROM moz_bookmarks b
|
||||
WHERE EXISTS
|
||||
@ -511,6 +549,11 @@ this.PlacesDBUtils = {
|
||||
fixInvalidParents.params["unsorted_folder"] = PlacesUtils.unfiledBookmarksFolderId;
|
||||
fixInvalidParents.params["bookmark_type"] = PlacesUtils.bookmarks.TYPE_BOOKMARK;
|
||||
fixInvalidParents.params["separator_type"] = PlacesUtils.bookmarks.TYPE_SEPARATOR;
|
||||
fixInvalidParents.params["rootGuid"] = PlacesUtils.bookmarks.rootGuid;
|
||||
fixInvalidParents.params["menuGuid"] = PlacesUtils.bookmarks.menuGuid;
|
||||
fixInvalidParents.params["toolbarGuid"] = PlacesUtils.bookmarks.toolbarGuid;
|
||||
fixInvalidParents.params["unfiledGuid"] = PlacesUtils.bookmarks.unfiledGuid;
|
||||
fixInvalidParents.params["tagsGuid"] = PlacesUtils.bookmarks.tagsGuid;
|
||||
cleanupStatements.push(fixInvalidParents);
|
||||
|
||||
// D.10 recalculate positions
|
||||
|
@ -1334,11 +1334,10 @@ PT.Tag.prototype = {
|
||||
|
||||
if (yield promiseIsBookmarked(currentURI)) {
|
||||
// Tagging is only allowed for bookmarked URIs (but see 424160).
|
||||
let unfiledGuid =
|
||||
yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let createTxn = TransactionsHistory.getRawTransaction(
|
||||
PT.NewBookmark({ uri: currentURI
|
||||
, tags: aTags, parentGuid: unfiledGuid }));
|
||||
, tags: aTags
|
||||
, parentGuid: PlacesUtils.bookmarks.unfiledGuid }));
|
||||
yield createTxn.execute();
|
||||
onUndo.unshift(createTxn.undo.bind(createTxn));
|
||||
onRedo.push(createTxn.redo.bind(createTxn));
|
||||
|
@ -1692,7 +1692,7 @@ this.PlacesUtils = {
|
||||
|
||||
|
||||
if (!aItemGuid)
|
||||
aItemGuid = yield this.promiseItemGuid(PlacesUtils.placesRootId);
|
||||
aItemGuid = this.bookmarks.rootGuid;
|
||||
|
||||
let hasExcludeItemsCallback =
|
||||
aOptions.hasOwnProperty("excludeItemsCallback");
|
||||
|
@ -277,34 +277,35 @@ nsNavBookmarks::ReadRoots()
|
||||
{
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
nsresult rv = mDB->MainConn()->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT root_name, folder_id FROM moz_bookmarks_roots"
|
||||
"SELECT guid, id FROM moz_bookmarks WHERE guid IN ( "
|
||||
"'root________', 'menu________', 'toolbar_____', "
|
||||
"'tags________', 'unfiled_____' )"
|
||||
), getter_AddRefs(stmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool hasResult;
|
||||
while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
|
||||
nsAutoCString rootName;
|
||||
rv = stmt->GetUTF8String(0, rootName);
|
||||
nsAutoCString guid;
|
||||
rv = stmt->GetUTF8String(0, guid);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
int64_t rootId;
|
||||
rv = stmt->GetInt64(1, &rootId);
|
||||
int64_t id;
|
||||
rv = stmt->GetInt64(1, &id);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ABORT_IF_FALSE(rootId != 0, "Root id is 0, that is an invalid value.");
|
||||
|
||||
if (rootName.EqualsLiteral("places")) {
|
||||
mRoot = rootId;
|
||||
if (guid.EqualsLiteral("root________")) {
|
||||
mRoot = id;
|
||||
}
|
||||
else if (rootName.EqualsLiteral("menu")) {
|
||||
mMenuRoot = rootId;
|
||||
else if (guid.EqualsLiteral("menu________")) {
|
||||
mMenuRoot = id;
|
||||
}
|
||||
else if (rootName.EqualsLiteral("toolbar")) {
|
||||
mToolbarRoot = rootId;
|
||||
else if (guid.EqualsLiteral("toolbar_____")) {
|
||||
mToolbarRoot = id;
|
||||
}
|
||||
else if (rootName.EqualsLiteral("tags")) {
|
||||
mTagsRoot = rootId;
|
||||
else if (guid.EqualsLiteral("tags________")) {
|
||||
mTagsRoot = id;
|
||||
}
|
||||
else if (rootName.EqualsLiteral("unfiled")) {
|
||||
mUnfiledRoot = rootId;
|
||||
else if (guid.EqualsLiteral("unfiled_____")) {
|
||||
mUnfiledRoot = id;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,11 +8,10 @@ add_task(function* test_eraseEverything() {
|
||||
let frecencyForMozilla = frecencyForUrl("http://example.com/");
|
||||
Assert.ok(frecencyForExample > 0);
|
||||
Assert.ok(frecencyForMozilla > 0);
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let unfiledFolder = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let unfiledFolder = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER });
|
||||
checkBookmarkObject(unfiledFolder);
|
||||
let unfiledBookmark = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let unfiledBookmark = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
url: "http://example.com/",
|
||||
keyword: "kw1" });
|
||||
@ -25,11 +24,10 @@ add_task(function* test_eraseEverything() {
|
||||
PlacesUtils.annotations.setItemAnnotation((yield PlacesUtils.promiseItemId(unfiledBookmarkInFolder.guid)),
|
||||
"testanno1", "testvalue1", 0, 0);
|
||||
|
||||
let menuGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.bookmarksMenuFolderId);
|
||||
let menuFolder = yield PlacesUtils.bookmarks.insert({ parentGuid: menuGuid,
|
||||
let menuFolder = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.menuGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER });
|
||||
checkBookmarkObject(menuFolder);
|
||||
let menuBookmark = yield PlacesUtils.bookmarks.insert({ parentGuid: menuGuid,
|
||||
let menuBookmark = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.menuGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
url: "http://example.com/",
|
||||
keyword: "kw2" });
|
||||
@ -42,11 +40,10 @@ add_task(function* test_eraseEverything() {
|
||||
PlacesUtils.annotations.setItemAnnotation((yield PlacesUtils.promiseItemId(menuBookmarkInFolder.guid)),
|
||||
"testanno1", "testvalue1", 0, 0);
|
||||
|
||||
let toolbarGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.toolbarFolderId);
|
||||
let toolbarFolder = yield PlacesUtils.bookmarks.insert({ parentGuid: toolbarGuid,
|
||||
let toolbarFolder = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER });
|
||||
checkBookmarkObject(toolbarFolder);
|
||||
let toolbarBookmark = yield PlacesUtils.bookmarks.insert({ parentGuid: toolbarGuid,
|
||||
let toolbarBookmark = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
url: "http://example.com/",
|
||||
keyword: "kw3" });
|
||||
@ -82,16 +79,11 @@ add_task(function* test_eraseEverything_roots() {
|
||||
yield PlacesUtils.bookmarks.eraseEverything();
|
||||
|
||||
// Ensure the roots have not been removed.
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
Assert.ok(yield PlacesUtils.bookmarks.fetch(unfiledGuid));
|
||||
let toolbarGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.toolbarFolderId);
|
||||
Assert.ok(yield PlacesUtils.bookmarks.fetch(toolbarGuid));
|
||||
let menuGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.bookmarksMenuFolderId);
|
||||
Assert.ok(yield PlacesUtils.bookmarks.fetch(menuGuid));
|
||||
let tagsGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.tagsFolderId);
|
||||
Assert.ok(yield PlacesUtils.bookmarks.fetch(tagsGuid));
|
||||
let rootGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.placesRootId);
|
||||
Assert.ok(yield PlacesUtils.bookmarks.fetch(rootGuid));
|
||||
Assert.ok(yield PlacesUtils.bookmarks.fetch(PlacesUtils.bookmarks.unfiledGuid));
|
||||
Assert.ok(yield PlacesUtils.bookmarks.fetch(PlacesUtils.bookmarks.toolbarGuid));
|
||||
Assert.ok(yield PlacesUtils.bookmarks.fetch(PlacesUtils.bookmarks.menuGuid));
|
||||
Assert.ok(yield PlacesUtils.bookmarks.fetch(PlacesUtils.bookmarks.tagsGuid));
|
||||
Assert.ok(yield PlacesUtils.bookmarks.fetch(PlacesUtils.bookmarks.rootGuid));
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
|
@ -100,8 +100,7 @@ add_task(function* fetch_nonexistent_guid() {
|
||||
});
|
||||
|
||||
add_task(function* fetch_bookmark() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
url: "http://example.com/",
|
||||
title: "a bookmark" });
|
||||
@ -115,7 +114,7 @@ add_task(function* fetch_bookmark() {
|
||||
Assert.deepEqual(gAccumulator.results[0], bm1);
|
||||
|
||||
Assert.deepEqual(bm1, bm2);
|
||||
Assert.equal(bm2.parentGuid, unfiledGuid);
|
||||
Assert.equal(bm2.parentGuid, PlacesUtils.bookmarks.unfiledGuid);
|
||||
Assert.equal(bm2.index, 0);
|
||||
Assert.deepEqual(bm2.dateAdded, bm2.lastModified);
|
||||
Assert.equal(bm2.type, PlacesUtils.bookmarks.TYPE_BOOKMARK);
|
||||
@ -127,8 +126,7 @@ add_task(function* fetch_bookmark() {
|
||||
});
|
||||
|
||||
add_task(function* fetch_bookmar_empty_title() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
url: "http://example.com/",
|
||||
title: "" });
|
||||
@ -146,8 +144,7 @@ add_task(function* fetch_bookmar_empty_title() {
|
||||
});
|
||||
|
||||
add_task(function* fetch_folder() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
title: "a folder" });
|
||||
checkBookmarkObject(bm1);
|
||||
@ -156,7 +153,7 @@ add_task(function* fetch_folder() {
|
||||
checkBookmarkObject(bm2);
|
||||
|
||||
Assert.deepEqual(bm1, bm2);
|
||||
Assert.equal(bm2.parentGuid, unfiledGuid);
|
||||
Assert.equal(bm2.parentGuid, PlacesUtils.bookmarks.unfiledGuid);
|
||||
Assert.equal(bm2.index, 0);
|
||||
Assert.deepEqual(bm2.dateAdded, bm2.lastModified);
|
||||
Assert.equal(bm2.type, PlacesUtils.bookmarks.TYPE_FOLDER);
|
||||
@ -168,8 +165,7 @@ add_task(function* fetch_folder() {
|
||||
});
|
||||
|
||||
add_task(function* fetch_folder_empty_title() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
title: "" });
|
||||
checkBookmarkObject(bm1);
|
||||
@ -185,9 +181,7 @@ add_task(function* fetch_folder_empty_title() {
|
||||
});
|
||||
|
||||
add_task(function* fetch_separator() {
|
||||
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_SEPARATOR });
|
||||
checkBookmarkObject(bm1);
|
||||
|
||||
@ -195,7 +189,7 @@ add_task(function* fetch_separator() {
|
||||
checkBookmarkObject(bm2);
|
||||
|
||||
Assert.deepEqual(bm1, bm2);
|
||||
Assert.equal(bm2.parentGuid, unfiledGuid);
|
||||
Assert.equal(bm2.parentGuid, PlacesUtils.bookmarks.unfiledGuid);
|
||||
Assert.equal(bm2.index, 0);
|
||||
Assert.deepEqual(bm2.dateAdded, bm2.lastModified);
|
||||
Assert.equal(bm2.type, PlacesUtils.bookmarks.TYPE_SEPARATOR);
|
||||
@ -215,8 +209,7 @@ add_task(function* fetch_byposition_nonexisting_parentGuid() {
|
||||
});
|
||||
|
||||
add_task(function* fetch_byposition_nonexisting_index() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm = yield PlacesUtils.bookmarks.fetch({ parentGuid: unfiledGuid,
|
||||
let bm = yield PlacesUtils.bookmarks.fetch({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
index: 100 },
|
||||
gAccumulator.callback);
|
||||
Assert.equal(bm, null);
|
||||
@ -224,8 +217,7 @@ add_task(function* fetch_byposition_nonexisting_index() {
|
||||
});
|
||||
|
||||
add_task(function* fetch_byposition() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
url: "http://example.com/",
|
||||
title: "a bookmark" });
|
||||
@ -240,7 +232,7 @@ add_task(function* fetch_byposition() {
|
||||
Assert.deepEqual(gAccumulator.results[0], bm1);
|
||||
|
||||
Assert.deepEqual(bm1, bm2);
|
||||
Assert.equal(bm2.parentGuid, unfiledGuid);
|
||||
Assert.equal(bm2.parentGuid, PlacesUtils.bookmarks.unfiledGuid);
|
||||
Assert.equal(bm2.index, 0);
|
||||
Assert.deepEqual(bm2.dateAdded, bm2.lastModified);
|
||||
Assert.equal(bm2.type, PlacesUtils.bookmarks.TYPE_BOOKMARK);
|
||||
@ -257,8 +249,7 @@ add_task(function* fetch_byurl_nonexisting() {
|
||||
});
|
||||
|
||||
add_task(function* fetch_byurl() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
url: "http://byurl.com/",
|
||||
title: "a bookmark" });
|
||||
@ -272,14 +263,14 @@ add_task(function* fetch_byurl() {
|
||||
Assert.deepEqual(gAccumulator.results[0], bm1);
|
||||
|
||||
Assert.deepEqual(bm1, bm2);
|
||||
Assert.equal(bm2.parentGuid, unfiledGuid);
|
||||
Assert.equal(bm2.parentGuid, PlacesUtils.bookmarks.unfiledGuid);
|
||||
Assert.deepEqual(bm2.dateAdded, bm2.lastModified);
|
||||
Assert.equal(bm2.type, PlacesUtils.bookmarks.TYPE_BOOKMARK);
|
||||
Assert.equal(bm2.url.href, "http://byurl.com/");
|
||||
Assert.equal(bm2.title, "a bookmark");
|
||||
Assert.ok(!("keyword" in bm2));
|
||||
|
||||
let bm3 = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm3 = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
url: "http://byurl.com/",
|
||||
title: "a bookmark" });
|
||||
@ -312,8 +303,7 @@ add_task(function* fetch_bykeyword_nonexisting() {
|
||||
});
|
||||
|
||||
add_task(function* fetch_bykeyword() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
url: "http://bykeyword1.com/",
|
||||
keyword: "bykeyword" });
|
||||
@ -327,13 +317,13 @@ add_task(function* fetch_bykeyword() {
|
||||
Assert.deepEqual(gAccumulator.results[0], bm1);
|
||||
|
||||
Assert.deepEqual(bm1, bm2);
|
||||
Assert.equal(bm2.parentGuid, unfiledGuid);
|
||||
Assert.equal(bm2.parentGuid, PlacesUtils.bookmarks.unfiledGuid);
|
||||
Assert.deepEqual(bm2.dateAdded, bm2.lastModified);
|
||||
Assert.equal(bm2.type, PlacesUtils.bookmarks.TYPE_BOOKMARK);
|
||||
Assert.equal(bm2.url.href, "http://bykeyword1.com/");
|
||||
Assert.equal(bm2.keyword, "bykeyword");
|
||||
|
||||
let bm3 = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm3 = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
url: "http://bykeyword2.com/",
|
||||
keyword: "bykeyword" });
|
||||
|
@ -115,12 +115,11 @@ add_task(function* long_title_trim() {
|
||||
for (let i = 0; i < 4096; i++) {
|
||||
longtitle += "a";
|
||||
}
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
title: longtitle });
|
||||
checkBookmarkObject(bm);
|
||||
Assert.equal(bm.parentGuid, unfiledGuid);
|
||||
Assert.equal(bm.parentGuid, PlacesUtils.bookmarks.unfiledGuid);
|
||||
Assert.equal(bm.index, 0);
|
||||
Assert.equal(bm.dateAdded, bm.lastModified);
|
||||
Assert.equal(bm.type, PlacesUtils.bookmarks.TYPE_FOLDER);
|
||||
@ -130,12 +129,11 @@ add_task(function* long_title_trim() {
|
||||
});
|
||||
|
||||
add_task(function* create_separator() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
|
||||
index: PlacesUtils.bookmarks.DEFAULT_INDEX });
|
||||
checkBookmarkObject(bm);
|
||||
Assert.equal(bm.parentGuid, unfiledGuid);
|
||||
Assert.equal(bm.parentGuid, PlacesUtils.bookmarks.unfiledGuid);
|
||||
Assert.equal(bm.index, 1);
|
||||
Assert.equal(bm.dateAdded, bm.lastModified);
|
||||
Assert.equal(bm.type, PlacesUtils.bookmarks.TYPE_SEPARATOR);
|
||||
@ -143,9 +141,8 @@ add_task(function* create_separator() {
|
||||
});
|
||||
|
||||
add_task(function* create_separator_w_title_fail() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
try {
|
||||
yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
|
||||
title: "a separator" });
|
||||
Assert.ok(false, "Trying to set title for a separator should reject");
|
||||
@ -162,14 +159,13 @@ add_task(function* create_separator_invalid_parent_fail() {
|
||||
});
|
||||
|
||||
add_task(function* create_separator_given_guid() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
|
||||
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
|
||||
guid: "123456789012" });
|
||||
checkBookmarkObject(bm);
|
||||
Assert.equal(bm.guid, "123456789012");
|
||||
Assert.equal(bm.parentGuid, unfiledGuid);
|
||||
Assert.equal(bm.parentGuid, PlacesUtils.bookmarks.unfiledGuid);
|
||||
Assert.equal(bm.index, 2);
|
||||
Assert.equal(bm.dateAdded, bm.lastModified);
|
||||
Assert.equal(bm.type, PlacesUtils.bookmarks.TYPE_SEPARATOR);
|
||||
@ -184,12 +180,11 @@ add_task(function* create_item_given_guid_no_type_fail() {
|
||||
});
|
||||
|
||||
add_task(function* create_separator_big_index() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
|
||||
index: 9999 });
|
||||
checkBookmarkObject(bm);
|
||||
Assert.equal(bm.parentGuid, unfiledGuid);
|
||||
Assert.equal(bm.parentGuid, PlacesUtils.bookmarks.unfiledGuid);
|
||||
Assert.equal(bm.index, 3);
|
||||
Assert.equal(bm.dateAdded, bm.lastModified);
|
||||
Assert.equal(bm.type, PlacesUtils.bookmarks.TYPE_SEPARATOR);
|
||||
@ -199,8 +194,7 @@ add_task(function* create_separator_big_index() {
|
||||
add_task(function* create_separator_given_dateAdded() {
|
||||
let time = new Date();
|
||||
let past = new Date(time - 86400000);
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
|
||||
dateAdded: past });
|
||||
checkBookmarkObject(bm);
|
||||
@ -209,11 +203,10 @@ add_task(function* create_separator_given_dateAdded() {
|
||||
});
|
||||
|
||||
add_task(function* create_folder() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER });
|
||||
checkBookmarkObject(bm);
|
||||
Assert.equal(bm.parentGuid, unfiledGuid);
|
||||
Assert.equal(bm.parentGuid, PlacesUtils.bookmarks.unfiledGuid);
|
||||
Assert.equal(bm.dateAdded, bm.lastModified);
|
||||
Assert.equal(bm.type, PlacesUtils.bookmarks.TYPE_FOLDER);
|
||||
Assert.ok(!("title" in bm), "title should not be set");
|
||||
@ -231,8 +224,7 @@ add_task(function* create_folder() {
|
||||
});
|
||||
|
||||
add_task(function* create_bookmark() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER });
|
||||
let parentGuid = bm.guid;
|
||||
|
||||
@ -278,8 +270,7 @@ add_task(function* create_bookmark() {
|
||||
});
|
||||
|
||||
add_task(function* create_bookmark_frecency() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
url: "http://example.com/",
|
||||
title: "a bookmark" });
|
||||
|
@ -2,10 +2,9 @@
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
add_task(function* insert_separator_notification() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let observer = expectNotifications();
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
|
||||
parentGuid: unfiledGuid});
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid});
|
||||
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
|
||||
let parentId = yield PlacesUtils.promiseItemId(bm.parentGuid);
|
||||
observer.check([ { name: "onItemAdded",
|
||||
@ -16,10 +15,9 @@ add_task(function* insert_separator_notification() {
|
||||
});
|
||||
|
||||
add_task(function* insert_folder_notification() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let observer = expectNotifications();
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
parentGuid: unfiledGuid,
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
title: "a folder" });
|
||||
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
|
||||
let parentId = yield PlacesUtils.promiseItemId(bm.parentGuid);
|
||||
@ -31,10 +29,9 @@ add_task(function* insert_folder_notification() {
|
||||
});
|
||||
|
||||
add_task(function* insert_folder_notitle_notification() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let observer = expectNotifications();
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
parentGuid: unfiledGuid });
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid });
|
||||
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
|
||||
let parentId = yield PlacesUtils.promiseItemId(bm.parentGuid);
|
||||
observer.check([ { name: "onItemAdded",
|
||||
@ -45,10 +42,9 @@ add_task(function* insert_folder_notitle_notification() {
|
||||
});
|
||||
|
||||
add_task(function* insert_bookmark_notification() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let observer = expectNotifications();
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
parentGuid: unfiledGuid,
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
url: new URL("http://example.com/"),
|
||||
title: "a bookmark" });
|
||||
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
|
||||
@ -61,10 +57,9 @@ add_task(function* insert_bookmark_notification() {
|
||||
});
|
||||
|
||||
add_task(function* insert_bookmark_notitle_notification() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let observer = expectNotifications();
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
parentGuid: unfiledGuid,
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
url: new URL("http://example.com/") });
|
||||
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
|
||||
let parentId = yield PlacesUtils.promiseItemId(bm.parentGuid);
|
||||
@ -76,10 +71,9 @@ add_task(function* insert_bookmark_notitle_notification() {
|
||||
});
|
||||
|
||||
add_task(function* insert_bookmark_keyword_notification() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let observer = expectNotifications();
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
parentGuid: unfiledGuid,
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
url: new URL("http://example.com/"),
|
||||
keyword: "kw" });
|
||||
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
|
||||
@ -96,16 +90,14 @@ add_task(function* insert_bookmark_keyword_notification() {
|
||||
});
|
||||
|
||||
add_task(function* insert_bookmark_tag_notification() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
parentGuid: unfiledGuid,
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
url: new URL("http://tag.example.com/") });
|
||||
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
|
||||
let parentId = yield PlacesUtils.promiseItemId(bm.parentGuid);
|
||||
|
||||
let tagsGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.tagsFolderId);
|
||||
let tagFolder = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
parentGuid: tagsGuid,
|
||||
parentGuid: PlacesUtils.bookmarks.tagsGuid,
|
||||
title: "tag" });
|
||||
let observer = expectNotifications();
|
||||
let tag = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
@ -130,9 +122,8 @@ add_task(function* insert_bookmark_tag_notification() {
|
||||
});
|
||||
|
||||
add_task(function* update_bookmark_lastModified() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
parentGuid: unfiledGuid,
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
url: new URL("http://lastmod.example.com/") });
|
||||
let observer = expectNotifications();
|
||||
bm = yield PlacesUtils.bookmarks.update({ guid: bm.guid,
|
||||
@ -148,9 +139,8 @@ add_task(function* update_bookmark_lastModified() {
|
||||
});
|
||||
|
||||
add_task(function* update_bookmark_title() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
parentGuid: unfiledGuid,
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
url: new URL("http://title.example.com/") });
|
||||
let observer = expectNotifications();
|
||||
bm = yield PlacesUtils.bookmarks.update({ guid: bm.guid,
|
||||
@ -166,9 +156,8 @@ add_task(function* update_bookmark_title() {
|
||||
});
|
||||
|
||||
add_task(function* update_bookmark_uri() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
parentGuid: unfiledGuid,
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
url: new URL("http://url.example.com/") });
|
||||
let observer = expectNotifications();
|
||||
bm = yield PlacesUtils.bookmarks.update({ guid: bm.guid,
|
||||
@ -184,9 +173,8 @@ add_task(function* update_bookmark_uri() {
|
||||
});
|
||||
|
||||
add_task(function* update_bookmark_keyword() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
parentGuid: unfiledGuid,
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
url: new URL("http://keyword.example.com/") });
|
||||
let observer = expectNotifications();
|
||||
bm = yield PlacesUtils.bookmarks.update({ guid: bm.guid,
|
||||
@ -202,9 +190,8 @@ add_task(function* update_bookmark_keyword() {
|
||||
});
|
||||
|
||||
add_task(function* remove_bookmark() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
parentGuid: unfiledGuid,
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
url: new URL("http://remove.example.com/") });
|
||||
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
|
||||
let parentId = yield PlacesUtils.promiseItemId(bm.parentGuid);
|
||||
@ -220,9 +207,8 @@ add_task(function* remove_bookmark() {
|
||||
});
|
||||
|
||||
add_task(function* remove_folder() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
parentGuid: unfiledGuid });
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid });
|
||||
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
|
||||
let parentId = yield PlacesUtils.promiseItemId(bm.parentGuid);
|
||||
|
||||
@ -235,16 +221,14 @@ add_task(function* remove_folder() {
|
||||
});
|
||||
|
||||
add_task(function* remove_bookmark_tag_notification() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
parentGuid: unfiledGuid,
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
url: new URL("http://untag.example.com/") });
|
||||
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
|
||||
let parentId = yield PlacesUtils.promiseItemId(bm.parentGuid);
|
||||
|
||||
let tagsGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.tagsFolderId);
|
||||
let tagFolder = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
parentGuid: tagsGuid,
|
||||
parentGuid: PlacesUtils.bookmarks.tagsGuid,
|
||||
title: "tag" });
|
||||
let tag = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
parentGuid: tagFolder.guid,
|
||||
@ -269,9 +253,8 @@ add_task(function* eraseEverything_notification() {
|
||||
// Let's start from a clean situation.
|
||||
yield PlacesUtils.bookmarks.eraseEverything();
|
||||
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let folder1 = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
parentGuid: unfiledGuid });
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid });
|
||||
let folder1Id = yield PlacesUtils.promiseItemId(folder1.guid);
|
||||
let folder1ParentId = yield PlacesUtils.promiseItemId(folder1.parentGuid);
|
||||
|
||||
@ -282,20 +265,18 @@ add_task(function* eraseEverything_notification() {
|
||||
let parentId = yield PlacesUtils.promiseItemId(bm.parentGuid);
|
||||
|
||||
let folder2 = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
parentGuid: unfiledGuid });
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid });
|
||||
let folder2Id = yield PlacesUtils.promiseItemId(folder2.guid);
|
||||
let folder2ParentId = yield PlacesUtils.promiseItemId(folder2.parentGuid);
|
||||
|
||||
let toolbarGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.toolbarFolderId);
|
||||
let toolbarBm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
parentGuid: toolbarGuid,
|
||||
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
url: new URL("http://example.com/") });
|
||||
let toolbarBmId = yield PlacesUtils.promiseItemId(toolbarBm.guid);
|
||||
let toolbarBmParentId = yield PlacesUtils.promiseItemId(toolbarBm.parentGuid);
|
||||
|
||||
let menuGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.bookmarksMenuFolderId);
|
||||
let menuBm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
parentGuid: menuGuid,
|
||||
parentGuid: PlacesUtils.bookmarks.menuGuid,
|
||||
url: new URL("http://example.com/") });
|
||||
let menuBmId = yield PlacesUtils.promiseItemId(menuBm.guid);
|
||||
let menuBmParentId = yield PlacesUtils.promiseItemId(menuBm.parentGuid);
|
||||
|
@ -44,17 +44,15 @@ add_task(function* remove_nonexistent_guid() {
|
||||
});
|
||||
|
||||
add_task(function* remove_roots_fail() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
try {
|
||||
yield PlacesUtils.bookmarks.remove(unfiledGuid);
|
||||
yield PlacesUtils.bookmarks.remove(PlacesUtils.bookmarks.unfiledGuid);
|
||||
Assert.ok(false, "Should have thrown");
|
||||
} catch (ex) {
|
||||
Assert.ok(/It's not possible to remove Places root folders/.test(ex));
|
||||
}
|
||||
|
||||
let placesRootGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.placesRootId);
|
||||
try {
|
||||
yield PlacesUtils.bookmarks.remove(placesRootGuid);
|
||||
yield PlacesUtils.bookmarks.remove(PlacesUtils.bookmarks.rootGuid);
|
||||
Assert.ok(false, "Should have thrown");
|
||||
} catch (ex) {
|
||||
Assert.ok(/It's not possible to remove Places root folders/.test(ex));
|
||||
@ -62,8 +60,7 @@ add_task(function* remove_roots_fail() {
|
||||
});
|
||||
|
||||
add_task(function* remove_bookmark() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
url: "http://example.com/",
|
||||
title: "a bookmark" });
|
||||
@ -73,7 +70,7 @@ add_task(function* remove_bookmark() {
|
||||
checkBookmarkObject(bm2);
|
||||
|
||||
Assert.deepEqual(bm1, bm2);
|
||||
Assert.equal(bm2.parentGuid, unfiledGuid);
|
||||
Assert.equal(bm2.parentGuid, PlacesUtils.bookmarks.unfiledGuid);
|
||||
Assert.equal(bm2.index, 0);
|
||||
Assert.deepEqual(bm2.dateAdded, bm2.lastModified);
|
||||
Assert.equal(bm2.type, PlacesUtils.bookmarks.TYPE_BOOKMARK);
|
||||
@ -84,8 +81,7 @@ add_task(function* remove_bookmark() {
|
||||
|
||||
|
||||
add_task(function* remove_bookmark_orphans() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
url: "http://example.com/",
|
||||
title: "a bookmark",
|
||||
@ -111,8 +107,7 @@ add_task(function* remove_bookmark_orphans() {
|
||||
});
|
||||
|
||||
add_task(function* remove_bookmark_empty_title() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
url: "http://example.com/",
|
||||
title: "" });
|
||||
@ -128,8 +123,7 @@ add_task(function* remove_bookmark_empty_title() {
|
||||
});
|
||||
|
||||
add_task(function* remove_folder() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
title: "a folder" });
|
||||
checkBookmarkObject(bm1);
|
||||
@ -138,7 +132,7 @@ add_task(function* remove_folder() {
|
||||
checkBookmarkObject(bm2);
|
||||
|
||||
Assert.deepEqual(bm1, bm2);
|
||||
Assert.equal(bm2.parentGuid, unfiledGuid);
|
||||
Assert.equal(bm2.parentGuid, PlacesUtils.bookmarks.unfiledGuid);
|
||||
Assert.equal(bm2.index, 0);
|
||||
Assert.deepEqual(bm2.dateAdded, bm2.lastModified);
|
||||
Assert.equal(bm2.type, PlacesUtils.bookmarks.TYPE_FOLDER);
|
||||
@ -148,8 +142,7 @@ add_task(function* remove_folder() {
|
||||
});
|
||||
|
||||
add_task(function* remove_folder_empty_title() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
title: "" });
|
||||
checkBookmarkObject(bm1);
|
||||
@ -163,8 +156,7 @@ add_task(function* remove_folder_empty_title() {
|
||||
});
|
||||
|
||||
add_task(function* remove_separator() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm1 = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_SEPARATOR });
|
||||
checkBookmarkObject(bm1);
|
||||
|
||||
@ -172,7 +164,7 @@ add_task(function* remove_separator() {
|
||||
checkBookmarkObject(bm2);
|
||||
|
||||
Assert.deepEqual(bm1, bm2);
|
||||
Assert.equal(bm2.parentGuid, unfiledGuid);
|
||||
Assert.equal(bm2.parentGuid, PlacesUtils.bookmarks.unfiledGuid);
|
||||
Assert.equal(bm2.index, 0);
|
||||
Assert.deepEqual(bm2.dateAdded, bm2.lastModified);
|
||||
Assert.equal(bm2.type, PlacesUtils.bookmarks.TYPE_SEPARATOR);
|
||||
|
@ -97,9 +97,8 @@ add_task(function* nonexisting_bookmark_throws() {
|
||||
});
|
||||
|
||||
add_task(function* invalid_properties_for_existing_bookmark() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
parentGuid: unfiledGuid,
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
url: "http://example.com/" });
|
||||
|
||||
try {
|
||||
@ -145,7 +144,7 @@ add_task(function* invalid_properties_for_existing_bookmark() {
|
||||
}
|
||||
|
||||
let folder = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
parentGuid: unfiledGuid });
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid });
|
||||
try {
|
||||
yield PlacesUtils.bookmarks.update({ guid: folder.guid,
|
||||
url: "http://example.com/" });
|
||||
@ -162,7 +161,7 @@ add_task(function* invalid_properties_for_existing_bookmark() {
|
||||
}
|
||||
|
||||
let separator = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
|
||||
parentGuid: unfiledGuid });
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid });
|
||||
try {
|
||||
yield PlacesUtils.bookmarks.update({ guid: separator.guid,
|
||||
url: "http://example.com/" });
|
||||
@ -191,8 +190,7 @@ add_task(function* long_title_trim() {
|
||||
for (let i = 0; i < 4096; i++) {
|
||||
longtitle += "a";
|
||||
}
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
title: "title" });
|
||||
checkBookmarkObject(bm);
|
||||
@ -207,9 +205,8 @@ add_task(function* long_title_trim() {
|
||||
});
|
||||
|
||||
add_task(function* update_lastModified() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let yesterday = new Date(Date.now() - 86400000);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
title: "title",
|
||||
dateAdded: yesterday });
|
||||
@ -242,8 +239,7 @@ add_task(function* update_lastModified() {
|
||||
});
|
||||
|
||||
add_task(function* update_keyword() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
url: "http://example.com/",
|
||||
title: "title",
|
||||
@ -277,8 +273,7 @@ add_task(function* update_keyword() {
|
||||
});
|
||||
|
||||
add_task(function* update_url() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
|
||||
url: "http://example.com/",
|
||||
title: "title",
|
||||
@ -303,8 +298,7 @@ add_task(function* update_url() {
|
||||
});
|
||||
|
||||
add_task(function* update_index() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let parent = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let parent = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER }) ;
|
||||
let f1 = yield PlacesUtils.bookmarks.insert({ parentGuid: parent.guid,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER });
|
||||
@ -344,8 +338,7 @@ add_task(function* update_index() {
|
||||
});
|
||||
|
||||
add_task(function* update_move_folder_into_descendant_throws() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let parent = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let parent = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER }) ;
|
||||
let descendant = yield PlacesUtils.bookmarks.insert({ parentGuid: parent.guid,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER });
|
||||
@ -370,8 +363,7 @@ add_task(function* update_move_folder_into_descendant_throws() {
|
||||
});
|
||||
|
||||
add_task(function* update_move() {
|
||||
let unfiledGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.unfiledBookmarksFolderId);
|
||||
let parent = yield PlacesUtils.bookmarks.insert({ parentGuid: unfiledGuid,
|
||||
let parent = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER }) ;
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: parent.guid,
|
||||
url: "http://example.com/",
|
||||
|
@ -3,7 +3,8 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const CURRENT_SCHEMA_VERSION = 24;
|
||||
const CURRENT_SCHEMA_VERSION = 25;
|
||||
const FIRST_UPGRADABLE_SCHEMA_VERSION = 11;
|
||||
|
||||
const NS_APP_USER_PROFILE_50_DIR = "ProfD";
|
||||
const NS_APP_PROFILE_DIR_STARTUP = "ProfDS";
|
||||
@ -43,6 +44,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "PlacesTransactions",
|
||||
"resource://gre/modules/PlacesTransactions.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS",
|
||||
"resource://gre/modules/osfile.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Sqlite",
|
||||
"resource://gre/modules/Sqlite.jsm");
|
||||
|
||||
// This imports various other objects in addition to PlacesUtils.
|
||||
Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
|
@ -1,10 +1,9 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const Cc = Components.classes;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
"use strict"
|
||||
|
||||
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
@ -16,25 +15,31 @@ let (commonFile = do_get_file("../head_common.js", false)) {
|
||||
|
||||
// Put any other stuff relative to this test folder below.
|
||||
|
||||
const kDBName = "places.sqlite";
|
||||
const DB_FILENAME = "places.sqlite";
|
||||
|
||||
/**
|
||||
* Sets the database to use for the given test. This should be the very first
|
||||
* thing we do otherwise, this database will not be used!
|
||||
* thing in the test, otherwise this database will not be used!
|
||||
*
|
||||
* @param aFileName
|
||||
* The filename of the database to use. This database must exist in
|
||||
* toolkit/components/places/tests/migration!
|
||||
* @return {Promise}
|
||||
*/
|
||||
function setPlacesDatabase(aFileName)
|
||||
{
|
||||
let file = do_get_file(aFileName);
|
||||
let setupPlacesDatabase = Task.async(function* (aFileName) {
|
||||
let currentDir = yield OS.File.getCurrentDirectory();
|
||||
|
||||
let src = OS.Path.join(currentDir, aFileName);
|
||||
Assert.ok((yield OS.File.exists(src)), "Database file found");
|
||||
|
||||
// Ensure that our database doesn't already exist.
|
||||
let (dbFile = gProfD.clone()) {
|
||||
dbFile.append(kDBName);
|
||||
do_check_false(dbFile.exists());
|
||||
}
|
||||
let dest = OS.Path.join(OS.Constants.Path.profileDir, DB_FILENAME);
|
||||
Assert.ok(!(yield OS.File.exists(dest)), "Database file should not exist yet");
|
||||
|
||||
file.copyToFollowingLinks(gProfD, kDBName);
|
||||
yield OS.File.copy(src, dest);
|
||||
});
|
||||
|
||||
// This works provided all tests in this folder use add_task.
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
Binary file not shown.
BIN
toolkit/components/places/tests/migration/places_v11.sqlite
Normal file
BIN
toolkit/components/places/tests/migration/places_v11.sqlite
Normal file
Binary file not shown.
Binary file not shown.
BIN
toolkit/components/places/tests/migration/places_v17.sqlite
Normal file
BIN
toolkit/components/places/tests/migration/places_v17.sqlite
Normal file
Binary file not shown.
BIN
toolkit/components/places/tests/migration/places_v21.sqlite
Normal file
BIN
toolkit/components/places/tests/migration/places_v21.sqlite
Normal file
Binary file not shown.
BIN
toolkit/components/places/tests/migration/places_v22.sqlite
Normal file
BIN
toolkit/components/places/tests/migration/places_v22.sqlite
Normal file
Binary file not shown.
BIN
toolkit/components/places/tests/migration/places_v23.sqlite
Normal file
BIN
toolkit/components/places/tests/migration/places_v23.sqlite
Normal file
Binary file not shown.
BIN
toolkit/components/places/tests/migration/places_v24.sqlite
Normal file
BIN
toolkit/components/places/tests/migration/places_v24.sqlite
Normal file
Binary file not shown.
BIN
toolkit/components/places/tests/migration/places_v25.sqlite
Normal file
BIN
toolkit/components/places/tests/migration/places_v25.sqlite
Normal file
Binary file not shown.
Binary file not shown.
@ -0,0 +1,19 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
add_task(function* setup() {
|
||||
yield setupPlacesDatabase(`places_v${CURRENT_SCHEMA_VERSION}.sqlite`);
|
||||
// Downgrade the schema version to the first supported one.
|
||||
let path = OS.Path.join(OS.Constants.Path.profileDir, DB_FILENAME);
|
||||
let db = yield Sqlite.openConnection({ path: path });
|
||||
yield db.setSchemaVersion(FIRST_UPGRADABLE_SCHEMA_VERSION);
|
||||
yield db.close();
|
||||
});
|
||||
|
||||
add_task(function* database_is_valid() {
|
||||
Assert.equal(PlacesUtils.history.databaseStatus,
|
||||
PlacesUtils.history.DATABASE_STATUS_UPGRADED);
|
||||
|
||||
let db = yield PlacesUtils.promiseDBConnection();
|
||||
Assert.equal((yield db.getSchemaVersion()), CURRENT_SCHEMA_VERSION);
|
||||
});
|
@ -1,368 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* This file tests migration invariants from schema version 10 to the current
|
||||
* schema version.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Constants
|
||||
|
||||
const kGuidAnnotationName = "sync/guid";
|
||||
const kExpectedAnnotations = 5;
|
||||
const kExpectedValidGuids = 2;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Globals
|
||||
|
||||
// Set in test_initial_state to the value in the database.
|
||||
var gItemGuid = [];
|
||||
var gItemId = [];
|
||||
var gPlaceGuid = [];
|
||||
var gPlaceId = [];
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Helpers
|
||||
|
||||
/**
|
||||
* Determines if a guid is valid or not.
|
||||
*
|
||||
* @return true if it is a valid guid, false otherwise.
|
||||
*/
|
||||
function isValidGuid(aGuid)
|
||||
{
|
||||
return /^[a-zA-Z0-9\-_]{12}$/.test(aGuid);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Test Functions
|
||||
|
||||
function test_initial_state()
|
||||
{
|
||||
// Mostly sanity checks our starting DB to make sure it's setup as we expect
|
||||
// it to be.
|
||||
let dbFile = gProfD.clone();
|
||||
dbFile.append(kDBName);
|
||||
let db = Services.storage.openUnsharedDatabase(dbFile);
|
||||
|
||||
let stmt = db.createStatement("PRAGMA journal_mode");
|
||||
do_check_true(stmt.executeStep());
|
||||
// WAL journal mode should not be set on this database.
|
||||
do_check_neq(stmt.getString(0).toLowerCase(), "wal");
|
||||
stmt.finalize();
|
||||
|
||||
do_check_false(db.indexExists("moz_bookmarks_guid_uniqueindex"));
|
||||
do_check_false(db.indexExists("moz_places_guid_uniqueindex"));
|
||||
|
||||
// There should be five item annotations for a bookmark guid.
|
||||
stmt = db.createStatement(
|
||||
`SELECT content AS guid, item_id
|
||||
FROM moz_items_annos
|
||||
WHERE anno_attribute_id = (
|
||||
SELECT id
|
||||
FROM moz_anno_attributes
|
||||
WHERE name = :attr_name
|
||||
)`
|
||||
);
|
||||
stmt.params.attr_name = kGuidAnnotationName;
|
||||
while (stmt.executeStep()) {
|
||||
gItemGuid.push(stmt.row.guid);
|
||||
gItemId.push(stmt.row.item_id)
|
||||
}
|
||||
do_check_eq(gItemGuid.length, gItemId.length);
|
||||
do_check_eq(gItemGuid.length, kExpectedAnnotations);
|
||||
stmt.finalize();
|
||||
|
||||
// There should be five item annotations for a place guid.
|
||||
stmt = db.createStatement(
|
||||
`SELECT content AS guid, place_id
|
||||
FROM moz_annos
|
||||
WHERE anno_attribute_id = (
|
||||
SELECT id
|
||||
FROM moz_anno_attributes
|
||||
WHERE name = :attr_name
|
||||
)`
|
||||
);
|
||||
stmt.params.attr_name = kGuidAnnotationName;
|
||||
while (stmt.executeStep()) {
|
||||
gPlaceGuid.push(stmt.row.guid);
|
||||
gPlaceId.push(stmt.row.place_id)
|
||||
}
|
||||
do_check_eq(gPlaceGuid.length, gPlaceId.length);
|
||||
do_check_eq(gPlaceGuid.length, kExpectedAnnotations);
|
||||
stmt.finalize();
|
||||
|
||||
// Check our schema version to make sure it is actually at 10.
|
||||
do_check_eq(db.schemaVersion, 10);
|
||||
|
||||
db.close();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_moz_bookmarks_guid_exists()
|
||||
{
|
||||
// This will throw if the column does not exist
|
||||
let stmt = DBConn().createStatement(
|
||||
`SELECT guid
|
||||
FROM moz_bookmarks`
|
||||
);
|
||||
stmt.finalize();
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_bookmark_guids_non_null()
|
||||
{
|
||||
// First, sanity check that we have a non-zero amount of bookmarks.
|
||||
let stmt = DBConn().createStatement(
|
||||
`SELECT COUNT(1)
|
||||
FROM moz_bookmarks`
|
||||
);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_neq(stmt.getInt32(0), 0);
|
||||
stmt.finalize();
|
||||
|
||||
// Now, make sure we have no NULL guid entry.
|
||||
stmt = DBConn().createStatement(
|
||||
`SELECT guid
|
||||
FROM moz_bookmarks
|
||||
WHERE guid IS NULL`
|
||||
);
|
||||
do_check_false(stmt.executeStep());
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_bookmark_guid_annotation_imported()
|
||||
{
|
||||
// Make sure we have the imported guid; not a newly generated one.
|
||||
let stmt = DBConn().createStatement(
|
||||
`SELECT id
|
||||
FROM moz_bookmarks
|
||||
WHERE guid = :guid
|
||||
AND id = :item_id`
|
||||
);
|
||||
let validGuids = 0;
|
||||
let seenGuids = [];
|
||||
for (let i = 0; i < gItemGuid.length; i++) {
|
||||
let guid = gItemGuid[i];
|
||||
stmt.params.guid = guid;
|
||||
stmt.params.item_id = gItemId[i];
|
||||
|
||||
// Check that it is a valid guid that we expect, and that it is not a
|
||||
// duplicate (which would violate the unique constraint).
|
||||
let valid = isValidGuid(guid) && seenGuids.indexOf(guid) == -1;
|
||||
seenGuids.push(guid);
|
||||
|
||||
if (valid) {
|
||||
validGuids++;
|
||||
do_check_true(stmt.executeStep());
|
||||
}
|
||||
else {
|
||||
do_check_false(stmt.executeStep());
|
||||
}
|
||||
stmt.reset();
|
||||
}
|
||||
do_check_eq(validGuids, kExpectedValidGuids);
|
||||
stmt.finalize();
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_bookmark_guid_annotation_removed()
|
||||
{
|
||||
let stmt = DBConn().createStatement(
|
||||
`SELECT COUNT(1)
|
||||
FROM moz_items_annos
|
||||
WHERE anno_attribute_id = (
|
||||
SELECT id
|
||||
FROM moz_anno_attributes
|
||||
WHERE name = :attr_name
|
||||
)`
|
||||
);
|
||||
stmt.params.attr_name = kGuidAnnotationName;
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq(stmt.getInt32(0), 0);
|
||||
stmt.finalize();
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_moz_places_guid_exists()
|
||||
{
|
||||
// This will throw if the column does not exist
|
||||
let stmt = DBConn().createStatement(
|
||||
`SELECT guid
|
||||
FROM moz_places`
|
||||
);
|
||||
stmt.finalize();
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_place_guids_non_null()
|
||||
{
|
||||
// First, sanity check that we have a non-zero amount of places.
|
||||
let stmt = DBConn().createStatement(
|
||||
`SELECT COUNT(1)
|
||||
FROM moz_places`
|
||||
);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_neq(stmt.getInt32(0), 0);
|
||||
stmt.finalize();
|
||||
|
||||
// Now, make sure we have no NULL guid entry.
|
||||
stmt = DBConn().createStatement(
|
||||
`SELECT guid
|
||||
FROM moz_places
|
||||
WHERE guid IS NULL`
|
||||
);
|
||||
do_check_false(stmt.executeStep());
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_place_guid_annotation_imported()
|
||||
{
|
||||
// Make sure we have the imported guid; not a newly generated one.
|
||||
let stmt = DBConn().createStatement(
|
||||
`SELECT id
|
||||
FROM moz_places
|
||||
WHERE guid = :guid
|
||||
AND id = :item_id`
|
||||
);
|
||||
let validGuids = 0;
|
||||
let seenGuids = [];
|
||||
for (let i = 0; i < gPlaceGuid.length; i++) {
|
||||
let guid = gPlaceGuid[i];
|
||||
stmt.params.guid = guid;
|
||||
stmt.params.item_id = gPlaceId[i];
|
||||
|
||||
// Check that it is a valid guid that we expect, and that it is not a
|
||||
// duplicate (which would violate the unique constraint).
|
||||
let valid = isValidGuid(guid) && seenGuids.indexOf(guid) == -1;
|
||||
seenGuids.push(guid);
|
||||
|
||||
if (valid) {
|
||||
validGuids++;
|
||||
do_check_true(stmt.executeStep());
|
||||
}
|
||||
else {
|
||||
do_check_false(stmt.executeStep());
|
||||
}
|
||||
stmt.reset();
|
||||
}
|
||||
do_check_eq(validGuids, kExpectedValidGuids);
|
||||
stmt.finalize();
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_place_guid_annotation_removed()
|
||||
{
|
||||
let stmt = DBConn().createStatement(
|
||||
`SELECT COUNT(1)
|
||||
FROM moz_annos
|
||||
WHERE anno_attribute_id = (
|
||||
SELECT id
|
||||
FROM moz_anno_attributes
|
||||
WHERE name = :attr_name
|
||||
)`
|
||||
);
|
||||
stmt.params.attr_name = kGuidAnnotationName;
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq(stmt.getInt32(0), 0);
|
||||
stmt.finalize();
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_moz_hosts()
|
||||
{
|
||||
// This will throw if the column does not exist
|
||||
let stmt = DBConn().createStatement(
|
||||
`SELECT host, frecency, typed, prefix
|
||||
FROM moz_hosts`
|
||||
);
|
||||
stmt.finalize();
|
||||
|
||||
// moz_hosts is populated asynchronously, so query asynchronously to serialize
|
||||
// to that.
|
||||
// check the number of entries in moz_hosts equals the number of
|
||||
// unique rev_host in moz_places
|
||||
stmt = DBConn().createAsyncStatement(
|
||||
`SELECT (SELECT COUNT(host) FROM moz_hosts),
|
||||
(SELECT COUNT(DISTINCT rev_host)
|
||||
FROM moz_places
|
||||
WHERE LENGTH(rev_host) > 1)`);
|
||||
try {
|
||||
stmt.executeAsync({
|
||||
handleResult: function (aResult) {
|
||||
this._hasResults = true;
|
||||
let row = aResult.getNextRow();
|
||||
let mozHostsCount = row.getResultByIndex(0);
|
||||
let mozPlacesCount = row.getResultByIndex(1);
|
||||
do_check_true(mozPlacesCount > 0);
|
||||
do_check_eq(mozPlacesCount, mozHostsCount);
|
||||
},
|
||||
handleError: function () {},
|
||||
handleCompletion: function (aReason) {
|
||||
do_check_eq(aReason, Ci.mozIStorageStatementCallback.REASON_FINISHED);
|
||||
do_check_true(this._hasResults);
|
||||
run_next_test();
|
||||
}
|
||||
});
|
||||
}
|
||||
finally {
|
||||
stmt.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
function test_final_state()
|
||||
{
|
||||
// We open a new database mostly so that we can check that the settings were
|
||||
// actually saved.
|
||||
let dbFile = gProfD.clone();
|
||||
dbFile.append(kDBName);
|
||||
let db = Services.storage.openUnsharedDatabase(dbFile);
|
||||
|
||||
let (stmt = db.createStatement("PRAGMA journal_mode")) {
|
||||
do_check_true(stmt.executeStep());
|
||||
// WAL journal mode should be set on this database.
|
||||
do_check_eq(stmt.getString(0).toLowerCase(), "wal");
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
do_check_true(db.indexExists("moz_bookmarks_guid_uniqueindex"));
|
||||
do_check_true(db.indexExists("moz_places_guid_uniqueindex"));
|
||||
do_check_true(db.indexExists("moz_favicons_guid_uniqueindex"));
|
||||
|
||||
do_check_eq(db.schemaVersion, CURRENT_SCHEMA_VERSION);
|
||||
|
||||
db.close();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Test Runner
|
||||
|
||||
[
|
||||
test_initial_state,
|
||||
test_moz_bookmarks_guid_exists,
|
||||
test_bookmark_guids_non_null,
|
||||
test_bookmark_guid_annotation_imported,
|
||||
test_bookmark_guid_annotation_removed,
|
||||
test_moz_places_guid_exists,
|
||||
test_place_guids_non_null,
|
||||
test_place_guid_annotation_imported,
|
||||
test_place_guid_annotation_removed,
|
||||
test_moz_hosts,
|
||||
test_final_state,
|
||||
].forEach(add_test);
|
||||
|
||||
function run_test()
|
||||
{
|
||||
setPlacesDatabase("places_v10.sqlite");
|
||||
run_next_test();
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* This file tests migration invariants from a database with schema version 14
|
||||
* that was then downgraded to a database with a schema version 10. Places
|
||||
* should then migrate this database to one with the current schema version.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Test Functions
|
||||
|
||||
function test_initial_state()
|
||||
{
|
||||
// Mostly sanity checks our starting DB to make sure it's setup as we expect
|
||||
// it to be.
|
||||
let dbFile = gProfD.clone();
|
||||
dbFile.append(kDBName);
|
||||
let db = Services.storage.openUnsharedDatabase(dbFile);
|
||||
|
||||
let stmt = db.createStatement("PRAGMA journal_mode");
|
||||
do_check_true(stmt.executeStep());
|
||||
// WAL journal mode should have been unset this database when it was migrated
|
||||
// down to v10.
|
||||
do_check_neq(stmt.getString(0).toLowerCase(), "wal");
|
||||
stmt.finalize();
|
||||
|
||||
do_check_true(db.indexExists("moz_bookmarks_guid_uniqueindex"));
|
||||
do_check_true(db.indexExists("moz_places_guid_uniqueindex"));
|
||||
|
||||
// There should be a non-zero amount of bookmarks without a guid.
|
||||
stmt = db.createStatement(
|
||||
`SELECT COUNT(1)
|
||||
FROM moz_bookmarks
|
||||
WHERE guid IS NULL`
|
||||
);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_neq(stmt.getInt32(0), 0);
|
||||
stmt.finalize();
|
||||
|
||||
// There should be a non-zero amount of places without a guid.
|
||||
stmt = db.createStatement(
|
||||
`SELECT COUNT(1)
|
||||
FROM moz_places
|
||||
WHERE guid IS NULL`
|
||||
);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_neq(stmt.getInt32(0), 0);
|
||||
stmt.finalize();
|
||||
|
||||
// Check our schema version to make sure it is actually at 10.
|
||||
do_check_eq(db.schemaVersion, 10);
|
||||
|
||||
db.close();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_bookmark_guids_non_null()
|
||||
{
|
||||
// First, sanity check that we have a non-zero amount of bookmarks. If
|
||||
// migration failed, we would have zero.
|
||||
let stmt = DBConn().createStatement(
|
||||
`SELECT COUNT(1)
|
||||
FROM moz_bookmarks`
|
||||
);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_neq(stmt.getInt32(0), 0);
|
||||
stmt.finalize();
|
||||
|
||||
// Now, make sure we have no NULL guid entries.
|
||||
stmt = DBConn().createStatement(
|
||||
`SELECT guid
|
||||
FROM moz_bookmarks
|
||||
WHERE guid IS NULL`
|
||||
);
|
||||
do_check_false(stmt.executeStep());
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_place_guids_non_null()
|
||||
{
|
||||
// First, sanity check that we have a non-zero amount of places. If migration
|
||||
// failed, we would have zero.
|
||||
let stmt = DBConn().createStatement(
|
||||
`SELECT COUNT(1)
|
||||
FROM moz_places`
|
||||
);
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_neq(stmt.getInt32(0), 0);
|
||||
stmt.finalize();
|
||||
|
||||
// Now, make sure we have no NULL guid entry.
|
||||
stmt = DBConn().createStatement(
|
||||
`SELECT guid
|
||||
FROM moz_places
|
||||
WHERE guid IS NULL`
|
||||
);
|
||||
do_check_false(stmt.executeStep());
|
||||
stmt.finalize();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_final_state()
|
||||
{
|
||||
// We open a new database mostly so that we can check that the settings were
|
||||
// actually saved.
|
||||
let dbFile = gProfD.clone();
|
||||
dbFile.append(kDBName);
|
||||
let db = Services.storage.openUnsharedDatabase(dbFile);
|
||||
|
||||
do_check_eq(db.schemaVersion, CURRENT_SCHEMA_VERSION);
|
||||
|
||||
db.close();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Test Runner
|
||||
|
||||
[
|
||||
test_initial_state,
|
||||
test_bookmark_guids_non_null,
|
||||
test_place_guids_non_null,
|
||||
test_final_state,
|
||||
].forEach(add_test);
|
||||
|
||||
function run_test()
|
||||
{
|
||||
setPlacesDatabase("places_v10_from_v14.sqlite");
|
||||
run_next_test();
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
add_task(function* setup() {
|
||||
yield setupPlacesDatabase("places_v16.sqlite");
|
||||
});
|
||||
|
||||
add_task(function* database_is_valid() {
|
||||
Assert.equal(PlacesUtils.history.databaseStatus,
|
||||
PlacesUtils.history.DATABASE_STATUS_UPGRADED);
|
||||
|
||||
let db = yield PlacesUtils.promiseDBConnection();
|
||||
Assert.equal((yield db.getSchemaVersion()), CURRENT_SCHEMA_VERSION);
|
||||
});
|
||||
|
||||
add_task(function* test_moz_hosts() {
|
||||
let db = yield PlacesUtils.promiseDBConnection();
|
||||
|
||||
// This will throw if the column does not exist.
|
||||
yield db.execute("SELECT host, frecency, typed, prefix FROM moz_hosts");
|
||||
|
||||
// moz_hosts is populated asynchronously, so we need to wait.
|
||||
yield promiseAsyncUpdates();
|
||||
|
||||
// check the number of entries in moz_hosts equals the number of
|
||||
// unique rev_host in moz_places
|
||||
let rows = yield db.execute(
|
||||
`SELECT (SELECT COUNT(host) FROM moz_hosts),
|
||||
(SELECT COUNT(DISTINCT rev_host)
|
||||
FROM moz_places
|
||||
WHERE LENGTH(rev_host) > 1)
|
||||
`);
|
||||
|
||||
Assert.equal(rows.length, 1);
|
||||
let mozHostsCount = rows[0].getResultByIndex(0);
|
||||
let mozPlacesCount = rows[0].getResultByIndex(1);
|
||||
|
||||
Assert.ok(mozPlacesCount > 0, "There is some url in the database");
|
||||
Assert.equal(mozPlacesCount, mozHostsCount, "moz_hosts has the expected number of entries");
|
||||
});
|
||||
|
||||
add_task(function* test_journal() {
|
||||
let db = yield PlacesUtils.promiseDBConnection();
|
||||
let rows = yield db.execute("PRAGMA journal_mode");
|
||||
Assert.equal(rows.length, 1);
|
||||
// WAL journal mode should be set on this database.
|
||||
Assert.equal(rows[0].getResultByIndex(0), "wal");
|
||||
});
|
@ -1,62 +1,42 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* This file tests migration invariants from schema version 19 to the current
|
||||
* schema version.
|
||||
*/
|
||||
const ANNO_LEGACYGUID = "placesInternal/GUID";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Globals
|
||||
|
||||
const kGuidAnnotationName = "placesInternal/GUID";
|
||||
|
||||
function getTotalGuidAnnotationsCount(aStorageConnection) {
|
||||
stmt = aStorageConnection.createStatement(
|
||||
let getTotalGuidAnnotationsCount = Task.async(function* (db) {
|
||||
let rows = yield db.execute(
|
||||
`SELECT count(*)
|
||||
FROM moz_items_annos a
|
||||
JOIN moz_anno_attributes b ON a.anno_attribute_id = b.id
|
||||
WHERE b.name = :attr_name`
|
||||
);
|
||||
try {
|
||||
stmt.params.attr_name = kGuidAnnotationName;
|
||||
do_check_true(stmt.executeStep());
|
||||
return stmt.getInt32(0);
|
||||
} finally {
|
||||
stmt.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Tests
|
||||
|
||||
function run_test()
|
||||
{
|
||||
setPlacesDatabase("places_v19.sqlite");
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_test(function test_initial_state()
|
||||
{
|
||||
let dbFile = gProfD.clone();
|
||||
dbFile.append(kDBName);
|
||||
let db = Services.storage.openUnsharedDatabase(dbFile);
|
||||
|
||||
// There should be an obsolete bookmark GUID annotation.
|
||||
do_check_eq(getTotalGuidAnnotationsCount(db), 1);
|
||||
|
||||
// Check our schema version to make sure it is actually at 19.
|
||||
do_check_eq(db.schemaVersion, 19);
|
||||
|
||||
db.close();
|
||||
run_next_test();
|
||||
WHERE b.name = :attr_name
|
||||
`, { attr_name: ANNO_LEGACYGUID });
|
||||
return rows[0].getResultByIndex(0);
|
||||
});
|
||||
|
||||
add_test(function test_bookmark_guid_annotation_removed()
|
||||
{
|
||||
|
||||
// There should be no obsolete bookmark GUID annotation anymore.
|
||||
do_check_eq(getTotalGuidAnnotationsCount(DBConn()), 0);
|
||||
|
||||
run_next_test();
|
||||
add_task(function* setup() {
|
||||
yield setupPlacesDatabase("places_v19.sqlite");
|
||||
});
|
||||
|
||||
add_task(function* initial_state() {
|
||||
let path = OS.Path.join(OS.Constants.Path.profileDir, DB_FILENAME);
|
||||
let db = yield Sqlite.openConnection({ path: path });
|
||||
|
||||
Assert.equal((yield getTotalGuidAnnotationsCount(db)), 1,
|
||||
"There should be 1 obsolete guid annotation");
|
||||
yield db.close();
|
||||
});
|
||||
|
||||
add_task(function* database_is_valid() {
|
||||
Assert.equal(PlacesUtils.history.databaseStatus,
|
||||
PlacesUtils.history.DATABASE_STATUS_UPGRADED);
|
||||
|
||||
let db = yield PlacesUtils.promiseDBConnection();
|
||||
Assert.equal((yield db.getSchemaVersion()), CURRENT_SCHEMA_VERSION);
|
||||
});
|
||||
|
||||
add_task(function test_bookmark_guid_annotation_removed()
|
||||
{
|
||||
let db = yield PlacesUtils.promiseDBConnection();
|
||||
Assert.equal((yield getTotalGuidAnnotationsCount(db)), 0,
|
||||
"There should be no more obsolete GUID annotations.");
|
||||
});
|
||||
|
@ -0,0 +1,35 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
add_task(function* setup() {
|
||||
yield setupPlacesDatabase("places_v24.sqlite");
|
||||
});
|
||||
|
||||
add_task(function* database_is_valid() {
|
||||
Assert.equal(PlacesUtils.history.databaseStatus,
|
||||
PlacesUtils.history.DATABASE_STATUS_UPGRADED);
|
||||
|
||||
let db = yield PlacesUtils.promiseDBConnection();
|
||||
Assert.equal((yield db.getSchemaVersion()), CURRENT_SCHEMA_VERSION);
|
||||
});
|
||||
|
||||
add_task(function test_bookmark_guid_annotation_removed()
|
||||
{
|
||||
yield PlacesUtils.bookmarks.eraseEverything();
|
||||
|
||||
let db = yield PlacesUtils.promiseDBConnection();
|
||||
let m = new Map([
|
||||
[PlacesUtils.placesRootId, PlacesUtils.bookmarks.rootGuid],
|
||||
[PlacesUtils.bookmarksMenuFolderId, PlacesUtils.bookmarks.menuGuid],
|
||||
[PlacesUtils.toolbarFolderId, PlacesUtils.bookmarks.toolbarGuid],
|
||||
[PlacesUtils.unfiledBookmarksFolderId, PlacesUtils.bookmarks.unfiledGuid],
|
||||
[PlacesUtils.tagsFolderId, PlacesUtils.bookmarks.tagsGuid]
|
||||
]);
|
||||
|
||||
let rows = yield db.execute(`SELECT id, guid FROM moz_bookmarks`);
|
||||
for (let row of rows) {
|
||||
let id = row.getResultByName("id");
|
||||
let guid = row.getResultByName("guid");
|
||||
Assert.equal(m.get(id), guid, "The root folder has the correct GUID");
|
||||
}
|
||||
});
|
@ -0,0 +1,38 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* This file tests migration from a preliminary schema version 6 that
|
||||
* lacks frecency column and moz_inputhistory table.
|
||||
*/
|
||||
|
||||
add_task(function* setup() {
|
||||
yield setupPlacesDatabase("places_v6.sqlite");
|
||||
});
|
||||
|
||||
add_task(function* corrupt_database_not_exists() {
|
||||
let corruptPath = OS.Path.join(OS.Constants.Path.profileDir,
|
||||
"places.sqlite.corrupt");
|
||||
Assert.ok(!(yield OS.File.exists(corruptPath)), "Corrupt file should not exist");
|
||||
});
|
||||
|
||||
add_task(function* database_is_valid() {
|
||||
Assert.equal(PlacesUtils.history.databaseStatus,
|
||||
PlacesUtils.history.DATABASE_STATUS_CORRUPT);
|
||||
|
||||
let db = yield PlacesUtils.promiseDBConnection();
|
||||
Assert.equal((yield db.getSchemaVersion()), CURRENT_SCHEMA_VERSION);
|
||||
});
|
||||
|
||||
add_task(function* check_columns() {
|
||||
// Check the database has been replaced, these would throw otherwise.
|
||||
let db = yield PlacesUtils.promiseDBConnection();
|
||||
yield db.execute("SELECT frecency from moz_places");
|
||||
yield db.execute("SELECT 1 from moz_inputhistory");
|
||||
});
|
||||
|
||||
add_task(function* corrupt_database_exists() {
|
||||
let corruptPath = OS.Path.join(OS.Constants.Path.profileDir,
|
||||
"places.sqlite.corrupt");
|
||||
Assert.ok((yield OS.File.exists(corruptPath)), "Corrupt file should exist");
|
||||
});
|
@ -1,34 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* This file tests migration replaces the database if schema version < 6.
|
||||
*/
|
||||
|
||||
add_test(function corrupt_database_not_exists() {
|
||||
let dbFile = gProfD.clone();
|
||||
dbFile.append("places.sqlite.corrupt");
|
||||
do_check_false(dbFile.exists());
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function database_is_valid() {
|
||||
do_check_eq(PlacesUtils.history.databaseStatus,
|
||||
PlacesUtils.history.DATABASE_STATUS_CORRUPT);
|
||||
do_check_eq(DBConn().schemaVersion, CURRENT_SCHEMA_VERSION);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function corrupt_database_exists() {
|
||||
let dbFile = gProfD.clone();
|
||||
dbFile.append("places.sqlite.corrupt");
|
||||
do_check_true(dbFile.exists());
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
function run_test()
|
||||
{
|
||||
setPlacesDatabase("places_alpha.sqlite");
|
||||
run_next_test();
|
||||
}
|
||||
|
@ -1,31 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* This file tests migration from a preliminary schema version 6 that
|
||||
* lacks frecency column and moz_inputhistory table.
|
||||
*/
|
||||
|
||||
add_test(function database_is_valid() {
|
||||
do_check_eq(PlacesUtils.history.databaseStatus,
|
||||
PlacesUtils.history.DATABASE_STATUS_UPGRADED);
|
||||
// This throws if frecency column does not exist.
|
||||
stmt = DBConn().createStatement("SELECT frecency from moz_places");
|
||||
stmt.finalize();
|
||||
// Check moz_inputhistory is in place.
|
||||
do_check_true(DBConn().tableExists("moz_inputhistory"));
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function corrupt_database_not_exists() {
|
||||
let dbFile = gProfD.clone();
|
||||
dbFile.append("places.sqlite.corrupt");
|
||||
do_check_false(dbFile.exists());
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
function run_test()
|
||||
{
|
||||
setPlacesDatabase("places_v6_no_frecency.sqlite");
|
||||
run_next_test();
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* This file tests migration from a preliminary schema version 6 that
|
||||
* lacks important indices. The database should be replaced.
|
||||
*/
|
||||
|
||||
add_test(function corrupt_database_not_exists() {
|
||||
let dbFile = gProfD.clone();
|
||||
dbFile.append("places.sqlite.corrupt");
|
||||
do_check_false(dbFile.exists());
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function database_is_valid() {
|
||||
do_check_eq(PlacesUtils.history.databaseStatus,
|
||||
PlacesUtils.history.DATABASE_STATUS_CORRUPT);
|
||||
do_check_eq(DBConn().schemaVersion, CURRENT_SCHEMA_VERSION);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function corrupt_database_exists() {
|
||||
let dbFile = gProfD.clone();
|
||||
dbFile.append("places.sqlite.corrupt");
|
||||
do_check_true(dbFile.exists());
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
function run_test()
|
||||
{
|
||||
setPlacesDatabase("places_v6_no_indices.sqlite");
|
||||
run_next_test();
|
||||
}
|
@ -1,18 +1,22 @@
|
||||
[DEFAULT]
|
||||
head = head_migration.js
|
||||
tail =
|
||||
skip-if = toolkit == 'android' || toolkit == 'gonk'
|
||||
support-files =
|
||||
places_alpha.sqlite
|
||||
places_v10.sqlite
|
||||
places_v10_from_v14.sqlite
|
||||
places_v19.sqlite
|
||||
places_v6_no_frecency.sqlite
|
||||
places_v6_no_indices.sqlite
|
||||
|
||||
[test_current_from_v10.js]
|
||||
[test_current_from_v10_migrated_from_v14.js]
|
||||
support-files =
|
||||
places_v6.sqlite
|
||||
places_v10.sqlite
|
||||
places_v11.sqlite
|
||||
places_v16.sqlite
|
||||
places_v17.sqlite
|
||||
places_v19.sqlite
|
||||
places_v21.sqlite
|
||||
places_v22.sqlite
|
||||
places_v23.sqlite
|
||||
places_v24.sqlite
|
||||
places_v25.sqlite
|
||||
|
||||
[test_current_from_downgraded.js]
|
||||
[test_current_from_v6.js]
|
||||
[test_current_from_v16.js]
|
||||
[test_current_from_v19.js]
|
||||
[test_database_from_alpha.js]
|
||||
[test_database_from_v6_no_frecency.js]
|
||||
[test_database_from_v6_no_indices.js]
|
||||
[test_current_from_v24.js]
|
||||
|
@ -210,17 +210,13 @@ function* test_promiseBookmarksTreeAgainstResult(aItemGuid = "",
|
||||
|
||||
add_task(function* () {
|
||||
// Add some bookmarks to cover various use cases.
|
||||
let toolbarGuid =
|
||||
yield PlacesUtils.promiseItemGuid(PlacesUtils.toolbarFolderId);
|
||||
let menuGuid =
|
||||
yield PlacesUtils.promiseItemGuid(PlacesUtils.bookmarksMenuFolderId);
|
||||
yield new_bookmark({ parentGuid: toolbarGuid });
|
||||
yield new_folder({ parentGuid: menuGuid
|
||||
yield new_bookmark({ parentGuid: PlacesUtils.bookmarks.toolbarGuid });
|
||||
yield new_folder({ parentGuid: PlacesUtils.bookmarks.menuGuid
|
||||
, annotations: [{ name: "TestAnnoA", value: "TestVal"
|
||||
, name: "TestAnnoB", value: 0 }]});
|
||||
yield PlacesTransactions.transact(
|
||||
PlacesTransactions.NewSeparator({ parentGuid: menuGuid }));
|
||||
let folderGuid = yield new_folder({ parentGuid: menuGuid });
|
||||
PlacesTransactions.NewSeparator({ parentGuid: PlacesUtils.bookmarks.menuGuid }));
|
||||
let folderGuid = yield new_folder({ parentGuid: PlacesUtils.bookmarks.menuGuid });
|
||||
yield new_bookmark({ title: null
|
||||
, parentGuid: folderGuid
|
||||
, keyword: "test_keyword"
|
||||
@ -234,8 +230,7 @@ add_task(function* () {
|
||||
yield test_promiseBookmarksTreeAgainstResult();
|
||||
|
||||
// Do specify it
|
||||
let rootGuid = yield PlacesUtils.promiseItemGuid(PlacesUtils.placesRootId);
|
||||
yield test_promiseBookmarksTreeAgainstResult(rootGuid);
|
||||
yield test_promiseBookmarksTreeAgainstResult(PlacesUtils.bookmarks.rootGuid);
|
||||
|
||||
// Exclude the bookmarks menu.
|
||||
// The calllback should be four times - once for the toolbar, once for
|
||||
@ -246,13 +241,13 @@ add_task(function* () {
|
||||
// passed in.
|
||||
let guidsPassedToExcludeCallback = new Set();
|
||||
let placesRootWithoutTheMenu =
|
||||
yield test_promiseBookmarksTreeAgainstResult(rootGuid, {
|
||||
yield test_promiseBookmarksTreeAgainstResult(PlacesUtils.bookmarks.rootGuid, {
|
||||
excludeItemsCallback: aItem => {
|
||||
guidsPassedToExcludeCallback.add(aItem.guid);
|
||||
return aItem.root == "bookmarksMenuFolder";
|
||||
},
|
||||
includeItemIds: true
|
||||
}, [menuGuid]);
|
||||
}, [PlacesUtils.bookmarks.menuGuid]);
|
||||
do_check_eq(guidsPassedToExcludeCallback.size, 4);
|
||||
do_check_eq(placesRootWithoutTheMenu.children.length, 2);
|
||||
});
|
||||
|
54
toolkit/modules/PromiseUtils.jsm
Normal file
54
toolkit/modules/PromiseUtils.jsm
Normal file
@ -0,0 +1,54 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict"
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["PromiseUtils"];
|
||||
|
||||
Components.utils.import("resource://gre/modules/Timer.jsm");
|
||||
|
||||
this.PromiseUtils = {
|
||||
/*
|
||||
* A simple timeout mechanism.
|
||||
*
|
||||
* Example:
|
||||
* resolveOrTimeout(myModule.shutdown(), 1000, new Error("The module took too long to shutdown"));
|
||||
*
|
||||
* @param {Promise} promise The Promise that should resolve/reject quickly.
|
||||
* @param {number} delay A delay after which to stop waiting for `promise`, in milliseconds.
|
||||
* @param {function} rejection If `promise` hasn't resolved/rejected after `delay`,
|
||||
* a value used to construct the rejection.
|
||||
*
|
||||
* @return {Promise} A promise that behaves as `promise`, if `promise` is
|
||||
* resolved/rejected within `delay` ms, or rejects with `rejection()` otherwise.
|
||||
*/
|
||||
resolveOrTimeout : function(promise, delay, rejection) {
|
||||
// throw a TypeError if <promise> is not a Promise object
|
||||
if (!(promise instanceof Promise)) {
|
||||
throw new TypeError("first argument <promise> must be a Promise object");
|
||||
}
|
||||
|
||||
// throw a TypeError if <delay> is not a number
|
||||
if (typeof delay != "number" || delay < 0) {
|
||||
throw new TypeError("second argument <delay> must be a positive number");
|
||||
}
|
||||
|
||||
// throws a TypeError if <rejection> is not a function
|
||||
if (rejection && typeof rejection != "function") {
|
||||
throw new TypeError("third optional argument <rejection> must be a function");
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
promise.then(resolve, reject);
|
||||
let id = setTimeout(() => {
|
||||
try {
|
||||
rejection ? reject(rejection()) : reject(new Error("Promise Timeout"));
|
||||
} catch(ex) {
|
||||
reject(ex);
|
||||
}
|
||||
clearTimeout(id);
|
||||
}, delay);
|
||||
});
|
||||
}
|
||||
}
|
@ -36,6 +36,7 @@ EXTRA_JS_MODULES += [
|
||||
'PrivateBrowsingUtils.jsm',
|
||||
'Promise-backend.js',
|
||||
'Promise.jsm',
|
||||
'PromiseUtils.jsm',
|
||||
'PropertyListUtils.jsm',
|
||||
'RemoteController.jsm',
|
||||
'RemoteFinder.jsm',
|
||||
|
78
toolkit/modules/tests/xpcshell/test_PromiseUtils.js
Normal file
78
toolkit/modules/tests/xpcshell/test_PromiseUtils.js
Normal file
@ -0,0 +1,78 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
Components.utils.import("resource://gre/modules/PromiseUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/Timer.jsm");
|
||||
|
||||
// Tests for PromiseUtils.jsm
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
/* Tests for the case when arguments to resolveOrTimeout
|
||||
* are not of correct type */
|
||||
add_task(function* test_wrong_arguments() {
|
||||
let p = new Promise((resolve, reject) => {});
|
||||
// for the first argument
|
||||
Assert.throws(() => PromiseUtils.resolveOrTimeout("string", 200), /first argument <promise> must be a Promise object/,
|
||||
"TypeError thrown because first argument is not a Promsie object");
|
||||
// for second argument
|
||||
Assert.throws(() => PromiseUtils.resolveOrTimeout(p, "string"), /second argument <delay> must be a positive number/,
|
||||
"TypeError thrown because second argument is not a positive number");
|
||||
// for the third argument
|
||||
Assert.throws(() => PromiseUtils.resolveOrTimeout(p, 200, "string"), /third optional argument <rejection> must be a function/,
|
||||
"TypeError thrown because thrird argument is not a function");
|
||||
});
|
||||
|
||||
/* Tests for the case when the optional third argument is not provided
|
||||
* In that case the returned promise rejects with a default Error */
|
||||
add_task(function* test_optional_third_argument() {
|
||||
let p = new Promise((resolve, reject) => {});
|
||||
yield Assert.rejects(PromiseUtils.resolveOrTimeout(p, 200), /Promise Timeout/, "Promise rejects with a default Error");
|
||||
});
|
||||
|
||||
/* Test for the case when the passed promise resolves quickly
|
||||
* In that case the returned promise also resolves with the same value */
|
||||
add_task(function* test_resolve_quickly() {
|
||||
let p = new Promise((resolve, reject) => setTimeout(() => resolve("Promise is resolved"), 20));
|
||||
let result = yield PromiseUtils.resolveOrTimeout(p, 200);
|
||||
Assert.equal(result, "Promise is resolved", "Promise resolves quickly");
|
||||
});
|
||||
|
||||
/* Test for the case when the passed promise rejects quickly
|
||||
* In that case the returned promise also rejects with the same value */
|
||||
add_task(function* test_reject_quickly() {
|
||||
let p = new Promise((resolve, reject) => setTimeout(() => reject("Promise is rejected"), 20));
|
||||
yield Assert.rejects(PromiseUtils.resolveOrTimeout(p, 200), /Promise is rejected/, "Promise rejects quickly");
|
||||
});
|
||||
|
||||
/* Tests for the case when the passed promise doesn't settle
|
||||
* and rejection returns string/object/undefined */
|
||||
add_task(function* test_rejection_function() {
|
||||
let p = new Promise((resolve, reject) => {});
|
||||
// for rejection returning string
|
||||
yield Assert.rejects(PromiseUtils.resolveOrTimeout(p, 200, () => {
|
||||
return "Rejection returned a string";
|
||||
}), /Rejection returned a string/, "Rejection returned a string");
|
||||
|
||||
// for rejection returning object
|
||||
yield Assert.rejects(PromiseUtils.resolveOrTimeout(p, 200, () => {
|
||||
return {Name:"Promise"};
|
||||
}), Object, "Rejection returned an object");
|
||||
|
||||
// for rejection returning undefined
|
||||
yield Assert.rejects(PromiseUtils.resolveOrTimeout(p, 200, () => {
|
||||
return;
|
||||
}), undefined, "Rejection returned undefined");
|
||||
});
|
||||
|
||||
/* Tests for the case when the passed promise doesn't settles
|
||||
* and rejection throws an error */
|
||||
add_task(function* test_rejection_throw_error() {
|
||||
let p = new Promise((resolve, reject) => {});
|
||||
yield Assert.rejects(PromiseUtils.resolveOrTimeout(p, 200, () => {
|
||||
throw new Error("Rejection threw an Error");
|
||||
}), /Rejection threw an Error/, "Rejection threw an error");
|
||||
});
|
@ -21,6 +21,7 @@ skip-if = os == 'android'
|
||||
[test_PermissionsUtils.js]
|
||||
[test_Preferences.js]
|
||||
[test_Promise.js]
|
||||
[test_PromiseUtils.js]
|
||||
[test_propertyListsUtils.js]
|
||||
[test_readCertPrefs.js]
|
||||
[test_Services.js]
|
||||
|
Loading…
x
Reference in New Issue
Block a user