├── .gitignore ├── .jshintrc ├── CONTRIBUTING.md ├── LICENSE ├── LICENSE.txt ├── Readme.md ├── about.css ├── about.js ├── about.xhtml ├── bootstrap.js ├── build.py ├── chrome.manifest ├── de └── about.dtd ├── defaults └── preferences │ └── prefs.js ├── en-US └── about.dtd ├── fr └── about.dtd ├── install.rdf ├── ja └── about.dtd ├── loader.jsm ├── loading.gif ├── main.js ├── sdk ├── cothreads.js ├── logging.js ├── observers.js ├── preferences.js ├── request.js ├── strings.js ├── timers.js └── windows.js ├── sv-SE └── about.dtd ├── zh-CN └── about.dtd └── zh-TW └── about.dtd /.gitignore: -------------------------------------------------------------------------------- 1 | *.xpi 2 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "browser": true, 3 | "curly": true, 4 | "esnext": true, 5 | "freeze": true, 6 | "globalstrict": true, 7 | "moz": true, 8 | "noarg": true, 9 | "nonbsp": true, 10 | "strict": true, 11 | "undef": true, 12 | "unused": true, 13 | "validthis": true, 14 | "maxlen": 80, 15 | "predef": ["Components", "Cc", "Ci", "Cr", "Cu", "require", "log", 16 | "LOG_DEBUG", "LOG_ERROR", "LOG_INFO", "prefs", "lazyRequire", 17 | "lazy", "Services", "Instances", "XPCOMUtils", "exports", 18 | "reportError", "ADDON", "unload", "BASE_PATH", "Iterator", 19 | "globalPrefs", "weak"] 20 | } 21 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | First of all, thanks for considering contributing code or other resources. 4 | Much appreciated! 5 | 6 | ## Submitting issues 7 | 8 | Please try to keep your texts short, but still include enough detail for other 9 | people to understand and **reproduce** your issue. 10 | E.g. if a bug affects a particular version, mention the version number. It might 11 | be also helpful to talk a bit about your environment, like operating system and 12 | platform (x86, x86_64, arm). 13 | 14 | For bugs or feature requests provide: 15 | 16 | - Sensible, but short summary/title. 17 | - Description of the actual behavior, preferably with detailed steps to 18 | reproduce the issue. 19 | - Description of the expected behavior. 20 | - If you know about older versions that worked correctly, mention that as well. 21 | 22 | Be polite, even if your issue does not (initially) get a lot of attention, 23 | or project maintainers close it as invalid/wontfix/etc. 24 | 25 | If you already diagnosed an issue and found the root cause, and feel like 26 | spending some extra time coding up a fix, then please don't hesitate to file a 27 | pull request instead of an issue. Project maintainers will love you. 28 | 29 | ### Bad 30 | 31 | > There is a bug in your software. When I click the button, it crashes. 32 | 33 | ### Better 34 | 35 | > Crash in version 1.0 when clicking Print button 36 | > 37 | > Hey, I'm using your software in version 1.0 on a Windows 7 (64-bit) system 38 | > along with FooBar Browser version 13.0. Unfortunately, it crashes when I 39 | > click on the Print button. This didn't happen in version 0.9. 40 | > 41 | > 1. Open document xyz 42 | > 2. Click Print button 43 | > 3. The application becomes unresponsive and has to be killed manually. 44 | > 45 | > I expect the application to print the document and then to continue working 46 | > normally. 47 | 48 | ## Submitting code and/or documentation 49 | 50 | - Your code should look and feel like the other code in the project, e.g. it 51 | should try to mimic/abide by the existing code formatting style and API 52 | design decisions. 53 | - If you plan to implement or revise a major feature/major API, or 54 | *break things* (for the better) in general, then please file an issue or 55 | pull request early. That way the project maintainers might suggest changes 56 | early and/or refine/reject ideas before you spend a lot time writing code that 57 | won't be merged in the current form. 58 | - Please provide (unit) tests, where appropriate and feasible. 59 | - Please use pull requests and avoid *plain* patches, etc. 60 | - Your commits should include at the very least an exploratory in plain English. 61 | Feel free to make use additional long messages current version control systems 62 | support, in particular if your commit is anything more than a simple bugfix. 63 | - Try to use gender-neutral language. Why? For four good reasons: 64 | - It is more inclusive and should work well-enough for all people. 65 | - It doesn't cost you anything, really, and no, it does not infringe upon your 66 | rights. It is easy enough to do, at least, in the English language. 67 | - Most importantly: because this file tells you to ;) 68 | 69 | ## Copyrights and License 70 | 71 | Any new code you submit **must be licensed** under the same license as the 72 | project license. 73 | Exception to this rule are only third-party libraries, which have to be 74 | licensed under a compatible license nonetheless and may **not** make the whole 75 | project less permissive. 76 | E.g. you may **not** submit code that uses plain GPL in a derivative way in a 77 | project otherwise licensed under a more permissive license such as the 78 | BSD/MIT/GPL licenses. 79 | 80 | You retain your copyright, or may assign it to the project/project maintainer. 81 | However, you must of course own the copyright or have the permission from the 82 | owners before submitting code. Work-for-hire-laws can be complicated. 83 | 84 | ## Enforcement 85 | 86 | The points outlined in this document are more guidelines than 87 | rules-set-in-stone. 88 | 89 | If in doubt, **the project maintainer(s) make the final decision**. 90 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | See: http://www.mozilla.org/MPL/2.0/index.txt 5 | Or see: LICENSE file in the repository 6 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | about:addons-memory 2 | === 3 | 4 | Provides an about:addons-memory page, listing the memory usage of all active add-ons 5 | 6 | This extension is based on [ExtSDK](https://github.com/nmaier/extsdk/) 7 | -------------------------------------------------------------------------------- /about.css: -------------------------------------------------------------------------------- 1 | @import url(chrome://global/skin/about.css); 2 | 3 | html { 4 | font-family: message-box, sans-serif; 5 | font-size: 16px; 6 | } 7 | body { 8 | max-width: 950px; 9 | } 10 | @media (max-width: 900px) { 11 | html { 12 | padding: 0; 13 | margin: 0; 14 | font-size: 12px; 15 | } 16 | body { 17 | width: -moz-calc(100% - 3em); 18 | max-width: -moz-calc(100% - 3em); 19 | margin: 0; 20 | border: 0; 21 | } 22 | } 23 | 24 | table { 25 | border-collapse: collapse; 26 | width: 100%; 27 | text-align: left; 28 | } 29 | thead tr, 30 | tbody tr:not(:last-child) { 31 | border-bottom: 1px dotted black; 32 | } 33 | 34 | tr.disabled { 35 | opacity: 0.6; 36 | } 37 | 38 | td, th { 39 | padding: 1ex; 40 | } 41 | 42 | th[title] { 43 | cursor: help; 44 | } 45 | th:not(:last-child), 46 | td:not(:last-child) { 47 | border-right: 1px dotted darkgray; 48 | } 49 | 50 | td > p { 51 | padding: 0; 52 | margin: 0; 53 | } 54 | .loading { 55 | height: 96px; 56 | background: url(loading.gif) no-repeat center center; 57 | } 58 | 59 | .total, 60 | .name { 61 | font-weight: bold; 62 | } 63 | .id { 64 | font-size: smaller; 65 | font-style: italic; 66 | color: gray; 67 | } 68 | .id:hover { 69 | color: black; 70 | } 71 | 72 | sup { 73 | padding-left: 1ex; 74 | padding-right: 1em; 75 | } 76 | sup > a { 77 | text-decoration: none; 78 | } 79 | 80 | figure.icon { 81 | float: left; 82 | margin: 0; 83 | padding: 0; 84 | } 85 | div.icon { 86 | width: 64px; 87 | height: 64px; 88 | margin: 0; 89 | padding: 0; 90 | padding-right: 1em; 91 | display: table-cell; 92 | text-align: center; 93 | vertical-align: middle; 94 | } 95 | .icon > img { 96 | margin: 0; 97 | padding: 0; 98 | min-width: 32px; 99 | min-height: 32px; 100 | max-width: 64px; 101 | max-height: 64px; 102 | } 103 | 104 | th#memory { 105 | width: 6em; 106 | } 107 | th#bar { 108 | width: 10em; 109 | } 110 | 111 | th:not([id]) { 112 | width: 3em; 113 | } 114 | 115 | .remark { 116 | color: red; 117 | font-size: small; 118 | } 119 | 120 | .bar { 121 | height: 1.5em; 122 | background: navy; 123 | } 124 | -------------------------------------------------------------------------------- /about.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 file, 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | "use strict"; 5 | 6 | const { 7 | classes: Cc, 8 | interfaces: Ci, 9 | utils: Cu, 10 | results: Cr, 11 | Constructor, ctor 12 | } = Components; 13 | 14 | const {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); 15 | 16 | const ChromeRegistry = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry); 17 | const MemoryReporterManager = Cc["@mozilla.org/memory-reporter-manager;1"].getService(Ci.nsIMemoryReporterManager); 18 | const ResProtoHandler = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler); 19 | const MainThread = Services.tm.mainThread; 20 | const NOFRAC = {maximumFractionDigits: 0}; 21 | const ONEFRAC = {maximumFractionDigits: 1}; 22 | const TWOFRAC = {maximumFractionDigits: 2}; 23 | const THREEFRAC = {maximumFractionDigits: 3}; 24 | 25 | function _(id) { 26 | return document.body.getAttribute("data-" + id); 27 | } 28 | 29 | function runSoon(f) MainThread.dispatch(f, 0); 30 | function minimizeMemoryUsage(callback) { 31 | function notify(i) { 32 | Services.obs.notifyObservers(null, "memory-pressure", "heap-minimize"); 33 | if (--i) { 34 | runSoon(notify.bind(null, i)); 35 | } 36 | else if (callback) { 37 | runSoon(callback); 38 | } 39 | } 40 | if ("minimizeMemoryUsage" in MemoryReporterManager) { 41 | MemoryReporterManager.minimizeMemoryUsage(callback); 42 | } 43 | else { 44 | notify(3); 45 | } 46 | } 47 | 48 | function formatBytes(b) { 49 | if (b < 900) { 50 | return b.toLocaleString(undefined, NOFRAC) + " bytes"; 51 | } 52 | b /= 1024; 53 | if (b < 900) { 54 | return b.toLocaleString(undefined, ONEFRAC) + " KB"; 55 | } 56 | b /= 1024; 57 | if (b < 900) { 58 | return b.toLocaleString(undefined, TWOFRAC) + " MB"; 59 | } 60 | b /= 1024; 61 | return b.toLocaleString(undefined, THREEFRAC) + " GB"; 62 | } 63 | 64 | function sortResults(a, b) { 65 | // size descending 66 | let rv = b.bytes - a.bytes; 67 | if (!rv) { 68 | // else name ascending 69 | rv = a.name < b.name ? -1 : 1; 70 | } 71 | return rv; 72 | } 73 | 74 | const resolveAboutURI = (function() { 75 | let resolved = new Map(); 76 | return function resolveAboutURI(uri) { 77 | let mod = uri.path.replace(/#\?.*$/i, ""); 78 | let rv = resolved.get(mod); 79 | if (!rv) { 80 | let c = Services.io.newChannelFromURI(uri); 81 | rv = c.URI.clone(); 82 | if (rv.equals(uri)) { 83 | throw new Error("cannot resolve about URI"); 84 | } 85 | resolved.set(mod, rv); 86 | } 87 | return rv; 88 | }; 89 | })(); 90 | 91 | function resolveURI(uri) { 92 | switch (uri.scheme) { 93 | case "jar": 94 | case "file": 95 | if (uri instanceof Ci.nsIJARURI) { 96 | return resolveURI(uri.JARFile); 97 | } 98 | return uri; 99 | case "chrome": 100 | return resolveURI(ChromeRegistry.convertChromeURL(uri)); 101 | case "resource": 102 | return resolveURI(Services.io.newURI(ResProtoHandler.resolveURI(uri), null, null)); 103 | case "about": 104 | if (uri.spec == "about:blank") { 105 | // hack: also map about:blank... to the app 106 | return resolveURI(resolveAboutURI(Services.io.newURI("about:memory", null, null))); 107 | } 108 | return resolveURI(resolveAboutURI(uri)); 109 | case "view-source": 110 | return resolveURI(Services.io.newURI(uri.path, null, null)); 111 | default: 112 | throw new Error("cannot handle"); 113 | } 114 | } 115 | 116 | function $(id) document.getElementById(id); 117 | function $e(tag, attrs, text) { 118 | let e = document.createElement(tag); 119 | if (attrs) { 120 | for (let [k,v] in Iterator(attrs)) { 121 | e.setAttribute(k, v); 122 | } 123 | } 124 | if (text) { 125 | e.textContent = text; 126 | } 127 | return e; 128 | } 129 | function process(addons) { 130 | const known = []; 131 | const compartments = Object.create(null);; 132 | const re_jscompartment = /^explicit\/.*?(?:\/non-window-global|js-non-window)\/.*?\/compartment\((.*?)\)/; 133 | const re_windowobject = /^explicit\/(?:.*\/)?window-objects\/top\((.*?), id=\d+\)\/active\//; 134 | const re_worker = /^explicit\/(?:.*\/)?workers\/workers\(\)\/worker\((.*?), 0x[\da-f]+\)/; 135 | const re_explicit = /^explicit\//; 136 | const re_compartment = /^(\[System Principal\], )?(?:in.*?\?ownedBy=)?(.+?)(?: \(from: (.+?)(?::\d+)?)?$/; 137 | const re_schemes = /^(?:about|chrome|file|jar|resource)/; 138 | let rss = 0; 139 | 140 | function handleReport(process, path, kind, units, amount, description) { 141 | if (path == "resident") { 142 | rss = amount; 143 | return; 144 | } 145 | let m, spec; 146 | if (m = path.match(re_jscompartment)) { 147 | m = m[1].match(re_compartment); 148 | let syscomp = !!m[1]; 149 | spec = m[2]; 150 | if (m[3] && (!syscomp || !re_schemes.test(spec))) { 151 | spec = m[3].split(" -> ").pop(); 152 | } 153 | } 154 | else if (m = path.match(re_windowobject)) { 155 | spec = m[1]; 156 | } 157 | else if (m = path.match(re_worker)) { 158 | spec = m[1]; 159 | } 160 | 161 | if (!spec) { 162 | return; 163 | } 164 | spec = spec.replace(/\\/g, "/").trim(); 165 | try { 166 | if (spec in compartments) { 167 | compartments[spec] += amount; 168 | } 169 | else { 170 | compartments[spec] = amount; 171 | } 172 | } 173 | catch (ex) { 174 | console.error(spec.toSource(), ex); 175 | } 176 | } 177 | 178 | function mapSpecToAddon(spec, bytes) { 179 | if (/omni\.ja$|\.apk$/.test(spec)) { 180 | known[0].bytes += bytes; 181 | return true; 182 | } 183 | for (let [,k] in Iterator(known)) { 184 | if (spec.lastIndexOf(k.spec, 0) == 0) { 185 | k.bytes += bytes; 186 | return true; 187 | } 188 | } 189 | return false; 190 | } 191 | 192 | function process() { 193 | // map reports to addons 194 | for (let [c, b] in Iterator(compartments)) { 195 | try { 196 | let spec = resolveURI(Services.io.newURI(c, null, null)).spec; 197 | if (!mapSpecToAddon(spec, b)) { 198 | throw new Error("not an addon uri:" + spec); 199 | } 200 | } 201 | catch (ex) { 202 | console.warn("failed to map", c, ex); 203 | } 204 | } 205 | 206 | // construct table 207 | known.sort(sortResults); 208 | let maxAddonBytes = 0; 209 | let totalAddons = known.reduce(function(p, e) { 210 | maxAddonBytes = Math.max(maxAddonBytes, e.bytes); 211 | return e.bytes + p; 212 | }, 0); 213 | 214 | let fragment = document.createDocumentFragment(); 215 | let noteid = 0; 216 | for (let [,k] in Iterator(known)) { 217 | let tr = $e("tr"); 218 | if (!k.addon.isActive) { 219 | tr.className = "disabled"; 220 | } 221 | let tdn = $e("td"); 222 | 223 | let icon = k.addon.icon64URL || k.addon.iconURL || "chrome://mozapps/skin/extensions/extensionGeneric.svg"; 224 | icon = $e("img", {"src": icon}); 225 | let iconBox = $e("div", {"class": "icon"}); 226 | let figure = $e("figure", {"class": "icon"}); 227 | iconBox.appendChild(icon); 228 | figure.appendChild(iconBox); 229 | tdn.appendChild(figure); 230 | 231 | let footnotes; 232 | if (k.footnotes) { 233 | footnotes = document.createDocumentFragment(); 234 | for (let [,note] in Iterator(k.footnotes)) { 235 | let id = ++noteid; 236 | let fn = $e("sup"); 237 | // hack: need to construct the absolute uri ourselves in about: 238 | fn.appendChild($e("a", {"href": "about:addons-memory#fn_" + id}, id.toFixed(0))); 239 | footnotes.appendChild(fn); 240 | let text = $e("p", {"class": "fn", "id": "fn_" + id}, note); 241 | let anc = $e("sup", null, "[" + id + "] "); 242 | text.insertBefore(anc, text.firstChild); 243 | document.body.appendChild(text); 244 | } 245 | } 246 | 247 | let pname = $e("p", {"class": "name"}); 248 | if (k.addon.homepageURL) { 249 | pname.appendChild($e("a", {"target":"_blank", "href": k.addon.homepageURL}, k.addon.name)); 250 | } 251 | else { 252 | pname.textContent = k.addon.name; 253 | } 254 | if (footnotes) { 255 | pname.appendChild(footnotes); 256 | } 257 | tdn.appendChild(pname); 258 | 259 | tdn.appendChild($e("p", {"class": "creator"}, _("by") + " " + k.addon.creator)); 260 | tdn.appendChild($e("p", {"class": "id"}, k.addon.id)); 261 | tr.appendChild(tdn); 262 | tr.appendChild($e("td", {"data-value": k.bytes}, formatBytes(k.bytes))); 263 | 264 | let pa = k.bytes / totalAddons; 265 | let spa = (pa * 100.0).toLocaleString(undefined, ONEFRAC) + "%"; 266 | let pe = k.bytes / rss; 267 | let spe = (pe * 100.0).toLocaleString(undefined, ONEFRAC) + "%"; 268 | let scale = (k.bytes / maxAddonBytes * 100.0).toFixed(1) + "%"; 269 | 270 | tr.appendChild($e("td", {"data-value": pa}, spa)); 271 | tr.appendChild($e("td", {"data-value": pe}, spe)); 272 | let progress = $e("div", {"class": "bar"}); 273 | progress.style.width = scale; 274 | let tdp = $e("td"); 275 | tdp.appendChild(progress); 276 | tr.appendChild(tdp); 277 | fragment.appendChild(tr); 278 | } 279 | let tr = $e("tr", {"class": "total"}); 280 | tr.appendChild($e("td", null, "Total")); 281 | tr.appendChild($e("td", null, formatBytes(totalAddons))); 282 | tr.appendChild($e("td", null, "100%")); 283 | tr.appendChild($e("td", null, (totalAddons / rss * 100.0).toLocaleString(undefined, ONEFRAC) + "%")); 284 | fragment.appendChild(tr); 285 | 286 | $("tbody").appendChild(fragment); 287 | 288 | { 289 | let l = $("loading"); 290 | l.parentNode.removeChild(l); 291 | } 292 | } 293 | 294 | try { 295 | // Forcefeed the "Application" add-on 296 | { 297 | let appuri = resolveURI(Services.io.newURI("about:config", null, null)); 298 | let iconURL = "chrome://branding/content/icon64.png" 299 | try { 300 | if (!/omni\.ja|\.apk$/.test(appuri.spec)) { 301 | appuri.path = appuri.path.replace("chrome/toolkit/content/global/global.xul", ""); 302 | } 303 | } 304 | catch (ex) { 305 | console.log("failed to get proper appuri; assuming omnijar"); 306 | } 307 | if (/\.apk$/.test(appuri.spec)) { 308 | iconURL = "chrome://branding/content/favicon64.png" 309 | } 310 | let addon = { 311 | name: "Application", 312 | isActive: true, 313 | id: Services.appinfo.ID, 314 | creator: "Mozilla", 315 | iconURL: iconURL 316 | }; 317 | try { 318 | let branding = Services.strings.createBundle("chrome://branding/locale/brand.properties"); 319 | addon.name = branding.GetStringFromName("brandFullName"); 320 | addon.creator = branding.GetStringFromName("vendorShortName"); 321 | } 322 | catch (ex) { 323 | console.error("failed to get branding", ex); 324 | } 325 | known.push({ 326 | addon: addon, 327 | base: appuri, 328 | spec: appuri.spec, 329 | bytes: 0, 330 | footnotes: [_("footnote-locations")] 331 | }); 332 | } 333 | 334 | // process addons 335 | for (let [,a] in Iterator(addons)) { 336 | try { 337 | let base = resolveURI(a.getResourceURI(".").cloneIgnoringRef()); 338 | let notes; 339 | if (a.id == "about-addons-memory@tn123.org") { 340 | notes = [_("footnote-thisaddon")]; 341 | } 342 | known.push({ 343 | addon: a, 344 | base: base, 345 | spec: base.spec, 346 | bytes: 0, 347 | footnotes: notes 348 | }); 349 | } 350 | catch (ex) { 351 | console.warn("addon not supported", a.id); 352 | } 353 | } 354 | 355 | // process reports 356 | if ("nsIMemoryMultiReporter" in Ci) { 357 | console.log("taking uni"); 358 | let e = MemoryReporterManager.enumerateReporters(); 359 | while (e.hasMoreElements()) { 360 | let r = e.getNext(); 361 | if (r instanceof Ci.nsIMemoryReporter) { 362 | handleReport(null, r.path, r.kind, r.units, r.amount); 363 | } 364 | } 365 | e = MemoryReporterManager.enumerateMultiReporters(); 366 | while (e.hasMoreElements()) { 367 | let r = e.getNext(); 368 | if (r instanceof Ci.nsIMemoryMultiReporter) { 369 | r.collectReports(handleReport, null); 370 | } 371 | } 372 | process(); 373 | } 374 | else if ("enumerateReporters" in MemoryReporterManager) { 375 | console.log("taking no-uni"); 376 | let e = MemoryReporterManager.enumerateReporters(); 377 | while (e.hasMoreElements()) { 378 | let r = e.getNext(); 379 | if (r instanceof Ci.nsIMemoryReporter) { 380 | r.collectReports(handleReport, null); 381 | } 382 | } 383 | process(); 384 | } 385 | else { 386 | console.log("taking getReports"); 387 | if (MemoryReporterManager.getReports.length == 5) { 388 | MemoryReporterManager.getReports(handleReport, null, process, null, false); 389 | } 390 | else { 391 | MemoryReporterManager.getReports(handleReport, null, process, null); 392 | } 393 | } 394 | 395 | } 396 | catch (ex) { 397 | console.error(ex); 398 | } 399 | } 400 | 401 | addEventListener("load", function load() { 402 | removeEventListener("load", load, false); 403 | Cu.import("resource://gre/modules/AddonManager.jsm", {}).AddonManager.getAllAddons(process); 404 | }, false); 405 | 406 | /* vim: set et ts=2 sw=2 : */ 407 | -------------------------------------------------------------------------------- /about.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 4 | %aboutDTD; 5 | 6 | %defaboutDTD; 7 | ]> 8 | 9 | 10 | about:addons-memory 11 | 12 | 13 | 14 | 15 | 16 | 17 |

about:addons-memory

18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
&addon.label;&usage.label;&addons.label;&explicit.label;
30 |

31 |

¬e.label; &remark.count.label;

32 |

&remark.conclusion.label;

33 | 34 | 35 | -------------------------------------------------------------------------------- /bootstrap.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 file, 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | "use strict"; 5 | 6 | const global = this; 7 | 8 | function install() {} 9 | function uninstall() {} 10 | function startup(data) { 11 | // will unload itself 12 | Components.utils.import("chrome://about-addons-memory/content/loader.jsm"); 13 | _setupLoader(data, function real_startup() { 14 | try { 15 | require("main"); 16 | } 17 | catch (ex) { 18 | Components.utils.reportError(ex); 19 | } 20 | }); 21 | } 22 | function shutdown(reason) { 23 | if (reason === APP_SHUTDOWN) { 24 | // No need to cleanup; stuff will vanish anyway 25 | return; 26 | } 27 | unload("shutdown"); 28 | } 29 | 30 | /* vim: set et ts=2 sw=2 : */ 31 | -------------------------------------------------------------------------------- /build.py: -------------------------------------------------------------------------------- 1 | # vim: set nosmartindent et ts=4 sw=4 : 2 | 3 | import os 4 | import re 5 | import sys 6 | 7 | from contextlib import contextmanager as ctx 8 | from glob import glob 9 | 10 | from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED 11 | 12 | resources = [ 13 | "install.rdf", 14 | "chrome.manifest", 15 | "*.xhtml", 16 | "*.dtd", "*/*.dtd", 17 | "*.css", 18 | "*.gif", 19 | "about.js", 20 | "LICENSE.txt" 21 | ] 22 | destination = "about-addons-memory.xpi" 23 | 24 | 25 | try: 26 | from xpisign.context import ZipFileMinorCompression as Minor 27 | except ImportError: 28 | from warnings import warn 29 | warn("No optimal compression available; install xpisign") 30 | 31 | @ctx 32 | def Minor(): 33 | yield 34 | 35 | 36 | def get_js_requires(scripts): 37 | known = set() 38 | scripts = list(scripts) 39 | for script in scripts: 40 | with open(script) as sp: 41 | for line in sp: 42 | m = re.search(r"(?:r|lazyR)equire\((['\"])(.+?)\1", line) 43 | if not m: 44 | continue 45 | m = m.group(2) + ".js" 46 | if m in known: 47 | continue 48 | known.add(m) 49 | scripts += m, 50 | return set(scripts) 51 | 52 | 53 | def get_files(resources): 54 | for r in get_js_requires(("bootstrap.js", "loader.jsm")): 55 | yield r 56 | for r in resources: 57 | if os.path.isfile(r): 58 | yield r 59 | continue 60 | for g in glob(r): 61 | yield g 62 | 63 | 64 | class ZipOutFile(ZipFile): 65 | def __init__(self, zfile): 66 | ZipFile.__init__(self, zfile, "w", ZIP_DEFLATED) 67 | 68 | def __enter__(self): 69 | return self 70 | 71 | def __exit__(self, type, value, traceback): 72 | self.close() 73 | 74 | 75 | if os.path.exists(destination): 76 | print >>sys.stderr, destination, "is in the way" 77 | sys.exit(1) 78 | 79 | with Minor(), ZipOutFile(destination) as zp: 80 | for f in sorted(get_files(resources), key=str.lower): 81 | compress_type = ZIP_STORED if f.endswith(".png") else ZIP_DEFLATED 82 | zp.write(f, compress_type=compress_type) 83 | -------------------------------------------------------------------------------- /chrome.manifest: -------------------------------------------------------------------------------- 1 | content about-addons-memory ./ 2 | locale about-addons-memory en-US en-US/ 3 | locale about-addons-memory de de/ 4 | locale about-addons-memory fr fr/ 5 | locale about-addons-memory ja ja/ 6 | locale about-addons-memory sv-SE sv-SE/ 7 | locale about-addons-memory zh-CN zh-CN/ 8 | locale about-addons-memory zh-TW zh-TW/ 9 | -------------------------------------------------------------------------------- /de/about.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /defaults/preferences/prefs.js: -------------------------------------------------------------------------------- 1 | pref("extensions.ID.loglevel", 0x7fffffff); 2 | -------------------------------------------------------------------------------- /en-US/about.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /fr/about.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /install.rdf: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | about-addons-memory@tn123.org 8 | about:addons-memory 9 | Adds an about:addons-memory page that details add-on memory usage 10 | 11 11 | Nils Maier 12 | 13 | true 14 | true 15 | 2 16 | 17 | 18 | 19 | 20 | {ec8030f7-c20a-464f-9b0e-13a3a9e97384} 21 | 45.0 22 | 50.* 23 | 24 | 25 | 26 | 27 | 28 | 29 | {aa3c5121-dab2-40e2-81ca-7ea25febc110} 30 | 45.0 31 | 50.* 32 | 33 | 34 | 35 | 36 | 37 | 38 | {92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a} 39 | 2.40 40 | 2.47.* 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /ja/about.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /loader.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 file, 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | "use strict"; 5 | 6 | var EXPORTED_SYMBOLS = ["BASE_PATH", "require", "unload", "_setupLoader"]; 7 | 8 | var { 9 | classes: Cc, 10 | interfaces: Ci, 11 | utils: Cu, 12 | results: Cr, 13 | Constructor: ctor, 14 | manager: Cm 15 | } = Components; 16 | 17 | var weak = Cu.getWeakReference.bind(Cu); 18 | var reportError = Cu.reportError.bind(Cu); 19 | 20 | Cm.QueryInterface(Ci.nsIComponentRegistrar); 21 | 22 | Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 23 | Cu.import("resource://gre/modules/Services.jsm"); 24 | 25 | var lazy = XPCOMUtils.defineLazyGetter; 26 | 27 | // hide our internals 28 | // Since require() uses .scriptloader, the loaded require scopes will have 29 | // access to the named stuff within this module scope, but we actually want 30 | // them to have access to certain stuff. 31 | (function setup_scope(exports) { 32 | Services = exports.Services = Object.create(Services); 33 | let dlsg = XPCOMUtils.defineLazyServiceGetter.bind(XPCOMUtils, Services); 34 | dlsg("catman", "@mozilla.org/categorymanager;1", "nsICategoryManager"); 35 | dlsg("clipbrd", "@mozilla.org/widget/clipboard;1", "nsIClipboard"); 36 | dlsg("drags", "@mozilla.org/widget/dragservice;1", "nsIDragService"); 37 | dlsg("eps", "@mozilla.org/uriloader/external-protocol-service;1", 38 | "nsIExternalProtocolService"); 39 | dlsg("fixups", "@mozilla.org/docshell/urifixup;1", "nsIURIFixup"); 40 | dlsg("memrm", "@mozilla.org/memory-reporter-manager;1", 41 | "nsIMemoryReporterManager"); 42 | dlsg("mime", "@mozilla.org/uriloader/external-helper-app-service;1", 43 | "nsIMIMEService"); 44 | dlsg("mimeheader", "@mozilla.org/network/mime-hdrparam;1", 45 | "nsIMIMEHeaderParam"); 46 | dlsg("sysprincipal", "@mozilla.org/systemprincipal;1", "nsIPrincipal"); 47 | dlsg("uuid", "@mozilla.org/uuid-generator;1", "nsIUUIDGenerator"); 48 | 49 | const Instances = exports.Instances = { 50 | get: function I_get(symbol, contract, iface, initializer) { 51 | if (!(symbol in this)) { 52 | this.register(symbol, contract, iface, initializer); 53 | } 54 | return this[symbol]; 55 | }, 56 | register: function I_register(symbol, contract, iface, initializer) { 57 | if (symbol in this) { 58 | let msg = "Symbol " + symbol + " already in Instances"; 59 | log(LOG_ERROR, msg); 60 | throw new Error(msg); 61 | } 62 | if (initializer) { 63 | lazy(this, symbol, () => ctor(contract, iface, initializer)); 64 | lazy(this, symbol + "_p", () => ctor(contract, iface)); 65 | } 66 | else { 67 | lazy(this, symbol, () => ctor(contract, iface)); 68 | lazy(this, symbol.toLowerCase(), () => new (ctor(contract, iface))()); 69 | } 70 | } 71 | }; 72 | 73 | const {SELF_PATH, BASE_PATH} = (function() { 74 | let rv; 75 | try { throw new Error("narf"); } 76 | catch (ex) { 77 | rv = { 78 | SELF_PATH: ex.fileName, 79 | BASE_PATH: /^(.+\/).*?$/.exec(ex.fileName)[1] 80 | }; 81 | } 82 | return rv; 83 | })(); 84 | exports.BASE_PATH = BASE_PATH; 85 | 86 | // logging stubs 87 | var log = function() {}; // stub 88 | var LOG_DEBUG = 0, LOG_INFO = 0, LOG_ERROR = 0; 89 | 90 | var _unloaders = []; 91 | let _runUnloader = function _runUnloader(fn) { 92 | try { 93 | fn(); 94 | } 95 | catch (ex) { 96 | log(LOG_ERROR, "unloader failed", ex); 97 | } 98 | }; 99 | exports.unload = function unload(fn) { 100 | if (fn == "shutdown") { 101 | log(LOG_INFO, "shutdown"); 102 | for (let i = _unloaders.length; ~(--i);) { 103 | _runUnloader(_unloaders[i]); 104 | } 105 | _unloaders.splice(0); 106 | return; 107 | } 108 | 109 | // add an unloader 110 | if (typeof(fn) != "function") { 111 | throw new Error("unloader is not a function"); 112 | } 113 | _unloaders.push(fn); 114 | return function() { 115 | _runUnloader(fn); 116 | _unloaders = _unloaders.filter(c => c != fn); 117 | }; 118 | }; 119 | 120 | const _registry = new Map(); 121 | exports.require = function require(mod) { 122 | mod = BASE_PATH + mod + ".js"; 123 | 124 | // already loaded? 125 | let scope = _registry.get(mod); 126 | if (scope) { 127 | return scope.exports; 128 | } 129 | 130 | // try to load the mod 131 | log(LOG_DEBUG, "going to load: " + mod); 132 | scope = Object.create(exports); 133 | scope.exports = Object.create(null); 134 | try { 135 | scope = Cu.Sandbox(Services.sysprincipal, { 136 | sandboxName: mod, 137 | sandboxPrototype: scope, 138 | wantXRays: false 139 | }); 140 | Services.scriptloader.loadSubScript(mod, scope, "utf-8"); 141 | } 142 | catch (ex) { 143 | log(LOG_ERROR, "failed to load " + mod, ex); 144 | throw ex; 145 | } 146 | 147 | _registry.set(mod, scope); 148 | log(LOG_DEBUG, "loaded module: " + mod); 149 | 150 | return scope.exports; 151 | }; 152 | 153 | exports.lazyRequire = function lazyRequire(mod) { 154 | function lazyBind(props, prop) { 155 | log(LOG_DEBUG, "lazily binding " + props + " for module " + mod); 156 | let m = require(mod); 157 | for (let [,p] in new Iterator(props)) { 158 | delete this[p]; 159 | this[p] = m[p]; 160 | } 161 | return this[prop]; 162 | } 163 | 164 | // Already loaded? 165 | let scope = _registry.get(mod); 166 | if (scope) { 167 | return scope.exports; 168 | } 169 | 170 | let props = Array.slice(arguments, 1); 171 | let rv = {}; 172 | let binder = lazyBind.bind(rv, props); 173 | for (let [,p] in new Iterator(props)) { 174 | let _p = p; 175 | lazy(rv, _p, () => binder(_p)); 176 | } 177 | return rv; 178 | }; 179 | 180 | unload(function() { 181 | for (let [mod, scope] of _registry) { 182 | _registry.delete(mod); 183 | Cu.nukeSandbox(scope); 184 | } 185 | if (_registry.clear) { 186 | _registry.clear(); 187 | } 188 | // unload ourselves 189 | Cu.unload(SELF_PATH); 190 | }); 191 | 192 | exports._setupLoader = function _setupLoader(data, callback) { 193 | delete exports._setupLoader; 194 | 195 | let _am = {}; 196 | Cu.import("resource://gre/modules/AddonManager.jsm", _am); 197 | _am.AddonManager.getAddonByID(data.id, function loader_startup(addon) { 198 | exports.ADDON = addon; 199 | unload(() => delete exports.ADDON); 200 | 201 | let logging; 202 | try { 203 | logging = require("sdk/logging"); 204 | for (let [k,v] in new Iterator(logging)) { 205 | exports[k] = v; 206 | } 207 | 208 | let prefs = require("sdk/preferences"); 209 | exports.prefs = prefs.prefs; 210 | exports.globalPrefs = prefs.globalPrefs; 211 | 212 | try { 213 | prefs.prefs.observe("loglevel", 214 | (p, v) => logging.setLogLevel(v), 215 | logging.LOG_NONE); 216 | } 217 | catch (ex) { 218 | logging.log(logging.LOG_ERROR, "failed to set log level", ex); 219 | } 220 | } 221 | catch (ex) { 222 | // probably do not have a working log() yet 223 | reportError(ex); 224 | return; 225 | } 226 | 227 | try { 228 | if (callback) { 229 | logging.log(logging.LOG_DEBUG, "loader: running callback"); 230 | callback(); 231 | } 232 | } 233 | catch (ex) { 234 | logging.log(logging.LOG_ERROR, "callback failed!", ex); 235 | } 236 | logging.log(logging.LOG_DEBUG, "loader: done"); 237 | }); 238 | }; 239 | 240 | })(this); 241 | 242 | /* vim: set et ts=2 sw=2 : */ 243 | -------------------------------------------------------------------------------- /loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nmaier/about-addons-memory/a613eb56b309c56c2bff8363b95e3f6976eec661/loading.gif -------------------------------------------------------------------------------- /main.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 file, 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | "use strict"; 5 | 6 | function AboutModule() { 7 | } 8 | AboutModule.prototype = { 9 | uri: Services.io.newURI("chrome://about-addons-memory/content/about.xhtml", null, null), 10 | classDescription: "about:addons-memory about module", 11 | classID: Components.ID("fda5ee40-a5d6-11e1-b3dd-0800200c9a66"), 12 | contractID: '@mozilla.org/network/protocol/about;1?what=addons-memory', 13 | 14 | QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]), 15 | 16 | newChannel: function(aURI) { 17 | let chan; 18 | try { 19 | chan = Services.io.newChannelFromURI2( 20 | this.uri, 21 | null, 22 | Services.scriptSecurityManager.getSystemPrincipal(), 23 | null, 24 | Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, 25 | Ci.nsIContentPolicy.TYPE_OTHER); 26 | } 27 | catch (ex) { 28 | chan = Services.io.newChannelFromURI(this.uri); 29 | } 30 | chan.originalURI = aURI; 31 | return chan; 32 | }, 33 | getURIFlags: function(aURI) 0 34 | }; 35 | 36 | (function registerComponents() { 37 | for (let [,cls] in Iterator([AboutModule])) { 38 | try { 39 | const factory = { 40 | _cls: cls, 41 | createInstance: function(outer, iid) { 42 | if (outer) { 43 | throw Cr.NS_ERROR_NO_AGGREGATION; 44 | } 45 | return new cls(); 46 | } 47 | }; 48 | Cm.registerFactory(cls.prototype.classID, cls.prototype.classDescription, cls.prototype.contractID, factory); 49 | unload(function() { 50 | Cm.unregisterFactory(factory._cls.prototype.classID, factory); 51 | }); 52 | } 53 | catch (ex) { 54 | log(LOG_ERROR, "failed to register module: " + cls.name, ex); 55 | } 56 | } 57 | })(); 58 | 59 | log(LOG_INFO, "ready"); 60 | 61 | /* vim: set et ts=2 sw=2 : */ 62 | -------------------------------------------------------------------------------- /sdk/cothreads.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 file, 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | "use strict"; 5 | 6 | // Note: only used for dispatching CoThreads to the mainThread event loop 7 | const ThreadManager = Cc["@mozilla.org/thread-manager;1"]. 8 | getService(Ci.nsIThreadManager); 9 | const MainThread = ThreadManager.mainThread; 10 | 11 | const CoThreadBase = { 12 | _idx: 0, 13 | _ran: false, 14 | _finishFunc: null, 15 | 16 | init: function CoThreadBase_init(func, yieldEvery, thisCtx) { 17 | this._thisCtx = thisCtx ? thisCtx : this; 18 | 19 | // default to 1 20 | this._yieldEvery = typeof yieldEvery == 'number' ? 21 | Math.floor(yieldEvery) : 22 | 1; 23 | if (yieldEvery < 1) { 24 | throw Cr.NS_ERROR_INVALID_ARG; 25 | } 26 | 27 | if (typeof func != 'function' && !(func instanceof Function)) { 28 | throw Cr.NS_ERROR_INVALID_ARG; 29 | } 30 | this._func = func; 31 | this.init = function() {}; 32 | }, 33 | 34 | start: function CoThreadBase_run(finishFunc) { 35 | if (this._ran) { 36 | throw new Error("You cannot run a CoThread/CoThreadListWalker instance " + 37 | "more than once."); 38 | } 39 | this._finishFunc = finishFunc; 40 | this._ran = true; 41 | MainThread.dispatch(this, 0); 42 | }, 43 | 44 | QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsICancelable, 45 | Ci.nsIRunnable]), 46 | 47 | _terminated: false, 48 | 49 | run: function CoThreadBase_run() { 50 | if (this._terminated) { 51 | return; 52 | } 53 | 54 | let y = this._yieldEvery; 55 | let g = this._generator; 56 | let f = this._func; 57 | let ctx = this._thisCtx; 58 | let callf = this._callf; 59 | const isStar = !('send' in g); 60 | try { 61 | for (let i = 0; i < y; ++i) { 62 | let next = g.next(); 63 | if (isStar) { 64 | if (next.done) { 65 | throw "complete"; 66 | } 67 | next = next.value; 68 | } 69 | if (!callf(ctx, next, this._idx++, f)) { 70 | throw 'complete'; 71 | } 72 | } 73 | if (!this._terminated) { 74 | MainThread.dispatch(this, 0); 75 | } 76 | } 77 | catch (ex) { 78 | this.cancel(); 79 | } 80 | }, 81 | 82 | cancel: function CoThreadBase_cancel() { 83 | if (this._terminated) { 84 | return; 85 | } 86 | this._terminated = true; 87 | if (this._finishFunc) { 88 | this._finishFunc.call(this._thisCtx); 89 | } 90 | } 91 | }; 92 | 93 | /** 94 | * Constructs a new CoThread (aka. pseudo-thread). 95 | * A CoThread will repeatedly call a specified function, but "breaking" 96 | * the operation temporarily after a certain amount of calls, 97 | * so that the main thread gets a chance to process any outstanding 98 | * events. 99 | * 100 | * Example: 101 | * new CoThread( 102 | * // What to do with each item? 103 | * // Print it! 104 | * function(count) document.write(count + "
") || (count < 30000), 105 | * // When to turn over Control? 106 | * // Each 1000 items 107 | * 1000 108 | * ).start(); 109 | * 110 | * @param {Function} func Function to be called. Is passed call count as 111 | * argument. Returning false will cancel the operation. 112 | * @param {Number} yieldEvery Optional. After how many items control should be 113 | * turned over to the main thread 114 | * @param {Object} thisCtx Optional. The function will be called in the scope of 115 | * this object (or if omitted in the scope of the 116 | * CoThread instance) 117 | */ 118 | exports.CoThread = function CoThread(func, yieldEvery, thisCtx) { 119 | this.init(func, yieldEvery, thisCtx); 120 | // fake generator so we may use a common implementation. ;) 121 | this._generator = (function() { 122 | for(;;) { 123 | yield null; 124 | } 125 | })(); 126 | }; 127 | 128 | exports.CoThread.prototype = Object.create(CoThreadBase, { 129 | _callf: { 130 | value: function CoThread__callf(ctx, i, idx, fn) { 131 | return fn.call(ctx, idx); 132 | }, 133 | enumerable: true 134 | } 135 | }); 136 | 137 | /** 138 | * Constructs a new CoThreadInterleaved (aka. pseudo-thread). 139 | * The CoThread will process a interleaved function (generator) 140 | * 141 | * Example: 142 | * new CoThread( 143 | * function(count) { 144 | * do_some(); 145 | * yield true; 146 | * do_more(); 147 | * yield true; 148 | * if (!do_even_more()) { 149 | * return; 150 | * } 151 | * do_last(); 152 | * }, 153 | * // When to turn over Control? 154 | * // Each 2 items 155 | * 2 156 | * ).start(); 157 | * 158 | * @param {Function} func Function to be called. Is passed call count as 159 | * argument. Returning false will cancel the operation. 160 | * @param {Number} yieldEvery Optional. After how many items control should be 161 | * turned over to the main thread 162 | * @param {Object} thisCtx Optional. The function will be called in the scope of 163 | * this object (or if omitted in the scope of the 164 | * CoThread instance) 165 | */ 166 | exports.CoThreadInterleaved = function CoThreadInterleaved(generator, 167 | yieldEvery, 168 | thisCtx) { 169 | this.init(() => true, yieldEvery, thisCtx); 170 | this._generator = generator; 171 | }; 172 | exports.CoThreadInterleaved.prototype = Object.create(CoThreadBase, { 173 | _callf: { 174 | value: () => true, 175 | enumerable: true 176 | } 177 | }); 178 | 179 | /** 180 | * Constructs a new CoThreadListWalker (aka. pseudo-thread). 181 | * A CoThreadListWalker will walk a specified list and call a specified function 182 | * on each item, but "breaking" the operation temporarily after a 183 | * certain amount of processed items, so that the main thread may 184 | * process any outstanding events. 185 | * 186 | * Example: 187 | * new CoThreadListWalker( 188 | * // What to do with each item? 189 | * // Print it! 190 | * function(item, idx) { 191 | * return document.write(item + "/" + idx + "
") || true; 192 | * }, 193 | * // What items? 194 | * // 0 - 29999 195 | * (function() { for (let i = 0; i < 30000; ++i) yield i; })(), 196 | * // When to turn over Control? 197 | * // Each 1000 items 198 | * 1000, 199 | * null, 200 | * ).start(function() alert('done')); 201 | * 202 | * @param {Function} func Function to be called on each item. Is passed item 203 | * and index as arguments. Returning false will cancel 204 | * the operation. 205 | * @param {Array/Generator} arrayOrGenerator Array or Generator object to be 206 | * used as the input list 207 | * @param {Number} yieldEvery Optional. After how many items control should be 208 | * turned over to the main thread 209 | * @param {Object} thisCtx Optional. The function will be called in the scope of 210 | * this object (or if omitted in the scope of the 211 | * CoThread instance) 212 | */ 213 | exports.CoThreadListWalker = function CoThreadListWalker(func, arrayOrGenerator, 214 | yieldEvery, thisCtx) { 215 | this.init(func, yieldEvery, thisCtx); 216 | 217 | if (arrayOrGenerator instanceof Array || 'length' in arrayOrGenerator) { 218 | // make a generator 219 | this._generator = (function*() { 220 | for (let i of arrayOrGenerator) { 221 | yield i; 222 | } 223 | })(); 224 | } 225 | else { 226 | this._generator = arrayOrGenerator; 227 | } 228 | 229 | if (this._lastFunc && (typeof func != 'function' && 230 | !(func instanceof Function))) { 231 | throw Cr.NS_ERROR_INVALID_ARG; 232 | } 233 | }; 234 | 235 | exports.CoThreadListWalker.prototype = Object.create(CoThreadBase, { 236 | _callf: { 237 | value: function CoThreadListWalker__callf(ctx, item, idx, fn) { 238 | return fn.call(ctx, item, idx); 239 | }, 240 | enumerable: true 241 | } 242 | }); 243 | 244 | /* vim: set et ts=2 sw=2 : */ 245 | -------------------------------------------------------------------------------- /sdk/logging.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 file, 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | "use strict"; 5 | 6 | const global = this; 7 | 8 | Instances.register("ScriptError", 9 | "@mozilla.org/scripterror;1", 10 | "nsIScriptError", 11 | "init"); 12 | 13 | var UNKNOWN_STACK = { 14 | stackMsg: "", 15 | sourceName: "unknown", 16 | sourceLine: "", 17 | lineNumber: 0, 18 | columnNumber: 0 19 | }; 20 | Object.freeze(UNKNOWN_STACK); 21 | 22 | function prepareStack(stack) { 23 | if (!stack || !(stack instanceof Ci.nsIStackFrame)) { 24 | stack = Components.stack; 25 | for (let i = 0; stack && i < 2; ++i) { 26 | stack = stack.caller; 27 | } 28 | if (!stack) { 29 | return UNKNOWN_STACK; 30 | } 31 | } 32 | let rv = {}; 33 | rv.sourceName = stack.filename; 34 | rv.sourceLine = stack.sourceLine; 35 | rv.lineNumber = stack.lineNumber; 36 | let message = []; 37 | for (let i = 0; stack && i < 6; ++i, stack = stack.caller) { 38 | if (stack.lineNumber) { 39 | message.push("\t" + (stack.name || "[anonymous]") + "() @ " + 40 | stack.filename + ":" + stack.lineNumber); 41 | } 42 | else { 43 | message.push("\t[native @ " + (stack.languageName || "???" ) + "]"); 44 | } 45 | } 46 | rv.stackMsg = message.join("\n"); 47 | rv.prototype = UNKNOWN_STACK; 48 | return rv; 49 | } 50 | 51 | const { 52 | errorFlag, 53 | warningFlag 54 | } = Ci.nsIScriptError; 55 | 56 | Object.defineProperties(exports, { 57 | LOG_DEBUG: {value: 0, enumerable: true}, 58 | LOG_INFO: {value: 1, enumerable: true}, 59 | LOG_ERROR: {value: 2, enumerable: true}, 60 | LOG_NONE: {value: 0x7FFFFFFF}, 61 | PREFIX: {get: () => prefix}, 62 | setLogLevel: {value: l => global.level = l} 63 | }); 64 | 65 | var prefix = ADDON.name; 66 | global.level = exports.LOG_NONE; 67 | 68 | exports.log = function(level, message, exception) { 69 | try { 70 | if (global.level > level) { 71 | return; 72 | } 73 | 74 | if (message instanceof Ci.nsIScriptError || 75 | message instanceof Ci.nsIException || message.fileName) { 76 | exception = message; 77 | message = exception.message; 78 | } 79 | else if (exception) { 80 | message = message + " [Exception: " + exception.message + "]"; 81 | } 82 | 83 | let { 84 | stackMsg, 85 | sourceName, 86 | sourceLine, 87 | lineNumber, 88 | columnNumber 89 | } = prepareStack((exception && exception.location) || null); 90 | 91 | if (stackMsg) { 92 | message += "\n" + stackMsg; 93 | } 94 | 95 | let category = "component javascript"; 96 | 97 | if (exception) { 98 | if (exception instanceof Ci.nsIScriptError) { 99 | sourceName = exception.sourceName; 100 | sourceLine = exception.sourceLine; 101 | lineNumber = exception.lineNumber; 102 | columnNumber = exception.columnNumber; 103 | category = exception.category; 104 | } 105 | else if (exception instanceof Ci.nsIException) { 106 | sourceName = exception.filename; 107 | lineNumber = exception.lineNumber; 108 | } 109 | else { 110 | sourceName = exception.fileName || sourceName; 111 | lineNumber = exception.lineNumber || lineNumber; 112 | } 113 | } 114 | 115 | let levelMsg; 116 | switch (level) { 117 | case exports.LOG_ERROR: 118 | levelMsg = "error"; 119 | break; 120 | case exports.LOG_INFO: 121 | levelMsg = "info"; 122 | break; 123 | default: 124 | levelMsg = "debug"; 125 | } 126 | 127 | message = global.prefix + " (" + levelMsg + ") - " + message; 128 | 129 | const scriptError = new Instances.ScriptError( 130 | message, 131 | sourceName, 132 | sourceLine, 133 | lineNumber, 134 | columnNumber, 135 | level >= exports.LOG_ERROR ? errorFlag : warningFlag, 136 | category); 137 | Services.console.logMessage(scriptError); 138 | } 139 | catch (ex) { 140 | reportError("failed to log"); 141 | reportError(ex); 142 | reportError(exception || message); 143 | } 144 | }; 145 | 146 | /* vim: set et ts=2 sw=2 : */ 147 | -------------------------------------------------------------------------------- /sdk/observers.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 file, 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | "use strict"; 5 | 6 | const manager = { 7 | topics: Object.create(null), 8 | observe: function observe(subject, topic, data) { 9 | let observers = this.topics[topic]; 10 | if (!observers) { 11 | return; 12 | } 13 | for (let i = observers.length; ~(--i);) { 14 | try { 15 | observers[i].call(null, subject, topic, data); 16 | } 17 | catch (ex) { 18 | log(LOG_ERROR, "observer threw", ex); 19 | } 20 | } 21 | }, 22 | add: function add(topic, cb) { 23 | try { 24 | this.topics[topic].push(cb); 25 | } 26 | catch (ex) { 27 | this.topics[topic] = [cb]; 28 | Services.obs.addObserver(this, topic, false); 29 | } 30 | }, 31 | remove: function remove(topic, cb) { 32 | let observers = this.topics[topic]; 33 | if (!observers) { 34 | log(LOG_ERROR, 35 | "tried to remove a non-existant observer for topic: " + topic); 36 | return; 37 | } 38 | observers = this.topics[topic] = observers.filter(fn => fn != cb); 39 | if (!observers.length) { 40 | delete this.topics[topic]; 41 | Services.obs.removeObserver(this, topic, false); 42 | } 43 | }, 44 | teardown: function teardown() { 45 | for (let k in this.topics) { 46 | this.topics[k].splice(0); 47 | delete this.topics[k]; 48 | Services.obs.removeObserver(this, k, false); 49 | } 50 | } 51 | }; 52 | unload(() => manager.teardown()); 53 | 54 | Object.defineProperties(exports, { 55 | add: {value: (topic, cb) => manager.add(topic, cb)}, 56 | remove: {value: (topic, cb) => manager.remove(topic, cb)} 57 | }); 58 | 59 | /* vim: set et ts=2 sw=2 : */ 60 | -------------------------------------------------------------------------------- /sdk/preferences.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 file, 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | "use strict"; 5 | 6 | /* This implementation was heavily inspired by the add-on SDK stuff ;) 7 | * 8 | * TODO: 9 | * - Implement enumerate/getChildren 10 | */ 11 | 12 | try { 13 | Instances.register("SupportsString", "@mozilla.org/supports-string;1", "nsISupportsString"); 14 | } 15 | catch (ex) { 16 | // ignore 17 | } 18 | 19 | const { 20 | PREF_INVALID: INVALID, 21 | PREF_STRING: STR, 22 | PREF_INT: INT, 23 | PREF_BOOL: BOOL 24 | } = Ci.nsIPrefBranch; 25 | 26 | function createProxy(branch) { 27 | return new Proxy(branch, { 28 | get: function(receiver, name) { 29 | if (name in branch) { 30 | log(LOG_DEBUG, "prefproxy: returning plain " + name); 31 | return branch[name]; 32 | } 33 | log(LOG_DEBUG, "prefproxy: returning pref " + branch.branch + name); 34 | return branch.get(name); 35 | }, 36 | set: function(receiver, name, value) { 37 | if (name in branch) { 38 | throw new Error("Cannot use this name as a preference"); 39 | } 40 | log(LOG_DEBUG, "prefproxy: setting pref " + branch.branch + name); 41 | branch.set(name, value); 42 | }, 43 | delete: function(name) { 44 | if (name in branch) { 45 | throw new Error("Cannot use this name as a preference"); 46 | } 47 | branch.delete(name); 48 | }, 49 | has: function(name) { 50 | if (name in branch) { 51 | throw new Error("Cannot use this name as a preference"); 52 | } 53 | log(LOG_DEBUG, "prefproxy: has pref " + branch.branch + name); 54 | return branch.has(name); 55 | } 56 | }); 57 | } 58 | 59 | function Branch(branch) { 60 | if (typeof(branch) == "string") { 61 | if (!/\.$|^$/.test(branch)) { 62 | branch += "."; 63 | } 64 | branch = Services.prefs.getBranch(branch); 65 | if ("nsIPrefBranch2" in Ci) { 66 | branch.QueryInterface(Ci.nsIPrefBranch2); 67 | } 68 | } 69 | this.branch = branch.root; 70 | 71 | let getType = this.getType = pref => branch.getPrefType(pref); 72 | this.has = pref => getType(pref) != INVALID; 73 | this.isChanged = pref => branch.prefHasUserValue(pref); 74 | this.isDefault = pref => !this[pref].isChanged(); 75 | let get = this.get = function(pref, defaultValue) { 76 | switch (getType(pref)) { 77 | case STR: 78 | return branch.getComplexValue(pref, Ci.nsISupportsString).data; 79 | case INT: 80 | return branch.getIntPref(pref); 81 | case BOOL: 82 | return branch.getBoolPref(pref); 83 | default: 84 | if (typeof(defaultValue) != "undefined") { 85 | return defaultValue; 86 | } 87 | log(LOG_DEBUG, "creating subbranch for " + branch.root + pref); 88 | return createProxy(new Branch(branch.root + pref)); 89 | } 90 | }; 91 | this.set = function(pref, value) { 92 | if (value === null || value === undefined) { 93 | log(LOG_DEBUG, "ignoring null value for pref " + pref); 94 | return; 95 | } 96 | switch (value.constructor.name) { 97 | case "Number": 98 | if (!isFinite(value)) { 99 | let msg = "attempt to set an invalid number to pref " + branch.root + 100 | pref + ": " + value; 101 | log(LOG_DEBUG, msg); 102 | throw new Error(msg); 103 | } 104 | if (value % 1) { 105 | log(LOG_DEBUG, "coercing float to int before setting pref " + 106 | branch.root + pref + ": " + value); 107 | value = parseInt(value); 108 | } 109 | branch.setIntPref(pref, value); 110 | break; 111 | 112 | case "Boolean": 113 | branch.setBoolPref(pref, value); 114 | break; 115 | 116 | default: 117 | log(LOG_DEBUG, "coercing object into string before setting pref " + 118 | branch.root + pref); 119 | value = value.toString(); 120 | // fall through 121 | 122 | case "String": 123 | { 124 | let str = new Instances.SupportsString(); 125 | str.data = value; 126 | branch.setComplexValue(pref, Ci.nsISupportsString, str); 127 | } 128 | break; 129 | } 130 | }; 131 | this.delete = function(pref) { 132 | branch.clearUserPref(pref); 133 | }; 134 | this.observe = function(pref, callback, defaultValue) { 135 | let obs = { 136 | observe: function(s, t, d) { 137 | [s, t, d] = [s, t, d]; 138 | callback(pref, get(pref, defaultValue)); 139 | } 140 | }; 141 | branch.addObserver(pref, obs, false); 142 | unload(() => branch.removeObserver(pref, obs)); 143 | obs.observe(); 144 | }; 145 | } 146 | 147 | (function setDefaultPrefs() { 148 | let branch = new Branch(Services.prefs.getDefaultBranch("")); 149 | let scope = { 150 | pref: (key, val) => { 151 | try { 152 | branch.set(key, val); 153 | } 154 | catch (ex) { 155 | Cu.reportError(ex); 156 | } 157 | } 158 | }; 159 | try { 160 | Services.scriptloader.loadSubScript(BASE_PATH + 161 | "defaults/preferences/prefs.js", scope); 162 | } 163 | // errors here should not kill addon 164 | catch (ex) { 165 | log(LOG_ERROR, "failed to setup default preferences", ex); 166 | } 167 | })(); 168 | 169 | var globalPrefs = createProxy(new Branch("")); 170 | var prefs = globalPrefs.extensions[ADDON.id]; 171 | 172 | Object.defineProperties(exports, { 173 | prefs: {get: () => prefs, enumerable: true}, 174 | globalPrefs: {value: globalPrefs, enumerable: true} 175 | }); 176 | 177 | /* vim: set et ts=2 sw=2 : */ 178 | -------------------------------------------------------------------------------- /sdk/request.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 file, 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | "use strict"; 5 | 6 | Cu.importGlobalProperties(["XMLHttpRequest", "Blob", "File"]); 7 | 8 | exports.Blob = Blob; 9 | exports.File = File; 10 | exports.xhr = exports.XMLHttpRequest = XMLHttpRequest; 11 | 12 | function Callbacks(underlay) { 13 | try { 14 | this.underlay = underlay.QueryInterface(Ci.nsIInterfaceRequestor); 15 | } 16 | catch (ex) { 17 | this.underlay = null; 18 | } 19 | } 20 | Callbacks.prototype = { 21 | badHost: null, 22 | getInterface: function(iid) { 23 | try { 24 | return this.QueryInterface(iid); 25 | } 26 | catch (ex) { 27 | if (this.underlay) { 28 | return this.underlay.getInterface(iid); 29 | } 30 | throw ex; 31 | } 32 | }, 33 | QueryInterface: XPCOMUtils.generateQI([Ci.nsIBadCertListener2, Ci.nsIInterfaceRequestor]), 34 | notifyCertProblem: function(si, status, host) { 35 | this.badHost = host; 36 | return false; 37 | } 38 | }; 39 | 40 | exports.request = (method, url, data, options) => { 41 | options = options || {}; 42 | return new Promise((resolve, reject) => { 43 | let req = new XMLHttpRequest(); 44 | let cbs = null; 45 | try { 46 | url = url.spec || url; 47 | if (method == "POST" && !data) { 48 | let [u, ...pieces] = url.split("?"); 49 | url = u; 50 | data = pieces.length && pieces.join("?") || null; 51 | } 52 | req.timeout = options.timeout || 10000; 53 | if (options.mime) { 54 | req.overrideMimeType(options.mime); 55 | } 56 | if (options.type) { 57 | req.responseType = options.type; 58 | } 59 | else { 60 | req.responseType = "json"; 61 | } 62 | if (options.setup) { 63 | options.setup(req); 64 | } 65 | req.addEventListener("loadend", function end() { 66 | req.removeEventListener("loadend", end, false); 67 | try { 68 | if (req.status < 100 || req.status >= 400) { 69 | if (cbs && cbs.badHost) { 70 | throw new Error(`${cbs.badHost} has an invalid TLS Certificate`); 71 | } 72 | throw new Error("Bad response"); 73 | } 74 | log(LOG_DEBUG, `request to ${url} resolved`); 75 | resolve(req); 76 | } 77 | catch (ex) { 78 | log(LOG_DEBUG, `request to ${url} rejected ${ex}`); 79 | reject(ex); 80 | } 81 | }, false); 82 | log(LOG_DEBUG, `Making request to ${url} w/ data ${data}`); 83 | req.open(method, url); 84 | if (req.channel) { 85 | try { 86 | cbs = new Callbacks(req.channel.notificationCallbacks); 87 | req.channel.notificationCallbacks = cbs; 88 | } 89 | catch (ex) { 90 | log(LOG_DEBUG, "Failed to set callbacks", ex); 91 | } 92 | if (req.channel instanceof Ci.nsIHttpChannel) { 93 | if (options.cookies) { 94 | for (let n of Object.keys(options.cookies)) { 95 | req.channel.setRequestHeader("Cookie", `${encodeURIComponent(n)}=${encodeURIComponent(options.cookies[n])}`, true); 96 | } 97 | } 98 | if (options.headers) { 99 | for (let n of Object.keys(options.headers)) { 100 | let h = options.headers[n]; 101 | let merge = true; 102 | if (Array.isArray(h)) { 103 | merge = !!h[1]; 104 | h = h[0]; 105 | } 106 | req.channel.setRequestHeader(n, h, merge); 107 | } 108 | } 109 | } 110 | } 111 | req.send(data || null); 112 | } 113 | catch (ex) { 114 | reject(ex); 115 | } 116 | }); 117 | }; 118 | 119 | /* vim: set et ts=2 sw=2 : */ 120 | -------------------------------------------------------------------------------- /sdk/strings.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 file, 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | "use strict"; 5 | 6 | var bundleFlush = null; 7 | 8 | function Bundle(uri) { 9 | lazy(this, "_bundle", function() { 10 | if (!bundleFlush) { 11 | bundleFlush = unload(() => Services.strings.flushBundles()); 12 | } 13 | log(LOG_DEBUG, `loading bundle ${uri}`); 14 | return Services.strings.createBundle(uri); 15 | }); 16 | this._dict = Object.create(null); 17 | } 18 | Bundle.prototype = { 19 | getString: function getString(id /*, args */) { 20 | if (arguments.length > 1) { 21 | let args = Array.slice(arguments, 1); 22 | return this._bundle.formatStringFromName(id, args, args.length); 23 | } 24 | return this._bundle.GetStringFromName(id); 25 | } 26 | }; 27 | 28 | Object.defineProperty(exports, "getBundle", {value: uri => { 29 | let bundle = new Bundle(uri); 30 | let rv = bundle.getString.bind(bundle); 31 | rv.getString = rv; 32 | return rv; 33 | }}); 34 | 35 | /* vim: set et ts=2 sw=2 : */ 36 | -------------------------------------------------------------------------------- /sdk/timers.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 file, 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | "use strict"; 5 | 6 | Instances.register("Timer", "@mozilla.org/timer;1", "nsITimer", "init"); 7 | 8 | const { 9 | TYPE_ONE_SHOT, 10 | TYPE_REPEATING_SLACK 11 | } = Ci.nsITimer; 12 | 13 | const CLAMP = globalPrefs.dom.min_timeout_value; 14 | 15 | function uuid() { 16 | return Services.uuid.generateUUID().toString(); 17 | } 18 | 19 | let timers = Object.create(Object.prototype, { 20 | create: {value: function(type, func, delay, args) { 21 | let id = uuid(); 22 | let timer = new Instances.Timer({ 23 | observe: function() { 24 | log(LOG_DEBUG, "timer " + id + "fired delay: " + delay + 25 | " type:" + type); 26 | try { 27 | func.apply(null, args); 28 | } 29 | finally { 30 | if (type == TYPE_ONE_SHOT) { 31 | delete this[id]; 32 | } 33 | } 34 | } 35 | }, delay, type); 36 | this[id] = timer; 37 | log(LOG_DEBUG, "timer " + id + " created delay: " + delay + 38 | " type:" + type); 39 | return id; 40 | }}, 41 | destroy: {value: function(id) { 42 | if (id in this) { 43 | try { 44 | this[id].cancel(); 45 | } catch (ex) {} 46 | delete this[id]; 47 | log(LOG_DEBUG, "timer " + id + " destroyed"); 48 | } 49 | }} 50 | }); 51 | unload(function unload_timers() { 52 | for (let [id,timer] in new Iterator(timers)) { 53 | try { 54 | timer.cancel(); 55 | } catch (ex) {} 56 | delete timers[id]; 57 | } 58 | }); 59 | 60 | Object.defineProperties(exports, { 61 | createTimeout: { 62 | value: function createTimeout(func, delay /*, ... */) { 63 | let args = Array.slice(2); 64 | return timers.create(TYPE_ONE_SHOT, func, Math.max(delay, CLAMP), args); 65 | }, 66 | enumerable: true 67 | }, 68 | createInterval: { 69 | value: function createInterval(func, delay /*, ... */) { 70 | let args = Array.slice(2); 71 | return timers.create(TYPE_REPEATING_SLACK, func, 72 | Math.max(delay, CLAMP), args); 73 | }, 74 | enumerable: true 75 | }, 76 | defer: { 77 | value: function defer(func /*. ... */) { 78 | let args = Array.slice(1); 79 | return timers.create(TYPE_ONE_SHOT, func, 0, args); 80 | }, 81 | enumerable: true 82 | }, 83 | destroy: { 84 | value: id => timers.destroy(id), 85 | enumerable: true 86 | } 87 | }); 88 | Object.freeze(exports); 89 | 90 | /* vim: set et ts=2 sw=2 : */ 91 | -------------------------------------------------------------------------------- /sdk/windows.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 file, 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | "use strict"; 5 | 6 | Instances.register("XHR", 7 | "@mozilla.org/xmlextras/xmlhttprequest;1", 8 | "nsIXMLHttpRequest"); 9 | 10 | /** 11 | * Specialized unloader that will trigger whenever either the window gets 12 | * unloaded or the add-on is shut down 13 | */ 14 | exports.unloadWindow = function unloadWindow(window, fn) { 15 | let handler = unload(function() { 16 | window.removeEventListener('unload', handler, false); 17 | try { 18 | fn(); 19 | } 20 | catch (ex) { 21 | log(LOG_ERROR, "failed to run window unloader", ex); 22 | } 23 | }); 24 | window.addEventListener('unload', handler, false); 25 | }; 26 | 27 | /** 28 | * Apply a callback to each open and new browser windows. 29 | */ 30 | exports.watchWindows = function watchWindows(location, callback) { 31 | // Wrap the callback in a function that ignores failures 32 | function watcher(window) { 33 | log(LOG_DEBUG, "watchwindows watcher"); 34 | try { 35 | callback(window, window.document); 36 | } 37 | catch(ex) { 38 | log(LOG_ERROR, "window watcher failed", ex); 39 | } 40 | } 41 | 42 | // Wait for the window to finish loading before running the callback 43 | function runOnLoad(window) { 44 | // Listen for one load event before checking the window type 45 | window.addEventListener("load", function runOnLoad_load() { 46 | window.removeEventListener("load", runOnLoad_load, false); 47 | 48 | // Now that the window has loaded, only handle requested windows 49 | if (window.location == location) { 50 | watcher(window); 51 | } 52 | else { 53 | log(LOG_DEBUG, "skipping window: " + window.location + 54 | " as another location was requested: " + location); 55 | } 56 | }, false); 57 | } 58 | 59 | // Add functionality to existing windows 60 | let windows = Services.wm.getEnumerator(null); 61 | while (windows.hasMoreElements()) { 62 | // Only run the watcher immediately if the browser is completely loaded 63 | let window = windows.getNext(); 64 | if (window.document.readyState == "complete") { 65 | if (window.location == location) { 66 | watcher(window); 67 | } 68 | else { 69 | log(LOG_DEBUG, "skipping early window: " + window.location + 70 | " as another location was requested: " + location); 71 | } 72 | } 73 | // Wait for the window to load before continuing 74 | else { 75 | runOnLoad(window); 76 | } 77 | } 78 | 79 | // Watch for new browser windows opening then wait for it to load 80 | function windowWatcher(subject, topic) { 81 | if (topic == "domwindowopened") { 82 | runOnLoad(subject); 83 | } 84 | } 85 | Services.ww.registerNotification(windowWatcher); 86 | 87 | // Make sure to stop watching for windows if we're unloading 88 | unload(() => Services.ww.unregisterNotification(windowWatcher)); 89 | }; 90 | 91 | /** 92 | * Register a new overlay (XUL) 93 | */ 94 | exports.registerOverlay = function registerOverlay(src, location, callback) { 95 | function inject(xul, window, document) { 96 | function $(id) { 97 | return document.getElementById(id); 98 | } 99 | function $$(q) { 100 | return document.querySelector(q); 101 | } 102 | 103 | // loadOverlay for the poor 104 | function addNode(target, node) { 105 | // helper: insert according to position 106 | function insertX(nn, attr, callback) { 107 | if (!nn.hasAttribute(attr)) { 108 | return false; 109 | } 110 | let places = nn.getAttribute(attr) 111 | .split(',') 112 | .map(p => p.trim()) 113 | .filter(p => !!p); 114 | for (let p of places) { 115 | let pn = $$('#' + target.id + ' > #' + p); 116 | if (!pn) { 117 | continue; 118 | } 119 | callback(pn); 120 | return true; 121 | } 122 | return false; 123 | } 124 | 125 | // bring the node to be inserted into the document 126 | let nn = document.importNode(node, true); 127 | 128 | // try to insert according to insertafter/before 129 | if (insertX(nn, 'insertafter', 130 | pn => pn.parentNode.insertBefore(nn, pn.nextSibling)) || 131 | insertX(nn, 'insertbefore', 132 | pn => pn.parentNode.insertBefore(nn, pn))) { 133 | } 134 | // just append 135 | else { 136 | target.appendChild(nn); 137 | } 138 | return nn; 139 | } 140 | log(LOG_DEBUG, "gonna stuff: " + src + " into: " + location); 141 | 142 | try { 143 | // store unloaders for all elements inserted 144 | let unloaders = []; 145 | 146 | // Add all overlays 147 | for (let id in xul) { 148 | let target = $(id); 149 | if (!target) { 150 | log(LOG_INFO, "no target for " + id + ", not inserting"); 151 | continue; 152 | } 153 | 154 | // insert all children 155 | for (let n = xul[id].firstChild; n; n = n.nextSibling) { 156 | if (n.nodeType != n.ELEMENT_NODE) { 157 | continue; 158 | } 159 | let nn = addNode(target, n); 160 | unloaders.push(() => nn.parentNode.removeChild(nn)); 161 | } 162 | } 163 | // install per-window unloader 164 | if (unloaders.length) { 165 | exports.unloadWindow(window, () => unloaders.forEach(u => u())); 166 | } 167 | 168 | callback && callback(window, document); 169 | } 170 | catch (ex) { 171 | log(LOG_ERROR, "failed to inject xul", ex); 172 | } 173 | } 174 | 175 | let _r = new Instances.XHR(); 176 | _r.onload = function() { 177 | log(LOG_DEBUG, "loaded: " + src); 178 | let document = _r.responseXML; 179 | 180 | // clean the document a bit 181 | let emptyNodes = document.evaluate( 182 | "//text()[normalize-space(.) = '']", document, null, 7, null); 183 | for (let i = 0, e = emptyNodes.snapshotLength; i < e; ++i) { 184 | let n = emptyNodes.snapshotItem(i); 185 | n.parentNode.removeChild(n); 186 | } 187 | 188 | // prepare all elements to be inserted 189 | let xul = {}; 190 | for (let n = document.documentElement.firstChild; n; n = n.nextSibling) { 191 | if (n.nodeType != n.ELEMENT_NODE || !n.hasAttribute("id")) { 192 | continue; 193 | } 194 | let id = n.getAttribute("id"); 195 | xul[id] = n; 196 | } 197 | if (!Object.keys(xul).length) { 198 | log(LOG_INFO, "There is only XUL ... but there wasn't"); 199 | return; 200 | } 201 | exports.watchWindows(location, inject.bind(null, xul)); 202 | }; 203 | _r.onerror = _r.onabort = function() { 204 | log(LOG_ERROR, "Failed to load " + src); 205 | } 206 | _r.overrideMimeType("application/xml"); 207 | _r.open("GET", BASE_PATH + src); 208 | // Elevate the request, so DTDs will work. Not a security issue since we 209 | // always load from BASE_PATH, and that is our privileged chrome package. 210 | // This is no different than regular overlays. 211 | let sec = Cc['@mozilla.org/scriptsecuritymanager;1']. 212 | getService(Ci.nsIScriptSecurityManager); 213 | try { 214 | _r.channel.owner = sec.getSystemPrincipal(); 215 | } 216 | catch (ex) { 217 | log(LOG_ERROR, "Failed to set system principal"); 218 | } 219 | _r.send(); 220 | }; 221 | 222 | /* vim: set et ts=2 sw=2 : */ 223 | -------------------------------------------------------------------------------- /sv-SE/about.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /zh-CN/about.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /zh-TW/about.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | --------------------------------------------------------------------------------