diff --git a/webextension/background.js b/webextension/background.js index 6448a99..24e679d 100644 --- a/webextension/background.js +++ b/webextension/background.js @@ -408,6 +408,15 @@ const messageHandler = { let response; switch (m.method) { + case "getContainers": + response = browser.contextualIdentities.query({}); + break; + case "openTab": + response = browser.tabs.create({ + url: m.url, + cookieStoreId: m.cookieStoreId + }); + break; case "deleteContainer": response = backgroundLogic.deleteContainer(m.message.userContextId); break; diff --git a/webextension/css/content.css b/webextension/css/content.css index 885b0fb..a389ed6 100644 --- a/webextension/css/content.css +++ b/webextension/css/content.css @@ -20,3 +20,67 @@ inline-size: 16px; margin-inline-end: 3px; } + +#containers-menu { + background: #fcfcfc; + border: 1px solid #979797; + border-radius: 3px; + box-shadow: 3px 3px 5px #91919166; + font-family: Roboto, Noto, "San Francisco", Ubuntu, "Segoe UI", "Fira Sans", message-box, Arial, sans-serif; + inline-size: 7rem; + position: absolute; + padding: 0; + z-index: 9999999; + /* This is the area we allow users to move away from the menu without it closing + */ + margin: 4px; + -moz-user-select: none; +} + +#containers-menu > li:first-of-type { + border-radius: 3px 3px 0 0; +} + +#containers-menu > li:last-of-type { + border: none; + border-radius: 0 0 3px 3px; +} + +.dark-theme#containers-menu { + background: #2b2f38; +} + +#containers-menu > li { + align-items: center; + border-block-end: 1px solid #979797; + color: #434343; + display: flex; + margin: 0; + padding: 10px; +} + +#containers-menu > li > span { + flex: 1; + mask-image: linear-gradient(to left, transparent, black 1em); + overflow: hidden; + white-space: nowrap; +} + +.dark-theme#containers-menu li { + color: #92959b; +} + +#containers-menu li:hover, +#containers-menu li:focus { + outline: none; + cursor: pointer; + background-color: #f0f0f0; +} + +#containers-menu > li > .usercontext-icon { + background-size: 16px; + flex: 0 0 16px; + margin-inline-end: 6px; + max-block-size: 16px; + min-block-size: 16px; +} diff --git a/webextension/css/popup.css b/webextension/css/popup.css index 231f6a3..18a51b6 100644 --- a/webextension/css/popup.css +++ b/webextension/css/popup.css @@ -64,95 +64,6 @@ table { white-space: nowrap; } -/* Color and icon helpers */ -[data-identity-color="blue"] { - --identity-tab-color: #37adff; - --identity-icon-color: #37adff; -} - -[data-identity-color="turquoise"] { - --identity-tab-color: #00c79a; - --identity-icon-color: #00c79a; -} - -[data-identity-color="green"] { - --identity-tab-color: #51cd00; - --identity-icon-color: #51cd00; -} - -[data-identity-color="yellow"] { - --identity-tab-color: #ffcb00; - --identity-icon-color: #ffcb00; -} - -[data-identity-color="orange"] { - --identity-tab-color: #ff9f00; - --identity-icon-color: #ff9f00; -} - -[data-identity-color="red"] { - --identity-tab-color: #ff613d; - --identity-icon-color: #ff613d; -} - -[data-identity-color="pink"] { - --identity-tab-color: #ff4bda; - --identity-icon-color: #ff4bda; -} - -[data-identity-color="purple"] { - --identity-tab-color: #af51f5; - --identity-icon-color: #af51f5; -} - -[data-identity-icon="fingerprint"] { - --identity-icon: url("/img/usercontext.svg#fingerprint"); -} - -[data-identity-icon="briefcase"] { - --identity-icon: url("/img/usercontext.svg#briefcase"); -} - -[data-identity-icon="dollar"] { - --identity-icon: url("/img/usercontext.svg#dollar"); -} - -[data-identity-icon="cart"] { - --identity-icon: url("/img/usercontext.svg#cart"); -} - -[data-identity-icon="circle"] { - --identity-icon: url("/img/usercontext.svg#circle"); -} - -[data-identity-icon="food"] { - --identity-icon: url("/img/usercontext.svg#food"); -} - -[data-identity-icon="gift"] { - --identity-icon: url("/img/usercontext.svg#gift"); -} - -[data-identity-icon="vacation"] { - --identity-icon: url("/img/usercontext.svg#vacation"); -} - -[data-identity-icon="fruit"] { - --identity-icon: url("/img/usercontext.svg#fruit"); -} - -[data-identity-icon="pet"] { - --identity-icon: url("/img/usercontext.svg#pet"); -} - -[data-identity-icon="tree"] { - --identity-icon: url("/img/usercontext.svg#tree"); -} - -[data-identity-icon="chill"] { - --identity-icon: url("/img/usercontext.svg#chill"); -} - #current-tab [data-identity-icon="default-tab"] { background: center center no-repeat url("/img/blank-tab.svg"); fill: currentColor; @@ -486,18 +397,6 @@ span ~ .panel-header-text { flex: 0 0 48px; } -/* .userContext-icon is used natively, Bug 1333811 was raised to fix */ -.usercontext-icon { - background-image: var(--identity-icon); - background-position: center center; - background-repeat: no-repeat; - background-size: 20px 20px; - block-size: 48px; - fill: var(--identity-icon-color); - filter: url('/img/filters.svg#fill'); - flex: 0 0 48px; -} - .container-panel-row:hover .clickable .usercontext-icon, .container-panel-row:focus .clickable .usercontext-icon { background-image: url('/img/container-newtab.svg'); diff --git a/webextension/css/user-context.css b/webextension/css/user-context.css new file mode 100644 index 0000000..7b5d799 --- /dev/null +++ b/webextension/css/user-context.css @@ -0,0 +1,100 @@ +/* Color and icon helpers */ +[data-identity-color="blue"] { + --identity-tab-color: #37adff; + --identity-icon-color: #37adff; +} + +[data-identity-color="turquoise"] { + --identity-tab-color: #00c79a; + --identity-icon-color: #00c79a; +} + +[data-identity-color="green"] { + --identity-tab-color: #51cd00; + --identity-icon-color: #51cd00; +} + +[data-identity-color="yellow"] { + --identity-tab-color: #ffcb00; + --identity-icon-color: #ffcb00; +} + +[data-identity-color="orange"] { + --identity-tab-color: #ff9f00; + --identity-icon-color: #ff9f00; +} + +[data-identity-color="red"] { + --identity-tab-color: #ff613d; + --identity-icon-color: #ff613d; +} + +[data-identity-color="pink"] { + --identity-tab-color: #ff4bda; + --identity-icon-color: #ff4bda; +} + +[data-identity-color="purple"] { + --identity-tab-color: #af51f5; + --identity-icon-color: #af51f5; +} + +[data-identity-icon="fingerprint"] { + --identity-icon: url("/img/usercontext.svg#fingerprint"); +} + +[data-identity-icon="briefcase"] { + --identity-icon: url("/img/usercontext.svg#briefcase"); +} + +[data-identity-icon="dollar"] { + --identity-icon: url("/img/usercontext.svg#dollar"); +} + +[data-identity-icon="cart"] { + --identity-icon: url("/img/usercontext.svg#cart"); +} + +[data-identity-icon="circle"] { + --identity-icon: url("/img/usercontext.svg#circle"); +} + +[data-identity-icon="food"] { + --identity-icon: url("/img/usercontext.svg#food"); +} + +[data-identity-icon="gift"] { + --identity-icon: url("/img/usercontext.svg#gift"); +} + +[data-identity-icon="vacation"] { + --identity-icon: url("/img/usercontext.svg#vacation"); +} + +[data-identity-icon="fruit"] { + --identity-icon: url("/img/usercontext.svg#fruit"); +} + +[data-identity-icon="pet"] { + --identity-icon: url("/img/usercontext.svg#pet"); +} + +[data-identity-icon="tree"] { + --identity-icon: url("/img/usercontext.svg#tree"); +} + +[data-identity-icon="chill"] { + --identity-icon: url("/img/usercontext.svg#chill"); +} + +/* .userContext-icon is used natively, Bug 1333811 was raised to fix */ +.usercontext-icon { + background-image: var(--identity-icon); + background-position: center center; + background-repeat: no-repeat; + background-size: 20px 20px; + block-size: 48px; + fill: var(--identity-icon-color); + filter: url('/img/filters.svg#fill'); + flex: 0 0 48px; +} diff --git a/webextension/js/content-script.js b/webextension/js/content-script.js index ef6b4c4..0e466f5 100644 --- a/webextension/js/content-script.js +++ b/webextension/js/content-script.js @@ -51,3 +51,146 @@ async function addMessage(message) { browser.runtime.onMessage.addListener((message) => { addMessage(message); }); + + +const menuClickHandler = { + menuElement: null, + lastUrl: null, + linkSelector:"a[href]", + + init() { + this.createMenu(); + + document.addEventListener("keydown", this); + document.addEventListener("click", this); + }, + + handleEvent(e) { + switch(e.type) { + case "keydown": + if (this.isMenuOpen()) { + if (e.key === "Escape") { + this.menuClose(); + } + } + if (e.altKey && (e.shiftKey || e.key === "Shift")) { + e.preventDefault(); + e.stopPropagation(); + this.addOpenListeners(); + } + break; + case "keyup": + if (e.altKey && (e.shiftKey || e.key === "Shift")) { + this.removeOpenListeners(); + } + break; + case "click": + if (this.isMenuOpen() && + (e.target.closest(this.linkSelector) || e.target.matches(this.linkSelector))) { + e.preventDefault(); + e.stopPropagation(); + } else { + this.menuClose(); + } + this.removeOpenListeners(); + break; + case "mousedown": + if (this.isMenuOpen()) { + if (!e.target.closest("#containers-menu")) { + this.menuClose(); + } + return; + } + if (e.target.closest(this.linkSelector) || + e.target.matches(this.linkSelector)) { + // Prevent text selection + e.preventDefault(); + e.stopPropagation(); + this.lastUrl = e.target.href; + this.showMenu(e); + } + /* + setTimeout(() => { + this.removeOpenListeners(); + }, 1000); + */ + break; + } + }, + + addOpenListeners() { + document.addEventListener("mousedown", this); + document.addEventListener("keyup", this); + }, + + removeOpenListeners() { + document.removeEventListener("mousedown", this); + document.removeEventListener("keyup", this); + }, + + isMenuOpen() { + return !this.menuElement.hidden; + }, + + menuOpen() { + this.menuElement.hidden = false; + }, + + menuClose() { + this.menuElement.hidden = true; + }, + + getContainers() { + return browser.runtime.sendMessage({ + method: "getContainers" + }); + }, + + async createMenu() { + if (this.menuElement) { + return this.menuElement; + } + const menuElement = document.createElement("ul"); + menuElement.id = "containers-menu"; + menuElement.hidden = true; + const containers = await this.getContainers(); + containers.forEach((container) => { + const containerElement = document.createElement("li"); + const spanElement = document.createElement("span"); + spanElement.innerText = container.name; + containerElement.appendChild(spanElement); + containerElement.setAttribute("tabindex", 0); + const iconElement = document.createElement("div"); + iconElement.classList.add("usercontext-icon"); + iconElement.setAttribute("data-identity-icon", container.icon); + iconElement.setAttribute("data-identity-color", container.color); + containerElement.prepend(iconElement); + menuElement.appendChild(containerElement); + containerElement.addEventListener("click", (e) => { + this.openContainer(container); + }); + }); + document.body.appendChild(menuElement); + this.menuElement = menuElement; + return menuElement; + }, + + async showMenu(e) { + const menuElement = await this.createMenu(); + this.menuOpen(); + menuElement.style.top = `${e.clientY}px`; + menuElement.style.left = `${e.clientX}px`; + menuElement.querySelector("div").focus(); + }, + + openContainer(container) { + return browser.runtime.sendMessage({ + method: "openTab", + url: this.lastUrl, + cookieStoreId: container.cookieStoreId + }); + } + +}; + +menuClickHandler.init(); diff --git a/webextension/manifest.json b/webextension/manifest.json index 19b99bc..5b84996 100644 --- a/webextension/manifest.json +++ b/webextension/manifest.json @@ -59,11 +59,13 @@ { "matches": [""], "js": ["js/content-script.js"], - "css": ["css/content.css"] + "css": ["css/user-context.css", "css/content.css"] } ], "web_accessible_resources": [ - "/img/container-site-d-24.png" + "/img/container-site-d-24.png", + "/img/usercontext.svg", + "/img/filters.svg" ] } diff --git a/webextension/popup.html b/webextension/popup.html index a524f7e..3f9bca3 100644 --- a/webextension/popup.html +++ b/webextension/popup.html @@ -2,6 +2,7 @@ Containers browserAction Popup +