Move to a Web Extension only. Fixes #1005

This commit is contained in:
Jonathan Kingston
2017-11-28 16:22:54 +00:00
parent 4a1597c87f
commit d944116e3e
52 changed files with 13 additions and 301 deletions
+36
View File
@@ -0,0 +1,36 @@
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Multi-Account Containers Confirm Navigation</title>
<link xmlns="http://www.w3.org/1999/xhtml" rel="stylesheet" href="chrome://browser/skin/aboutNetError.css" type="text/css" media="all" />
<link rel="stylesheet" href="/css/confirm-page.css" />
</head>
<body>
<main>
<div class="title">
<h1 class="title-text">Open this site in your assigned container?</h1>
</div>
<form id="redirect-form">
<p>
You asked <dfn id="browser-name" title="Thanks for trying out Containers. Sorry we may have got your browser name wrong. #FxNightly" >Firefox</dfn> to always open <dfn class="container-name"></dfn> for this site:<br />
</p>
<div id="redirect-url"></div>
<p>Would you still like to open in this current container?</p>
<br />
<br />
<label for="never-ask" class="check-label">
<input id="never-ask" type="checkbox" />
Remember my decision for this site
</label>
<br />
<div class="button-container">
<button id="deny" class="button">Open in <dfn id="current-container-name">Current</dfn> Container</button>
<button id="confirm" class="button primary" autofocus>Open in <dfn class="container-name"></dfn> Container</button>
</div>
</form>
</main>
<script src="js/utils.js"></script>
<script src="js/confirm-page.js"></script>
</body>
</html>
+78
View File
@@ -0,0 +1,78 @@
/* General Rules and Resets */
.title {
background-image: none;
}
main {
background: url(/img/onboarding-4.png) no-repeat;
background-position: -10px -15px;
background-size: 300px;
margin-inline-start: -350px;
padding-inline-start: 350px;
}
.container-name {
font-weight: bold;
}
button .container-name,
#current-container-name {
font-weight: bold;
text-transform: capitalize;
}
@media only screen and (max-width: 1300px) {
main {
background: none;
}
/* for a mid sized window we have enough for this but not our image */
.title {
background-image: url("chrome://global/skin/icons/info.svg");
}
}
html {
box-sizing: border-box;
font: message-box;
}
#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;
}
#redirect-url {
background: #efefef;
border-radius: 2px;
line-height: 1.5;
padding-block-end: 0.5rem;
padding-block-start: 0.5rem;
padding-inline-end: 0.5rem;
padding-inline-start: 0.5rem;
}
#redirect-url img {
block-size: 16px;
inline-size: 16px;
margin-inline-end: 6px;
offset-block-start: 3px;
position: relative;
}
dfn {
font-style: normal;
}
.button-container > button {
min-inline-size: 240px;
}
.check-label {
align-items: center;
display: flex;
}
+27
View File
@@ -0,0 +1,27 @@
.container-notification {
align-items: center;
background: #efefef;
color: #003f07;
display: flex;
font: 12px sans-serif;
inline-size: 100vw;
justify-content: start;
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;
text-align: start;
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;
display: inline-block;
inline-size: 16px;
margin-inline-end: 3px;
}
+940
View File
@@ -0,0 +1,940 @@
/* General Rules and Resets */
* {
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 {
background-color: #fefefe;
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 {
--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;
}
}
*,
*::before,
*::after {
box-sizing: inherit;
}
form {
margin-block-end: 0;
margin-block-start: 0;
margin-inline-end: 0;
margin-inline-start: 0;
}
table {
border: 0;
border-spacing: 0;
inline-size: 100%;
margin-block-end: 0;
margin-block-start: 0;
margin-inline-end: 0;
margin-inline-start: 0;
}
/* Helper Classes */
.hide {
display: none !important;
}
.scrollable {
border-block-start: 1px solid #f1f1f1;
inline-size: 100%;
max-block-size: 400px;
overflow: auto;
}
.offpage {
opacity: 0;
}
[hidden] {
display: none !important;
}
/* Effect borrowed from tabs in Firefox, ensure that the element flexes to the full width */
.truncate-text {
mask-image: linear-gradient(to left, transparent, black 1em);
overflow: hidden;
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="grey"] {
/* Only used for the edit panel */
--identity-icon-color: #616161;
}
[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;
}
/* Buttons */
.button {
color: black;
}
.button.primary {
background-color: #0996f8;
color: white;
}
.button.primary:hover,
.button.primary:focus {
background-color: #0675d3;
}
.button.secondary:hover,
.button.secondary:focus {
background-color: rgba(0, 0, 0, 0.05);
}
/* Text links with actions */
.action-link:link {
color: var(--primary-action-color);
text-decoration: none;
}
.action-link:active,
.action-link:hover {
text-decoration: underline;
}
/* Panels keep everything togethert */
.panel {
display: flex;
flex-direction: column;
justify-content: space-between;
min-block-size: 400px;
}
.panel.onboarding,
.achievement-panel {
align-items: center;
block-size: 360px;
margin-block-end: 16px;
margin-block-start: 16px;
margin-inline-end: 16px;
margin-inline-start: 16px;
min-block-size: 360px;
}
.panel .columns {
display: flex;
flex: 1;
}
.panel-content {
flex: 1;
}
/* Column panels for edit screens */
.column-panel-content {
display: flex;
flex-direction: column;
inline-size: 268px;
}
.column-panel-content .panel-footer {
align-items: center;
display: flex;
justify-content: center;
}
.column-panel-content .button,
.panel-footer .button {
align-items: center;
block-size: 100%;
display: flex;
flex: 1;
justify-content: center;
}
/* Column panels have a special back arrow */
.panel-back-arrow {
align-items: center;
background: #ebebeb;
box-shadow: inset -2px 0 4px -2px rgba(0, 0, 0, 0.4);
display: flex;
flex: 0 0 32px;
flex-direction: column;
justify-content: center;
}
.panel-back-arrow:hover,
.panel-back-arrow:focus {
background: #dedede;
}
.back-arrow-img {
block-size: 16px;
inline-size: 16px;
transform: rotate(180deg);
}
/* Onboarding styles */
.onboarding * {
text-align: center;
}
.onboarding-img {
block-size: 132px;
inline-size: 180px;
}
.onboarding-title {
color: #43484e;
font-size: var(--font-size-heading);
margin-block-end: 0;
margin-block-start: 0;
margin-inline-end: 0;
margin-inline-start: 0;
max-inline-size: 80%;
}
.onboarding p {
color: var(--text-normal-color);
font-size: 14px;
margin-block-end: 16px;
max-inline-size: 84%;
}
.onboarding-button {
align-items: center;
background-color: #0996f8;
border-radius: 3px;
color: white;
display: flex;
flex: 0 0 44px;
font-size: 14px;
inline-size: 100%;
justify-content: center;
text-decoration: none;
transition: background-color 75ms;
}
.onboarding-button:hover,
.onboarding-button:active {
background-color: #0675d3;
}
/* Pop buttons are the square shaped buttons used to
manage things like container crud */
.pop-button {
align-items: center;
block-size: var(--icon-button-size);
cursor: pointer;
display: flex;
flex: 0 0 var(--icon-button-size);
justify-content: center;
}
.pop-button:hover,
.pop-button:focus,
.panel-footer-secondary:focus,
.panel-footer-secondary:hover {
background-color: rgba(0, 0, 0, 0.05);
}
.pop-button:focus,
.panel-footer-secondary:focus {
background-color: rgba(0, 0, 0, 0.08);
}
.pop-button a,
.panel-footer a,
.panel-footer-secondary a {
text-decoration: none;
}
.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 {
block-size: 12px;
flex: 0 0 12px;
}
/* Panel Header */
.panel-header {
align-items: center;
block-size: 48px;
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: var(--text-normal-color);
flex: 1;
font-size: var(--font-size-heading);
font-weight: normal;
margin-block-end: 0;
margin-block-start: 0;
margin-inline-end: 0;
margin-inline-start: 0;
padding-block-end: 16px;
padding-block-start: 16px;
padding-inline-end: 16px;
padding-inline-start: 16px;
}
#container-panel .panel-header {
background-color: #efefef;
block-size: 26px;
font-size: 14px;
}
#container-panel .panel-header-text {
color: #727272;
font-size: 14px;
padding-block-end: 0;
padding-block-start: 0;
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 {
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 {
padding-block-end: 0;
padding-block-start: 0;
padding-inline-end: 0;
padding-inline-start: 0;
}
#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%;
}
#current-tab img {
max-block-size: var(--icon-size);
}
#current-tab > h3 {
color: var(--text-heading-color);
font-weight: normal;
grid-column: span 3;
margin-block-end: 0;
margin-block-start: 0;
margin-inline-end: 0;
margin-inline-start: 0;
}
#current-page {
display: contents;
}
#current-tab .page-title {
font-size: var(--font-size-heading);
grid-column: 2 / 4;
}
#current-tab > label {
display: contents;
font-size: var(--small-text-size);
}
#current-tab > label > input {
-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;
}
#current-tab > label > .usercontext-icon {
background-size: 16px;
block-size: 16px;
display: block;
flex: 0 0 20px;
inline-size: 20px;
margin-inline-end: 3px;
margin-inline-start: 3px;
}
/* Rows used when iterating over panels */
.container-panel-row {
align-items: center;
background-color: #fefefe !important;
border-block-end: 1px solid #f1f1f1;
box-sizing: border-box;
display: flex;
justify-content: space-between;
}
.container-panel-row .container-name {
flex: 1;
max-inline-size: 160px;
padding-inline-end: 4px;
padding-inline-start: 4px;
}
.edit-containers-panel .userContext-wrapper {
max-inline-size: 204px;
}
.userContext-wrapper {
align-items: center;
display: flex;
flex: 1 1;
transition: background-color 75ms;
}
.container-panel-row:hover .clickable.userContext-wrapper,
.container-panel-row:focus .clickable.userContext-wrapper {
background: #f2f2f2;
}
.userContext-icon-wrapper {
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 */
.usercontext-icon {
background-image: var(--identity-icon);
background-position: center center;
background-repeat: no-repeat;
background-size: 20px 20px;
block-size: 100%;
fill: var(--identity-icon-color);
filter: url('/img/filters.svg#fill');
}
.container-panel-row:hover .clickable .usercontext-icon,
.container-panel-row:focus .clickable .usercontext-icon,
.container-panel-row .clickable:focus .usercontext-icon {
background-image: url('/img/container-newtab.svg');
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: var(--icon-button-size);
border-block-end: 1px solid #d8d8d8;
color: #000;
display: flex;
font-size: 13px;
inline-size: 100%;
justify-content: space-between;
}
.edit-containers-text {
align-items: center;
block-size: 100%;
border-inline-end: solid 1px #d8d8d8;
display: flex;
flex: 1;
justify-content: center;
}
.edit-containers-text a {
align-items: center;
block-size: 100%;
color: #0a0a0a;
display: flex;
flex: 1;
justify-content: center;
}
/* Container info list */
.container-info-tab-title {
flex: 1;
}
#container-info-hideorshow {
margin-block-start: 4px;
}
#container-info-movetabs-incompat {
font-size: 10px;
opacity: 0.3;
}
.container-info-tab-row:not(.clickable),
.select-row:not(.clickable) {
opacity: 0.3;
}
.container-info-has-tabs,
.container-info-tab-row {
align-items: center;
display: flex;
flex: 0 0 28px;
font-size: 14px;
justify-content: flex-start;
margin-block-end: 0;
margin-block-start: 0;
margin-inline-end: 0;
margin-inline-start: 0;
padding-inline-end: 16px;
padding-inline-start: 16px;
}
.container-info-has-tabs img,
.container-info-tab-row img {
block-size: 16px;
flex: 0 0 16px;
margin-inline-end: 4px;
}
.container-info-tab-row img[src=""] {
margin-inline-end: 0;
}
.container-info-tab-row td {
max-inline-size: 200px;
}
.container-info-list {
display: flex;
flex-direction: column;
margin-block-start: 4px;
padding-block-start: 4px;
}
.container-info-list tbody {
display: contents;
}
.clickable {
cursor: pointer;
}
.clickable:hover,
.clickable:focus {
background-color: #ebebeb;
}
.edit-containers-exit-text {
align-items: center;
background: var(--primary-action-color);
block-size: 100%;
color: #fff;
display: flex;
flex: 1;
justify-content: center;
}
.exit-edit-mode-link::before {
background: url('/img/container-arrow.svg') no-repeat;
block-size: 16px;
content: "";
display: block;
filter: grayscale(100%) brightness(5);
float: left;
inline-size: 16px;
margin-inline-end: 5px;
transform: scaleX(-1);
}
.delete-container-confirm {
padding-inline-end: 20px;
padding-inline-start: 20px;
}
.delete-container-confirm-title {
color: #000;
font-size: var(--font-size-heading);
}
/* Form info */
.column-panel-content form {
flex: 1;
padding-block-end: 16px;
padding-block-start: 16px;
padding-inline-end: 16px;
padding-inline-start: 16px;
}
#edit-sites-assigned {
flex: 1;
}
#edit-sites-assigned h3 {
font-size: 14px;
font-weight: normal;
padding-block-end: 6px;
padding-block-start: 6px;
padding-inline-end: 16px;
padding-inline-start: 16px;
}
.assigned-sites-list > div {
display: flex;
padding-block-end: 6px;
padding-block-start: 6px;
}
.assigned-sites-list > div > .icon {
margin-inline-end: 10px;
}
.assigned-sites-list > div > .delete-assignment {
display: none;
}
.assigned-sites-list > div:hover > .delete-assignment {
display: block;
}
.assigned-sites-list > div > .hostname {
flex: 1;
}
.radio-choice > .radio-container {
align-items: center;
block-size: 29px;
display: flex;
flex: 0 0 calc(100% / 8);
}
.radio-choice > .radio-container > label {
background: none;
block-size: 23px;
border: 0;
filter: none;
inline-size: 23px;
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;
}
.radio-choice > .radio-container > label::before {
background-color: unset;
background-image: var(--identity-icon);
background-position: center;
background-repeat: no-repeat;
background-size: 16px;
block-size: 23px;
border: none;
content: "";
display: block;
fill: var(--identity-icon-color);
filter: url('/img/filters.svg#fill');
inline-size: 23px;
position: relative;
}
.radio-choice > .radio-container > [type="radio"] {
-moz-appearance: none;
display: inline;
opacity: 0;
}
.radio-choice > .radio-container > [type="radio"]:checked + label {
background: #d3d3d3;
border-radius: 100%;
}
/* When focusing the element add a thin blue highlight to match input fields. This gives a distinction to other selected radio items */
.radio-choice > .radio-container > [type="radio"]:focus + label {
outline: 1px solid #1f9ffc;
-moz-outline-radius: 100%;
}
.edit-container-panel fieldset {
background: none;
border: none;
display: flex;
flex-direction: row;
flex-wrap: wrap;
inline-size: 100%;
margin-block-end: 10px;
margin-inline-end: 0;
margin-inline-start: 0;
padding-block-end: 0;
padding-block-start: 0;
padding-inline-end: 0;
padding-inline-start: 0;
}
.edit-container-panel fieldset:last-of-type {
margin-block-end: 0;
}
.edit-container-panel input[type="text"] {
block-size: 36px;
border-radius: 3px;
font-size: 14px;
inline-size: 100%;
padding-block-end: 5px;
padding-block-start: 5px;
padding-inline-end: 5px;
padding-inline-start: 5px;
}
.edit-container-panel legend {
flex: 1 0;
font-size: 14px !important;
padding-block-end: 6px;
}
/* Achievement panel elements */
.share-ctas {
padding-block-end: 0.5em;
padding-block-start: 0.5em;
padding-inline-end: 0.5em;
padding-inline-start: 0.5em;
text-align: center;
}
.cta-link {
text-decoration: none;
}
.cta {
color: #fff;
font-size: 0.7em;
font-weight: bold;
margin-block-end: 0.4em;
margin-block-start: 0.4em;
margin-inline-end: 0.4em;
margin-inline-start: 0.4em;
padding-block-end: 0.5em;
padding-block-start: 0.5em;
padding-inline-end: 0.5em;
padding-inline-start: 0.5em;
text-transform: uppercase;
}
.cta-icon {
height: 18px;
padding-right: 0.5em;
vertical-align: middle;
}
.fb-share-cta {
background: #375496;
}
.fb-share-cta .cta-icon {
margin-block-start: -5px;
}
.tweet-cta {
background: #37bae7;
}
.amo-rate-cta {
background: #0f1126;
}
+1
View File
@@ -0,0 +1 @@
<svg width="32px" height="33px" viewBox="0 0 32 33" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch --> <desc>Created with Sketch.</desc> <defs> <linearGradient x1="74.0423237%" y1="18.5882821%" x2="0%" y2="100%" id="linearGradient-1"> <stop stop-color="#00FEFF" offset="0%"/> <stop stop-color="#3D85FF" offset="100%"/> </linearGradient> </defs> <g id="Specs" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="Header-Copy" transform="translate(-182.000000, -152.000000)" fill="url(#linearGradient-1)"> <path d="M205.58574,176.859518 L205.58574,169.287998 C205.58574,169.287998 205.800116,167.315137 207.086372,167.315137 C208.372629,167.315137 208.265441,169.394639 210.677171,169.394639 C211.909834,169.394639 214,168.754792 214,165.022352 C214,161.289912 211.909834,160.810027 210.677171,160.810027 C208.265441,160.810027 208.372629,162.782888 207.086372,162.782888 C205.800116,162.782888 205.58574,160.756707 205.58574,160.756707 L205.58574,157.664114 C205.58574,156.491061 204.621048,155.531291 203.44198,155.531291 L197.814608,155.531291 C197.814608,155.531291 195.992412,155.211368 195.992412,153.931674 C195.992412,152.65198 198.028985,152.545339 198.028985,150.145914 C198.028985,148.91954 197.332262,147 193.580682,147 C189.829101,147 189.293161,148.91954 189.293161,150.145914 C189.293161,152.545339 191.115357,152.65198 191.115357,153.931674 C191.115357,155.211368 189.293161,155.531291 189.293161,155.531291 L184.148135,155.531291 C182.969067,155.531291 182.004375,156.491061 182.004375,157.664114 L182.004375,161.823118 C182.004375,161.823118 181.789999,165.022352 184.362512,165.022352 C186.023926,165.022352 186.07752,162.836209 188.274874,162.836209 C189.346755,162.836209 190.418635,163.8493 190.418635,166.035443 C190.418635,168.274907 189.346755,169.394639 188.274874,169.394639 C186.131114,169.394639 186.023926,167.208496 184.362512,167.208496 C181.789999,167.208496 182.004375,170.301089 182.004375,170.301089 L182.004375,176.859518 C182.004375,178.032571 182.969067,178.992341 184.148135,178.992341 L191.115357,178.992341 C191.115357,178.992341 194.49178,179.205623 194.49178,176.646236 C194.49178,174.993299 192.348019,174.726696 192.348019,172.540552 C192.348019,171.474141 193.527088,170.141127 195.778036,170.141127 C198.028985,170.141127 199.315241,171.474141 199.315241,172.540552 C199.315241,174.673375 197.225074,174.993299 197.225074,176.646236 C197.225074,179.258944 200.601497,178.992341 200.601497,178.992341 L203.44198,178.992341 C204.621048,178.992341 205.58574,178.032571 205.58574,176.859518 Z" id="Shape-Copy-23" transform="translate(198.000000, 163.000000) rotate(-42.000000) translate(-198.000000, -163.000000) "/> </g> </g> </svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
<path d="M17,12v2a1,1,0,0,1-1,1H2a1,1,0,0,1-1-1V12a1,1,0,0,1,1-1H1.142c2.3,0,2.536-1.773,2.874-4,0.351-2.316.083-4,3.13-4h3.707C13.917,3,13.647,4.684,14,7c0.34,2.228.582,4,2.89,4H16A1,1,0,0,1,17,12Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 307 B

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 11 11" style="enable-background:new 0 0 11 11;" xml:space="preserve">
<style type="text/css">
.st0{fill:#858585;}
</style>
<title>firefox</title>
<g id="General-icons">
<polygon class="st0" points="10.8,4.4 6.4,4.4 6.4,0.2 4.6,0.2 4.6,4.4 0.2,4.4 0.2,6.4 4.6,6.4 4.6,10.8 6.4,10.8 6.4,6.4
10.8,6.4 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 595 B

+9
View File
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 6 8" style="enable-background:new 0 0 6 8;" xml:space="preserve">
<style type="text/css">
.st0{fill:#4C4C4C;}
</style>
<polygon id="Arrow---Disclosure---Collapsed-Copy" class="st0" points="5.5,4 1.5,7.7 0.5,6.8 3.5,4 0.5,1.2 1.5,0.3 "/>
</svg>

After

Width:  |  Height:  |  Size: 520 B

+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 12 12" style="enable-background:new 0 0 12 12;" xml:space="preserve">
<style type="text/css">
.st0{fill:#858585;}
</style>
<path class="st0" d="M4.6,0.3h2.7c0.1,0,0.2,0.1,0.2,0.2v1H4.4v-1C4.4,0.4,4.5,0.3,4.6,0.3z M1.7,1.5h8.6c0.1,0,0.2,0.1,0.2,0.2
l0.2,1.4H1.3l0.2-1.4C1.5,1.6,1.6,1.5,1.7,1.5z M6,11.7H3.2L2.1,3.9H6h3.9l-1.1,7.8H6L6,11.7z"/>
</svg>

After

Width:  |  Height:  |  Size: 626 B

+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 12 12" style="enable-background:new 0 0 12 12;" xml:space="preserve">
<style type="text/css">
.st0{fill:#858585;}
</style>
<g>
<path class="st0" d="M11.4,2.6L9.6,0.9c-0.1-0.1-0.2-0.1-0.3,0l-7,7l2,2l7-7C11.4,2.8,11.4,2.7,11.4,2.6z"/>
<path class="st0" d="M0.8,10.9c-0.1,0.3,0,0.4,0.4,0.4l2.3-0.6l-2-2L0.8,10.9z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 603 B

+19
View File
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 12 12" style="enable-background:new 0 0 12 12;" xml:space="preserve">
<style type="text/css">
.st0{fill:#858585;}
</style>
<polygon class="st0" points="10.3,0.5 8.9,2 2,8.9 0.5,10.3 1.3,11.1 11.1,1.3 "/>
<g>
<path class="st0" d="M4.8,7.5l2.8-2.8C7.2,4.3,6.7,4,6.1,4c-1.1,0-2,0.9-2,2C4.1,6.6,4.4,7.1,4.8,7.5z"/>
<path class="st0" d="M5.9,7.9c0,0,0.1,0,0.2,0c1.1,0,2-0.9,2-2c0,0,0-0.1,0-0.2L5.9,7.9z"/>
</g>
<g>
<path class="st0" d="M4.1,8.2C2.6,7.4,1.3,6.1,1.2,6C1.3,5.8,3.8,3.1,6,3.1c0.8,0,1.6,0.4,2.4,0.8L9,3.3C8.1,2.7,7,2.2,6,2.2
c-2.6,0-5.4,2.9-5.5,3.1C0.3,5.6,0.2,5.7,0.2,6v0c0,0.2,0.1,0.4,0.2,0.6c0.1,0.1,1.3,1.4,2.9,2.3L4.1,8.2z"/>
<path class="st0" d="M9.9,3.9L9.2,4.5c0.9,0.7,1.5,1.3,1.6,1.4C10.6,6.2,8.1,8.8,6,8.8c-0.3,0-0.6,0-0.9-0.1L4.4,9.4
C4.9,9.6,5.5,9.7,6,9.7c2.6,0,5.4-2.9,5.5-3.1c0.2-0.2,0.2-0.4,0.2-0.6v0c0-0.2,0-0.4-0.2-0.6C11.4,5.3,10.8,4.6,9.9,3.9z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+19
View File
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 20 20" style="enable-background:new 0 0 20 20;" xml:space="preserve">
<style type="text/css">
.st0{fill:#979797;}
</style>
<title>icon-newtab</title>
<g id="Visual-Spec">
<g id="Normal-Panel-Hover" transform="translate(-80.000000, -246.000000)">
<g id="Panel" transform="translate(58.000000, 170.000000)">
<g id="icon-newtab" transform="translate(21.000000, 75.000000)">
<path id="Fill-1" class="st0" d="M15.3,11.7h-3.6v3.7h-1.4v-3.7H6.7v-1.6h3.6V6.6h1.4v3.5h3.6C15.3,10.1,15.3,11.7,15.3,11.7z
M11,3c-4.4,0-8,3.6-8,8s3.6,8,8,8s8-3.6,8-8S15.4,3,11,3L11,3z"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 883 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="context-fill #4c4c4c" fill-opacity="context-fill-opacity" d="M12.9137931,3.0862069 L12.9137931,1.27586207 C12.9137931,0.84736528 12.5664278,0.5 12.137931,0.5 C11.7094342,0.5 11.362069,0.84736528 11.362069,1.27586207 L11.362069,1.27586207 L11.362069,3.0862069 L9.55172414,3.0862069 C9.12322735,3.0862069 8.77586207,3.43357218 8.77586207,3.86206897 C8.77586207,4.29056575 9.12322735,4.63793103 9.55172414,4.63793103 L11.362069,4.63793103 L11.362069,6.44827586 C11.362069,6.87677265 11.7094342,7.22413793 12.137931,7.22413793 L12.137931,7.22413793 C12.5664278,7.22413793 12.9137931,6.87677265 12.9137931,6.44827586 L12.9137931,6.44827586 L12.9137931,4.63793103 L14.7241379,4.63793103 C15.1526347,4.63793103 15.5,4.29056575 15.5,3.86206897 L15.5,3.86206897 C15.5,3.43357218 15.1526347,3.0862069 14.7241379,3.0862069 L14.7241379,3.0862069 L12.9137931,3.0862069 Z M0.5,9.76803178 C0.5,9.22007158 0.94118947,8.77586207 1.49216971,8.77586207 L6.23196822,8.77586207 C6.77992842,8.77586207 7.22413793,9.21705154 7.22413793,9.76803178 L7.22413793,14.5078303 C7.22413793,15.0557905 6.78294846,15.5 6.23196822,15.5 L1.49216971,15.5 C0.94420951,15.5 0.5,15.0588105 0.5,14.5078303 L0.5,9.76803178 Z M8.77586207,9.76803178 C8.77586207,9.22007158 9.21705154,8.77586207 9.76803178,8.77586207 L14.5078303,8.77586207 C15.0557905,8.77586207 15.5,9.21705154 15.5,9.76803178 L15.5,14.5078303 C15.5,15.0557905 15.0588105,15.5 14.5078303,15.5 L9.76803178,15.5 C9.22007158,15.5 8.77586207,15.0588105 8.77586207,14.5078303 L8.77586207,9.76803178 Z M0.5,1.49216971 C0.5,0.94420951 0.94118947,0.5 1.49216971,0.5 L6.23196822,0.5 C6.77992842,0.5 7.22413793,0.94118947 7.22413793,1.49216971 L7.22413793,6.23196822 C7.22413793,6.77992842 6.78294846,7.22413793 6.23196822,7.22413793 L1.49216971,7.22413793 C0.94420951,7.22413793 0.5,6.78294846 0.5,6.23196822 L0.5,1.49216971 Z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 18 18" style="enable-background:new 0 0 18 18;" xml:space="preserve">
<style type="text/css">
.st0{fill:#858585;}
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#858585;}
</style>
<path class="st0" d="M11.9,7.9c-1.2,0-1.5-0.8-1.8-2.4C10,4.4,9.8,2.5,8,2.5H4.6c-1.8,0-2,1.9-2.1,2.9C2.2,7.1,1.9,7.9,0.7,7.9v1.9
h11.2h5.4V7.9H11.9z"/>
<path class="st0" d="M16,5.7c-0.1-1-0.3-2.9-2.1-2.9c0,0-3.8,0-3.9,0c-0.1,0-0.1,0.2-0.1,0.2c1.1,0.5,1.3,1.9,1.4,2.7
c0.1,1.1,0.3,1.5,0.8,1.5c0.1,0,4.1,0,4.1,0s0.1,0,0.1-0.1C16.2,6.6,16.1,6.2,16,5.7z"/>
<path class="st1" d="M8,12.1H3.7v-1.2c0-0.3-0.2-0.4-0.5-0.2l-2.2,1.9c-0.3,0.2-0.3,0.6,0,0.9l2.2,1.9c0.3,0.2,0.5,0.2,0.5-0.2v-1.2
H8V12.1z"/>
<path class="st1" d="M17.1,12.6l-2.2-1.9c-0.3-0.2-0.5-0.2-0.5,0.2v1.2H10v1.9h4.3v1.2c0,0.3,0.2,0.4,0.5,0.2l2.2-1.9
C17.4,13.2,17.4,12.8,17.1,12.6z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 12 12" style="enable-background:new 0 0 12 12;" xml:space="preserve">
<style type="text/css">
.st0{fill:#858585;}
</style>
<circle class="st0" cx="6" cy="6" r="2"/>
<path class="st0" d="M11.5,5.4C11.4,5.2,8.6,2.3,6,2.3s-5.4,3-5.5,3.1C0.3,5.6,0.2,5.8,0.2,6v0c0,0.2,0.1,0.4,0.2,0.6
C0.6,6.8,3.4,9.8,6,9.8s5.4-3,5.5-3.1c0.2-0.2,0.2-0.4,0.2-0.6v0C11.7,5.8,11.7,5.6,11.5,5.4z M10.8,6C10.6,6.2,8.1,8.9,6,8.9
S1.3,6.2,1.1,6l0,0C1.3,5.8,3.8,3.1,6,3.1S10.6,5.8,10.8,6L10.8,6z"/>
</svg>

After

Width:  |  Height:  |  Size: 755 B

+9
View File
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg xmlns="http://www.w3.org/2000/svg">
<filter id="fill">
<feComposite in="FillPaint" in2="SourceGraphic" operator="in"/>
</filter>
</svg>

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

+72
View File
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;">
<style>
path, circle, g {
fill: menutext;
}
path:not(:target),
circle:not(:target),
g:not(:target) {
display: none;
}
</style>
<path id="dollar" d="M16.2,0c-8.9,0-16,7.3-16,16c0,8.9,7.1,16,15.8,16s15.8-7.1,15.8-16C32,7.3,24.9,0,16.2,0z M17.1,25.1v1.6
c0,0.4-0.4,0.5-0.7,0.5c-0.4,0-0.7-0.2-0.7-0.5v-1.6c-3.2-0.2-5-1.8-5.5-4.3c0-0.2,0-0.2,0-0.4c0-0.5,0.4-0.9,0.9-0.9
c0.2,0,0.2,0,0.4,0c0.5,0,0.9,0.2,1.1,0.7c0.4,1.8,1.2,2.7,3.4,2.8v-6.8c-3.6-0.4-5.3-1.8-5.3-4.6c0-3,2.5-4.6,5.2-4.8V5.7
c0-0.4,0.4-0.5,0.7-0.5c0.4,0,0.7,0.2,0.7,0.5v1.1c2.7,0.4,4.4,1.8,5,3.9c0,0.2,0,0.2,0,0.4c0,0.5-0.4,0.7-0.7,0.9
c-0.2,0-0.2,0-0.4,0c-0.4,0-0.7-0.2-0.9-0.7c-0.4-1.4-1.2-2.3-3-2.5v6c3.2,0.7,5.5,1.8,5.5,5.2C22.8,23.5,20.1,25.1,17.1,25.1z
M12.4,11.6c0,1.6,0.7,2.5,3.2,3V8.7C13.7,8.9,12.4,10,12.4,11.6z M17.1,16.9v6.4c2.3-0.2,3.6-1.2,3.6-3.2
C20.6,17.8,19.2,17.2,17.1,16.9z"/>
<path id="briefcase" d="M23.1,5.3c0-1.4-1.2-2.7-2.8-2.7h-8.7c-1.4,0-2.7,1.2-2.7,2.7v4.4H7.1v19.6h17.8V9.8h-1.8V5.3z M20.8,9.8H11
V5.3c0-0.4,0.2-0.5,0.5-0.5h8.7c0.4,0,0.5,0.2,0.5,0.5V9.8z M1.8,9.8h2.7v19.6H1.8c-0.9,0-1.8-0.9-1.8-1.8v-16
C0,10.5,0.9,9.8,1.8,9.8z M32,11.6v16c0,0.9-0.7,1.8-1.8,1.8h-2.7V9.8h2.7C31.3,9.8,32,10.5,32,11.6z"/>
<path id="fingerprint" d="M7.17741905,12 C7.10965537,12 7.041327,11.9953181 6.97243393,11.985018 C6.33263187,11.8918489 5.90515601,11.3862071 6.01809547,10.8552833 C7.41798011,4.26321358 12.2613889,2.57493207 15.0238882,2.15590491 C19.6448063,1.45690206 24.3408291,3.21541158 25.8344535,5.29743816 C26.1664955,5.76047488 25.9835336,6.35881757 25.4244832,6.63364321 C24.8654329,6.9098734 24.1437497,6.75583996 23.8122724,6.29327142 C22.8923805,5.01043967 19.1749781,3.51130562 15.4479759,4.07406612 C12.8080159,4.474834 9.43056132,6.03623689 8.33561323,11.1942506 C8.23453242,11.666651 7.73816348,12 7.17741905,12 Z M16.63127,26 C16.1452186,26 15.6509104,25.9658335 15.147795,25.8938767 C10.637921,25.257137 6.71207921,21.8114952 6.01575422,17.8807924 C5.91171832,17.2932317 6.33391695,16.7382846 6.95813239,16.6404441 C7.58454965,16.5343208 8.17298555,16.9406954 8.27757192,17.5272206 C8.80876054,20.5255916 11.9766264,23.26409 15.4885263,23.7610576 C17.3975027,24.02766 20.959494,23.8221432 23.3220449,19.3789425 C24.4625867,17.2331815 23.0049831,11.881462 19.9521622,9.34692739 C18.2380468,7.92384005 16.4573263,7.76905536 14.6628445,8.89499751 C13.26469,9.77142052 11.8070864,12.2857658 11.8665355,14.6287608 C11.9127737,16.4835887 12.8386382,17.9325598 14.6171568,18.9363308 C15.2210054,19.2764429 16.9411759,19.4933486 17.9424527,18.8296898 C18.7257495,18.3104622 18.9591422,17.2761485 18.6365758,15.7583267 C18.3822659,14.5650869 17.2219077,12.4452096 16.6664991,12.3711821 C16.6692513,12.3722175 16.4666841,12.4312324 16.1276041,12.9095636 C15.8545786,13.2936782 15.58981,14.7297074 15.9476054,15.3581643 C16.0142104,15.4761941 16.0725586,15.5465978 16.3202632,15.5465978 C16.9532859,15.5465978 17.46686,16.0290705 17.46686,16.6249139 C17.46686,17.2207573 16.9543868,17.7042653 16.3213641,17.7042653 C15.2644914,17.7042653 14.4140391,17.2336992 13.9268868,16.3774655 C13.1083609,14.9388479 13.5536787,12.6548678 14.2202791,11.7137354 C15.2540327,10.2564816 16.3631986,10.1151564 17.1123672,10.2564816 C19.7066595,10.7389543 20.8763754,15.2908666 20.8857331,15.3359043 C21.5303153,18.3648181 20.3594985,19.8665919 19.264094,20.593407 C17.4151172,21.8192603 14.6920186,21.493643 13.4380832,20.7859819 C10.3280151,19.0310652 9.62013053,16.497566 9.5744428,14.6805283 C9.49022326,11.3643051 11.4779146,8.30018945 13.391845,7.10021984 C16.0417332,5.43848454 18.9877658,5.66781436 21.4714167,7.72919442 C25.1176276,10.7565552 27.0871539,17.1229168 25.3746898,20.3433702 C23.4326862,23.9950465 20.2983981,26 16.63127,26 Z M16.0845157,30 C14.9348455,30 13.9050564,29.8557557 13.0394288,29.6610017 C10.2114238,29.0257442 7.58700058,27.4599412 6.18892823,25.5735955 C5.84440518,25.1078371 5.98426642,24.4803503 6.50105099,24.1700066 C7.01675554,23.8596629 7.71552172,23.986423 8.06112477,24.4507244 C9.89498097,26.9252176 15.9397944,29.9781448 22.2508301,26.1937972 C22.7676147,25.8844249 23.4658409,26.0087566 23.8109039,26.474515 C24.155427,26.9397877 24.0161057,27.5672745 23.4993212,27.8776182 C20.7987573,29.4963593 18.2315746,30 16.0845157,30 Z"/>
<path id="cart" d="M26.9,21.4H9.4c-0.7,0-1.3,0.5-1.3,1.3s0.5,1.3,1.3,1.3h17.5c0.7,0,1.3-0.5,1.3-1.3
C28.5,21.9,27.8,21.4,26.9,21.4z M13.3,30.1c1.3,0,2.7-1.2,2.7-2.7c0-1.3-1.2-2.7-2.7-2.7s-2.7,1.2-2.7,2.7
C10.6,29,12,30.1,13.3,30.1z M23.9,30.1c1.3,0,2.7-1.2,2.7-2.7c0-1.3-1.2-2.7-2.7-2.7c-1.5,0-2.7,1.2-2.7,2.7
C21.4,29,22.6,30.1,23.9,30.1z M31.5,7.4L31.5,7.4H7.6V7.2L5.7,2.5C5.4,2.2,5.1,1.9,4.6,1.9H0.7C0,1.9,0,2.5,0,2.9
C0,3.5-0.2,4,0.7,4.2h2.7l0.7,1.5l4,13.3c0,0.2,0.2,0.5,0.8,0.5h18.5c0.3,0,0.7-0.2,0.7-0.5L32,8.3C32,8.1,31.8,7.4,31.5,7.4z"/>
<path id="vacation" d="M3.6,27l-2.5-1.8L0.8,25c-0.7-0.4-0.7-1.2-0.4-2c0.4-0.5,1.1-0.7,1.6-0.5l3.6,1.2c0-0.4,0.2-0.9,0.4-1.4
c0.2-0.7,0.5-1.6,1.1-2.3c0.2-0.4,0.5-0.7,0.7-1.2c0.2-0.4,0.5-0.9,0.9-1.2c0.4-0.9,1.1-1.6,1.8-2.5c0.7-0.9,1.4-1.6,2.3-2.5
c0.4-0.4,0.9-0.7,1.2-1.2c0.4-0.2,0.9-0.7,1.2-1.1c0.2-0.2,0.2-0.2,0.4-0.4L3.1,7.3c-0.2,0-0.2,0-0.4,0l-2,0.9
C0.2,8.3-0.3,7.6,0.2,7.1l2-2C2.4,5,2.4,5,2.5,5h17.9c0.5-0.5,1.2-1.1,1.8-1.6c0.7-0.7,1.4-1.2,2.1-1.8c0.4-0.4,0.7-0.5,1.1-0.7
c0.4-0.2,0.7-0.4,1.1-0.5c0.5,0,0.9-0.2,1.2-0.2s0.7,0,1.1,0s0.7,0,1.1,0c0.4,0,0.5,0,0.7,0.2c0.5,0,0.7,0.2,0.7,0.2
c0.2,0,0.2,0.2,0.4,0.4c0,0,0,0.4,0.2,0.7c0,0.2,0,0.5,0.2,0.7c0,0.4,0,0.7,0,1.1s0,0.7,0,1.1c0,0.4,0,0.9-0.2,1.2
c-0.2,0.4-0.4,0.7-0.5,1.1c-0.2,0.4-0.5,0.7-0.7,1.1c-0.5,0.7-1.1,1.4-1.8,2.1c-0.4,0.4-0.7,0.7-1.1,1.1v17.8c0,0.2,0,0.4-0.2,0.4
l-2,2c-0.5,0.5-1.2,0-1.1-0.5l0.7-2c0-0.2,0-0.2,0-0.4L22.8,16c-0.4,0.4-0.7,0.7-0.9,0.9c-0.4,0.4-0.7,0.9-1.2,1.2
c-0.4,0.4-0.7,0.9-1.2,1.2c-0.7,0.9-1.6,1.6-2.5,2.3c-0.9,0.7-1.6,1.4-2.5,2c-0.4,0.4-0.9,0.5-1.2,0.9c-0.4,0.2-0.7,0.5-1.2,0.7
c-0.7,0.4-1.6,0.7-2.3,1.1c-0.4,0.2-0.7,0.2-1.2,0.4L9.6,30c0.4,0.7,0,1.4-0.7,1.8c-0.5,0.2-1.2,0-1.6-0.5l-0.2-0.4l-1.8-2.3
c-0.2,0-0.2,0-0.4,0.2c-0.4,0-0.5,0.2-0.7,0.2s-0.2,0-0.4,0c-0.2,0-0.2,0-0.4,0c-0.2,0-0.4,0-0.5,0c-0.2,0-0.2,0-0.2,0s0,0,0-0.2
c0-0.2,0-0.2,0-0.5c0-0.2,0-0.2,0-0.4c0-0.2,0-0.2,0-0.4C3.4,27.5,3.4,27.3,3.6,27L3.6,27z M5.7,28.4L5.7,28.4L5.7,28.4L5.7,28.4z"
/>
<path id="gift" d="M30.3,8.1h-4.5V8c0.7-0.7,1.3-1.9,1.3-3.2c0-2.6-2.1-4.7-4.9-4.7c-1.5,0-4.5,1.5-6.4,3.4C14,1.6,11,0.1,9.5,0.1
c-2.6,0-4.9,2.1-4.9,4.7C4.7,6.1,5.2,7.2,6,8H1.7C0.6,8,0,8.7,0,9.6v4.5c0,0.2,0.2,0.4,0.4,0.4h13.8V9.6h3.2v4.9h14.2
c0.2,0,0.4-0.2,0.4-0.4V9.6C32,8.7,31.4,8.1,30.3,8.1z M9.5,6.5C8.6,6.5,8,5.9,8,4.8s0.6-1.5,1.5-1.5s3.7,1.9,4.7,2.8
C13.7,6.3,9.5,6.5,9.5,6.5z M22.3,6.5c0,0-4.1-0.2-4.7-0.4c0.9-1.1,3.7-2.8,4.7-2.8S24,3.8,24,4.8S23.2,6.5,22.3,6.5z M1.7,17.7
h12.7v14.2H1.7V17.7z M17.6,17.7h12.7v14.2H17.6V17.7z"/>
<path id="food" d="M14.1,0.9v5.3h-1.4V0.9c0-1.1-1.4-1.1-1.4,0v5.3h-1.2V0.9c0-1.1-1.4-1.1-1.4,0v5.3H7.2V0.9c0-1.2-1.6-1.1-1.6,0
v10.4c0,1.8,1.2,3,2.8,3v15.2c0,1.6,1.1,2.5,2.1,2.5s2.1-0.9,2.1-2.5V14.3c1.6,0,2.8-1.4,2.8-2.8V0.9C15.6-0.4,14.1-0.2,14.1,0.9z
M19.8,3.7v25.8c0,3.2,4.2,3.2,4.2,0V17.1h2.3V3.7C26.5-1.2,19.8-1.2,19.8,3.7z"/>
<path id="fruit" d="M16.5,8c-2.1-0.9-3.9-1.2-6.6-0.9C4.6,8,1.8,12.6,1.8,18c0,5.9,4.8,14,9.8,14c1.6,0,3.9-1.2,4.4-1.2
c0.5,0,2.8,1.2,4.4,1.2c5,0,9.8-8.4,9.8-14c0-5.9-3.2-10.8-9.8-10.8C19,7.1,17.8,7.5,16.5,8z M11.7,0c1.1,0.2,3.2,0.9,4.1,2.3
c0.9,1.4,0.5,3.6,0.2,4.6c-1.2-0.2-3.2-0.7-4.1-2.3C11,3.2,11.4,1.1,11.7,0L11.7,0z"/>
<path id="pet" d="M28.5,8.1c0-1.1-1-1.9-2.1-2.4V3.7c-0.2-0.2-0.3-0.3-0.6-0.3c-0.6,0-1.1,0.8-1.3,2.1c-0.2,0-0.3,0-0.5,0l0,0
c0-0.2,0-0.3-0.2-0.5c-0.3-1.1-0.8-1.9-1.3-2.6C22,2.6,21.7,3.2,21.7,4L22,6.3c-0.3,0.2-0.6,0.3-1,0.6l-3.5,3.7l0,0
c0,0-6.3-0.8-10.9,0.2c-0.6,0-1,0.2-1.1,0.3c-0.5,0.2-0.8,0.3-1.1,0.6c-1.1-0.8-2.2-2.1-3.2-4c0-0.3-0.5-0.5-0.8-0.5s-0.5,0.6-0.3,1
c0.8,2.1,2.1,3.5,3.4,4.5c-0.5,0.5-0.8,1-1,1.6c0,0-0.3,2.2-0.3,5.5l1.4,8c0,1,0.8,1.8,1.9,1.8c1,0,1.9-0.8,1.9-1.8V23l0.5-1.3h8.8
l0.8,1.3v4.7c0,1,0.8,1.8,1.9,1.8c1,0,1.6-0.6,1.8-1.4l0,0l1.9-9l0,0l2.1-6.4h3c3.4,0,3.7-2.9,3.7-2.9L28.5,8.1z"/>
<path id="tree" d="M0.7,18c0,4.9,3.6,8.8,8.1,9.5v4.3c0.2,0,3.2,0,3.2,0v-4.3c1.8-0.4,3.6-1.1,4.9-2.5c0.2-0.2,0.2-0.2,0.2-0.5
c-0.2-0.4-0.2-1.1-0.2-1.6c0-2,0.2-4.9,1.6-7.9c0,0,0.9-1.6,0.7-1.8C18,7.2,14.4,0,10.4,0C5,0,0.7,12.6,0.7,18z M18.3,22.8
c0,3.1,2.2,5.6,4.9,6.3V32h3.2v-2.9c2.7-0.7,4.9-3.2,4.9-6.3c0-3.6-2.9-12.9-6.5-12.9S18.3,19.2,18.3,22.8z"/>
<path id="chill" d="M9.1,18.5l-5.7,5.9C3.2,23.8,3,23.3,3,22.6c0-2.5,2-4.4,4.4-4.4C7.8,18.1,8.5,18.3,9.1,18.5 M26.5,18.5l-5.7,5.9
c-0.2-0.5-0.4-1.1-0.4-1.8c0-2.5,2-4.4,4.4-4.4C25.4,18.1,26,18.3,26.5,18.5 M24.7,2L24.7,2c-0.7,0-1.4,0.7-1.4,1.4s0.7,1.4,1.4,1.4
c2.5,0,4.4,2,4.4,4.4v7.6c-1.6-1.2-3.6-1.8-5.5-1.4c-2.1,0.4-3.9,1.6-5,3.4c-1.6-1.2-3.9-1.2-5.5,0c-1.1-1.8-2.8-3-5-3.4
c-2-0.4-3.9,0.2-5.5,1.4V9.2c0-2.5,2-4.4,4.4-4.4c0.5,0,0.9-0.4,1.2-0.7c0.2-0.4,0.2-0.9,0-1.4C8.2,2.3,7.6,2,7.1,2
C3.2,2,0,5.2,0,9.2v13.5C0,26.7,3.2,30,7.1,30l0,0c3.9,0,7.1-3.2,7.1-7.3c0-0.2,0-0.4,0-0.5c0.2-0.9,0.9-1.4,1.8-1.4
s1.6,0.5,1.8,1.4v0.2c0,0.2,0,0.2,0,0.4c0,2,0.7,3.7,2.1,5c1.4,1.4,3,2.1,5,2.1l0,0c2,0,3.6-0.7,5-2.1c1.4-1.2,2.1-3.2,2.1-5V9.2
C32,5.2,28.8,2,24.7,2"/>
<circle id="circle" r="16" cx="16" cy="16" fill-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 9.5 KiB

+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="-14 -14 48 48" enable-background="new -14 -14 48 48" xml:space="preserve">
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="90.0527" y1="-99.7603" x2="90.0527" y2="-106.3809" gradientTransform="matrix(7.2338 0 0 -7.2338 -641.4998 -735.5619)">
<stop offset="0" style="stop-color:#4B71B8"/>
<stop offset="1" style="stop-color:#293F7E"/>
</linearGradient>
<path fill="url(#SVGID_1_)" d="M33.931,27.993c0,3.304-2.689,5.983-6.002,5.983H-8.082c-3.315,0-6.001-2.683-6.001-5.983V-7.928
c0-3.308,2.687-5.988,6.001-5.988h36.011c3.312,0,6.002,2.681,6.002,5.988V27.993z"/>
<path fill="#FFFFFF" d="M25.613-4.557c0,0-3.707,0-6.166,0c-3.662,0-7.732,1.535-7.732,6.835c0.019,1.845,0,3.613,0,5.603H7.481
v6.728h4.366v19.37h8.021V14.48h5.295l0.479-6.618h-5.913c0,0,0.016-2.946,0-3.8c0-2.093,2.184-1.974,2.312-1.974
c1.042,0,3.059,0.003,3.578,0v-6.646H25.613z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+24
View File
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="23.9995" y1="0" x2="23.9995" y2="48.0005">
<stop offset="0" style="stop-color:#4BD0EF"/>
<stop offset="1" style="stop-color:#29AAE1"/>
</linearGradient>
<path fill-rule="evenodd" clip-rule="evenodd" fill="url(#SVGID_1_)" d="M48,42c0,3.313-2.687,6-6,6H6c-3.313,0-6-2.687-6-6V6
c0-3.313,2.687-6,6-6h36c3.313,0,6,2.687,6,6V42z"/>
<path fill="#29AAE1" d="M40.231,13.413c-1.12,0.497-2.323,0.833-3.588,0.984c1.291-0.774,2.28-1.998,2.747-3.457
c-1.206,0.716-2.543,1.236-3.968,1.516c-1.139-1.214-2.763-1.972-4.56-1.972c-3.449,0-6.246,2.796-6.246,6.247
c0,0.49,0.055,0.966,0.161,1.424c-5.192-0.261-9.795-2.749-12.876-6.528c-0.538,0.923-0.846,1.996-0.846,3.141
c0,2.167,1.103,4.08,2.779,5.199c-1.024-0.032-1.987-0.313-2.83-0.781c0,0.026,0,0.053,0,0.079c0,3.026,2.153,5.551,5.011,6.125
c-0.525,0.143-1.076,0.219-1.646,0.219c-0.403,0-0.794-0.038-1.176-0.11c0.795,2.48,3.102,4.287,5.835,4.338
c-2.138,1.675-4.832,2.675-7.758,2.675c-0.504,0-1.002-0.03-1.491-0.089c2.765,1.773,6.048,2.808,9.576,2.808
c11.49,0,17.774-9.519,17.774-17.774c0-0.271-0.006-0.54-0.019-0.809C38.334,15.766,39.394,14.666,40.231,13.413z"/>
<path fill="#FFFFFF" d="M40.231,14.739c-1.12,0.497-2.323,0.833-3.588,0.984c1.291-0.773,2.28-1.998,2.747-3.456
c-1.206,0.716-2.543,1.236-3.968,1.516c-1.139-1.214-2.763-1.972-4.56-1.972c-3.449,0-6.246,2.796-6.246,6.247
c0,0.489,0.055,0.966,0.161,1.424c-5.192-0.261-9.795-2.748-12.876-6.527c-0.538,0.923-0.846,1.996-0.846,3.141
c0,2.167,1.103,4.079,2.779,5.199c-1.024-0.032-1.987-0.313-2.83-0.781c0,0.026,0,0.052,0,0.079c0,3.027,2.153,5.551,5.011,6.125
c-0.525,0.144-1.076,0.219-1.646,0.219c-0.403,0-0.794-0.038-1.176-0.11c0.795,2.481,3.102,4.287,5.835,4.338
c-2.138,1.676-4.832,2.675-7.758,2.675c-0.504,0-1.002-0.03-1.491-0.089c2.765,1.773,6.048,2.808,9.576,2.808
c11.49,0,17.774-9.519,17.774-17.774c0-0.271-0.006-0.54-0.019-0.808C38.334,17.092,39.394,15.992,40.231,14.739z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

+12
View File
@@ -0,0 +1,12 @@
module.exports = {
"extends": [
"../../.eslintrc.js"
],
"globals": {
"assignManager": true,
"badge": true,
"backgroundLogic": true,
"identityState": true,
"messageHandler": true
}
};
+382
View File
@@ -0,0 +1,382 @@
const assignManager = {
MENU_ASSIGN_ID: "open-in-this-container",
MENU_REMOVE_ID: "remove-open-in-this-container",
MENU_SEPARATOR_ID: "separator",
MENU_HIDE_ID: "hide-container",
MENU_MOVE_ID: "move-to-new-window-container",
storageArea: {
area: browser.storage.local,
exemptedTabs: {},
getSiteStoreKey(pageUrl) {
const url = new window.URL(pageUrl);
const storagePrefix = "siteContainerMap@@_";
return `${storagePrefix}${url.hostname}`;
},
setExempted(pageUrl, tabId) {
const siteStoreKey = this.getSiteStoreKey(pageUrl);
if (!(siteStoreKey in this.exemptedTabs)) {
this.exemptedTabs[siteStoreKey] = [];
}
this.exemptedTabs[siteStoreKey].push(tabId);
},
removeExempted(pageUrl) {
const siteStoreKey = this.getSiteStoreKey(pageUrl);
this.exemptedTabs[siteStoreKey] = [];
},
isExempted(pageUrl, tabId) {
const siteStoreKey = this.getSiteStoreKey(pageUrl);
if (!(siteStoreKey in this.exemptedTabs)) {
return false;
}
return this.exemptedTabs[siteStoreKey].includes(tabId);
},
get(pageUrl) {
const siteStoreKey = this.getSiteStoreKey(pageUrl);
return new Promise((resolve, reject) => {
this.area.get([siteStoreKey]).then((storageResponse) => {
if (storageResponse && siteStoreKey in storageResponse) {
resolve(storageResponse[siteStoreKey]);
}
resolve(null);
}).catch((e) => {
reject(e);
});
});
},
set(pageUrl, data, exemptedTabIds) {
const siteStoreKey = this.getSiteStoreKey(pageUrl);
if (exemptedTabIds) {
exemptedTabIds.forEach((tabId) => {
this.setExempted(pageUrl, tabId);
});
}
return this.area.set({
[siteStoreKey]: data
});
},
remove(pageUrl) {
const siteStoreKey = this.getSiteStoreKey(pageUrl);
// When we remove an assignment we should clear all the exemptions
this.removeExempted(pageUrl);
return this.area.remove([siteStoreKey]);
},
async deleteContainer(userContextId) {
const sitesByContainer = await this.getByContainer(userContextId);
this.area.remove(Object.keys(sitesByContainer));
},
async getByContainer(userContextId) {
const sites = {};
const siteConfigs = await this.area.get();
Object.keys(siteConfigs).forEach((key) => {
// For some reason this is stored as string... lets check them both as that
if (String(siteConfigs[key].userContextId) === String(userContextId)) {
const site = siteConfigs[key];
// In hindsight we should have stored this
// TODO file a follow up to clean the storage onLoad
site.hostname = key.replace(/^siteContainerMap@@_/, "");
sites[key] = site;
}
});
return sites;
}
},
_neverAsk(m) {
const pageUrl = m.pageUrl;
if (m.neverAsk === true) {
// If we have existing data and for some reason it hasn't been deleted etc lets update it
this.storageArea.get(pageUrl).then((siteSettings) => {
if (siteSettings) {
siteSettings.neverAsk = true;
this.storageArea.set(pageUrl, siteSettings);
}
}).catch((e) => {
throw e;
});
}
},
// We return here so the confirm page can load the tab when exempted
async _exemptTab(m) {
const pageUrl = m.pageUrl;
this.storageArea.setExempted(pageUrl, m.tabId);
return true;
},
// Before a request is handled by the browser we decide if we should route through a different container
async onBeforeRequest(options) {
if (options.frameId !== 0 || options.tabId === -1) {
return {};
}
this.removeContextMenu();
const [tab, siteSettings] = await Promise.all([
browser.tabs.get(options.tabId),
this.storageArea.get(options.url)
]);
let container;
try {
container = await browser.contextualIdentities.get(backgroundLogic.cookieStoreId(siteSettings.userContextId));
} catch (e) {
container = false;
}
// The container we have in the assignment map isn't present any more so lets remove it
// then continue the existing load
if (!container) {
this.deleteContainer(siteSettings.userContextId);
return {};
}
const userContextId = this.getUserContextIdFromCookieStore(tab);
if (!siteSettings
|| userContextId === siteSettings.userContextId
|| tab.incognito
|| this.storageArea.isExempted(options.url, tab.id)) {
return {};
}
this.reloadPageInContainer(options.url, userContextId, siteSettings.userContextId, tab.index + 1, tab.active, siteSettings.neverAsk);
this.calculateContextMenu(tab);
/* 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 (NEW_TAB_PAGES), 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 (backgroundLogic.NEW_TAB_PAGES.has(tab.url)
|| (messageHandler.lastCreatedTab
&& messageHandler.lastCreatedTab.id === tab.id)) {
browser.tabs.remove(tab.id);
}
return {
cancel: true,
};
},
init() {
browser.contextMenus.onClicked.addListener((info, tab) => {
this._onClickedHandler(info, tab);
});
// Before a request is handled by the browser we decide if we should route through a different container
browser.webRequest.onBeforeRequest.addListener((options) => {
return this.onBeforeRequest(options);
},{urls: ["<all_urls>"], types: ["main_frame"]}, ["blocking"]);
},
async _onClickedHandler(info, tab) {
const userContextId = this.getUserContextIdFromCookieStore(tab);
// Mapping ${URL(info.pageUrl).hostname} to ${userContextId}
let remove;
if (userContextId) {
switch (info.menuItemId) {
case this.MENU_ASSIGN_ID:
case this.MENU_REMOVE_ID:
if (info.menuItemId === this.MENU_ASSIGN_ID) {
remove = false;
} else {
remove = true;
}
await this._setOrRemoveAssignment(tab.id, info.pageUrl, userContextId, remove);
break;
case this.MENU_MOVE_ID:
backgroundLogic.moveTabsToWindow({
cookieStoreId: tab.cookieStoreId,
windowId: tab.windowId,
});
break;
case this.MENU_HIDE_ID:
backgroundLogic.hideTabs({
cookieStoreId: tab.cookieStoreId,
windowId: tab.windowId,
});
break;
}
}
},
deleteContainer(userContextId) {
this.storageArea.deleteContainer(userContextId);
},
getUserContextIdFromCookieStore(tab) {
if (!("cookieStoreId" in tab)) {
return false;
}
return backgroundLogic.getUserContextIdFromCookieStoreId(tab.cookieStoreId);
},
isTabPermittedAssign(tab) {
// Ensure we are not an important about url
// Ensure we are not in incognito mode
const url = new URL(tab.url);
if (url.protocol === "about:"
|| url.protocol === "moz-extension:"
|| tab.incognito) {
return false;
}
return true;
},
async _setOrRemoveAssignment(tabId, pageUrl, userContextId, remove) {
let actionName;
// https://github.com/mozilla/testpilot-containers/issues/626
// Context menu has stored context IDs as strings, so we need to coerce
// the value to a string for accurate checking
userContextId = String(userContextId);
if (!remove) {
const tabs = await browser.tabs.query({});
const assignmentStoreKey = this.storageArea.getSiteStoreKey(pageUrl);
const exemptedTabIds = tabs.filter((tab) => {
const tabStoreKey = this.storageArea.getSiteStoreKey(tab.url);
/* Auto exempt all tabs that exist for this hostname that are not in the same container */
if (tabStoreKey === assignmentStoreKey &&
this.getUserContextIdFromCookieStore(tab) !== userContextId) {
return true;
}
return false;
}).map((tab) => {
return tab.id;
});
await this.storageArea.set(pageUrl, {
userContextId,
neverAsk: false
}, exemptedTabIds);
actionName = "added";
} else {
await this.storageArea.remove(pageUrl);
actionName = "removed";
}
browser.tabs.sendMessage(tabId, {
text: `Successfully ${actionName} site to always open in this container`
});
const tab = await browser.tabs.get(tabId);
this.calculateContextMenu(tab);
},
async _getAssignment(tab) {
const cookieStore = this.getUserContextIdFromCookieStore(tab);
// Ensure we have a cookieStore to assign to
if (cookieStore
&& this.isTabPermittedAssign(tab)) {
return await this.storageArea.get(tab.url);
}
return false;
},
_getByContainer(userContextId) {
return this.storageArea.getByContainer(userContextId);
},
removeContextMenu() {
// There is a focus issue in this menu where if you change window with a context menu click
// you get the wrong menu display because of async
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1215376#c16
// We also can't change for always private mode
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1352102
browser.contextMenus.remove(this.MENU_ASSIGN_ID);
browser.contextMenus.remove(this.MENU_REMOVE_ID);
browser.contextMenus.remove(this.MENU_SEPARATOR_ID);
browser.contextMenus.remove(this.MENU_HIDE_ID);
browser.contextMenus.remove(this.MENU_MOVE_ID);
},
async calculateContextMenu(tab) {
this.removeContextMenu();
const siteSettings = await this._getAssignment(tab);
// Return early and not add an item if we have false
// False represents assignment is not permitted
if (siteSettings === false) {
return false;
}
let checked = false;
let menuId = this.MENU_ASSIGN_ID;
const tabUserContextId = this.getUserContextIdFromCookieStore(tab);
if (siteSettings &&
Number(siteSettings.userContextId) === Number(tabUserContextId)) {
checked = true;
menuId = this.MENU_REMOVE_ID;
}
browser.contextMenus.create({
id: menuId,
title: "Always Open in This Container",
checked,
type: "checkbox",
contexts: ["all"],
});
browser.contextMenus.create({
id: this.MENU_SEPARATOR_ID,
type: "separator",
contexts: ["all"],
});
browser.contextMenus.create({
id: this.MENU_HIDE_ID,
title: "Hide This Container",
contexts: ["all"],
});
browser.contextMenus.create({
id: this.MENU_MOVE_ID,
title: "Move Tabs to a New Window",
contexts: ["all"],
});
},
encodeURLProperty(url) {
return encodeURIComponent(url).replace(/[!'()*]/g, (c) => {
const charCode = c.charCodeAt(0).toString(16);
return `%${charCode}`;
});
},
reloadPageInContainer(url, currentUserContextId, userContextId, index, active, neverAsk = false) {
const cookieStoreId = backgroundLogic.cookieStoreId(userContextId);
const loadPage = browser.extension.getURL("confirm-page.html");
// False represents assignment is not permitted
// If the user has explicitly checked "Never Ask Again" on the warning page we will send them straight there
if (neverAsk) {
browser.tabs.create({url, cookieStoreId, index, active});
} else {
let confirmUrl = `${loadPage}?url=${this.encodeURLProperty(url)}&cookieStoreId=${cookieStoreId}`;
let currentCookieStoreId;
if (currentUserContextId) {
currentCookieStoreId = backgroundLogic.cookieStoreId(currentUserContextId);
confirmUrl += `&currentCookieStoreId=${currentCookieStoreId}`;
}
browser.tabs.create({
url: confirmUrl,
cookieStoreId: currentCookieStoreId,
index,
active
}).then(() => {
// We don't want to sync this URL ever nor clutter the users history
browser.history.deleteUrl({url: confirmUrl});
}).catch((e) => {
throw e;
});
}
}
};
assignManager.init();
+306
View File
@@ -0,0 +1,306 @@
const DEFAULT_TAB = "about:newtab";
const backgroundLogic = {
NEW_TAB_PAGES: new Set([
"about:startpage",
"about:newtab",
"about:home",
"about:blank"
]),
async getExtensionInfo() {
const manifestPath = browser.extension.getURL("manifest.json");
const response = await fetch(manifestPath);
const extensionInfo = await response.json();
return extensionInfo;
},
getUserContextIdFromCookieStoreId(cookieStoreId) {
if (!cookieStoreId) {
return false;
}
const container = cookieStoreId.replace("firefox-container-", "");
if (container !== cookieStoreId) {
return container;
}
return false;
},
async deleteContainer(userContextId, removed = false) {
await this._closeTabs(userContextId);
if (!removed) {
await browser.contextualIdentities.remove(this.cookieStoreId(userContextId));
}
assignManager.deleteContainer(userContextId);
return {done: true, userContextId};
},
async createOrUpdateContainer(options) {
let donePromise;
if (options.userContextId !== "new") {
donePromise = browser.contextualIdentities.update(
this.cookieStoreId(options.userContextId),
options.params
);
} else {
donePromise = browser.contextualIdentities.create(options.params);
}
await donePromise;
browser.runtime.sendMessage({
method: "refreshNeeded"
});
},
async openNewTab(options) {
let url = options.url || undefined;
const userContextId = ("userContextId" in options) ? options.userContextId : 0;
const active = ("nofocus" in options) ? options.nofocus : true;
const cookieStoreId = backgroundLogic.cookieStoreId(userContextId);
// Autofocus url bar will happen in 54: https://bugzilla.mozilla.org/show_bug.cgi?id=1295072
// We can't open new tab pages, so open a blank tab. Used in tab un-hide
if (this.NEW_TAB_PAGES.has(url)) {
url = undefined;
}
if (!this.isPermissibleURL(url)) {
return;
}
return browser.tabs.create({
url,
active,
pinned: options.pinned || false,
cookieStoreId
});
},
isPermissibleURL(url) {
const protocol = new URL(url).protocol;
// We can't open these we just have to throw them away
if (protocol === "about:"
|| protocol === "chrome:"
|| protocol === "moz-extension:") {
return false;
}
return true;
},
checkArgs(requiredArguments, options, methodName) {
requiredArguments.forEach((argument) => {
if (!(argument in options)) {
return new Error(`${methodName} must be called with ${argument} argument.`);
}
});
},
async getTabs(options) {
const requiredArguments = ["cookieStoreId", "windowId"];
this.checkArgs(requiredArguments, options, "getTabs");
const { cookieStoreId, windowId } = options;
const list = [];
const tabs = await browser.tabs.query({
cookieStoreId,
windowId
});
tabs.forEach((tab) => {
list.push(identityState._createTabObject(tab));
});
const containerState = await identityState.storageArea.get(cookieStoreId);
return list.concat(containerState.hiddenTabs);
},
async moveTabsToWindow(options) {
const requiredArguments = ["cookieStoreId", "windowId"];
this.checkArgs(requiredArguments, options, "moveTabsToWindow");
const { cookieStoreId, windowId } = options;
const list = await browser.tabs.query({
cookieStoreId,
windowId
});
const containerState = await identityState.storageArea.get(cookieStoreId);
// Nothing to do
if (list.length === 0 &&
containerState.hiddenTabs.length === 0) {
return;
}
let newWindowObj;
let hiddenDefaultTabToClose;
if (list.length) {
newWindowObj = await browser.windows.create({
tabId: list.shift().id
});
browser.tabs.move(list.map((tab) => tab.id), {
windowId: newWindowObj.id,
index: -1
});
} else {
//As we get a blank tab here we will need to await the tabs creation
newWindowObj = await browser.windows.create({
});
hiddenDefaultTabToClose = true;
}
const showHiddenPromises = [];
// Let's show the hidden tabs.
for (let object of containerState.hiddenTabs) { // eslint-disable-line prefer-const
showHiddenPromises.push(browser.tabs.create({
url: object.url || DEFAULT_TAB,
windowId: newWindowObj.id,
cookieStoreId
}));
}
if (hiddenDefaultTabToClose) {
// Lets wait for hidden tabs to show before closing the others
await showHiddenPromises;
}
containerState.hiddenTabs = [];
// Let's close all the normal tab in the new window. In theory it
// should be only the first tab, but maybe there are addons doing
// crazy stuff.
const tabs = await browser.tabs.query({windowId: newWindowObj.id});
for (let tab of tabs) { // eslint-disable-line prefer-const
if (tab.cookieStoreId !== cookieStoreId) {
browser.tabs.remove(tab.id);
}
}
return await identityState.storageArea.set(cookieStoreId, containerState);
},
async _closeTabs(userContextId, windowId = false) {
const cookieStoreId = this.cookieStoreId(userContextId);
let tabs;
/* if we have no windowId we are going to close all this container (used for deleting) */
if (windowId !== false) {
tabs = await browser.tabs.query({
cookieStoreId,
windowId
});
} else {
tabs = await browser.tabs.query({
cookieStoreId
});
}
const tabIds = tabs.map((tab) => tab.id);
return browser.tabs.remove(tabIds);
},
async queryIdentitiesState(windowId) {
const identities = await browser.contextualIdentities.query({});
const identitiesOutput = {};
const identitiesPromise = identities.map(async function (identity) {
const { cookieStoreId } = identity;
const containerState = await identityState.storageArea.get(cookieStoreId);
const openTabs = await browser.tabs.query({
cookieStoreId,
windowId
});
identitiesOutput[cookieStoreId] = {
hasHiddenTabs: !!containerState.hiddenTabs.length,
hasOpenTabs: !!openTabs.length
};
return;
});
await Promise.all(identitiesPromise);
return identitiesOutput;
},
async sortTabs() {
const windows = await browser.windows.getAll();
for (let windowObj of windows) { // eslint-disable-line prefer-const
// First the pinned tabs, then the normal ones.
await this._sortTabsInternal(windowObj, true);
await this._sortTabsInternal(windowObj, false);
}
},
async _sortTabsInternal(windowObj, pinnedTabs) {
const tabs = await browser.tabs.query({windowId: windowObj.id});
let pos = 0;
// Let's collect UCIs/tabs for this window.
const map = new Map;
for (const tab of tabs) {
if (pinnedTabs && !tab.pinned) {
// We don't have, or we already handled all the pinned tabs.
break;
}
if (!pinnedTabs && tab.pinned) {
// pinned tabs must be consider as taken positions.
++pos;
continue;
}
const userContextId = backgroundLogic.getUserContextIdFromCookieStoreId(tab.cookieStoreId);
if (!map.has(userContextId)) {
map.set(userContextId, []);
}
map.get(userContextId).push(tab);
}
// Let's sort the map.
const sortMap = new Map([...map.entries()].sort((a, b) => a[0] > b[0]));
// Let's move tabs.
sortMap.forEach(tabs => {
for (const tab of tabs) {
++pos;
browser.tabs.move(tab.id, {
windowId: windowObj.id,
index: pos
});
}
});
},
async hideTabs(options) {
const requiredArguments = ["cookieStoreId", "windowId"];
this.checkArgs(requiredArguments, options, "hideTabs");
const { cookieStoreId, windowId } = options;
const userContextId = backgroundLogic.getUserContextIdFromCookieStoreId(cookieStoreId);
const containerState = await identityState.storeHidden(cookieStoreId, windowId);
await this._closeTabs(userContextId, windowId);
return containerState;
},
async showTabs(options) {
if (!("cookieStoreId" in options)) {
return Promise.reject("showTabs must be called with cookieStoreId argument.");
}
const userContextId = backgroundLogic.getUserContextIdFromCookieStoreId(options.cookieStoreId);
const promises = [];
const containerState = await identityState.storageArea.get(options.cookieStoreId);
for (let object of containerState.hiddenTabs) { // eslint-disable-line prefer-const
promises.push(this.openNewTab({
userContextId: userContextId,
url: object.url,
nofocus: options.nofocus || false,
pinned: object.pinned,
}));
}
containerState.hiddenTabs = [];
await Promise.all(promises);
return await identityState.storageArea.set(options.cookieStoreId, containerState);
},
cookieStoreId(userContextId) {
return `firefox-container-${userContextId}`;
}
};
+25
View File
@@ -0,0 +1,25 @@
const MAJOR_VERSIONS = ["2.3.0", "2.4.0"];
const badge = {
async init() {
const currentWindow = await browser.windows.getCurrent();
this.displayBrowserActionBadge(currentWindow.incognito);
},
disableAddon(tabId) {
browser.browserAction.disable(tabId);
browser.browserAction.setTitle({ tabId, title: "Containers disabled in Private Browsing Mode" });
},
async displayBrowserActionBadge() {
const extensionInfo = await backgroundLogic.getExtensionInfo();
const storage = await browser.storage.local.get({browserActionBadgesClicked: []});
if (MAJOR_VERSIONS.indexOf(extensionInfo.version) > -1 &&
storage.browserActionBadgesClicked.indexOf(extensionInfo.version) < 0) {
browser.browserAction.setBadgeBackgroundColor({color: "rgba(0,217,0,255)"});
browser.browserAction.setBadgeText({text: "NEW"});
}
}
};
badge.init();
+62
View File
@@ -0,0 +1,62 @@
const identityState = {
storageArea: {
area: browser.storage.local,
getContainerStoreKey(cookieStoreId) {
const storagePrefix = "identitiesState@@_";
return `${storagePrefix}${cookieStoreId}`;
},
async get(cookieStoreId) {
const storeKey = this.getContainerStoreKey(cookieStoreId);
const storageResponse = await this.area.get([storeKey]);
if (storageResponse && storeKey in storageResponse) {
return storageResponse[storeKey];
}
const defaultContainerState = identityState._createIdentityState();
await this.set(cookieStoreId, defaultContainerState);
return defaultContainerState;
},
set(cookieStoreId, data) {
const storeKey = this.getContainerStoreKey(cookieStoreId);
return this.area.set({
[storeKey]: data
});
},
remove(cookieStoreId) {
const storeKey = this.getContainerStoreKey(cookieStoreId);
return this.area.remove([storeKey]);
}
},
_createTabObject(tab) {
return Object.assign({}, tab);
},
async storeHidden(cookieStoreId, windowId) {
const containerState = await this.storageArea.get(cookieStoreId);
const tabsByContainer = await browser.tabs.query({cookieStoreId, windowId});
tabsByContainer.forEach((tab) => {
const tabObject = this._createTabObject(tab);
if (!backgroundLogic.isPermissibleURL(tab.url)) {
return;
}
// This tab is going to be closed. Let's mark this tabObject as
// non-active.
tabObject.active = false;
tabObject.hiddenState = true;
containerState.hiddenTabs.push(tabObject);
});
return this.storageArea.set(cookieStoreId, containerState);
},
_createIdentityState() {
return {
hiddenTabs: []
};
},
};
+22
View File
@@ -0,0 +1,22 @@
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<!--
This didn't work for debugging in the manifest.
"scripts": [
"js/background/backgroundLogic.js",
"js/background/assignManager.js",
"js/background/badge.js",
"js/background/identityState.js",
"js/background/messageHandler.js",
]
-->
<script type="text/javascript" src="backgroundLogic.js"></script>
<script type="text/javascript" src="assignManager.js"></script>
<script type="text/javascript" src="badge.js"></script>
<script type="text/javascript" src="identityState.js"></script>
<script type="text/javascript" src="messageHandler.js"></script>
</body>
</html>
+178
View File
@@ -0,0 +1,178 @@
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,
unhideQueue: [],
init() {
// Handles messages from webextension code
browser.runtime.onMessage.addListener((m) => {
let response;
switch (m.method) {
case "deleteContainer":
response = backgroundLogic.deleteContainer(m.message.userContextId);
break;
case "createOrUpdateContainer":
response = backgroundLogic.createOrUpdateContainer(m.message);
break;
case "neverAsk":
assignManager._neverAsk(m);
break;
case "getAssignment":
response = browser.tabs.get(m.tabId).then((tab) => {
return assignManager._getAssignment(tab);
});
break;
case "getAssignmentObjectByContainer":
response = assignManager._getByContainer(m.message.userContextId);
break;
case "setOrRemoveAssignment":
// m.tabId is used for where to place the in content message
// m.url is the assignment to be removed/added
response = browser.tabs.get(m.tabId).then((tab) => {
return assignManager._setOrRemoveAssignment(tab.id, m.url, m.userContextId, m.value);
});
break;
case "sortTabs":
backgroundLogic.sortTabs();
break;
case "showTabs":
this.unhideContainer(m.cookieStoreId);
break;
case "hideTabs":
backgroundLogic.hideTabs({
cookieStoreId: m.cookieStoreId,
windowId: m.windowId
});
break;
case "checkIncompatibleAddons":
// TODO
break;
case "moveTabsToWindow":
response = backgroundLogic.moveTabsToWindow({
cookieStoreId: m.cookieStoreId,
windowId: m.windowId
});
break;
case "getTabs":
response = backgroundLogic.getTabs({
cookieStoreId: m.cookieStoreId,
windowId: m.windowId
});
break;
case "queryIdentitiesState":
response = backgroundLogic.queryIdentitiesState(m.message.windowId);
break;
case "exemptContainerAssignment":
response = assignManager._exemptTab(m);
break;
}
return response;
});
if (browser.contextualIdentities.onRemoved) {
browser.contextualIdentities.onRemoved.addListener(({contextualIdentity}) => {
const userContextId = backgroundLogic.getUserContextIdFromCookieStoreId(contextualIdentity.cookieStoreId);
backgroundLogic.deleteContainer(userContextId, true);
});
}
browser.tabs.onActivated.addListener((info) => {
assignManager.removeContextMenu();
browser.tabs.get(info.tabId).then((tab) => {
assignManager.calculateContextMenu(tab);
}).catch((e) => {
throw e;
});
});
browser.windows.onFocusChanged.addListener((windowId) => {
this.onFocusChangedCallback(windowId);
});
browser.webRequest.onCompleted.addListener((details) => {
if (details.frameId !== 0 || details.tabId === -1) {
return {};
}
assignManager.removeContextMenu();
browser.tabs.get(details.tabId).then((tab) => {
assignManager.calculateContextMenu(tab);
}).catch((e) => {
throw e;
});
}, {urls: ["<all_urls>"], types: ["main_frame"]});
browser.tabs.onCreated.addListener((tab) => {
if (tab.incognito) {
badge.disableAddon(tab.id);
}
// lets remember the last tab created so we can close it if it looks like a redirect
this.lastCreatedTab = tab;
if (tab.cookieStoreId) {
// Don't count firefox-default, firefox-private, nor our own confirm page loads
if (tab.cookieStoreId !== "firefox-default" &&
tab.cookieStoreId !== "firefox-private" &&
!tab.url.startsWith("moz-extension")) {
// increment the counter of container tabs opened
this.incrementCountOfContainerTabsOpened();
}
this.unhideContainer(tab.cookieStoreId);
}
setTimeout(() => {
this.lastCreatedTab = null;
}, this.LAST_CREATED_TAB_TIMER);
});
},
async incrementCountOfContainerTabsOpened() {
const key = "containerTabsOpened";
const count = await browser.storage.local.get({[key]: 0});
const countOfContainerTabsOpened = ++count[key];
browser.storage.local.set({[key]: countOfContainerTabsOpened});
// When the user opens their _ tab, give them the achievement
if (countOfContainerTabsOpened === 100) {
const storage = await browser.storage.local.get({achievements: []});
storage.achievements.push({"name": "manyContainersOpened", "done": false});
// use set and spread to create a unique array
const achievements = [...new Set(storage.achievements)];
browser.storage.local.set({achievements});
browser.browserAction.setBadgeBackgroundColor({color: "rgba(0,217,0,255)"});
browser.browserAction.setBadgeText({text: "NEW"});
}
},
async unhideContainer(cookieStoreId) {
if (!this.unhideQueue.includes(cookieStoreId)) {
this.unhideQueue.push(cookieStoreId);
// Unhide all hidden tabs
await backgroundLogic.showTabs({
cookieStoreId
});
this.unhideQueue.splice(this.unhideQueue.indexOf(cookieStoreId), 1);
}
},
async onFocusChangedCallback(windowId) {
assignManager.removeContextMenu();
// browserAction loses background color in new windows ...
// https://bugzil.la/1314674
// https://github.com/mozilla/testpilot-containers/issues/608
// ... so re-call displayBrowserActionBadge on window changes
badge.displayBrowserActionBadge();
browser.tabs.query({active: true, windowId}).then((tabs) => {
if (tabs && tabs[0]) {
assignManager.calculateContextMenu(tabs[0]);
}
}).catch((e) => {
throw e;
});
}
};
// Lets do this last as theme manager did a check before connecting before
messageHandler.init();
+84
View File
@@ -0,0 +1,84 @@
async function load() {
const searchParams = new URL(window.location).searchParams;
const redirectUrl = decodeURIComponent(searchParams.get("url"));
const cookieStoreId = searchParams.get("cookieStoreId");
const currentCookieStoreId = searchParams.get("currentCookieStoreId");
const redirectUrlElement = document.getElementById("redirect-url");
redirectUrlElement.textContent = redirectUrl;
appendFavicon(redirectUrl, redirectUrlElement);
const container = await browser.contextualIdentities.get(cookieStoreId);
[...document.querySelectorAll(".container-name")].forEach((containerNameElement) => {
containerNameElement.textContent = container.name;
});
// If default container, button will default to normal HTML content
if (currentCookieStoreId) {
const currentContainer = await browser.contextualIdentities.get(currentCookieStoreId);
document.getElementById("current-container-name").textContent = currentContainer.name;
}
document.getElementById("redirect-form").addEventListener("submit", (e) => {
e.preventDefault();
const buttonTarget = e.explicitOriginalTarget;
switch (buttonTarget.id) {
case "confirm":
confirmSubmit(redirectUrl, cookieStoreId);
break;
case "deny":
denySubmit(redirectUrl);
break;
}
});
}
function appendFavicon(pageUrl, redirectUrlElement) {
const origin = new URL(pageUrl).origin;
const favIconElement = Utils.createFavIconElement(`${origin}/favicon.ico`);
redirectUrlElement.prepend(favIconElement);
}
function confirmSubmit(redirectUrl, cookieStoreId) {
const neverAsk = document.getElementById("never-ask").checked;
// Sending neverAsk message to background to store for next time we see this process
if (neverAsk) {
browser.runtime.sendMessage({
method: "neverAsk",
neverAsk: true,
pageUrl: redirectUrl
});
}
openInContainer(redirectUrl, cookieStoreId);
}
function getCurrentTab() {
return browser.tabs.query({
active: true,
windowId: browser.windows.WINDOW_ID_CURRENT
});
}
async function denySubmit(redirectUrl) {
const tab = await getCurrentTab();
await browser.runtime.sendMessage({
method: "exemptContainerAssignment",
tabId: tab[0].id,
pageUrl: redirectUrl
});
document.location.replace(redirectUrl);
}
load();
async function openInContainer(redirectUrl, cookieStoreId) {
const tab = await getCurrentTab();
await browser.tabs.create({
index: tab[0].index + 1,
cookieStoreId,
url: redirectUrl
});
if (tab.length > 0) {
browser.tabs.remove(tab[0].id);
}
}
+46
View File
@@ -0,0 +1,46 @@
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 addMessage(message) {
const divElement = document.createElement("div");
divElement.classList.add("container-notification");
// Ideally we would use https://bugzilla.mozilla.org/show_bug.cgi?id=1340930 when this is available
divElement.innerText = message.text;
const imageElement = document.createElement("img");
const imagePath = browser.extension.getURL("/img/container-site-d-24.png");
const response = await fetch(imagePath);
const blob = await response.blob();
const objectUrl = URL.createObjectURL(blob);
imageElement.src = objectUrl;
divElement.prepend(imageElement);
document.body.appendChild(divElement);
await delayAnimation(100);
await doAnimation(divElement, "transform", "translateY(0)");
await delayAnimation(3000);
await doAnimation(divElement, "transform", "translateY(-100%)");
divElement.remove();
}
browser.runtime.onMessage.addListener((message) => {
addMessage(message);
});
+1079
View File
File diff suppressed because it is too large Load Diff
+23
View File
@@ -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;
}
};
+67
View File
@@ -0,0 +1,67 @@
{
"manifest_version": 2,
"name": "Firefox Multi-Account Containers",
"version": "5.0.0",
"description": "Multi-Account Containers helps you keep all the parts of your online life contained in different tabs. Custom labels and color-coded tabs help keep different activities — like online shopping, travel planning, or checking work email — separate.",
"icons": {
"48": "img/container-site-d-48.png",
"96": "img/container-site-d-96.png"
},
"applications": {
"gecko": {
"strict_min_version": "57.0"
}
},
"homepage_url": "https://testpilot.firefox.com/",
"permissions": [
"<all_urls>",
"activeTab",
"cookies",
"contextMenus",
"contextualIdentities",
"history",
"idle",
"storage",
"tabs",
"webRequestBlocking",
"webRequest"
],
"commands": {
"_execute_browser_action": {
"suggested_key": {
"default": "Ctrl+Period",
"mac": "MacCtrl+Period"
},
"description": "Open containers panel"
}
},
"browser_action": {
"browser_style": true,
"default_icon": "img/container-site.svg",
"default_title": "Multi-Account Containers",
"default_popup": "popup.html"
},
"background": {
"page": "js/background/index.html"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["js/content-script.js"],
"css": ["css/content.css"],
"run_at": "document_start"
}
],
"web_accessible_resources": [
"/img/container-site-d-24.png"
]
}
+218
View File
@@ -0,0 +1,218 @@
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Multi-Account Containers</title>
<link rel="stylesheet" href="/css/popup.css">
</head>
<body>
<div class="hide panel onboarding onboarding-panel-1">
<img class="onboarding-img" alt="Container Tabs Overview" src="/img/onboarding-1.png" />
<h3 class="onboarding-title">A better way to manage all the things you do online</h3>
<p>
Use containers to organize tasks, manage accounts, and keep your focus where you want it.
</p>
<a href="#" class="onboarding-button onboarding-start-button">Get Started</a>
</div>
<div class="hide panel onboarding security-onboarding-panel-1">
<img class="onboarding-img" alt="Container Tabs Overview" src="/img/onboarding-1.png" />
<h3 class="onboarding-title">A simple and secure way to manage your online life</h3>
<p>
Use containers to organize tasks, manage accounts, and store sensitive data.
</p>
<a href="#" class="onboarding-button onboarding-start-button">Get Started</a>
</div>
<div class="panel onboarding onboarding-panel-2 hide">
<img class="onboarding-img" alt="How Containers Work" src="/img/onboarding-2.png" />
<h3 class="onboarding-title">Put containers to work for you.</h3>
<p>Features like color-coding and separate container tabs help you find things easily, focus your attention, and minimize distractions.</p>
<a href="#" class="onboarding-button onboarding-next-button">Next</a>
</div>
<div class="panel onboarding security-onboarding-panel-2 hide">
<img class="onboarding-img" alt="How Containers Work" src="/img/onboarding-2.png" />
<h3 class="onboarding-title">Put containers to work for you.</h3>
<p>Color-coding helps you categorize your online life, find things easily, and minimize distractions.</p>
<a href="#" class="onboarding-button onboarding-next-button">Next</a>
</div>
<div class="panel onboarding onboarding-panel-3 hide">
<img class="onboarding-img" alt="How Containers Work" src="/img/onboarding-3.png" />
<h3 class="onboarding-title">A place for everything, and everything in its place.</h3>
<p>Start with the containers we've created, or create your own.</p>
<a href="#" class="onboarding-button onboarding-almost-done-button">Next</a>
</div>
<div class="panel onboarding security-onboarding-panel-3 hide">
<img class="onboarding-img" alt="How Containers Work" src="/img/onboarding-3-security.png" />
<h3 class="onboarding-title">Set boundaries for your browsing.</h3>
<p>Cookies are stored within a container, so you can segment sensitive data and browsing history to stay organized and to limit the impact of online trackers.</p>
<a href="#" class="onboarding-button onboarding-almost-done-button">Next</a>
</div>
<div class="panel onboarding onboarding-panel-4 hide" id="onboarding-panel-4">
<img class="onboarding-img" alt="How to assign sites to containers" src="/img/onboarding-4.png" />
<h3 class="onboarding-title">Always open sites in the containers you want.</h3>
<p>Right-click inside a container tab to assign the site to always open in the container.</p>
<a href="#" id="onboarding-done-button" class="onboarding-button">Next</a>
</div>
<div class="panel onboarding onboarding-panel-5 hide" id="onboarding-panel-5">
<img class="onboarding-img" alt="Long-press the New Tab button to create a new container tab." src="/img/onboarding-3.png" />
<h3 class="onboarding-title">Container tabs when you need them.</h3>
<p>Long-press the New Tab button to create a new container tab.</p>
<a href="#" id="onboarding-longpress-button" class="onboarding-button">Done</a>
</div>
<div class="panel achievement-panel hide" id="achievement-panel">
<img class="onboarding-img" alt="You achieved a Containers milestone!" src="/img/onboarding-3.png" />
<h3 class="onboarding-title">100 tabs!</h3>
<p>You've opened 100 Container tabs.</p>
<p>If you enjoy Containers, help us spread the word!</p>
<p class="share-ctas">
<a class="cta-link" href="https://mzl.la/2gJtIZ4" id="achievement-rate-button" target="_blank">
<span class="cta amo-rate-cta">
<img src="/img/amo-icon.svg" class="cta-icon" alt="addons.mozilla.org Icon">
Rate
</span>
</a>
<a class="cta-link" href="https://bit.ly/fb-share-mac-addon" target="_blank">
<span class="cta fb-share-cta">
<img src="/img/webicon-facebook.svg" class="cta-icon" alt="Facebook Icon">
Share
</span>
</a>
<a class="cta-link" href="http://bit.ly/tweet-100-tabs-mac-addon" target="_blank">
<span class="cta tweet-cta">
<img src="/img/webicon-twitter.svg" class="cta-icon" alt="Twitter Icon">
Tweet
</span>
</a>
</p>
<a href="#" id="achievement-done-button" class="onboarding-button">Done</a>
</div>
<div class="panel container-panel hide" id="container-panel">
<div id="current-tab">
<h3>Current Tab</h3>
<div id="current-page"></div>
<label for="container-page-assigned">
<input type="checkbox" id="container-page-assigned" />
<span class="truncate-text">
Always open in
<span id="current-container"></span>
</span>
</label>
</div>
<div class="container-panel-controls">
<a href="#" class="action-link" id="sort-containers-link" title="Sort tabs into container order">Sort Tabs</a>
</div>
<div class="scrollable panel-content" tabindex="-1">
<table class="identities-list">
<tbody></tbody>
</table>
</div>
<div class="panel-footer edit-identities">
<div class="edit-containers-text panel-footer-secondary">
<a href="#" tabindex="0" id="edit-containers-link">Edit Containers</a>
</div>
<a href="#" tabindex="0" class="add-container-link pop-button" id="container-add-link" title="Create new container">
<img class="pop-button-image-small icon" alt="Create new container icon" src="/img/container-add.svg" />
</a>
</div>
</div>
<div class="hide panel container-info-panel" id="container-info-panel" tabindex="-1">
<div class="columns">
<div class="panel-back-arrow" id="close-container-info-panel">
<img alt="Panel Back Arrow" src="/img/container-arrow.svg" class="back-arrow-img" />
</div>
<div class="column-panel-content">
<div class="panel-header container-info-panel-header">
<span class="usercontext-icon" id="container-info-icon"></span>
<h3 id="container-info-name" class="panel-header-text container-name truncate-text"></h3>
</div>
<div class="select-row clickable container-info-panel-hide container-info-has-tabs" id="container-info-hideorshow">
<img id="container-info-hideorshow-icon" alt="Hide Container icon" src="/img/container-hide.svg" class="icon container-info-panel-hideorshow-icon"/>
<span id="container-info-hideorshow-label">Hide this container</span>
</div>
<div class="select-row clickable container-info-panel-movetabs container-info-has-tabs" id="container-info-movetabs">Move tabs to a new window</div>
<div class="scrollable">
<table id="container-info-table" class="container-info-list">
</table>
</div>
</div>
</div>
</div>
<div class="panel edit-containers-panel hide" id="edit-containers-panel">
<div class="panel-header">
<h3 class="panel-header-text">Edit Containers</h3>
</div>
<div class="scrollable panel-content">
<table class="unstriped">
<tbody id="edit-identities-list"></tbody>
</table>
</div>
<div class="panel-footer edit-containers-panel-footer">
<a href="#" id="exit-edit-mode-link" class="exit-edit-mode-link edit-containers-exit-text">Exit Edit Mode</a>
</div>
</div>
<div class="panel edit-container-panel hide" id="edit-container-panel">
<div class="columns">
<div class="panel-back-arrow" id="edit-container-panel-back-arrow">
<img alt="Panel Back Arrow" src="/img/container-arrow.svg" class="back-arrow-img" />
</div>
<div class="column-panel-content">
<form id="edit-container-panel-form">
<input type="hidden" name="container-id" id="edit-container-panel-usercontext-input" />
<fieldset>
<legend>Name</legend>
<input type="text" name="container-name" id="edit-container-panel-name-input" maxlength="25"/>
</fieldset>
<fieldset id="edit-container-panel-choose-color" class="radio-choice">
<legend>Choose a color</legend>
</fieldset>
<fieldset id="edit-container-panel-choose-icon" class="radio-choice">
<legend>Choose an icon</legend>
</fieldset>
</form>
<div id="edit-sites-assigned" class="scrollable" hidden>
<h3>Sites assigned to this container</h3>
<div class="assigned-sites-list">
</div>
</div>
<div class="panel-footer">
<a href="#" class="button secondary expanded footer-button cancel-button" id="edit-container-cancel-link">Cancel</a>
<a class="button primary expanded footer-button" id="edit-container-ok-link">OK</a>
</div>
</div>
</div>
</div>
<div class="hide panel delete-container-panel" id="delete-container-panel">
<div class="panel-header">
<span class="usercontext-icon" id="delete-container-icon"></span>
<h3 id="delete-container-name" class="panel-header-text container-name"></h3>
</div>
<div class="panel-content delete-container-confirm">
<h4 class="delete-container-confirm-title">Remove This Container</h4>
<p>If you remove this container now, <span id="delete-container-tab-count"></span> container tabs will be closed. Are you sure you want to remove this Container?</p>
</div>
<div class="panel-footer">
<a href="#" class="button expanded secondary footer-button cancel-button" id="delete-container-cancel-link">Cancel</a>
<a href="#" class="button expanded primary footer-button" id="delete-container-ok-link">OK</a>
</div>
</div>
<script src="js/utils.js"></script>
<script src="js/popup.js"></script>
</body>
</html>