зеркало из https://github.com/mozilla/gecko-dev.git
Merge fx-team to m-c a=merge
This commit is contained in:
Коммит
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();
|
||||
}
|
||||
|
|
Двоичные данные
toolkit/components/places/tests/migration/places_alpha.sqlite
Двоичные данные
toolkit/components/places/tests/migration/places_alpha.sqlite
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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]
|
||||
|
|
Загрузка…
Ссылка в новой задаче