From e467988a7197c866bc66281085bb6ba3dde83d09 Mon Sep 17 00:00:00 2001 From: Jonathan Kingston Date: Wed, 31 May 2017 15:03:29 +0100 Subject: [PATCH 1/5] Fixing favicon loading for all icons. Part of #561 --- .eslintrc.js | 1 + webextension/confirm-page.html | 1 + webextension/js/confirm-page.js | 9 ++++----- webextension/js/popup.js | 23 +++++------------------ webextension/js/utils.js | 23 +++++++++++++++++++++++ webextension/popup.html | 2 +- 6 files changed, 35 insertions(+), 24 deletions(-) create mode 100644 webextension/js/utils.js diff --git a/.eslintrc.js b/.eslintrc.js index f2a7957..d9f7270 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -9,6 +9,7 @@ module.exports = { "webextensions": true }, "globals": { + "Utils": true, "CustomizableUI": true, "CustomizableWidgets": true, "SessionStore": true, diff --git a/webextension/confirm-page.html b/webextension/confirm-page.html index 09d4852..20d11c8 100644 --- a/webextension/confirm-page.html +++ b/webextension/confirm-page.html @@ -27,6 +27,7 @@ + diff --git a/webextension/js/confirm-page.js b/webextension/js/confirm-page.js index 5c5038b..83a8435 100644 --- a/webextension/js/confirm-page.js +++ b/webextension/js/confirm-page.js @@ -5,7 +5,7 @@ async function load() { const currentCookieStoreId = searchParams.get("currentCookieStoreId"); const redirectUrlElement = document.getElementById("redirect-url"); redirectUrlElement.textContent = redirectUrl; - createFavicon(redirectUrl, redirectUrlElement); + appendFavicon(redirectUrl, redirectUrlElement); const container = await browser.contextualIdentities.get(cookieStoreId); [...document.querySelectorAll(".container-name")].forEach((containerNameElement) => { @@ -32,12 +32,11 @@ async function load() { }); } -function createFavicon(pageUrl, redirectUrlElement) { +function appendFavicon(pageUrl, redirectUrlElement) { const origin = new URL(pageUrl).origin; - const imageElement = document.createElement("img"); - imageElement.src = `${origin}/favicon.ico`; + const favIconElement = Utils.createFavIconElement(`${origin}/favicon.ico`); - redirectUrlElement.prepend(imageElement); + redirectUrlElement.prepend(favIconElement); } function confirmSubmit(redirectUrl, cookieStoreId) { diff --git a/webextension/js/popup.js b/webextension/js/popup.js index 12ca299..7c43e5d 100644 --- a/webextension/js/popup.js +++ b/webextension/js/popup.js @@ -18,7 +18,6 @@ const P_CONTAINERS_EDIT = "containersEdit"; const P_CONTAINER_INFO = "containerInfo"; const P_CONTAINER_EDIT = "containerEdit"; const P_CONTAINER_DELETE = "containerDelete"; -const DEFAULT_FAVICON = "moz-icon://goat?size=16"; /** * Escapes any occurances of &, ", <, > or / with XML entities. @@ -446,22 +445,9 @@ Logic.registerPanel(P_CONTAINERS_LIST, { const siteSettings = await Logic.getAssignment(currentTab); this.setupAssignmentCheckbox(siteSettings); const currentPage = document.getElementById("current-page"); - const favIconUrl = currentTab.favIconUrl || ""; - currentPage.innerHTML = escaped` - ${currentTab.title} - `; - - const imageElement = currentPage.querySelector("img"); - const loadListener = (e) => { - e.target.classList.remove("offpage"); - e.target.removeEventListener("load", loadListener); - e.target.removeEventListener("error", errorListener); - }; - const errorListener = (e) => { - e.target.src = DEFAULT_FAVICON; - }; - imageElement.addEventListener("error", errorListener); - imageElement.addEventListener("load", loadListener); + currentPage.innerHTML = escaped`${currentTab.title}`; + const favIconElement = Utils.createFavIconElement(currentTab.favIconUrl || ""); + currentPage.prepend(favIconElement); const currentContainer = document.getElementById("current-container"); currentContainer.innerText = identity.name; @@ -645,8 +631,9 @@ Logic.registerPanel(P_CONTAINER_INFO, { fragment.appendChild(tr); tr.classList.add("container-info-tab-row"); tr.innerHTML = escaped` - + ${tab.title}`; + tr.querySelector("td").appendChild(Utils.createFavIconElement(tab.favicon)); // On click, we activate this tab. But only if this tab is active. if (tab.active) { diff --git a/webextension/js/utils.js b/webextension/js/utils.js new file mode 100644 index 0000000..5d5046b --- /dev/null +++ b/webextension/js/utils.js @@ -0,0 +1,23 @@ +const DEFAULT_FAVICON = "moz-icon://goat?size=16"; + +// TODO use export here instead of globals +window.Utils = { + + createFavIconElement(url) { + const imageElement = document.createElement("img"); + imageElement.classList.add("icon", "offpage"); + imageElement.src = url; + const loadListener = (e) => { + e.target.classList.remove("offpage"); + e.target.removeEventListener("load", loadListener); + e.target.removeEventListener("error", errorListener); + }; + const errorListener = (e) => { + e.target.src = DEFAULT_FAVICON; + }; + imageElement.addEventListener("error", errorListener); + imageElement.addEventListener("load", loadListener); + return imageElement; + } + +}; diff --git a/webextension/popup.html b/webextension/popup.html index 25a7d0d..a524f7e 100644 --- a/webextension/popup.html +++ b/webextension/popup.html @@ -148,7 +148,7 @@ - + From 9903e811c2a0361eaa6f0fa1e823cd626c0f81cf Mon Sep 17 00:00:00 2001 From: Jonathan Kingston Date: Wed, 31 May 2017 15:36:45 +0100 Subject: [PATCH 2/5] Fix first focus issue on opening browser action. Fixes #564 --- webextension/js/popup.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/webextension/js/popup.js b/webextension/js/popup.js index 7c43e5d..cac57b6 100644 --- a/webextension/js/popup.js +++ b/webextension/js/popup.js @@ -523,8 +523,14 @@ Logic.registerPanel(P_CONTAINERS_LIST, { /* Not sure why extensions require a focus for the doorhanger, however it allows us to have a tabindex before the first selected item */ - document.addEventListener("focus", () => { + const focusHandler = () => { list.querySelector("tr").focus(); + document.removeEventListener("focus", focusHandler); + }; + document.addEventListener("focus", focusHandler); + /* If the user mousedown's first then remove the focus handler */ + document.addEventListener("mousedown", () => { + document.removeEventListener("focus", focusHandler); }); return Promise.resolve(); From 49e8afaf9a62c92dce3f6e9e51c49425d3df6713 Mon Sep 17 00:00:00 2001 From: Jonathan Kingston Date: Wed, 31 May 2017 16:12:07 +0100 Subject: [PATCH 3/5] Fixing truncation for info screen tabs. --- webextension/css/popup.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/webextension/css/popup.css b/webextension/css/popup.css index ad2966e..bd524a1 100644 --- a/webextension/css/popup.css +++ b/webextension/css/popup.css @@ -529,6 +529,10 @@ span ~ .panel-header-text { margin-inline-end: 0.5rem; } +.container-info-tab-title { + flex: 1; +} + #container-info-hideorshow { margin-block-start: 4px; } From 06d35e65ce7f6c59897b5eee8b8c9540d855d3db Mon Sep 17 00:00:00 2001 From: Jonathan Kingston Date: Thu, 1 Jun 2017 03:28:02 +0100 Subject: [PATCH 4/5] Adding in content notification to look more browser like. --- webextension/background.js | 13 +++----- webextension/css/content.css | 22 +++++++++++++ webextension/js/content-script.js | 53 +++++++++++++++++++++++++++++++ webextension/manifest.json | 15 +++++++-- 4 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 webextension/css/content.css create mode 100644 webextension/js/content-script.js diff --git a/webextension/background.js b/webextension/background.js index ca01d89..6448a99 100644 --- a/webextension/background.js +++ b/webextension/background.js @@ -159,7 +159,7 @@ const assignManager = { //storageAction = this.storageArea.remove(info.pageUrl); remove = true; } - await this._setOrRemoveAssignment(info.pageUrl, userContextId, remove); + await this._setOrRemoveAssignment(tab.id, info.pageUrl, userContextId, remove); this.calculateContextMenu(tab); } }, @@ -192,7 +192,7 @@ const assignManager = { return true; }, - async _setOrRemoveAssignment(pageUrl, userContextId, remove) { + async _setOrRemoveAssignment(tabId, pageUrl, userContextId, remove) { let actionName; if (!remove) { await this.storageArea.set(pageUrl, { @@ -205,11 +205,8 @@ const assignManager = { await this.storageArea.remove(pageUrl); actionName = "removed"; } - browser.notifications.create({ - type: "basic", - title: "Containers", - message: `Successfully ${actionName} site to always open in this container`, - iconUrl: browser.extension.getURL("/img/onboarding-1.png") + browser.tabs.sendMessage(tabId, { + text: `Successfully ${actionName} site to always open in this container` }); backgroundLogic.sendTelemetryPayload({ event: `${actionName}-container-assignment`, @@ -432,7 +429,7 @@ const messageHandler = { case "setOrRemoveAssignment": response = browser.tabs.get(m.tabId).then((tab) => { const userContextId = assignManager.getUserContextIdFromCookieStore(tab); - return assignManager._setOrRemoveAssignment(tab.url, userContextId, m.value); + return assignManager._setOrRemoveAssignment(tab.id, tab.url, userContextId, m.value); }); break; case "exemptContainerAssignment": diff --git a/webextension/css/content.css b/webextension/css/content.css new file mode 100644 index 0000000..885b0fb --- /dev/null +++ b/webextension/css/content.css @@ -0,0 +1,22 @@ +.container-notification { + background: #33f70c; + color: #003f07; + display: block; + inline-size: 100vw; + offset-block-start: 0; + offset-inline-start: 0; + padding-block-end: 8px; + padding-block-start: 8px; + padding-inline-end: 8px; + padding-inline-start: 8px; + position: fixed; + transform: translateY(-100%); + transition: transform 0.3s cubic-bezier(0.07, 0.95, 0, 1) 0.3s; + z-index: 999999999999; +} + +.container-notification img { + block-size: 16px; + inline-size: 16px; + margin-inline-end: 3px; +} diff --git a/webextension/js/content-script.js b/webextension/js/content-script.js new file mode 100644 index 0000000..ef6b4c4 --- /dev/null +++ b/webextension/js/content-script.js @@ -0,0 +1,53 @@ +async function delayAnimation(delay = 350) { + return new Promise((resolve) => { + setTimeout(resolve, delay); + }); +} + +async function doAnimation(element, property, value) { + return new Promise((resolve) => { + const handler = () => { + resolve(); + element.removeEventListener("transitionend", handler); + }; + element.addEventListener("transitionend", handler); + window.requestAnimationFrame(() => { + element.style[property] = value; + }); + }); +} +/* +async function awaitEvent(eventName) { + return new Promise((resolve) => { + const handler = () => { + resolve(); + divElement.removeEventListener(eventName, handler); + }; + divElement.addEventListener(eventName, handler); + }); +} +*/ + +async function addMessage(message) { + const divElement = document.createElement("div"); + divElement.classList.add("container-notification"); + // For the eager eyed, this is an experiment. It is however likely that a website will know it is "contained" anyway + divElement.innerText = message.text; + + const imageElement = document.createElement("img"); + imageElement.src = browser.extension.getURL("/img/container-site-d-24.png"); + divElement.prepend(imageElement); + + document.body.appendChild(divElement); + + await delayAnimation(100); + await doAnimation(divElement, "transform", "translateY(0)"); + await delayAnimation(2000); + await doAnimation(divElement, "transform", "translateY(-100%)"); + + divElement.remove(); +} + +browser.runtime.onMessage.addListener((message) => { + addMessage(message); +}); diff --git a/webextension/manifest.json b/webextension/manifest.json index b99a5f3..19b99bc 100644 --- a/webextension/manifest.json +++ b/webextension/manifest.json @@ -26,7 +26,6 @@ "contextualIdentities", "history", "idle", - "notifications", "storage", "tabs", "webRequestBlocking", @@ -54,5 +53,17 @@ "background": { "scripts": ["background.js"] - } + }, + + "content_scripts": [ + { + "matches": [""], + "js": ["js/content-script.js"], + "css": ["css/content.css"] + } + ], + + "web_accessible_resources": [ + "/img/container-site-d-24.png" + ] } From 15477dc384b7f7d03015d61d8d6fde02bbe160a6 Mon Sep 17 00:00:00 2001 From: Jonathan Kingston Date: Fri, 2 Jun 2017 03:23:11 +0100 Subject: [PATCH 5/5] Start fixing styles --- webextension/css/popup.css | 197 ++++++++++++++++++++++++++++--------- webextension/js/popup.js | 8 +- webextension/popup.html | 16 +-- 3 files changed, 161 insertions(+), 60 deletions(-) diff --git a/webextension/css/popup.css b/webextension/css/popup.css index bd524a1..e6c0385 100644 --- a/webextension/css/popup.css +++ b/webextension/css/popup.css @@ -1,16 +1,55 @@ /* General Rules and Resets */ -body { - inline-size: 300px; - max-inline-size: 300px; +* { + font-size: inherit; + margin-block-end: 0; + margin-block-start: 0; + margin-inline-end: 0; + margin-inline-start: 0; + padding-block-end: 0; + padding-block-start: 0; + padding-inline-end: 0; + padding-inline-start: 0; } html { box-sizing: border-box; + font-size: 12px; +} + +body { + font-family: Roboto, Noto, "San Francisco", Ubuntu, "Segoe UI", "Fira Sans", message-box, Arial, sans-serif; + inline-size: 300px; + max-inline-size: 300px; } :root { - --font-size-heading: 16px; --primary-action-color: #248aeb; + --title-text-color: #000; + --text-normal-color: #4a4a4a; + --text-heading-color: #000; + + /* calculated from 12px */ + --font-size-heading: 1.33rem; /* 16px */ + --block-line-space-size: 0.5rem; /* 6px */ + --inline-item-space-size: 0.5rem; /* 6px */ + --block-line-separation-size: 0.33rem; /* 10px */ + --inline-icon-space-size: 0.833rem; /* 10px */ + + /* Use for url and icon size */ + --block-url-label-size: 2rem; /* 24px */ + --inline-start-size: 1.66rem; /* 20px */ + --inline-button-size: 5.833rem; /* 70px */ + --icon-size: 1.166rem; /* 14px */ + + --small-text-size: 0.833rem; /* 10px */ + --small-radius: 3px; + --icon-button-size: calc(calc(var(--block-line-separation-size) * 2) + 1.66rem); /* 20px */ +} + +@media (min-resolution: 1dppx) { + html { + font-size: 14px; + } } *, @@ -35,6 +74,7 @@ table { } .scrollable { + border-block-start: 1px solid #f1f1f1; inline-size: 100%; max-block-size: 400px; overflow: auto; @@ -146,6 +186,10 @@ table { } /* Buttons */ +.button { + color: black; +} + .button.primary { background-color: #0996f8; color: white; @@ -216,7 +260,7 @@ table { .column-panel-content .button, .panel-footer .button { align-items: center; - block-size: 54px; + block-size: 100%; display: flex; flex: 1; justify-content: center; @@ -265,7 +309,7 @@ table { } .onboarding p { - color: #4a4a4a; + color: var(--text-normal-color); font-size: 14px; margin-block-end: 16px; max-inline-size: 84%; @@ -294,9 +338,10 @@ table { manage things like container crud */ .pop-button { align-items: center; - block-size: 48px; + block-size: var(--icon-button-size); + cursor: pointer; display: flex; - flex: 0 0 48px; + flex: 0 0 var(--icon-button-size); justify-content: center; } @@ -321,6 +366,10 @@ manage things like container crud */ .pop-button-image { block-size: 20px; flex: 0 0 20px; + margin-block-end: auto; + margin-block-start: auto; + margin-inline-end: auto; + margin-inline-start: auto; } .pop-button-image-small { @@ -332,18 +381,21 @@ manage things like container crud */ .panel-header { align-items: center; block-size: 48px; - border-block-end: 1px solid #ebebeb; display: flex; justify-content: space-between; } +.panel-header .usercontext-icon { + inline-size: var(--icon-button-size); +} + .column-panel-content .panel-header { flex: 0 0 48px; inline-size: 100%; } .panel-header-text { - color: #4a4a4a; + color: var(--text-normal-color); flex: 1; font-size: var(--font-size-heading); font-weight: normal; @@ -371,8 +423,31 @@ manage things like container crud */ text-transform: uppercase; } +.container-panel-controls { + display: flex; + justify-content: flex-end; + margin-block-end: var(--block-line-space-size); + margin-block-start: var(--block-line-space-size); + margin-inline-end: var(--inline-item-space-size); + margin-inline-start: var(--inline-item-space-size); +} + #container-panel #sort-containers-link { - margin-inline-end: 16px; + align-items: center; + block-size: var(--block-url-label-size); + border: 1px solid #d8d8d8; + border-radius: var(--small-radius); + color: var(--title-text-color); + display: flex; + font-size: var(--small-text-size); + inline-size: var(--inline-button-size); + justify-content: center; + text-decoration: none; +} + +#container-panel #sort-containers-link:hover, +#container-panel #sort-containers-link:focus { + background: #f2f2f2; } span ~ .panel-header-text { @@ -383,41 +458,75 @@ span ~ .panel-header-text { } #current-tab { + align-items: center; + color: var(--text-normal-color); + display: grid; + font-size: var(--small-text-size); + grid-column-gap: var(--inline-item-space-size); + grid-row-gap: var(--block-line-space-size); + grid-template-columns: var(--icon-size) var(--icon-size) 1fr; + margin-block-end: var(--block-line-space-size); + margin-block-start: var(--block-line-separation-size); + margin-inline-end: var(--inline-start-size); + margin-inline-start: var(--inline-start-size); max-inline-size: 100%; - min-block-size: 91px; - padding-block-end: 13px; - padding-block-start: 13px; - padding-inline-end: 16px; - padding-inline-start: 16px; +} + +#current-tab img { + max-block-size: var(--icon-size); } #current-tab > h3 { - color: #4a4a4a; - font-size: var(--font-size-heading); + color: var(--text-heading-color); font-weight: normal; - margin-block-end: 3px; + grid-column: span 3; + margin-block-end: 0; margin-block-start: 0; + margin-inline-end: 0; + margin-inline-start: 0; } -#current-page > img { - block-size: 16px; - inline-size: 16px; +#current-page { + display: contents; +} + +#current-tab .page-title { + font-size: var(--font-size-heading); + grid-column: 2 / 4; } #current-tab > label { - align-items: center; - display: flex; - margin-inline-start: 17px; - white-space: nowrap; + display: contents; + font-size: var(--small-text-size); } #current-tab > label > input { - display: inline; + -moz-appearance: none; + block-size: var(--icon-size); + border: 1px solid #d8d8d8; + border-radius: var(--small-radius); + display: block; + grid-column-start: 2; + inline-size: var(--icon-size); + margin-block-end: 0; + margin-block-start: 0; + margin-inline-end: 0; + margin-inline-start: 0; +} + +#current-tab > label > input[disabled] { + background-color: #efefef; +} + +#current-tab > label > input:checked { + background-image: url("chrome://global/skin/in-content/check.svg#check-native"); + background-position: -1px -1px; + background-size: var(--icon-size); } #current-container { + color: var(--identity-tab-color); flex: 1; - text-transform: lowercase; } #current-tab > label > .usercontext-icon { @@ -434,7 +543,6 @@ span ~ .panel-header-text { .container-panel-row { align-items: center; background-color: #fefefe !important; - block-size: 48px; border-block-end: 1px solid #f1f1f1; box-sizing: border-box; display: flex; @@ -465,8 +573,9 @@ span ~ .panel-header-text { } .userContext-icon-wrapper { - block-size: 48px; - flex: 0 0 48px; + block-size: var(--icon-button-size); + flex: 0 0 var(--icon-button-size); + margin-inline-start: var(--inline-icon-space-size); } /* .userContext-icon is used natively, Bug 1333811 was raised to fix */ @@ -475,24 +584,28 @@ span ~ .panel-header-text { background-position: center center; background-repeat: no-repeat; background-size: 20px 20px; - block-size: 48px; + block-size: 100%; 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'); - fill: 'gray'; + fill: #979797; filter: url('/img/filters.svg#fill'); } +.container-panel-row .clickable:hover .usercontext-icon, +.container-panel-row .clickable:focus .usercontext-icon { + fill: #0094fb; +} + /* Panel Footer */ .panel-footer { align-items: center; background: #efefef; - block-size: 54px; + block-size: var(--icon-button-size); border-block-end: 1px solid #d8d8d8; color: #000; display: flex; @@ -501,14 +614,9 @@ span ~ .panel-header-text { justify-content: space-between; } -.panel-footer .pop-button { - block-size: 54px; - flex: 0 0 54px; -} - .edit-containers-text { align-items: center; - block-size: 54px; + block-size: 100%; border-inline-end: solid 1px #d8d8d8; display: flex; flex: 1; @@ -517,7 +625,7 @@ span ~ .panel-header-text { .edit-containers-text a { align-items: center; - block-size: 54px; + block-size: 100%; color: #0a0a0a; display: flex; flex: 1; @@ -525,10 +633,6 @@ span ~ .panel-header-text { } /* Container info list */ -#container-info-name { - margin-inline-end: 0.5rem; -} - .container-info-tab-title { flex: 1; } @@ -578,7 +682,6 @@ span ~ .panel-header-text { } .container-info-list { - border-block-start: 1px solid #ebebeb; display: flex; flex-direction: column; margin-block-start: 4px; diff --git a/webextension/js/popup.js b/webextension/js/popup.js index cac57b6..c4cf13f 100644 --- a/webextension/js/popup.js +++ b/webextension/js/popup.js @@ -445,16 +445,14 @@ Logic.registerPanel(P_CONTAINERS_LIST, { const siteSettings = await Logic.getAssignment(currentTab); this.setupAssignmentCheckbox(siteSettings); const currentPage = document.getElementById("current-page"); - currentPage.innerHTML = escaped`${currentTab.title}`; + currentPage.innerHTML = escaped`${currentTab.title}`; const favIconElement = Utils.createFavIconElement(currentTab.favIconUrl || ""); currentPage.prepend(favIconElement); const currentContainer = document.getElementById("current-container"); currentContainer.innerText = identity.name; - const currentContainerIcon = document.getElementById("current-container-icon"); - currentContainerIcon.setAttribute("data-identity-icon", identity.icon); - currentContainerIcon.setAttribute("data-identity-color", identity.color); + currentContainer.setAttribute("data-identity-color", identity.color); } }, @@ -516,7 +514,7 @@ Logic.registerPanel(P_CONTAINERS_LIST, { }); }); - const list = document.querySelector(".identities-list"); + const list = document.querySelector(".identities-list tbody"); list.innerHTML = ""; list.appendChild(fragment); diff --git a/webextension/popup.html b/webextension/popup.html index a524f7e..9dfdccb 100644 --- a/webextension/popup.html +++ b/webextension/popup.html @@ -40,21 +40,21 @@

Current Tab

-
+
-
-

Containers

+
- - +
+