diff --git a/docs/metrics.md b/docs/metrics.md index 648af7f..135ed97 100644 --- a/docs/metrics.md +++ b/docs/metrics.md @@ -189,6 +189,17 @@ of a `testpilottest` telemetry ping for each scenario. } ``` +* The user goes idle + +```js + { + "uuid": , + "userContextId": , + "event": "page-requests-completed-per-activity", + "pageRequestCount": + } +``` + * The user chooses "Always Open in this Container" context menu option. (Note: We send two separate event names: one for assigning a site to a container, one for removing a site from a container.) ```js diff --git a/package.json b/package.json index ab0f5bc..8fa61ea 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "testpilot-containers", "title": "Containers Experiment", "description": "Containers works by isolating cookie jars using separate origin-attributes defined visually by colored ‘Container Tabs’. This add-on is a modified version of the containers feature for Firefox Test Pilot.", - "version": "2.1.2", + "version": "2.2.0", "author": "Andrea Marchesini, Luke Crouch and Jonathan Kingston", "bugs": { "url": "https://github.com/mozilla/testpilot-containers/issues" diff --git a/webextension/background.js b/webextension/background.js index 9631949..13782e3 100644 --- a/webextension/background.js +++ b/webextension/background.js @@ -127,8 +127,21 @@ const assignManager = { this.reloadPageInContainer(options.url, siteSettings.userContextId, tab.index + 1, siteSettings.neverAsk); this.calculateContextMenu(tab); - // If the user just opened the tab, we can auto close it - if (this.CLOSEABLE_WINDOWS.has(tab.url)) { + + /* Removal of existing tabs: + We aim to open the new assigned container tab / warning prompt in it's own tab: + - As the history won't span from one container to another it seems most sane to not try and reopen a tab on history.back() + - When users open a new tab themselves we want to make sure we don't end up with three tabs as per: https://github.com/mozilla/testpilot-containers/issues/421 + If we are coming from an internal url that are used for the new tab page (CLOSEABLE_WINDOWS), we can safely close as user is unlikely losing history + Detecting redirects on "new tab" opening actions is pretty hard as we don't get tab history: + - Redirects happen from Short URLs and tracking links that act as a gateway + - Extensions don't provide a way to history crawl for tabs, we could inject content scripts to do this + however they don't run on about:blank so this would likely be just as hacky. + We capture the time the tab was created and close if it was within the timeout to try to capture pages which haven't had user interaction or history. + */ + if (this.CLOSEABLE_WINDOWS.has(tab.url) + || (messageHandler.lastCreatedTab + && messageHandler.lastCreatedTab.id === tab.id)) { browser.tabs.remove(tab.id); } return { @@ -228,6 +241,11 @@ const assignManager = { }; const messageHandler = { + // After the timer completes we assume it's a tab the user meant to keep open + // We use this to catch redirected tabs that have just opened + // If this were in platform we would change how the tab opens based on "new tab" link navigations such as ctrl+click + LAST_CREATED_TAB_TIMER: 2000, + init() { // Handles messages from index.js const port = browser.runtime.connect(); @@ -280,6 +298,20 @@ const messageHandler = { }); }); + browser.idle.onStateChanged.addListener((newState) => { + browser.tabs.query({}).then(tabs => { + for (let tab of tabs) { // eslint-disable-line prefer-const + if (newState === "idle") { + tabPageCounter.sendTabCountAndDelete(tab.id, "user-went-idle"); + } else if (newState === "active" && tab.active) { + tabPageCounter.initTabCounter(tab); + } + } + }).catch(e => { + throw e; + }); + }); + browser.webRequest.onCompleted.addListener((details) => { if (details.frameId !== 0 || details.tabId === -1) { return {}; @@ -292,6 +324,15 @@ const messageHandler = { throw e; }); }, {urls: [""], types: ["main_frame"]}); + + // lets remember the last tab created so we can close it if it looks like a redirect + browser.tabs.onCreated.addListener((details) => { + this.lastCreatedTab = details; + setTimeout(() => { + this.lastCreatedTab = null; + }, this.LAST_CREATED_TAB_TIMER); + }); + } }; @@ -333,30 +374,68 @@ const themeManager = { }; const tabPageCounter = { - counter: {}, + counters: {}, initTabCounter(tab) { - if (tab.id in this.counter) { - return; + if (tab.id in this.counters) { + if (!("activity" in this.counters[tab.id])) { + this.counters[tab.id].activity = { + "cookieStoreId": tab.cookieStoreId, + "pageRequests": 0 + }; + } + if (!("tab" in this.counters[tab.id])) { + this.counters[tab.id].tab = { + "cookieStoreId": tab.cookieStoreId, + "pageRequests": 0 + }; + } + } else { + this.counters[tab.id] = {}; + this.counters[tab.id].tab = { + "cookieStoreId": tab.cookieStoreId, + "pageRequests": 0 + }; + this.counters[tab.id].activity = { + "cookieStoreId": tab.cookieStoreId, + "pageRequests": 0 + }; } - this.counter[tab.id] = { - "cookieStoreId": tab.cookieStoreId, - "pageRequests": 0 - }; }, - sendTabCountAndDelete(tabId) { - browser.runtime.sendMessage({ - method: "sendTelemetryPayload", - event: "page-requests-completed-per-tab", - userContextId: this.counter[tabId].cookieStoreId, - pageRequestCount: this.counter[tabId].pageRequests - }); - delete this.counter[tabId]; + sendTabCountAndDelete(tabId, why = "user-closed-tab") { + if (!(this.counters[tabId])) { + return; + } + if (why === "user-closed-tab" && this.counters[tabId].tab) { + browser.runtime.sendMessage({ + method: "sendTelemetryPayload", + event: "page-requests-completed-per-tab", + userContextId: this.counters[tabId].tab.cookieStoreId, + pageRequestCount: this.counters[tabId].tab.pageRequests + }); + // When we send the ping because the user closed the tab, + // delete both the 'tab' and 'activity' counters + delete this.counters[tabId]; + } else if (why === "user-went-idle" && this.counters[tabId].activity) { + browser.runtime.sendMessage({ + method: "sendTelemetryPayload", + event: "page-requests-completed-per-activity", + userContextId: this.counters[tabId].activity.cookieStoreId, + pageRequestCount: this.counters[tabId].activity.pageRequests + }); + // When we send the ping because the user went idle, + // only reset the 'activity' counter + this.counters[tabId].activity = { + "cookieStoreId": this.counters[tabId].tab.cookieStoreId, + "pageRequests": 0 + }; + } }, incrementTabCount(tab) { - this.counter[tab.id].pageRequests++; + this.counters[tab.id].tab.pageRequests++; + this.counters[tab.id].activity.pageRequests++; } }; diff --git a/webextension/confirm-page.html b/webextension/confirm-page.html index ca182bb..48e12f9 100644 --- a/webextension/confirm-page.html +++ b/webextension/confirm-page.html @@ -23,7 +23,7 @@
- +
diff --git a/webextension/css/confirm-page.css b/webextension/css/confirm-page.css index efbeb41..a595e4c 100644 --- a/webextension/css/confirm-page.css +++ b/webextension/css/confirm-page.css @@ -30,6 +30,10 @@ html { #redirect-url, #redirect-origin { font-weight: bold; + + /* max-inline-size is needed to force this text smaller than the layout at a mid-sized window */ + max-inline-size: 40rem; + word-break: break-all; } dfn { diff --git a/webextension/manifest.json b/webextension/manifest.json index 4e30c9d..d8e094a 100644 --- a/webextension/manifest.json +++ b/webextension/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Containers Experiment", - "version": "2.1.2", + "version": "2.2.0", "description": "Containers works by isolating cookie jars using separate origin-attributes defined visually by colored ‘Container Tabs’. This add-on is a modified version of the containers feature for Firefox Test Pilot.", "icons": { @@ -24,6 +24,7 @@ "cookies", "contextMenus", "history", + "idle", "notifications", "storage", "tabs",