diff --git a/index.js b/index.js index 01a4afc..f350885 100644 --- a/index.js +++ b/index.js @@ -113,7 +113,7 @@ const ContainerService = { tabs.on("activate", tab => { this._hideAllPanels(); - this._restyleTab(tab).catch(() => {}); + this._restyleActiveTab(tab).catch(() => {}); }); // Modify CSS and other stuff for each window. @@ -556,7 +556,7 @@ const ContainerService = { } }, - _restyleTab(tab) { + _restyleActiveTab(tab) { if (!tab) { return Promise.resolve(null); } @@ -604,6 +604,9 @@ ContainerWindow.prototype = { return Promise.all([ this._configurePlusButtonMenu(), this._configureActiveTab(), + this._configureFileMenu(), + this._configureContextMenu(), + // TODO: this should change the decoration of the tab. ]); }, @@ -686,7 +689,99 @@ ContainerWindow.prototype = { _configureActiveTab() { const tab = modelFor(this._window).tabs.activeTab; - return ContainerService._restyleTab(tab); + return ContainerService._restyleActiveTab(tab); + }, + + _configureFileMenu() { + return this._configureMenu("menu_newUserContext", null, e => { + const userContextId = parseInt(e.target.getAttribute("data-usercontextid"), 10); + ContainerService.openTab({ userContextId }); + }); + }, + + _configureContextMenu() { + return this._configureMenu("context-openlinkinusercontext-menu", + () => { + // This userContextId is what we want to exclude. + const tab = modelFor(this._window).tabs.activeTab; + return ContainerService._getUserContextIdFromTab(tab); + }, + e => { + // This is a super internal method. Hopefully it will be stable in the + // next FF releases. + this._window.gContextMenu.openLinkInTab(e); + } + ); + }, + + // Generic menu configuration. + _configureMenu(menuId, excludedContainerCb, clickCb) { + const menu = this._window.document.getElementById(menuId); + // containerAddonMagic attribute is a custom attribute we set in order to + // know if this menu has been already converted. + if (!menu || menu.hasAttribute("containerAddonMagic")) { + return Promise.reject(null); + } + + // We don't want to recreate the menu each time. + menu.setAttribute("containerAddonMagic", "42"); + + while (menu.firstChild) { + menu.firstChild.remove(); + } + + const menupopup = this._window.document.createElementNS(XUL_NS, "menupopup"); + menu.appendChild(menupopup); + + menupopup.addEventListener("command", clickCb); + menupopup.addEventListener("popupshowing", e => { + return this._createMenu(e, excludedContainerCb); + }); + + return Promise.resolve(null); + }, + + _createMenu(event, excludedContainerCb) { + while (event.target.hasChildNodes()) { + event.target.removeChild(event.target.firstChild); + } + + ContainerService.queryIdentities().then(identities => { + const fragment = this._window.document.createDocumentFragment(); + + const excludedUserContextId = excludedContainerCb ? excludedContainerCb() : 0; + if (excludedUserContextId) { + const bundle = this._window.document.getElementById("bundle_browser"); + + const menuitem = this._window.document.createElementNS(XUL_NS, "menuitem"); + menuitem.setAttribute("data-usercontextid", "0"); + menuitem.setAttribute("label", bundle.getString("userContextNone.label")); + menuitem.setAttribute("accesskey", bundle.getString("userContextNone.accesskey")); + + fragment.appendChild(menuitem); + + const menuseparator = this._window.document.createElementNS(XUL_NS, "menuseparator"); + fragment.appendChild(menuseparator); + } + + identities.forEach(identity => { + if (identity.userContextId === excludedUserContextId) { + return; + } + + const menuitem = this._window.document.createElementNS(XUL_NS, "menuitem"); + menuitem.setAttribute("label", identity.name); + menuitem.classList.add("menuitem-iconic"); + menuitem.setAttribute("data-usercontextid", identity.userContextId); + menuitem.setAttribute("data-identity-color", identity.color); + menuitem.setAttribute("data-identity-icon", identity.image); + fragment.appendChild(menuitem); + }); + + event.target.appendChild(fragment); + }).catch(() => {}); + + return true; }, // This function puts the popup in the correct place.