diff --git a/src/js/background/assignManager.js b/src/js/background/assignManager.js index 83acd4b..d5aca77 100644 --- a/src/js/background/assignManager.js +++ b/src/js/background/assignManager.js @@ -4,7 +4,7 @@ const assignManager = { MENU_SEPARATOR_ID: "separator", MENU_HIDE_ID: "hide-container", MENU_MOVE_ID: "move-to-new-window-container", - + OPEN_IN_CONTAINER: "open-bookmark-in-container-tab", storageArea: { area: browser.storage.local, exemptedTabs: {}, @@ -220,7 +220,7 @@ const assignManager = { init() { browser.contextMenus.onClicked.addListener((info, tab) => { - this._onClickedHandler(info, tab); + info.bookmarkId ? this._onClickedBookmark(info) : this._onClickedHandler(info, tab); }); // Before a request is handled by the browser we decide if we should route through a different container @@ -240,7 +240,39 @@ const assignManager = { delete this.canceledRequests[options.tabId]; } },{urls: [""], types: ["main_frame"]}); + this.getPermissions(); + }, + async getPermissions() { + const {permissions} = await browser.permissions.getAll(); + permissions.includes("bookmarks") ? this.makeBookmarksMenu() : browser.contextMenus.remove(this.OPEN_IN_CONTAINER); + }, + + makeBookmarksMenu() { + this.initBookmarksMenu(); + browser.contextualIdentities.onCreated.addListener(this.contextualIdentityCreated); + browser.contextualIdentities.onUpdated.addListener(this.contextualIdentityUpdated); + browser.contextualIdentities.onRemoved.addListener(this.contextualIdentityRemoved); + }, + + contextualIdentityCreated(changeInfo) { + browser.contextMenus.create({ + parentId: assignManager.OPEN_IN_CONTAINER, + id: changeInfo.contextualIdentity.cookieStoreId, + title: changeInfo.contextualIdentity.name, + icons: { "16": `img/usercontext.svg#${changeInfo.contextualIdentity.icon}` } + }); + }, + + contextualIdentityUpdated(changeInfo) { + browser.contextMenus.update(changeInfo.contextualIdentity.cookieStoreId, { + title: changeInfo.contextualIdentity.name, + icons: { "16": `img/usercontext.svg#${changeInfo.contextualIdentity.icon}` } + }); + }, + + contextualIdentityRemoved(changeInfo) { + browser.contextMenus.remove(changeInfo.contextualIdentity.cookieStoreId); }, async _onClickedHandler(info, tab) { @@ -274,6 +306,38 @@ const assignManager = { } }, + async _onClickedBookmark(info) { + + async function _getBookmarksFromInfo(info) { + const [bookmarkTreeNode] = await browser.bookmarks.get(info.bookmarkId); + if (bookmarkTreeNode.type === "folder") { + return await browser.bookmarks.getChildren(bookmarkTreeNode.id); + } + return [bookmarkTreeNode]; + } + + const bookmarks = await _getBookmarksFromInfo(info); + for (const bookmark of bookmarks) { + // Some checks on the urls from https://github.com/Rob--W/bookmark-container-tab/ thanks! + if ( !/^(javascript|place):/i.test(bookmark.url) && bookmark.type !== "folder") { + const openInReaderMode = bookmark.url.startsWith("about:reader"); + if(openInReaderMode) { + try { + const parsed = new URL(bookmark.url); + bookmark.url = parsed.searchParams.get("url") + parsed.hash; // can't believe const lets me do this ... + } catch (err) { + return err.message; + } + } + browser.tabs.create({ + cookieStoreId: info.menuItemId, + url: bookmark.url, + openInReaderMode: openInReaderMode + }); + } + } + }, + deleteContainer(userContextId) { this.storageArea.deleteContainer(userContextId); @@ -439,6 +503,24 @@ const assignManager = { throw e; }); } + }, + + async initBookmarksMenu() { + browser.contextMenus.create({ + id: this.OPEN_IN_CONTAINER, + title: "Open Bookmark in Container Tab", + contexts: ["bookmark"], + }); + + const identities = await browser.contextualIdentities.query({}); + for (const identity of identities) { + browser.contextMenus.create({ + parentId: this.OPEN_IN_CONTAINER, + id: identity.cookieStoreId, + title: identity.name, + icons: { "16": `img/usercontext.svg#${identity.icon}` } + }); + } } }; diff --git a/src/js/background/backgroundLogic.js b/src/js/background/backgroundLogic.js index ee1bd22..96a1b26 100644 --- a/src/js/background/backgroundLogic.js +++ b/src/js/background/backgroundLogic.js @@ -46,9 +46,6 @@ const backgroundLogic = { donePromise = browser.contextualIdentities.create(options.params); } await donePromise; - browser.runtime.sendMessage({ - method: "refreshNeeded" - }); }, async openNewTab(options) { @@ -329,4 +326,4 @@ const backgroundLogic = { cookieStoreId(userContextId) { return `firefox-container-${userContextId}`; } -}; +}; \ No newline at end of file diff --git a/src/js/background/messageHandler.js b/src/js/background/messageHandler.js index 046d163..132f0a7 100644 --- a/src/js/background/messageHandler.js +++ b/src/js/background/messageHandler.js @@ -10,6 +10,9 @@ const messageHandler = { let response; switch (m.method) { + case "resetBookmarksContext": + response = assignManager.getPermissions(); + break; case "deleteContainer": response = backgroundLogic.deleteContainer(m.message.userContextId); break; diff --git a/src/js/options.js b/src/js/options.js new file mode 100644 index 0000000..0eb1bb5 --- /dev/null +++ b/src/js/options.js @@ -0,0 +1,41 @@ + +function requestPermissions() { + const checkbox = document.querySelector("#bookmarksPermissions"); + if (checkbox.checked) { + browser.permissions.request({permissions: ["bookmarks"]}). + then((response) => { + if (response) { + browser.runtime.sendMessage({ method: "resetBookmarksContext" }); + } else { + checkbox.checked = false; + } + }). + catch((err) => { + return err.message; + }); + } else { + browser.permissions.remove({permissions: ["bookmarks"]}). + then(() => { + browser.runtime.sendMessage({ method: "resetBookmarksContext" }); + }). + catch((err) => { + return err.message; + }); + } +} + +function restoreOptions() { + browser.permissions.getAll() + .then((permissions) => { + if (permissions.permissions.includes("bookmarks")) { + document.querySelector("#bookmarksPermissions").checked = true; + } + }). + catch((err) => { + return err.message; + }); +} + + +document.addEventListener("DOMContentLoaded", restoreOptions); +document.querySelector("#bookmarksPermissions").addEventListener( "change", requestPermissions); \ No newline at end of file diff --git a/src/manifest.json b/src/manifest.json index 26d2dd8..285388d 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -29,6 +29,9 @@ "webRequestBlocking", "webRequest" ], + "optional_permissions": [ + "bookmarks" + ], "commands": { "_execute_browser_action": { "suggested_key": { @@ -70,5 +73,8 @@ ], "web_accessible_resources": [ "/img/container-site-d-24.png" - ] + ], + "options_ui": { + "page": "options.html" + } } \ No newline at end of file diff --git a/src/options.html b/src/options.html new file mode 100644 index 0000000..84f7353 --- /dev/null +++ b/src/options.html @@ -0,0 +1,16 @@ + + + + + + + + +
+ + +

This setting allows you to open a bookmark or folder of bookmarks in a container.

+
+ + + \ No newline at end of file diff --git a/test/browser.mock.js b/test/browser.mock.js index d6d38dd..fbff362 100644 --- a/test/browser.mock.js +++ b/test/browser.mock.js @@ -58,7 +58,16 @@ module.exports = () => { contextualIdentities: { create: sinon.stub(), get: sinon.stub(), - query: sinon.stub().resolves([]) + query: sinon.stub().resolves([]), + onCreated: { + addListener: sinon.stub() + }, + onUpdated: { + addListener: sinon.stub() + }, + onRemoved: { + addListener: sinon.stub() + } }, contextMenus: { create: sinon.stub(), @@ -82,6 +91,9 @@ module.exports = () => { }, extension: { getURL: sinon.stub().returns("moz-extension://multi-account-containers/confirm-page.html") + }, + permissions: { + getAll: sinon.stub().returns({"permissions": ["bookmarks"]}) } };