зеркало из https://github.com/mozilla/gecko-dev.git
377 строки
11 KiB
JavaScript
377 строки
11 KiB
JavaScript
/*
|
|
# 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/.
|
|
*/
|
|
|
|
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",
|
|
"ReadingList:ShowIntro",
|
|
],
|
|
|
|
/**
|
|
* 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();
|
|
},
|
|
|
|
/**
|
|
* Un-initialize the ReadingList UI.
|
|
*/
|
|
uninit() {
|
|
Preferences.ignore("browser.readinglist.enabled", this.updateUI, this);
|
|
|
|
const mm = window.messageManager;
|
|
for (let msg of this.MESSAGES) {
|
|
mm.removeMessageListener(msg, this);
|
|
}
|
|
|
|
if (this.listenerRegistered) {
|
|
ReadingList.removeListener(this);
|
|
this.listenerRegistered = false;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Whether the ReadingList feature is enabled or not.
|
|
* @type {boolean}
|
|
*/
|
|
get enabled() {
|
|
return Preferences.get("browser.readinglist.enabled", false);
|
|
},
|
|
|
|
/**
|
|
* Whether the ReadingList sidebar is currently open or not.
|
|
* @type {boolean}
|
|
*/
|
|
get isSidebarOpen() {
|
|
return SidebarUI.isOpen && SidebarUI.currentID == READINGLIST_COMMAND_ID;
|
|
},
|
|
|
|
/**
|
|
* 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 = false;
|
|
}
|
|
|
|
this.hideSidebar();
|
|
}
|
|
|
|
document.getElementById(READINGLIST_COMMAND_ID).setAttribute("hidden", !enabled);
|
|
document.getElementById(READINGLIST_COMMAND_ID).setAttribute("disabled", !enabled);
|
|
},
|
|
|
|
/**
|
|
* Show the ReadingList sidebar.
|
|
* @return {Promise}
|
|
*/
|
|
showSidebar() {
|
|
if (this.enabled) {
|
|
return SidebarUI.show(READINGLIST_COMMAND_ID);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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");
|
|
}
|
|
|
|
while (!target.firstChild.id)
|
|
target.firstChild.remove();
|
|
|
|
let classList = "menuitem-iconic bookmark-item menuitem-with-favicon";
|
|
let insertPoint = target.firstChild;
|
|
if (insertPoint.classList.contains("subviewbutton"))
|
|
classList += " subviewbutton";
|
|
|
|
let hasItems = false;
|
|
yield ReadingList.forEachItem(item => {
|
|
hasItems = true;
|
|
|
|
let menuitem = document.createElement("menuitem");
|
|
menuitem.setAttribute("label", item.title || item.url);
|
|
menuitem.setAttribute("class", classList);
|
|
|
|
let node = menuitem._placesNode = {
|
|
// Passing the PlacesUtils.nodeIsURI check is required for the
|
|
// onCommand handler to load our URI.
|
|
type: Ci.nsINavHistoryResultNode.RESULT_TYPE_URI,
|
|
|
|
// makes PlacesUIUtils.canUserRemove return false.
|
|
// The context menu is broken without this.
|
|
parent: {type: Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER},
|
|
|
|
// A -1 id makes this item a non-bookmark, which avoids calling
|
|
// PlacesUtils.annotations.itemHasAnnotation to check if the
|
|
// bookmark should be opened in the sidebar (this call fails for
|
|
// readinglist item, and breaks loading our URI).
|
|
itemId: -1,
|
|
|
|
// Used by the tooltip and onCommand handlers.
|
|
uri: item.url,
|
|
|
|
// Used by the tooltip.
|
|
title: item.title
|
|
};
|
|
|
|
Favicons.getFaviconURLForPage(item.uri, uri => {
|
|
if (uri) {
|
|
menuitem.setAttribute("image",
|
|
Favicons.getFaviconLinkForIcon(uri).spec);
|
|
}
|
|
});
|
|
|
|
target.insertBefore(menuitem, insertPoint);
|
|
}, {sort: "addedOn", descending: true});
|
|
|
|
if (!hasItems) {
|
|
let menuitem = document.createElement("menuitem");
|
|
let bundle =
|
|
Services.strings.createBundle("chrome://browser/locale/places/places.properties");
|
|
menuitem.setAttribute("label", bundle.GetStringFromName("bookmarksMenuEmptyFolder"));
|
|
menuitem.setAttribute("class", "bookmark-item");
|
|
menuitem.setAttribute("disabled", true);
|
|
target.insertBefore(menuitem, insertPoint);
|
|
}
|
|
}),
|
|
|
|
/**
|
|
* Hide the ReadingList sidebar, if it is currently shown.
|
|
*/
|
|
toggleSidebar() {
|
|
if (this.enabled) {
|
|
SidebarUI.toggle(READINGLIST_COMMAND_ID);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Respond to messages.
|
|
*/
|
|
receiveMessage(message) {
|
|
switch (message.name) {
|
|
case "ReadingList:GetVisibility": {
|
|
if (message.target.messageManager) {
|
|
message.target.messageManager.sendAsyncMessage("ReadingList:VisibilityStatus",
|
|
{ isOpen: this.isSidebarOpen });
|
|
}
|
|
break;
|
|
}
|
|
|
|
case "ReadingList:ToggleVisibility": {
|
|
this.toggleSidebar();
|
|
break;
|
|
}
|
|
|
|
case "ReadingList:ShowIntro": {
|
|
if (this.enabled && !Preferences.get("browser.readinglist.introShown", false)) {
|
|
Preferences.set("browser.readinglist.introShown", true);
|
|
this.showSidebar();
|
|
}
|
|
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.toolbarButton) {
|
|
// nothing to do if we have no button.
|
|
return;
|
|
}
|
|
|
|
let uri;
|
|
if (this.enabled && state == "valid") {
|
|
uri = gBrowser.currentURI;
|
|
if (uri.schemeIs("about"))
|
|
uri = ReaderMode.getOriginalUrl(uri.spec);
|
|
else if (!uri.schemeIs("http") && !uri.schemeIs("https"))
|
|
uri = null;
|
|
}
|
|
|
|
let msg = {topic: "UpdateActiveItem", url: null};
|
|
if (!uri) {
|
|
this.toolbarButton.setAttribute("hidden", true);
|
|
if (this.isSidebarOpen)
|
|
document.getElementById("sidebar").contentWindow.postMessage(msg, "*");
|
|
return;
|
|
}
|
|
|
|
let isInList = yield ReadingList.hasItemForURL(uri);
|
|
|
|
if (window.closed) {
|
|
// Skip updating the UI if the window was closed since our hasItemForURL call.
|
|
return;
|
|
}
|
|
|
|
if (this.isSidebarOpen) {
|
|
if (isInList)
|
|
msg.url = typeof uri == "string" ? uri : uri.spec;
|
|
document.getElementById("sidebar").contentWindow.postMessage(msg, "*");
|
|
}
|
|
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");
|
|
},
|
|
|
|
buttonClick(event) {
|
|
if (event.button != 0) {
|
|
return;
|
|
}
|
|
this.togglePageByBrowser(gBrowser.selectedBrowser);
|
|
},
|
|
|
|
/**
|
|
* 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 uri = browser.currentURI;
|
|
if (uri.spec.startsWith("about:reader?"))
|
|
uri = ReaderMode.getOriginalUrl(uri.spec);
|
|
if (!uri)
|
|
return;
|
|
|
|
let item = yield ReadingList.itemForURL(uri);
|
|
if (item) {
|
|
yield item.delete();
|
|
} else {
|
|
yield ReadingList.addItemFromBrowser(browser, uri);
|
|
}
|
|
}),
|
|
|
|
/**
|
|
* 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 (currentURL.startsWith("about:reader?"))
|
|
currentURL = ReaderMode.getOriginalUrl(currentURL);
|
|
|
|
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 (!Services.prefs.getBoolPref("browser.readinglist.sidebarEverOpened")) {
|
|
SidebarUI.show("readingListSidebar");
|
|
}
|
|
if (this.isItemForCurrentBrowser(item)) {
|
|
this.setToolbarButtonState(true);
|
|
if (this.isSidebarOpen) {
|
|
let msg = {topic: "UpdateActiveItem", url: item.url};
|
|
document.getElementById("sidebar").contentWindow.postMessage(msg, "*");
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* ReadingList event handler for when an item is deleted.
|
|
*
|
|
* @param {ReadingListItem} item - Item deleted.
|
|
*/
|
|
onItemDeleted(item) {
|
|
if (this.isItemForCurrentBrowser(item)) {
|
|
this.setToolbarButtonState(false);
|
|
}
|
|
},
|
|
};
|