summaryrefslogtreecommitdiffstats
path: root/browser/components/contextualidentity
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/contextualidentity')
-rw-r--r--browser/components/contextualidentity/content/briefcase.svg11
-rw-r--r--browser/components/contextualidentity/content/cart.svg13
-rw-r--r--browser/components/contextualidentity/content/chill.svg14
-rw-r--r--browser/components/contextualidentity/content/circle.svg8
-rw-r--r--browser/components/contextualidentity/content/dollar.svg14
-rw-r--r--browser/components/contextualidentity/content/fence.svg7
-rw-r--r--browser/components/contextualidentity/content/fingerprint.svg8
-rw-r--r--browser/components/contextualidentity/content/food.svg10
-rw-r--r--browser/components/contextualidentity/content/fruit.svg10
-rw-r--r--browser/components/contextualidentity/content/gift.svg12
-rw-r--r--browser/components/contextualidentity/content/pet.svg12
-rw-r--r--browser/components/contextualidentity/content/tree.svg10
-rw-r--r--browser/components/contextualidentity/content/usercontext.css139
-rw-r--r--browser/components/contextualidentity/content/vacation.svg20
-rw-r--r--browser/components/contextualidentity/jar.mn21
-rw-r--r--browser/components/contextualidentity/moz.build14
-rw-r--r--browser/components/contextualidentity/test/browser/blank.html2
-rw-r--r--browser/components/contextualidentity/test/browser/browser.ini59
-rw-r--r--browser/components/contextualidentity/test/browser/browser_aboutURLs.js70
-rw-r--r--browser/components/contextualidentity/test/browser/browser_blobUrl.js92
-rw-r--r--browser/components/contextualidentity/test/browser/browser_broadcastchannel.js83
-rw-r--r--browser/components/contextualidentity/test/browser/browser_count_and_remove.js111
-rw-r--r--browser/components/contextualidentity/test/browser/browser_eme.js223
-rw-r--r--browser/components/contextualidentity/test/browser/browser_favicon.js150
-rw-r--r--browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js257
-rw-r--r--browser/components/contextualidentity/test/browser/browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js95
-rw-r--r--browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js170
-rw-r--r--browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js425
-rw-r--r--browser/components/contextualidentity/test/browser/browser_imageCache.js69
-rw-r--r--browser/components/contextualidentity/test/browser/browser_middleClick.js50
-rw-r--r--browser/components/contextualidentity/test/browser/browser_newtabButton.js214
-rw-r--r--browser/components/contextualidentity/test/browser/browser_originattrs_reopenin.js184
-rw-r--r--browser/components/contextualidentity/test/browser/browser_relatedTab.js94
-rw-r--r--browser/components/contextualidentity/test/browser/browser_reopenIn.js164
-rw-r--r--browser/components/contextualidentity/test/browser/browser_restore_getCookiesWithOriginAttributes.js122
-rw-r--r--browser/components/contextualidentity/test/browser/browser_saveLink.js135
-rw-r--r--browser/components/contextualidentity/test/browser/browser_serviceworkers.js121
-rw-r--r--browser/components/contextualidentity/test/browser/browser_tab_color_update.js42
-rw-r--r--browser/components/contextualidentity/test/browser/browser_usercontext.js88
-rw-r--r--browser/components/contextualidentity/test/browser/browser_usercontextid_new_window.js93
-rw-r--r--browser/components/contextualidentity/test/browser/browser_usercontextid_tabdrop.js181
-rw-r--r--browser/components/contextualidentity/test/browser/browser_windowName.js80
-rw-r--r--browser/components/contextualidentity/test/browser/browser_windowOpen.js41
-rw-r--r--browser/components/contextualidentity/test/browser/empty_file.html5
-rw-r--r--browser/components/contextualidentity/test/browser/favicon-normal32.pngbin0 -> 344 bytes
-rw-r--r--browser/components/contextualidentity/test/browser/file_reflect_cookie_into_title.html22
-rw-r--r--browser/components/contextualidentity/test/browser/file_set_storages.html41
-rw-r--r--browser/components/contextualidentity/test/browser/head.js48
-rw-r--r--browser/components/contextualidentity/test/browser/saveLink.sjs55
-rw-r--r--browser/components/contextualidentity/test/browser/serviceworker.html12
-rw-r--r--browser/components/contextualidentity/test/browser/worker.js1
51 files changed, 3922 insertions, 0 deletions
diff --git a/browser/components/contextualidentity/content/briefcase.svg b/browser/components/contextualidentity/content/briefcase.svg
new file mode 100644
index 0000000000..ab1b969df0
--- /dev/null
+++ b/browser/components/contextualidentity/content/briefcase.svg
@@ -0,0 +1,11 @@
+<!-- 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"
+ width="32" height="32" viewBox="0 0 32 32">
+ <path fill="context-fill" fill-rule="evenodd"
+ 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"/>
+</svg>
+
diff --git a/browser/components/contextualidentity/content/cart.svg b/browser/components/contextualidentity/content/cart.svg
new file mode 100644
index 0000000000..af432d1666
--- /dev/null
+++ b/browser/components/contextualidentity/content/cart.svg
@@ -0,0 +1,13 @@
+<!-- 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"
+ width="32" height="32" viewBox="0 0 32 32">
+ <path fill="context-fill" fill-rule="evenodd"
+ 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"/>
+</svg>
+
diff --git a/browser/components/contextualidentity/content/chill.svg b/browser/components/contextualidentity/content/chill.svg
new file mode 100644
index 0000000000..375e16040a
--- /dev/null
+++ b/browser/components/contextualidentity/content/chill.svg
@@ -0,0 +1,14 @@
+<!-- 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"
+ width="32" height="32" viewBox="0 0 32 32">
+ <path fill="context-fill" 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"/>
+</svg>
+
diff --git a/browser/components/contextualidentity/content/circle.svg b/browser/components/contextualidentity/content/circle.svg
new file mode 100644
index 0000000000..24bd6dc601
--- /dev/null
+++ b/browser/components/contextualidentity/content/circle.svg
@@ -0,0 +1,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"
+ width="32" height="32" viewBox="0 0 32 32">
+ <circle fill="context-fill" cx="16" cy="16" r="16"/>
+</svg>
+
diff --git a/browser/components/contextualidentity/content/dollar.svg b/browser/components/contextualidentity/content/dollar.svg
new file mode 100644
index 0000000000..fd3597f2e2
--- /dev/null
+++ b/browser/components/contextualidentity/content/dollar.svg
@@ -0,0 +1,14 @@
+<!-- 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"
+ width="32" height="32" viewBox="0 0 32 32">
+ <path fill="context-fill" 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"/>
+</svg>
+
diff --git a/browser/components/contextualidentity/content/fence.svg b/browser/components/contextualidentity/content/fence.svg
new file mode 100644
index 0000000000..0fdec6b104
--- /dev/null
+++ b/browser/components/contextualidentity/content/fence.svg
@@ -0,0 +1,7 @@
+<!-- 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"
+ width="32" height="32" viewBox="0 0 32 32">
+ <path fill="context-fill" d="M28,4l-2,2v4h-4V6l-2-2l-2,2v4h-4V6l-2-2l-2,2v4H6V6L4,4L2,6v22h4v-4h4v4h4v-4h4v4h4v-4h4v4h4V6L28,4z M6,22V12 h4v10H6z M14,22V12h4v10H14z M22,22V12h4v10H22z"/>
+</svg>
diff --git a/browser/components/contextualidentity/content/fingerprint.svg b/browser/components/contextualidentity/content/fingerprint.svg
new file mode 100644
index 0000000000..58dd78cd09
--- /dev/null
+++ b/browser/components/contextualidentity/content/fingerprint.svg
@@ -0,0 +1,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"
+ width="32" height="32" viewBox="0 0 32 32">
+ <path fill="context-fill" 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"/>
+</svg>
+
diff --git a/browser/components/contextualidentity/content/food.svg b/browser/components/contextualidentity/content/food.svg
new file mode 100644
index 0000000000..a97a44a64b
--- /dev/null
+++ b/browser/components/contextualidentity/content/food.svg
@@ -0,0 +1,10 @@
+<!-- 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"
+ width="32" height="32" viewBox="0 0 32 32">
+ <path fill="context-fill" 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"/>
+</svg>
+
diff --git a/browser/components/contextualidentity/content/fruit.svg b/browser/components/contextualidentity/content/fruit.svg
new file mode 100644
index 0000000000..38b482a226
--- /dev/null
+++ b/browser/components/contextualidentity/content/fruit.svg
@@ -0,0 +1,10 @@
+<!-- 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"
+ width="32" height="32" viewBox="0 0 32 32">
+ <path fill="context-fill" 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"/>
+</svg>
+
diff --git a/browser/components/contextualidentity/content/gift.svg b/browser/components/contextualidentity/content/gift.svg
new file mode 100644
index 0000000000..0f03f10b66
--- /dev/null
+++ b/browser/components/contextualidentity/content/gift.svg
@@ -0,0 +1,12 @@
+<!-- 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"
+ width="32" height="32" viewBox="0 0 32 32">
+ <path fill="context-fill" 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"/>
+</svg>
+
diff --git a/browser/components/contextualidentity/content/pet.svg b/browser/components/contextualidentity/content/pet.svg
new file mode 100644
index 0000000000..42e01ea590
--- /dev/null
+++ b/browser/components/contextualidentity/content/pet.svg
@@ -0,0 +1,12 @@
+<!-- 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"
+ width="32" height="32" viewBox="0 0 32 32">
+ <path fill="context-fill" 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"/>
+</svg>
+
diff --git a/browser/components/contextualidentity/content/tree.svg b/browser/components/contextualidentity/content/tree.svg
new file mode 100644
index 0000000000..478c0d518f
--- /dev/null
+++ b/browser/components/contextualidentity/content/tree.svg
@@ -0,0 +1,10 @@
+<!-- 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"
+ width="32" height="32" viewBox="0 0 32 32">
+ <path fill="context-fill" 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"/>
+</svg>
+
diff --git a/browser/components/contextualidentity/content/usercontext.css b/browser/components/contextualidentity/content/usercontext.css
new file mode 100644
index 0000000000..53f7b8ebba
--- /dev/null
+++ b/browser/components/contextualidentity/content/usercontext.css
@@ -0,0 +1,139 @@
+/* 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/. */
+
+.identity-color-blue {
+ --identity-tab-color: #37adff;
+ --identity-icon-color: #37adff;
+}
+
+.identity-color-turquoise {
+ --identity-tab-color: #00c79a;
+ --identity-icon-color: #00c79a;
+}
+
+.identity-color-green {
+ --identity-tab-color: #51cd00;
+ --identity-icon-color: #51cd00;
+}
+
+.identity-color-yellow {
+ --identity-tab-color: #ffcb00;
+ --identity-icon-color: #ffcb00;
+}
+
+.identity-color-orange {
+ --identity-tab-color: #ff9f00;
+ --identity-icon-color: #ff9f00;
+}
+
+.identity-color-red {
+ --identity-tab-color: #ff613d;
+ --identity-icon-color: #ff613d;
+}
+
+.identity-color-pink {
+ --identity-tab-color: #ff4bda;
+ --identity-icon-color: #ff4bda;
+}
+
+.identity-color-purple {
+ --identity-tab-color: #af51f5;
+ --identity-icon-color: #af51f5;
+}
+
+.identity-color-toolbar {
+ --identity-tab-color: var(--toolbar-field-color);
+ --identity-icon-color: var(--toolbar-field-color);
+}
+
+.identity-icon-fence {
+ --identity-icon: url("resource://usercontext-content/fence.svg");
+}
+
+.identity-icon-fingerprint {
+ --identity-icon: url("resource://usercontext-content/fingerprint.svg");
+}
+
+.identity-icon-briefcase {
+ --identity-icon: url("resource://usercontext-content/briefcase.svg");
+}
+
+.identity-icon-dollar {
+ --identity-icon: url("resource://usercontext-content/dollar.svg");
+}
+
+.identity-icon-cart {
+ --identity-icon: url("resource://usercontext-content/cart.svg");
+}
+
+.identity-icon-circle {
+ --identity-icon: url("resource://usercontext-content/circle.svg");
+}
+
+.identity-icon-vacation {
+ --identity-icon: url("resource://usercontext-content/vacation.svg");
+}
+
+.identity-icon-gift {
+ --identity-icon: url("resource://usercontext-content/gift.svg");
+}
+
+.identity-icon-food {
+ --identity-icon: url("resource://usercontext-content/food.svg");
+}
+
+.identity-icon-fruit {
+ --identity-icon: url("resource://usercontext-content/fruit.svg");
+}
+
+.identity-icon-pet {
+ --identity-icon: url("resource://usercontext-content/pet.svg");
+}
+
+.identity-icon-tree {
+ --identity-icon: url("resource://usercontext-content/tree.svg");
+}
+
+.identity-icon-chill {
+ --identity-icon: url("resource://usercontext-content/chill.svg");
+}
+
+#userContext-indicator {
+ height: 16px;
+ width: 16px;
+ margin-inline-start: 3px;
+}
+
+#userContext-label {
+ margin: 0;
+ color: var(--identity-tab-color);
+}
+
+#userContext-icons {
+ -moz-box-align: center;
+}
+
+.tabbrowser-tab[usercontextid] > .tab-stack > .tab-background > .tab-context-line {
+ background-color: var(--identity-icon-color);
+ height: 2px;
+ border-radius: 2px;
+ margin: -3px 2px 0;
+}
+
+.userContext-icon,
+.subviewbutton[usercontextid] > .toolbarbutton-icon,
+#userContext-indicator {
+ background-image: var(--identity-icon);
+ -moz-context-properties: fill;
+ fill: var(--identity-icon-color);
+ background-size: contain;
+ background-repeat: no-repeat;
+ background-position: center center;
+}
+
+.menuitem-iconic[data-usercontextid] {
+ list-style-image: var(--identity-icon);
+ -moz-context-properties: fill;
+ fill: var(--identity-icon-color);
+}
diff --git a/browser/components/contextualidentity/content/vacation.svg b/browser/components/contextualidentity/content/vacation.svg
new file mode 100644
index 0000000000..ea6dc1e299
--- /dev/null
+++ b/browser/components/contextualidentity/content/vacation.svg
@@ -0,0 +1,20 @@
+<!-- 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"
+ width="32" height="32" viewBox="0 0 32 32">
+ <path fill="context-fill" 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"
+ />
+</svg>
+
diff --git a/browser/components/contextualidentity/jar.mn b/browser/components/contextualidentity/jar.mn
new file mode 100644
index 0000000000..d939affab0
--- /dev/null
+++ b/browser/components/contextualidentity/jar.mn
@@ -0,0 +1,21 @@
+# 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/.
+
+browser.jar:
+ content/browser/usercontext/usercontext.css (content/usercontext.css)
+
+% resource usercontext-content %content/ contentaccessible=yes
+ content/briefcase.svg (content/briefcase.svg)
+ content/cart.svg (content/cart.svg)
+ content/circle.svg (content/circle.svg)
+ content/dollar.svg (content/dollar.svg)
+ content/fence.svg (content/fence.svg)
+ content/fingerprint.svg (content/fingerprint.svg)
+ content/gift.svg (content/gift.svg)
+ content/vacation.svg (content/vacation.svg)
+ content/food.svg (content/food.svg)
+ content/fruit.svg (content/fruit.svg)
+ content/pet.svg (content/pet.svg)
+ content/tree.svg (content/tree.svg)
+ content/chill.svg (content/chill.svg)
diff --git a/browser/components/contextualidentity/moz.build b/browser/components/contextualidentity/moz.build
new file mode 100644
index 0000000000..4101066ad3
--- /dev/null
+++ b/browser/components/contextualidentity/moz.build
@@ -0,0 +1,14 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+BROWSER_CHROME_MANIFESTS += [
+ "test/browser/browser.ini",
+]
+
+JAR_MANIFESTS += ["jar.mn"]
+
+with Files("**"):
+ BUG_COMPONENT = ("Core", "DOM: Security")
diff --git a/browser/components/contextualidentity/test/browser/blank.html b/browser/components/contextualidentity/test/browser/blank.html
new file mode 100644
index 0000000000..bcc2e389b8
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/blank.html
@@ -0,0 +1,2 @@
+<!doctype html>
+This page intentionally left blank.
diff --git a/browser/components/contextualidentity/test/browser/browser.ini b/browser/components/contextualidentity/test/browser/browser.ini
new file mode 100644
index 0000000000..77b1018ea8
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser.ini
@@ -0,0 +1,59 @@
+[DEFAULT]
+support-files =
+ head.js
+ empty_file.html
+ file_reflect_cookie_into_title.html
+ favicon-normal32.png
+ file_set_storages.html
+ serviceworker.html
+ worker.js
+ blank.html
+
+[browser_aboutURLs.js]
+skip-if =
+ os == 'linux' && bits == 64 && !debug # Bug 1731442
+[browser_eme.js]
+skip-if = (os == "win" && processor == "aarch64") # bug 1531927
+[browser_favicon.js]
+[browser_forgetaboutsite.js]
+skip-if = true # Bug 1541885
+[browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js]
+[browser_restore_getCookiesWithOriginAttributes.js]
+[browser_forgetAPI_EME_forgetThisSite.js]
+skip-if = (os == "win" && processor == "aarch64") # bug 1531927
+[browser_forgetAPI_quota_clearStoragesForPrincipal.js]
+https_first_disabled = true
+skip-if = verify
+[browser_newtabButton.js]
+[browser_reopenIn.js]
+https_first_disabled = true
+[browser_tab_color_update.js]
+[browser_usercontext.js]
+[browser_usercontextid_new_window.js]
+[browser_usercontextid_tabdrop.js]
+https_first_disabled = true
+skip-if = os == "mac" || os == "win" # Intermittent failure - bug 1268276
+[browser_windowName.js]
+tags = openwindow
+[browser_windowOpen.js]
+tags = openwindow
+[browser_serviceworkers.js]
+[browser_broadcastchannel.js]
+[browser_blobUrl.js]
+[browser_middleClick.js]
+skip-if =
+ (verify && debug && (os == 'linux'))
+ apple_silicon # Disabled due to bleedover with other tests when run in regular suites; passes in "failures" jobs
+ os == 'linux' && socketprocess_networking
+ win10_2004 && !debug
+[browser_imageCache.js]
+skip-if = (verify && debug && (os == 'win'))
+[browser_count_and_remove.js]
+[browser_relatedTab.js]
+[browser_saveLink.js]
+skip-if = (verify && !debug && (os == 'win'))
+support-files =
+ saveLink.sjs
+ !/toolkit/content/tests/browser/common/mockTransfer.js
+[browser_originattrs_reopenin.js]
+https_first_disabled = true
diff --git a/browser/components/contextualidentity/test/browser/browser_aboutURLs.js b/browser/components/contextualidentity/test/browser/browser_aboutURLs.js
new file mode 100644
index 0000000000..51e0258be1
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_aboutURLs.js
@@ -0,0 +1,70 @@
+"use strict";
+
+// For some about: URLs, they will take more time to load and cause timeout.
+// See Bug 1270998.
+requestLongerTimeout(2);
+
+add_task(async function() {
+ let aboutURLs = [];
+
+ // List of about: URLs that may cause problem, so we skip them in this test.
+ let skipURLs = [
+ // about:addons triggers an assertion in NS_CompareLoadInfoAndLoadContext:
+ // "The value of mUserContextId in the loadContext and in the loadInfo are not the same"
+ // This is due to a fetch request that has the default user context. Since
+ // the fetch request omits credentials, the user context doesn't matter.
+ "addons",
+ // about:credits and about:logins will initiate network request.
+ "credits",
+ "logins",
+ // about:telemetry will fetch Telemetry asynchronously and takes longer,
+ // so we skip this for now.
+ "telemetry",
+ // about:downloads causes a shutdown leak with stylo-chrome. bug 1419943.
+ "downloads",
+ // about:debugging requires specific wait code for internal pending RDP requests.
+ "debugging",
+ "debugging-new",
+ // about:protections uses RPM to send a message as soon as the page loads,
+ // the page is destoryed before getting a response.
+ "protections",
+ ];
+
+ for (let cid in Cc) {
+ let result = cid.match(
+ /@mozilla.org\/network\/protocol\/about;1\?what\=(.*)$/
+ );
+ if (!result) {
+ continue;
+ }
+
+ let aboutType = result[1];
+ let contract = "@mozilla.org/network/protocol/about;1?what=" + aboutType;
+ try {
+ let am = Cc[contract].getService(Ci.nsIAboutModule);
+ let uri = Services.io.newURI("about:" + aboutType);
+ let flags = am.getURIFlags(uri);
+ if (
+ !(flags & Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT) &&
+ !skipURLs.includes(aboutType)
+ ) {
+ aboutURLs.push(aboutType);
+ }
+ } catch (e) {
+ // getService might have thrown if the component doesn't actually
+ // implement nsIAboutModule
+ }
+ }
+
+ for (let url of aboutURLs) {
+ info("Loading about:" + url);
+ let tab = BrowserTestUtils.addTab(gBrowser, "about:" + url, {
+ userContextId: 1,
+ });
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ ok(true, "Done loading about:" + url);
+
+ BrowserTestUtils.removeTab(tab);
+ }
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_blobUrl.js b/browser/components/contextualidentity/test/browser/browser_blobUrl.js
new file mode 100644
index 0000000000..4469f56fc3
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_blobUrl.js
@@ -0,0 +1,92 @@
+"use strict";
+
+// Here we want to test that blob URLs are not available cross containers.
+
+const BASE_URI =
+ "http://mochi.test:8888/browser/browser/components/" +
+ "contextualidentity/test/browser/empty_file.html";
+
+add_setup(async function() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+});
+
+add_task(async function test() {
+ info("Creating a tab with UCI = 1...");
+ let tab1 = BrowserTestUtils.addTab(gBrowser, BASE_URI, { userContextId: 1 });
+ is(tab1.getAttribute("usercontextid"), "1", "New tab has UCI equal 1");
+
+ let browser1 = gBrowser.getBrowserForTab(tab1);
+ await BrowserTestUtils.browserLoaded(browser1);
+
+ let blobURL;
+
+ info("Creating a blob URL...");
+ await SpecialPowers.spawn(browser1, [], function() {
+ return Promise.resolve(
+ content.window.URL.createObjectURL(new content.window.Blob([123]))
+ );
+ }).then(newURL => {
+ blobURL = newURL;
+ });
+
+ info("Blob URL: " + blobURL);
+
+ info("Creating a tab with UCI = 2...");
+ let tab2 = BrowserTestUtils.addTab(gBrowser, BASE_URI, { userContextId: 2 });
+ is(tab2.getAttribute("usercontextid"), "2", "New tab has UCI equal 2");
+
+ let browser2 = gBrowser.getBrowserForTab(tab2);
+ await BrowserTestUtils.browserLoaded(browser2);
+
+ await SpecialPowers.spawn(browser2, [blobURL], function(url) {
+ return new Promise(resolve => {
+ var xhr = new content.window.XMLHttpRequest();
+ xhr.onerror = function() {
+ resolve("SendErrored");
+ };
+ xhr.onload = function() {
+ resolve("SendLoaded");
+ };
+ xhr.open("GET", url);
+ xhr.send();
+ });
+ }).then(status => {
+ is(
+ status,
+ "SendErrored",
+ "Using a blob URI from one user context id in another should not work"
+ );
+ });
+
+ info("Creating a tab with UCI = 1...");
+ let tab3 = BrowserTestUtils.addTab(gBrowser, BASE_URI, { userContextId: 1 });
+ is(tab3.getAttribute("usercontextid"), "1", "New tab has UCI equal 1");
+
+ let browser3 = gBrowser.getBrowserForTab(tab3);
+ await BrowserTestUtils.browserLoaded(browser3);
+
+ await SpecialPowers.spawn(browser3, [blobURL], function(url) {
+ return new Promise(resolve => {
+ var xhr = new content.window.XMLHttpRequest();
+ xhr.open("GET", url);
+ try {
+ xhr.send();
+ resolve("SendSucceeded");
+ } catch (e) {
+ resolve("SendThrew");
+ }
+ });
+ }).then(status => {
+ is(
+ status,
+ "SendSucceeded",
+ "Using a blob URI within a single user context id should work"
+ );
+ });
+
+ BrowserTestUtils.removeTab(tab1);
+ BrowserTestUtils.removeTab(tab2);
+ BrowserTestUtils.removeTab(tab3);
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_broadcastchannel.js b/browser/components/contextualidentity/test/browser/browser_broadcastchannel.js
new file mode 100644
index 0000000000..cc3354fbeb
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_broadcastchannel.js
@@ -0,0 +1,83 @@
+const BASE_ORIGIN = "http://example.com";
+const URI =
+ BASE_ORIGIN +
+ "/browser/browser/components/contextualidentity/test/browser/empty_file.html";
+
+// Opens `uri' in a new tab with the provided userContextId and focuses it.
+// Returns the newly opened tab and browser.
+async function openTabInUserContext(uri, userContextId) {
+ // open the tab in the correct userContextId
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // select tab and make sure its browser is focused
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+ return { tab, browser };
+}
+
+async function runTestForReceiver(receiver) {
+ let channelName = "contextualidentity-broadcastchannel";
+
+ // reflect the received message on title
+ await SpecialPowers.spawn(receiver.browser, [channelName], function(name) {
+ content.window.testPromise = new content.window.Promise(resolve => {
+ content.window.bc = new content.window.BroadcastChannel(name);
+ content.window.bc.onmessage = function(e) {
+ content.document.title += e.data;
+ resolve();
+ };
+ });
+ });
+
+ let sender1 = await openTabInUserContext(URI, 1);
+ let sender2 = await openTabInUserContext(URI, 2);
+ sender1.message = "Message from user context #1";
+ sender2.message = "Message from user context #2";
+
+ // send a message from a tab in different user context first
+ // then send a message from a tab in the same user context
+ for (let sender of [sender1, sender2]) {
+ await SpecialPowers.spawn(
+ sender.browser,
+ [{ name: channelName, message: sender.message }],
+ function(opts) {
+ let bc = new content.window.BroadcastChannel(opts.name);
+ bc.postMessage(opts.message);
+ }
+ );
+ }
+
+ // Since sender1 sends before sender2, if the title is exactly
+ // sender2's message, sender1's message must've been blocked
+ await SpecialPowers.spawn(receiver.browser, [sender2.message], async function(
+ message
+ ) {
+ await content.window.testPromise.then(function() {
+ is(
+ content.document.title,
+ message,
+ "should only receive messages from the same user context"
+ );
+ });
+ });
+
+ gBrowser.removeTab(sender1.tab);
+ gBrowser.removeTab(sender2.tab);
+}
+
+add_setup(async function() {
+ // make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+});
+
+add_task(async function test() {
+ info("Checking broadcast channel with browser tab receiver");
+ let receiver = await openTabInUserContext(URI, 2);
+ await runTestForReceiver(receiver);
+ gBrowser.removeTab(receiver.tab);
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_count_and_remove.js b/browser/components/contextualidentity/test/browser/browser_count_and_remove.js
new file mode 100644
index 0000000000..795bf1e2b2
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_count_and_remove.js
@@ -0,0 +1,111 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const { ContextualIdentityService } = ChromeUtils.importESModule(
+ "resource://gre/modules/ContextualIdentityService.sys.mjs"
+);
+
+function openTabInUserContext(userContextId) {
+ let tab = BrowserTestUtils.addTab(gBrowser, "about:blank", { userContextId });
+ gBrowser.selectedTab = tab;
+}
+
+add_setup(async function() {
+ // make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+});
+
+add_task(async function test() {
+ is(
+ ContextualIdentityService.countContainerTabs(),
+ 0,
+ "0 container tabs by default."
+ );
+
+ openTabInUserContext(1);
+ is(
+ ContextualIdentityService.countContainerTabs(),
+ 1,
+ "1 container tab created"
+ );
+ is(
+ ContextualIdentityService.countContainerTabs(1),
+ 1,
+ "1 container tab created with id 1"
+ );
+ is(
+ ContextualIdentityService.countContainerTabs(2),
+ 0,
+ "0 container tabs created with id 2"
+ );
+
+ openTabInUserContext(1);
+ is(
+ ContextualIdentityService.countContainerTabs(),
+ 2,
+ "2 container tabs created"
+ );
+ is(
+ ContextualIdentityService.countContainerTabs(1),
+ 2,
+ "2 container tabs created with id 1"
+ );
+ is(
+ ContextualIdentityService.countContainerTabs(2),
+ 0,
+ "0 container tabs created with id 2"
+ );
+
+ openTabInUserContext(2);
+ is(
+ ContextualIdentityService.countContainerTabs(),
+ 3,
+ "3 container tab created"
+ );
+ is(
+ ContextualIdentityService.countContainerTabs(1),
+ 2,
+ "2 container tabs created with id 1"
+ );
+ is(
+ ContextualIdentityService.countContainerTabs(2),
+ 1,
+ "1 container tab created with id 2"
+ );
+
+ await ContextualIdentityService.closeContainerTabs(1);
+ is(
+ ContextualIdentityService.countContainerTabs(),
+ 1,
+ "1 container tab created"
+ );
+ is(
+ ContextualIdentityService.countContainerTabs(1),
+ 0,
+ "0 container tabs created with id 1"
+ );
+ is(
+ ContextualIdentityService.countContainerTabs(2),
+ 1,
+ "1 container tab created with id 2"
+ );
+
+ await ContextualIdentityService.closeContainerTabs();
+ is(
+ ContextualIdentityService.countContainerTabs(),
+ 0,
+ "0 container tabs at the end."
+ );
+ is(
+ ContextualIdentityService.countContainerTabs(1),
+ 0,
+ "0 container tabs at the end with id 1."
+ );
+ is(
+ ContextualIdentityService.countContainerTabs(2),
+ 0,
+ "0 container tabs at the end with id 2."
+ );
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_eme.js b/browser/components/contextualidentity/test/browser/browser_eme.js
new file mode 100644
index 0000000000..62c2b22d38
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_eme.js
@@ -0,0 +1,223 @@
+/*
+ * Bug 1283325 - A test case to test the EME is originAttributes aware or not.
+ */
+const CC = Components.Constructor;
+
+const TEST_HOST = "example.com";
+const TEST_URL =
+ "http://" +
+ TEST_HOST +
+ "/browser/browser/components/contextualidentity/test/browser/";
+
+const TESTKEY = {
+ initDataType: "keyids",
+ initData: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A"], "type":"persistent-license"}',
+ kid: "LwVHf8JLtPrv2GUXFW2v_A",
+ key: "97b9ddc459c8d5ff23c1f2754c95abe8",
+ sessionType: "persistent-license",
+};
+
+const USER_ID_DEFAULT = 0;
+const USER_ID_PERSONAL = 1;
+
+async function openTabInUserContext(uri, userContextId) {
+ // Open the tab in the correct userContextId.
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // Select tab and make sure its browser is focused.
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+ return { tab, browser };
+}
+
+function HexToBase64(hex) {
+ var bin = "";
+ for (var i = 0; i < hex.length; i += 2) {
+ bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
+ }
+ return window
+ .btoa(bin)
+ .replace(/=/g, "")
+ .replace(/\+/g, "-")
+ .replace(/\//g, "_");
+}
+
+function Base64ToHex(str) {
+ var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/"));
+ var res = "";
+ for (var i = 0; i < bin.length; i++) {
+ res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
+ }
+ return res;
+}
+
+function ByteArrayToHex(array) {
+ let bin = String.fromCharCode.apply(null, new Uint8Array(array));
+ let res = "";
+
+ for (let i = 0; i < bin.length; i++) {
+ res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
+ }
+
+ return res;
+}
+
+function generateKeyObject(aKid, aKey) {
+ let keyObj = {
+ kty: "oct",
+ kid: aKid,
+ k: HexToBase64(aKey),
+ };
+
+ return new TextEncoder().encode(
+ JSON.stringify({
+ keys: [keyObj],
+ })
+ );
+}
+
+function generateKeyInfo(aData) {
+ let keyInfo = {
+ initDataType: aData.initDataType,
+ initData: new TextEncoder().encode(aData.initData),
+ sessionType: aData.sessionType,
+ keyObj: generateKeyObject(aData.kid, aData.key),
+ };
+
+ return keyInfo;
+}
+
+add_setup(async function() {
+ // Make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ ["media.mediasource.enabled", true],
+ ["media.mediasource.webm.enabled", true],
+ ["media.clearkey.persistent-license.enabled", true],
+ ],
+ });
+});
+
+add_task(async function test() {
+ // Open a tab with the default container.
+ let defaultContainer = await openTabInUserContext(
+ TEST_URL + "empty_file.html",
+ USER_ID_DEFAULT
+ );
+
+ // Generate the key info for the default container.
+ let keyInfo = generateKeyInfo(TESTKEY);
+
+ // Update the media key for the default container.
+ let result = await SpecialPowers.spawn(
+ defaultContainer.browser,
+ [keyInfo],
+ async function(aKeyInfo) {
+ let access = await content.navigator.requestMediaKeySystemAccess(
+ "org.w3.clearkey",
+ [
+ {
+ initDataTypes: [aKeyInfo.initDataType],
+ videoCapabilities: [{ contentType: "video/webm" }],
+ sessionTypes: ["persistent-license"],
+ persistentState: "required",
+ },
+ ]
+ );
+ let mediaKeys = await access.createMediaKeys();
+ let session = mediaKeys.createSession(aKeyInfo.sessionType);
+ let res = {};
+
+ // Insert the media key.
+ await new Promise(resolve => {
+ session.addEventListener("message", function(event) {
+ session
+ .update(aKeyInfo.keyObj)
+ .then(() => {
+ resolve();
+ })
+ .catch(() => {
+ ok(false, "Update the media key fail.");
+ resolve();
+ });
+ });
+
+ session.generateRequest(aKeyInfo.initDataType, aKeyInfo.initData);
+ });
+
+ let map = session.keyStatuses;
+
+ is(map.size, 1, "One media key has been added.");
+
+ if (map.size === 1) {
+ res.keyId = map.keys().next().value;
+ res.sessionId = session.sessionId;
+ }
+
+ // Close the session.
+ session.close();
+ await session.closed;
+
+ return res;
+ }
+ );
+
+ // Check the media key ID.
+ is(
+ ByteArrayToHex(result.keyId),
+ Base64ToHex(TESTKEY.kid),
+ "The key Id of the default container is correct."
+ );
+
+ // Store the sessionId for the further checking.
+ keyInfo.sessionId = result.sessionId;
+
+ // Open a tab with personal container.
+ let personalContainer = await openTabInUserContext(
+ TEST_URL + "empty_file.html",
+ USER_ID_PERSONAL
+ );
+
+ await SpecialPowers.spawn(
+ personalContainer.browser,
+ [keyInfo],
+ async function(aKeyInfo) {
+ let access = await content.navigator.requestMediaKeySystemAccess(
+ "org.w3.clearkey",
+ [
+ {
+ initDataTypes: [aKeyInfo.initDataType],
+ videoCapabilities: [{ contentType: "video/webm" }],
+ sessionTypes: ["persistent-license"],
+ persistentState: "required",
+ },
+ ]
+ );
+ let mediaKeys = await access.createMediaKeys();
+ let session = mediaKeys.createSession(aKeyInfo.sessionType);
+
+ // First, load the session to check that mediakeys do not share with
+ // default container.
+ await session.load(aKeyInfo.sessionId);
+
+ let map = session.keyStatuses;
+
+ // Check that there is no media key here.
+ is(
+ map.size,
+ 0,
+ "No media key should be here for the personal container."
+ );
+ }
+ );
+
+ // Close default container tab.
+ BrowserTestUtils.removeTab(defaultContainer.tab);
+
+ // Close personal container tab.
+ BrowserTestUtils.removeTab(personalContainer.tab);
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_favicon.js b/browser/components/contextualidentity/test/browser/browser_favicon.js
new file mode 100644
index 0000000000..d08884c45a
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_favicon.js
@@ -0,0 +1,150 @@
+/*
+ * Bug 1270678 - A test case to test does the favicon obey originAttributes.
+ */
+const { PlacesUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/PlacesUtils.sys.mjs"
+);
+let { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
+
+const USER_CONTEXTS = ["default", "personal", "work"];
+
+let gHttpServer = null;
+let gUserContextId;
+let gFaviconData;
+
+function getIconFile() {
+ new Promise(resolve => {
+ NetUtil.asyncFetch(
+ {
+ uri:
+ "http://www.example.com/browser/browser/components/contextualidentity/test/browser/favicon-normal32.png",
+ loadUsingSystemPrincipal: true,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE_FAVICON,
+ },
+ function(inputStream, status) {
+ let size = inputStream.available();
+ gFaviconData = NetUtil.readInputStreamToString(inputStream, size);
+ resolve();
+ }
+ );
+ });
+}
+
+async function openTabInUserContext(uri, userContextId) {
+ // open the tab in the correct userContextId
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // select tab and make sure its browser is focused
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+ return { tab, browser };
+}
+
+function loadIndexHandler(metadata, response) {
+ response.setStatusLine(metadata.httpVersion, 200, "Ok");
+ response.setHeader("Content-Type", "text/html", false);
+ let body = `
+ <!DOCTYPE HTML>
+ <html>
+ <head>
+ <meta charset='utf-8'>
+ <title>Favicon Test</title>
+ </head>
+ <body>
+ Favicon!!
+ </body>
+ </html>`;
+ response.bodyOutputStream.write(body, body.length);
+}
+
+function loadFaviconHandler(metadata, response) {
+ let expectedCookie = "userContext=" + USER_CONTEXTS[gUserContextId];
+
+ if (metadata.hasHeader("Cookie")) {
+ is(
+ metadata.getHeader("Cookie"),
+ expectedCookie,
+ "The cookie has matched with the expected cookie."
+ );
+ } else {
+ ok(false, "The request should have a cookie.");
+ }
+
+ response.setStatusLine(metadata.httpVersion, 200, "Ok");
+ response.setHeader("Content-Type", "image/png", false);
+ response.bodyOutputStream.write(gFaviconData, gFaviconData.length);
+}
+
+add_setup(async function() {
+ // Make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+
+ // Create a http server for the image cache test.
+ if (!gHttpServer) {
+ gHttpServer = new HttpServer();
+ gHttpServer.registerPathHandler("/", loadIndexHandler);
+ gHttpServer.registerPathHandler("/favicon.png", loadFaviconHandler);
+ gHttpServer.start(-1);
+ }
+});
+
+registerCleanupFunction(() => {
+ gHttpServer.stop(() => {
+ gHttpServer = null;
+ });
+});
+
+add_task(async function test() {
+ waitForExplicitFinish();
+
+ // First, get the icon data.
+ await getIconFile();
+
+ let serverPort = gHttpServer.identity.primaryPort;
+ let testURL = "http://localhost:" + serverPort + "/";
+ let testFaviconURL = "http://localhost:" + serverPort + "/favicon.png";
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ gUserContextId = userContextId;
+
+ // Load the page in 3 different contexts and set a cookie
+ // which should only be visible in that context.
+
+ // Open our tab in the given user context.
+ let tabInfo = await openTabInUserContext(testURL, userContextId);
+
+ // Write a cookie according to the userContext.
+ await SpecialPowers.spawn(
+ tabInfo.browser,
+ [{ userContext: USER_CONTEXTS[userContextId] }],
+ function(arg) {
+ content.document.cookie = "userContext=" + arg.userContext;
+ }
+ );
+
+ let pageURI = NetUtil.newURI(testURL);
+ let favIconURI = NetUtil.newURI(testFaviconURL);
+
+ await new Promise(resolve => {
+ PlacesUtils.favicons.setAndFetchFaviconForPage(
+ pageURI,
+ favIconURI,
+ true,
+ PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+ {
+ onComplete() {
+ resolve();
+ },
+ },
+ tabInfo.browser.contentPrincipal
+ );
+ });
+
+ BrowserTestUtils.removeTab(tabInfo.tab);
+ }
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js b/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js
new file mode 100644
index 0000000000..910bfc5896
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js
@@ -0,0 +1,257 @@
+/*
+ * Bug 1278037 - A Test case for checking whether forgetting APIs are working for the media key.
+ */
+
+const CC = Components.Constructor;
+
+const TEST_HOST = "example.com";
+const TEST_URL =
+ "http://" +
+ TEST_HOST +
+ "/browser/browser/components/contextualidentity/test/browser/";
+
+const USER_CONTEXTS = ["default", "personal"];
+
+const TEST_EME_KEY = {
+ initDataType: "keyids",
+ initData: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A"], "type":"persistent-license"}',
+ kid: "LwVHf8JLtPrv2GUXFW2v_A",
+ key: "97b9ddc459c8d5ff23c1f2754c95abe8",
+ sessionType: "persistent-license",
+};
+
+//
+// Support functions.
+//
+
+async function openTabInUserContext(uri, userContextId) {
+ // Open the tab in the correct userContextId.
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // Select tab and make sure its browser is focused.
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+ return { tab, browser };
+}
+
+function HexToBase64(hex) {
+ var bin = "";
+ for (var i = 0; i < hex.length; i += 2) {
+ bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
+ }
+ return window
+ .btoa(bin)
+ .replace(/=/g, "")
+ .replace(/\+/g, "-")
+ .replace(/\//g, "_");
+}
+
+function Base64ToHex(str) {
+ var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/"));
+ var res = "";
+ for (var i = 0; i < bin.length; i++) {
+ res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
+ }
+ return res;
+}
+
+function ByteArrayToHex(array) {
+ let bin = String.fromCharCode.apply(null, new Uint8Array(array));
+ let res = "";
+
+ for (let i = 0; i < bin.length; i++) {
+ res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
+ }
+
+ return res;
+}
+
+function generateKeyObject(aKid, aKey) {
+ let keyObj = {
+ kty: "oct",
+ kid: aKid,
+ k: HexToBase64(aKey),
+ };
+
+ return new TextEncoder().encode(
+ JSON.stringify({
+ keys: [keyObj],
+ })
+ );
+}
+
+function generateKeyInfo(aData) {
+ let keyInfo = {
+ initDataType: aData.initDataType,
+ initData: new TextEncoder().encode(aData.initData),
+ sessionType: aData.sessionType,
+ keyObj: generateKeyObject(aData.kid, aData.key),
+ };
+
+ return keyInfo;
+}
+
+// Setup a EME key for the given browser, and return the sessionId.
+async function setupEMEKey(browser) {
+ // Generate the key info.
+ let keyInfo = generateKeyInfo(TEST_EME_KEY);
+
+ // Setup the EME key.
+ let result = await SpecialPowers.spawn(browser, [keyInfo], async function(
+ aKeyInfo
+ ) {
+ let access = await content.navigator.requestMediaKeySystemAccess(
+ "org.w3.clearkey",
+ [
+ {
+ initDataTypes: [aKeyInfo.initDataType],
+ videoCapabilities: [{ contentType: "video/webm" }],
+ sessionTypes: ["persistent-license"],
+ persistentState: "required",
+ },
+ ]
+ );
+ let mediaKeys = await access.createMediaKeys();
+ let session = mediaKeys.createSession(aKeyInfo.sessionType);
+ let res = {};
+
+ // Insert the EME key.
+ await new Promise(resolve => {
+ session.addEventListener("message", function(event) {
+ session
+ .update(aKeyInfo.keyObj)
+ .then(() => {
+ resolve();
+ })
+ .catch(() => {
+ ok(false, "Update the EME key fail.");
+ resolve();
+ });
+ });
+
+ session.generateRequest(aKeyInfo.initDataType, aKeyInfo.initData);
+ });
+
+ let map = session.keyStatuses;
+
+ is(map.size, 1, "One EME key has been added.");
+
+ if (map.size === 1) {
+ res.keyId = map.keys().next().value;
+ res.sessionId = session.sessionId;
+ }
+
+ // Close the session.
+ session.close();
+ await session.closed;
+
+ return res;
+ });
+
+ // Check the EME key ID.
+ is(
+ ByteArrayToHex(result.keyId),
+ Base64ToHex(TEST_EME_KEY.kid),
+ "The key Id is correct."
+ );
+ return result.sessionId;
+}
+
+// Check whether the EME key has been cleared.
+async function checkEMEKey(browser, emeSessionId) {
+ // Generate the key info.
+ let keyInfo = generateKeyInfo(TEST_EME_KEY);
+ keyInfo.sessionId = emeSessionId;
+
+ await SpecialPowers.spawn(browser, [keyInfo], async function(aKeyInfo) {
+ let access = await content.navigator.requestMediaKeySystemAccess(
+ "org.w3.clearkey",
+ [
+ {
+ initDataTypes: [aKeyInfo.initDataType],
+ videoCapabilities: [{ contentType: "video/webm" }],
+ sessionTypes: ["persistent-license"],
+ persistentState: "required",
+ },
+ ]
+ );
+ let mediaKeys = await access.createMediaKeys();
+ let session = mediaKeys.createSession(aKeyInfo.sessionType);
+
+ // First, load the session with the sessionId.
+ await session.load(aKeyInfo.sessionId);
+
+ let map = session.keyStatuses;
+
+ // Check that there is no media key here.
+ is(
+ map.size,
+ 0,
+ "No media key should be here after forgetThisSite() was called."
+ );
+ });
+}
+
+//
+// Test functions.
+//
+
+add_setup(async function() {
+ // Make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ ["media.mediasource.enabled", true],
+ ["media.mediasource.webm.enabled", true],
+ ["media.clearkey.persistent-license.enabled", true],
+ ],
+ });
+});
+
+add_task(async function test_EME_forgetThisSite() {
+ let tabs = [];
+ let emeSessionIds = [];
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Open our tab in the given user context.
+ tabs[userContextId] = await openTabInUserContext(
+ TEST_URL + "empty_file.html",
+ userContextId
+ );
+
+ // Setup EME Key.
+ emeSessionIds[userContextId] = await setupEMEKey(
+ tabs[userContextId].browser
+ );
+
+ // Close this tab.
+ BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+
+ // Clear all EME data for a given domain with originAttributes pattern.
+ let mps = Cc["@mozilla.org/gecko-media-plugin-service;1"].getService(
+ Ci.mozIGeckoMediaPluginChromeService
+ );
+ mps.forgetThisSite(TEST_HOST, JSON.stringify({}));
+
+ // Open tabs again to check EME keys have been cleared.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Open our tab in the given user context.
+ tabs[userContextId] = await openTabInUserContext(
+ TEST_URL + "empty_file.html",
+ userContextId
+ );
+
+ // Check whether EME Key has been cleared.
+ await checkEMEKey(
+ tabs[userContextId].browser,
+ emeSessionIds[userContextId]
+ );
+
+ // Close this tab.
+ BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js b/browser/components/contextualidentity/test/browser/browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js
new file mode 100644
index 0000000000..960a5d886a
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js
@@ -0,0 +1,95 @@
+/*
+ * Bug 1278037 - A Test case for checking whether forgetting APIs are working for cookies.
+ */
+
+const CC = Components.Constructor;
+
+const TEST_HOST = "example.com";
+const TEST_URL =
+ "http://" +
+ TEST_HOST +
+ "/browser/browser/components/contextualidentity/test/browser/";
+
+const USER_CONTEXTS = ["default", "personal"];
+
+//
+// Support functions.
+//
+
+async function openTabInUserContext(uri, userContextId) {
+ // Open the tab in the correct userContextId.
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // Select tab and make sure its browser is focused.
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+ return { tab, browser };
+}
+
+function getCookiesForOA(host, userContextId) {
+ return Services.cookies.getCookiesFromHost(host, { userContextId });
+}
+
+//
+// Test functions.
+//
+
+add_setup(async function() {
+ // Make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+});
+
+add_task(async function test_cookie_getCookiesWithOriginAttributes() {
+ let tabs = [];
+ let cookieName = "userContextId";
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Load the page in 2 different contexts and set a cookie
+ // which should only be visible in that context.
+ let value = USER_CONTEXTS[userContextId];
+
+ // Open our tab in the given user context.
+ tabs[userContextId] = await openTabInUserContext(
+ TEST_URL + "file_reflect_cookie_into_title.html?" + value,
+ userContextId
+ );
+
+ // Close this tab.
+ BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+
+ // Check that cookies have been set properly.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let cookies = getCookiesForOA(TEST_HOST, userContextId);
+ ok(cookies.length, "Cookies available");
+
+ let foundCookie = cookies[0];
+ is(foundCookie.name, cookieName, "Check cookie name");
+ is(foundCookie.value, USER_CONTEXTS[userContextId], "Check cookie value");
+ }
+
+ // Using getCookiesWithOriginAttributes() to get all cookies for a certain
+ // domain by using the originAttributes pattern, and clear all these cookies.
+ for (let cookie of Services.cookies.getCookiesWithOriginAttributes(
+ JSON.stringify({}),
+ TEST_HOST
+ )) {
+ Services.cookies.remove(
+ cookie.host,
+ cookie.name,
+ cookie.path,
+ cookie.originAttributes
+ );
+ }
+
+ // Check that whether cookies has been cleared.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let cookies = getCookiesForOA(TEST_HOST, userContextId);
+ ok(!cookies.length, "No Cookie should be here");
+ }
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js b/browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js
new file mode 100644
index 0000000000..b749e73514
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js
@@ -0,0 +1,170 @@
+/*
+ * Bug 1278037 - A Test case for checking whether forgetting APIs are working for the quota manager.
+ */
+
+const CC = Components.Constructor;
+
+const TEST_HOST = "example.com";
+const TEST_URL =
+ "http://" +
+ TEST_HOST +
+ "/browser/browser/components/contextualidentity/test/browser/";
+
+const USER_CONTEXTS = ["default", "personal"];
+
+//
+// Support functions.
+//
+
+async function openTabInUserContext(uri, userContextId) {
+ // Open the tab in the correct userContextId.
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // Select tab and make sure its browser is focused.
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+ return { tab, browser };
+}
+
+// Setup an entry for the indexedDB.
+async function setupIndexedDB(browser) {
+ await SpecialPowers.spawn(
+ browser,
+ [{ input: "TestForgetAPIs" }],
+ async function(arg) {
+ let request = content.indexedDB.open("idb", 1);
+
+ request.onerror = function() {
+ throw new Error("error opening db connection");
+ };
+
+ request.onupgradeneeded = event => {
+ let db = event.target.result;
+ let store = db.createObjectStore("obj", { keyPath: "id" });
+ store.createIndex("userContext", "userContext", { unique: false });
+ };
+
+ let db = await new Promise(resolve => {
+ request.onsuccess = event => {
+ resolve(event.target.result);
+ };
+ });
+
+ // Add an entry into the indexedDB.
+ let transaction = db.transaction(["obj"], "readwrite");
+ let store = transaction.objectStore("obj");
+ store.add({ id: 1, userContext: arg.input });
+
+ await new Promise(resolve => {
+ transaction.oncomplete = () => {
+ resolve();
+ };
+ });
+
+ // Check the indexedDB has been set properly.
+ transaction = db.transaction(["obj"], "readonly");
+ store = transaction.objectStore("obj");
+ let getRequest = store.get(1);
+ await new Promise(resolve => {
+ getRequest.onsuccess = () => {
+ let res = getRequest.result;
+ is(res.userContext, arg.input, "Check the indexedDB value");
+ resolve();
+ };
+ });
+ }
+ );
+}
+
+// Check whether the indexedDB has been cleared.
+async function checkIndexedDB(browser) {
+ await SpecialPowers.spawn(browser, [], async function() {
+ let request = content.indexedDB.open("idb", 1);
+
+ let db = await new Promise(done => {
+ request.onsuccess = event => {
+ done(event.target.result);
+ };
+ });
+
+ try {
+ db.transaction(["obj"], "readonly");
+ ok(false, "The indexedDB should not exist");
+ } catch (e) {
+ is(e.name, "NotFoundError", "The indexedDB does not exist as expected");
+ }
+
+ db.close();
+
+ content.indexedDB.deleteDatabase("idb");
+ });
+}
+
+//
+// Test functions.
+//
+
+add_setup(async function() {
+ // Make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+});
+
+add_task(async function test_quota_clearStoragesForPrincipal() {
+ let tabs = [];
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Open our tab in the given user context.
+ tabs[userContextId] = await openTabInUserContext(
+ TEST_URL + "empty_file.html",
+ userContextId
+ );
+
+ // Setup an entry for the indexedDB.
+ await setupIndexedDB(tabs[userContextId].browser);
+
+ // Close this tab.
+ BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+
+ // Using quota manager to clear all indexed DB for a given domain.
+ let caUtils = {};
+ Services.scriptloader.loadSubScript(
+ "chrome://global/content/contentAreaUtils.js",
+ caUtils
+ );
+ let httpURI = caUtils.makeURI("http://" + TEST_HOST);
+ let httpPrincipal = Services.scriptSecurityManager.createContentPrincipal(
+ httpURI,
+ {}
+ );
+ let clearRequest = Services.qms.clearStoragesForPrincipal(
+ httpPrincipal,
+ null,
+ null,
+ true
+ );
+ await new Promise(resolve => {
+ clearRequest.callback = () => {
+ resolve();
+ };
+ });
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Open our tab in the given user context.
+ tabs[userContextId] = await openTabInUserContext(
+ TEST_URL + "empty_file.html",
+ userContextId
+ );
+
+ // Check whether indexed DB has been cleared.
+ await checkIndexedDB(tabs[userContextId].browser);
+
+ // Close this tab.
+ BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js b/browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js
new file mode 100644
index 0000000000..20c6653aa1
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js
@@ -0,0 +1,425 @@
+/*
+ * Bug 1238183 - Test cases for forgetAboutSite with userContextId.
+ */
+
+const CC = Components.Constructor;
+
+let { ForgetAboutSite } = ChromeUtils.import(
+ "resource://gre/modules/ForgetAboutSite.jsm"
+);
+let { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
+
+const USER_CONTEXTS = ["default", "personal"];
+const TEST_HOST = "example.com";
+const TEST_URL =
+ "http://" +
+ TEST_HOST +
+ "/browser/browser/components/contextualidentity/test/browser/";
+const COOKIE_NAME = "userContextId";
+
+// Counter for image load hits.
+let gHits = 0;
+
+let gHttpServer = null;
+
+function imageHandler(metadata, response) {
+ // A 1x1 PNG image.
+ // Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain)
+ const IMAGE = atob(
+ "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" +
+ "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII="
+ );
+ gHits++;
+ response.setHeader("Cache-Control", "max-age=10000", false);
+ response.setStatusLine(metadata.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "image/png", false);
+ response.write(IMAGE);
+}
+
+function loadImagePageHandler(metadata, response) {
+ response.setHeader("Cache-Control", "max-age=10000", false);
+ response.setStatusLine(metadata.httpVersion, 200, "Ok");
+ response.setHeader("Content-Type", "text/html", false);
+ let body =
+ "<!DOCTYPE HTML>\
+ <html>\
+ <head>\
+ <meta charset='utf-8'>\
+ <title>Load Image</title>\
+ </head>\
+ <body>\
+ <img src='image.png'>\
+ </body>\
+ </html>";
+ response.bodyOutputStream.write(body, body.length);
+}
+
+async function openTabInUserContext(uri, userContextId) {
+ // Open the tab in the correct userContextId.
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // Select tab and make sure its browser is focused.
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+ return { tab, browser };
+}
+
+function getCookiesForOA(host, userContextId) {
+ return Services.cookies.getCookiesFromHost(host, { userContextId });
+}
+
+function createURI(uri) {
+ return Services.io.newURI(uri);
+}
+
+function getCacheStorage(where, lci) {
+ if (!lci) {
+ lci = Services.loadContextInfo.default;
+ }
+ switch (where) {
+ case "disk":
+ return Services.cache2.diskCacheStorage(lci);
+ case "memory":
+ return Services.cache2.memoryCacheStorage(lci);
+ case "pin":
+ return Services.cache2.pinningCacheStorage(lci);
+ }
+ return null;
+}
+
+function OpenCacheEntry(key, where, flags, lci) {
+ return new Promise(resolve => {
+ key = createURI(key);
+ function CacheListener() {}
+ CacheListener.prototype = {
+ QueryInterface: ChromeUtils.generateQI(["nsICacheEntryOpenCallback"]),
+
+ onCacheEntryCheck(entry) {
+ return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED;
+ },
+
+ onCacheEntryAvailable(entry, isnew, status) {
+ resolve();
+ },
+
+ run() {
+ let storage = getCacheStorage(where, lci);
+ storage.asyncOpenURI(key, "", flags, this);
+ },
+ };
+
+ new CacheListener().run();
+ });
+}
+
+//
+// Test functions.
+//
+
+// Cookies
+async function test_cookie_cleared() {
+ let tabs = [];
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Load the page in 2 different contexts and set a cookie
+ // which should only be visible in that context.
+ let value = USER_CONTEXTS[userContextId];
+
+ // Open our tab in the given user context.
+ tabs[userContextId] = await openTabInUserContext(
+ TEST_URL + "file_reflect_cookie_into_title.html?" + value,
+ userContextId
+ );
+
+ // Close this tab.
+ BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+ // Check that cookies have been set properly.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let cookies = getCookiesForOA(TEST_HOST, userContextId);
+ ok(cookies.length, "Cookies available");
+
+ let foundCookie = cookies[0];
+ Assert.equal(foundCookie.name, COOKIE_NAME, "Check cookie name");
+ Assert.equal(
+ foundCookie.value,
+ USER_CONTEXTS[userContextId],
+ "Check cookie value"
+ );
+ }
+
+ // Forget the site.
+ await ForgetAboutSite.removeDataFromDomain(TEST_HOST);
+
+ // Check that whether cookies has been cleared or not.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let cookies = getCookiesForOA(TEST_HOST, userContextId);
+ ok(!cookies.length, "No Cookie should be here");
+ }
+}
+
+// Cache
+async function test_cache_cleared() {
+ // First, add some caches.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ await OpenCacheEntry(
+ "http://" + TEST_HOST + "/",
+ "disk",
+ Ci.nsICacheStorage.OPEN_NORMALLY,
+ Services.loadContextInfo.custom(false, { userContextId })
+ );
+
+ await OpenCacheEntry(
+ "http://" + TEST_HOST + "/",
+ "memory",
+ Ci.nsICacheStorage.OPEN_NORMALLY,
+ Services.loadContextInfo.custom(false, { userContextId })
+ );
+ }
+
+ // Check that caches have been set correctly.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let mem = getCacheStorage(
+ "memory",
+ Services.loadContextInfo.custom(false, { userContextId })
+ );
+ let disk = getCacheStorage(
+ "disk",
+ Services.loadContextInfo.custom(false, { userContextId })
+ );
+
+ Assert.ok(
+ mem.exists(createURI("http://" + TEST_HOST + "/"), ""),
+ "The memory cache has been set correctly"
+ );
+ Assert.ok(
+ disk.exists(createURI("http://" + TEST_HOST + "/"), ""),
+ "The disk cache has been set correctly"
+ );
+ }
+
+ // Forget the site.
+ await ForgetAboutSite.removeDataFromDomain(TEST_HOST);
+
+ // Check that do caches be removed or not?
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let mem = getCacheStorage(
+ "memory",
+ Services.loadContextInfo.custom(false, { userContextId })
+ );
+ let disk = getCacheStorage(
+ "disk",
+ Services.loadContextInfo.custom(false, { userContextId })
+ );
+
+ Assert.ok(
+ !mem.exists(createURI("http://" + TEST_HOST + "/"), ""),
+ "The memory cache is cleared"
+ );
+ Assert.ok(
+ !disk.exists(createURI("http://" + TEST_HOST + "/"), ""),
+ "The disk cache is cleared"
+ );
+ }
+}
+
+// Image Cache
+async function test_image_cache_cleared() {
+ let tabs = [];
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Open our tab in the given user context to cache image.
+ tabs[userContextId] = await openTabInUserContext(
+ "http://localhost:" +
+ gHttpServer.identity.primaryPort +
+ "/loadImage.html",
+ userContextId
+ );
+ BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+
+ let expectedHits = USER_CONTEXTS.length;
+
+ // Check that image cache works with the userContextId.
+ is(
+ gHits,
+ expectedHits,
+ "The image should be loaded" + expectedHits + "times."
+ );
+
+ // Reset the cache count.
+ gHits = 0;
+
+ // Forget the site.
+ await ForgetAboutSite.removeDataFromDomain("localhost");
+
+ // Load again.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Open our tab in the given user context to cache image.
+ tabs[userContextId] = await openTabInUserContext(
+ "http://localhost:" +
+ gHttpServer.identity.primaryPort +
+ "/loadImage.html",
+ userContextId
+ );
+ BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+
+ // Check that image cache was cleared and the server gets another two hits.
+ is(
+ gHits,
+ expectedHits,
+ "The image should be loaded" + expectedHits + "times."
+ );
+}
+
+// Offline Storage
+async function test_storage_cleared() {
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Load the page in 2 different contexts and set the local storage
+ // which should only be visible in that context.
+ let value = USER_CONTEXTS[userContextId];
+
+ // Open our tab in the given user context.
+ let tabInfo = await openTabInUserContext(
+ TEST_URL + "file_set_storages.html?" + value,
+ userContextId
+ );
+
+ // Check that the storages has been set correctly.
+ await SpecialPowers.spawn(
+ tabInfo.browser,
+ [{ userContext: USER_CONTEXTS[userContextId] }],
+ async function(arg) {
+ // Check that the local storage has been set correctly.
+ Assert.equal(
+ content.localStorage.getItem("userContext"),
+ arg.userContext,
+ "Check the local storage value"
+ );
+
+ // Check that the session storage has been set correctly.
+ Assert.equal(
+ content.sessionStorage.getItem("userContext"),
+ arg.userContext,
+ "Check the session storage value"
+ );
+
+ // Check that the indexedDB has been set correctly.
+ let request = content.indexedDB.open("idb", 1);
+
+ let db = await new Promise(done => {
+ request.onsuccess = event => {
+ done(event.target.result);
+ };
+ });
+
+ let transaction = db.transaction(["obj"], "readonly");
+ let store = transaction.objectStore("obj");
+ let storeRequest = store.get(1);
+
+ await new Promise(done => {
+ storeRequest.onsuccess = event => {
+ let res = storeRequest.result;
+ Assert.equal(
+ res.userContext,
+ arg.userContext,
+ "Check the indexedDB value"
+ );
+ done();
+ };
+ });
+ }
+ );
+
+ // Close this tab.
+ BrowserTestUtils.removeTab(tabInfo.tab);
+ }
+
+ // Forget the site.
+ await ForgetAboutSite.removeDataFromDomain(TEST_HOST);
+
+ // Open the tab again without setting the localStorage and check that the
+ // local storage has been cleared or not.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Open our tab in the given user context without setting local storage.
+ let tabInfo = await openTabInUserContext(
+ TEST_URL + "file_set_storages.html",
+ userContextId
+ );
+
+ // Check that do storages be cleared or not.
+ await SpecialPowers.spawn(tabInfo.browser, [], async function() {
+ // Check that does the local storage be cleared or not.
+ Assert.ok(
+ !content.localStorage.getItem("userContext"),
+ "The local storage has been cleared"
+ );
+
+ // Check that does the session storage be cleared or not.
+ Assert.ok(
+ !content.sessionStorage.getItem("userContext"),
+ "The session storage has been cleared"
+ );
+
+ // Check that does the indexedDB be cleared or not.
+ let request = content.indexedDB.open("idb", 1);
+
+ let db = await new Promise(done => {
+ request.onsuccess = event => {
+ done(event.target.result);
+ };
+ });
+ try {
+ db.transaction(["obj"], "readonly");
+ Assert.ok(false, "The indexedDB should not exist");
+ } catch (e) {
+ Assert.equal(
+ e.name,
+ "NotFoundError",
+ "The indexedDB does not exist as expected"
+ );
+ }
+ });
+
+ // Close the tab.
+ BrowserTestUtils.removeTab(tabInfo.tab);
+ }
+}
+
+add_setup(async function() {
+ // Make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+
+ // Create a http server for the image cache test.
+ if (!gHttpServer) {
+ gHttpServer = new HttpServer();
+ gHttpServer.registerPathHandler("/image.png", imageHandler);
+ gHttpServer.registerPathHandler("/loadImage.html", loadImagePageHandler);
+ gHttpServer.start(-1);
+ }
+});
+
+let tests = [
+ test_cookie_cleared,
+ test_cache_cleared,
+ test_image_cache_cleared,
+ test_storage_cleared,
+];
+
+add_task(async function test() {
+ for (let i = 0; i < tests.length; i++) {
+ add_task(tests[i]);
+ }
+});
+
+registerCleanupFunction(() => {
+ gHttpServer.stop(() => {
+ gHttpServer = null;
+ });
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_imageCache.js b/browser/components/contextualidentity/test/browser/browser_imageCache.js
new file mode 100644
index 0000000000..0c12907096
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_imageCache.js
@@ -0,0 +1,69 @@
+let { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
+
+const NUM_USER_CONTEXTS = 3;
+
+let gHits = 0;
+
+let server = new HttpServer();
+server.registerPathHandler("/image.png", imageHandler);
+server.registerPathHandler("/file.html", fileHandler);
+server.start(-1);
+
+let BASE_URI = "http://localhost:" + server.identity.primaryPort;
+let IMAGE_URI = BASE_URI + "/image.png";
+let FILE_URI = BASE_URI + "/file.html";
+
+function imageHandler(metadata, response) {
+ gHits++;
+ response.setHeader("Cache-Control", "max-age=10000", false);
+ response.setStatusLine(metadata.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "image/png", false);
+ var body =
+ "iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII=";
+ response.bodyOutputStream.write(body, body.length);
+}
+
+function fileHandler(metadata, response) {
+ response.setStatusLine(metadata.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "text/html", false);
+ let body = `<html><body><image src=${IMAGE_URI}></body></html>`;
+ response.bodyOutputStream.write(body, body.length);
+}
+
+add_setup(async function() {
+ // make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+});
+
+// opens `uri' in a new tab with the provided userContextId and focuses it.
+// returns the newly opened tab
+async function openTabInUserContext(uri, userContextId) {
+ // open the tab in the correct userContextId
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // select tab and make sure its browser is focused
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+ return tab;
+}
+
+add_task(async function test() {
+ for (
+ let userContextId = 0;
+ userContextId < NUM_USER_CONTEXTS;
+ userContextId++
+ ) {
+ let tab = await openTabInUserContext(FILE_URI, userContextId);
+ gBrowser.removeTab(tab);
+ }
+ is(
+ gHits,
+ NUM_USER_CONTEXTS,
+ "should get an image request for each user contexts"
+ );
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_middleClick.js b/browser/components/contextualidentity/test/browser/browser_middleClick.js
new file mode 100644
index 0000000000..d15f981f1e
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_middleClick.js
@@ -0,0 +1,50 @@
+"use strict";
+
+const BASE_ORIGIN = "http://example.com";
+const URI =
+ BASE_ORIGIN +
+ "/browser/browser/components/contextualidentity/test/browser/empty_file.html";
+
+add_task(async function() {
+ info("Opening a new container tab...");
+
+ let tab = BrowserTestUtils.addTab(gBrowser, URI, { userContextId: 1 });
+ gBrowser.selectedTab = tab;
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ info("Create a HTMLAnchorElement...");
+ await SpecialPowers.spawn(browser, [URI], function(uri) {
+ let anchor = content.document.createElement("a");
+ anchor.setAttribute("id", "clickMe");
+ anchor.setAttribute("href", uri);
+ anchor.appendChild(content.document.createTextNode("click me!"));
+ content.document.body.appendChild(anchor);
+ });
+
+ info("Synthesize a mouse click and wait for a new tab...");
+ let newTab = await new Promise((resolve, reject) => {
+ gBrowser.tabContainer.addEventListener(
+ "TabOpen",
+ function(openEvent) {
+ resolve(openEvent.target);
+ },
+ { once: true }
+ );
+
+ BrowserTestUtils.synthesizeMouseAtCenter(
+ "#clickMe",
+ { button: 1 },
+ browser
+ );
+ });
+
+ is(newTab.getAttribute("usercontextid"), "1", "Correct UserContextId?");
+
+ // newTab shouldn't be closed in the same event tick as TabOpen.
+ await TestUtils.waitForTick();
+
+ BrowserTestUtils.removeTab(tab);
+ BrowserTestUtils.removeTab(newTab);
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_newtabButton.js b/browser/components/contextualidentity/test/browser/browser_newtabButton.js
new file mode 100644
index 0000000000..02f50f27d6
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_newtabButton.js
@@ -0,0 +1,214 @@
+"use strict";
+
+// Testing that when the user opens the add tab menu and clicks menu items
+// the correct context id is opened
+
+function findPopup(browser = gBrowser) {
+ return browser.tabContainer.querySelector(".new-tab-popup");
+}
+
+function findContextPopup() {
+ return document.querySelector("#new-tab-button-popup");
+}
+
+add_task(async function test_containers_no_left_click() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ ["privacy.userContext.newTabContainerOnLeftClick.enabled", false],
+ ],
+ });
+
+ let newTabButton = gBrowser.tabContainer.newTabButton;
+ ok(newTabButton, "New tab button exists");
+ ok(!newTabButton.hidden, "New tab button is visible");
+ let popup = findPopup();
+ ok(popup, "new tab should have a popup");
+
+ // Test context menu
+ let contextMenu = findContextPopup();
+ is(contextMenu.state, "closed", "Context menu is initally closed.");
+
+ let popupshown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
+
+ let target = gBrowser.tabContainer.newTabButton;
+ EventUtils.synthesizeMouseAtCenter(target, { type: "contextmenu" });
+ await popupshown;
+
+ is(contextMenu.state, "open", "Context menu is open.");
+ let contextIdItems = contextMenu.querySelectorAll("menuitem");
+ // 4 + default + manage containers
+ is(contextIdItems.length, 6, "Has 6 menu items");
+ contextMenu.hidePopup();
+});
+
+add_task(async function test_containers_with_left_click() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ ["privacy.userContext.newTabContainerOnLeftClick.enabled", true],
+ ],
+ });
+
+ let newTabButton = gBrowser.tabContainer.newTabButton;
+ ok(newTabButton, "New tab button exists");
+ ok(!newTabButton.hidden, "New tab button is visible");
+
+ await BrowserTestUtils.waitForCondition(
+ () => !!findPopup(),
+ "Wait for popup to exist"
+ );
+ let popup = findPopup();
+
+ let popupShownPromise = BrowserTestUtils.waitForEvent(popup, "popupshown");
+ let popupHiddenPromise = BrowserTestUtils.waitForEvent(popup, "popuphidden");
+ EventUtils.synthesizeMouseAtCenter(newTabButton, { type: "mousedown" });
+ await popupShownPromise;
+ let contextIdItems = popup.querySelectorAll("menuitem");
+ // 4 + default + manage containers
+ is(contextIdItems.length, 6, "Has 6 menu items");
+ popup.hidePopup();
+ await popupHiddenPromise;
+
+ for (let i = 0; i <= 4; i++) {
+ popupShownPromise = BrowserTestUtils.waitForEvent(popup, "popupshown");
+ EventUtils.synthesizeMouseAtCenter(newTabButton, { type: "mousedown" });
+
+ await popupShownPromise;
+ let contextIdItem = popup.querySelector(
+ `menuitem[data-usercontextid="${i}"]`
+ );
+
+ ok(contextIdItem, `User context id ${i} exists`);
+
+ // waitForNewTab doesn't work for default tabs due to a different code path that doesn't cause a load event
+ let waitForTabPromise = BrowserTestUtils.waitForEvent(
+ gBrowser.tabContainer,
+ "TabOpen"
+ );
+ EventUtils.synthesizeMouseAtCenter(contextIdItem, {});
+
+ let tabEvent = await waitForTabPromise;
+ let tab = tabEvent.target;
+ if (i > 0) {
+ is(
+ tab.getAttribute("usercontextid"),
+ "" + i,
+ `New tab has UCI equal ${i}`
+ );
+ } else {
+ ok(!tab.hasAttribute("usercontextid"), `New tab has no UCI`);
+ }
+ BrowserTestUtils.removeTab(tab);
+ }
+
+ // Test context menu
+ let contextMenu = findContextPopup();
+ is(contextMenu.state, "closed", "Context menu is initally closed.");
+
+ let popupshown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
+
+ let target = gBrowser.tabContainer.newTabButton;
+ EventUtils.synthesizeMouseAtCenter(target, { type: "contextmenu" });
+ await popupshown;
+
+ is(contextMenu.state, "open", "Context menu is open.");
+ contextIdItems = contextMenu.querySelectorAll("menuitem");
+ // 4 + default + manage containers
+ is(contextIdItems.length, 6, "Has 6 menu items");
+ contextMenu.hidePopup();
+});
+
+add_task(async function test_no_containers_no_left_click() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", false],
+ ["privacy.userContext.newTabContainerOnLeftClick.enabled", false],
+ ],
+ });
+
+ let newTabButton = gBrowser.tabContainer.newTabButton;
+ ok(newTabButton, "New tab button exists");
+ ok(!newTabButton.hidden, "New tab button is visible");
+ let popup = findPopup();
+ ok(!popup, "new tab should not have a popup");
+
+ // Test context menu
+ let contextMenu = findContextPopup();
+ let target = gBrowser.tabContainer.newTabButton;
+ EventUtils.synthesizeMouseAtCenter(target, { type: "contextmenu" });
+ is(contextMenu.state, "closed", "Context menu does not open.");
+});
+
+add_task(async function test_private_mode() {
+ let privateWindow = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+ let privateDocument = privateWindow.document;
+ let { tabContainer } = privateWindow.gBrowser;
+ let newTab = tabContainer.newTabButton;
+ let newTab2 = privateDocument.getElementById("new-tab-button");
+ // Check to ensure we are talking about the right button
+ ok(!!newTab.clientWidth, "new tab button should not be hidden");
+ ok(!newTab2.clientWidth, "overflow new tab button should be hidden");
+ let popup = findPopup(privateWindow.gBrowser);
+ ok(!popup, "new tab should not have a popup");
+ await BrowserTestUtils.closeWindow(privateWindow);
+});
+
+add_task(async function test_opening_container_tab_context() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ ["privacy.userContext.newTabContainerOnLeftClick.enabled", true],
+ ],
+ });
+
+ let newTabButton = gBrowser.tabContainer.newTabButton;
+ ok(newTabButton, "New tab button exists");
+ ok(!newTabButton.hidden, "New tab button is visible");
+
+ let popup = findContextPopup();
+
+ let popupShownPromise = BrowserTestUtils.waitForEvent(popup, "popupshown");
+ let popupHiddenPromise = BrowserTestUtils.waitForEvent(popup, "popuphidden");
+ EventUtils.synthesizeMouseAtCenter(newTabButton, { type: "contextmenu" });
+ await popupShownPromise;
+ let contextIdItems = popup.querySelectorAll("menuitem");
+ // 4 + default + manage containers
+ is(contextIdItems.length, 6, "Has 6 menu items");
+ popup.hidePopup();
+ await popupHiddenPromise;
+
+ for (let i = 0; i <= 4; i++) {
+ popupShownPromise = BrowserTestUtils.waitForEvent(popup, "popupshown");
+ EventUtils.synthesizeMouseAtCenter(newTabButton, { type: "contextmenu" });
+
+ await popupShownPromise;
+ let contextIdItem = popup.querySelector(
+ `menuitem[data-usercontextid="${i}"]`
+ );
+
+ ok(contextIdItem, `User context id ${i} exists`);
+
+ // waitForNewTab doesn't work for default tabs due to a different code path that doesn't cause a load event
+ let waitForTabPromise = BrowserTestUtils.waitForEvent(
+ gBrowser.tabContainer,
+ "TabOpen"
+ );
+ popup.activateItem(contextIdItem);
+
+ let tabEvent = await waitForTabPromise;
+ let tab = tabEvent.target;
+ if (i > 0) {
+ is(
+ tab.getAttribute("usercontextid"),
+ "" + i,
+ `New tab has UCI equal ${i}`
+ );
+ } else {
+ ok(!tab.hasAttribute("usercontextid"), `New tab has no UCI`);
+ }
+ BrowserTestUtils.removeTab(tab);
+ }
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_originattrs_reopenin.js b/browser/components/contextualidentity/test/browser/browser_originattrs_reopenin.js
new file mode 100644
index 0000000000..777c0f010e
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_originattrs_reopenin.js
@@ -0,0 +1,184 @@
+/* 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/. */
+
+/* import-globals-from ../../../../base/content/test/tabs/helper_origin_attrs_testing.js */
+loadTestSubscript(
+ "../../../../base/content/test/tabs/helper_origin_attrs_testing.js"
+);
+
+const PATH =
+ "/browser/browser/components/contextualidentity/test/browser/blank.html";
+
+const URI_EXAMPLECOM = "https://example.com/" + PATH;
+const URI_EXAMPLEORG = "https://example.org/" + PATH;
+
+var TEST_CASES = [
+ URI_EXAMPLECOM,
+ URI_EXAMPLEORG,
+ "about:preferences",
+ "about:config",
+];
+
+var remoteTypes;
+
+var xulFrameLoaderCreatedCounter = {};
+
+function handleEventLocal(aEvent) {
+ if (aEvent.type != "XULFrameLoaderCreated") {
+ return;
+ }
+ // Ignore <browser> element in about:preferences and any other special pages
+ if ("gBrowser" in aEvent.target.ownerGlobal) {
+ xulFrameLoaderCreatedCounter.numCalledSoFar++;
+ }
+}
+const NUM_PAGES_OPEN_FOR_EACH_TEST_CASE = 5;
+add_setup(async function() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ // don't preload tabs so we don't have extra XULFrameLoaderCreated events
+ // firing
+ ["browser.newtab.preload", false],
+ ],
+ });
+
+ requestLongerTimeout(5);
+});
+
+function setupRemoteTypes() {
+ remoteTypes = {
+ regular: { "about:preferences": null, "about:config": null },
+ "1": { "about:preferences": null, "about:config": null },
+ "2": { "about:preferences": null, "about:config": null },
+ "3": { "about:preferences": null, "about:config": null },
+ };
+ if (gFissionBrowser) {
+ remoteTypes.regular[URI_EXAMPLECOM] = "webIsolated=https://example.com";
+ remoteTypes.regular[URI_EXAMPLEORG] = "webIsolated=https://example.org";
+ remoteTypes["1"][URI_EXAMPLECOM] =
+ "webIsolated=https://example.com^userContextId=1";
+ remoteTypes["1"][URI_EXAMPLEORG] =
+ "webIsolated=https://example.org^userContextId=1";
+ remoteTypes["2"][URI_EXAMPLECOM] =
+ "webIsolated=https://example.com^userContextId=2";
+ remoteTypes["2"][URI_EXAMPLEORG] =
+ "webIsolated=https://example.org^userContextId=2";
+ remoteTypes["3"][URI_EXAMPLECOM] =
+ "webIsolated=https://example.com^userContextId=3";
+ remoteTypes["3"][URI_EXAMPLEORG] =
+ "webIsolated=https://example.org^userContextId=3";
+ } else {
+ remoteTypes.regular[URI_EXAMPLECOM] = "web";
+ remoteTypes.regular[URI_EXAMPLEORG] = "web";
+ remoteTypes["1"][URI_EXAMPLECOM] = "web";
+ remoteTypes["1"][URI_EXAMPLEORG] = "web";
+ remoteTypes["2"][URI_EXAMPLECOM] = "web";
+ remoteTypes["2"][URI_EXAMPLEORG] = "web";
+ remoteTypes["3"][URI_EXAMPLECOM] = "web";
+ remoteTypes["3"][URI_EXAMPLEORG] = "web";
+ }
+}
+
+add_task(async function testReopen() {
+ setupRemoteTypes();
+ /**
+ * Open a regular tab
+ * For each url
+ * navigate regular tab to that url
+ * pid_seen = [regular tab pid]
+ * For each container
+ * reopen regular tab in that container
+ * verify remote type
+ * reopen container tab in regular tab
+ * Close all the tabs
+ * This tests that behaviour of reopening tabs is correct
+ * */
+
+ let regularPage = await openURIInRegularTab("about:blank");
+ var currRemoteType;
+
+ for (const uri of TEST_CASES) {
+ // Navigate regular tab to a test-uri
+ let loaded = BrowserTestUtils.browserLoaded(
+ regularPage.tab.linkedBrowser,
+ false,
+ uri
+ );
+ await BrowserTestUtils.loadURI(regularPage.tab.linkedBrowser, uri);
+ await loaded;
+ info(`Start Opened ${uri} in a regular tab`);
+ currRemoteType = regularPage.tab.linkedBrowser.remoteType;
+ is(currRemoteType, remoteTypes.regular[uri], "correct remote type");
+
+ let containerTabs = [];
+
+ // Reopen this test-uri in different containers and ensure pids are different
+ for (var userCtxId = 1; userCtxId <= NUM_USER_CONTEXTS; userCtxId++) {
+ // Prepare the reopen menu
+ let reopenMenu = await openReopenMenuForTab(regularPage.tab);
+
+ // Add a listener for XULFrameLoaderCreated
+ initXulFrameLoaderCreatedCounter(xulFrameLoaderCreatedCounter);
+ regularPage.tab.ownerGlobal.gBrowser.addEventListener(
+ "XULFrameLoaderCreated",
+ handleEventLocal
+ );
+
+ // Reopen the page in a container
+ let containerTab = await openTabInContainer(
+ gBrowser,
+ uri,
+ reopenMenu,
+ userCtxId.toString()
+ );
+ info(`Re-opened ${uri} in a container tab ${userCtxId}`);
+
+ // Check correct remote type
+ currRemoteType = containerTab.linkedBrowser.remoteType;
+ is(
+ currRemoteType,
+ remoteTypes[userCtxId.toString()][uri],
+ "correct remote type"
+ );
+
+ // Check that XULFrameLoaderCreated has fired off correct number of times
+ is(
+ xulFrameLoaderCreatedCounter.numCalledSoFar,
+ 1,
+ `XULFrameLoaderCreated was fired once when reopening ${uri} in container ${userCtxId}`
+ );
+ regularPage.tab.ownerGlobal.gBrowser.removeEventListener(
+ "XULFrameLoaderCreated",
+ handleEventLocal
+ );
+
+ // Save the tab so we can close it later
+ containerTabs.push(containerTab);
+ }
+ let containedTabsOpenedInNoContainer = [];
+
+ for (const containerTab of containerTabs) {
+ info(`About to re-open ${uri} in a regular, no-container tab`);
+ let reopenMenu = await openReopenMenuForTab(containerTab);
+ let reopenedInNoContainerTab = await openTabInContainer(
+ gBrowser,
+ uri,
+ reopenMenu,
+ "0"
+ );
+ info(`Re-opened ${uri} in a regular, no-container tab`);
+ // Check remote type
+ currRemoteType = reopenedInNoContainerTab.linkedBrowser.remoteType;
+ is(currRemoteType, remoteTypes.regular[uri], "correct remote type");
+ containedTabsOpenedInNoContainer.push(reopenedInNoContainerTab);
+ }
+
+ // Close tabs
+ for (const tab of containerTabs.concat(containedTabsOpenedInNoContainer)) {
+ BrowserTestUtils.removeTab(tab);
+ }
+ }
+ BrowserTestUtils.removeTab(regularPage.tab);
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_relatedTab.js b/browser/components/contextualidentity/test/browser/browser_relatedTab.js
new file mode 100644
index 0000000000..56c72d599e
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_relatedTab.js
@@ -0,0 +1,94 @@
+"use strict";
+
+/*
+ * Bug 1325014 - Adding tab related to current tab inherits current tab's container usercontextid unless otherwise specified
+ */
+
+add_task(async function() {
+ let tab = BrowserTestUtils.addTab(gBrowser, "about:blank", {
+ userContextId: 1,
+ });
+ let ReferrerInfo = Components.Constructor(
+ "@mozilla.org/referrer-info;1",
+ "nsIReferrerInfo",
+ "init"
+ );
+
+ gBrowser.selectedTab = tab;
+ let relatedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", {
+ relatedToCurrent: true,
+ });
+ is(
+ relatedTab.getAttribute("usercontextid"),
+ "1",
+ "Related tab (relatedToCurrent) inherits current tab's usercontextid"
+ );
+ is(
+ relatedTab.linkedBrowser.contentPrincipal.userContextId,
+ 1,
+ "Related tab's browser actually inherits the current tab's usercontextid"
+ );
+ BrowserTestUtils.removeTab(relatedTab);
+
+ gBrowser.selectedTab = tab;
+ relatedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", {
+ relatedToCurrent: true,
+ userContextId: 2,
+ });
+ is(
+ relatedTab.getAttribute("usercontextid"),
+ "2",
+ "Related tab (relatedToCurrent) with overridden usercontextid"
+ );
+ is(
+ relatedTab.linkedBrowser.contentPrincipal.userContextId,
+ 2,
+ "Related tab's browser actually gets overridden usercontextid"
+ );
+ BrowserTestUtils.removeTab(relatedTab);
+
+ gBrowser.selectedTab = tab;
+ let referrerInfo = new ReferrerInfo(
+ Ci.nsIReferrerInfo.EMPTY,
+ true,
+ gBrowser.currentURI
+ );
+ relatedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", {
+ referrerInfo,
+ });
+ is(
+ relatedTab.getAttribute("usercontextid"),
+ "1",
+ "Related tab (referrer) inherits current tab's usercontextid"
+ );
+ is(
+ relatedTab.linkedBrowser.contentPrincipal.userContextId,
+ 1,
+ "Related tab's browser (referrer) actually inherits the current tab's usercontextid"
+ );
+ BrowserTestUtils.removeTab(relatedTab);
+
+ gBrowser.selectedTab = tab;
+ referrerInfo = new ReferrerInfo(
+ Ci.nsIReferrerInfo.EMPTY,
+ true,
+ gBrowser.currentURI
+ );
+ relatedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", {
+ referrerInfo,
+ userContextId: 2,
+ });
+ is(
+ relatedTab.getAttribute("usercontextid"),
+ "2",
+ "Related tab (referrer) with overridden usercontextid"
+ );
+ is(
+ relatedTab.linkedBrowser.contentPrincipal.userContextId,
+ 2,
+ "Related tab's browser (referrer) actually gets overridden usercontextid"
+ );
+ BrowserTestUtils.removeTab(relatedTab);
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_reopenIn.js b/browser/components/contextualidentity/test/browser/browser_reopenIn.js
new file mode 100644
index 0000000000..9bda63b201
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_reopenIn.js
@@ -0,0 +1,164 @@
+"use strict";
+
+const TEST_HOST = "example.com";
+const TEST_URL =
+ "http://" +
+ TEST_HOST +
+ "/browser/browser/components/contextualidentity/test/browser/";
+
+async function openTabMenuFor(tab) {
+ let tabMenu = tab.ownerDocument.getElementById("tabContextMenu");
+
+ let tabMenuShown = BrowserTestUtils.waitForEvent(tabMenu, "popupshown");
+ EventUtils.synthesizeMouseAtCenter(
+ tab,
+ { type: "contextmenu" },
+ tab.ownerGlobal
+ );
+ await tabMenuShown;
+
+ return tabMenu;
+}
+
+async function openReopenMenuForTab(tab) {
+ await openTabMenuFor(tab);
+
+ let reopenItem = tab.ownerDocument.getElementById(
+ "context_reopenInContainer"
+ );
+ ok(!reopenItem.hidden, "Reopen in Container item should be shown");
+
+ let reopenMenu = reopenItem.getElementsByTagName("menupopup")[0];
+ let reopenMenuShown = BrowserTestUtils.waitForEvent(reopenMenu, "popupshown");
+ reopenItem.openMenu(true);
+ await reopenMenuShown;
+
+ return reopenMenu;
+}
+
+function checkMenuItem(reopenMenu, shown, hidden) {
+ for (let id of shown) {
+ ok(
+ reopenMenu.querySelector(`menuitem[data-usercontextid="${id}"]`),
+ `User context id ${id} should exist`
+ );
+ }
+ for (let id of hidden) {
+ ok(
+ !reopenMenu.querySelector(`menuitem[data-usercontextid="${id}"]`),
+ `User context id ${id} shouldn't exist`
+ );
+ }
+}
+
+function openTabInContainer(gBrowser, reopenMenu, id) {
+ let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, TEST_URL, true);
+ let menuitem = reopenMenu.querySelector(
+ `menuitem[data-usercontextid="${id}"]`
+ );
+ reopenMenu.activateItem(menuitem);
+ return tabPromise;
+}
+
+add_task(async function testReopen() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+
+ let tab = await BrowserTestUtils.openNewForegroundTab({
+ gBrowser,
+ url: TEST_URL,
+ });
+ ok(
+ !tab.hasAttribute("usercontextid"),
+ "Tab with No Container should be opened"
+ );
+
+ let reopenMenu = await openReopenMenuForTab(tab);
+ checkMenuItem(reopenMenu, [1, 2, 3, 4], [0]);
+
+ let containerTab = await openTabInContainer(gBrowser, reopenMenu, "1");
+
+ is(
+ containerTab.getAttribute("usercontextid"),
+ "1",
+ "Tab with UCI=1 should be opened"
+ );
+ is(
+ containerTab.linkedBrowser.currentURI.spec,
+ TEST_URL,
+ "Same page should be opened"
+ );
+
+ reopenMenu = await openReopenMenuForTab(containerTab);
+ checkMenuItem(reopenMenu, [0, 2, 3, 4], [1]);
+
+ let noContainerTab = await openTabInContainer(gBrowser, reopenMenu, "0");
+
+ ok(
+ !noContainerTab.hasAttribute("usercontextid"),
+ "Tab with no UCI should be opened"
+ );
+ is(
+ noContainerTab.linkedBrowser.currentURI.spec,
+ TEST_URL,
+ "Same page should be opened"
+ );
+
+ BrowserTestUtils.removeTab(tab);
+ BrowserTestUtils.removeTab(containerTab);
+ BrowserTestUtils.removeTab(noContainerTab);
+});
+
+add_task(async function testDisabled() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", false]],
+ });
+
+ let tab = await BrowserTestUtils.openNewForegroundTab({
+ gBrowser,
+ url: TEST_URL,
+ });
+ ok(
+ !tab.hasAttribute("usercontextid"),
+ "Tab with No Container should be opened"
+ );
+
+ let tabMenu = await openTabMenuFor(tab);
+ let reopenItem = document.getElementById("context_reopenInContainer");
+ ok(reopenItem.hidden, "Reopen in Container item should be hidden");
+
+ // Close the tab menu.
+ tabMenu.hidePopup();
+
+ BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function testPrivateMode() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+
+ let privateWindow = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+ let tab = await BrowserTestUtils.openNewForegroundTab({
+ gBrowser: privateWindow.gBrowser,
+ url: TEST_URL,
+ });
+ ok(
+ !tab.hasAttribute("usercontextid"),
+ "Tab with No Container should be opened"
+ );
+
+ let tabMenu = await openTabMenuFor(tab);
+ let reopenItem = privateWindow.document.getElementById(
+ "context_reopenInContainer"
+ );
+ ok(reopenItem.hidden, "Reopen in Container item should be hidden");
+
+ // Close the tab menu.
+ tabMenu.hidePopup();
+
+ await BrowserTestUtils.closeWindow(privateWindow);
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_restore_getCookiesWithOriginAttributes.js b/browser/components/contextualidentity/test/browser/browser_restore_getCookiesWithOriginAttributes.js
new file mode 100644
index 0000000000..6bb617094e
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_restore_getCookiesWithOriginAttributes.js
@@ -0,0 +1,122 @@
+/*
+ * Bug 1334587 - A Test case for checking whether forgetting APIs are working for cookies.
+ */
+
+const CC = Components.Constructor;
+
+const TEST_HOST = "example.com";
+const TEST_URL =
+ "http://" +
+ TEST_HOST +
+ "/browser/browser/components/contextualidentity/test/browser/";
+
+const USER_CONTEXTS = ["default", "personal", "work"];
+
+const DELETE_CONTEXT = 1;
+const COOKIE_NAME = "userContextId";
+
+//
+// Support functions.
+//
+
+async function openTabInUserContext(uri, userContextId) {
+ // Open the tab in the correct userContextId.
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // Select tab and make sure its browser is focused.
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+ return { tab, browser };
+}
+
+function getCookiesForOA(host, userContextId) {
+ return Services.cookies.getCookiesFromHost(host, { userContextId });
+}
+
+//
+// Test functions.
+//
+
+add_setup(async function() {
+ // Make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+});
+
+function checkCookies(ignoreContext = null) {
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ if (ignoreContext && userContextId === String(ignoreContext)) {
+ continue;
+ }
+ let cookies = getCookiesForOA(TEST_HOST, userContextId);
+ ok(cookies.length, "Cookies available");
+
+ let foundCookie = cookies[0];
+ is(foundCookie.name, COOKIE_NAME, "Check cookie name");
+ is(foundCookie.value, USER_CONTEXTS[userContextId], "Check cookie value");
+ }
+}
+
+function deleteCookies(onlyContext = null) {
+ // Using getCookiesWithOriginAttributes() to get all cookies for a certain
+ // domain by using the originAttributes pattern, and clear all these cookies.
+ for (let cookie of Services.cookies.getCookiesWithOriginAttributes(
+ JSON.stringify({}),
+ TEST_HOST
+ )) {
+ if (!onlyContext || cookie.originAttributes.userContextId == onlyContext) {
+ Services.cookies.remove(
+ cookie.host,
+ cookie.name,
+ cookie.path,
+ cookie.originAttributes
+ );
+ }
+ }
+}
+
+add_task(async function test_cookie_getCookiesWithOriginAttributes() {
+ let tabs = [];
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Load the page in different contexts and set a cookie
+ // which should only be visible in that context.
+ let value = USER_CONTEXTS[userContextId];
+
+ // Open our tab in the given user context.
+ tabs[userContextId] = await openTabInUserContext(
+ TEST_URL + "file_reflect_cookie_into_title.html?" + value,
+ userContextId
+ );
+
+ // Close this tab.
+ BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+
+ // Check that cookies have been set properly.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let cookies = getCookiesForOA(TEST_HOST, userContextId);
+ ok(cookies.length, "Cookies available");
+
+ let foundCookie = cookies[0];
+ is(foundCookie.name, COOKIE_NAME, "Check cookie name");
+ is(foundCookie.value, USER_CONTEXTS[userContextId], "Check cookie value");
+ }
+ checkCookies();
+
+ deleteCookies(DELETE_CONTEXT);
+
+ checkCookies(DELETE_CONTEXT);
+
+ deleteCookies();
+
+ // Check that whether cookies has been cleared.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let cookies = getCookiesForOA(TEST_HOST, userContextId);
+ ok(!cookies.length, "No Cookie should be here");
+ }
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_saveLink.js b/browser/components/contextualidentity/test/browser/browser_saveLink.js
new file mode 100644
index 0000000000..d8df584d59
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_saveLink.js
@@ -0,0 +1,135 @@
+"use strict";
+
+const BASE_ORIGIN = "https://example.com";
+const URI =
+ BASE_ORIGIN +
+ "/browser/browser/components/contextualidentity/test/browser/saveLink.sjs";
+
+let MockFilePicker = SpecialPowers.MockFilePicker;
+MockFilePicker.init(window);
+
+add_setup(async function() {
+ info("Setting the prefs.");
+
+ // make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ // This test does a redirect from https to http and it checks the
+ // cookies. This is incompatible with the cookie SameSite schemeful
+ // feature and we need to disable it.
+ ["network.cookie.sameSite.schemeful", false],
+ // This Test trys to download mixed content
+ // so we need to make sure that this is not blocked
+ ["dom.block_download_insecure", false],
+ ],
+ });
+});
+
+add_task(async function test() {
+ info("Let's open a new window");
+ let win = await BrowserTestUtils.openNewBrowserWindow();
+
+ info("Opening tab with UCI: 1");
+ let tab = BrowserTestUtils.addTab(win.gBrowser, URI + "?UCI=1", {
+ userContextId: 1,
+ });
+
+ // select tab and make sure its browser is focused
+ win.gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ info("Waiting to load content");
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ let popupShownPromise = BrowserTestUtils.waitForEvent(
+ win.document,
+ "popupshown"
+ );
+
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ "#fff",
+ { type: "contextmenu", button: 2 },
+ win.gBrowser.selectedBrowser
+ );
+ info("Right clicked!");
+
+ await popupShownPromise;
+ info("Context menu opened");
+
+ info("Let's create a temporary dir");
+ let tempDir = createTemporarySaveDirectory();
+ let destFile;
+
+ MockFilePicker.displayDirectory = tempDir;
+ MockFilePicker.showCallback = fp => {
+ info("MockFilePicker showCallback");
+
+ let fileName = fp.defaultString;
+ destFile = tempDir.clone();
+ destFile.append(fileName);
+
+ MockFilePicker.setFiles([destFile]);
+ MockFilePicker.filterIndex = 1; // kSaveAsType_URL
+
+ info("MockFilePicker showCallback done");
+ };
+
+ let transferCompletePromise = new Promise(resolve => {
+ function onTransferComplete(downloadSuccess) {
+ ok(downloadSuccess, "File should have been downloaded successfully");
+ resolve();
+ }
+
+ mockTransferCallback = onTransferComplete;
+ mockTransferRegisterer.register();
+ });
+
+ registerCleanupFunction(function() {
+ mockTransferRegisterer.unregister();
+ MockFilePicker.cleanup();
+ tempDir.remove(true);
+ });
+
+ // Select "Save Link As" option from context menu
+ let saveLinkCommand = win.document.getElementById("context-savelink");
+ info("saveLinkCommand: " + saveLinkCommand);
+ saveLinkCommand.doCommand();
+
+ let contextMenu = win.document.getElementById("contentAreaContextMenu");
+ let popupHiddenPromise = BrowserTestUtils.waitForEvent(
+ contextMenu,
+ "popuphidden"
+ );
+ contextMenu.hidePopup();
+ await popupHiddenPromise;
+ info("popup hidden");
+
+ await transferCompletePromise;
+
+ // Let's query the SJS to know if the download happened with the correct cookie.
+ let response = await fetch(URI + "?result=1");
+ let text = await response.text();
+ is(text, "Result:UCI=1", "Correct cookie used: -" + text + "-");
+
+ info("Closing the window");
+ await BrowserTestUtils.closeWindow(win);
+});
+
+/* import-globals-from ../../../../../toolkit/content/tests/browser/common/mockTransfer.js */
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/toolkit/content/tests/browser/common/mockTransfer.js",
+ this
+);
+
+function createTemporarySaveDirectory() {
+ let saveDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ saveDir.append("testsavedir");
+ if (!saveDir.exists()) {
+ info("create testsavedir!");
+ saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+ }
+ info("return from createTempSaveDir: " + saveDir.path);
+ return saveDir;
+}
diff --git a/browser/components/contextualidentity/test/browser/browser_serviceworkers.js b/browser/components/contextualidentity/test/browser/browser_serviceworkers.js
new file mode 100644
index 0000000000..763d2be761
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_serviceworkers.js
@@ -0,0 +1,121 @@
+let swm = Cc["@mozilla.org/serviceworkers/manager;1"].getService(
+ Ci.nsIServiceWorkerManager
+);
+
+const BASE_ORIGIN = "https://example.com";
+const URI =
+ BASE_ORIGIN +
+ "/browser/browser/components/contextualidentity/test/browser/serviceworker.html";
+const NUM_USER_CONTEXTS = 3;
+
+// opens `uri' in a new tab with the provided userContextId and focuses it.
+// returns the newly opened tab
+function openTabInUserContext(uri, userContextId) {
+ // open the tab in the correct userContextId
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // select tab and make sure its browser is focused
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ return tab;
+}
+
+add_setup(async function() {
+ // make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ ["dom.ipc.processCount", 1],
+ ],
+ });
+});
+
+let infos = [];
+
+add_task(async function test() {
+ // Open the same URI in multiple user contexts, and make sure we have a
+ // separate service worker in each of the contexts
+ for (
+ let userContextId = 0;
+ userContextId < NUM_USER_CONTEXTS;
+ userContextId++
+ ) {
+ // Open a tab in given user contexts
+ let tab = openTabInUserContext(URI, userContextId);
+
+ // wait for tab load
+ await BrowserTestUtils.browserLoaded(gBrowser.getBrowserForTab(tab));
+
+ // remove the tab
+ gBrowser.removeTab(tab);
+ }
+
+ if (!allRegistered()) {
+ await promiseAllRegistered();
+ }
+ ok(true, "all service workers are registered");
+
+ // Unregistered all service workers added in this test
+ for (let info of infos) {
+ await promiseUnregister(info);
+ }
+});
+
+function allRegistered() {
+ let results = [];
+ let registrations = swm.getAllRegistrations();
+ for (let i = 0; i < registrations.length; i++) {
+ let info = registrations.queryElementAt(
+ i,
+ Ci.nsIServiceWorkerRegistrationInfo
+ );
+ let principal = info.principal;
+ if (principal.originNoSuffix === BASE_ORIGIN) {
+ results[principal.userContextId] = true;
+ infos[principal.userContextId] = info;
+ }
+ }
+ for (
+ let userContextId = 0;
+ userContextId < NUM_USER_CONTEXTS;
+ userContextId++
+ ) {
+ if (!results[userContextId]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+function promiseAllRegistered() {
+ return new Promise(function(resolve) {
+ let listener = {
+ onRegister() {
+ if (allRegistered()) {
+ swm.removeListener(listener);
+ resolve();
+ }
+ },
+ };
+ swm.addListener(listener);
+ });
+}
+
+function promiseUnregister(info) {
+ return new Promise(function(resolve) {
+ swm.unregister(
+ info.principal,
+ {
+ unregisterSucceeded(aState) {
+ ok(aState, "ServiceWorkerRegistration exists");
+ resolve();
+ },
+ unregisterFailed(aState) {
+ ok(false, "unregister should succeed");
+ },
+ },
+ info.scope
+ );
+ });
+}
diff --git a/browser/components/contextualidentity/test/browser/browser_tab_color_update.js b/browser/components/contextualidentity/test/browser/browser_tab_color_update.js
new file mode 100644
index 0000000000..e4ae649d0a
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_tab_color_update.js
@@ -0,0 +1,42 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+async function openTabInUserContext(uri, userContextId) {
+ // open the tab in the correct userContextId
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser, false, uri);
+ return { tab };
+}
+
+/**
+ * When a container's colour changes, the tab colour should update.
+ */
+add_task(async function test_tab_color_updates() {
+ const kId = 2;
+ let { tab } = await openTabInUserContext("https://example.com/", kId);
+ let contextIdInfo = ContextualIdentityService.getPublicIdentityFromId(kId);
+ ok(
+ tab.classList.contains("identity-color-" + contextIdInfo.color),
+ `Should use color ${contextIdInfo.color} for tab`
+ );
+
+ // The container has a localized name which isn't in the contextIdInfo object.
+ // We need to pass a valid name for the update to go through, so make one up.
+ let name = "Foo";
+ // Don't hardcode a colour so changes in defaults won't cause the following
+ // test to fail or be a no-op.
+ let otherColor = contextIdInfo.color == "green" ? "orange" : "green";
+ registerCleanupFunction(() => ContextualIdentityService.resetDefault());
+ ContextualIdentityService.update(kId, name, contextIdInfo.icon, otherColor);
+
+ ok(
+ tab.classList.contains("identity-color-" + otherColor),
+ `Should use color ${otherColor} for tab after update`
+ );
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_usercontext.js b/browser/components/contextualidentity/test/browser/browser_usercontext.js
new file mode 100644
index 0000000000..79f7c2e646
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_usercontext.js
@@ -0,0 +1,88 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const USER_CONTEXTS = ["default", "personal", "work"];
+
+const BASE_URI =
+ "http://mochi.test:8888/browser/browser/components/" +
+ "contextualidentity/test/browser/file_reflect_cookie_into_title.html";
+
+// opens `uri' in a new tab with the provided userContextId and focuses it.
+// returns the newly opened tab
+function openTabInUserContext(uri, userContextId) {
+ // open the tab in the correct userContextId
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // select tab and make sure its browser is focused
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ return tab;
+}
+
+add_setup(async function() {
+ // make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ ["dom.ipc.processCount", 1],
+ ],
+ });
+});
+
+add_task(async function test() {
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // load the page in 3 different contexts and set a cookie
+ // which should only be visible in that context
+ let cookie = USER_CONTEXTS[userContextId];
+
+ // open our tab in the given user context
+ let tab = openTabInUserContext(BASE_URI + "?" + cookie, userContextId);
+
+ // wait for tab load
+ await BrowserTestUtils.browserLoaded(gBrowser.getBrowserForTab(tab));
+
+ // remove the tab
+ gBrowser.removeTab(tab);
+ }
+
+ {
+ // Set a cookie in a different context so we can detect if that affects
+ // cross-context properly. If we don't do that, we get an UNEXPECTED-PASS
+ // for the localStorage case for the last tab we set.
+ let tab = openTabInUserContext(BASE_URI + "?foo", 9999);
+ await BrowserTestUtils.browserLoaded(gBrowser.getBrowserForTab(tab));
+ gBrowser.removeTab(tab);
+ }
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Load the page without setting the cookie this time
+ let expectedContext = USER_CONTEXTS[userContextId];
+
+ let tab = openTabInUserContext(BASE_URI, userContextId);
+
+ // wait for load
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ // get the title
+ let title = browser.contentTitle.trim().split("|");
+
+ // check each item in the title and validate it meets expectatations
+ for (let part of title) {
+ let [storageMethodName, value] = part.split("=");
+ is(
+ value,
+ expectedContext,
+ "the title reflects the expected contextual identity of " +
+ expectedContext +
+ " for method " +
+ storageMethodName +
+ ": " +
+ value
+ );
+ }
+
+ gBrowser.removeTab(tab);
+ }
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_usercontextid_new_window.js b/browser/components/contextualidentity/test/browser/browser_usercontextid_new_window.js
new file mode 100644
index 0000000000..b018a1f4c3
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_usercontextid_new_window.js
@@ -0,0 +1,93 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the content of new browser windows have the expected
+// userContextId when it passed as the window arguments.
+
+const TEST_URI =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "http://mochi.test:8888"
+ ) + "empty_file.html";
+
+function openWindowWithUserContextId(userContextId, isPrivate) {
+ let flags = "chrome,dialog=no,all";
+ if (isPrivate) {
+ flags += ",private";
+ }
+
+ let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+
+ let urlSupports = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+ );
+ urlSupports.data = TEST_URI;
+ args.appendElement(urlSupports);
+
+ args.appendElement(null);
+ args.appendElement(null);
+ args.appendElement(null);
+ args.appendElement(null);
+
+ let userContextIdSupports = Cc[
+ "@mozilla.org/supports-PRUint32;1"
+ ].createInstance(Ci.nsISupportsPRUint32);
+ userContextIdSupports.data = userContextId;
+ args.appendElement(userContextIdSupports);
+
+ args.appendElement(Services.scriptSecurityManager.getSystemPrincipal());
+ args.appendElement(Services.scriptSecurityManager.getSystemPrincipal());
+ args.appendElement(Services.scriptSecurityManager.getSystemPrincipal());
+
+ let windowPromise = BrowserTestUtils.waitForNewWindow({ url: TEST_URI });
+ Services.ww.openWindow(
+ null,
+ AppConstants.BROWSER_CHROME_URL,
+ "_blank",
+ flags,
+ args
+ );
+ return windowPromise;
+}
+
+add_setup(async function() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+});
+
+add_task(async function test_new_window() {
+ let win = await openWindowWithUserContextId(1, false);
+
+ await SpecialPowers.spawn(win.gBrowser.selectedBrowser, [TEST_URI], url => {
+ Assert.equal(content.document.URL, url, "expected document URL");
+ let { originAttributes } = content.document.nodePrincipal;
+ Assert.equal(originAttributes.userContextId, 1, "expected userContextId");
+ Assert.equal(
+ originAttributes.privateBrowsingId,
+ 0,
+ "expected non-private context"
+ );
+ });
+
+ await BrowserTestUtils.closeWindow(win);
+});
+
+add_task(async function test_new_private_window() {
+ let win = await openWindowWithUserContextId(1, true);
+
+ await SpecialPowers.spawn(win.gBrowser.selectedBrowser, [TEST_URI], url => {
+ Assert.equal(content.document.URL, url, "expected document URL");
+ let { originAttributes } = content.document.nodePrincipal;
+ Assert.equal(originAttributes.userContextId, 1, "expected userContextId");
+ Assert.equal(
+ originAttributes.privateBrowsingId,
+ 1,
+ "expected private context"
+ );
+ });
+
+ await BrowserTestUtils.closeWindow(win);
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_usercontextid_tabdrop.js b/browser/components/contextualidentity/test/browser/browser_usercontextid_tabdrop.js
new file mode 100644
index 0000000000..d61b4e5586
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_usercontextid_tabdrop.js
@@ -0,0 +1,181 @@
+"use strict";
+
+let EventUtils = {};
+Services.scriptloader.loadSubScript(
+ "chrome://mochikit/content/tests/SimpleTest/EventUtils.js",
+ EventUtils
+);
+
+/**
+ * Dragging an URL to a tab without userContextId set.
+ */
+add_task(async function() {
+ let tab = BrowserTestUtils.addTab(gBrowser, "http://example.com/");
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ let awaitDrop = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "drop");
+ let newTabPromise = BrowserTestUtils.waitForNewTab(
+ gBrowser,
+ "http://test1.example.com/",
+ true
+ );
+
+ // A drop type of "link" onto an existing tab would normally trigger a
+ // load in that same tab, but tabbrowser code in _getDragTargetTab treats
+ // drops on the outer edges of a tab differently (loading a new tab
+ // instead). Make events created by synthesizeDrop have all of their
+ // coordinates set to 0 (screenX/screenY), so they're treated as drops
+ // on the outer edge of the tab, thus they open new tabs.
+ let event = {
+ clientX: 0,
+ clientY: 0,
+ screenX: 0,
+ screenY: 0,
+ };
+ EventUtils.synthesizeDrop(
+ tab,
+ tab,
+ [[{ type: "text/plain", data: "http://test1.example.com/" }]],
+ "link",
+ window,
+ undefined,
+ event
+ );
+
+ await awaitDrop;
+
+ let tab2 = await newTabPromise;
+ Assert.ok(
+ !tab2.hasAttribute("usercontextid"),
+ "Tab shouldn't have usercontextid attribute"
+ );
+
+ await SpecialPowers.spawn(tab2.linkedBrowser, [], async function() {
+ Assert.equal(content.document.documentURI, "http://test1.example.com/");
+ Assert.equal(
+ content.document.nodePrincipal.originAttributes.userContextId,
+ 0
+ );
+
+ // referrer is empty when urls are dragged to new or existing tabs.
+ // If this changes in the future, it would be okay to send the referrer
+ // in this case because we are creating a new tab with the default
+ // usercontextid as the original tab.
+ Assert.equal(content.document.referrer, "", "referrer should be empty");
+ });
+
+ BrowserTestUtils.removeTab(tab);
+ BrowserTestUtils.removeTab(tab2);
+});
+
+/**
+ * When dragging an URL to a new tab, the new tab should have the same
+ * userContextId as the original tab.
+ */
+add_task(async function() {
+ let tab = BrowserTestUtils.addTab(gBrowser, "http://example.com/", {
+ userContextId: 1,
+ });
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ let awaitDrop = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "drop");
+ let newTabPromise = BrowserTestUtils.waitForNewTab(
+ gBrowser,
+ "http://test1.example.com/",
+ true
+ );
+
+ // A drop type of "link" onto an existing tab would normally trigger a
+ // load in that same tab, but tabbrowser code in _getDragTargetTab treats
+ // drops on the outer edges of a tab differently (loading a new tab
+ // instead). Make events created by synthesizeDrop have all of their
+ // coordinates set to 0 (screenX/screenY), so they're treated as drops
+ // on the outer edge of the tab, thus they open new tabs.
+ let event = {
+ clientX: 0,
+ clientY: 0,
+ screenX: 0,
+ screenY: 0,
+ };
+ EventUtils.synthesizeDrop(
+ tab,
+ tab,
+ [[{ type: "text/plain", data: "http://test1.example.com/" }]],
+ "link",
+ window,
+ undefined,
+ event
+ );
+
+ await awaitDrop;
+
+ let tab2 = await newTabPromise;
+ Assert.equal(tab2.getAttribute("usercontextid"), 1);
+
+ await SpecialPowers.spawn(tab2.linkedBrowser, [], async function() {
+ Assert.equal(content.document.documentURI, "http://test1.example.com/");
+ Assert.equal(
+ content.document.nodePrincipal.originAttributes.userContextId,
+ 1
+ );
+
+ // referrer is empty when urls are dragged to new or existing tabs.
+ // If this changes in the future, it would be okay to send the referrer
+ // in this case because we are creating a new tab with the same
+ // usercontextid as the original tab.
+ Assert.equal(content.document.referrer, "", "referrer should be empty");
+ });
+
+ BrowserTestUtils.removeTab(tab);
+ BrowserTestUtils.removeTab(tab2);
+});
+
+/**
+ * When dragging a URL from one tab or link on a tab to an existing tab, the
+ * existing tab should not change its userContextId.
+ * Ex: if you drag a link from tab 1 with userContext 1 to tab 2 with
+ * userContext 2, the link will open in tab 2 with userContext 2.
+ */
+add_task(async function() {
+ let tab = BrowserTestUtils.addTab(gBrowser, "http://example.com/", {
+ userContextId: 1,
+ });
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ let tab2 = BrowserTestUtils.addTab(gBrowser, "http://example.org/", {
+ userContextId: 2,
+ });
+ await BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
+
+ let awaitDrop = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "drop");
+
+ EventUtils.synthesizeDrop(
+ tab,
+ tab2,
+ [[{ type: "text/plain", data: "http://test1.example.com/" }]],
+ "link",
+ window
+ );
+
+ await awaitDrop;
+ Assert.equal(tab2.getAttribute("usercontextid"), 2);
+
+ await BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
+
+ await SpecialPowers.spawn(tab2.linkedBrowser, [], async function() {
+ Assert.equal(content.document.documentURI, "http://test1.example.com/");
+ Assert.equal(
+ content.document.nodePrincipal.originAttributes.userContextId,
+ 2
+ );
+
+ // referrer is empty when urls are dragged to new or existing tabs.
+ // If this changes in the future, we should ensure that we are not sending
+ // a referrer for this case! When opening links across user contexts, we
+ // don't want the referrer to follow the user from one context to another.
+ Assert.equal(content.document.referrer, "", "referrer should be empty");
+ });
+
+ BrowserTestUtils.removeTab(tab);
+ BrowserTestUtils.removeTab(tab2);
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_windowName.js b/browser/components/contextualidentity/test/browser/browser_windowName.js
new file mode 100644
index 0000000000..71c003d280
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_windowName.js
@@ -0,0 +1,80 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const USER_CONTEXTS = ["default", "personal", "work"];
+
+const BASE_URI =
+ "http://mochi.test:8888/browser/browser/components/" +
+ "contextualidentity/test/browser/empty_file.html";
+
+add_setup(async function() {
+ // make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ ["browser.link.open_newwindow", 3],
+ ],
+ });
+});
+
+add_task(async function test() {
+ info("Creating first tab...");
+ let tab1 = BrowserTestUtils.addTab(gBrowser, BASE_URI + "?old", {
+ userContextId: 1,
+ });
+ let browser1 = gBrowser.getBrowserForTab(tab1);
+ await BrowserTestUtils.browserLoaded(browser1);
+ await SpecialPowers.spawn(browser1, [], function(opts) {
+ content.window.name = "tab-1";
+ });
+
+ info("Creating second tab...");
+ let tab2 = BrowserTestUtils.addTab(gBrowser, BASE_URI + "?old", {
+ userContextId: 2,
+ });
+ let browser2 = gBrowser.getBrowserForTab(tab2);
+ await BrowserTestUtils.browserLoaded(browser2);
+ await SpecialPowers.spawn(browser2, [], function(opts) {
+ content.window.name = "tab-2";
+ });
+
+ // Let's try to open a window from tab1 with a name 'tab-2'.
+ info("Opening a window from the first tab...");
+ await SpecialPowers.spawn(
+ browser1,
+ [{ url: BASE_URI + "?new" }],
+ async function(opts) {
+ await new content.window.wrappedJSObject.Promise(resolve => {
+ let w = content.window.wrappedJSObject.open(opts.url, "tab-2");
+ w.onload = function() {
+ resolve();
+ };
+ });
+ }
+ );
+
+ is(browser1.contentTitle, "?old", "Tab1 title must be 'old'");
+ is(browser1.contentPrincipal.userContextId, 1, "Tab1 UCI must be 1");
+
+ is(browser2.contentTitle, "?old", "Tab2 title must be 'old'");
+ is(browser2.contentPrincipal.userContextId, 2, "Tab2 UCI must be 2");
+
+ let found = false;
+ for (let i = 0; i < gBrowser.tabs.length; ++i) {
+ let tab = gBrowser.tabs[i];
+ let browser = gBrowser.getBrowserForTab(tab);
+ if (browser.contentTitle == "?new") {
+ is(browser.contentPrincipal.userContextId, 1, "Tab3 UCI must be 1");
+ isnot(browser, browser1, "Tab3 is not browser 1");
+ isnot(browser, browser2, "Tab3 is not browser 2");
+ gBrowser.removeTab(tab);
+ found = true;
+ break;
+ }
+ }
+
+ ok(found, "We have tab3");
+
+ gBrowser.removeTab(tab1);
+ gBrowser.removeTab(tab2);
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_windowOpen.js b/browser/components/contextualidentity/test/browser/browser_windowOpen.js
new file mode 100644
index 0000000000..d289425066
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_windowOpen.js
@@ -0,0 +1,41 @@
+"use strict";
+
+// Here we want to test that a new opened window shows the same UI of the
+// parent one if this has been loaded from a particular container.
+
+const BASE_URI =
+ "http://mochi.test:8888/browser/browser/components/" +
+ "contextualidentity/test/browser/empty_file.html";
+
+add_setup(async function() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ ["browser.link.open_newwindow", 2],
+ ],
+ });
+});
+
+add_task(async function test() {
+ info("Creating a tab with UCI = 1...");
+ let tab = BrowserTestUtils.addTab(gBrowser, BASE_URI, { userContextId: 1 });
+ is(tab.getAttribute("usercontextid"), "1", "New tab has UCI equal 1");
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ info("Opening a new window from this tab...");
+ let newWinPromise = BrowserTestUtils.waitForNewWindow({ url: BASE_URI });
+ SpecialPowers.spawn(browser, [BASE_URI], function(url) {
+ content.window.newWindow = content.window.open(url, "_blank");
+ });
+
+ let newWin = await newWinPromise;
+ let newTab = newWin.gBrowser.selectedTab;
+
+ is(newTab.getAttribute("usercontextid"), "1", "New tab has UCI equal 1");
+
+ info("Closing the new window and tab...");
+ await BrowserTestUtils.closeWindow(newWin);
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/components/contextualidentity/test/browser/empty_file.html b/browser/components/contextualidentity/test/browser/empty_file.html
new file mode 100644
index 0000000000..c6d11dcd57
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/empty_file.html
@@ -0,0 +1,5 @@
+<html><body>
+<script>
+document.title = window.location.search;
+</script>
+</body></html>
diff --git a/browser/components/contextualidentity/test/browser/favicon-normal32.png b/browser/components/contextualidentity/test/browser/favicon-normal32.png
new file mode 100644
index 0000000000..5535363c94
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/favicon-normal32.png
Binary files differ
diff --git a/browser/components/contextualidentity/test/browser/file_reflect_cookie_into_title.html b/browser/components/contextualidentity/test/browser/file_reflect_cookie_into_title.html
new file mode 100644
index 0000000000..cc05453507
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/file_reflect_cookie_into_title.html
@@ -0,0 +1,22 @@
+<html>
+ <head>
+ <meta charset="UTF-8">
+ <title>title not set</title>
+ <script>
+ // if we have a query string, use it to set the cookie and localStorage
+ if (window.location.search.length) {
+ let context_name = window.location.search.substr(1);
+ document.cookie = "userContextId=" + context_name;
+ localStorage.setItem("userContext", context_name);
+ }
+
+ // get the cookie
+ let [name, val] = document.cookie.split("=");
+
+ // set the title to reflect the cookie and local storage values we find
+ document.title = "cookie=" + val + "|"
+ + "local=" + localStorage.getItem("userContext");
+ </script>
+ </head>
+ <body></body>
+</html>
diff --git a/browser/components/contextualidentity/test/browser/file_set_storages.html b/browser/components/contextualidentity/test/browser/file_set_storages.html
new file mode 100644
index 0000000000..96c46f9062
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/file_set_storages.html
@@ -0,0 +1,41 @@
+<html>
+ <head>
+ <meta charset="UTF-8">
+ <title>Bug 1238183</title>
+ </head>
+ <body>
+ <script type="application/javascript">
+ "use strict";
+
+ // if we have a query string, use it to set storages
+ if (window.location.search.length) {
+ let context_name = window.location.search.substr(1);
+ localStorage.setItem("userContext", context_name);
+ sessionStorage.setItem("userContext", context_name);
+
+ let request = indexedDB.open("idb", 1);
+
+ request.onerror = function() {
+ throw new Error("error opening db connection");
+ };
+
+ request.onupgradeneeded = event => {
+ let db = event.target.result;
+ let store = db.createObjectStore("obj", { keyPath: "id" });
+ store.createIndex("userContext", "userContext", { unique: false });
+ };
+
+ request.onsuccess = event => {
+ let db = request.result;
+ let transaction = db.transaction(["obj"], "readwrite");
+ let store = transaction.objectStore("obj");
+ store.add({id: 1, userContext: context_name});
+
+ transaction.oncomplete = () => {
+ db.close();
+ };
+ };
+ }
+ </script>
+ </body>
+</html>
diff --git a/browser/components/contextualidentity/test/browser/head.js b/browser/components/contextualidentity/test/browser/head.js
new file mode 100644
index 0000000000..b5bf451d25
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/head.js
@@ -0,0 +1,48 @@
+async function openTabMenuFor(tab) {
+ let tabMenu = tab.ownerDocument.getElementById("tabContextMenu");
+
+ let tabMenuShown = BrowserTestUtils.waitForEvent(tabMenu, "popupshown");
+ EventUtils.synthesizeMouseAtCenter(
+ tab,
+ { type: "contextmenu" },
+ tab.ownerGlobal
+ );
+ await tabMenuShown;
+
+ return tabMenu;
+}
+
+async function openReopenMenuForTab(tab) {
+ await openTabMenuFor(tab);
+
+ let reopenItem = tab.ownerDocument.getElementById(
+ "context_reopenInContainer"
+ );
+ ok(!reopenItem.hidden, "Reopen in Container item should be shown");
+
+ const menuPopup = reopenItem.menupopup;
+ const menuPopupPromise = BrowserTestUtils.waitForEvent(
+ menuPopup,
+ "popupshown"
+ );
+ info(`About to open a popup`);
+ reopenItem.openMenu(true);
+ info(`Waiting for the menu popup promise`);
+ await menuPopupPromise;
+ info(`Awaited menu popup promise`);
+ return menuPopup;
+}
+
+function openTabInContainer(gBrowser, url, reopenMenu, id) {
+ let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, url, true);
+ let menuitem = reopenMenu.querySelector(
+ `menuitem[data-usercontextid="${id}"]`
+ );
+ info(`about to activate item`);
+ reopenMenu.activateItem(menuitem);
+ return tabPromise;
+}
+
+function loadTestSubscript(filePath) {
+ Services.scriptloader.loadSubScript(new URL(filePath, gTestPath).href, this);
+}
diff --git a/browser/components/contextualidentity/test/browser/saveLink.sjs b/browser/components/contextualidentity/test/browser/saveLink.sjs
new file mode 100644
index 0000000000..bbdb756364
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/saveLink.sjs
@@ -0,0 +1,55 @@
+const HTTP_ORIGIN = "http://example.com";
+const HTTPS_ORIGIN = "https://example.com";
+const URI_PATH =
+ "/browser/browser/components/contextualidentity/test/browser/saveLink.sjs";
+
+Cu.importGlobalProperties(["URLSearchParams"]);
+
+function handleRequest(aRequest, aResponse) {
+ var params = new URLSearchParams(aRequest.queryString);
+
+ // This is the first request, where we set the cookie.
+ if (params.has("UCI")) {
+ aResponse.setStatusLine(aRequest.httpVersion, 200);
+ aResponse.setHeader("Set-Cookie", "UCI=" + params.get("UCI"));
+ aResponse.write(
+ "<html><body><a href='" +
+ HTTPS_ORIGIN +
+ URI_PATH +
+ "?redirect=1' id='fff'>this is a link</a></body></html>"
+ );
+ return;
+ }
+
+ // Second request. This is the save-as content, but we make a redirect to see
+ // if we are able to follow it.
+ if (params.has("redirect")) {
+ aResponse.setStatusLine(aRequest.httpVersion, 302, "Found");
+ aResponse.setHeader(
+ "Location",
+ HTTP_ORIGIN + URI_PATH + "?download=1",
+ false
+ );
+ aResponse.write("Redirect!");
+ return;
+ }
+
+ // This is the 3rd request where we offer the content to be saved.
+ if (params.has("download")) {
+ setState("downloadUCI", aRequest.getHeader("Cookie"));
+ aResponse.setStatusLine(aRequest.httpVersion, 200);
+ aResponse.write("All Good!");
+ return;
+ }
+
+ // This is the last request to check that the download happened with the correct cookie
+ if (params.has("result")) {
+ aResponse.setStatusLine(aRequest.httpVersion, 200);
+ aResponse.write("Result:" + getState("downloadUCI"));
+ return;
+ }
+
+ // We should not be here!
+ aResponse.setStatusLine(aRequest.httpVersion, 500);
+ aResponse.write("ERROR!!!");
+}
diff --git a/browser/components/contextualidentity/test/browser/serviceworker.html b/browser/components/contextualidentity/test/browser/serviceworker.html
new file mode 100644
index 0000000000..11edd001a2
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/serviceworker.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <script>
+ navigator.serviceWorker.register("worker.js");
+ </script>
+ </head>
+ <body>
+ This is a test page.
+ </body>
+<html>
diff --git a/browser/components/contextualidentity/test/browser/worker.js b/browser/components/contextualidentity/test/browser/worker.js
new file mode 100644
index 0000000000..2aba167d18
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/worker.js
@@ -0,0 +1 @@
+// empty worker, always succeed!