├── Extras
├── 16.png
├── 256.png
├── 32.png
├── 48.png
├── EdgeIcon.ico
├── EdgeIcon.png
└── edgelogobig.png
├── INSTALLATION.md
├── README.md
├── ffroot
├── config.js
└── defaults
│ └── pref
│ ├── channel-prefs.js
│ └── config-prefs.js
├── images
├── edgelogo.png
├── example.png
└── guide.png
└── profile
└── chrome
├── JS
├── activity_throbber.uc.js
├── change-toolbar-icon-values.uc.js
├── favicon_in_urlbar.uc.js
└── space_and_separator_restorer.uc.js
├── alltabs.png
├── arrow.png
├── checkmark.png
├── cheveron.png
├── close.png
├── ie.png
├── internet.png
├── items
├── copy.png
├── cut.png
├── download.png
├── email.png
├── extensions.png
├── favorites.png
├── fullscreen.png
├── history.png
├── home.png
├── left.png
├── paste.png
├── personal.png
├── print.png
├── reload.png
├── right.png
├── search.png
└── stop.png
├── newtab.png
├── radio.png
├── spinnerdown.png
├── throbber.gif
├── throbber_busy.gif
├── user.png
├── userChrome.css
└── utils
├── BootstrapLoader.jsm
├── RDFDataSource.jsm
├── RDFManifestConverter.jsm
├── aboutconfig
├── aboutconfig.xhtml
├── config.css
└── config.js
├── boot.jsm
├── chrome.manifest
├── passwordmgr
├── passwordManager.js
├── passwordManager.xhtml
└── passwordmgr.css
├── userChrome.jsm
├── userPrefs.jsm
└── xPref.jsm
/Extras/16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/Extras/16.png
--------------------------------------------------------------------------------
/Extras/256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/Extras/256.png
--------------------------------------------------------------------------------
/Extras/32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/Extras/32.png
--------------------------------------------------------------------------------
/Extras/48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/Extras/48.png
--------------------------------------------------------------------------------
/Extras/EdgeIcon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/Extras/EdgeIcon.ico
--------------------------------------------------------------------------------
/Extras/EdgeIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/Extras/EdgeIcon.png
--------------------------------------------------------------------------------
/Extras/edgelogobig.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/Extras/edgelogobig.png
--------------------------------------------------------------------------------
/INSTALLATION.md:
--------------------------------------------------------------------------------
1 | # Installation Instructions
2 | ## Copying files
3 | In Firefox, go to about:profiles and look for the profile in use. Open the "Root Directory" folder. Copy the contents of /profile into this folder.
4 | Next you can go to your Firefox installation directory (It should be C:/Program Files (x86)/Mozilla Firefox. Copy the contents of /ffroot into this directory, you may need administrator permissions.
5 |
6 | ## Setting up
7 | After restarting, you're going to need to go to about:config and enable these flags:
8 | - toolkit.legacyUserProfileCustomizations.stylesheets - set to true
9 | - svg.context-properties.content.enabled - set to true
10 | - ui.prefersReducedMotion - set to 1, you may need to create a new "Number" value
11 | - widget.non-native-theme.enabled - set to false,
12 | - extensions.unifiedExtensions.enabled - set to true, 109+ only
13 |
14 | then restart Firefox.
15 |
16 | ## UI Configuration
17 | After installation, right click on the tabs toolbar and press "Customize Toolbar". Here is a guide to help you:
18 |
19 | 
20 |
21 | **Make sure to enable Title Bar!!**
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Edge2003
2 | 
3 |
4 | ## What is this?
5 | **Edge2003** is a Firefox CSS mod that replicates **MondySpartan**'s [Microsoft Edge in 2003 Mockup](https://www.reddit.com/r/Windows_Redesign/comments/11mj5zr/microsoft_edge_in_2003_made_before_that_edge_from/) (to the best of my abilities).
6 |
7 | It was developed on Firefox 111, so it should work on most recent versions of Firefox, unless they mess everything up (and they probably will lol).
8 | UPDATE: The icons no longer display properly when you run this skin on Firefox 112... so, I'm gonna be working on that
9 |
10 | This theme is based on the IE6 theme made for the Windows eXPerience iso, which... you can find in [**Windows eXPerience**](https://archive.org/details/windows-experience-freestyle/), go check it out! And the page is based on [Aerofox by NWinXP.](https://github.com/NWinXP/Aerofox)
11 |
12 | ## Supported operating systems
13 | While you can use the theme on macOS or Linux, it will probably not look right. This theme was made to work with Windows 10 using a version of [BasicThemer](https://github.com/arukateru/BasicThemer5) and the [PerfectLuna10 theme](https://winclassic.boards.net/thread/1528/noir10-perfectluna10). While it has not been tested on other Windows versions, it should work as long as you have an XP-based theme.
14 |
15 | Tested on:
16 | - Windows 10 (Version 22H2) x64
17 | - Windows 10 (Version 1809) x64
18 |
19 | ## Screenshots
20 | - Windows 10 1809 x64 with BasicThemer3, SecureUxTheme and PerfectLuna10
21 |
22 | 
23 |
24 | ## Download
25 | Go to the [releases](https://github.com/SoniKast/Edge2003/releases "releases") page and download the linked .ZIP file.
26 |
27 | ## FAQ
28 | - **Why though?**
29 | No particular reason, I just thought the mockup looked really cool, like an improvement over IE6's base design.
30 | - **How do I install it?**
31 | Read [INSTALLATION.md](https://github.com/SoniKast/Edge2003/blob/main/INSTALLATION.md "INSTALLATION.md") for more detailed installation instructions.
32 | - **Can I have the Edge2003 icon?**
33 | Check out the Extras folder for [those](https://github.com/SoniKast/Edge2003/tree/main/Extras).
34 | - **Can I modify it?**
35 | You are free to do whatever you want with this theme, it's freely available after all!
36 | - **The icons are broken...**
37 | I'm working on a fix right now, don't worry. Though it's only compatible with Firefox 115 ESR.
38 |
39 | ###### Internet Explorer and Edge are a trademark of Microsoft Corporation. All assets from Internet Explorer used in this project belong to Microsoft Corporation.
40 | ###### Firefox and the Firefox logo are trademarks of the Mozilla Foundation.
41 | ###### Edge2003 assets by MondySpartan.
42 |
--------------------------------------------------------------------------------
/ffroot/config.js:
--------------------------------------------------------------------------------
1 | // skip 1st line
2 | try {
3 |
4 | let {
5 | classes: Cc,
6 | interfaces: Ci,
7 | manager: Cm,
8 | utils: Cu
9 | } = Components;
10 |
11 | let cmanifest = Cc['@mozilla.org/file/directory_service;1'].getService(Ci.nsIProperties).get('UChrm', Ci.nsIFile);
12 | cmanifest.append('utils');
13 | cmanifest.append('chrome.manifest');
14 |
15 | if(cmanifest.exists()){
16 | Cm.QueryInterface(Ci.nsIComponentRegistrar).autoRegister(cmanifest);
17 | Cu.import('chrome://userchromejs/content/boot.jsm');
18 | }
19 |
20 | } catch(ex) {};
--------------------------------------------------------------------------------
/ffroot/defaults/pref/channel-prefs.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 | //
5 | // This pref is in its own file for complex reasons. See the comment in
6 | // browser/app/Makefile.in, bug 756325, and bug 1431342 for details. Do not add
7 | // other prefs to this file.
8 |
9 | pref("app.update.channel", "release");
10 |
--------------------------------------------------------------------------------
/ffroot/defaults/pref/config-prefs.js:
--------------------------------------------------------------------------------
1 | pref("general.config.obscure_value", 0);
2 | pref("general.config.filename", "config.js");
3 | // Sandbox needs to be disabled in release and Beta versions
4 | pref("general.config.sandbox_enabled", false);
--------------------------------------------------------------------------------
/images/edgelogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/images/edgelogo.png
--------------------------------------------------------------------------------
/images/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/images/example.png
--------------------------------------------------------------------------------
/images/guide.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/images/guide.png
--------------------------------------------------------------------------------
/profile/chrome/JS/activity_throbber.uc.js:
--------------------------------------------------------------------------------
1 | Components.utils.import("resource:///modules/CustomizableUI.jsm");
2 | var {Services} = Components.utils.import("resource://gre/modules/Services.jsm", {});
3 | var sss = Components.classes["@mozilla.org/content/style-sheet-service;1"].getService(Components.interfaces.nsIStyleSheetService);
4 |
5 | var at_label = "Download Status";
6 |
7 | var ActivityThrobber = {
8 | init: function() {
9 |
10 | try {
11 |
12 | document.addEventListener("TabAttrModified", _ActivityThrobber, false);
13 | document.addEventListener('TabSelect', _ActivityThrobber, false);
14 | document.addEventListener('TabOpen', _ActivityThrobber, false);
15 | document.addEventListener('TabClose', _ActivityThrobber, false);
16 | document.addEventListener('load', _ActivityThrobber, false);
17 |
18 | // add or remove 'busy' tab from activity item
19 | function _ActivityThrobber() {
20 |
21 | if(gBrowser.selectedTab.hasAttribute('busy')) {
22 | document.querySelector('#download_status').setAttribute('busy','true');
23 | } else document.querySelector('#download_status').removeAttribute('busy');
24 |
25 | }
26 |
27 | // create a default toolbar button
28 | CustomizableUI.createWidget({
29 | id: "download_status", // button id
30 | defaultArea: CustomizableUI.AREA_MENUBAR,
31 | removable: false,
32 | label: at_label, // button title
33 | onCreated: function(button) {
34 | return button;
35 | }
36 |
37 | });
38 | } catch (e) { Components.utils.reportError(e); }
39 |
40 | }
41 |
42 | };
43 |
44 | document.addEventListener("DOMContentLoaded", ActivityThrobber.init(), false);
--------------------------------------------------------------------------------
/profile/chrome/JS/change-toolbar-icon-values.uc.js:
--------------------------------------------------------------------------------
1 | function waitForElm(selector) {
2 | return new Promise(resolve => {
3 | if (document.querySelector(selector)) {
4 | return resolve(document.querySelector(selector));
5 | }
6 |
7 | const observer = new MutationObserver(mutations => {
8 | if (document.querySelector(selector)) {
9 | resolve(document.querySelector(selector));
10 | observer.disconnect();
11 | }
12 | });
13 |
14 | observer.observe(document.body, {
15 | childList: true,
16 | subtree: true
17 | });
18 | });
19 | }
20 |
21 | waitForElm('#find-button').then((elm) => {
22 | var find = document.querySelector("#find-button label.toolbarbutton-text");
23 | find.setAttribute("value", "Search");
24 | });
25 |
26 | setTimeout(function () {
27 | waitForElm('#nav-bar').then((elm) => {
28 | var library = document.querySelector("#bookmarks-menu-button label.toolbarbutton-text");
29 | library.setAttribute("value", "Favorites");
30 | });
31 | }, 0);
32 |
33 | waitForElm('#bookmarksMenu').then((elm) => {
34 | var bookmarksmenu = document.querySelector("#bookmarksMenu label.menubar-text");
35 | bookmarksmenu.setAttribute("value", "Favorites");
36 | bookmarksmenu.setAttribute("accesskey", "a");
37 | });
38 |
39 | waitForElm('#bookmarksMenu').then((elm) => {
40 | var bookmarksmenu = document.querySelector("#bookmarksMenu");
41 | bookmarksmenu.setAttribute("accesskey", "a");
42 | });
--------------------------------------------------------------------------------
/profile/chrome/JS/favicon_in_urlbar.uc.js:
--------------------------------------------------------------------------------
1 | // 'Favicon in urlbars identity box' script for Firefox 92+ by Aris
2 | //
3 | // This script restores current pages favicon inside urlbar (aka location bar, address bar or awesome bar).
4 | // [!] If a page does not offer a favicon, browser branches default icon is shown.
5 | // [!] In a multi-window environment pages without favicons might show wrong icons.
6 | // option: set icon for pages without favicon
7 |
8 | var i_icon = 'chrome://global/skin/icons/info.svg';
9 | var sheet = 'chrome://global/skin/icons/Portrait.png';
10 | var brand = 'https://cdn.discordapp.com/attachments/407220400578101257/1089237043521015949/ie.png';
11 | var globe = 'https://cdn.discordapp.com/attachments/407220400578101257/1089237043521015949/ie.png';
12 |
13 | var icon_for_pages_without_favicon = brand; // i_icon, sheet, globe or brand (colorized Fx channel icon)
14 |
15 | var favicon_click_opens_page_info_window = false;
16 |
17 | var FaviconInUrlbar = {
18 | init: function() {
19 | try {
20 |
21 | var favimginurlbar = document.createXULElement("image");
22 | favimginurlbar.setAttribute("id","favimginurlbar");
23 |
24 | if(favicon_click_opens_page_info_window)
25 | favimginurlbar.setAttribute("onclick","gIdentityHandler.handleMoreInfoClick(event);");
26 |
27 | favimginurlbar.style.width = "16px";
28 | favimginurlbar.style.height = "16px";
29 | favimginurlbar.style.marginLeft = "3px";
30 | favimginurlbar.style.marginRight = "3px";
31 | favimginurlbar.style.marginTop = "3px";
32 | favimginurlbar.style.marginBottom = "3px";
33 |
34 | //document.getElementById('identity-box').insertBefore(favimginurlbar,document.getElementById('identity-box').firstChild);
35 | document.getElementById('identity-box').appendChild(favimginurlbar);
36 |
37 | // update script every time tab attributes get modified (switch/open tabs/windows)
38 | document.addEventListener("TabAttrModified", updateIcon, false);
39 | document.addEventListener('TabSelect', updateIcon, false);
40 | document.addEventListener('TabOpen', updateIcon, false);
41 | document.addEventListener('TabClose', updateIcon, false);
42 | document.addEventListener('load', updateIcon, false);
43 | document.addEventListener("DOMContentLoaded", updateIcon, false);
44 |
45 |
46 | function updateIcon() {
47 |
48 | setTimeout(function(){ // timeout fixes wrong icon detection in some cases
49 |
50 | // get current tabs favicon
51 | var favicon_in_urlbar = gBrowser.selectedTab.image;
52 |
53 | // if current tab offers no icon, use selected icon (icon_for_pages_without_favicon)
54 | if(!gBrowser.selectedTab.image || gBrowser.selectedTab.image == null)
55 | if(!icon_for_pages_without_favicon) favicon_in_urlbar = brand;
56 | else favicon_in_urlbar = icon_for_pages_without_favicon;
57 |
58 | document.querySelector('#favimginurlbar').style.listStyleImage = "url("+favicon_in_urlbar+")";
59 |
60 | },1);
61 |
62 | }
63 |
64 | } catch(e) {}
65 | }
66 | };
67 |
68 | // initiate script after DOM/browser content is loaded
69 | document.addEventListener("DOMContentLoaded", FaviconInUrlbar.init(), false);
70 |
--------------------------------------------------------------------------------
/profile/chrome/JS/space_and_separator_restorer.uc.js:
--------------------------------------------------------------------------------
1 | // Restore 'Space & Separator' items script for Firefox 60+ by Aris
2 | //
3 | // Default browser scripts always remove spaces and separators from default palette, so
4 | // because of that they are added to an own toolbar now.
5 | //
6 | // - spaces and separators can be moved to any toolbar
7 | // - to remove spaces or separators move them into palette
8 | // - configuration toolbar behaves like a default toolbar, items and buttons can be placed on it
9 | // - configuration toolbar is not visible outside customizing mode
10 | // - default "Flexible Space" item is hidden from palette and added to configuration toolbar
11 | // [!] BUG: do not move spaces, flexible spaces or separator to configuration toolbar or it will cause glitches
12 | // [!] BUG: do not move main 'space'-item to palette or it will be hidden until customizing mode gets reopened
13 |
14 | // [!] Fix for WebExtensions with own windows by 黒仪大螃蟹 (for 1-N scripts)
15 |
16 |
17 | Components.utils.import("resource:///modules/CustomizableUI.jsm");
18 | var {Services} = Components.utils.import("resource://gre/modules/Services.jsm", {});
19 | var appversion = parseInt(Services.appinfo.version);
20 |
21 | var AddSeparator = {
22 | init: function() {
23 |
24 | if (appversion >= 76 && location != 'chrome://browser/content/browser.xhtml')
25 | return;
26 |
27 | /* blank tab workaround */
28 | try {
29 | if(gBrowser.selectedBrowser.getAttribute('blank')) gBrowser.selectedBrowser.removeAttribute('blank');
30 | } catch(e) {}
31 |
32 | var tb_config_label = "Configuration Toolbar";
33 | var tb_spacer_label = "Space";
34 | var tb_sep_label = "Separator";
35 | var tb_spring_label = "Flexible Space";
36 |
37 | try {
38 | if(document.getElementById('configuration_toolbar') == null) {
39 |
40 | if(appversion <= 62) var tb_config = document.createElement("toolbar");
41 | else var tb_config = document.createXULElement("toolbar");
42 | tb_config.setAttribute("id","configuration_toolbar");
43 | tb_config.setAttribute("customizable","true");
44 | tb_config.setAttribute("class","toolbar-primary chromeclass-toolbar browser-toolbar customization-target");
45 | tb_config.setAttribute("mode","icons");
46 | tb_config.setAttribute("iconsize","small");
47 | tb_config.setAttribute("toolboxid","navigator-toolbox");
48 | tb_config.setAttribute("lockiconsize","true");
49 | tb_config.setAttribute("ordinal","1005");
50 | tb_config.setAttribute("defaultset","toolbarspacer,toolbarseparator");
51 |
52 | document.querySelector('#navigator-toolbox').appendChild(tb_config);
53 |
54 | CustomizableUI.registerArea("configuration_toolbar", {legacy: true});
55 | if(appversion >= 65) CustomizableUI.registerToolbarNode(tb_config);
56 |
57 | if(appversion <= 62) var tb_label = document.createElement("label");
58 | else var tb_label = document.createXULElement("label");
59 | tb_label.setAttribute("label", tb_config_label+": ");
60 | tb_label.setAttribute("value", tb_config_label+": ");
61 | tb_label.setAttribute("id","tb_config_tb_label");
62 | tb_label.setAttribute("removable","false");
63 |
64 | tb_config.appendChild(tb_label);
65 |
66 |
67 | if(appversion <= 62) var tb_spacer = document.createElement("toolbarspacer");
68 | else var tb_spacer = document.createXULElement("toolbarspacer");
69 | tb_spacer.setAttribute("id","spacer");
70 | tb_spacer.setAttribute("class","chromeclass-toolbar-additional");
71 | tb_spacer.setAttribute("customizableui-areatype","toolbar");
72 | tb_spacer.setAttribute("removable","false");
73 | tb_spacer.setAttribute("label", tb_spacer_label);
74 |
75 | tb_config.appendChild(tb_spacer);
76 |
77 |
78 | if(appversion <= 62) var tb_sep = document.createElement("toolbarseparator");
79 | else var tb_sep = document.createXULElement("toolbarseparator");
80 | tb_sep.setAttribute("id","separator");
81 | tb_sep.setAttribute("class","chromeclass-toolbar-additional");
82 | tb_sep.setAttribute("customizableui-areatype","toolbar");
83 | tb_sep.setAttribute("removable","false");
84 | tb_sep.setAttribute("label", tb_sep_label);
85 |
86 | tb_config.appendChild(tb_sep);
87 |
88 |
89 | if(appversion <= 62) var tb_spring = document.createElement("toolbarspring");
90 | else var tb_spring = document.createXULElement("toolbarspring");
91 | tb_spring.setAttribute("id","spring");
92 | tb_spring.setAttribute("class","chromeclass-toolbar-additional");
93 | tb_spring.setAttribute("customizableui-areatype","toolbar");
94 | tb_spring.setAttribute("removable","false");
95 | tb_spring.setAttribute("flex","1");
96 | tb_spring.setAttribute("label", tb_spring_label);
97 |
98 | tb_config.appendChild(tb_spring);
99 |
100 | // CSS
101 | var sss = Components.classes["@mozilla.org/content/style-sheet-service;1"].getService(Components.interfaces.nsIStyleSheetService);
102 |
103 | var uri = Services.io.newURI("data:text/css;charset=utf-8," + encodeURIComponent('\
104 | \
105 | #configuration_toolbar { \
106 | -moz-appearance: none !important; \
107 | background-color: var(--toolbar-bgcolor); \
108 | background-image: var(--toolbar-bgimage); \
109 | background-clip: padding-box; \
110 | color: var(--toolbar-color, inherit); \
111 | } \
112 | #main-window:not([customizing]) #configuration_toolbar { \
113 | visibility: collapse; \
114 | }\
115 | #main-window[customizing] #configuration_toolbar #tb_config_tb_label { \
116 | font-weight: bold !important; \
117 | }\
118 | #main-window[customizing] #configuration_toolbar :-moz-any(#spacer,#separator,#spring) { \
119 | -moz-margin-start: 20px; \
120 | }\
121 | #main-window[customizing] #configuration_toolbar :-moz-any(#wrapper-spacer,#wrapper-separator,#wrapper-spring) .toolbarpaletteitem-label { \
122 | display: block !important; \
123 | -moz-margin-end: 20px; \
124 | }\
125 | #main-window[customizing] #wrapper-spacer #spacer { \
126 | margin: 2px 0 !important; \
127 | }\
128 | #main-window[customizing] #configuration_toolbar #wrapper-spring #spring { \
129 | margin: -1px 0 !important; \
130 | min-width: 80px !important; \
131 | }\
132 | #main-window[customizing] #configuration_toolbar > * { \
133 | padding: 10px !important; \
134 | }\
135 | #main-window[customizing] #configuration_toolbar > :-moz-any(#wrapper-spacer,#wrapper-separator,#wrapper-spring) { \
136 | border: 1px dotted !important; \
137 | -moz-margin-start: 2px !important; \
138 | -moz-margin-end: 2px !important; \
139 | }\
140 | #main-window[customizing] toolbarspacer { \
141 | border: 1px solid !important; \
142 | }\
143 | toolbar[orient="vertical"] toolbarseparator { \
144 | -moz-appearance: none !important; \
145 | border-top: 1px solid rgba(15,17,38, 0.5) !important; \
146 | border-bottom: 1px solid rgba(255,255,255, 0.3) !important; \
147 | margin: 2px 2px !important; \
148 | height: 1px !important; \
149 | width: 18px !important; \
150 | }\
151 | toolbar[orient="vertical"] toolbarspacer { \
152 | -moz-appearance: none !important; \
153 | height: 18px !important; \
154 | width: 18px !important; \
155 | }\
156 | #customization-palette toolbarpaletteitem[id^="wrapper-customizableui-special-spring"], \
157 | #customization-palette-container :-moz-any(#spring,#wrapper-spring) { \
158 | display: none !important; \
159 | }\
160 | #main-window:not([customizing]) toolbar:not(#configuration_toolbar) toolbarspring {\
161 | max-width: 100% !important; \
162 | }\
163 | \
164 | '), null, null);
165 |
166 | sss.loadAndRegisterSheet(uri, sss.AGENT_SHEET);
167 | }
168 | } catch(e){}
169 |
170 | }
171 |
172 | }
173 |
174 | /* initialization delay workaround */
175 | document.addEventListener("DOMContentLoaded", AddSeparator.init(), false);
176 | /* Use the below code instead of the one above this line, if issues occur */
177 | /*
178 | setTimeout(function(){
179 | AddSeparator.init();
180 | },2000);
181 | */
182 |
--------------------------------------------------------------------------------
/profile/chrome/alltabs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/alltabs.png
--------------------------------------------------------------------------------
/profile/chrome/arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/arrow.png
--------------------------------------------------------------------------------
/profile/chrome/checkmark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/checkmark.png
--------------------------------------------------------------------------------
/profile/chrome/cheveron.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/cheveron.png
--------------------------------------------------------------------------------
/profile/chrome/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/close.png
--------------------------------------------------------------------------------
/profile/chrome/ie.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/ie.png
--------------------------------------------------------------------------------
/profile/chrome/internet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/internet.png
--------------------------------------------------------------------------------
/profile/chrome/items/copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/items/copy.png
--------------------------------------------------------------------------------
/profile/chrome/items/cut.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/items/cut.png
--------------------------------------------------------------------------------
/profile/chrome/items/download.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/items/download.png
--------------------------------------------------------------------------------
/profile/chrome/items/email.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/items/email.png
--------------------------------------------------------------------------------
/profile/chrome/items/extensions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/items/extensions.png
--------------------------------------------------------------------------------
/profile/chrome/items/favorites.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/items/favorites.png
--------------------------------------------------------------------------------
/profile/chrome/items/fullscreen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/items/fullscreen.png
--------------------------------------------------------------------------------
/profile/chrome/items/history.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/items/history.png
--------------------------------------------------------------------------------
/profile/chrome/items/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/items/home.png
--------------------------------------------------------------------------------
/profile/chrome/items/left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/items/left.png
--------------------------------------------------------------------------------
/profile/chrome/items/paste.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/items/paste.png
--------------------------------------------------------------------------------
/profile/chrome/items/personal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/items/personal.png
--------------------------------------------------------------------------------
/profile/chrome/items/print.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/items/print.png
--------------------------------------------------------------------------------
/profile/chrome/items/reload.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/items/reload.png
--------------------------------------------------------------------------------
/profile/chrome/items/right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/items/right.png
--------------------------------------------------------------------------------
/profile/chrome/items/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/items/search.png
--------------------------------------------------------------------------------
/profile/chrome/items/stop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/items/stop.png
--------------------------------------------------------------------------------
/profile/chrome/newtab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/newtab.png
--------------------------------------------------------------------------------
/profile/chrome/radio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/radio.png
--------------------------------------------------------------------------------
/profile/chrome/spinnerdown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/spinnerdown.png
--------------------------------------------------------------------------------
/profile/chrome/throbber.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/throbber.gif
--------------------------------------------------------------------------------
/profile/chrome/throbber_busy.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/throbber_busy.gif
--------------------------------------------------------------------------------
/profile/chrome/user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoniKast/Edge2003/879c3c4555d268a3b2b4ec8546a1479ca4e34b1a/profile/chrome/user.png
--------------------------------------------------------------------------------
/profile/chrome/userChrome.css:
--------------------------------------------------------------------------------
1 | #main-window[privatebrowsingmode=temporary]:root, :root {
2 | --toolbar-bgcolor: -moz-Dialog !important;
3 | --toolbar-bgimage: none !important;
4 | --toolbar-color: -moz-DialogText !important;
5 | --lwt-text-color: -moz-DialogText !important;
6 | --lwt-toolbarbutton-icon-fill: -moz-DialogText !important;
7 | --toolbar-field-focus-border-color: Highlight !important;
8 | --lwt-toolbar-field-background-color: -moz-Field !important;
9 | --lwt-toolbar-field-color: -moz-FieldText !important;
10 | --lwt-toolbar-field-border-color: rgb(204, 204, 204) !important;
11 | --lwt-toolbar-field-focus: -moz-Field !important;
12 | --lwt-toolbar-field-focus-color: -moz-FieldText !important;
13 | --toolbarbutton-vertical-text-padding: calc(var(--toolbarbutton-inner-padding) - 1px) !important;
14 | --toolbarbutton-border-radius: 2px !important;
15 | --toolbarbutton-icon-fill-opacity: 1 !important;
16 | --panel-separator-color: ThreeDLightShadow !important;
17 | --arrowpanel-background: -moz-Field !important;
18 | --arrowpanel-color: -moz-FieldText !important;
19 | --arrowpanel-border-color: ThreeDShadow !important;
20 | --arrowpanel-dimmed: hsla(0,0%,80%,.35) !important;
21 | --arrowpanel-dimmed-further: hsla(0,0%,80%,.5) !important;
22 | --arrowpanel-dimmed-even-further: hsla(0,0%,80%,.8) !important;
23 | --urlbar-separator-color: ThreeDLightShadow !important;
24 | --chrome-content-separator-color: ThreeDShadow !important;
25 | --toolbarbutton-hover-transition-duration: 0 !important;
26 | --toolbarbutton-outer-padding: 2px !important;
27 | --toolbarbutton-inner-padding: 6px !important;
28 | --toolbarbutton-focus-outline: 2px solid currentColor !important;
29 | --autocomplete-popup-background: -moz-Field !important;
30 | --autocomplete-popup-color: -moz-FieldText !important;
31 | --toolbarbutton-hover-background: var(hsla(240,5%,5%,.1)) !important;
32 | --toolbarbutton-active-background: var(hsla(240,5%,5%,.15)) !important;
33 | --autocomplete-popup-highlight-background: Highlight !important;
34 | --autocomplete-popup-highlight-color: HighlightText !important;
35 | --tab-min-height: 12px !important;
36 | --arrowpanel-border-radius: 0px !important;
37 | --panel-item-hover-bgcolor: Highlight !important;
38 | --panel-separator-color: #ACA899 !important;
39 | }
40 |
41 | toolbarbutton menupopup[placespopup] {
42 | --panel-border-radius: 0 !important;
43 | --panel-padding: 2px !important;
44 | --arrowpanel-menuitem-margin: 0 !important;
45 | }
46 |
47 | toolbarbutton menupopup > :is(menu, menuitem) {
48 | min-height: 17px !important;
49 | }
50 |
51 | toolbox,
52 | toolbar,
53 | toolbarbutton:not(#sidebar-close):not(#sidebar-switcher-target):not(#download_status):not(#PanelUI-menu-button):not(#PlacesChevron),
54 | tab,
55 | .toolbarbutton-icon,
56 | #urlbar-background,
57 | .toolbarbutton-text
58 | {
59 | box-shadow: none !important;
60 | border: 0 !important;
61 | -moz-appearance: auto !important;
62 | background-color: transparent !important;
63 | }
64 |
65 | .tabbrowser-tab {
66 | }
67 |
68 | .toolbarbutton-badge-stack
69 | {
70 | background-color: transparent !important;
71 | }
72 |
73 | #download_status .toolbarbutton-icon,
74 | #toolbar-menubar spacer,
75 | #alltabs-button,
76 | .tab-background,
77 | #tracking-protection-icon-container,
78 | #identity-icon-box,
79 | #identity-permission-box,
80 | identity-icon-box,
81 | #star-button-box
82 | {
83 | display: none !important;
84 | }
85 |
86 | #download_status
87 | {
88 | width: 38px !important;
89 | height: 22px !important;
90 | position: relative !important;
91 | background-color: rgb(255, 255, 255) !important;
92 | background-position: center center;
93 | background-repeat: no-repeat;
94 | background-image: url('throbber.gif');
95 | }
96 |
97 | #download_status[busy]
98 | {
99 | background-image: url('throbber_busy.gif');
100 | }
101 |
102 | #download_status::after
103 | {
104 | content: '';
105 | display: inline-block;
106 | width: 23px !important;
107 | height: 1px !important;
108 | appearance: toolbar !important;
109 | transform: rotate(-0.250006turn);
110 | position: absolute !important;
111 | left: -12px !important;
112 | bottom: 9px !important;
113 | z-index: 999999999 !important;
114 | }
115 |
116 | #menubar-items > #main-menubar > menu {
117 | height: 19px !important;
118 | margin-top: 1px !important;
119 | }
120 |
121 | .menubar-text {
122 | margin: 1px 9px !important;
123 | }
124 |
125 | #additional_top_toolbar1 #PlacesToolbarItems {
126 | width: 0 !important;
127 | max-width: 0 !important;
128 | }
129 |
130 | #additional_top_toolbar1 #PlacesToolbar {
131 | -moz-box-align: center !important;
132 | }
133 |
134 | #toolbar-menubar {
135 | padding-left: 2px !important;
136 | }
137 |
138 | #additional_top_toolbar1 #PlacesToolbar::before {
139 | content: 'Links';
140 | display: inline-block;
141 | margin-inline: 4px !important;
142 | color: Scrollbar !important;
143 | }
144 |
145 | #additional_top_toolbar1 #personal-bookmarks::before {
146 | content: '';
147 | display: inline-block;
148 | width: 23px !important;
149 | height: 1px !important;
150 | appearance: toolbar !important;
151 | transform: rotate(-0.250006turn);
152 | position: absolute !important;
153 | left: -12px !important;
154 | bottom: 9px !important;
155 | z-index: 999999999 !important;
156 | }
157 |
158 | #additional_top_toolbar1 #personal-bookmarks {
159 | margin-left: 2px !important;
160 | }
161 |
162 | #tabbrowser-tabs, #tabbrowser-arrowscrollbox, #tabbrowser-tabs[positionpinnedtabs] > #tabbrowser-arrowscrollbox > .tabbrowser-tab[pinned] {
163 | min-height: 0 !important;
164 | }
165 |
166 | .tabbrowser-tab:first-of-type:last-of-type,
167 | .tabbrowser-tab:first-of-type:last-of-type ~ #tabbrowser-arrowscrollbox-periphery {
168 | display: none !important;
169 | }
170 |
171 | #additional_top_toolbar1 {
172 | -moz-box-ordinal-group: 3 !important;
173 | padding-top: 2px;
174 | position: relative;
175 | height: 23px !important;
176 | }
177 |
178 | #TabsToolbar {
179 | padding-inline: 2px !important;
180 | -moz-box-ordinal-group: 4 !important;
181 | }
182 |
183 | #additional_top_toolbar1::before {
184 | content: '';
185 | display: inline-block;
186 | height: 1px;
187 | width: 100%;
188 | appearance: toolbar;
189 | position: absolute;
190 | top: 0;
191 | }
192 |
193 | #additional_top_toolbar1::after {
194 | content: 'Address';
195 | display: inline-block;
196 | margin-inline: 5px !important;
197 | -moz-box-ordinal-group: 0 !important;
198 | line-height: 22px !important;
199 | color: Scrollbar !important;
200 | }
201 |
202 | #urlbar-container {
203 | --urlbar-container-height: 32px !important;
204 | margin-inline: 3px !important;
205 | padding-block: 0 !important;
206 | min-width: unset !important;
207 | }
208 |
209 | #urlbar-container.chromeclass-location
210 | {
211 | top: 8px !important;
212 | }
213 |
214 | #urlbar {
215 | --urlbar-height: 22px !important;
216 | --urlbar-toolbar-height: 22px !important;
217 | appearance: listbox !important;
218 | -moz-default-appearance: listbox !important;
219 | font: -moz-pull-down-menu !important;
220 | min-height: 22px !important;
221 | z-index: 9999999 !important;
222 | }
223 |
224 | #urlbar-background {
225 | appearance: listbox !important;
226 | -moz-default-appearance: listbox !important;
227 | }
228 |
229 | #urlbar[breakout][breakout-extend] #urlbar-background {
230 | height: var(--urlbar-height) !important;
231 | }
232 |
233 | #urlbar[breakout]:not([breakout-extend]) #urlbar-background {
234 | display: none !important;
235 | }
236 |
237 | #urlbar[breakout][breakout-extend] {
238 | appearance: none !important;
239 | -moz-default-appearance: none !important;
240 | width: 100% !important;
241 | left: 0 !important;
242 | z-index: 999999 !important;
243 | }
244 |
245 | .urlbarView {
246 | width: 100% !important;
247 | background-color: rgb(255, 255, 255) !important;
248 | border: 1px solid rgb(0, 0, 0) !important;
249 | margin-inline: 0 !important;
250 | z-index: 999999 !important;
251 | }
252 |
253 | #urlbar[breakout][breakout-extend] > #urlbar-input-container {
254 | height: 22px !important;
255 | }
256 |
257 | #urlbar[open] > .urlbarView > .urlbarView-body-outer > .urlbarView-body-inner {
258 | border: 0 !important;
259 | }
260 |
261 | #urlbar .search-one-offs:not([hidden]) {
262 | display: none !important;
263 | }
264 |
265 | #urlbar-input-container {
266 | padding: 0 !important;
267 | border: 0 !important;
268 | }
269 |
270 | #urlbar[breakout] {
271 | top: 0 !important;
272 | }
273 |
274 | .urlbarView-row[label][dynamicType="quickactions"] {
275 | display: none !important;
276 | }
277 |
278 | .urlbarView-results,
279 | .urlbarView-row {
280 | padding-block: 0 !important;
281 | min-height: 16px !important;
282 | border-radius: 0 !important;
283 | border: 0 !important;
284 | }
285 |
286 | .urlbarView-row-inner {
287 | border-radius: 0 !important;
288 | padding-block: 0 !important;
289 | min-height: 16px !important;
290 | padding-inline: 0 !important;
291 | margin-left: 3px !important;
292 | flex: unset !important;
293 | padding-right: 1px !important;
294 | }
295 |
296 | .urlbarView-row[row-selectable]:not([selected]):hover {
297 | background-color: Highlight !important;
298 | color: HighlightText !important;
299 | }
300 |
301 | .urlbarView-row[label] {
302 | margin-block-start: 0 !important;
303 | }
304 |
305 | .urlbarView-row[label]::before {
306 | display: none !important;
307 | }
308 |
309 | .urlbarView-favicon {
310 | padding-right: 3px !important;
311 | margin-inline-end: 1px !important;\
312 | }
313 |
314 | .urlbarView-row[selectable]:hover > .urlbarView-row-inner {
315 | background-color: var(--autocomplete-popup-highlight-background) !important;
316 | color: var(--autocomplete-popup-highlight-color) !important;
317 | }
318 |
319 | .urlbarView-tags, .urlbarView-url, .urlbarView-title:not(:empty) ~ .urlbarView-action {
320 | font-size: unset !important;
321 | }
322 |
323 | .urlbarView-title-separator::before {
324 | display: none !important;
325 | }
326 |
327 | .urlbarView-title-separator {
328 | margin-inline: 4px !important;
329 | }
330 |
331 | .urlbarView-row[selectable]:hover > .urlbarView-row-inner .urlbarView-url, .urlbarView-row-inner[selectable]:hover .urlbarView-url {
332 | color: var(--autocomplete-popup-highlight-color) !important;
333 | }
334 |
335 | #PersonalToolbar{
336 | z-index: 99999 !important;
337 | overflow: hidden !important;
338 | }
339 |
340 | #PlacesChevron, #nav-bar-overflow-button {
341 | list-style-image: none !important;
342 | min-height: 18px !important;
343 | width: 10px !important;
344 | margin-left: 4px !important;
345 | }
346 |
347 | #PlacesChevron .toolbarbutton-icon, #nav-bar-overflow-button .toolbarbutton-icon {
348 | background-image: url('cheveron.png') !important;
349 | padding: 0 !important;
350 | background-repeat: no-repeat !important;
351 | background-position: center top !important;
352 | height: 10px !important;
353 | width: 10px !important;
354 | }
355 |
356 | #PanelUI-menu-button {
357 | list-style-image: none !important;
358 | margin-inline-end: 0 !important;
359 | padding: 0 !important;
360 | margin: 0 !important;
361 | min-width: 0 !important;
362 | }
363 |
364 | #PanelUI-menu-button .toolbarbutton-badge-stack {
365 | padding: 0 !important;
366 | display: none !important;
367 | }
368 |
369 | .browserStack > browser {
370 | box-shadow: inset -1px -1px 0 ThreeDHighlight, inset 1px 1px 0 ThreeDShadow, inset -2px -2px 0 ThreeDFace, inset 2px 2px 0 ThreeDDarkShadow !important;
371 | padding: 2px !important;
372 | }
373 |
374 | #statuspanel[inactive],
375 | #statuspanel {
376 | opacity: 1 !important;
377 | visibility: visible !important;
378 | width: 100% !important;
379 | height: 22px !important;
380 | -moz-appearance: statusbar !important;
381 | padding-top: 0 !important;
382 | padding-inline: 16px !important;
383 | display: flex !important;
384 | align-items: center !important;
385 | position: fixed !important;
386 | bottom: 0 !important;
387 | left: 0 !important;
388 | }
389 |
390 | :root[sizemode="fullscreen"] #statuspanel {
391 | display: none !important;
392 | }
393 |
394 | :root[sizemode="fullscreen"] #tabbrowser-tabpanels {
395 | margin-bottom: 0 !important;
396 | }
397 |
398 | #sidebar-box {
399 | margin-bottom: 23px !important;
400 | }
401 |
402 | #statuspanel::after {
403 | content: 'Internet';
404 | display: block;
405 | position: fixed !important;
406 | right: 86px !important;
407 | bottom: 2px !important;
408 | padding-left: 20px !important;
409 | height: 16px;
410 | line-height: 16px;
411 | background-image: url('internet.png') !important;
412 | background-repeat: no-repeat;
413 | }
414 |
415 | #statuspanel::before {
416 | content: '' !important;
417 | display: inline-block;
418 | background-image: url('ie.png') !important;
419 | background-repeat: no-repeat;
420 | height: 16px;
421 | line-height: 17px;
422 | padding-left: 20px !important;
423 | margin-top: 1px !important;
424 | position: absolute;
425 | left: 2px !important;
426 | }
427 |
428 | #statuspanel[inactive]::before {
429 | content: 'Done' !important;
430 | width: 16px !important;
431 | }
432 |
433 | #statuspanel[inactive] #statuspanel-label::before {
434 | opacity: 0 !important;
435 | visibility: collapse !important;
436 | }
437 |
438 | #statuspanel-label {
439 | background-color: transparent !important;
440 | border: 0 !important;
441 | padding: 0 !important;
442 | margin-inline: 0 !important;
443 | height: 22px !important;
444 | width: calc(100% - 138px) !important;
445 | appearance: statusbarpanel !important;
446 | margin-top: 8px !important;
447 | margin-left: 6px !important;
448 | color: #000000 !important;
449 | }
450 |
451 | :root[sizemode="fullscreen"] .browserStack > browser {
452 | padding: 0 !important;
453 | box-shadow: none !important;
454 | }
455 |
456 | #back-button,
457 | #forward-button,
458 | #stop-button,
459 | #reload-button,
460 | #home-button,
461 | #downloads-button,
462 | #history-panelmenu,
463 | #bookmarks-menu-button,
464 | #print-button,
465 | #new-window-button,
466 | #cut-button,
467 | #copy-button,
468 | #paste-button,
469 | #fullscreen-button,
470 | #find-button,
471 | #email-link-button,
472 | #sidebar-button,
473 | #unified-extensions-button,
474 | #fxa-toolbar-menu-button{
475 | width: 38px !important;
476 | list-style-image: none !important;
477 | -moz-context-properties: unset !important;
478 | fill: unset !important;
479 | opacity: unset !important;
480 | }
481 |
482 | #back-button > .toolbarbutton-icon,
483 | #forward-button > .toolbarbutton-icon,
484 | #reload-button > .toolbarbutton-icon,
485 | #stop-button > .toolbarbutton-icon,
486 | #home-button > .toolbarbutton-icon,
487 | #find-button > .toolbarbutton-icon,
488 | #bookmarks-menu-button > .toolbarbutton-icon,
489 | #history-panelmenu > .toolbarbutton-icon,
490 | #print-button > .toolbarbutton-icon,
491 | #copy-button > .toolbarbutton-icon,
492 | #cut-button > .toolbarbutton-icon,
493 | #paste-button > .toolbarbutton-icon,
494 | #downloads-button > .toolbarbutton-icon,
495 | #downloads-button > .toolbarbutton-badge-stack,
496 | #unified-extensions-button > .toolbarbutton-icon,
497 | #new-window-button > .toolbarbutton-icon,
498 | #fullscreen-button > .toolbarbutton-icon,
499 | #email-link-button > .toolbarbutton-icon,
500 | #sidebar-button > .toolbarbutton-icon,
501 | #fxa-toolbar-menu-button > .toolbarbutton-badge-stack{
502 | padding: 0 !important;
503 | opacity: unset !important;
504 | width: 25px !important;
505 | list-style-image: url('list.png');
506 | height: 25px !important;
507 | }
508 |
509 | #nav-bar .toolbarbutton-1 {
510 | min-height: 31px !important;
511 | }
512 |
513 | #nav-bar .toolbarbutton-1:active,
514 | #nav-bar .toolbarbutton-1[open="true"] {
515 | color: #ffffff !important;
516 | }
517 |
518 | .toolbarbutton-1:not(#activity_throbber) {
519 | border: 1px solid transparent;
520 | }
521 |
522 | toolbarbutton:where([disabled="true"]) {
523 | color: ThreeDShadow !important;
524 | text-shadow: 1px 1px 0 ThreeDHighlight;
525 | }
526 |
527 | #nav-bar #back-button:where([disabled="true"]),
528 | #nav-bar #forward-button:where([disabled="true"]) {
529 | border-color: transparent !important;
530 | filter: grayscale(1);
531 | }
532 |
533 | #nav-bar #back-button:where([disabled="true"]) .toolbarbutton-icon,
534 | #nav-bar #forward-button:where([disabled="true"]) .toolbarbutton-icon {
535 | opacity: 0.5 !important;;
536 | }
537 |
538 | toolbar:not(#nav-bar) .webextension-browser-action {
539 | padding: 0 4px !important;
540 | }
541 |
542 | #PersonalToolbar .toolbarbutton-1:not([disabled="true"]):is([open], [checked], :hover:active), #tabbrowser-arrowscrollbox:not([scrolledtostart="true"])::part(scrollbutton-up):hover:active, #tabbrowser-arrowscrollbox:not([scrolledtoend="true"])::part(scrollbutton-down):hover:active, toolbarbutton.bookmark-item:hover:active:not(.subviewbutton, [disabled="true"]), toolbarbutton.bookmark-item[open="true"], toolbar .toolbarbutton-1:not([disabled="true"]):is([open], [checked], :hover:active) > .toolbarbutton-icon, toolbar .toolbarbutton-1:not([disabled="true"]):is([open], [checked], :hover:active) > .toolbarbutton-text, toolbar .toolbarbutton-1:not([disabled="true"]):is([open], [checked], :hover:active) > .toolbarbutton-badge-stack {
543 | background: transparent !important;
544 | }
545 |
546 | #nav-bar .toolbarbutton-icon {
547 | margin-left: 3px !important;
548 | margin-right: 2px !important;
549 | }
550 |
551 | #nav-bar #back-button[disabled="true"] > *,
552 | #nav-bar #forward-button[disabled="true"] > * {
553 | transform: none !important;
554 | }
555 |
556 | #nav-bar #back-button[disabled="true"] .toolbarbutton-text {
557 | text-shadow: 1px 1px 0 ThreeDHighlight !important;
558 | }
559 |
560 |
561 | #nav-bar .toolbarbutton-1:not(#back-button[disabled="true"]):not(#forward-button[disabled="true"]):hover .toolbarbutton-icon,
562 | #nav-bar .toolbarbutton-1:not(#back-button[disabled="true"]):not(#forward-button[disabled="true"]):active .toolbarbutton-icon,
563 | #nav-bar .toolbarbutton-1:not(#back-button[disabled="true"]):not(#forward-button[disabled="true"])[open="true"] .toolbarbutton-icon {
564 | filter: none !important;
565 | }
566 |
567 | #nav-bar-customization-target > :is(toolbarbutton, toolbaritem):first-child, #nav-bar-customization-target > toolbarpaletteitem:first-child > :is(toolbarbutton, toolbaritem) {
568 | padding-inline-start: 0 !important;
569 | }
570 |
571 | #nav-bar {
572 | height: 40px;
573 | padding-left: 2px !important;
574 | }
575 |
576 | #nav-bar .toolbarbutton-1 .toolbarbutton-text {
577 | padding-left: 3px !important;
578 | padding-top: 0 !important;
579 | padding-bottom: 0 !important;
580 | min-height: unset !important;
581 | }
582 |
583 | #nav-bar .toolbarbutton-1 > .toolbarbutton-icon, #nav-bar .toolbarbutton-1 > .toolbarbutton-badge-stack > .toolbarbutton-icon {
584 | width: 25px !important;
585 | height: 25px !important;
586 | }
587 |
588 | #nav-bar .toolbarbutton-1 > .toolbarbutton-badge-stack {
589 | padding: 0 !important;
590 | }
591 |
592 | #nav-bar .toolbarbutton-1 {
593 | height: 100% !important;
594 | }
595 |
596 | #back-button {
597 | -moz-image-region: rect(0px 25px 25px 0px) !important;
598 | }
599 |
600 | #forward-button {
601 | -moz-image-region: rect(0px 50px 25px 25px) !important;
602 | }
603 |
604 | #reload-button {
605 | -moz-image-region: rect(0px 75px 25px 50px) !important;
606 | }
607 |
608 | #stop-button {
609 | -moz-image-region: rect(0px 100px 25px 75px) !important;
610 | }
611 |
612 | #home-button {
613 | -moz-image-region: rect(0px 125px 25px 100px) !important;
614 | }
615 |
616 | #find-button {
617 | -moz-image-region: rect(0px 150px 25px 125px) !important;
618 | }
619 |
620 | #bookmarks-menu-button {
621 | -moz-image-region: rect(0px 175px 25px 150px) !important;
622 | }
623 |
624 | #history-panelmenu {
625 | -moz-image-region: rect(0px 200px 25px 175px) !important;
626 | }
627 |
628 | #downloads-button {
629 | width: 38px !important;
630 | -moz-image-region: rect(0px 325px 25px 300px) !important;
631 | }
632 |
633 | #print-button {
634 | -moz-image-region: rect(0px 225px 25px 200px) !important;
635 | }
636 |
637 | #email-link-button {
638 | -moz-image-region: rect(0px 375px 25px 350px) !important;
639 | }
640 |
641 | #cut-button {
642 | -moz-image-region: rect(0px 275px 25px 250px) !important;
643 | }
644 |
645 | #copy-button {
646 | -moz-image-region: rect(0px 250px 25px 225px) !important;
647 | }
648 |
649 | #paste-button {
650 | -moz-image-region: rect(0px 300px 25px 275px) !important;
651 | }
652 |
653 | #sidebar-button {
654 | -moz-image-region: rect(0px 224px 16px 208px) !important;
655 | }
656 |
657 | #fullscreen-button {
658 | -moz-image-region: rect(0px 400px 25px 375px) !important;
659 | }
660 |
661 | #new-window-button {
662 | -moz-image-region: rect(0px 272px 16px 256px) !important;
663 | }
664 |
665 | #unified-extensions-button {
666 | -moz-image-region: rect(0px 350px 25px 325px) !important;
667 | }
668 |
669 | #fxa-toolbar-menu-button {
670 | width: unset !important;
671 | padding-left: 3px !important;
672 | -moz-image-region: rect(0px 428px 25px 403px) !important;
673 | }
674 |
675 | #fxa-toolbar-menu-button::after {
676 | color: black;
677 | content: 'Personal';
678 | padding-right: 5px !important;
679 | padding-left: 3px !important;
680 | }
681 |
682 | #fxa-toolbar-menu-button::before {
683 | content: '';
684 | padding-right: 5px !important;
685 | -moz-box-ordinal-group: 2 !important;
686 | background-image: url('alltabs.png') !important;
687 | background-repeat: no-repeat;
688 | display: inline-block;
689 | width: 5px;
690 | height: 3px;
691 | margin-top: 2px !important;
692 | }
693 |
694 | #edit-controls {
695 | margin: 0 !important;
696 | display: flex;
697 | }
698 |
699 | #edit-controls separator {
700 | display: none !important;
701 | }
702 |
703 | #edit-controls .toolbarbutton-1 {
704 | width: 33px !important;
705 | height: 100% !important;
706 | }
707 |
708 | #downloads-button .toolbarbutton-animatable-box {
709 | display: none !important;
710 | }
711 |
712 | #downloads-button[indicator] > .toolbarbutton-badge-stack > image.toolbarbutton-icon {
713 | display: unset !important;
714 | }
715 |
716 | #downloads-button .toolbarbutton-badge-stack > .toolbarbutton-icon {
717 | margin: 0 !important;
718 | }
719 |
720 | #favimginurlbar {
721 | margin: 2px !important;
722 | }
723 |
724 | #identity-box {
725 | margin-inline-end: 2px !important;
726 | }
727 |
728 | #urlbar[breakout][breakout-extend] #favimginurlbar {
729 | margin-top: 3px !important;
730 | margin-left: 3px !important;
731 | }
732 |
733 | .tab-label-container {
734 | height: 15px !important;
735 | }
736 |
737 | .tab-label {
738 | line-height: 0 !important;
739 | }
740 |
741 | .tab-icon-sound-label {
742 | display: none !important;
743 | }
744 |
745 | #tabs-newtab-button .toolbarbutton-icon {
746 | width: 16px !important;
747 | height: 16px !important;
748 | padding: 0 !important;
749 | }
750 |
751 | .tab-close-button {
752 | width: 16px !important;
753 | height: 16px !important;
754 | padding: 0 !important;
755 | display: none !important;
756 | }
757 |
758 | .tab-content {
759 | padding: 0 2px !important;
760 | }
761 |
762 | .tab-icon-image {
763 | margin-inline-end: 2px !important;
764 | }
765 |
766 | #TabsToolbar .toolbarbutton-1 {
767 | margin: 0 !important;
768 | }
769 |
770 | #tabs-newtab-button .toolbarbutton-icon {
771 | border-radius: 0 !important;
772 | }
773 |
774 | .tabbrowser-tab:not([visuallyselected="true"]) {
775 | margin-top: 4px !important;
776 | }
777 |
778 | #tabs-newtab-button {
779 | list-style-image: url('newtab.png') !important;
780 | padding: 0 !important;
781 | }
782 |
783 | #tabbrowser-tabs[haspinnedtabs]:not([positionpinnedtabs]) > #tabbrowser-arrowscrollbox > .tabbrowser-tab[first-visible-unpinned-tab] {
784 | margin-inline-start: 0 !important;
785 | }
786 |
787 | #context-navigation {
788 | -moz-box-orient: vertical !important;
789 | }
790 |
791 | #context-navigation menuitem {
792 | -moz-box-pack: end !important;
793 | -moz-box-direction: reverse !important;
794 | list-style-image: none !important;
795 | }
796 |
797 | #context-navigation .menu-iconic-icon {
798 | width: 16px !important;
799 | margin: 0 !important;
800 | margin-right: 8px !important;
801 | margin-left: 5px !important;
802 | }
803 |
804 | #context-navigation menuitem::before {
805 | content: attr(aria-label);
806 | margin-left: -11px !important;
807 | }
808 |
809 | menupopup {
810 | appearance: none !important;
811 | padding: 1px !important;
812 | }
813 |
814 | menuseparator {
815 | appearance: none !important;
816 | padding: 0 !important;
817 | margin-block: 2px !important;
818 | }
819 |
820 | menuitem {
821 | appearance: none !important;
822 | height: 17px !important;
823 | }
824 |
825 | .menu-text {
826 | appearance: none !important;
827 | }
828 |
829 | .menuitem-iconic > .menu-iconic-left {
830 | appearance: none !important;
831 | padding-top: 0 !important;
832 | }
833 |
834 | /*
835 | menuitem:is([type="checkbox"], [checked="true"]) > .menu-iconic-left {
836 | background-image: url('checkmark.png') !important;
837 | appearance: none !important;
838 | }*/
839 |
840 | menuitem[type="radio"][checked="true"] > .menu-iconic-left {
841 | background-image: url('radio.png') !important;
842 | }
843 |
844 | menuitem:is([type="checkbox"], [checked="true"], [type="radio"]) > .menu-iconic-left > .menu-iconic-icon {
845 | display: -moz-Box !important;
846 | }
847 |
848 | .menu-right {
849 | appearance: none !important;
850 | -moz-default-appearance: none !important;
851 | height: 17px !important;
852 | }
853 |
854 | /*
855 | .menu-right image {
856 | width: 16px !important;
857 | height: 16px !important;
858 | background-image: url('arrow.png') !important;
859 | }*/
860 |
861 | menu {
862 | appearance: none !important;
863 | }
864 |
865 | menuitem[type="radio"] > .menu-iconic-left {
866 | appearance: none !important;
867 | }
868 |
869 | toolbarpaletteitem[place="toolbar"][id^="wrapper-customizableui-special-spring"], toolbarspring {
870 | width: 100% !important;
871 | max-width: 100% !important;
872 | }
873 |
874 | toolbarpaletteitem[removable="false"] {
875 | opacity: 1 !important;
876 | }
877 |
878 | #personal-toolbar-empty-description, toolbarbutton.bookmark-item:not(.subviewbutton) {
879 | height: 22px !important;
880 | margin: 0 !important;
881 | }
882 |
883 | #PersonalToolbar {
884 | padding-inline: 0 !important;
885 | }
886 |
887 | #PersonalToolbar::after {
888 | -moz-box-ordinal-group: 0 !important;
889 | line-height: 26px !important;
890 | color: scrollbar !important;
891 | margin-inline: 5px !important;
892 | }
893 |
894 | .tabbrowser-tab[visuallyselected="true"] {
895 | font-weight: bold;
896 | }
897 |
898 | .searchbar-textbox {
899 | margin-block: 4px !important;
900 | }
901 |
902 | #sidebar-header {
903 | appearance: toolbox !important;
904 | font-size: 8pt !important;
905 | padding: 0 6px !important;
906 | height: 23px !important;
907 | box-shadow: inset 0 -1px 0 ThreeDDarkShadow !important;
908 | }
909 |
910 | .sidebar-splitter {
911 | margin-inline-start: 0 !important;
912 | }
913 |
914 | #sidebar-close {
915 | background-color: transparent !important;
916 | border-radius: 0 !important;
917 | list-style-image: url('close.png') !important;
918 | width: 8px !important;
919 | height: 7px !important;
920 | padding: 0 !important;
921 | margin-top: -4px !important;
922 | }
923 |
924 | #sidebar-box {
925 | z-index: 1 !important;
926 | background-color: #F1F2EE !important;
927 | border-top: 1px solid #E0E2E8 !important;
928 | }
929 |
930 | .sidebar-placesTree {
931 | margin: 6px !important;
932 | border: 1px solid #85878C !important;
933 | background-color: #FFFFFF !important;
934 | }
935 |
936 | #sidebar-switcher-target {
937 | padding: 0 !important;
938 | padding-inline: 0 !important;
939 | background-color: transparent !important;
940 | }
941 |
942 | #sidebar-title {
943 | padding-inline: 0 !important;
944 | }
945 |
946 | #sidebar-switcher-arrow {
947 | list-style-image: none !important;
948 | background-position: center center !important;
949 | background-repeat: no-repeat !important;
950 | background-image: url('alltabs.png') !important;
951 | width: 12px !important;
952 | background-color: #DFDEE2 !important;
953 | height: 100% !important;
954 | opacity: 1 !important;
955 | box-shadow: inset 1px 0 0 #FFFFFF, inset 0 1px 0.5px #D2D1D3, inset 0 -1px 0.5px rgb(255 255 255 / 20%) !important;
956 | }
957 |
958 | #sidebar-box {
959 | margin-bottom: 23px !important;
960 | }
961 |
962 | #sidebar-search-container {
963 | border-bottom: 2px solid -moz-Dialog !important;
964 | }
965 |
966 | #sidebar-search-container::before {
967 | content: 'Search for:';
968 | display: block;
969 | margin-bottom: 7px !important;
970 | font-family: 'MS Sans Serif' !important;
971 | }
972 |
973 | #sidebar-search-container {
974 | display: flex;
975 | flex-direction: column;
976 | padding: 11px 6px !important;
977 | }
978 |
979 | #sidebar-icon, #sidebar-switcher-arrow {
980 | display: none !important;
981 | }
982 |
983 | #search-box {
984 | font-family: 'MS Sans Serif' !important;
985 | height: 23px !important;
986 | }
987 |
988 | .textbox-search-sign {
989 | display: none !important;
990 | }
991 |
992 | #viewButton {
993 | width: 75px !important;
994 | margin-inline-start : 0 !important;
995 | appearance: button !important;
996 | margin-top: 6px !important;
997 | position: relative !important;
998 | }
999 |
1000 | #viewButton > .button-box > .button-menu-dropmarker {
1001 | display: none !important;
1002 | }
1003 |
1004 | #urlbar-input::placeholder, .searchbar-textbox::placeholder {
1005 | opacity: 1 !important;
1006 | color: #B4B4B4 !important;
1007 | }
1008 |
1009 | .searchbar-textbox {
1010 | margin-block: 4px !important;
1011 | }
1012 |
1013 | #PanelUI-menu-button {
1014 | min-width: 0 !important;
1015 | }
1016 |
1017 | #page-action-buttons::after {
1018 | content: '';
1019 | display: inline-block;
1020 | height: calc(100% - 1px) !important;
1021 | appearance: spinner-downbutton;
1022 | }
1023 |
1024 | #page-action-buttons {
1025 | position: relative;
1026 | }
1027 |
1028 | #page-action-buttons::before {
1029 | content: '';
1030 | width: 9px;
1031 | height: 6px;
1032 | position: absolute;
1033 | top: 7px;
1034 | right: 3px;
1035 | background-image: url('spinnerdown.png');
1036 | z-index: 999;
1037 | }
1038 |
1039 | #reload-button:not([displaystop]) {
1040 | display: -moz-Box !important;
1041 | }
1042 |
1043 | #stop-button {
1044 | -moz-box-ordinal-group: 0 !important;
1045 | }
1046 |
1047 | #favimginurlbar[style='width: 16px; height: 16px; margin: 3px; list-style-image: url("chrome://branding/content/icon32.png");'] {
1048 | list-style-image: url('ie.png') !important;
1049 | }
1050 |
1051 | #favimginurlbar[style='width: 16px; height: 16px; margin: 3px; list-style-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAABgUExURYaGhv///5mZmVVVVf/78ABmzDMzMwAAgDNm//Hx8czMzHd3d2aZmZnMzDMzzGb//+rq6pmZZv//mQDM/+fn1v/MmcyZZjNmzF9fXwAzmcDAwABmmYCAADMzZgQEBAAAANSdJEsAAAAgdFJOU/////////////////////////////////////////8AXFwb7QAAAAlwSFlzAAAOwwAADsMBx2+oZAAAAIpJREFUKFNNjFsWgyAMREUIRGhTbOlLW7P/XRoD2t6Tj8w9k3TM3O3IzipMpbeHcEpvxOwCwDnrvVcRQHA4xD7ZTYTTmegCg8tRvnQcxqvU6QZDLnET6Y7bCUAuKtLjidKQg1Ibr/GNOE2Ec2t86EsoSG4/iJdlnjVXwZRMliDTBKd40IT9oeIP5hU1fA8vue4FsgAAAABJRU5ErkJggg==");'] {
1052 | list-style-image: url('ie.png') !important;
1053 | }
1054 |
1055 | .tab-icon-image[src="chrome://branding/content/icon32.png"] {
1056 | content: url('ie.png') !important;
1057 | }
1058 |
1059 | .tab-icon-image[src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAABgUExURYaGhv///5mZmVVVVf/78ABmzDMzMwAAgDNm//Hx8czMzHd3d2aZmZnMzDMzzGb//+rq6pmZZv//mQDM/+fn1v/MmcyZZjNmzF9fXwAzmcDAwABmmYCAADMzZgQEBAAAANSdJEsAAAAgdFJOU/////////////////////////////////////////8AXFwb7QAAAAlwSFlzAAAOwwAADsMBx2+oZAAAAIpJREFUKFNNjFsWgyAMREUIRGhTbOlLW7P/XRoD2t6Tj8w9k3TM3O3IzipMpbeHcEpvxOwCwDnrvVcRQHA4xD7ZTYTTmegCg8tRvnQcxqvU6QZDLnET6Y7bCUAuKtLjidKQg1Ibr/GNOE2Ec2t86EsoSG4/iJdlnjVXwZRMliDTBKd40IT9oeIP5hU1fA8vue4FsgAAAABJRU5ErkJggg=="] {
1060 | content: url('ie.png') !important;
1061 | }
1062 |
1063 | #urlbar[breakout][breakout-extend] #page-action-buttons {
1064 | display: none !important;
1065 | }
1066 |
1067 | menu[_moz-menuactive="true"] {
1068 | background-color: Highlight !important;
1069 | color: HighlightText !important;
1070 | }
1071 |
1072 | .menu-iconic > .menu-iconic-left, .menuitem-iconic > .menu-iconic-left {
1073 | appearance: none !important;
1074 | }
1075 |
1076 | toolbarbutton menupopup[placespopup] > :is(menu, menuitem):not([disabled])[_moz-menuactive] {
1077 | color: HighlightText !important;
1078 | }
1079 |
1080 | toolbarbutton menupopup[placespopup] > :is(menu, menuitem) > .menu-accel-container > :is(.menu-accel, .menu-iconic-accel) {
1081 | color: inherit !important;
1082 | padding-inline-end: 16px !important;
1083 | }
1084 |
1085 | toolbarbutton menupopup[placespopup] > :is(.menu-iconic, .menuitem-iconic) > .menu-iconic-left {
1086 | margin-inline-end: 2px !important;
1087 | }
1088 |
1089 | toolbarbutton menupopup[placespopup] > menu > .menu-right {
1090 | list-style-image: none !important;
1091 | }
1092 |
1093 | toolbarbutton menupopup[placespopup] .menu-text {
1094 | margin-left: 18px !important;
1095 | }
1096 |
1097 | toolbarbutton menupopup[placespopup] menuseparator {
1098 | margin: 3px 0 5px 0 !important;
1099 | }
1100 |
1101 | toolbarbutton menupopup[placespopup] {
1102 | --panel-shadow: 2px 2px 2px rgb(0 0 0 / 50%) !important;
1103 | }
1104 |
1105 | #userScriptsMenu {
1106 | display: none !important;
1107 | }
1108 |
1109 | #searchFilter {
1110 | appearance: listbox !important;
1111 | }
1112 |
1113 | #statuspanel-label::after {
1114 | content: '';
1115 | appearance: resizer;
1116 | position: absolute;
1117 | right: 0;
1118 | bottom: 0;
1119 | }
1120 |
1121 | #statuspanel[inactive="true"] #statuspanel-label::before {
1122 | display: none !important;
1123 | }
1124 |
1125 | #statuspanel-label::before {
1126 | margin-left: 5px;
1127 | margin-top: 3px;
1128 | }
1129 |
1130 | findbar {
1131 | transition: none !important;
1132 | background-color: -moz-Dialog !important;
1133 | border-top: 0 !important;
1134 | }
1135 |
1136 | #tabbrowser-tabpanels {
1137 | margin-bottom: 22px !important;
1138 | }
1139 |
1140 | #main-window[privatebrowsingmode=temporary] label
1141 | {
1142 | color: black;
1143 | }
1144 |
1145 | #main-window[privatebrowsingmode=temporary] menu-text
1146 | {
1147 | color: white;
1148 | }
1149 |
1150 | #main-window[privatebrowsingmode=temporary] #fxa-toolbar-menu-button::after, #main-window[privatebrowsingmode=temporary] content, #main-window[privatebrowsingmode=temporary] #statuspanel
1151 | {
1152 | color: black;
1153 | }
1154 |
1155 | #main-window[privatebrowsingmode=temporary] #fxa-toolbar-menu-button:enabled
1156 | {
1157 | color: white;
1158 | }
--------------------------------------------------------------------------------
/profile/chrome/utils/BootstrapLoader.jsm:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | 'use strict';
6 |
7 | let EXPORTED_SYMBOLS = [];
8 |
9 | const { XPCOMUtils } = ChromeUtils.import('resource://gre/modules/XPCOMUtils.jsm');
10 |
11 | XPCOMUtils.defineLazyModuleGetters(this, {
12 | Blocklist: 'resource://gre/modules/Blocklist.jsm',
13 | ConsoleAPI: 'resource://gre/modules/Console.jsm',
14 | InstallRDF: 'chrome://userchromejs/content/RDFManifestConverter.jsm',
15 | Services: 'resource://gre/modules/Services.jsm',
16 | });
17 |
18 | // signing bypass by onemen
19 | const Constants = ChromeUtils.import('resource://gre/modules/AppConstants.jsm');
20 | const temp = Object.assign({}, Constants.AppConstants);
21 | temp.MOZ_REQUIRE_SIGNING = false
22 | Constants.AppConstants = Object.freeze(temp);
23 |
24 | Services.obs.addObserver(doc => {
25 | if (doc.location.protocol + doc.location.pathname === 'about:addons' ||
26 | doc.location.protocol + doc.location.pathname === 'chrome:/content/extensions/aboutaddons.html') {
27 | const win = doc.defaultView;
28 | let handleEvent_orig = win.customElements.get('addon-card').prototype.handleEvent;
29 | win.customElements.get('addon-card').prototype.handleEvent = function (e) {
30 | if (e.type === 'click' &&
31 | e.target.getAttribute('action') === 'preferences' &&
32 | this.addon.optionsType == 1/*AddonManager.OPTIONS_TYPE_DIALOG*/) {
33 | var windows = Services.wm.getEnumerator(null);
34 | while (windows.hasMoreElements()) {
35 | var win2 = windows.getNext();
36 | if (win2.closed) {
37 | continue;
38 | }
39 | if (win2.document.documentURI == this.addon.optionsURL) {
40 | win2.focus();
41 | return;
42 | }
43 | }
44 | var features = 'chrome,titlebar,toolbar,centerscreen';
45 | win.docShell.rootTreeItem.domWindow.openDialog(this.addon.optionsURL, this.addon.id, features);
46 | } else {
47 | handleEvent_orig.apply(this, arguments);
48 | }
49 | }
50 | let update_orig = win.customElements.get('addon-options').prototype.update;
51 | win.customElements.get('addon-options').prototype.update = function (card, addon) {
52 | update_orig.apply(this, arguments);
53 | if (addon.optionsType == 1/*AddonManager.OPTIONS_TYPE_DIALOG*/)
54 | this.querySelector('panel-item[data-l10n-id="preferences-addon-button"]').hidden = false;
55 | }
56 | }
57 | }, 'chrome-document-loaded');
58 |
59 | const {AddonManager} = ChromeUtils.import('resource://gre/modules/AddonManager.jsm');
60 | const {XPIDatabase, AddonInternal} = ChromeUtils.import('resource://gre/modules/addons/XPIDatabase.jsm');
61 |
62 | const { defineAddonWrapperProperty } = Cu.import('resource://gre/modules/addons/XPIDatabase.jsm');
63 | defineAddonWrapperProperty('optionsType', function optionsType() {
64 | if (!this.isActive) {
65 | return null;
66 | }
67 |
68 | let addon = this.__AddonInternal__;
69 | let hasOptionsURL = !!this.optionsURL;
70 |
71 | if (addon.optionsType) {
72 | switch (parseInt(addon.optionsType, 10)) {
73 | case 1/*AddonManager.OPTIONS_TYPE_DIALOG*/:
74 | case AddonManager.OPTIONS_TYPE_TAB:
75 | case AddonManager.OPTIONS_TYPE_INLINE_BROWSER:
76 | return hasOptionsURL ? addon.optionsType : null;
77 | }
78 | return null;
79 | }
80 |
81 | return null;
82 | });
83 |
84 | XPIDatabase.isDisabledLegacy = () => false;
85 |
86 | XPCOMUtils.defineLazyGetter(this, 'BOOTSTRAP_REASONS', () => {
87 | const {XPIProvider} = ChromeUtils.import('resource://gre/modules/addons/XPIProvider.jsm');
88 | return XPIProvider.BOOTSTRAP_REASONS;
89 | });
90 |
91 | const {Log} = ChromeUtils.import('resource://gre/modules/Log.jsm');
92 | var logger = Log.repository.getLogger('addons.bootstrap');
93 |
94 | /**
95 | * Valid IDs fit this pattern.
96 | */
97 | var gIDTest = /^(\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}|[a-z0-9-\._]*\@[a-z0-9-\._]+)$/i;
98 |
99 | // Properties that exist in the install manifest
100 | const PROP_METADATA = ['id', 'version', 'type', 'internalName', 'updateURL',
101 | 'optionsURL', 'optionsType', 'aboutURL', 'iconURL'];
102 | const PROP_LOCALE_SINGLE = ['name', 'description', 'creator', 'homepageURL'];
103 | const PROP_LOCALE_MULTI = ['developers', 'translators', 'contributors'];
104 |
105 | // Map new string type identifiers to old style nsIUpdateItem types.
106 | // Retired values:
107 | // 32 = multipackage xpi file
108 | // 8 = locale
109 | // 256 = apiextension
110 | // 128 = experiment
111 | // theme = 4
112 | const TYPES = {
113 | extension: 2,
114 | dictionary: 64,
115 | };
116 |
117 | const COMPATIBLE_BY_DEFAULT_TYPES = {
118 | extension: true,
119 | dictionary: true,
120 | };
121 |
122 | const hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty);
123 |
124 | function isXPI(filename) {
125 | let ext = filename.slice(-4).toLowerCase();
126 | return ext === '.xpi' || ext === '.zip';
127 | }
128 |
129 | /**
130 | * Gets an nsIURI for a file within another file, either a directory or an XPI
131 | * file. If aFile is a directory then this will return a file: URI, if it is an
132 | * XPI file then it will return a jar: URI.
133 | *
134 | * @param {nsIFile} aFile
135 | * The file containing the resources, must be either a directory or an
136 | * XPI file
137 | * @param {string} aPath
138 | * The path to find the resource at, '/' separated. If aPath is empty
139 | * then the uri to the root of the contained files will be returned
140 | * @returns {nsIURI}
141 | * An nsIURI pointing at the resource
142 | */
143 | function getURIForResourceInFile(aFile, aPath) {
144 | if (!isXPI(aFile.leafName)) {
145 | let resource = aFile.clone();
146 | if (aPath)
147 | aPath.split('/').forEach(part => resource.append(part));
148 |
149 | return Services.io.newFileURI(resource);
150 | }
151 |
152 | return buildJarURI(aFile, aPath);
153 | }
154 |
155 | /**
156 | * Creates a jar: URI for a file inside a ZIP file.
157 | *
158 | * @param {nsIFile} aJarfile
159 | * The ZIP file as an nsIFile
160 | * @param {string} aPath
161 | * The path inside the ZIP file
162 | * @returns {nsIURI}
163 | * An nsIURI for the file
164 | */
165 | function buildJarURI(aJarfile, aPath) {
166 | let uri = Services.io.newFileURI(aJarfile);
167 | uri = 'jar:' + uri.spec + '!/' + aPath;
168 | return Services.io.newURI(uri);
169 | }
170 |
171 | var BootstrapLoader = {
172 | name: 'bootstrap',
173 | manifestFile: 'install.rdf',
174 | async loadManifest(pkg) {
175 | /**
176 | * Reads locale properties from either the main install manifest root or
177 | * an em:localized section in the install manifest.
178 | *
179 | * @param {Object} aSource
180 | * The resource to read the properties from.
181 | * @param {boolean} isDefault
182 | * True if the locale is to be read from the main install manifest
183 | * root
184 | * @param {string[]} aSeenLocales
185 | * An array of locale names already seen for this install manifest.
186 | * Any locale names seen as a part of this function will be added to
187 | * this array
188 | * @returns {Object}
189 | * an object containing the locale properties
190 | */
191 | function readLocale(aSource, isDefault, aSeenLocales) {
192 | let locale = {};
193 | if (!isDefault) {
194 | locale.locales = [];
195 | for (let localeName of aSource.locales || []) {
196 | if (!localeName) {
197 | logger.warn('Ignoring empty locale in localized properties');
198 | continue;
199 | }
200 | if (aSeenLocales.includes(localeName)) {
201 | logger.warn('Ignoring duplicate locale in localized properties');
202 | continue;
203 | }
204 | aSeenLocales.push(localeName);
205 | locale.locales.push(localeName);
206 | }
207 |
208 | if (locale.locales.length == 0) {
209 | logger.warn('Ignoring localized properties with no listed locales');
210 | return null;
211 | }
212 | }
213 |
214 | for (let prop of [...PROP_LOCALE_SINGLE, ...PROP_LOCALE_MULTI]) {
215 | if (hasOwnProperty(aSource, prop)) {
216 | locale[prop] = aSource[prop];
217 | }
218 | }
219 |
220 | return locale;
221 | }
222 |
223 | let manifestData = await pkg.readString('install.rdf');
224 | let manifest = InstallRDF.loadFromString(manifestData).decode();
225 |
226 | let addon = new AddonInternal();
227 | for (let prop of PROP_METADATA) {
228 | if (hasOwnProperty(manifest, prop)) {
229 | addon[prop] = manifest[prop];
230 | }
231 | }
232 |
233 | if (!addon.type) {
234 | addon.type = 'extension';
235 | } else {
236 | let type = addon.type;
237 | addon.type = null;
238 | for (let name in TYPES) {
239 | if (TYPES[name] == type) {
240 | addon.type = name;
241 | break;
242 | }
243 | }
244 | }
245 |
246 | if (!(addon.type in TYPES))
247 | throw new Error('Install manifest specifies unknown type: ' + addon.type);
248 |
249 | if (!addon.id)
250 | throw new Error('No ID in install manifest');
251 | if (!gIDTest.test(addon.id))
252 | throw new Error('Illegal add-on ID ' + addon.id);
253 | if (!addon.version)
254 | throw new Error('No version in install manifest');
255 |
256 | addon.strictCompatibility = (!(addon.type in COMPATIBLE_BY_DEFAULT_TYPES) ||
257 | manifest.strictCompatibility == 'true');
258 |
259 | // Only read these properties for extensions.
260 | if (addon.type == 'extension') {
261 | if (manifest.bootstrap != 'true') {
262 | throw new Error('Non-restartless extensions no longer supported');
263 | }
264 |
265 | if (addon.optionsType &&
266 | addon.optionsType != 1/*AddonManager.OPTIONS_TYPE_DIALOG*/ &&
267 | addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE_BROWSER &&
268 | addon.optionsType != AddonManager.OPTIONS_TYPE_TAB) {
269 | throw new Error('Install manifest specifies unknown optionsType: ' + addon.optionsType);
270 | }
271 |
272 | if (addon.optionsType)
273 | addon.optionsType = parseInt(addon.optionsType);
274 | }
275 |
276 | addon.defaultLocale = readLocale(manifest, true);
277 |
278 | let seenLocales = [];
279 | addon.locales = [];
280 | for (let localeData of manifest.localized || []) {
281 | let locale = readLocale(localeData, false, seenLocales);
282 | if (locale)
283 | addon.locales.push(locale);
284 | }
285 |
286 | let dependencies = new Set(manifest.dependencies);
287 | addon.dependencies = Object.freeze(Array.from(dependencies));
288 |
289 | let seenApplications = [];
290 | addon.targetApplications = [];
291 | for (let targetApp of manifest.targetApplications || []) {
292 | if (!targetApp.id || !targetApp.minVersion ||
293 | !targetApp.maxVersion) {
294 | logger.warn('Ignoring invalid targetApplication entry in install manifest');
295 | continue;
296 | }
297 | if (seenApplications.includes(targetApp.id)) {
298 | logger.warn('Ignoring duplicate targetApplication entry for ' + targetApp.id +
299 | ' in install manifest');
300 | continue;
301 | }
302 | seenApplications.push(targetApp.id);
303 | addon.targetApplications.push(targetApp);
304 | }
305 |
306 | // Note that we don't need to check for duplicate targetPlatform entries since
307 | // the RDF service coalesces them for us.
308 | addon.targetPlatforms = [];
309 | for (let targetPlatform of manifest.targetPlatforms || []) {
310 | let platform = {
311 | os: null,
312 | abi: null,
313 | };
314 |
315 | let pos = targetPlatform.indexOf('_');
316 | if (pos != -1) {
317 | platform.os = targetPlatform.substring(0, pos);
318 | platform.abi = targetPlatform.substring(pos + 1);
319 | } else {
320 | platform.os = targetPlatform;
321 | }
322 |
323 | addon.targetPlatforms.push(platform);
324 | }
325 |
326 | addon.userDisabled = false;
327 | addon.softDisabled = addon.blocklistState == Blocklist.STATE_SOFTBLOCKED;
328 | addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;
329 |
330 | addon.userPermissions = null;
331 |
332 | addon.icons = {};
333 | if (await pkg.hasResource('icon.png')) {
334 | addon.icons[32] = 'icon.png';
335 | addon.icons[48] = 'icon.png';
336 | }
337 |
338 | if (await pkg.hasResource('icon64.png')) {
339 | addon.icons[64] = 'icon64.png';
340 | }
341 |
342 | return addon;
343 | },
344 |
345 | loadScope(addon) {
346 | let file = addon.file || addon._sourceBundle;
347 | let uri = getURIForResourceInFile(file, 'bootstrap.js').spec;
348 | let principal = Services.scriptSecurityManager.getSystemPrincipal();
349 |
350 | let sandbox = new Cu.Sandbox(principal, {
351 | sandboxName: uri,
352 | addonId: addon.id,
353 | wantGlobalProperties: ['ChromeUtils'],
354 | metadata: { addonID: addon.id, URI: uri },
355 | });
356 |
357 | try {
358 | Object.assign(sandbox, BOOTSTRAP_REASONS);
359 |
360 | XPCOMUtils.defineLazyGetter(sandbox, 'console', () =>
361 | new ConsoleAPI({ consoleID: `addon/${addon.id}` }));
362 |
363 | Services.scriptloader.loadSubScript(uri, sandbox);
364 | } catch (e) {
365 | logger.warn(`Error loading bootstrap.js for ${addon.id}`, e);
366 | }
367 |
368 | function findMethod(name) {
369 | if (sandbox[name]) {
370 | return sandbox[name];
371 | }
372 |
373 | try {
374 | let method = Cu.evalInSandbox(name, sandbox);
375 | return method;
376 | } catch (err) { }
377 |
378 | return () => {
379 | logger.warn(`Add-on ${addon.id} is missing bootstrap method ${name}`);
380 | };
381 | }
382 |
383 | let install = findMethod('install');
384 | let uninstall = findMethod('uninstall');
385 | let startup = findMethod('startup');
386 | let shutdown = findMethod('shutdown');
387 |
388 | return {
389 | install(...args) {
390 | install(...args);
391 | // Forget any cached files we might've had from this extension.
392 | Services.obs.notifyObservers(null, 'startupcache-invalidate');
393 | },
394 |
395 | uninstall(...args) {
396 | uninstall(...args);
397 | // Forget any cached files we might've had from this extension.
398 | Services.obs.notifyObservers(null, 'startupcache-invalidate');
399 | },
400 |
401 | startup(...args) {
402 | if (addon.type == 'extension') {
403 | logger.debug(`Registering manifest for ${file.path}\n`);
404 | Components.manager.addBootstrappedManifestLocation(file);
405 | }
406 | return startup(...args);
407 | },
408 |
409 | shutdown(data, reason) {
410 | try {
411 | return shutdown(data, reason);
412 | } catch (err) {
413 | throw err;
414 | } finally {
415 | if (reason != BOOTSTRAP_REASONS.APP_SHUTDOWN) {
416 | logger.debug(`Removing manifest for ${file.path}\n`);
417 | Components.manager.removeBootstrappedManifestLocation(file);
418 | }
419 | }
420 | },
421 | };
422 | },
423 | };
424 |
425 | AddonManager.addExternalExtensionLoader(BootstrapLoader);
426 |
427 | if (AddonManager.isReady) {
428 | AddonManager.getAllAddons().then(addons => {
429 | addons.forEach(addon => {
430 | if (addon.type == 'extension' && !addon.isWebExtension && !addon.userDisabled) {
431 | addon.reload();
432 | };
433 | });
434 | });
435 | }
436 |
--------------------------------------------------------------------------------
/profile/chrome/utils/RDFDataSource.jsm:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | /**
6 | * This module creates a new API for accessing and modifying RDF graphs. The
7 | * goal is to be able to serialise the graph in a human readable form. Also
8 | * if the graph was originally loaded from an RDF/XML the serialisation should
9 | * closely match the original with any new data closely following the existing
10 | * layout. The output should always be compatible with Mozilla's RDF parser.
11 | *
12 | * This is all achieved by using a DOM Document to hold the current state of the
13 | * graph in XML form. This can be initially loaded and parsed from disk or
14 | * a blank document used for an empty graph. As assertions are added to the
15 | * graph, appropriate DOM nodes are added to the document to represent them
16 | * along with any necessary whitespace to properly layout the XML.
17 | *
18 | * In general the order of adding assertions to the graph will impact the form
19 | * the serialisation takes. If a resource is first added as the object of an
20 | * assertion then it will eventually be serialised inside the assertion's
21 | * property element. If a resource is first added as the subject of an assertion
22 | * then it will be serialised at the top level of the XML.
23 | */
24 |
25 | const NS_XML = "http://www.w3.org/XML/1998/namespace";
26 | const NS_XMLNS = "http://www.w3.org/2000/xmlns/";
27 | const NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
28 | const NS_NC = "http://home.netscape.com/NC-rdf#";
29 |
30 | /* eslint prefer-template: 1 */
31 |
32 | var EXPORTED_SYMBOLS = ["RDFLiteral", "RDFBlankNode", "RDFResource", "RDFDataSource"];
33 |
34 | const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
35 |
36 | XPCOMUtils.defineLazyGlobalGetters(this, ["DOMParser", "Element", "fetch"]);
37 |
38 | ChromeUtils.defineModuleGetter(this, "Services",
39 | "resource://gre/modules/Services.jsm");
40 |
41 | function isElement(obj) {
42 | return Element.isInstance(obj);
43 | }
44 | function isText(obj) {
45 | return obj && typeof obj == "object" && ChromeUtils.getClassName(obj) == "Text";
46 | }
47 |
48 | /**
49 | * Returns either an rdf namespaced attribute or an un-namespaced attribute
50 | * value. Returns null if neither exists,
51 | */
52 | function getRDFAttribute(element, name) {
53 | if (element.hasAttributeNS(NS_RDF, name))
54 | return element.getAttributeNS(NS_RDF, name);
55 | if (element.hasAttribute(name))
56 | return element.getAttribute(name);
57 | return undefined;
58 | }
59 |
60 | /**
61 | * Represents an assertion in the datasource
62 | */
63 | class RDFAssertion {
64 | constructor(subject, predicate, object) {
65 | // The subject on this assertion, an RDFSubject
66 | this._subject = subject;
67 | // The predicate, a string
68 | this._predicate = predicate;
69 | // The object, an RDFNode
70 | this._object = object;
71 | // The datasource this assertion exists in
72 | this._ds = this._subject._ds;
73 | // Marks that _DOMnode is the subject's element
74 | this._isSubjectElement = false;
75 | // The DOM node that represents this assertion. Could be a property element,
76 | // a property attribute or the subject's element for rdf:type
77 | this._DOMNode = null;
78 | }
79 |
80 | getPredicate() {
81 | return this._predicate;
82 | }
83 |
84 | getObject() {
85 | return this._object;
86 | }
87 | }
88 |
89 | class RDFNode {
90 | equals(rdfnode) {
91 | return (rdfnode.constructor === this.constructor &&
92 | rdfnode._value == this._value);
93 | }
94 | }
95 |
96 | /**
97 | * A simple literal value
98 | */
99 | class RDFLiteral extends RDFNode {
100 | constructor(value) {
101 | super();
102 | this._value = value;
103 | }
104 |
105 | getValue() {
106 | return this._value;
107 | }
108 | }
109 |
110 | /**
111 | * This is an RDF node that can be a subject so a resource or a blank node
112 | */
113 | class RDFSubject extends RDFNode {
114 | constructor(ds) {
115 | super();
116 | // A lookup of the assertions with this as the subject. Keyed on predicate
117 | this._assertions = {};
118 | // A lookup of the assertions with this as the object. Keyed on predicate
119 | this._backwards = {};
120 | // The datasource this subject belongs to
121 | this._ds = ds;
122 | // The DOM elements in the document that represent this subject. Array of Element
123 | this._elements = [];
124 | }
125 |
126 | /**
127 | * Parses the given Element from the DOM document
128 | */
129 | /* eslint-disable complexity */
130 | _parseElement(element) {
131 | this._elements.push(element);
132 |
133 | // There might be an inferred rdf:type assertion in the element name
134 | if (element.namespaceURI != NS_RDF ||
135 | element.localName != "Description") {
136 | var assertion = new RDFAssertion(this, RDF_R("type"),
137 | this._ds.getResource(element.namespaceURI + element.localName));
138 | assertion._DOMnode = element;
139 | assertion._isSubjectElement = true;
140 | this._addAssertion(assertion);
141 | }
142 |
143 | // Certain attributes can be literal properties
144 | for (let attr of element.attributes) {
145 | if (attr.namespaceURI == NS_XML || attr.namespaceURI == NS_XMLNS ||
146 | attr.nodeName == "xmlns")
147 | continue;
148 | if ((attr.namespaceURI == NS_RDF || !attr.namespaceURI) &&
149 | (["nodeID", "about", "resource", "ID", "parseType"].includes(attr.localName)))
150 | continue;
151 | var object = null;
152 | if (attr.namespaceURI == NS_RDF) {
153 | if (attr.localName == "type")
154 | object = this._ds.getResource(attr.nodeValue);
155 | }
156 | if (!object)
157 | object = new RDFLiteral(attr.nodeValue);
158 | assertion = new RDFAssertion(this, attr.namespaceURI + attr.localName, object);
159 | assertion._DOMnode = attr;
160 | this._addAssertion(assertion);
161 | }
162 |
163 | var child = element.firstChild;
164 | element.listCounter = 1;
165 | while (child) {
166 | if (isElement(child)) {
167 | object = null;
168 | var predicate = child.namespaceURI + child.localName;
169 | if (child.namespaceURI == NS_RDF) {
170 | if (child.localName == "li") {
171 | predicate = RDF_R(`_${element.listCounter}`);
172 | element.listCounter++;
173 | }
174 | }
175 |
176 | // Check for and bail out on unknown attributes on the property element
177 | for (let attr of child.attributes) {
178 | // Ignore XML namespaced attributes
179 | if (attr.namespaceURI == NS_XML)
180 | continue;
181 | // These are reserved by XML for future use
182 | if (attr.localName.substring(0, 3).toLowerCase() == "xml")
183 | continue;
184 | // We can handle these RDF attributes
185 | if ((!attr.namespaceURI || attr.namespaceURI == NS_RDF) &&
186 | ["resource", "nodeID"].includes(attr.localName))
187 | continue;
188 | // This is a special attribute we handle for compatibility with Mozilla RDF
189 | if (attr.namespaceURI == NS_NC &&
190 | attr.localName == "parseType")
191 | continue;
192 | }
193 |
194 | var parseType = child.getAttributeNS(NS_NC, "parseType");
195 |
196 | var resource = getRDFAttribute(child, "resource");
197 | var nodeID = getRDFAttribute(child, "nodeID");
198 |
199 | if (resource !== undefined) {
200 | var base = Services.io.newURI(element.baseURI);
201 | object = this._ds.getResource(base.resolve(resource));
202 | } else if (nodeID !== undefined) {
203 | object = this._ds.getBlankNode(nodeID);
204 | } else {
205 | var hasText = false;
206 | var childElement = null;
207 | var subchild = child.firstChild;
208 | while (subchild) {
209 | if (isText(subchild) && /\S/.test(subchild.nodeValue)) {
210 | hasText = true;
211 | } else if (isElement(subchild)) {
212 | childElement = subchild;
213 | }
214 | subchild = subchild.nextSibling;
215 | }
216 |
217 | if (childElement) {
218 | object = this._ds._getSubjectForElement(childElement);
219 | object._parseElement(childElement);
220 | } else
221 | object = new RDFLiteral(child.textContent);
222 | }
223 |
224 | assertion = new RDFAssertion(this, predicate, object);
225 | this._addAssertion(assertion);
226 | assertion._DOMnode = child;
227 | }
228 | child = child.nextSibling;
229 | }
230 | }
231 | /* eslint-enable complexity */
232 |
233 | /**
234 | * Adds a new assertion to the internal hashes. Should be called for every
235 | * new assertion parsed or created programmatically.
236 | */
237 | _addAssertion(assertion) {
238 | var predicate = assertion.getPredicate();
239 | if (predicate in this._assertions)
240 | this._assertions[predicate].push(assertion);
241 | else
242 | this._assertions[predicate] = [ assertion ];
243 |
244 | var object = assertion.getObject();
245 | if (object instanceof RDFSubject) {
246 | // Create reverse assertion
247 | if (predicate in object._backwards)
248 | object._backwards[predicate].push(assertion);
249 | else
250 | object._backwards[predicate] = [ assertion ];
251 | }
252 | }
253 |
254 | /**
255 | * Returns all objects in assertions with this subject and the given predicate.
256 | */
257 | getObjects(predicate) {
258 | if (predicate in this._assertions)
259 | return Array.from(this._assertions[predicate],
260 | i => i.getObject());
261 |
262 | return [];
263 | }
264 |
265 | /**
266 | * Retrieves the first property value for the given predicate.
267 | */
268 | getProperty(predicate) {
269 | if (predicate in this._assertions)
270 | return this._assertions[predicate][0].getObject();
271 | return null;
272 | }
273 | }
274 |
275 | /**
276 | * Creates a new RDFResource for the datasource. Private.
277 | */
278 | class RDFResource extends RDFSubject {
279 | constructor(ds, uri) {
280 | super(ds);
281 | // This is the uri that the resource represents.
282 | this._uri = uri;
283 | }
284 | }
285 |
286 | /**
287 | * Creates a new blank node. Private.
288 | */
289 | class RDFBlankNode extends RDFSubject {
290 | constructor(ds, nodeID) {
291 | super(ds);
292 | // The nodeID of this node. May be null if there is no ID.
293 | this._nodeID = nodeID;
294 | }
295 |
296 | /**
297 | * Sets attributes on the DOM element to mark it as representing this node
298 | */
299 | _applyToElement(element) {
300 | if (!this._nodeID)
301 | return;
302 | if (USE_RDFNS_ATTR) {
303 | var prefix = this._ds._resolvePrefix(element, RDF_R("nodeID"));
304 | element.setAttributeNS(prefix.namespaceURI, prefix.qname, this._nodeID);
305 | } else {
306 | element.setAttribute("nodeID", this._nodeID);
307 | }
308 | }
309 |
310 | /**
311 | * Creates a new Element in the document for holding assertions about this
312 | * subject. The URI controls what tagname to use.
313 | */
314 | _createNewElement(uri) {
315 | // If there are already nodes representing this in the document then we need
316 | // a nodeID to match them
317 | if (!this._nodeID && this._elements.length > 0) {
318 | this._ds._createNodeID(this);
319 | for (let element of this._elements)
320 | this._applyToElement(element);
321 | }
322 |
323 | return super._createNewElement.call(uri);
324 | }
325 |
326 | /**
327 | * Adds a reference to this node to the given property Element.
328 | */
329 | _addReferenceToElement(element) {
330 | if (this._elements.length > 0 && !this._nodeID) {
331 | // In document elsewhere already
332 | // Create a node ID and update the other nodes referencing
333 | this._ds._createNodeID(this);
334 | for (let element of this._elements)
335 | this._applyToElement(element);
336 | }
337 |
338 | if (this._nodeID) {
339 | if (USE_RDFNS_ATTR) {
340 | let prefix = this._ds._resolvePrefix(element, RDF_R("nodeID"));
341 | element.setAttributeNS(prefix.namespaceURI, prefix.qname, this._nodeID);
342 | } else {
343 | element.setAttribute("nodeID", this._nodeID);
344 | }
345 | } else {
346 | // Add the empty blank node, this is generally right since further
347 | // assertions will be added to fill this out
348 | var newelement = this._ds._addElement(element, RDF_R("Description"));
349 | newelement.listCounter = 1;
350 | this._elements.push(newelement);
351 | }
352 | }
353 |
354 | /**
355 | * Removes any reference to this node from the given property Element.
356 | */
357 | _removeReferenceFromElement(element) {
358 | if (element.hasAttributeNS(NS_RDF, "nodeID"))
359 | element.removeAttributeNS(NS_RDF, "nodeID");
360 | if (element.hasAttribute("nodeID"))
361 | element.removeAttribute("nodeID");
362 | }
363 |
364 | getNodeID() {
365 | return this._nodeID;
366 | }
367 | }
368 |
369 | /**
370 | * Creates a new RDFDataSource from the given document. The document will be
371 | * changed as assertions are added and removed to the RDF. Pass a null document
372 | * to start with an empty graph.
373 | */
374 | class RDFDataSource {
375 | constructor(document) {
376 | // All known resources, indexed on URI
377 | this._resources = {};
378 | // All blank nodes
379 | this._allBlankNodes = [];
380 |
381 | // The underlying DOM document for this datasource
382 | this._document = document;
383 | this._parseDocument();
384 | }
385 |
386 | static loadFromString(text) {
387 | let parser = new DOMParser();
388 | let document = parser.parseFromString(text, "application/xml");
389 |
390 | return new this(document);
391 | }
392 |
393 | /**
394 | * Returns an rdf subject for the given DOM Element. If the subject has not
395 | * been seen before a new one is created.
396 | */
397 | _getSubjectForElement(element) {
398 | var about = getRDFAttribute(element, "about");
399 |
400 | if (about !== undefined) {
401 | let base = Services.io.newURI(element.baseURI);
402 | return this.getResource(base.resolve(about));
403 | }
404 | return this.getBlankNode(null);
405 | }
406 |
407 | /**
408 | * Parses the document for subjects at the top level.
409 | */
410 | _parseDocument() {
411 | var domnode = this._document.documentElement.firstChild;
412 | while (domnode) {
413 | if (isElement(domnode)) {
414 | var subject = this._getSubjectForElement(domnode);
415 | subject._parseElement(domnode);
416 | }
417 | domnode = domnode.nextSibling;
418 | }
419 | }
420 |
421 | /**
422 | * Gets a blank node. nodeID may be null and if so a new blank node is created.
423 | * If a nodeID is given then the blank node with that ID is returned or created.
424 | */
425 | getBlankNode(nodeID) {
426 | var rdfnode = new RDFBlankNode(this, nodeID);
427 | this._allBlankNodes.push(rdfnode);
428 | return rdfnode;
429 | }
430 |
431 | /**
432 | * Gets the resource for the URI. The resource is created if it has not been
433 | * used already.
434 | */
435 | getResource(uri) {
436 | if (uri in this._resources)
437 | return this._resources[uri];
438 |
439 | var resource = new RDFResource(this, uri);
440 | this._resources[uri] = resource;
441 | return resource;
442 | }
443 | }
444 |
--------------------------------------------------------------------------------
/profile/chrome/utils/RDFManifestConverter.jsm:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 | "use strict";
5 |
6 | var EXPORTED_SYMBOLS = ["InstallRDF"];
7 |
8 | ChromeUtils.defineModuleGetter(this, "RDFDataSource",
9 | "chrome://userchromejs/content/RDFDataSource.jsm");
10 |
11 | const RDFURI_INSTALL_MANIFEST_ROOT = "urn:mozilla:install-manifest";
12 |
13 | function EM_R(aProperty) {
14 | return `http://www.mozilla.org/2004/em-rdf#${aProperty}`;
15 | }
16 |
17 | function getValue(literal) {
18 | return literal && literal.getValue();
19 | }
20 |
21 | function getProperty(resource, property) {
22 | return getValue(resource.getProperty(EM_R(property)));
23 | }
24 |
25 | class Manifest {
26 | constructor(ds) {
27 | this.ds = ds;
28 | }
29 |
30 | static loadFromString(text) {
31 | return new this(RDFDataSource.loadFromString(text));
32 | }
33 | }
34 |
35 | class InstallRDF extends Manifest {
36 | _readProps(source, obj, props) {
37 | for (let prop of props) {
38 | let val = getProperty(source, prop);
39 | if (val != null) {
40 | obj[prop] = val;
41 | }
42 | }
43 | }
44 |
45 | _readArrayProp(source, obj, prop, target, decode = getValue) {
46 | let result = Array.from(source.getObjects(EM_R(prop)),
47 | target => decode(target));
48 | if (result.length) {
49 | obj[target] = result;
50 | }
51 | }
52 |
53 | _readArrayProps(source, obj, props, decode = getValue) {
54 | for (let [prop, target] of Object.entries(props)) {
55 | this._readArrayProp(source, obj, prop, target, decode);
56 | }
57 | }
58 |
59 | _readLocaleStrings(source, obj) {
60 | this._readProps(source, obj, ["name", "description", "creator", "homepageURL"]);
61 | this._readArrayProps(source, obj, {
62 | locale: "locales",
63 | developer: "developers",
64 | translator: "translators",
65 | contributor: "contributors",
66 | });
67 | }
68 |
69 | decode() {
70 | let root = this.ds.getResource(RDFURI_INSTALL_MANIFEST_ROOT);
71 | let result = {};
72 |
73 | let props = ["id", "version", "type", "updateURL", "optionsURL",
74 | "optionsType", "aboutURL", "iconURL",
75 | "bootstrap", "unpack", "strictCompatibility"];
76 | this._readProps(root, result, props);
77 |
78 | let decodeTargetApplication = source => {
79 | let app = {};
80 | this._readProps(source, app, ["id", "minVersion", "maxVersion"]);
81 | return app;
82 | };
83 |
84 | let decodeLocale = source => {
85 | let localized = {};
86 | this._readLocaleStrings(source, localized);
87 | return localized;
88 | };
89 |
90 | this._readLocaleStrings(root, result);
91 |
92 | this._readArrayProps(root, result, {"targetPlatform": "targetPlatforms"});
93 | this._readArrayProps(root, result, {"targetApplication": "targetApplications"},
94 | decodeTargetApplication);
95 | this._readArrayProps(root, result, {"localized": "localized"},
96 | decodeLocale);
97 | this._readArrayProps(root, result, {"dependency": "dependencies"},
98 | source => getProperty(source, "id"));
99 |
100 | return result;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/profile/chrome/utils/aboutconfig/aboutconfig.xhtml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
11 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
71 |
72 |
74 |
75 |
84 |
87 |
88 |
90 |
91 |
93 |
94 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/profile/chrome/utils/aboutconfig/config.css:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | #warningScreen {
6 | font-size: 15px;
7 | padding-top: 0;
8 | padding-bottom: 0;
9 | padding-inline-start: calc(48px + 4.6em);
10 | padding-inline-end: 48px;
11 | }
12 |
13 | .title {
14 | background-image: url("chrome://global/skin/icons/warning.svg");
15 | fill: #fcd100;
16 | }
17 |
18 | #warningTitle {
19 | font-weight: lighter;
20 | line-height: 1.2;
21 | margin: 0;
22 | margin-bottom: .5em;
23 | }
24 |
25 | #warningText {
26 | margin: 1em 0;
27 | }
28 |
29 | #warningButton {
30 | margin-top: 0.6em;
31 | }
32 |
33 | #filterRow {
34 | margin-top: 4px;
35 | margin-inline-start: 4px;
36 | }
37 |
38 | #configTree {
39 | margin-top: 4px;
40 | margin-bottom: 4px;
41 | }
42 |
43 | #configTreeBody::-moz-tree-cell-text(user) {
44 | font-weight: bold;
45 | }
46 |
47 | #configTreeBody::-moz-tree-cell-text(locked) {
48 | font-style: italic;
49 | }
--------------------------------------------------------------------------------
/profile/chrome/utils/aboutconfig/config.js:
--------------------------------------------------------------------------------
1 | // -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
2 |
3 | /* This Source Code Form is subject to the terms of the Mozilla Public
4 | * License, v. 2.0. If a copy of the MPL was not distributed with this
5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 |
7 | const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
8 |
9 | const nsIPrefLocalizedString = Ci.nsIPrefLocalizedString;
10 | const nsISupportsString = Ci.nsISupportsString;
11 | const nsIPrefBranch = Ci.nsIPrefBranch;
12 | const nsIClipboardHelper = Ci.nsIClipboardHelper;
13 |
14 | const nsClipboardHelper_CONTRACTID = "@mozilla.org/widget/clipboardhelper;1";
15 |
16 | const gPrefBranch = Services.prefs;
17 | const gClipboardHelper = Cc[nsClipboardHelper_CONTRACTID].getService(
18 | nsIClipboardHelper
19 | );
20 |
21 | var gLockProps = ["default", "user", "locked"];
22 | // we get these from a string bundle
23 | var gLockStrs = [];
24 | var gTypeStrs = [];
25 |
26 | const PREF_IS_DEFAULT_VALUE = 0;
27 | const PREF_IS_MODIFIED = 1;
28 | const PREF_IS_LOCKED = 2;
29 |
30 | var gPrefHash = {};
31 | var gPrefArray = [];
32 | var gPrefView = gPrefArray; // share the JS array
33 | var gSortedColumn = "prefCol";
34 | var gSortFunction = null;
35 | var gSortDirection = 1; // 1 is ascending; -1 is descending
36 | var gFilter = null;
37 |
38 | var view = {
39 | get rowCount() {
40 | return gPrefView.length;
41 | },
42 | getCellText(index, col) {
43 | if (!(index in gPrefView)) {
44 | return "";
45 | }
46 |
47 | var value = gPrefView[index][col.id];
48 |
49 | switch (col.id) {
50 | case "lockCol":
51 | return gLockStrs[value];
52 | case "typeCol":
53 | return gTypeStrs[value];
54 | default:
55 | return value;
56 | }
57 | },
58 | getRowProperties(index) {
59 | return "";
60 | },
61 | getCellProperties(index, col) {
62 | if (index in gPrefView) {
63 | return gLockProps[gPrefView[index].lockCol];
64 | }
65 |
66 | return "";
67 | },
68 | getColumnProperties(col) {
69 | return "";
70 | },
71 | treebox: null,
72 | selection: null,
73 | isContainer(index) {
74 | return false;
75 | },
76 | isContainerOpen(index) {
77 | return false;
78 | },
79 | isContainerEmpty(index) {
80 | return false;
81 | },
82 | isSorted() {
83 | return true;
84 | },
85 | canDrop(index, orientation) {
86 | return false;
87 | },
88 | drop(row, orientation) {},
89 | setTree(out) {
90 | this.treebox = out;
91 | },
92 | getParentIndex(rowIndex) {
93 | return -1;
94 | },
95 | hasNextSibling(rowIndex, afterIndex) {
96 | return false;
97 | },
98 | getLevel(index) {
99 | return 1;
100 | },
101 | getImageSrc(row, col) {
102 | return "";
103 | },
104 | toggleOpenState(index) {},
105 | cycleHeader(col) {
106 | var index = this.selection.currentIndex;
107 | if (col.id == gSortedColumn) {
108 | gSortDirection = -gSortDirection;
109 | gPrefArray.reverse();
110 | if (gPrefView != gPrefArray) {
111 | gPrefView.reverse();
112 | }
113 | if (index >= 0) {
114 | index = gPrefView.length - index - 1;
115 | }
116 | } else {
117 | var pref = null;
118 | if (index >= 0) {
119 | pref = gPrefView[index];
120 | }
121 |
122 | var old = document.getElementById(gSortedColumn);
123 | old.removeAttribute("sortDirection");
124 | gPrefArray.sort((gSortFunction = gSortFunctions[col.id]));
125 | if (gPrefView != gPrefArray) {
126 | gPrefView.sort(gSortFunction);
127 | }
128 | gSortedColumn = col.id;
129 | if (pref) {
130 | index = getViewIndexOfPref(pref);
131 | }
132 | }
133 | col.element.setAttribute(
134 | "sortDirection",
135 | gSortDirection > 0 ? "ascending" : "descending"
136 | );
137 | this.treebox.invalidate();
138 | if (index >= 0) {
139 | this.selection.select(index);
140 | this.treebox.ensureRowIsVisible(index);
141 | }
142 | },
143 | selectionChanged() {},
144 | cycleCell(row, col) {},
145 | isEditable(row, col) {
146 | return false;
147 | },
148 | setCellValue(row, col, value) {},
149 | setCellText(row, col, value) {},
150 | isSeparator(index) {
151 | return false;
152 | },
153 | };
154 |
155 | // find the index in gPrefView of a pref object
156 | // or -1 if it does not exist in the filtered view
157 | function getViewIndexOfPref(pref) {
158 | var low = -1,
159 | high = gPrefView.length;
160 | var index = (low + high) >> 1;
161 | while (index > low) {
162 | var mid = gPrefView[index];
163 | if (mid == pref) {
164 | return index;
165 | }
166 | if (gSortFunction(mid, pref) < 0) {
167 | low = index;
168 | } else {
169 | high = index;
170 | }
171 | index = (low + high) >> 1;
172 | }
173 | return -1;
174 | }
175 |
176 | // find the index in gPrefView where a pref object belongs
177 | function getNearestViewIndexOfPref(pref) {
178 | var low = -1,
179 | high = gPrefView.length;
180 | var index = (low + high) >> 1;
181 | while (index > low) {
182 | if (gSortFunction(gPrefView[index], pref) < 0) {
183 | low = index;
184 | } else {
185 | high = index;
186 | }
187 | index = (low + high) >> 1;
188 | }
189 | return high;
190 | }
191 |
192 | // find the index in gPrefArray of a pref object
193 | function getIndexOfPref(pref) {
194 | var low = -1,
195 | high = gPrefArray.length;
196 | var index = (low + high) >> 1;
197 | while (index > low) {
198 | var mid = gPrefArray[index];
199 | if (mid == pref) {
200 | return index;
201 | }
202 | if (gSortFunction(mid, pref) < 0) {
203 | low = index;
204 | } else {
205 | high = index;
206 | }
207 | index = (low + high) >> 1;
208 | }
209 | return index;
210 | }
211 |
212 | function getNearestIndexOfPref(pref) {
213 | var low = -1,
214 | high = gPrefArray.length;
215 | var index = (low + high) >> 1;
216 | while (index > low) {
217 | if (gSortFunction(gPrefArray[index], pref) < 0) {
218 | low = index;
219 | } else {
220 | high = index;
221 | }
222 | index = (low + high) >> 1;
223 | }
224 | return high;
225 | }
226 |
227 | var gPrefListener = {
228 | observe(subject, topic, prefName) {
229 | if (topic != "nsPref:changed") {
230 | return;
231 | }
232 |
233 | var arrayIndex = gPrefArray.length;
234 | var viewIndex = arrayIndex;
235 | var selectedIndex = view.selection.currentIndex;
236 | var pref;
237 | var updateView = false;
238 | var updateArray = false;
239 | var addedRow = false;
240 | if (prefName in gPrefHash) {
241 | pref = gPrefHash[prefName];
242 | viewIndex = getViewIndexOfPref(pref);
243 | arrayIndex = getIndexOfPref(pref);
244 | fetchPref(prefName, arrayIndex);
245 | // fetchPref replaces the existing pref object
246 | pref = gPrefHash[prefName];
247 | if (viewIndex >= 0) {
248 | // Might need to update the filtered view
249 | gPrefView[viewIndex] = gPrefHash[prefName];
250 | view.treebox.invalidateRow(viewIndex);
251 | }
252 | if (gSortedColumn == "lockCol" || gSortedColumn == "valueCol") {
253 | updateArray = true;
254 | gPrefArray.splice(arrayIndex, 1);
255 | if (gFilter && gFilter.test(pref.prefCol + ";" + pref.valueCol)) {
256 | updateView = true;
257 | gPrefView.splice(viewIndex, 1);
258 | }
259 | }
260 | } else {
261 | fetchPref(prefName, arrayIndex);
262 | pref = gPrefArray.pop();
263 | updateArray = true;
264 | addedRow = true;
265 | if (gFilter && gFilter.test(pref.prefCol + ";" + pref.valueCol)) {
266 | updateView = true;
267 | }
268 | }
269 | if (updateArray) {
270 | // Reinsert in the data array
271 | var newIndex = getNearestIndexOfPref(pref);
272 | gPrefArray.splice(newIndex, 0, pref);
273 |
274 | if (updateView) {
275 | // View is filtered, reinsert in the view separately
276 | newIndex = getNearestViewIndexOfPref(pref);
277 | gPrefView.splice(newIndex, 0, pref);
278 | } else if (gFilter) {
279 | // View is filtered, but nothing to update
280 | return;
281 | }
282 |
283 | if (addedRow) {
284 | view.treebox.rowCountChanged(newIndex, 1);
285 | }
286 |
287 | // Invalidate the changed range in the view
288 | var low = Math.min(viewIndex, newIndex);
289 | var high = Math.max(viewIndex, newIndex);
290 | view.treebox.invalidateRange(low, high);
291 |
292 | if (selectedIndex == viewIndex) {
293 | selectedIndex = newIndex;
294 | } else if (selectedIndex >= low && selectedIndex <= high) {
295 | selectedIndex += newIndex > viewIndex ? -1 : 1;
296 | }
297 | if (selectedIndex >= 0) {
298 | view.selection.select(selectedIndex);
299 | if (selectedIndex == newIndex) {
300 | view.treebox.ensureRowIsVisible(selectedIndex);
301 | }
302 | }
303 | }
304 | },
305 | };
306 |
307 | function prefObject(prefName, prefIndex) {
308 | this.prefCol = prefName;
309 | }
310 |
311 | prefObject.prototype = {
312 | lockCol: PREF_IS_DEFAULT_VALUE,
313 | typeCol: nsIPrefBranch.PREF_STRING,
314 | valueCol: "",
315 | };
316 |
317 | function fetchPref(prefName, prefIndex) {
318 | var pref = new prefObject(prefName);
319 |
320 | gPrefHash[prefName] = pref;
321 | gPrefArray[prefIndex] = pref;
322 |
323 | if (gPrefBranch.prefIsLocked(prefName)) {
324 | pref.lockCol = PREF_IS_LOCKED;
325 | } else if (gPrefBranch.prefHasUserValue(prefName)) {
326 | pref.lockCol = PREF_IS_MODIFIED;
327 | }
328 |
329 | try {
330 | switch (gPrefBranch.getPrefType(prefName)) {
331 | case gPrefBranch.PREF_BOOL:
332 | pref.typeCol = gPrefBranch.PREF_BOOL;
333 | // convert to a string
334 | pref.valueCol = gPrefBranch.getBoolPref(prefName).toString();
335 | break;
336 | case gPrefBranch.PREF_INT:
337 | pref.typeCol = gPrefBranch.PREF_INT;
338 | // convert to a string
339 | pref.valueCol = gPrefBranch.getIntPref(prefName).toString();
340 | break;
341 | default:
342 | case gPrefBranch.PREF_STRING:
343 | pref.valueCol = gPrefBranch.getStringPref(prefName);
344 | // Try in case it's a localized string (will throw an exception if not)
345 | if (
346 | pref.lockCol == PREF_IS_DEFAULT_VALUE &&
347 | /^chrome:\/\/.+\/locale\/.+\.properties/.test(pref.valueCol)
348 | ) {
349 | pref.valueCol = gPrefBranch.getComplexValue(
350 | prefName,
351 | nsIPrefLocalizedString
352 | ).data;
353 | }
354 | break;
355 | }
356 | } catch (e) {
357 | // Also catch obscure cases in which you can't tell in advance
358 | // that the pref exists but has no user or default value...
359 | }
360 | }
361 |
362 | async function onConfigLoad() {
363 | let configContext = document.getElementById("configContext");
364 | configContext.addEventListener("popupshowing", function(event) {
365 | if (event.target == this) {
366 | updateContextMenu();
367 | }
368 | });
369 |
370 | let commandListeners = {
371 | toggleSelected: ModifySelected,
372 | modifySelected: ModifySelected,
373 | copyPref,
374 | copyName,
375 | copyValue,
376 | resetSelected: ResetSelected,
377 | };
378 |
379 | configContext.addEventListener("command", e => {
380 | if (e.target.id in commandListeners) {
381 | commandListeners[e.target.id]();
382 | }
383 | });
384 |
385 | let configString = document.getElementById("configString");
386 | configString.addEventListener("command", function() {
387 | NewPref(nsIPrefBranch.PREF_STRING);
388 | });
389 |
390 | let configInt = document.getElementById("configInt");
391 | configInt.addEventListener("command", function() {
392 | NewPref(nsIPrefBranch.PREF_INT);
393 | });
394 |
395 | let configBool = document.getElementById("configBool");
396 | configBool.addEventListener("command", function() {
397 | NewPref(nsIPrefBranch.PREF_BOOL);
398 | });
399 |
400 | let keyVKReturn = document.getElementById("keyVKReturn");
401 | keyVKReturn.addEventListener("command", ModifySelected);
402 |
403 | let textBox = document.getElementById("textbox");
404 | textBox.addEventListener("command", FilterPrefs);
405 |
406 | let configFocuSearch = document.getElementById("configFocuSearch");
407 | configFocuSearch.addEventListener("command", function() {
408 | textBox.focus();
409 | });
410 |
411 | let configFocuSearch2 = document.getElementById("configFocuSearch2");
412 | configFocuSearch2.addEventListener("command", function() {
413 | textBox.focus();
414 | });
415 |
416 | let warningButton = document.getElementById("warningButton");
417 | warningButton.addEventListener("command", ShowPrefs);
418 |
419 | let configTree = document.getElementById("configTree");
420 | configTree.addEventListener("select", function() {
421 | window.updateCommands("select");
422 | });
423 |
424 | let configTreeBody = document.getElementById("configTreeBody");
425 | configTreeBody.addEventListener("dblclick", function(event) {
426 | if (event.button == 0) {
427 | ModifySelected();
428 | }
429 | });
430 |
431 | gLockStrs[PREF_IS_DEFAULT_VALUE] = 'default';
432 | gLockStrs[PREF_IS_MODIFIED] = 'modified';
433 | gLockStrs[PREF_IS_LOCKED] = 'locked';
434 | gTypeStrs[nsIPrefBranch.PREF_STRING] = 'string';
435 | gTypeStrs[nsIPrefBranch.PREF_INT] = 'integer';
436 | gTypeStrs[nsIPrefBranch.PREF_BOOL] = 'boolean';
437 |
438 | var showWarning = gPrefBranch.getBoolPref("general.warnOnAboutConfig");
439 |
440 | if (showWarning) {
441 | document.getElementById("warningButton").focus();
442 | } else {
443 | ShowPrefs();
444 | }
445 | }
446 |
447 | // Unhide the warning message
448 | function ShowPrefs() {
449 | gPrefBranch.getChildList("").forEach(fetchPref);
450 |
451 | var descending = document.getElementsByAttribute(
452 | "sortDirection",
453 | "descending"
454 | );
455 | if (descending.item(0)) {
456 | gSortedColumn = descending[0].id;
457 | gSortDirection = -1;
458 | } else {
459 | var ascending = document.getElementsByAttribute(
460 | "sortDirection",
461 | "ascending"
462 | );
463 | if (ascending.item(0)) {
464 | gSortedColumn = ascending[0].id;
465 | } else {
466 | document
467 | .getElementById(gSortedColumn)
468 | .setAttribute("sortDirection", "ascending");
469 | }
470 | }
471 | gSortFunction = gSortFunctions[gSortedColumn];
472 | gPrefArray.sort(gSortFunction);
473 |
474 | gPrefBranch.addObserver("", gPrefListener);
475 |
476 | var configTree = document.getElementById("configTree");
477 | configTree.view = view;
478 | configTree.controllers.insertControllerAt(0, configController);
479 |
480 | document.getElementById("configDeck").setAttribute("selectedIndex", 1);
481 | document.getElementById("configTreeKeyset").removeAttribute("disabled");
482 | if (!document.getElementById("showWarningNextTime").checked) {
483 | gPrefBranch.setBoolPref("general.warnOnAboutConfig", false);
484 | }
485 |
486 | // Process about:config?filter=
487 | var textbox = document.getElementById("textbox");
488 | // About URIs don't support query params, so do this manually
489 | var loc = document.location.href;
490 | var matches = /[?&]filter\=([^&]+)/i.exec(loc);
491 | if (matches) {
492 | textbox.value = decodeURIComponent(matches[1]);
493 | }
494 |
495 | // Even if we did not set the filter string via the URL query,
496 | // textbox might have been set via some other mechanism
497 | if (textbox.value) {
498 | FilterPrefs();
499 | }
500 | textbox.focus();
501 | }
502 |
503 | function onConfigUnload() {
504 | if (
505 | document.getElementById("configDeck").getAttribute("selectedIndex") == 1
506 | ) {
507 | gPrefBranch.removeObserver("", gPrefListener);
508 | var configTree = document.getElementById("configTree");
509 | configTree.view = null;
510 | configTree.controllers.removeController(configController);
511 | }
512 | }
513 |
514 | function FilterPrefs() {
515 | if (
516 | document.getElementById("configDeck").getAttribute("selectedIndex") != 1
517 | ) {
518 | return;
519 | }
520 |
521 | var substring = document.getElementById("textbox").value;
522 | // Check for "/regex/[i]"
523 | if (substring.charAt(0) == "/") {
524 | var r = substring.match(/^\/(.*)\/(i?)$/);
525 | try {
526 | gFilter = RegExp(r[1], r[2]);
527 | } catch (e) {
528 | return; // Do nothing on incomplete or bad RegExp
529 | }
530 | } else if (substring) {
531 | gFilter = RegExp(
532 | substring
533 | .replace(/([^* \w])/g, "\\$1")
534 | .replace(/^\*+/, "")
535 | .replace(/\*+/g, ".*"),
536 | "i"
537 | );
538 | } else {
539 | gFilter = null;
540 | }
541 |
542 | var prefCol =
543 | view.selection && view.selection.currentIndex < 0
544 | ? null
545 | : gPrefView[view.selection.currentIndex].prefCol;
546 | var oldlen = gPrefView.length;
547 | gPrefView = gPrefArray;
548 | if (gFilter) {
549 | gPrefView = [];
550 | for (var i = 0; i < gPrefArray.length; ++i) {
551 | if (gFilter.test(gPrefArray[i].prefCol + ";" + gPrefArray[i].valueCol)) {
552 | gPrefView.push(gPrefArray[i]);
553 | }
554 | }
555 | }
556 | view.treebox.invalidate();
557 | view.treebox.rowCountChanged(oldlen, gPrefView.length - oldlen);
558 | gotoPref(prefCol);
559 | }
560 |
561 | function prefColSortFunction(x, y) {
562 | if (x.prefCol > y.prefCol) {
563 | return gSortDirection;
564 | }
565 | if (x.prefCol < y.prefCol) {
566 | return -gSortDirection;
567 | }
568 | return 0;
569 | }
570 |
571 | function lockColSortFunction(x, y) {
572 | if (x.lockCol != y.lockCol) {
573 | return gSortDirection * (y.lockCol - x.lockCol);
574 | }
575 | return prefColSortFunction(x, y);
576 | }
577 |
578 | function typeColSortFunction(x, y) {
579 | if (x.typeCol != y.typeCol) {
580 | return gSortDirection * (y.typeCol - x.typeCol);
581 | }
582 | return prefColSortFunction(x, y);
583 | }
584 |
585 | function valueColSortFunction(x, y) {
586 | if (x.valueCol > y.valueCol) {
587 | return gSortDirection;
588 | }
589 | if (x.valueCol < y.valueCol) {
590 | return -gSortDirection;
591 | }
592 | return prefColSortFunction(x, y);
593 | }
594 |
595 | const gSortFunctions = {
596 | prefCol: prefColSortFunction,
597 | lockCol: lockColSortFunction,
598 | typeCol: typeColSortFunction,
599 | valueCol: valueColSortFunction,
600 | };
601 |
602 | const gCategoryLabelForSortColumn = {
603 | prefCol: "SortByName",
604 | lockCol: "SortByStatus",
605 | typeCol: "SortByType",
606 | valueCol: "SortByValue",
607 | };
608 |
609 | const configController = {
610 | supportsCommand: function supportsCommand(command) {
611 | return command == "cmd_copy";
612 | },
613 | isCommandEnabled: function isCommandEnabled(command) {
614 | return view.selection && view.selection.currentIndex >= 0;
615 | },
616 | doCommand: function doCommand(command) {
617 | copyPref();
618 | },
619 | onEvent: function onEvent(event) {},
620 | };
621 |
622 | function updateContextMenu() {
623 | var lockCol = PREF_IS_LOCKED;
624 | var typeCol = nsIPrefBranch.PREF_STRING;
625 | var valueCol = "";
626 | var copyDisabled = true;
627 | var prefSelected = view.selection.currentIndex >= 0;
628 |
629 | if (prefSelected) {
630 | var prefRow = gPrefView[view.selection.currentIndex];
631 | lockCol = prefRow.lockCol;
632 | typeCol = prefRow.typeCol;
633 | valueCol = prefRow.valueCol;
634 | copyDisabled = false;
635 | }
636 |
637 | var copyPref = document.getElementById("copyPref");
638 | copyPref.setAttribute("disabled", copyDisabled);
639 |
640 | var copyName = document.getElementById("copyName");
641 | copyName.setAttribute("disabled", copyDisabled);
642 |
643 | var copyValue = document.getElementById("copyValue");
644 | copyValue.setAttribute("disabled", copyDisabled);
645 |
646 | var resetSelected = document.getElementById("resetSelected");
647 | resetSelected.setAttribute("disabled", lockCol != PREF_IS_MODIFIED);
648 |
649 | var canToggle = typeCol == nsIPrefBranch.PREF_BOOL && valueCol != "";
650 | // indicates that a pref is locked or no pref is selected at all
651 | var isLocked = lockCol == PREF_IS_LOCKED;
652 |
653 | var modifySelected = document.getElementById("modifySelected");
654 | modifySelected.setAttribute("disabled", isLocked);
655 | modifySelected.hidden = canToggle;
656 |
657 | var toggleSelected = document.getElementById("toggleSelected");
658 | toggleSelected.setAttribute("disabled", isLocked);
659 | toggleSelected.hidden = !canToggle;
660 | }
661 |
662 | function copyPref() {
663 | var pref = gPrefView[view.selection.currentIndex];
664 | gClipboardHelper.copyString(pref.prefCol + ";" + pref.valueCol);
665 | }
666 |
667 | function copyName() {
668 | gClipboardHelper.copyString(gPrefView[view.selection.currentIndex].prefCol);
669 | }
670 |
671 | function copyValue() {
672 | gClipboardHelper.copyString(gPrefView[view.selection.currentIndex].valueCol);
673 | }
674 |
675 | function ModifySelected() {
676 | if (view.selection.currentIndex >= 0) {
677 | ModifyPref(gPrefView[view.selection.currentIndex]);
678 | }
679 | }
680 |
681 | function ResetSelected() {
682 | var entry = gPrefView[view.selection.currentIndex];
683 | gPrefBranch.clearUserPref(entry.prefCol);
684 | }
685 |
686 | async function NewPref(type) {
687 | var result = { value: "" };
688 | var dummy = { value: 0 };
689 |
690 | let [newTitle, newPrompt] = [`New ${gTypeStrs[type]} value`, 'Enter the preference name'];
691 |
692 | if (
693 | Services.prompt.prompt(window, newTitle, newPrompt, result, null, dummy)
694 | ) {
695 | result.value = result.value.trim();
696 | if (!result.value) {
697 | return;
698 | }
699 |
700 | var pref;
701 | if (result.value in gPrefHash) {
702 | pref = gPrefHash[result.value];
703 | } else {
704 | pref = {
705 | prefCol: result.value,
706 | lockCol: PREF_IS_DEFAULT_VALUE,
707 | typeCol: type,
708 | valueCol: "",
709 | };
710 | }
711 | if (ModifyPref(pref)) {
712 | setTimeout(gotoPref, 0, result.value);
713 | }
714 | }
715 | }
716 |
717 | function gotoPref(pref) {
718 | // make sure the pref exists and is displayed in the current view
719 | var index = pref in gPrefHash ? getViewIndexOfPref(gPrefHash[pref]) : -1;
720 | if (index >= 0) {
721 | view.selection.select(index);
722 | view.treebox.ensureRowIsVisible(index);
723 | } else {
724 | view.selection.clearSelection();
725 | view.selection.currentIndex = -1;
726 | }
727 | }
728 |
729 | async function ModifyPref(entry) {
730 | if (entry.lockCol == PREF_IS_LOCKED) {
731 | return false;
732 | }
733 |
734 | let [title] = [`Enter ${gTypeStrs[entry.typeCol]} value`];
735 |
736 | if (entry.typeCol == nsIPrefBranch.PREF_BOOL) {
737 | var check = { value: entry.valueCol == "false" };
738 | if (
739 | !entry.valueCol &&
740 | !Services.prompt.select(
741 | window,
742 | title,
743 | entry.prefCol,
744 | [false, true],
745 | check
746 | )
747 | ) {
748 | return false;
749 | }
750 | gPrefBranch.setBoolPref(entry.prefCol, check.value);
751 | } else {
752 | var result = { value: entry.valueCol };
753 | var dummy = { value: 0 };
754 | if (
755 | !Services.prompt.prompt(window, title, entry.prefCol, result, null, dummy)
756 | ) {
757 | return false;
758 | }
759 | if (entry.typeCol == nsIPrefBranch.PREF_INT) {
760 | // | 0 converts to integer or 0; - 0 to float or NaN.
761 | // Thus, this check should catch all cases.
762 | var val = result.value | 0;
763 | if (val != result.value - 0) {
764 | const [err_title, err_text] = ['Invalid value', 'The text you entered is not a number.'];
765 |
766 | Services.prompt.alert(window, err_title, err_text);
767 | return false;
768 | }
769 | gPrefBranch.setIntPref(entry.prefCol, val);
770 | } else {
771 | gPrefBranch.setStringPref(entry.prefCol, result.value);
772 | }
773 | }
774 |
775 | Services.prefs.savePrefFile(null);
776 | return true;
777 | }
778 |
779 | window.onload = onConfigLoad;
780 | window.onunload = onConfigUnload;
--------------------------------------------------------------------------------
/profile/chrome/utils/boot.jsm:
--------------------------------------------------------------------------------
1 | let EXPORTED_SYMBOLS = [];
2 |
3 | console.warn( "Browser is executing custom scripts via autoconfig" );
4 | const {Services} = ChromeUtils.import('resource://gre/modules/Services.jsm');
5 | const {AppConstants} = ChromeUtils.import('resource://gre/modules/AppConstants.jsm');
6 |
7 | const yPref = {
8 | get: function (prefPath) {
9 | const sPrefs = Services.prefs;
10 | try {
11 | switch (sPrefs.getPrefType(prefPath)) {
12 | case 0:
13 | return undefined;
14 | case 32:
15 | return sPrefs.getStringPref(prefPath);
16 | case 64:
17 | return sPrefs.getIntPref(prefPath);
18 | case 128:
19 | return sPrefs.getBoolPref(prefPath);
20 | }
21 | } catch (ex) {
22 | return undefined;
23 | }
24 | return;
25 | },
26 | set: function (prefPath, value) {
27 | const sPrefs = Services.prefs;
28 | switch (typeof value) {
29 | case 'string':
30 | return sPrefs.setCharPref(prefPath, value) || value;
31 | case 'number':
32 | return sPrefs.setIntPref(prefPath, value) || value;
33 | case 'boolean':
34 | return sPrefs.setBoolPref(prefPath, value) || value;
35 | }
36 | return;
37 | },
38 | addListener:(a,b) => {
39 | let o = (q,w,e)=>(b(yPref.get(e),e));
40 | Services.prefs.addObserver(a,o);
41 | return{pref:a,observer:o}
42 | },
43 | removeListener:(a)=>( Services.prefs.removeObserver(a.pref,a.observer) )
44 | };
45 |
46 | function resolveChromeURL(str){
47 | const registry = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry);
48 | try{
49 | return registry.convertChromeURL(Services.io.newURI(str.replace(/\\/g,"/"))).spec
50 | }catch(e){
51 | console.error(e);
52 | return ""
53 | }
54 | }
55 |
56 | // relative to "chrome" folder
57 | function resolveChromePath(str){
58 | let parts = resolveChromeURL(str).split("/");
59 | return parts.slice(parts.indexOf("chrome") + 1,parts.length - 1).join("/");
60 | }
61 |
62 | const SHARED_GLOBAL = {};
63 | Object.defineProperty(SHARED_GLOBAL,"widgetCallbacks",{value:new Map()});
64 |
65 | const APP_VARIANT = (() => {
66 | let is_tb = AppConstants.BROWSER_CHROME_URL.startsWith("chrome://messenger");
67 | return {
68 | THUNDERBIRD: is_tb,
69 | FIREFOX: !is_tb
70 | }
71 | })();
72 |
73 | const BROWSERCHROME = (() => {
74 | if(APP_VARIANT.FIREFOX){
75 | return AppConstants.BROWSER_CHROME_URL
76 | }
77 | return "chrome://messenger/content/messenger.xhtml"
78 | })();
79 |
80 | const PREF_ENABLED = 'userChromeJS.enabled';
81 | const PREF_SCRIPTSDISABLED = 'userChromeJS.scriptsDisabled';
82 | const PREF_GBROWSERHACKENABLED = 'userChromeJS.gBrowser_hack.enabled';
83 | const SCRIPT_DIR = resolveChromePath('chrome://userscripts/content/');
84 | const RESOURCE_DIR = resolveChromePath('chrome://userchrome/content/');
85 | const BASE_FILEURI = Services.io.getProtocolHandler('file')
86 | .QueryInterface(Ci.nsIFileProtocolHandler)
87 | .getURLSpecFromDir(Services.dirsvc.get('UChrm',Ci.nsIFile));
88 |
89 | class ScriptData {
90 | constructor(leafName, headerText){
91 | this.filename = leafName;
92 | this.name = headerText.match(/\/\/ @name\s+(.+)\s*$/im)?.[1];
93 | this.charset = headerText.match(/\/\/ @charset\s+(.+)\s*$/im)?.[1];
94 | this.description = headerText.match(/\/\/ @description\s+(.+)\s*$/im)?.[1];
95 | this.version = headerText.match(/\/\/ @version\s+(.+)\s*$/im)?.[1];
96 | this.author = headerText.match(/\/\/ @author\s+(.+)\s*$/im)?.[1];
97 | this.homepageURL = headerText.match(/\/\/ @homepageURL\s+(.+)\s*$/im)?.[1];
98 | this.downloadURL = headerText.match(/\/\/ @downloadURL\s+(.+)\s*$/im)?.[1];
99 | this.updateURL = headerText.match(/\/\/ @updateURL\s+(.+)\s*$/im)?.[1];
100 | this.optionsURL = headerText.match(/\/\/ @optionsURL\s+(.+)\s*$/im)?.[1];
101 | this.startup = headerText.match(/\/\/ @startup\s+(.+)\s*$/im)?.[1];
102 | this.id = headerText.match(/\/\/ @id\s+(.+)\s*$/im)?.[1]
103 | || `${leafName.split('.uc.js')[0]}@${this.author||'userChromeJS'}`;
104 | this.isESM = this.filename.endsWith("sys.mjs");
105 | this.onlyonce = /\/\/ @onlyonce\b/.test(headerText);
106 | this.inbackground = this.isESM || /\/\/ @backgroundmodule\b/.test(headerText);
107 | this.ignoreCache = /\/\/ @ignorecache\b/.test(headerText);
108 | this.isRunning = false;
109 |
110 | // Construct regular expression to use to match target document
111 | let match, rex = {
112 | include: [],
113 | exclude: []
114 | };
115 | let findNextRe = /^\/\/ @(include|exclude)\s+(.+)\s*$/gm;
116 | while (match = findNextRe.exec(headerText)) {
117 | rex[match[1]].push(
118 | match[2].replace(/^main$/i, BROWSERCHROME).replace(/\*/g, '.*?')
119 | );
120 | }
121 | if (!rex.include.length) {
122 | rex.include.push(BROWSERCHROME);
123 | }
124 | let exclude = rex.exclude.length ? `(?!${rex.exclude.join('$|')}$)` : '';
125 | this.regex = new RegExp(`^${exclude}(${rex.include.join('|') || '.*'})$`,'i');
126 |
127 | if(this.inbackground){
128 | this.loadOrder = -1;
129 | }else{
130 | let loadOrder = headerText.match(/\/\/ @loadOrder\s+(\d+)\s*$/im)?.[1];
131 | this.loadOrder = Number.parseInt(loadOrder) || 10;
132 | }
133 |
134 | Object.seal(this);
135 | }
136 | get isEnabled() {
137 | return (yPref.get(PREF_SCRIPTSDISABLED) || '')
138 | .split(',').indexOf(this.filename) === -1;
139 | }
140 |
141 | tryLoadIntoWindow(win) {
142 | if (this.inbackground || !this.regex.test(win.location.href)) {
143 | return
144 | }
145 | try {
146 | if(this.onlyonce && this.isRunning) {
147 | if (this.startup) {
148 | SHARED_GLOBAL[this.startup]._startup(win)
149 | }
150 | return
151 | }
152 |
153 | Services.scriptloader.loadSubScriptWithOptions(
154 | `chrome://userscripts/content/${this.filename}`,
155 | {
156 | target: win,
157 | ignoreCache: this.ignoreCache
158 | }
159 | );
160 |
161 | this.isRunning = true;
162 | this.startup && SHARED_GLOBAL[this.startup]._startup(win)
163 |
164 | } catch (ex) {
165 | console.error(new Error(`@ ${this.filename}`,{cause:ex}));
166 | }
167 | return
168 | }
169 |
170 | getInfo(){
171 | let info = {...this};
172 | info.regex = new RegExp(this.regex.source,this.regex.flags);
173 | return info;
174 | }
175 |
176 | static fromFile(aFile){
177 | const headerText = utils.readFile(aFile,true)
178 | .match(/^\/\/ ==UserScript==\s*\n(?:.*\n)*?\/\/ ==\/UserScript==\s*\n/m);
179 | return new ScriptData(aFile.leafName, headerText ? headerText[0] : '');
180 | }
181 | }
182 |
183 | function getDirEntry(filename,isLoader = false){
184 | if(typeof filename !== "string"){
185 | return null
186 | }
187 | filename = filename.replace("\\","/");
188 | let pathParts = ((filename.startsWith("..") ? "" : (isLoader ? SCRIPT_DIR : RESOURCE_DIR)) + "/" + filename)
189 | .split("/").filter( (a) => (!!a && a != "..") );
190 | let entry = Services.dirsvc.get('UChrm',Ci.nsIFile);
191 |
192 | for(let part of pathParts){
193 | entry.append(part)
194 | }
195 | if(!entry.exists()){
196 | return null
197 | }
198 | if(entry.isDirectory()){
199 | return entry.directoryEntries.QueryInterface(Ci.nsISimpleEnumerator);
200 | }else if(entry.isFile()){
201 | return entry
202 | }else{
203 | return null
204 | }
205 | }
206 |
207 | async function getProfileDir(){
208 | if(APP_VARIANT.FIREFOX){
209 | return PathUtils.profileDir
210 | }
211 | // APP_VARIANT = THUNDERBIRD
212 | return await PathUtils.getProfileDir()
213 | }
214 |
215 | function updateStyleSheet(name,type) {
216 | if(type){
217 | let sss = Cc['@mozilla.org/content/style-sheet-service;1'].getService(Ci.nsIStyleSheetService);
218 | try{
219 | let uri = Services.io.newURI(`chrome://userchrome/content/${name}`);
220 | switch(type){
221 | case "agent":
222 | sss.unregisterSheet(uri,sss.AGENT_SHEET);
223 | sss.loadAndRegisterSheet(uri,sss.AGENT_SHEET);
224 | return true
225 | case "author":
226 | sss.unregisterSheet(uri,sss.AUTHOR_SHEET);
227 | sss.loadAndRegisterSheet(uri,sss.AUTHOR_SHEET);
228 | return true
229 | default:
230 | return false
231 | }
232 | }catch(e){
233 | console.error(e);
234 | return false
235 | }
236 | }
237 | let entry = getDirEntry(name);
238 | if(!(entry && entry.isFile())){
239 | return false
240 | }
241 | let recentWindow = Services.wm.getMostRecentBrowserWindow();
242 | if(!recentWindow){
243 | return false
244 | }
245 | function recurseImports(sheet,all){
246 | let z = 0;
247 | let rule = sheet.cssRules[0];
248 | // loop through import rules and check that the "all"
249 | // doesn't already contain the same object
250 | while(rule instanceof CSSImportRule && !all.includes(rule.styleSheet) ){
251 | all.push(rule.styleSheet);
252 | recurseImports(rule.styleSheet,all);
253 | rule = sheet.cssRules[++z];
254 | }
255 | return all
256 | }
257 |
258 | let sheets = recentWindow.InspectorUtils.getAllStyleSheets(recentWindow.document,false);
259 |
260 | sheets = sheets.flatMap( x => recurseImports(x,[x]) );
261 |
262 | // If a sheet is imported multiple times, then there will be
263 | // duplicates, because style system does create an object for
264 | // each instace but that's OK since sheets.find below will
265 | // only find the first instance and reload that which is
266 | // "probably" fine.
267 | let entryFilePath = `file:///${entry.path.replaceAll("\\","/")}`;
268 |
269 | let target = sheets.find(sheet => sheet.href === entryFilePath);
270 | if(target){
271 | recentWindow.InspectorUtils.parseStyleSheet(target,utils.readFile(entry));
272 | return true
273 | }
274 | return false
275 | }
276 |
277 | const utils = {
278 |
279 | get sharedGlobal(){ return SHARED_GLOBAL },
280 |
281 | get brandName(){ return AppConstants.MOZ_APP_DISPLAYNAME_DO_NOT_USE },
282 |
283 | createElement: function(doc,tag,props,isHTML = false){
284 | let el = isHTML ? doc.createElement(tag) : doc.createXULElement(tag);
285 | for(let prop in props){
286 | el.setAttribute(prop,props[prop])
287 | }
288 | return el
289 | },
290 |
291 | createWidget(desc){
292 | if(!desc || !desc.id ){
293 | console.error("custom widget description is missing 'id' property");
294 | return null
295 | }
296 | if(!(['toolbaritem','toolbarbutton']).includes(desc.type)){
297 | console.error("custom widget has unsupported type: "+desc.type);
298 | return null
299 | }
300 | const CUI = Services.wm.getMostRecentBrowserWindow().CustomizableUI;
301 | let newWidget = CUI.getWidget(desc.id);
302 |
303 | if(newWidget && newWidget.hasOwnProperty("source")){
304 | // very likely means that the widget with this id already exists
305 | // There isn't a very reliable way to 'really' check if it exists or not
306 | return newWidget
307 | }
308 | // This is pretty ugly but makes onBuild much cleaner.
309 | let itemStyle = "";
310 | if(desc.image){
311 | if(desc.type==="toolbarbutton"){
312 | itemStyle += "list-style-image:";
313 | }else{
314 | itemStyle += "background: transparent center no-repeat ";
315 | }
316 | itemStyle += `url(chrome://userChrome/content/${desc.image});`;
317 | itemStyle += desc.style || "";
318 | }
319 | SHARED_GLOBAL.widgetCallbacks.set(desc.id,desc.callback);
320 |
321 | return CUI.createWidget({
322 | id: desc.id,
323 | type: 'custom',
324 | onBuild: function(aDocument) {
325 | let toolbaritem = aDocument.createXULElement(desc.type);
326 | let props = {
327 | id: desc.id,
328 | class: `toolbarbutton-1 chromeclass-toolbar-additional ${desc.class?desc.class:""}`,
329 | overflows: !!desc.overflows,
330 | label: desc.label || desc.id,
331 | tooltiptext: desc.tooltip || desc.id,
332 | style: itemStyle,
333 | onclick: `${desc.allEvents?"":"event.button===0 && "}_ucUtils.sharedGlobal.widgetCallbacks.get(this.id)(event,window)`
334 | };
335 | for (let p in props){
336 | toolbaritem.setAttribute(p, props[p]);
337 | }
338 | return toolbaritem;
339 | }
340 | });
341 | },
342 |
343 | readFile: function (aFile, metaOnly = false) {
344 | if(typeof aFile === "string"){
345 | aFile = getDirEntry(aFile);
346 | }
347 | if(!aFile){
348 | return null
349 | }
350 | let stream = Cc['@mozilla.org/network/file-input-stream;1'].createInstance(Ci.nsIFileInputStream);
351 | let cvstream = Cc['@mozilla.org/intl/converter-input-stream;1'].createInstance(Ci.nsIConverterInputStream);
352 | try{
353 | stream.init(aFile, 0x01, 0, 0);
354 | cvstream.init(stream, 'UTF-8', 1024, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
355 | }catch(e){
356 | console.error(e);
357 | return null
358 | }
359 | let content = '',
360 | data = {};
361 | while (cvstream.readString(4096, data)) {
362 | content += data.value;
363 | if (metaOnly && content.indexOf('// ==/UserScript==') > 0) {
364 | break;
365 | }
366 | }
367 | cvstream.close();
368 | stream.close();
369 | return content.replace(/\r\n?/g, '\n');
370 | },
371 |
372 | readFileAsync: function(path){
373 | if(typeof path !== "string"){
374 | return Promise.reject("readFileAsync: path is not a string")
375 | }
376 | let base = ["chrome",RESOURCE_DIR];
377 | let parts = path.split(/[\\\/]/);
378 | while(parts[0] === ".."){
379 | base.pop();
380 | parts.shift();
381 | }
382 |
383 | return new Promise(resolve => {
384 | getProfileDir()
385 | .then((path) => PathUtils.join(path, ...base.concat(parts)))
386 | .then(IOUtils.readUTF8)
387 | .then(resolve)
388 | })
389 |
390 | },
391 |
392 | readJSON: async function(path){
393 | try{
394 | let content = await utils.readFileAsync(path);
395 | return JSON.parse(content);
396 | }catch(ex){
397 | console.error(ex)
398 | }
399 | return null
400 | },
401 |
402 | writeFile: async function(path, content, options = {}){
403 | if(!path || typeof path !== "string"){
404 | throw "writeFile: path is invalid"
405 | }
406 | if(typeof content !== "string"){
407 | throw "writeFile: content to write must be a string"
408 | }
409 |
410 | let base = ["chrome",RESOURCE_DIR];
411 | let parts = path.split(/[\\\/]/);
412 |
413 | // Normally, this API can only write into resources directory
414 | // Writing outside of resources can be enabled using following pref
415 | const disallowUnsafeWrites = !yPref.get("userChromeJS.allowUnsafeWrites");
416 |
417 | while(parts[0] === ".."){
418 | if(disallowUnsafeWrites){
419 | throw "Writing outside of resources directory is not allowed"
420 | }
421 | base.pop();
422 | parts.shift();
423 | }
424 | const fileName = PathUtils.join( await getProfileDir(), ...base.concat(parts) );
425 |
426 | if(!options.tmpPath){
427 | options.tmpPath = fileName + ".tmp";
428 | }
429 | return IOUtils.writeUTF8( fileName, content, options );
430 | },
431 |
432 | createFileURI: (fileName = "") => {
433 | fileName = String(fileName);
434 | let u = resolveChromeURL(`chrome://userchrome/content/${fileName}`);
435 | return fileName ? u : u.substr(0,u.lastIndexOf("/") + 1);
436 | },
437 |
438 | get chromeDir(){
439 | return {
440 | get files(){
441 | const dir = Services.dirsvc.get('UChrm',Ci.nsIFile);
442 | return dir.directoryEntries.QueryInterface(Ci.nsISimpleEnumerator)
443 | },
444 | uri: BASE_FILEURI
445 | }
446 | },
447 |
448 | getFSEntry: (fileName) => ( getDirEntry(fileName) ),
449 |
450 | getScriptData: () => {
451 | let scripts = [];
452 | const disabledScripts = (yPref.get(PREF_SCRIPTSDISABLED) || '').split(",");
453 | for(let script of _ucjs.scripts){
454 | let scriptInfo = script.getInfo();
455 | scriptInfo.isEnabled = !disabledScripts.includes(script.filename);
456 | scripts.push(scriptInfo);
457 | }
458 | return scripts
459 | },
460 |
461 | get windows(){
462 | return {
463 | get: function (onlyBrowsers = true) {
464 | let windowType = APP_VARIANT.FIREFOX ? "navigator:browser" : "mail:3pane";
465 | let windows = Services.wm.getEnumerator(onlyBrowsers ? windowType : null);
466 | let wins = [];
467 | while (windows.hasMoreElements()) {
468 | wins.push(windows.getNext());
469 | }
470 | return wins
471 | },
472 | forEach: function(fun,onlyBrowsers = true){
473 | let wins = this.get(onlyBrowsers);
474 | wins.forEach((w)=>(fun(w.document,w)))
475 | }
476 | }
477 | },
478 |
479 | toggleScript: function(el){
480 | let isElement = !!el.tagName;
481 | if(!isElement && typeof el != "string"){
482 | return
483 | }
484 | const name = isElement ? el.getAttribute("filename") : el;
485 | let script = _ucjs.scripts.find(script => script.filename === name);
486 | if(!script){
487 | return null
488 | }
489 | let newstate = true;
490 | if (script.isEnabled) {
491 | yPref.set(PREF_SCRIPTSDISABLED, `${script.filename},${yPref.get(PREF_SCRIPTSDISABLED)}`);
492 | newstate = false;
493 | } else {
494 | yPref.set(PREF_SCRIPTSDISABLED, yPref.get(PREF_SCRIPTSDISABLED).replace(new RegExp(`^${script.filename},?|,${script.filename}`), ''));
495 | }
496 | Services.appinfo.invalidateCachesOnRestart();
497 | return { script: name, enabled: newstate }
498 | },
499 |
500 | updateStyleSheet: function(name = "../userChrome.css",type){
501 | return updateStyleSheet(name,type)
502 | },
503 |
504 | updateMenuStatus: function(menu){
505 | if(!menu){
506 | return
507 | }
508 | let disabledScripts = yPref.get(PREF_SCRIPTSDISABLED).split(",");
509 | for(let item of menu.children){
510 | if (disabledScripts.includes(item.getAttribute("filename"))){
511 | item.removeAttribute("checked");
512 | }else{
513 | item.setAttribute("checked","true");
514 | }
515 | }
516 | },
517 |
518 | startupFinished: function(){
519 | return new Promise(resolve => {
520 | if(_ucjs.SESSION_RESTORED){
521 | resolve();
522 | }else{
523 | const obs_topic = APP_VARIANT.FIREFOX
524 | ? "sessionstore-windows-restored"
525 | : "mail-delayed-startup-finished";
526 |
527 | let observer = (subject, topic, data) => {
528 | Services.obs.removeObserver(observer, obs_topic);
529 | resolve();
530 | };
531 | Services.obs.addObserver(observer, obs_topic);
532 | }
533 | });
534 | },
535 |
536 | windowIsReady: function(win){
537 | if(win && win.isChromeWindow){
538 | return new Promise(resolve => {
539 |
540 | if(APP_VARIANT.FIREFOX){
541 | if(win.gBrowserInit.delayedStartupFinished){
542 | resolve();
543 | return
544 | }
545 | }else{ // APP_VARIANT = THUNDERBIRD
546 | if(win.gMailInit.delayedStartupFinished){
547 | resolve();
548 | return
549 | }
550 | }
551 | const obs_topic = APP_VARIANT.FIREFOX
552 | ? "browser-delayed-startup-finished"
553 | : "mail-delayed-startup-finished";
554 |
555 | let observer = (subject, topic, data) => {
556 | if(subject === win){
557 | Services.obs.removeObserver(observer, obs_topic);
558 | resolve();
559 | }
560 | };
561 | Services.obs.addObserver(observer, obs_topic);
562 |
563 | });
564 | }else{
565 | return Promise.reject(new Error("reference is not a window"))
566 | }
567 | },
568 |
569 | registerHotkey: function(desc,func){
570 | const validMods = ["accel","alt","ctrl","meta","shift"];
571 | const validKey = (k)=>((/^[\w-]$/).test(k) ? 1 : (/^F(?:1[0,2]|[1-9])$/).test(k) ? 2 : 0);
572 | const NOK = (a) => (typeof a != "string");
573 | const eToO = (e) => ({"metaKey":e.metaKey,"ctrlKey":e.ctrlKey,"altKey":e.altKey,"shiftKey":e.shiftKey,"key":e.srcElement.getAttribute("key"),"id":e.srcElement.getAttribute("id")});
574 |
575 | if(NOK(desc.id) || NOK(desc.key) || NOK(desc.modifiers)){
576 | return false
577 | }
578 |
579 | try{
580 | let mods = desc.modifiers.toLowerCase().split(" ").filter((a)=>(validMods.includes(a)));
581 | let key = validKey(desc.key);
582 | if(!key || (mods.length === 0 && key === 1)){
583 | return false
584 | }
585 |
586 | utils.windows.forEach((doc,win) => {
587 | if(doc.getElementById(desc.id)){
588 | return
589 | }
590 | let details = { "id": desc.id, "modifiers": mods.join(",").replace("ctrl","accel"), "oncommand": "//" };
591 | if(key === 1){
592 | details.key = desc.key.toUpperCase();
593 | }else{
594 | details.keycode = `VK_${desc.key}`;
595 | }
596 |
597 | let el = utils.createElement(doc,"key",details);
598 |
599 | el.addEventListener("command",(ev) => {func(ev.target.ownerGlobal,eToO(ev))});
600 | let keyset = doc.getElementById("mainKeyset") || doc.body.appendChild(utils.createElement(doc,"keyset",{id:"ucKeys"}));
601 | keyset.insertBefore(el,keyset.firstChild);
602 | });
603 | }catch(e){
604 | console.error(e);
605 | return false
606 | }
607 | return true
608 | },
609 | loadURI: function(win,desc){
610 | if(APP_VARIANT.THUNDERBIRD){
611 | console.warn("_ucUtils.loadURI is not supported on Thunderbird");
612 | return false
613 | }
614 | if( !win
615 | || !desc
616 | || !desc.url
617 | || typeof desc.url !== "string"
618 | || !(["tab","tabshifted","window","current"]).includes(desc.where)
619 | ){
620 | return false
621 | }
622 | const isJsURI = desc.url.slice(0,11) === "javascript:";
623 | try{
624 | win.openTrustedLinkIn(
625 | desc.url,
626 | desc.where,
627 | { "allowPopups":isJsURI,
628 | "inBackground":false,
629 | "allowInheritPrincipal":false,
630 | "private":!!desc.private,
631 | "userContextId":desc.url.startsWith("http")?desc.userContextId:null});
632 | }catch(e){
633 | console.error(e);
634 | return false
635 | }
636 | return true
637 | },
638 | get prefs(){ return yPref },
639 |
640 | showNotification: async function(description){
641 | if(APP_VARIANT.THUNDERBIRD){
642 | console.warn('_ucUtils.showNotification is not supported on Thunderbird\nNotification label was: "'+description.label+'"');
643 | return
644 | }
645 | await utils.startupFinished();
646 | let window = description.window;
647 | if(!(window && window.isChromeWindow)){
648 | window = Services.wm.getMostRecentBrowserWindow();
649 | }
650 | let aNotificationBox = window.gNotificationBox;
651 | if(description.tab){
652 | let aBrowser = description.tab.linkedBrowser;
653 | if(!aBrowser){ return }
654 | aNotificationBox = window.gBrowser.getNotificationBox(aBrowser);
655 | }
656 | if(!aNotificationBox){ return }
657 | let type = description.type || "default";
658 | let priority = aNotificationBox.PRIORITY_INFO_HIGH;
659 | switch (description.priority){
660 | case "system":
661 | priority = aNotificationBox.PRIORITY_SYSTEM;
662 | break;
663 | case "critical":
664 | priority = aNotificationBox.PRIORITY_CRITICAL_HIGH;
665 | break;
666 | case "warning":
667 | priority = aNotificationBox.PRIORITY_WARNING_HIGH;
668 | break;
669 | }
670 | aNotificationBox.appendNotification(
671 | type,
672 | {
673 | label: description.label || "ucUtils message",
674 | image: "chrome://browser/skin/notification-icons/popup.svg",
675 | priority: priority,
676 | eventCallback: typeof description.callback === "function" ? description.callback : null
677 | },
678 | description.buttons
679 | );
680 | },
681 |
682 | restart: function (clearCache){
683 | clearCache && Services.appinfo.invalidateCachesOnRestart();
684 | let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
685 | Services.obs.notifyObservers(
686 | cancelQuit,
687 | "quit-application-requested",
688 | "restart"
689 | );
690 | if (!cancelQuit.data) {
691 | Services.startup.quit(
692 | Services.startup.eAttemptQuit | Services.startup.eRestart
693 | );
694 | return true
695 | }
696 | return false
697 | }
698 | }
699 |
700 | Object.freeze(utils);
701 |
702 | if (yPref.get(PREF_ENABLED) === undefined) {
703 | yPref.set(PREF_ENABLED, true);
704 | }
705 |
706 | if (yPref.get(PREF_SCRIPTSDISABLED) === undefined) {
707 | yPref.set(PREF_SCRIPTSDISABLED, '');
708 | }
709 |
710 | if (yPref.get(PREF_GBROWSERHACKENABLED) === undefined) {
711 | yPref.set(PREF_GBROWSERHACKENABLED, false);
712 | }
713 |
714 | function showgBrowserNotification(){
715 | yPref.set(PREF_GBROWSERHACKENABLED,true);
716 | utils.showNotification(
717 | {
718 | label : "fx-autoconfig: Something was broken in last startup",
719 | type : "fx-autoconfig-gbrowser-notification",
720 | priority: "critical",
721 | buttons: [{
722 | label: "Why am I seeing this?",
723 | callback: (notification) => {
724 | notification.ownerGlobal.openWebLinkIn(
725 | "https://github.com/MrOtherGuy/fx-autoconfig#startup-error",
726 | "tab"
727 | );
728 | return false
729 | }
730 | }]
731 | }
732 | )
733 | }
734 |
735 | function showBrokenNotification(window){
736 | let aNotificationBox = window.gNotificationBox;
737 | aNotificationBox.appendNotification(
738 | "fx-autoconfig-broken-notification",
739 | {
740 | label: "fx-autoconfig: Startup is broken",
741 | image: "chrome://browser/skin/notification-icons/popup.svg",
742 | priority: "critical"
743 | },
744 | [{
745 | label: "Enable workaround",
746 | callback: (notification) => {
747 | yPref.set("userChromeJS.gBrowser_hack.required",true);
748 | utils.restart(false);
749 | return false
750 | }
751 | }]
752 | );
753 | }
754 |
755 | function escapeXUL(markup) {
756 | return markup.replace(/[<>&'"]/g, (char) => {
757 | switch (char) {
758 | case `<`:
759 | return "<";
760 | case `>`:
761 | return ">";
762 | case `&`:
763 | return "&";
764 | case `'`:
765 | return "'";
766 | case '"':
767 | return """;
768 | }
769 | });
770 | }
771 |
772 | function UserChrome_js() {
773 | this.scripts = [];
774 | this.SESSION_RESTORED = false;
775 | this.isInitialWindow = true;
776 |
777 | const gBrowserHackRequired = yPref.get("userChromeJS.gBrowser_hack.required") ? 2 : 0;
778 | const gBrowserHackEnabled = yPref.get(PREF_GBROWSERHACKENABLED) ? 1 : 0;
779 | this.GBROWSERHACK_ENABLED = gBrowserHackRequired|gBrowserHackEnabled;
780 |
781 | if(!yPref.get(PREF_ENABLED) || !(/^[\w_]*$/.test(SCRIPT_DIR))){
782 | console.log("Scripts are disabled or the given script directory name is invalid");
783 | return
784 | }
785 | let files = getDirEntry('',true);
786 | while(files.hasMoreElements()){
787 | let file = files.getNext().QueryInterface(Ci.nsIFile);
788 | if (/(.+\.uc\.js)|(.+\.sys\.mjs)$/i.test(file.leafName)) {
789 | let script = ScriptData.fromFile(file);
790 | this.scripts.push(script);
791 | if(script.inbackground && script.isEnabled){
792 | try{
793 | const fileName = `chrome://userscripts/content/${script.filename}`;
794 | if(script.isESM){
795 | ChromeUtils.importESModule( fileName );
796 | }else{
797 | ChromeUtils.import( fileName );
798 | }
799 | script.isRunning = true;
800 | }catch(ex){
801 | console.error(new Error(`@ ${script.filename}`,{cause:ex}));
802 | }
803 | }
804 | }
805 | }
806 | this.scripts.sort((a,b) => a.loadOrder > b.loadOrder);
807 |
808 | Services.obs.addObserver(this, 'domwindowopened', false);
809 | }
810 |
811 | UserChrome_js.prototype = {
812 | observe: function (aSubject, aTopic, aData) {
813 | aSubject.addEventListener('DOMContentLoaded', this, true);
814 | },
815 |
816 | handleEvent: function (aEvent) {
817 | let document = aEvent.originalTarget;
818 | let window = document.defaultView;
819 | let regex = /^chrome:(?!\/\/global\/content\/(commonDialog|alerts\/alert)\.xhtml)|about:(?!blank)/i;
820 | // Don't inject scripts to modal prompt windows or notifications
821 | if(regex.test(window.location.href)) {
822 | Object.defineProperty(window,"_ucUtils",{ get: () => utils });
823 | document.allowUnsafeHTML = false; // https://bugzilla.mozilla.org/show_bug.cgi?id=1432966
824 |
825 | // This is a hack to make gBrowser available for scripts.
826 | // Without it, scripts would need to check if gBrowser exists and deal
827 | // with it somehow. See bug 1443849
828 | const _gb = APP_VARIANT.FIREFOX && "_gBrowser" in window;
829 | if(this.GBROWSERHACK_ENABLED && _gb){
830 | window.gBrowser = window._gBrowser;
831 | }else if(_gb && this.isInitialWindow){
832 | this.isInitialWindow = false;
833 | let timeout = window.setTimeout(() => {
834 | showBrokenNotification(window);
835 | },5000);
836 | utils.windowIsReady(window)
837 | .then(() => {
838 | // startup is fine, clear timeout
839 | window.clearTimeout(timeout);
840 | })
841 | }
842 | let isWindow = window.isChromeWindow;
843 |
844 | // Inject scripts to window
845 | if(yPref.get(PREF_ENABLED)){
846 | const disabledScripts = (yPref.get(PREF_SCRIPTSDISABLED) || '').split(",");
847 | for(let script of this.scripts){
848 | if(script.inbackground){
849 | continue
850 | }
851 | if(!disabledScripts.includes(script.filename)){
852 | script.tryLoadIntoWindow(window)
853 | }
854 | }
855 | }
856 |
857 | // Add simple script menu to menubar tools popup
858 | const menu = document.querySelector(
859 | APP_VARIANT.FIREFOX ? "#menu_openDownloads" : "menuitem#addressBook");
860 | if(isWindow && menu){
861 | window.MozXULElement.insertFTLIfNeeded("toolkit/about/aboutSupport.ftl");
862 | let menuFragment = window.MozXULElement.parseXULToFragment(`
863 |
870 | `);
871 | const itemsFragment = window.MozXULElement.parseXULToFragment("");
872 | for(let script of this.scripts){
873 | itemsFragment.append(
874 | window.MozXULElement.parseXULToFragment(`
875 |
881 | `)
882 | );
883 | }
884 | menuFragment.getElementById("menuUserScriptsPopup").prepend(itemsFragment);
885 | menu.parentNode.insertBefore(menuFragment,menu);
886 |
887 | document.l10n.formatValues(["restart-button-label","clear-startup-cache-label"])
888 | .then(values => {
889 | let baseTitle = `${values[0]} ${utils.brandName}`;
890 | document.getElementById("userScriptsRestart").setAttribute("label", baseTitle);
891 | document.getElementById("userScriptsClearCache").setAttribute("label", values[1].replace("…","") + " & " + baseTitle);
892 | })
893 | }
894 | }
895 | }
896 | }
897 |
898 | const _ucjs = !Services.appinfo.inSafeMode && new UserChrome_js();
899 | _ucjs && utils.startupFinished().then(() => {
900 | _ucjs.SESSION_RESTORED = true;
901 | _ucjs.GBROWSERHACK_ENABLED === 2 && showgBrowserNotification();
902 | if(!yPref.get("userChromeJS.firstRunShown")){
903 | yPref.set("userChromeJS.firstRunShown",true);
904 | utils.showNotification({
905 | type: "fx-autoconfig-installed",
906 | label: `fx-autoconfig: ${utils.brandName} is being modified with custom autoconfig scripting`
907 | });
908 | }
909 | });
910 |
--------------------------------------------------------------------------------
/profile/chrome/utils/chrome.manifest:
--------------------------------------------------------------------------------
1 | content userchromejs ./
2 | content userscripts ../JS/
3 | content userchrome ../resources/
4 |
--------------------------------------------------------------------------------
/profile/chrome/utils/passwordmgr/passwordManager.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | /** * =================== SAVED SIGNONS CODE =================== ***/
6 | /* eslint-disable-next-line no-var */
7 | var { AppConstants } = ChromeUtils.import(
8 | "resource://gre/modules/AppConstants.jsm"
9 | );
10 | /* eslint-disable-next-line no-var */
11 | var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
12 |
13 | ChromeUtils.defineModuleGetter(
14 | this,
15 | "DeferredTask",
16 | "resource://gre/modules/DeferredTask.jsm"
17 | );
18 | ChromeUtils.defineModuleGetter(
19 | this,
20 | "PlacesUtils",
21 | "resource://gre/modules/PlacesUtils.jsm"
22 | );
23 |
24 | // Default value for signon table sorting
25 | let lastSignonSortColumn = "origin";
26 | let lastSignonSortAscending = true;
27 |
28 | let showingPasswords = false;
29 |
30 | // password-manager lists
31 | let signons = [];
32 | let deletedSignons = [];
33 |
34 | // Elements that would be used frequently
35 | let filterField;
36 | let togglePasswordsButton;
37 | let signonsIntro;
38 | let removeButton;
39 | let removeAllButton;
40 | let signonsTree;
41 |
42 | let signonReloadDisplay = {
43 | observe(subject, topic, data) {
44 | if (topic == "passwordmgr-storage-changed") {
45 | switch (data) {
46 | case "addLogin":
47 | case "modifyLogin":
48 | case "removeLogin":
49 | case "removeAllLogins":
50 | if (!signonsTree) {
51 | return;
52 | }
53 | signons.length = 0;
54 | LoadSignons();
55 | // apply the filter if needed
56 | if (filterField && filterField.value != "") {
57 | FilterPasswords();
58 | }
59 | signonsTree.ensureRowIsVisible(
60 | signonsTree.view.selection.currentIndex
61 | );
62 | break;
63 | }
64 | Services.obs.notifyObservers(null, "passwordmgr-dialog-updated");
65 | }
66 | },
67 | };
68 |
69 | // Formatter for localization.
70 | let dateFormatter = new Services.intl.DateTimeFormat(undefined, {
71 | dateStyle: "medium",
72 | });
73 | let dateAndTimeFormatter = new Services.intl.DateTimeFormat(undefined, {
74 | dateStyle: "medium",
75 | timeStyle: "short",
76 | });
77 |
78 | function Startup() {
79 | // be prepared to reload the display if anything changes
80 | Services.obs.addObserver(signonReloadDisplay, "passwordmgr-storage-changed");
81 |
82 | signonsTree = document.getElementById("signonsTree");
83 | filterField = document.getElementById("filter");
84 | togglePasswordsButton = document.getElementById("togglePasswords");
85 | signonsIntro = document.getElementById("signonsIntro");
86 | removeButton = document.getElementById("removeSignon");
87 | removeAllButton = document.getElementById("removeAllSignons");
88 |
89 | togglePasswordsButton.label = "Show Passwords";
90 | togglePasswordsButton.accessKey = "P";
91 | signonsIntro.textContent = "Logins for the following sites are stored on your computer";
92 | removeAllButton.label = "Remove All";
93 | removeAllButton.accessKey = "A";
94 |
95 | if (Services.policies && !Services.policies.isAllowed("passwordReveal")) {
96 | togglePasswordsButton.hidden = true;
97 | }
98 |
99 | document
100 | .getElementsByTagName("treecols")[0]
101 | .addEventListener("click", event => {
102 | let { target, button } = event;
103 | let sortField = target.getAttribute("data-field-name");
104 |
105 | if (target.nodeName != "treecol" || button != 0 || !sortField) {
106 | return;
107 | }
108 |
109 | SignonColumnSort(sortField);
110 | Services.telemetry
111 | .getKeyedHistogramById("PWMGR_MANAGE_SORTED")
112 | .add(sortField);
113 | });
114 |
115 | LoadSignons();
116 |
117 | // filter the table if requested by caller
118 | if (
119 | window.arguments &&
120 | window.arguments[0] &&
121 | window.arguments[0].filterString
122 | ) {
123 | setFilter(window.arguments[0].filterString);
124 | }
125 |
126 | FocusFilterBox();
127 | }
128 |
129 | function Shutdown() {
130 | Services.obs.removeObserver(
131 | signonReloadDisplay,
132 | "passwordmgr-storage-changed"
133 | );
134 | }
135 |
136 | function setFilter(aFilterString) {
137 | filterField.value = aFilterString;
138 | FilterPasswords();
139 | }
140 |
141 | let signonsTreeView = {
142 | _filterSet: [],
143 | _lastSelectedRanges: [],
144 | selection: null,
145 |
146 | rowCount: 0,
147 | setTree(tree) {},
148 | getImageSrc(row, column) {
149 | if (column.element.getAttribute("id") !== "siteCol") {
150 | return "";
151 | }
152 |
153 | const signon = GetVisibleLogins()[row];
154 |
155 | return PlacesUtils.urlWithSizeRef(window, "page-icon:" + signon.origin, 16);
156 | },
157 | getCellValue(row, column) {},
158 | getCellText(row, column) {
159 | let time;
160 | let signon = GetVisibleLogins()[row];
161 | switch (column.id) {
162 | case "siteCol":
163 | return signon.httpRealm
164 | ? signon.origin + " (" + signon.httpRealm + ")"
165 | : signon.origin;
166 | case "userCol":
167 | return signon.username || "";
168 | case "passwordCol":
169 | return signon.password || "";
170 | case "timeCreatedCol":
171 | time = new Date(signon.timeCreated);
172 | return dateFormatter.format(time);
173 | case "timeLastUsedCol":
174 | time = new Date(signon.timeLastUsed);
175 | return dateAndTimeFormatter.format(time);
176 | case "timePasswordChangedCol":
177 | time = new Date(signon.timePasswordChanged);
178 | return dateFormatter.format(time);
179 | case "timesUsedCol":
180 | return signon.timesUsed;
181 | default:
182 | return "";
183 | }
184 | },
185 | isEditable(row, col) {
186 | if (col.id == "userCol" || col.id == "passwordCol") {
187 | return true;
188 | }
189 | return false;
190 | },
191 | isSeparator(index) {
192 | return false;
193 | },
194 | isSorted() {
195 | return false;
196 | },
197 | isContainer(index) {
198 | return false;
199 | },
200 | cycleHeader(column) {},
201 | getRowProperties(row) {
202 | return "";
203 | },
204 | getColumnProperties(column) {
205 | return "";
206 | },
207 | getCellProperties(row, column) {
208 | if (column.element.getAttribute("id") == "siteCol") {
209 | return "ltr";
210 | }
211 |
212 | return "";
213 | },
214 | setCellText(row, col, value) {
215 | let table = GetVisibleLogins();
216 | function _editLogin(field) {
217 | if (value == table[row][field]) {
218 | return;
219 | }
220 | let existingLogin = table[row].clone();
221 | table[row][field] = value;
222 | table[row].timePasswordChanged = Date.now();
223 | Services.logins.modifyLogin(existingLogin, table[row]);
224 | signonsTree.invalidateRow(row);
225 | }
226 |
227 | if (col.id == "userCol") {
228 | _editLogin("username");
229 | } else if (col.id == "passwordCol") {
230 | if (!value) {
231 | return;
232 | }
233 | _editLogin("password");
234 | }
235 | },
236 | };
237 |
238 | function SortTree(column, ascending) {
239 | let table = GetVisibleLogins();
240 | // remember which item was selected so we can restore it after the sort
241 | let selections = GetTreeSelections();
242 | let selectedNumber = selections.length ? table[selections[0]].number : -1;
243 | function compareFunc(a, b) {
244 | let valA, valB;
245 | switch (column) {
246 | case "origin":
247 | let realmA = a.httpRealm;
248 | let realmB = b.httpRealm;
249 | realmA = realmA == null ? "" : realmA.toLowerCase();
250 | realmB = realmB == null ? "" : realmB.toLowerCase();
251 |
252 | valA = a[column].toLowerCase() + realmA;
253 | valB = b[column].toLowerCase() + realmB;
254 | break;
255 | case "username":
256 | case "password":
257 | valA = a[column].toLowerCase();
258 | valB = b[column].toLowerCase();
259 | break;
260 |
261 | default:
262 | valA = a[column];
263 | valB = b[column];
264 | }
265 |
266 | if (valA < valB) {
267 | return -1;
268 | }
269 | if (valA > valB) {
270 | return 1;
271 | }
272 | return 0;
273 | }
274 |
275 | // do the sort
276 | table.sort(compareFunc);
277 | if (!ascending) {
278 | table.reverse();
279 | }
280 |
281 | // restore the selection
282 | let selectedRow = -1;
283 | if (selectedNumber >= 0 && false) {
284 | for (let s = 0; s < table.length; s++) {
285 | if (table[s].number == selectedNumber) {
286 | // update selection
287 | // note: we need to deselect before reselecting in order to trigger ...Selected()
288 | signonsTree.view.selection.select(-1);
289 | signonsTree.view.selection.select(s);
290 | selectedRow = s;
291 | break;
292 | }
293 | }
294 | }
295 |
296 | // display the results
297 | signonsTree.invalidate();
298 | if (selectedRow >= 0) {
299 | signonsTree.ensureRowIsVisible(selectedRow);
300 | }
301 | }
302 |
303 | function LoadSignons() {
304 | // loads signons into table
305 | try {
306 | signons = Services.logins.getAllLogins();
307 | } catch (e) {
308 | signons = [];
309 | }
310 | signons.forEach(login => login.QueryInterface(Ci.nsILoginMetaInfo));
311 | signonsTreeView.rowCount = signons.length;
312 |
313 | // sort and display the table
314 | signonsTree.view = signonsTreeView;
315 | // The sort column didn't change. SortTree (called by
316 | // SignonColumnSort) assumes we want to toggle the sort
317 | // direction but here we don't so we have to trick it
318 | lastSignonSortAscending = !lastSignonSortAscending;
319 | SignonColumnSort(lastSignonSortColumn);
320 |
321 | // disable "remove all signons" button if there are no signons
322 | if (!signons.length) {
323 | removeAllButton.setAttribute("disabled", "true");
324 | togglePasswordsButton.setAttribute("disabled", "true");
325 | } else {
326 | removeAllButton.removeAttribute("disabled");
327 | togglePasswordsButton.removeAttribute("disabled");
328 | }
329 |
330 | return true;
331 | }
332 |
333 | function GetVisibleLogins() {
334 | return signonsTreeView._filterSet.length
335 | ? signonsTreeView._filterSet
336 | : signons;
337 | }
338 |
339 | function GetTreeSelections() {
340 | let selections = [];
341 | let select = signonsTree.view.selection;
342 | if (select) {
343 | let count = select.getRangeCount();
344 | let min = {};
345 | let max = {};
346 | for (let i = 0; i < count; i++) {
347 | select.getRangeAt(i, min, max);
348 | for (let k = min.value; k <= max.value; k++) {
349 | if (k != -1) {
350 | selections[selections.length] = k;
351 | }
352 | }
353 | }
354 | }
355 | return selections;
356 | }
357 |
358 | function SignonSelected() {
359 | let selections = GetTreeSelections();
360 | if (selections.length) {
361 | removeButton.removeAttribute("disabled");
362 | } else {
363 | removeButton.setAttribute("disabled", true);
364 | }
365 | }
366 |
367 | function DeleteSignon() {
368 | let syncNeeded = !!signonsTreeView._filterSet.length;
369 | let tree = signonsTree;
370 | let view = signonsTreeView;
371 | let table = GetVisibleLogins();
372 |
373 | // Turn off tree selection notifications during the deletion
374 | tree.view.selection.selectEventsSuppressed = true;
375 |
376 | // remove selected items from list (by setting them to null) and place in deleted list
377 | let selections = GetTreeSelections();
378 | for (let s = selections.length - 1; s >= 0; s--) {
379 | let i = selections[s];
380 | deletedSignons.push(table[i]);
381 | table[i] = null;
382 | }
383 |
384 | // collapse list by removing all the null entries
385 | for (let j = 0; j < table.length; j++) {
386 | if (table[j] == null) {
387 | let k = j;
388 | while (k < table.length && table[k] == null) {
389 | k++;
390 | }
391 | table.splice(j, k - j);
392 | view.rowCount -= k - j;
393 | tree.rowCountChanged(j, j - k);
394 | }
395 | }
396 |
397 | // update selection and/or buttons
398 | if (table.length) {
399 | // update selection
400 | let nextSelection =
401 | selections[0] < table.length ? selections[0] : table.length - 1;
402 | tree.view.selection.select(nextSelection);
403 | } else {
404 | // disable buttons
405 | removeButton.setAttribute("disabled", "true");
406 | removeAllButton.setAttribute("disabled", "true");
407 | }
408 | tree.view.selection.selectEventsSuppressed = false;
409 | FinalizeSignonDeletions(syncNeeded);
410 | }
411 |
412 | async function DeleteAllSignons() {
413 | // Confirm the user wants to remove all passwords
414 | let dummy = { value: false };
415 | if (
416 | Services.prompt.confirmEx(
417 | window,
418 | "Remove all passwords",
419 | "Are you sure you wish to remove all passwords?",
420 | Services.prompt.STD_YES_NO_BUTTONS + Services.prompt.BUTTON_POS_1_DEFAULT,
421 | null,
422 | null,
423 | null,
424 | null,
425 | dummy
426 | ) == 1
427 | ) {
428 | // 1 == "No" button
429 | return;
430 | }
431 |
432 | let syncNeeded = !!signonsTreeView._filterSet.length;
433 | let view = signonsTreeView;
434 | let table = GetVisibleLogins();
435 |
436 | // remove all items from table and place in deleted table
437 | for (let i = 0; i < table.length; i++) {
438 | deletedSignons.push(table[i]);
439 | }
440 | table.length = 0;
441 |
442 | // clear out selections
443 | view.selection.select(-1);
444 |
445 | // update the tree view and notify the tree
446 | view.rowCount = 0;
447 |
448 | signonsTree.rowCountChanged(0, -deletedSignons.length);
449 | signonsTree.invalidate();
450 |
451 | // disable buttons
452 | removeButton.setAttribute("disabled", "true");
453 | removeAllButton.setAttribute("disabled", "true");
454 | FinalizeSignonDeletions(syncNeeded);
455 | Services.telemetry.getHistogramById("PWMGR_MANAGE_DELETED_ALL").add(1);
456 | Services.obs.notifyObservers(
457 | null,
458 | "weave:telemetry:histogram",
459 | "PWMGR_MANAGE_DELETED_ALL"
460 | );
461 | }
462 |
463 | async function TogglePasswordVisible() {
464 | if (showingPasswords || (await masterPasswordLogin(AskUserShowPasswords))) {
465 | showingPasswords = !showingPasswords;
466 | togglePasswordsButton.label = showingPasswords ? "Hide Passwords" : "Show Passwords";
467 | togglePasswordsButton.accessKey = "P";
468 | document.getElementById("passwordCol").hidden = !showingPasswords;
469 | FilterPasswords();
470 | }
471 |
472 | // Notify observers that the password visibility toggling is
473 | // completed. (Mostly useful for tests)
474 | Services.obs.notifyObservers(null, "passwordmgr-password-toggle-complete");
475 | Services.telemetry
476 | .getHistogramById("PWMGR_MANAGE_VISIBILITY_TOGGLED")
477 | .add(showingPasswords);
478 | Services.obs.notifyObservers(
479 | null,
480 | "weave:telemetry:histogram",
481 | "PWMGR_MANAGE_VISIBILITY_TOGGLED"
482 | );
483 | }
484 |
485 | async function AskUserShowPasswords() {
486 | let dummy = { value: false };
487 |
488 | // Confirm the user wants to display passwords
489 | return (
490 | Services.prompt.confirmEx(
491 | window,
492 | null,
493 | "Are you sure you wish to show your passwords?",
494 | Services.prompt.STD_YES_NO_BUTTONS,
495 | null,
496 | null,
497 | null,
498 | null,
499 | dummy
500 | ) == 0
501 | ); // 0=="Yes" button
502 | }
503 |
504 | function FinalizeSignonDeletions(syncNeeded) {
505 | for (let s = 0; s < deletedSignons.length; s++) {
506 | Services.logins.removeLogin(deletedSignons[s]);
507 | Services.telemetry.getHistogramById("PWMGR_MANAGE_DELETED").add(1);
508 | Services.obs.notifyObservers(
509 | null,
510 | "weave:telemetry:histogram",
511 | "PWMGR_MANAGE_DELETED"
512 | );
513 | }
514 | // If the deletion has been performed in a filtered view, reflect the deletion in the unfiltered table.
515 | // See bug 405389.
516 | if (syncNeeded) {
517 | try {
518 | signons = Services.logins.getAllLogins();
519 | } catch (e) {
520 | signons = [];
521 | }
522 | }
523 | deletedSignons.length = 0;
524 | }
525 |
526 | function HandleSignonKeyPress(e) {
527 | // If editing is currently performed, don't do anything.
528 | if (signonsTree.getAttribute("editing")) {
529 | return;
530 | }
531 | if (
532 | e.keyCode == KeyboardEvent.DOM_VK_DELETE ||
533 | (AppConstants.platform == "macosx" &&
534 | e.keyCode == KeyboardEvent.DOM_VK_BACK_SPACE)
535 | ) {
536 | DeleteSignon();
537 | e.preventDefault();
538 | }
539 | }
540 |
541 | function getColumnByName(column) {
542 | switch (column) {
543 | case "origin":
544 | return document.getElementById("siteCol");
545 | case "username":
546 | return document.getElementById("userCol");
547 | case "password":
548 | return document.getElementById("passwordCol");
549 | case "timeCreated":
550 | return document.getElementById("timeCreatedCol");
551 | case "timeLastUsed":
552 | return document.getElementById("timeLastUsedCol");
553 | case "timePasswordChanged":
554 | return document.getElementById("timePasswordChangedCol");
555 | case "timesUsed":
556 | return document.getElementById("timesUsedCol");
557 | }
558 | return undefined;
559 | }
560 |
561 | function SignonColumnSort(column) {
562 | let sortedCol = getColumnByName(column);
563 | let lastSortedCol = getColumnByName(lastSignonSortColumn);
564 |
565 | // clear out the sortDirection attribute on the old column
566 | lastSortedCol.removeAttribute("sortDirection");
567 |
568 | // determine if sort is to be ascending or descending
569 | lastSignonSortAscending =
570 | column == lastSignonSortColumn ? !lastSignonSortAscending : true;
571 |
572 | // sort
573 | lastSignonSortColumn = column;
574 | SortTree(lastSignonSortColumn, lastSignonSortAscending);
575 |
576 | // set the sortDirection attribute to get the styling going
577 | // first we need to get the right element
578 | sortedCol.setAttribute(
579 | "sortDirection",
580 | lastSignonSortAscending ? "ascending" : "descending"
581 | );
582 | }
583 |
584 | function SignonClearFilter() {
585 | let singleSelection = signonsTreeView.selection.count == 1;
586 |
587 | // Clear the Tree Display
588 | signonsTreeView.rowCount = 0;
589 | signonsTree.rowCountChanged(0, -signonsTreeView._filterSet.length);
590 | signonsTreeView._filterSet = [];
591 |
592 | // Just reload the list to make sure deletions are respected
593 | LoadSignons();
594 |
595 | // Restore selection
596 | if (singleSelection) {
597 | signonsTreeView.selection.clearSelection();
598 | for (let i = 0; i < signonsTreeView._lastSelectedRanges.length; ++i) {
599 | let range = signonsTreeView._lastSelectedRanges[i];
600 | signonsTreeView.selection.rangedSelect(range.min, range.max, true);
601 | }
602 | } else {
603 | signonsTreeView.selection.select(0);
604 | }
605 | signonsTreeView._lastSelectedRanges = [];
606 |
607 | signonsIntro.textContent = "Logins for the following sites are stored on your computer";
608 | removeAllButton.label = "Remove All";
609 | removeAllButton.accessKey = "A";
610 | }
611 |
612 | function FocusFilterBox() {
613 | if (filterField.getAttribute("focused") != "true") {
614 | filterField.focus();
615 | }
616 | }
617 |
618 | function SignonMatchesFilter(aSignon, aFilterValue) {
619 | if (aSignon.origin.toLowerCase().includes(aFilterValue)) {
620 | return true;
621 | }
622 | if (
623 | aSignon.username &&
624 | aSignon.username.toLowerCase().includes(aFilterValue)
625 | ) {
626 | return true;
627 | }
628 | if (
629 | aSignon.httpRealm &&
630 | aSignon.httpRealm.toLowerCase().includes(aFilterValue)
631 | ) {
632 | return true;
633 | }
634 | if (
635 | showingPasswords &&
636 | aSignon.password &&
637 | aSignon.password.toLowerCase().includes(aFilterValue)
638 | ) {
639 | return true;
640 | }
641 |
642 | return false;
643 | }
644 |
645 | function _filterPasswords(aFilterValue, view) {
646 | aFilterValue = aFilterValue.toLowerCase();
647 | return signons.filter(s => SignonMatchesFilter(s, aFilterValue));
648 | }
649 |
650 | function SignonSaveState() {
651 | // Save selection
652 | let seln = signonsTreeView.selection;
653 | signonsTreeView._lastSelectedRanges = [];
654 | let rangeCount = seln.getRangeCount();
655 | for (let i = 0; i < rangeCount; ++i) {
656 | let min = {};
657 | let max = {};
658 | seln.getRangeAt(i, min, max);
659 | signonsTreeView._lastSelectedRanges.push({
660 | min: min.value,
661 | max: max.value,
662 | });
663 | }
664 | }
665 |
666 | function FilterPasswords() {
667 | if (filterField.value == "") {
668 | SignonClearFilter();
669 | return;
670 | }
671 |
672 | let newFilterSet = _filterPasswords(filterField.value, signonsTreeView);
673 | if (!signonsTreeView._filterSet.length) {
674 | // Save Display Info for the Non-Filtered mode when we first
675 | // enter Filtered mode.
676 | SignonSaveState();
677 | }
678 | signonsTreeView._filterSet = newFilterSet;
679 |
680 | // Clear the display
681 | let oldRowCount = signonsTreeView.rowCount;
682 | signonsTreeView.rowCount = 0;
683 | signonsTree.rowCountChanged(0, -oldRowCount);
684 | // Set up the filtered display
685 | signonsTreeView.rowCount = signonsTreeView._filterSet.length;
686 | signonsTree.rowCountChanged(0, signonsTreeView.rowCount);
687 |
688 | // if the view is not empty then select the first item
689 | if (signonsTreeView.rowCount > 0) {
690 | signonsTreeView.selection.select(0);
691 | }
692 |
693 | signonsIntro.textContent = "The following logins match your search:";
694 | removeAllButton.label = "Remove All Shown";
695 | removeAllButton.accessKey = "A";
696 | }
697 |
698 | function CopySiteUrl() {
699 | // Copy selected site url to clipboard
700 | let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(
701 | Ci.nsIClipboardHelper
702 | );
703 | let row = signonsTree.currentIndex;
704 | let url = signonsTreeView.getCellText(row, { id: "siteCol" });
705 | clipboard.copyString(url);
706 | }
707 |
708 | async function CopyPassword() {
709 | // Don't copy passwords if we aren't already showing the passwords & a master
710 | // password hasn't been entered.
711 | if (!showingPasswords && !(await masterPasswordLogin())) {
712 | return;
713 | }
714 | // Copy selected signon's password to clipboard
715 | let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(
716 | Ci.nsIClipboardHelper
717 | );
718 | let row = signonsTree.currentIndex;
719 | let password = signonsTreeView.getCellText(row, { id: "passwordCol" });
720 | clipboard.copyString(password);
721 | Services.telemetry.getHistogramById("PWMGR_MANAGE_COPIED_PASSWORD").add(1);
722 | Services.obs.notifyObservers(
723 | null,
724 | "weave:telemetry:histogram",
725 | "PWMGR_MANAGE_COPIED_PASSWORD"
726 | );
727 | }
728 |
729 | function CopyUsername() {
730 | // Copy selected signon's username to clipboard
731 | let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(
732 | Ci.nsIClipboardHelper
733 | );
734 | let row = signonsTree.currentIndex;
735 | let username = signonsTreeView.getCellText(row, { id: "userCol" });
736 | clipboard.copyString(username);
737 | Services.telemetry.getHistogramById("PWMGR_MANAGE_COPIED_USERNAME").add(1);
738 | Services.obs.notifyObservers(
739 | null,
740 | "weave:telemetry:histogram",
741 | "PWMGR_MANAGE_COPIED_USERNAME"
742 | );
743 | }
744 |
745 | function EditCellInSelectedRow(columnName) {
746 | let row = signonsTree.currentIndex;
747 | let columnElement = getColumnByName(columnName);
748 | signonsTree.startEditing(
749 | row,
750 | signonsTree.columns.getColumnFor(columnElement)
751 | );
752 | }
753 |
754 | function LaunchSiteUrl() {
755 | let row = signonsTree.currentIndex;
756 | let url = signonsTreeView.getCellText(row, { id: "siteCol" });
757 | window.openWebLinkIn(url, "tab");
758 | }
759 |
760 | function UpdateContextMenu() {
761 | let singleSelection = signonsTreeView.selection.count == 1;
762 | let menuItems = new Map();
763 | let menupopup = document.getElementById("signonsTreeContextMenu");
764 | for (let menuItem of menupopup.querySelectorAll("menuitem")) {
765 | menuItems.set(menuItem.id, menuItem);
766 | }
767 |
768 | if (!singleSelection) {
769 | for (let menuItem of menuItems.values()) {
770 | menuItem.setAttribute("disabled", "true");
771 | }
772 | return;
773 | }
774 |
775 | let selectedRow = signonsTree.currentIndex;
776 |
777 | // Don't display "Launch Site URL" if we're not a browser.
778 | if (window.openWebLinkIn) {
779 | menuItems.get("context-launchsiteurl").removeAttribute("disabled");
780 | } else {
781 | menuItems.get("context-launchsiteurl").setAttribute("disabled", "true");
782 | menuItems.get("context-launchsiteurl").setAttribute("hidden", "true");
783 | }
784 |
785 | // Disable "Copy Username" if the username is empty.
786 | if (signonsTreeView.getCellText(selectedRow, { id: "userCol" }) != "") {
787 | menuItems.get("context-copyusername").removeAttribute("disabled");
788 | } else {
789 | menuItems.get("context-copyusername").setAttribute("disabled", "true");
790 | }
791 |
792 | menuItems.get("context-copysiteurl").removeAttribute("disabled");
793 | menuItems.get("context-editusername").removeAttribute("disabled");
794 | menuItems.get("context-copypassword").removeAttribute("disabled");
795 |
796 | // Disable "Edit Password" if the password column isn't showing.
797 | if (!document.getElementById("passwordCol").hidden) {
798 | menuItems.get("context-editpassword").removeAttribute("disabled");
799 | } else {
800 | menuItems.get("context-editpassword").setAttribute("disabled", "true");
801 | }
802 | }
803 |
804 | async function masterPasswordLogin(noPasswordCallback) {
805 | // This does no harm if master password isn't set.
806 | let tokendb = Cc["@mozilla.org/security/pk11tokendb;1"].createInstance(
807 | Ci.nsIPK11TokenDB
808 | );
809 | let token = tokendb.getInternalKeyToken();
810 |
811 | // If there is no master password, still give the user a chance to opt-out of displaying passwords
812 | if (token.checkPassword("")) {
813 | return noPasswordCallback ? noPasswordCallback() : true;
814 | }
815 |
816 | // So there's a master password. But since checkPassword didn't succeed, we're logged out (per nsIPK11Token.idl).
817 | try {
818 | // Relogin and ask for the master password.
819 | token.login(true); // 'true' means always prompt for token password. User will be prompted until
820 | // clicking 'Cancel' or entering the correct password.
821 | } catch (e) {
822 | // An exception will be thrown if the user cancels the login prompt dialog.
823 | // User is also logged out of Software Security Device.
824 | }
825 |
826 | return token.isLoggedIn();
827 | }
828 |
829 | function escapeKeyHandler() {
830 | // If editing is currently performed, don't do anything.
831 | if (signonsTree.getAttribute("editing")) {
832 | return;
833 | }
834 | window.close();
835 | }
836 |
837 | function OpenMigrator() {
838 | const { MigrationUtils } = ChromeUtils.import(
839 | "resource:///modules/MigrationUtils.jsm"
840 | );
841 | // We pass in the type of source we're using for use in telemetry:
842 | MigrationUtils.showMigrationWizard(window, [
843 | MigrationUtils.MIGRATION_ENTRYPOINT_PASSWORDS,
844 | ]);
845 | }
846 |
--------------------------------------------------------------------------------
/profile/chrome/utils/passwordmgr/passwordManager.xhtml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
67 |
68 |
69 |
70 |
71 |
78 |
79 |
83 |
84 |
87 |
88 |
92 |
93 |
96 |
97 |
100 |
101 |
103 |
104 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
117 |
119 |
120 |
123 |
125 |
126 |
127 |
128 |
129 |
130 |
133 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/profile/chrome/utils/passwordmgr/passwordmgr.css:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 | @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
6 |
7 | .contentPane {
8 | margin: 9px 8px 5px;
9 | }
10 |
11 | .actionButtons {
12 | margin: 0 3px 6px;
13 | }
14 |
15 | treechildren::-moz-tree-image(siteCol) {
16 | list-style-image: url(chrome://mozapps/skin/places/defaultFavicon.svg);
17 | -moz-context-properties: fill;
18 | fill: currentColor;
19 | width: 16px;
20 | height: 16px;
21 | margin-inline-end: 5px;
22 | }
23 |
--------------------------------------------------------------------------------
/profile/chrome/utils/userChrome.jsm:
--------------------------------------------------------------------------------
1 | let EXPORTED_SYMBOLS = [];
2 |
3 | const { Services } = ChromeUtils.import('resource://gre/modules/Services.jsm');
4 | const { xPref } = ChromeUtils.import('chrome://userchromejs/content/xPref.jsm');
5 | const { Management } = ChromeUtils.import('resource://gre/modules/Extension.jsm');
6 | const { AppConstants } = ChromeUtils.import('resource://gre/modules/AppConstants.jsm');
7 |
8 | let UC = {
9 | webExts: new Map(),
10 | sidebar: new Map()
11 | };
12 |
13 | let _uc = {
14 | ALWAYSEXECUTE: 'rebuild_userChrome.uc.js',
15 | BROWSERCHROME: AppConstants.MOZ_APP_NAME == 'thunderbird' ? 'chrome://messenger/content/messenger.xhtml' : 'chrome://browser/content/browser.xhtml',
16 | BROWSERTYPE: AppConstants.MOZ_APP_NAME == 'thunderbird' ? 'mail:3pane' : 'navigator:browser',
17 | BROWSERNAME: AppConstants.MOZ_APP_NAME.charAt(0).toUpperCase() + AppConstants.MOZ_APP_NAME.slice(1),
18 | PREF_ENABLED: 'userChromeJS.enabled',
19 | PREF_SCRIPTSDISABLED: 'userChromeJS.scriptsDisabled',
20 |
21 | chromedir: Services.dirsvc.get('UChrm', Ci.nsIFile),
22 | scriptsDir: '',
23 |
24 | sss: Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService),
25 |
26 | getScripts: function () {
27 | this.scripts = {};
28 | let files = this.chromedir.directoryEntries.QueryInterface(Ci.nsISimpleEnumerator);
29 | while (files.hasMoreElements()) {
30 | let file = files.getNext().QueryInterface(Ci.nsIFile);
31 | if (/\.uc\.js$/i.test(file.leafName)) {
32 | _uc.getScriptData(file);
33 | }
34 | }
35 | },
36 |
37 | getScriptData: function (aFile) {
38 | let aContent = this.readFile(aFile);
39 | let header = (aContent.match(/^\/\/ ==UserScript==\s*\n(?:.*\n)*?\/\/ ==\/UserScript==\s*\n/m) || [''])[0];
40 | let match, rex = {
41 | include: [],
42 | exclude: []
43 | };
44 | let findNextRe = /^\/\/ @(include|exclude)\s+(.+)\s*$/gm;
45 | while ((match = findNextRe.exec(header))) {
46 | rex[match[1]].push(match[2].replace(/^main$/i, _uc.BROWSERCHROME).replace(/\*/g, '.*?'));
47 | }
48 | if (!rex.include.length) {
49 | rex.include.push(_uc.BROWSERCHROME);
50 | }
51 | let exclude = rex.exclude.length ? '(?!' + rex.exclude.join('$|') + '$)' : '';
52 |
53 | let def = ['', ''];
54 | let author = (header.match(/\/\/ @author\s+(.+)\s*$/im) || def)[1];
55 | let filename = aFile.leafName || '';
56 |
57 | return this.scripts[filename] = {
58 | filename: filename,
59 | file: aFile,
60 | url: Services.io.getProtocolHandler('file').QueryInterface(Ci.nsIFileProtocolHandler).getURLSpecFromDir(this.chromedir) + filename,
61 | name: (header.match(/\/\/ @name\s+(.+)\s*$/im) || def)[1],
62 | description: (header.match(/\/\/ @description\s+(.+)\s*$/im) || def)[1],
63 | version: (header.match(/\/\/ @version\s+(.+)\s*$/im) || def)[1],
64 | author: (header.match(/\/\/ @author\s+(.+)\s*$/im) || def)[1],
65 | regex: new RegExp('^' + exclude + '(' + (rex.include.join('|') || '.*') + ')$', 'i'),
66 | id: (header.match(/\/\/ @id\s+(.+)\s*$/im) || ['', filename.split('.uc.js')[0] + '@' + (author || 'userChromeJS')])[1],
67 | homepageURL: (header.match(/\/\/ @homepageURL\s+(.+)\s*$/im) || def)[1],
68 | downloadURL: (header.match(/\/\/ @downloadURL\s+(.+)\s*$/im) || def)[1],
69 | updateURL: (header.match(/\/\/ @updateURL\s+(.+)\s*$/im) || def)[1],
70 | optionsURL: (header.match(/\/\/ @optionsURL\s+(.+)\s*$/im) || def)[1],
71 | startup: (header.match(/\/\/ @startup\s+(.+)\s*$/im) || def)[1],
72 | shutdown: (header.match(/\/\/ @shutdown\s+(.+)\s*$/im) || def)[1],
73 | onlyonce: /\/\/ @onlyonce\b/.test(header),
74 | isRunning: false,
75 | get isEnabled() {
76 | return (xPref.get(_uc.PREF_SCRIPTSDISABLED) || '').split(',').indexOf(this.filename) == -1;
77 | }
78 | }
79 | },
80 |
81 | readFile: function (aFile, metaOnly = false) {
82 | let stream = Cc['@mozilla.org/network/file-input-stream;1'].createInstance(Ci.nsIFileInputStream);
83 | stream.init(aFile, 0x01, 0, 0);
84 | let cvstream = Cc['@mozilla.org/intl/converter-input-stream;1'].createInstance(Ci.nsIConverterInputStream);
85 | cvstream.init(stream, 'UTF-8', 1024, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
86 | let content = '',
87 | data = {};
88 | while (cvstream.readString(4096, data)) {
89 | content += data.value;
90 | if (metaOnly && content.indexOf('// ==/UserScript==') > 0) {
91 | break;
92 | }
93 | }
94 | cvstream.close();
95 | return content.replace(/\r\n?/g, '\n');
96 | },
97 |
98 | everLoaded: [],
99 |
100 | loadScript: function (script, win) {
101 | if (!script.regex.test(win.location.href) || (script.filename != this.ALWAYSEXECUTE && !script.isEnabled)) {
102 | return;
103 | }
104 |
105 | if (script.onlyonce && script.isRunning) {
106 | if (script.startup) {
107 | eval(script.startup);
108 | }
109 | return;
110 | }
111 |
112 | try {
113 | Services.scriptloader.loadSubScript(script.url + '?' + script.file.lastModifiedTime,
114 | script.onlyonce ? { window: win } : win);
115 | script.isRunning = true;
116 | if (script.startup) {
117 | eval(script.startup);
118 | }
119 | if (!script.shutdown) {
120 | this.everLoaded.push(script.id);
121 | }
122 | } catch (ex) {
123 | Cu.reportError(ex);
124 | }
125 | },
126 |
127 | windows: function (fun, onlyBrowsers = true) {
128 | let windows = Services.wm.getEnumerator(onlyBrowsers ? this.BROWSERTYPE : null);
129 | while (windows.hasMoreElements()) {
130 | let win = windows.getNext();
131 | if (!win._uc)
132 | continue;
133 | if (!onlyBrowsers) {
134 | let frames = win.docShell.getAllDocShellsInSubtree(Ci.nsIDocShellTreeItem.typeAll, Ci.nsIDocShell.ENUMERATE_FORWARDS);
135 | let res = frames.some(frame => {
136 | let fWin = frame.domWindow;
137 | let {document, location} = fWin;
138 | if (fun(document, fWin, location))
139 | return true;
140 | });
141 | if (res)
142 | break;
143 | } else {
144 | let {document, location} = win;
145 | if (fun(document, win, location))
146 | break;
147 | }
148 | }
149 | },
150 |
151 | createElement: function (doc, tag, atts, XUL = true) {
152 | let el = XUL ? doc.createXULElement(tag) : doc.createElement(tag);
153 | for (let att in atts) {
154 | el.setAttribute(att, atts[att]);
155 | }
156 | return el
157 | }
158 | };
159 |
160 | if (xPref.get(_uc.PREF_ENABLED) === undefined) {
161 | xPref.set(_uc.PREF_ENABLED, true, true);
162 | }
163 |
164 | if (xPref.get(_uc.PREF_SCRIPTSDISABLED) === undefined) {
165 | xPref.set(_uc.PREF_SCRIPTSDISABLED, '', true);
166 | }
167 |
168 | let UserChrome_js = {
169 | observe: function (aSubject) {
170 | aSubject.addEventListener('DOMContentLoaded', this, {once: true});
171 | },
172 |
173 | handleEvent: function (aEvent) {
174 | let document = aEvent.originalTarget;
175 | let window = document.defaultView;
176 | let location = window.location;
177 |
178 | if (!this.sharedWindowOpened && location.href == 'chrome://extensions/content/dummy.xhtml') {
179 | this.sharedWindowOpened = true;
180 |
181 | Management.on('extension-browser-inserted', function (topic, browser) {
182 | browser.messageManager.addMessageListener('Extension:ExtensionViewLoaded', this.messageListener.bind(this));
183 | }.bind(this));
184 | } else if (/^(chrome:(?!\/\/global\/content\/commonDialog\.x?html)|about:(?!blank))/i.test(location.href)) {
185 | window.UC = UC;
186 | window._uc = _uc;
187 | window.xPref = xPref;
188 | if (window._gBrowser) // bug 1443849
189 | window.gBrowser = window._gBrowser;
190 |
191 | if (xPref.get(_uc.PREF_ENABLED)) {
192 | Object.values(_uc.scripts).forEach(script => {
193 | _uc.loadScript(script, window);
194 | });
195 | } else if (!UC.rebuild) {
196 | _uc.loadScript(_uc.scripts[_uc.ALWAYSEXECUTE], window);
197 | }
198 | }
199 | },
200 |
201 | messageListener: function (msg) {
202 | const browser = msg.target;
203 | const { addonId } = browser._contentPrincipal;
204 |
205 | browser.messageManager.removeMessageListener('Extension:ExtensionViewLoaded', this.messageListener);
206 |
207 | if (browser.ownerGlobal.location.href == 'chrome://extensions/content/dummy.xhtml') {
208 | UC.webExts.set(addonId, browser);
209 | Services.obs.notifyObservers(null, 'UCJS:WebExtLoaded', addonId);
210 | } else {
211 | let win = browser.ownerGlobal.windowRoot.ownerGlobal;
212 | UC.sidebar.get(addonId)?.set(win, browser) || UC.sidebar.set(addonId, new Map([[win, browser]]));
213 | Services.obs.notifyObservers(win, 'UCJS:SidebarLoaded', addonId);
214 | }
215 | }
216 | };
217 |
218 | if (!Services.appinfo.inSafeMode) {
219 | _uc.chromedir.append(_uc.scriptsDir);
220 | _uc.getScripts();
221 | Services.obs.addObserver(UserChrome_js, 'chrome-document-global-created', false);
222 | }
223 |
--------------------------------------------------------------------------------
/profile/chrome/utils/userPrefs.jsm:
--------------------------------------------------------------------------------
1 | let EXPORTED_SYMBOLS = [];
2 |
3 | const {xPref} = ChromeUtils.import('chrome://userchromejs/content/xPref.jsm');
4 |
5 | xPref.lock('extensions.legacy.enabled', true);
6 | xPref.lock('xpinstall.signatures.required', false);
7 |
--------------------------------------------------------------------------------
/profile/chrome/utils/xPref.jsm:
--------------------------------------------------------------------------------
1 | let EXPORTED_SYMBOLS = ['xPref'];
2 |
3 | const {Services} = ChromeUtils.import('resource://gre/modules/Services.jsm');
4 |
5 | var xPref = {
6 | // Retorna o valor da preferência, seja qual for o tipo, mas não
7 | // testei com tipos complexos como nsIFile, não sei como detectar
8 | // uma preferência assim, na verdade nunca vi uma
9 | get: function (prefPath, def = false, valueIfUndefined, setDefault = true) {
10 | let sPrefs = def ?
11 | Services.prefs.getDefaultBranch(null) :
12 | Services.prefs;
13 |
14 | try {
15 | switch (sPrefs.getPrefType(prefPath)) {
16 | case 0:
17 | if (valueIfUndefined != undefined)
18 | return this.set(prefPath, valueIfUndefined, setDefault);
19 | else
20 | return undefined;
21 | case 32:
22 | return sPrefs.getStringPref(prefPath);
23 | case 64:
24 | return sPrefs.getIntPref(prefPath);
25 | case 128:
26 | return sPrefs.getBoolPref(prefPath);
27 | }
28 | } catch (ex) {
29 | return undefined;
30 | }
31 | return;
32 | },
33 |
34 | set: function (prefPath, value, def = false) {
35 | let sPrefs = def ?
36 | Services.prefs.getDefaultBranch(null) :
37 | Services.prefs;
38 |
39 | switch (typeof value) {
40 | case 'string':
41 | return sPrefs.setStringPref(prefPath, value) || value;
42 | case 'number':
43 | return sPrefs.setIntPref(prefPath, value) || value;
44 | case 'boolean':
45 | return sPrefs.setBoolPref(prefPath, value) || value;
46 | }
47 | return;
48 | },
49 |
50 | lock: function (prefPath, value) {
51 | let sPrefs = Services.prefs;
52 | this.lockedBackupDef[prefPath] = this.get(prefPath, true);
53 | if (sPrefs.prefIsLocked(prefPath))
54 | sPrefs.unlockPref(prefPath);
55 |
56 | this.set(prefPath, value, true);
57 | sPrefs.lockPref(prefPath);
58 | },
59 |
60 | lockedBackupDef: {},
61 |
62 | unlock: function (prefPath) {
63 | Services.prefs.unlockPref(prefPath);
64 | let bkp = this.lockedBackupDef[prefPath];
65 | if (bkp == undefined)
66 | Services.prefs.deleteBranch(prefPath);
67 | else
68 | this.set(prefPath, bkp, true);
69 | },
70 |
71 | clear: Services.prefs.clearUserPref,
72 |
73 | // Detecta mudanças na preferência e retorna:
74 | // return[0]: valor da preferência alterada
75 | // return[1]: nome da preferência alterada
76 | // Guardar chamada numa var se quiser interrompê-la depois
77 | addListener: function (prefPath, trat) {
78 | this.observer = function (aSubject, aTopic, prefPath) {
79 | return trat(xPref.get(prefPath), prefPath);
80 | }
81 |
82 | Services.prefs.addObserver(prefPath, this.observer);
83 | return {
84 | prefPath: prefPath,
85 | observer: this.observer
86 | };
87 | },
88 |
89 | // Encerra pref observer
90 | // Só precisa passar a var definida quando adicionou
91 | removeListener: function (obs) {
92 | Services.prefs.removeObserver(obs.prefPath, obs.observer);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------