author | Mark Hammond <mhammond@skippinet.com.au> |
Mon, 16 Mar 2015 18:45:06 -0700 | |
changeset 233866 | 8beb212cceb8627a642204e5161c3eafacc6cfc9 |
parent 233865 | f60585b7803685dfb39464cc2a92be00945ff62b |
child 233867 | a194e80230901e7e3644c3c16b3356c5c3099316 |
child 234050 | 448e02bb26208e20920b14a43802b788543e736a |
push id | 28425 |
push user | cbook@mozilla.com |
push date | Tue, 17 Mar 2015 10:33:06 +0000 |
treeherder | mozilla-central@a194e8023090 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
bugs | 1131457 |
milestone | 39.0a1 |
backs out | 79e1475e5b35564f2cb3a99b3448f62c4176406b |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/base/content/browser-readinglist.js +++ b/browser/base/content/browser-readinglist.js @@ -5,44 +5,25 @@ */ XPCOMUtils.defineLazyModuleGetter(this, "ReadingList", "resource:///modules/readinglist/ReadingList.jsm"); const READINGLIST_COMMAND_ID = "readingListSidebar"; let ReadingListUI = { - /** - * Frame-script messages we want to listen to. - * @type {[string]} - */ MESSAGES: [ "ReadingList:GetVisibility", "ReadingList:ToggleVisibility", ], /** - * Add-to-ReadingList toolbar button in the URLbar. - * @type {Element} - */ - toolbarButton: null, - - /** - * Whether this object is currently registered as a listener with ReadingList. - * Used to avoid inadvertantly loading the ReadLingList.jsm module on startup. - * @type {Boolean} - */ - listenerRegistered: false, - - /** * Initialize the ReadingList UI. */ init() { - this.toolbarButton = document.getElementById("readinglist-addremove-button"); - Preferences.observe("browser.readinglist.enabled", this.updateUI, this); const mm = window.messageManager; for (let msg of this.MESSAGES) { mm.addMessageListener(msg, this); } this.updateUI(); @@ -77,28 +58,17 @@ let ReadingListUI = { }, /** * Update the UI status, ensuring the UI is shown or hidden depending on * whether the feature is enabled or not. */ updateUI() { let enabled = this.enabled; - if (enabled) { - // This is a no-op if we're already registered. - ReadingList.addListener(this); - this.listenerRegistered = true; - } else { - if (this.listenerRegistered) { - // This is safe to call if we're not currently registered, but we don't - // want to forcibly load the normally lazy-loaded module on startup. - ReadingList.removeListener(this); - this.listenerRegistered = true; - } - + if (!enabled) { this.hideSidebar(); } document.getElementById(READINGLIST_COMMAND_ID).setAttribute("hidden", !enabled); }, /** * Show the ReadingList sidebar. @@ -114,21 +84,16 @@ let ReadingListUI = { * Hide the ReadingList sidebar, if it is currently shown. */ hideSidebar() { if (this.isSidebarOpen) { SidebarUI.hide(); } }, - /** - * Re-refresh the ReadingList bookmarks submenu when it opens. - * - * @param {Element} target - Menu element opening. - */ onReadingListPopupShowing: Task.async(function* (target) { if (target.id == "BMB_readingListPopup") { // Setting this class in the .xul file messes with the way // browser-places.js inserts bookmarks in the menu. document.getElementById("BMB_viewReadingListSidebar") .classList.add("panel-subview-footer"); } @@ -214,97 +179,9 @@ let ReadingListUI = { } case "ReadingList:ToggleVisibility": { this.toggleSidebar(); break; } } }, - - /** - * Handles toolbar button styling based on page proxy state changes. - * - * @see SetPageProxyState() - * - * @param {string} state - New state. Either "valid" or "invalid". - */ - onPageProxyStateChanged: Task.async(function* (state) { - if (!this.enabled || state == "invalid") { - this.toolbarButton.setAttribute("hidden", true); - return; - } - - let isInList = yield ReadingList.containsURL(gBrowser.currentURI); - this.setToolbarButtonState(isInList); - }), - - /** - * Set the state of the ReadingList toolbar button in the urlbar. - * If the current tab's page is in the ReadingList (active), sets the button - * to allow removing the page. Otherwise, sets the button to allow adding the - * page (not active). - * - * @param {boolean} active - True if the button should be active (page is - * already in the list). - */ - setToolbarButtonState(active) { - this.toolbarButton.setAttribute("already-added", active); - - let type = (active ? "remove" : "add"); - let tooltip = gNavigatorBundle.getString(`readingList.urlbar.${type}`); - this.toolbarButton.setAttribute("tooltiptext", tooltip); - - this.toolbarButton.removeAttribute("hidden"); - }, - - /** - * Toggle a page (from a browser) in the ReadingList, adding if it's not already added, or - * removing otherwise. - * - * @param {<xul:browser>} browser - Browser with page to toggle. - * @returns {Promise} Promise resolved when operation has completed. - */ - togglePageByBrowser: Task.async(function* (browser) { - let item = yield ReadingList.getItemForURL(browser.currentURI); - if (item) { - yield item.delete(); - } else { - yield ReadingList.addItemFromBrowser(browser); - } - }), - - /** - * Checks if a given item matches the current tab in this window. - * - * @param {ReadingListItem} item - Item to check - * @returns True if match, false otherwise. - */ - isItemForCurrentBrowser(item) { - let currentURL = gBrowser.currentURI.spec; - if (item.url == currentURL || item.resolvedURL == currentURL) { - return true; - } - return false; - }, - - /** - * ReadingList event handler for when an item is added. - * - * @param {ReadingListItem} item - Item added. - */ - onItemAdded(item) { - if (this.isItemForCurrentBrowser(item)) { - this.setToolbarButtonState(true); - } - }, - - /** - * ReadingList event handler for when an item is deleted. - * - * @param {ReadingListItem} item - Item deleted. - */ - onItemDeleted(item) { - if (this.isItemForCurrentBrowser(item)) { - this.setToolbarButtonState(false); - } - }, };
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1017,17 +1017,16 @@ var gBrowserInit = { } goSetCommandEnabled("cmd_newNavigatorTab", false); } // Misc. inits. CombinedStopReload.init(); gPrivateBrowsingUI.init(); TabsInTitlebar.init(); - ReadingListUI.init(); #ifdef XP_WIN if (window.matchMedia("(-moz-os-version: windows-win8)").matches && window.matchMedia("(-moz-windows-default-theme)").matches) { let windowFrameColor = Cu.import("resource:///modules/Windows8WindowFrameColor.jsm", {}) .Windows8WindowFrameColor.get(); // Formula from W3C's WCAG 2.0 spec's color ratio and relative luminance, @@ -1375,16 +1374,17 @@ var gBrowserInit = { return; } // Enable the Restore Last Session command if needed RestoreLastSessionObserver.init(); SocialUI.init(); TabView.init(); + ReadingListUI.init(); // Telemetry for master-password - we do this after 5 seconds as it // can cause IO if NSS/PSM has not already initialized. setTimeout(() => { if (window.closed) { return; } let secmodDB = Cc["@mozilla.org/security/pkcs11moduledb;1"] @@ -2416,17 +2416,16 @@ function UpdatePageProxyState() { if (gURLBar && gURLBar.value != gLastValidURLStr) SetPageProxyState("invalid"); } function SetPageProxyState(aState) { BookmarkingUI.onPageProxyStateChanged(aState); - ReadingListUI.onPageProxyStateChanged(aState); if (!gURLBar) return; if (!gProxyFavIcon) gProxyFavIcon = document.getElementById("page-proxy-favicon"); gURLBar.setAttribute("pageproxystate", aState);
--- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -822,20 +822,16 @@ <label class="urlbar-display urlbar-display-switchtab" value="&urlbar.switchToTab.label;"/> </box> <hbox id="urlbar-icons"> <image id="page-report-button" class="urlbar-icon" hidden="true" tooltiptext="&pageReportIcon.tooltip;" onclick="gPopupBlockerObserver.onReportButtonClick(event);"/> - <toolbarbutton id="readinglist-addremove-button" - class="tabbable urlbar-icon" - hidden="true" - oncommand="ReadingListUI.togglePageByBrowser(gBrowser.selectedBrowser);"/> <toolbarbutton id="reader-mode-button" class="tabbable" hidden="true" onclick="ReaderParent.handleReaderButtonEvent(event);" onkeypress="ReaderParent.handleReaderButtonEvent(event);"/> </hbox> <toolbarbutton id="urlbar-go-button" class="chromeclass-toolbar-additional"
--- a/browser/base/content/content.js +++ b/browser/base/content/content.js @@ -1012,30 +1012,30 @@ addEventListener("pageshow", function(ev if (event.target == content.document) { sendAsyncMessage("PageVisibility:Show", { persisted: event.persisted, }); } }); let PageMetadataMessenger = { - init() { + init: function() { addMessageListener("PageMetadata:GetPageData", this); addMessageListener("PageMetadata:GetMicrodata", this); }, - receiveMessage(message) { - switch(message.name) { + receiveMessage: function(aMessage) { + switch(aMessage.name) { case "PageMetadata:GetPageData": { let result = PageMetadata.getData(content.document); sendAsyncMessage("PageMetadata:PageDataResult", result); break; } case "PageMetadata:GetMicrodata": { - let target = message.objects; + let target = aMessage.objects; let result = PageMetadata.getMicrodata(content.document, target); sendAsyncMessage("PageMetadata:MicrodataResult", result); break; } } } } PageMetadataMessenger.init();
--- a/browser/components/readinglist/ReadingList.jsm +++ b/browser/components/readinglist/ReadingList.jsm @@ -125,48 +125,16 @@ ReadingListImpl.prototype = { * @return Promise<number> The number of matching items in the list. Rejected * with an Error on error. */ count: Task.async(function* (...optsList) { return (yield this._store.count(...optsList)); }), /** - * Checks whether a given URL is in the ReadingList already. - * - * @param {String/nsIURI} url - URL to check. - * @returns {Promise} Promise that is fulfilled with a boolean indicating - * whether the URL is in the list or not. - */ - containsURL: Task.async(function* (url) { - url = normalizeURI(url).spec; - - // This is used on every tab switch and page load of the current tab, so we - // want it to be quick and avoid a DB query whenever possible. - - // First check if any cached items have a direct match. - if (this._itemsByURL.has(url)) { - return true; - } - - // Then check if any cached items may have a different resolved URL - // that matches. - for (let itemWeakRef of this._itemsByURL.values()) { - let item = itemWeakRef.get(); - if (item && item.resolvedURL == url) { - return true; - } - } - - // Finally, fall back to the DB. - let count = yield this.count({url: url}, {resolvedURL: url}); - return (count > 0); - }), - - /** * Enumerates the items in the list that match the given options. * * @param callback Called for each item in the enumeration. It's passed a * single object, a ReadingListItem. It may return a promise; if so, * the callback will not be called for the next item until the promise * is resolved. * @param optsList A variable number of options objects that control the * items that are matched. See Options Objects. @@ -217,17 +185,16 @@ ReadingListImpl.prototype = { * returned promise is rejected in that case. * * @param obj A simple object representing an item. * @return Promise<ReadingListItem> Resolved with the new item when the list * is updated. Rejected with an Error on error. */ addItem: Task.async(function* (obj) { obj = stripNonItemProperties(obj); - normalizeReadingListProperties(obj); yield this._store.addItem(obj); this._invalidateIterators(); let item = this._itemFromObject(obj); this._callListeners("onItemAdded", item); let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager); mm.broadcastAsyncMessage("Reader:Added", item); return item; }), @@ -276,48 +243,21 @@ ReadingListImpl.prototype = { /** * Find any item that matches a given URL - either the item's URL, or its * resolved URL. * * @param {String/nsIURI} uri - URI to match against. This will be normalized. */ getItemForURL: Task.async(function* (uri) { - let url = normalizeURI(uri).spec; + let url = this._normalizeURI(uri).spec; let [item] = yield this.iterator({url: url}, {resolvedURL: url}).items(1); return item; }), - /** - * Add to the ReadingList the page that is loaded in a given browser. - * - * @param {<xul:browser>} browser - Browser element for the document. - * @return {Promise} Promise that is fullfilled with the added item. - */ - addItemFromBrowser: Task.async(function* (browser) { - let metadata = yield getMetadataFromBrowser(browser); - let itemData = { - url: browser.currentURI, - title: metadata.title, - resolvedURL: metadata.url, - excerpt: metadata.description, - }; - - if (metadata.description) { - itemData.exerpt = metadata.description; - } - - if (metadata.previews.length > 0) { - itemData.image = metadata.previews[0]; - } - - let item = yield ReadingList.addItem(itemData); - return item; - }), - /** * Adds a listener that will be notified when the list changes. Listeners * are objects with the following optional methods: * * onItemAdded(item) * onItemUpdated(item) * onItemDeleted(item) * @@ -360,16 +300,32 @@ ReadingListImpl.prototype = { // A Set containing nsIWeakReferences that refer to valid iterators produced // by the list. _iterators: null, // A Set containing listener objects. _listeners: null, /** + * Normalize a URI, stripping away extraneous parts we don't want to store + * or compare against. + * + * @param {nsIURI/String} uri - URI to normalize. + * @returns {nsIURI} Cloned and normalized version of the input URI. + */ + _normalizeURI(uri) { + if (typeof uri == "string") { + uri = Services.io.newURI(uri, "", null); + } + uri = uri.cloneIgnoringRef(); + uri.userPass = ""; + return uri; + }, + + /** * Returns the ReadingListItem represented by the given simple object. If * the item doesn't exist yet, it's created first. * * @param obj A simple object with item properties. * @return The ReadingListItem. */ _itemFromObject(obj) { let itemWeakRef = this._itemsByURL.get(obj.url); @@ -414,36 +370,22 @@ ReadingListImpl.prototype = { catch (err) { Cu.reportError(err); } } } }, _ensureItemBelongsToList(item) { - if (!item || !item._ensureBelongsToList) { - throw new Error("The item is not a ReadingListItem"); + if (item.list != this) { + throw new Error("The item does not belong to this list"); } - item._ensureBelongsToList(); }, }; -/* - * normalize the properties of a "regular" object that reflects a ReadingListItem - */ -function normalizeReadingListProperties(obj) { - if (obj.url) { - obj.url = normalizeURI(obj.url).spec; - } - if (obj.resolvedURL) { - obj.resolvedURL = normalizeURI(obj.resolvedURL).spec; - } -} - - let _unserializable = () => {}; // See comments in the ReadingListItem ctor. /** * An item in a reading list. * * Each item belongs to a list, and it's an error to use an item with a * ReadingList that the item doesn't belong to. * @@ -484,53 +426,65 @@ ReadingListItem.prototype = { * guarenteed to be set for local items. * @type string */ get guid() { return this._properties.guid || undefined; }, set guid(val) { this._properties.guid = val; + if (this.list) { + this.commit(); + } }, /** * The date the item was last modified. * @type Date */ get lastModified() { return this._properties.lastModified ? new Date(this._properties.lastModified) : undefined; }, set lastModified(val) { this._properties.lastModified = val.valueOf(); + if (this.list) { + this.commit(); + } }, /** * The item's URL. * @type string */ get url() { return this._properties.url; }, set url(val) { - this._properties.url = normalizeURI(val).spec; + this._properties.url = val; + if (this.list) { + this.commit(); + } }, /** * The item's URL as an nsIURI. * @type nsIURI */ get uri() { return this._properties.url ? Services.io.newURI(this._properties.url, "", null) : undefined; }, set uri(val) { - this.url = normalizeURI(val).spec; + this.url = val.spec; + if (this.list) { + this.commit(); + } }, /** * Returns the domain (a string) of the item's URL. If the URL doesn't have a * domain, then the URL itself (also a string) is returned. */ get domain() { try { @@ -543,211 +497,265 @@ ReadingListItem.prototype = { /** * The item's resolved URL. * @type string */ get resolvedURL() { return this._properties.resolvedURL; }, set resolvedURL(val) { - this._properties.resolvedURL = normalizeURI(val).spec; + this._properties.resolvedURL = val; + if (this.list) { + this.commit(); + } }, /** * The item's resolved URL as an nsIURI. * @type nsIURI */ get resolvedURI() { return this._properties.resolvedURL ? Services.io.newURI(this._properties.resolvedURL, "", null) : undefined; }, set resolvedURI(val) { this.resolvedURL = val.spec; + if (this.list) { + this.commit(); + } }, /** * The item's title. * @type string */ get title() { return this._properties.title; }, set title(val) { this._properties.title = val; + if (this.list) { + this.commit(); + } }, /** * The item's resolved title. * @type string */ get resolvedTitle() { return this._properties.resolvedTitle; }, set resolvedTitle(val) { this._properties.resolvedTitle = val; + if (this.list) { + this.commit(); + } }, /** * The item's excerpt. * @type string */ get excerpt() { return this._properties.excerpt; }, set excerpt(val) { this._properties.excerpt = val; + if (this.list) { + this.commit(); + } }, /** * The item's status. * @type integer */ get status() { return this._properties.status; }, set status(val) { this._properties.status = val; + if (this.list) { + this.commit(); + } }, /** * Whether the item is a favorite. * @type boolean */ get favorite() { return !!this._properties.favorite; }, set favorite(val) { this._properties.favorite = !!val; + if (this.list) { + this.commit(); + } }, /** * Whether the item is an article. * @type boolean */ get isArticle() { return !!this._properties.isArticle; }, set isArticle(val) { this._properties.isArticle = !!val; + if (this.list) { + this.commit(); + } }, /** * The item's word count. * @type integer */ get wordCount() { return this._properties.wordCount; }, set wordCount(val) { this._properties.wordCount = val; + if (this.list) { + this.commit(); + } }, /** * Whether the item is unread. * @type boolean */ get unread() { return !!this._properties.unread; }, set unread(val) { this._properties.unread = !!val; + if (this.list) { + this.commit(); + } }, /** * The date the item was added. * @type Date */ get addedOn() { return this._properties.addedOn ? new Date(this._properties.addedOn) : undefined; }, set addedOn(val) { this._properties.addedOn = val.valueOf(); + if (this.list) { + this.commit(); + } }, /** * The date the item was stored. * @type Date */ get storedOn() { return this._properties.storedOn ? new Date(this._properties.storedOn) : undefined; }, set storedOn(val) { this._properties.storedOn = val.valueOf(); + if (this.list) { + this.commit(); + } }, /** * The GUID of the device that marked the item read. * @type string */ get markedReadBy() { return this._properties.markedReadBy; }, set markedReadBy(val) { this._properties.markedReadBy = val; + if (this.list) { + this.commit(); + } }, /** * The date the item marked read. * @type Date */ get markedReadOn() { return this._properties.markedReadOn ? new Date(this._properties.markedReadOn) : undefined; }, set markedReadOn(val) { this._properties.markedReadOn = val.valueOf(); + if (this.list) { + this.commit(); + } }, /** * The item's read position. * @param integer */ get readPosition() { return this._properties.readPosition; }, set readPosition(val) { this._properties.readPosition = val; + if (this.list) { + this.commit(); + } }, /** - * Sets the given properties of the item, optionally calling list.updateItem(). + * Sets the given properties of the item, optionally calling commit(). * * @param props A simple object containing the properties to set. - * @param update If true, updateItem() is called for this item. - * @return Promise<null> If update is true, resolved when the update + * @param commit If true, commit() is called. + * @return Promise<null> If commit is true, resolved when the commit * completes; otherwise resolved immediately. */ - setProperties: Task.async(function* (props, update=true) { + setProperties: Task.async(function* (props, commit=true) { for (let name in props) { this._properties[name] = props[name]; } - // make sure everything is normalized. - normalizeReadingListProperties(this._properties); - if (update) { - yield this.list.updateItem(this); + if (commit) { + yield this.commit(); } }), /** * Deletes the item from its list. * * @return Promise<null> Resolved when the list has been updated. */ delete: Task.async(function* () { this._ensureBelongsToList(); yield this.list.deleteItem(this); this.delete = () => Promise.reject("The item has already been deleted"); }), + /** + * Notifies the item's list that the item has changed so that the list can + * update itself. + * + * @return Promise<null> Resolved when the list has been updated. + */ + commit: Task.async(function* () { + this._ensureBelongsToList(); + yield this.list.updateItem(this); + }), + toJSON() { return this._properties; }, _ensureBelongsToList() { if (!this.list) { throw new Error("The item must belong to a reading list"); } @@ -842,33 +850,16 @@ ReadingListItemIterator.prototype = { _ensureValid() { if (this.invalid) { throw new Error("The iterator has been invalidated"); } }, }; -/** - * Normalize a URI, stripping away extraneous parts we don't want to store - * or compare against. - * - * @param {nsIURI/String} uri - URI to normalize. - * @returns {nsIURI} Cloned and normalized version of the input URI. - */ -function normalizeURI(uri) { - if (typeof uri == "string") { - uri = Services.io.newURI(uri, "", null); - } - uri = uri.cloneIgnoringRef(); - try { - uri.userPass = ""; - } catch (ex) {} // nsSimpleURI etc don't support .userPass - return uri; -}; function stripNonItemProperties(item) { let obj = {}; for (let name of ITEM_BASIC_PROPERTY_NAMES) { if (name in item) { obj[name] = item[name]; } } @@ -889,34 +880,16 @@ function hash(str) { join(""); return hexStr; } function clone(obj) { return Cu.cloneInto(obj, {}, { cloneFunctions: false }); } -/** - * Get page metadata from the content document in a given <xul:browser>. - * @see PageMetadata.jsm - * - * @param {<xul:browser>} browser - Browser element for the document. - * @returns {Promise} Promise that is fulfilled with an object describing the metadata. - */ -function getMetadataFromBrowser(browser) { - let mm = browser.messageManager; - return new Promise(resolve => { - function handleResult(msg) { - mm.removeMessageListener("PageMetadata:PageDataResult", handleResult); - resolve(msg.json); - } - mm.addMessageListener("PageMetadata:PageDataResult", handleResult); - mm.sendAsyncMessage("PageMetadata:GetPageData"); - }); -} Object.defineProperty(this, "ReadingList", { get() { if (!this._singleton) { let store = new SQLiteStore("reading-list-temp.sqlite"); this._singleton = new ReadingListImpl(store); } return this._singleton;
--- a/browser/components/readinglist/test/xpcshell/test_ReadingList.js +++ b/browser/components/readinglist/test/xpcshell/test_ReadingList.js @@ -117,21 +117,20 @@ add_task(function* constraints() { yield gList.addItem(item); } catch (e) { err = e; } checkError(err); // update an item with an existing url - let rlitem = yield gList.getItemForURL(gItems[0].url); - rlitem.guid = gItems[1].guid; + item.guid = gItems[1].guid; err = null; try { - yield gList.updateItem(rlitem); + yield gList.updateItem(item); } catch (e) { err = e; } checkError(err); // add a new item with an existing resolvedURL item = kindOfClone(gItems[0]); @@ -141,58 +140,59 @@ add_task(function* constraints() { yield gList.addItem(item); } catch (e) { err = e; } checkError(err); // update an item with an existing resolvedURL - rlitem = yield gList.getItemForURL(gItems[0].url); - rlitem.url = gItems[1].url; + item.url = gItems[1].url; err = null; try { - yield gList.updateItem(rlitem); + yield gList.updateItem(item); } catch (e) { err = e; } checkError(err); // add a new item with no guid, which is allowed item = kindOfClone(gItems[0]); delete item.guid; err = null; - let rlitem1; try { - rlitem1 = yield gList.addItem(item); + yield gList.addItem(item); } catch (e) { err = e; } Assert.ok(!err, err ? err.message : undefined); + let item1 = item; // add a second item with no guid, which is allowed item = kindOfClone(gItems[1]); delete item.guid; err = null; - let rlitem2; try { - rlitem2 = yield gList.addItem(item); + yield gList.addItem(item); } catch (e) { err = e; } Assert.ok(!err, err ? err.message : undefined); + let item2 = item; // Delete both items since other tests assume the store contains only gItems. - yield gList.deleteItem(rlitem1); - yield gList.deleteItem(rlitem2); + item1.list = gList; + item2.list = gList; + yield gList.deleteItem(item1); + yield gList.deleteItem(item2); let items = []; - yield gList.forEachItem(i => items.push(i), { url: [rlitem1.url, rlitem2.url] }); + yield gList.forEachItem(i => items.push(i), { url: [item1.url, item2.url] }); Assert.equal(items.length, 0); // add a new item with no url item = kindOfClone(gItems[0]); delete item.url; err = null; try { yield gList.addItem(item); @@ -508,22 +508,25 @@ add_task(function* iterator_forEach_prom add_task(function* updateItem() { // get an item let items = []; yield gList.forEachItem(i => items.push(i), { guid: gItems[0].guid, }); Assert.equal(items.length, 1); - let item = items[0]; + let item = { + _properties: items[0]._properties, + list: items[0].list, + }; // update its title let newTitle = "updateItem new title"; Assert.notEqual(item.title, newTitle); - item.title = newTitle; + item._properties.title = newTitle; yield gList.updateItem(item); // get the item again items = []; yield gList.forEachItem(i => items.push(i), { guid: gItems[0].guid, }); Assert.equal(items.length, 1); @@ -534,98 +537,92 @@ add_task(function* updateItem() { add_task(function* item_setProperties() { // get an item let iter = gList.iterator({ sort: "guid", }); let item = (yield iter.items(1))[0]; Assert.ok(item); - // item.setProperties(update=false). After fetching the item again, its title + // item.setProperties(commit=false). After fetching the item again, its title // should be the old title. let oldTitle = item.title; let newTitle = "item_setProperties title 1"; Assert.notEqual(oldTitle, newTitle); item.setProperties({ title: newTitle }, false); Assert.equal(item.title, newTitle); iter = gList.iterator({ sort: "guid", }); let sameItem = (yield iter.items(1))[0]; Assert.ok(item === sameItem); Assert.equal(sameItem.title, oldTitle); - // item.setProperties(update=true). After fetching the item again, its title + // item.setProperties(commit=true). After fetching the item again, its title // should be the new title. newTitle = "item_setProperties title 2"; item.setProperties({ title: newTitle }, true); Assert.equal(item.title, newTitle); iter = gList.iterator({ sort: "guid", }); sameItem = (yield iter.items(1))[0]; Assert.ok(item === sameItem); Assert.equal(sameItem.title, newTitle); // Set item.title directly. After fetching the item again, its title should // be the new title. newTitle = "item_setProperties title 3"; item.title = newTitle; - gList.updateItem(item); Assert.equal(item.title, newTitle); iter = gList.iterator({ sort: "guid", }); sameItem = (yield iter.items(1))[0]; Assert.ok(item === sameItem); Assert.equal(sameItem.title, newTitle); }); add_task(function* listeners() { - Assert.equal((yield gList.count()), gItems.length); // add an item let resolve; let listenerPromise = new Promise(r => resolve = r); let listener = { onItemAdded: resolve, }; gList.addListener(listener); let item = kindOfClone(gItems[0]); let items = yield Promise.all([listenerPromise, gList.addItem(item)]); Assert.ok(items[0]); Assert.ok(items[0] === items[1]); gList.removeListener(listener); - Assert.equal((yield gList.count()), gItems.length + 1); // update an item listenerPromise = new Promise(r => resolve = r); listener = { onItemUpdated: resolve, }; gList.addListener(listener); items[0].title = "listeners new title"; - gList.updateItem(items[0]); let listenerItem = yield listenerPromise; Assert.ok(listenerItem); Assert.ok(listenerItem === items[0]); gList.removeListener(listener); - Assert.equal((yield gList.count()), gItems.length + 1); // delete an item listenerPromise = new Promise(r => resolve = r); listener = { onItemDeleted: resolve, }; gList.addListener(listener); items[0].delete(); listenerItem = yield listenerPromise; Assert.ok(listenerItem); Assert.ok(listenerItem === items[0]); gList.removeListener(listener); - Assert.equal((yield gList.count()), gItems.length); }); // This test deletes items so it should probably run last. add_task(function* deleteItem() { // delete first item with item.delete() let iter = gList.iterator({ sort: "guid", }); @@ -636,27 +633,27 @@ add_task(function* deleteItem() { Assert.equal((yield gList.count()), gItems.length - 1); let items = []; yield gList.forEachItem(i => items.push(i), { sort: "guid", }); checkItems(items, gItems.slice(1)); // delete second item with list.deleteItem() - yield gList.deleteItem(items[0]); + yield gList.deleteItem(gItems[1]); gItems[1].list = null; Assert.equal((yield gList.count()), gItems.length - 2); items = []; yield gList.forEachItem(i => items.push(i), { sort: "guid", }); checkItems(items, gItems.slice(2)); // delete third item with list.deleteItem() - yield gList.deleteItem(items[0]); + yield gList.deleteItem(gItems[2]); gItems[2].list = null; Assert.equal((yield gList.count()), gItems.length - 3); items = []; yield gList.forEachItem(i => items.push(i), { sort: "guid", }); checkItems(items, gItems.slice(3)); }); @@ -671,17 +668,17 @@ function checkItems(actualItems, expecte } } Assert.equal(actualItems[i].list, expectedItems[i].list); } } function checkError(err) { Assert.ok(err); - Assert.ok(err instanceof Cu.getGlobalForObject(Sqlite).Error, err); + Assert.ok(err instanceof Cu.getGlobalForObject(Sqlite).Error); } function kindOfClone(item) { let newItem = {}; for (let prop in item) { newItem[prop] = item[prop]; if (typeof(newItem[prop]) == "string") { newItem[prop] += " -- make this string different";
--- a/browser/modules/ReaderParent.jsm +++ b/browser/modules/ReaderParent.jsm @@ -55,22 +55,22 @@ let ReaderParent = { }); break; case "Reader:FaviconRequest": { // XXX: To implement. break; } case "Reader:ListStatusRequest": - ReadingList.containsURL(message.data.url).then(inList => { + ReadingList.count(message.data).then(count => { let mm = message.target.messageManager // Make sure the target browser is still alive before trying to send data back. if (mm) { mm.sendAsyncMessage("Reader:ListStatusData", - { inReadingList: inList, url: message.data.url }); + { inReadingList: !!count, url: message.data.url }); } }); break; case "Reader:RemoveFromList": // We need to get the "real" item to delete it. ReadingList.getItemForURL(message.data.url).then(item => { ReadingList.deleteItem(item)
--- a/browser/themes/linux/browser.css +++ b/browser/themes/linux/browser.css @@ -1624,18 +1624,16 @@ richlistitem[type~="action"][actiontype= -moz-image-region: rect(28px, 28px, 42px, 14px); } /* Popup blocker button */ #page-report-button { list-style-image: url("chrome://browser/skin/Info.png"); } -%include ../shared/readinglist.inc.css - /* Reader mode button */ #reader-mode-button { -moz-appearance: none; padding: 0; list-style-image: url("chrome://browser/skin/reader-mode-16.png"); -moz-image-region: rect(0, 16px, 16px, 0); }
--- a/browser/themes/linux/jar.mn +++ b/browser/themes/linux/jar.mn @@ -86,17 +86,16 @@ browser.jar: skin/classic/browser/Toolbar-inverted.png skin/classic/browser/Toolbar-small.png skin/classic/browser/undoCloseTab.png (../shared/undoCloseTab.png) skin/classic/browser/urlbar-arrow.png skin/classic/browser/session-restore.svg (../shared/incontent-icons/session-restore.svg) skin/classic/browser/tab-crashed.svg (../shared/incontent-icons/tab-crashed.svg) skin/classic/browser/welcome-back.svg (../shared/incontent-icons/welcome-back.svg) skin/classic/browser/reader-mode-16.png (../shared/reader/reader-mode-16.png) - skin/classic/browser/readinglist/icons.svg (../shared/readinglist/icons.svg) skin/classic/browser/readinglist/readinglist-icon.svg (../shared/readinglist/readinglist-icon.svg) skin/classic/browser/readinglist/sidebar.css (../shared/readinglist/sidebar.css) skin/classic/browser/webRTC-shareDevice-16.png skin/classic/browser/webRTC-shareDevice-64.png skin/classic/browser/webRTC-sharingDevice-16.png (../shared/webrtc/webRTC-sharingDevice-16.png) skin/classic/browser/webRTC-shareMicrophone-16.png skin/classic/browser/webRTC-shareMicrophone-64.png skin/classic/browser/webRTC-sharingMicrophone-16.png (../shared/webrtc/webRTC-sharingMicrophone-16.png)
--- a/browser/themes/osx/browser.css +++ b/browser/themes/osx/browser.css @@ -2518,18 +2518,16 @@ richlistitem[type~="action"][actiontype= } #page-report-button:hover:active, #page-report-button[open="true"] { -moz-image-region: rect(0, 64px, 32px, 32px); } } -%include ../shared/readinglist.inc.css - /* Reader mode button */ #reader-mode-button { -moz-appearance: none; padding: 0; list-style-image: url("chrome://browser/skin/reader-mode-16.png"); -moz-image-region: rect(0, 16px, 16px, 0); }
--- a/browser/themes/osx/jar.mn +++ b/browser/themes/osx/jar.mn @@ -137,17 +137,16 @@ browser.jar: skin/classic/browser/urlbar-arrow@2x.png skin/classic/browser/urlbar-popup-blocked.png skin/classic/browser/urlbar-popup-blocked@2x.png skin/classic/browser/session-restore.svg (../shared/incontent-icons/session-restore.svg) skin/classic/browser/tab-crashed.svg (../shared/incontent-icons/tab-crashed.svg) skin/classic/browser/welcome-back.svg (../shared/incontent-icons/welcome-back.svg) skin/classic/browser/reader-mode-16.png (../shared/reader/reader-mode-16.png) skin/classic/browser/reader-mode-16@2x.png (../shared/reader/reader-mode-16@2x.png) - skin/classic/browser/readinglist/icons.svg (../shared/readinglist/icons.svg) skin/classic/browser/readinglist/readinglist-icon.svg (../shared/readinglist/readinglist-icon.svg) skin/classic/browser/readinglist/sidebar.css (../shared/readinglist/sidebar.css) skin/classic/browser/webRTC-shareDevice-16.png skin/classic/browser/webRTC-shareDevice-16@2x.png skin/classic/browser/webRTC-shareDevice-64.png skin/classic/browser/webRTC-shareDevice-64@2x.png skin/classic/browser/webRTC-sharingDevice-16.png (../shared/webrtc/webRTC-sharingDevice-16.png) skin/classic/browser/webRTC-sharingDevice-16@2x.png (../shared/webrtc/webRTC-sharingDevice-16@2x.png)
deleted file mode 100644 --- a/browser/themes/shared/readinglist.inc.css +++ /dev/null @@ -1,38 +0,0 @@ -/* Reading List button */ - -#readinglist-addremove-button { - -moz-appearance: none; - border: none; - list-style-image: url("chrome://browser/skin/readinglist/icons.svg#addpage"); - padding: 3px; -} - -#readinglist-addremove-button:hover { - border: none; -} - -#readinglist-addremove-button > .toolbarbutton-icon { - width: 16px; - height: 16px -} - -#readinglist-addremove-button:not([already-added="true"]):hover { - list-style-image: url("chrome://browser/skin/readinglist/icons.svg#addpage-hover"); -} - -#readinglist-addremove-button:not([already-added="true"]):active { - list-style-image: url("chrome://browser/skin/readinglist/icons.svg#addpage-active"); -} - -#readinglist-addremove-button[already-added="true"] { - list-style-image: url("chrome://browser/skin/readinglist/icons.svg#alreadyadded"); -} - -#readinglist-addremove-button[already-added="true"]:hover { - list-style-image: url("chrome://browser/skin/readinglist/icons.svg#alreadyadded-hover"); -} - -#readinglist-addremove-button[already-added="true"]:active { - list-style-image: url("chrome://browser/skin/readinglist/icons.svg#alreadyadded-active"); -} -
deleted file mode 100644 --- a/browser/themes/shared/readinglist/icons.svg +++ /dev/null @@ -1,57 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> -<svg xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - viewBox="0 0 16 16" - xml:space="preserve"> - - <defs> - <style type="text/css"> - use:not(:target) { - display: none; - } - - #addpage { - fill: #808080; - } - #addpage-hover { - fill: #555555; - } - #addpage-active { - fill: #0095DD; - } - - #alreadyadded { - fill: #0095DD; - } - #alreadyadded-hover { - fill: #555555; - } - #alreadyadded-active { - fill: #808080; - } - </style> - - <mask id="plus-mask"> - <rect width="100%" height="100%" fill="white"/> - <rect x="4" y="7.5" width="8" height="1"/> - <rect x="7.5" y="4" width="1" height="8"/> - </mask> - - <g id="addpage-shape"> - <circle cx="8" cy="8" r="7" mask="url(#plus-mask)"/> - </g> - - </defs> - - <use id="addpage" xlink:href="#addpage-shape"/> - <use id="addpage-hover" xlink:href="#addpage-shape"/> - <use id="addpage-active" xlink:href="#addpage-shape"/> - - <use id="alreadyadded" xlink:href="#addpage-shape"/> - <use id="alreadyadded-hover" xlink:href="#addpage-shape"/> - <use id="alreadyadded-active" xlink:href="#addpage-shape"/> - -</svg>
--- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -1571,18 +1571,16 @@ richlistitem[type~="action"][actiontype= -moz-image-region: rect(0, 32px, 16px, 16px); } #page-report-button:hover:active, #page-report-button[open="true"] { -moz-image-region: rect(0, 48px, 16px, 32px); } -%include ../shared/readinglist.inc.css - /* Reader mode button */ #reader-mode-button { -moz-appearance: none; padding: 0; list-style-image: url("chrome://browser/skin/reader-mode-16.png"); -moz-image-region: rect(0, 16px, 16px, 0); }
--- a/browser/themes/windows/jar.mn +++ b/browser/themes/windows/jar.mn @@ -105,17 +105,16 @@ browser.jar: skin/classic/browser/undoCloseTab@2x.png (../shared/undoCloseTab@2x.png) skin/classic/browser/urlbar-arrow.png skin/classic/browser/urlbar-popup-blocked.png skin/classic/browser/urlbar-history-dropmarker.png skin/classic/browser/session-restore.svg (../shared/incontent-icons/session-restore.svg) skin/classic/browser/tab-crashed.svg (../shared/incontent-icons/tab-crashed.svg) skin/classic/browser/welcome-back.svg (../shared/incontent-icons/welcome-back.svg) skin/classic/browser/reader-mode-16.png (../shared/reader/reader-mode-16.png) - skin/classic/browser/readinglist/icons.svg (../shared/readinglist/icons.svg) skin/classic/browser/readinglist/readinglist-icon.svg (../shared/readinglist/readinglist-icon.svg) skin/classic/browser/readinglist/sidebar.css (../shared/readinglist/sidebar.css) skin/classic/browser/notification-pluginNormal.png (../shared/plugins/notification-pluginNormal.png) skin/classic/browser/notification-pluginAlert.png (../shared/plugins/notification-pluginAlert.png) skin/classic/browser/notification-pluginBlocked.png (../shared/plugins/notification-pluginBlocked.png) skin/classic/browser/webRTC-shareDevice-16.png skin/classic/browser/webRTC-shareDevice-64.png skin/classic/browser/webRTC-sharingDevice-16.png (../shared/webrtc/webRTC-sharingDevice-16.png) @@ -573,17 +572,16 @@ browser.jar: skin/classic/aero/browser/undoCloseTab@2x.png (../shared/undoCloseTab@2x.png) skin/classic/aero/browser/urlbar-arrow.png skin/classic/aero/browser/urlbar-popup-blocked.png skin/classic/aero/browser/urlbar-history-dropmarker.png skin/classic/aero/browser/session-restore.svg (../shared/incontent-icons/session-restore.svg) skin/classic/aero/browser/tab-crashed.svg (../shared/incontent-icons/tab-crashed.svg) skin/classic/aero/browser/welcome-back.svg (../shared/incontent-icons/welcome-back.svg) skin/classic/aero/browser/reader-mode-16.png (../shared/reader/reader-mode-16.png) - skin/classic/aero/browser/readinglist/icons.svg (../shared/readinglist/icons.svg) skin/classic/aero/browser/readinglist/readinglist-icon.svg (../shared/readinglist/readinglist-icon.svg) skin/classic/aero/browser/readinglist/sidebar.css (../shared/readinglist/sidebar.css) skin/classic/aero/browser/notification-pluginNormal.png (../shared/plugins/notification-pluginNormal.png) skin/classic/aero/browser/notification-pluginAlert.png (../shared/plugins/notification-pluginAlert.png) skin/classic/aero/browser/notification-pluginBlocked.png (../shared/plugins/notification-pluginBlocked.png) skin/classic/aero/browser/webRTC-shareDevice-16.png skin/classic/aero/browser/webRTC-shareDevice-64.png skin/classic/aero/browser/webRTC-sharingDevice-16.png (../shared/webrtc/webRTC-sharingDevice-16.png)