[Pkg-mozext-commits] [requestpolicy] 272/280: Imported Upstream version 1.0.0~beta9.1+dfsg
David Prévot
taffit at moszumanska.debian.org
Sat May 2 20:30:38 UTC 2015
This is an automated email from the git hooks/post-receive script.
taffit pushed a commit to branch master
in repository requestpolicy.
commit 0e84f398a39a8f415cdad40b44bd7715f52eaed7
Merge: eeb28da c295bff
Author: David Prévot <taffit at debian.org>
Date: Sat May 2 14:57:42 2015 -0400
Imported Upstream version 1.0.0~beta9.1+dfsg
LICENSE | 526 +++++-
META-INF/manifest.mf | 702 +++++++-
META-INF/zigbert.rsa | Bin 3293 -> 3293 bytes
META-INF/zigbert.sf | 702 +++++++-
README | 2 +-
bootstrap.js | 78 +
chrome.manifest | 58 +-
chrome/requestpolicy.jar!/content/menu.js | 1208 --------------
chrome/requestpolicy.jar!/content/overlay.js | 1750 --------------------
chrome/requestpolicy.jar!/content/overlay.xul | 155 --
chrome/requestpolicy.jar!/content/requestLog.js | 99 --
.../content/requestLogTreeView.js | 249 ---
.../content/settings/advancedprefs.js | 141 --
.../content/settings/basicprefs.js | 84 -
.../content/settings/defaultpolicy.js | 82 -
.../requestpolicy.jar!/locale/de/requestpolicy.dtd | 20 -
.../locale/de/requestpolicy.properties | 109 --
.../locale/en-US/requestpolicy.dtd | 20 -
.../requestpolicy.jar!/locale/eo/requestpolicy.dtd | 20 -
.../locale/es-MX/requestpolicy.dtd | 20 -
.../requestpolicy.jar!/locale/eu/requestpolicy.dtd | 20 -
.../requestpolicy.jar!/locale/fr/requestpolicy.dtd | 20 -
.../requestpolicy.jar!/locale/it/requestpolicy.dtd | 20 -
.../requestpolicy.jar!/locale/ja/requestpolicy.dtd | 20 -
.../locale/ja/requestpolicy.properties | 109 --
.../locale/ko-KR/requestpolicy.dtd | 20 -
.../locale/lv-LV/requestpolicy.dtd | 20 -
.../requestpolicy.jar!/locale/nl/requestpolicy.dtd | 20 -
.../locale/pt-BR/requestpolicy.dtd | 20 -
.../locale/ru-RU/requestpolicy.dtd | 20 -
.../locale/sk-SK/requestpolicy.dtd | 20 -
.../locale/sv-SE/requestpolicy.dtd | 20 -
.../requestpolicy.jar!/locale/tr/requestpolicy.dtd | 20 -
.../locale/uk-UA/requestpolicy.dtd | 20 -
.../locale/zh-CN/requestpolicy.dtd | 20 -
.../locale/zh-TW/requestpolicy.dtd | 20 -
components/requestpolicyService.js | 1137 -------------
.../lib/default-preferences.js | 2 +
content/lib/environment.jsm | 517 ++++++
content/lib/environment.process.js | 217 +++
.../lib/framescript-to-overlay-communication.jsm | 166 ++
.../lib/gui-location.jsm | 36 +-
content/lib/http-response.jsm | 182 ++
content/lib/logger.jsm | 191 +++
content/lib/manager-for-event-listeners.jsm | 153 ++
content/lib/manager-for-message-listeners.jsm | 187 +++
content/lib/observer-manager.jsm | 184 ++
content/lib/policy-manager.alias-functions.js | 99 ++
.../lib/policy-manager.jsm | 251 +--
content/lib/prefs.jsm | 150 ++
content/lib/request-processor.compat.js | 321 ++++
content/lib/request-processor.jsm | 1076 ++++++++++++
content/lib/request-processor.redirects.js | 414 +++++
.../lib/request-result.jsm | 10 +-
content/lib/request-set.jsm | 242 +++
modules/Request.jsm => content/lib/request.jsm | 191 +--
.../lib/ruleset-storage.jsm | 42 +-
modules/Ruleset.jsm => content/lib/ruleset.jsm | 186 ++-
content/lib/script-loader.jsm | 210 +++
.../lib/subscription.jsm | 48 +-
content/lib/utils.jsm | 193 +++
content/lib/utils/constants.jsm | 55 +
content/lib/utils/dom.jsm | 51 +
.../lib/utils/domains.jsm | 157 +-
.../FileUtil.jsm => content/lib/utils/files.jsm | 104 +-
content/lib/utils/observers.jsm | 93 ++
content/lib/utils/strings.jsm | 75 +
content/lib/utils/windows.jsm | 148 ++
content/lib/utils/xul.jsm | 176 ++
content/main/about-uri.jsm | 130 ++
content/main/content-policy.jsm | 188 +++
content/main/default-pref-handler.js | 115 ++
content/main/pref-manager.jsm | 146 ++
content/main/requestpolicy-service.jsm | 234 +++
content/main/window-manager-toolbarbutton.js | 153 ++
content/main/window-manager.jsm | 238 +++
content/main/window-manager.listener.js | 151 ++
.../settings/advancedprefs.html | 45 +-
content/settings/advancedprefs.js | 134 ++
.../content => content}/settings/basicprefs.html | 27 +-
content/settings/basicprefs.js | 80 +
.../content => content}/settings/common.js | 100 +-
.../settings/defaultpolicy.html | 42 +-
content/settings/defaultpolicy.js | 80 +
.../content => content}/settings/oldrules.html | 29 +-
.../content => content}/settings/oldrules.js | 39 +-
.../content => content}/settings/settings.css | 0
.../content => content}/settings/setup.css | 0
.../content => content}/settings/setup.html | 15 +-
.../content => content}/settings/setup.js | 74 +-
.../settings/subscriptions.html | 43 +-
.../content => content}/settings/subscriptions.js | 40 +-
.../content => content}/settings/yourpolicy.html | 39 +-
.../content => content}/settings/yourpolicy.js | 80 +-
.../content => content/ui}/classicmenu.js | 105 +-
content/ui/frame.blocked-content.js | 97 ++
content/ui/frame.dom-content-loaded.js | 316 ++++
content/ui/frame.js | 165 ++
content/ui/menu.js | 1216 ++++++++++++++
content/ui/overlay.js | 1225 ++++++++++++++
content/ui/request-log.filtering.js | 111 ++
content/ui/request-log.interface.js | 135 ++
content/ui/request-log.js | 82 +
content/ui/request-log.tree-view.js | 186 +++
.../requestLog.xul => content/ui/request-log.xul | 44 +-
content/ui/xul-trees.js | 186 +++
install.rdf | 17 +-
locale/de/requestpolicy.dtd | 12 +
locale/de/requestpolicy.properties | 124 ++
locale/en-US/requestpolicy.dtd | 12 +
.../en-US/requestpolicy.properties | 17 +-
locale/eo/requestpolicy.dtd | 12 +
.../locale => locale}/eo/requestpolicy.properties | 17 +-
locale/es-MX/requestpolicy.dtd | 12 +
.../es-MX/requestpolicy.properties | 17 +-
locale/eu/requestpolicy.dtd | 12 +
.../locale => locale}/eu/requestpolicy.properties | 17 +-
locale/fr/requestpolicy.dtd | 12 +
.../locale => locale}/fr/requestpolicy.properties | 17 +-
locale/it/requestpolicy.dtd | 12 +
.../locale => locale}/it/requestpolicy.properties | 17 +-
locale/ja/requestpolicy.dtd | 12 +
locale/ja/requestpolicy.properties | 124 ++
locale/ko-KR/requestpolicy.dtd | 12 +
.../ko-KR/requestpolicy.properties | 17 +-
locale/lv-LV/requestpolicy.dtd | 12 +
.../lv-LV/requestpolicy.properties | 17 +-
locale/nl/requestpolicy.dtd | 12 +
.../locale => locale}/nl/requestpolicy.properties | 17 +-
locale/pt-BR/requestpolicy.dtd | 12 +
.../pt-BR/requestpolicy.properties | 17 +-
locale/ru-RU/requestpolicy.dtd | 12 +
.../ru-RU/requestpolicy.properties | 17 +-
locale/sk-SK/requestpolicy.dtd | 12 +
.../sk-SK/requestpolicy.properties | 17 +-
locale/sv-SE/requestpolicy.dtd | 12 +
.../sv-SE/requestpolicy.properties | 17 +-
locale/tr/requestpolicy.dtd | 12 +
.../locale => locale}/tr/requestpolicy.properties | 17 +-
locale/uk-UA/requestpolicy.dtd | 12 +
.../uk-UA/requestpolicy.properties | 17 +-
locale/zh-CN/requestpolicy.dtd | 12 +
.../zh-CN/requestpolicy.properties | 17 +-
locale/zh-TW/requestpolicy.dtd | 12 +
.../zh-TW/requestpolicy.properties | 17 +-
modules/JSON.jsm | 81 -
modules/Logger.jsm | 117 --
modules/Prompter.jsm | 33 -
modules/RequestProcessor.jsm | 1209 --------------
modules/RequestUtil.jsm | 426 -----
modules/Services.jsm | 31 -
modules/Util.jsm | 66 -
{chrome/requestpolicy.jar!/skin => skin}/close.png | Bin
{chrome/requestpolicy.jar!/skin => skin}/dot.png | Bin
.../initialSetup.css => skin/initial-setup.css | 0
.../skin => skin}/menu-allowed.svg | 0
.../skin => skin}/menu-blocked.svg | 0
.../skin => skin}/menu-default.svg | 0
.../skin => skin}/menu-other-origins.png | Bin
.../skin/requestLog.css => skin/request-log.css | 0
.../requestpolicy-icon-24-allowed.png | Bin
.../requestpolicy-icon-24-blocked.png | Bin
.../requestpolicy-icon-24-disabled.png | Bin
.../requestpolicy-icon-32-allowed.png | Bin
.../requestpolicy-icon-32-blocked.png | Bin
.../requestpolicy-icon-32-disabled.png | Bin
.../skin => skin}/requestpolicy-icon-32.png | Bin
.../skin => skin}/requestpolicy-icon-allowed.png | Bin
.../skin => skin}/requestpolicy-icon-blocked.png | Bin
.../skin => skin}/requestpolicy-icon-disabled.png | Bin
.../requestpolicy-statusbar-allowed.png | Bin
.../requestpolicy-statusbar-blocked.png | Bin
.../requestpolicy-statusbar-disabled.png | Bin
.../skin => skin}/requestpolicy.css | 98 --
skin/toolbarbutton-seamonkey.css | 64 +
skin/toolbarbutton.css | 164 ++
176 files changed, 14258 insertions(+), 8601 deletions(-)
diff --cc META-INF/manifest.mf
index b8545ef,0000000..18d3eff
mode 100644,000000..100644
--- a/META-INF/manifest.mf
+++ b/META-INF/manifest.mf
@@@ -1,118 -1,0 +1,688 @@@
+Manifest-Version: 1.0
+Created-By: Signtool (signtool 3.17.2 Basic ECC)
+Comments: PLEASE DO NOT EDIT THIS FILE. YOU WILL BREAK IT.
+
- Name: chrome/requestpolicy.jar
++Name: content/main/pref-manager.jsm
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: aTjT/ygvKwjupz4DwBpKmg==
- SHA1-Digest: wYEM74kZPp0rX4AN6RZ0OcQg9yE=
++MD5-Digest: qaEcU+XGZ+q5kKuX49xaMA==
++SHA1-Digest: FZ93kmh7wZrNYKjHVo3sWZ6xriU=
+
- Name: defaults/preferences/defaults.js
++Name: content/main/about-uri.jsm
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: IBsOABUBNIaD8y4JGVVSRg==
- SHA1-Digest: lgTc31x9iqv30DnEghQhjhkjF+I=
++MD5-Digest: XvCAmgDFnHjenEV3Nx3Cgg==
++SHA1-Digest: 7Q+avDVT3L6PCg59jtEw8IpYcvk=
+
- Name: README
++Name: content/main/requestpolicy-service.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: RF+r7QMFgTjC5U9upJ65BA==
++SHA1-Digest: BIgNH0hOKfjqghGNB18C2qQOcSo=
++
++Name: content/main/window-manager.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: dwPnAYf9Eh+nQdqQZHx0ZQ==
++SHA1-Digest: qoGBZCC018L11tsM1KzF8vnThlE=
++
++Name: content/main/window-manager.listener.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: miZk3iC65O67HodkGln9Vg==
++SHA1-Digest: hkdB7YXsN0FHIrRt7SnuYxYb3qs=
++
++Name: content/main/window-manager-toolbarbutton.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: I7wn/EAyyp2XFXv0c1eZvQ==
++SHA1-Digest: ME2Iz/dCWuYyzAUjgwc2Ie+qsts=
++
++Name: content/main/default-pref-handler.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: nwbIgibWPf0+49hsvM6FqA==
++SHA1-Digest: hAW/iaKpN8SiDk2KBrTG3m03BoA=
++
++Name: content/main/content-policy.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: CHIIi6Hr5dRwcHDk+LQgnQ==
++SHA1-Digest: 0qsJngcTpb1Wqi+3KNWwxxZ6tJM=
++
++Name: content/ui/overlay.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: R5LvIWVe1mKHFdSwNpQkyQ==
++SHA1-Digest: 3a3l1l3gFRURVjlCUYGe2VpRK0Q=
++
++Name: content/ui/frame.dom-content-loaded.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: auYD1TFKZkLESRYL0T1RXA==
++SHA1-Digest: 3Jq0bJdyYr9gQBBlsOISwHfeKyA=
++
++Name: content/ui/frame.blocked-content.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: mYQw56+9+Xcn6q3tz5BdzA==
++SHA1-Digest: v5/10F+mJ8/7ReCsmI4HwQUpHlE=
++
++Name: content/ui/request-log.xul
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: LKlT8I1UQKG1m/OEI5EVXA==
++SHA1-Digest: f9HZWLQKD7hU3Cm58cNpPDHVnUg=
++
++Name: content/ui/request-log.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: dFovAT0NBeInAGl0sDdaFA==
++SHA1-Digest: f5b6+xa71Bfd66UqFSYU2RI47cI=
++
++Name: content/ui/frame.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: nCottjXtAmBym28s2OJpqw==
++SHA1-Digest: +7XgKrOytQLf0LkGoowr/xPz7FI=
++
++Name: content/ui/request-log.interface.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: tvMnvvM15vaFzApTEXCxvg==
++SHA1-Digest: v0+KmH7g21NmvbPVXKKAmHX3GtI=
++
++Name: content/ui/request-log.tree-view.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 52tyES9OLfBP9PLm1pPp7w==
++SHA1-Digest: ptMO8CJbInLJL+3tH6QbJjIBdWU=
++
++Name: content/ui/request-log.filtering.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: bKqtT7dxrjqI7FXp7NcUKw==
++SHA1-Digest: yZGcUhiNZVqOtTcETQat3qetQ9k=
++
++Name: content/ui/xul-trees.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: dV3uuyYdD0ioRxhApjoZOA==
++SHA1-Digest: IgjnLTJtQxqH5WASycaFzNaYIes=
++
++Name: content/ui/menu.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: QK8dd30y/iHBXC4isyelxQ==
++SHA1-Digest: wzdd05OS0pWdtPkOTTRf0ZLrZMI=
++
++Name: content/ui/classicmenu.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: JfmnE0g4lHLawWUAcgxJ9w==
++SHA1-Digest: lnsQnL0NQv3TIFB3W41eWMrYjKM=
++
++Name: content/lib/ruleset-storage.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: bYxnWXZ63ScmTMLg+VdRUg==
++SHA1-Digest: 7KrI6vQyggF+A/n3BRNAfGOdH84=
++
++Name: content/lib/policy-manager.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: ZSlOd8PyR/MyzfOou36b3g==
++SHA1-Digest: CdHWzdGuqNSyUEOR3uWMDB4WBgQ=
++
++Name: content/lib/manager-for-message-listeners.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: qGZN/5ZR0u9Ln9gDjfSIOQ==
++SHA1-Digest: JGagsCy2RaszH+Kky6sLn+uENgI=
++
++Name: content/lib/ruleset.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: IRp67PHX/EHU3Er4wmT5Kw==
++SHA1-Digest: soabMCaR4JflvnjuhIiZ2SFb+DU=
++
++Name: content/lib/prefs.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: MOs1W9+Fq7oePGQEh3qIKQ==
++SHA1-Digest: m0vI08LULvIyY5ADKw/JUNkk2tU=
++
++Name: content/lib/gui-location.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: GoE2Qx0qZ0bnbRiF/UmB8Q==
++SHA1-Digest: r/s6Y+e6bbmfMQevFXDRKeWfdKU=
++
++Name: content/lib/request.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: WrrtzCGt/dIUnSt2/NSggA==
++SHA1-Digest: KBtV00utrf4cdhkDNf2SrPZQkws=
++
++Name: content/lib/observer-manager.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 31Z4XMCNt2ITBnTcMSWzew==
++SHA1-Digest: pugtpqvjFnET7MyCOUYl2TuuR38=
++
++Name: content/lib/environment.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: g7ZQgxO+CzvuIn0fYs7Tsw==
++SHA1-Digest: ykABX0ACQQ5Df40X92gtMvMeyew=
++
++Name: content/lib/request-processor.redirects.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: ak/zQY7RiBSpjnjZUNghmA==
++SHA1-Digest: tGGQB0yFymekmKfHditBBiIRBCc=
++
++Name: content/lib/request-result.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: wsa4Pka+ZpXcBSOs991WiA==
++SHA1-Digest: Gn4bvK2HjzZoHMB1GmVH87INzc8=
++
++Name: content/lib/utils/domains.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: tQ5FvvSfz18WN4/B86KkSw==
++SHA1-Digest: sEQB7n7q3TvjSDJMRPvhF6Z2heo=
++
++Name: content/lib/utils/files.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: b1Fpp6Ctd55aqkmfvuSXyA==
++SHA1-Digest: XyB5e7Mp5Jvi0s1aeWHR7k87xcM=
++
++Name: content/lib/utils/windows.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 2zajnJKJA24SAR6pM8VtXA==
++SHA1-Digest: rZkPXzIz92e9osx+mxGMtayftGE=
++
++Name: content/lib/utils/constants.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: DmU+88GnN/GGvF5eh2SBUg==
++SHA1-Digest: LFOdeu1FJ7pSBps0FLHXf1H7urU=
++
++Name: content/lib/utils/xul.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: z/wimVHrka+9v8Kw5e7Dag==
++SHA1-Digest: feKYjMJQKooYIkYElbxObgeHU6A=
++
++Name: content/lib/utils/strings.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: K2HsdWVlSddV0vq2bIhr3Q==
++SHA1-Digest: UHSuTCKYTQBirABMdotoT6cYp40=
++
++Name: content/lib/utils/dom.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: U1y5YwWhwAV6LpVncxgvEw==
++SHA1-Digest: 3g5j8dfxLsZpB693dsEFAJo5pks=
++
++Name: content/lib/utils/observers.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: HGU7YL1Ba3qGkNJGxRrbzQ==
++SHA1-Digest: /C6Z4pvlQd3O1D7o6sQAwCxcToA=
++
++Name: content/lib/subscription.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: LSmZZOxgBlsu8/5GrLozFQ==
++SHA1-Digest: RDKTuPlinnnz3lAhQfBGA6gbwNU=
++
++Name: content/lib/utils.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: NvE0kWlmFhlruYDUuN1z1w==
++SHA1-Digest: ygjKZz/nrOsLaHWq/sAaelLb6JA=
++
++Name: content/lib/default-preferences.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: nHOfxoGV7+KfjKTbFQtAPQ==
++SHA1-Digest: Ysdpf+pJpQZPot5PEA+SS1lBXVw=
++
++Name: content/lib/request-set.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: TG5RHNLFyUjEQgF2GhSl7g==
++SHA1-Digest: SULFw69lrwgNPayh/jy89rbkUrE=
++
++Name: content/lib/policy-manager.alias-functions.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: jYjdlbe2edw/0/iiAYh+Bg==
++SHA1-Digest: /uzAHaEyttWJnSyo1uhPgh/1f6o=
++
++Name: content/lib/logger.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: dty/DgjMGBxZAdnXaDutiw==
++SHA1-Digest: cLyFnegiO1a6MH5oqOjheKRpJus=
++
++Name: content/lib/request-processor.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: pLlz2OE0QaRFM9lldVSOyg==
++SHA1-Digest: HI1FsWmpXqgLNo34kHngcX3F8n0=
++
++Name: content/lib/framescript-to-overlay-communication.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: mwIl823JXV0IcOq9sVzikQ==
++SHA1-Digest: 6AX7pKlJFHI3xfX2RbEITShD2/Y=
++
++Name: content/lib/http-response.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 6dCM5zSVDe1LdzZYGf19YA==
++SHA1-Digest: LzxtbXHypmXp9g2u/CdNHY8OBKs=
++
++Name: content/lib/manager-for-event-listeners.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: Yl+HZU90wU6jw+IWU47NxA==
++SHA1-Digest: I0wFIcHFxBOYH+60mExIpeC0Wsg=
++
++Name: content/lib/environment.process.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: PKljIll8ECwkO/jpUHo+9Q==
++SHA1-Digest: 8OX+1R/c5+rPKPxEBImdcWZHfCE=
++
++Name: content/lib/request-processor.compat.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: I62EQRGd9jNr9yoUKDxOJw==
++SHA1-Digest: HMWgg2AS+0VXPP2hXnUVSPZ2aPs=
++
++Name: content/lib/script-loader.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 1JGpf8hIwthcilKJYatrhg==
++SHA1-Digest: 6pLxyMV6ce2KAuUpvhYGO9lzWZ0=
++
++Name: content/settings/subscriptions.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: vGoe2o430BB0u2Ga+1WCzA==
++SHA1-Digest: GbYfl99OCcwBR3m4iUHR8e5ZDew=
++
++Name: content/settings/setup.css
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 1B2M2Y8AsgTpgAmY7PhCfg==
++SHA1-Digest: 2jmj7l5rSw0yVb/vlWAYkK/YBwk=
++
++Name: content/settings/common.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 3wjE4FayeCO2voQNEne4Bg==
++SHA1-Digest: hEeOd3d8eeO41vYPI+BeGFt3DMU=
++
++Name: content/settings/advancedprefs.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: nUWJKjURjmD6fnbcEiSMJw==
++SHA1-Digest: AYBFGx2mvwq63YJkkJHVEegOG/0=
++
++Name: content/settings/setup.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: dXpITARU9yDW35mrGByoUg==
++SHA1-Digest: afKOofQJYc1FraRajCg1T8sZTt0=
++
++Name: content/settings/subscriptions.html
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 9O63R4/91LbGQom/6mIWXw==
++SHA1-Digest: 5tLA94rjwdm4TFmFrZx71tSgPrU=
++
++Name: content/settings/basicprefs.html
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: l239BOUiFMBYq+EeYu1cXQ==
++SHA1-Digest: dVzTM566MbEZp76tKu/hqNOYJWo=
++
++Name: content/settings/defaultpolicy.html
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: 32oxpYH4KoNUWGq0sY44MA==
- SHA1-Digest: g/E8iypzREFNMsM39YoBewJAR7s=
++MD5-Digest: DvlHUgo/AKfT2SoCzyRcIg==
++SHA1-Digest: SSLoqAau/54tJlNrxjfPrQTflvI=
+
- Name: components/requestpolicyService.js
++Name: content/settings/yourpolicy.js
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: C5L6kAr21uXgQhknlmqAiw==
- SHA1-Digest: eNC9HC9SgsLMwKtynDoeYBtZMow=
++MD5-Digest: ZlaVoYyAYmO86w2lMy9LnA==
++SHA1-Digest: 53TpHzktsgmWjX7b71YtafKum4E=
++
++Name: content/settings/oldrules.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: lUM194TKHEmkOlOzEuJWnw==
++SHA1-Digest: 6tvQ5MCsHJHlMGxuB077F7yIC+0=
++
++Name: content/settings/jquery.min.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: TxNjnTNZXvJw9hFQkAhB6Q==
++SHA1-Digest: jacHesddtmkZTyT/NOy74s7jwoU=
++
++Name: content/settings/defaultpolicy.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: AeHp46dxnsGMDZ/2J1WG6A==
++SHA1-Digest: DNbKh1KCcZSxC5Lh9oyVw+QPUXE=
++
++Name: content/settings/yourpolicy.html
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: Xc5ohNMbWioD1X2dUUFT8g==
++SHA1-Digest: JpO632zIPlqfLRMaiLXoihplPaY=
++
++Name: content/settings/oldrules.html
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: qtdsy09pWo7hn4Q+Hr28Ow==
++SHA1-Digest: RlomqWm7Qqd6s+Teb/Eb4KlvjUc=
++
++Name: content/settings/setup.html
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 1pnFL1mCkXyth1L9vWzQhA==
++SHA1-Digest: AADjy5Aswhd719uoH04roTd/E6w=
++
++Name: content/settings/settings.css
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: w6wNTVHjUVJzaH6EmPrItQ==
++SHA1-Digest: XFLmd5icAUDEtJ3jlHlpxgKdXj8=
++
++Name: content/settings/basicprefs.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: L5h06IQ3gqyKEhmXqPv8Uw==
++SHA1-Digest: C+7JP0vPPDoZsPo26eUcG98OaxI=
++
++Name: content/settings/advancedprefs.html
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: m8VBBIkukJXTHW/8wkpcDw==
++SHA1-Digest: aIht/dvxYtcePYG3szEF1rNYHNY=
++
++Name: README
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 6IfZq3sTTDTENL+Se1ZoIA==
++SHA1-Digest: iDJpM8K+fZKkpeqnpi0kVupmPDk=
+
+Name: install.rdf
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: D6D9vL7r++VRQX9iDpV1lQ==
- SHA1-Digest: qvEse8Guni/WQWfDykWt5BUYRno=
++MD5-Digest: TJ3paHwjO9WXVsvbrRmEVg==
++SHA1-Digest: RrEa4ypt0qXMifoqxm7PI5hqpMU=
+
+Name: LICENSE
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: /YHJAxMA510Jme5F86jn2A==
- SHA1-Digest: tITdPM3Z5CuWoHPmuuwzVwTlv6s=
++MD5-Digest: V+iUvQ8339+uK2nUxBt4oQ==
++SHA1-Digest: BKr7OaEqkHyN92EAoQgXBoUWHk4=
++
++Name: locale/tr/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: YHGevfSYcNwB5aR/DjrRLQ==
++SHA1-Digest: 7BKNoiJcmmRhC+PF2+2GaFOiNBI=
++
++Name: locale/tr/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 8VMm/0yULog/NuMgM4QcoQ==
++SHA1-Digest: ZQUBp7O5zZ0I4nXPlABNZObvDcc=
++
++Name: locale/uk-UA/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: oGWBoRlf+qVuz66wmB/J2Q==
++SHA1-Digest: Qucwrc3cqcj6CZhVG99sQWh+2LY=
++
++Name: locale/uk-UA/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: Tk1CVFfpYGdF8G1C02R4qg==
++SHA1-Digest: KS2CiBLX1sQW+d0vLOKjwQaTlHM=
+
- Name: modules/RequestResult.jsm
++Name: locale/eu/requestpolicy.dtd
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: cIgpDI9lF3WpQl6fkW1vIg==
- SHA1-Digest: nRn1agGp8auUZNvf/qQMLNwKNIU=
++MD5-Digest: C4s7AtLXDSyI2Wy0OrJb2w==
++SHA1-Digest: U3N8NZX4Mz2Q6MrBnf4nxhRRo2c=
+
- Name: modules/Logger.jsm
++Name: locale/eu/requestpolicy.properties
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: iqhFoovgBCDmaDeLCTHbpg==
- SHA1-Digest: +dZ6Od0P0gNDLQ4usTw0K+zacrQ=
++MD5-Digest: 1PUm3ipXh0Y904ADOEgd9w==
++SHA1-Digest: lf+1Vtt1UvoDC/CY7yyIj4Tr3wE=
+
- Name: modules/Util.jsm
++Name: locale/sk-SK/requestpolicy.dtd
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: qRTcXSephxnsSXdIhyRcDQ==
- SHA1-Digest: n3CDD2sUStZN0zQga02RQYmWM9Q=
++MD5-Digest: /HrV/tEV1jqBNtrqcBs1WQ==
++SHA1-Digest: 20a1yeDGOjjAu9a4QNJiYcz5SeU=
+
- Name: modules/RulesetStorage.jsm
++Name: locale/sk-SK/requestpolicy.properties
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: SAPk/vJ359sK52M9cjfesg==
- SHA1-Digest: okJtAVdPR1guIDoVAmAjB5JtSE0=
++MD5-Digest: KV5OqgFa8lDCRK8E2rYm0Q==
++SHA1-Digest: 3rLXSU4m3cqZS1iOgs5bOVqb0z8=
+
- Name: modules/FileUtil.jsm
++Name: locale/ru-RU/requestpolicy.dtd
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: zwyHqryKl+/X583ZQ8Urnw==
- SHA1-Digest: F1BPgSOn4NubRuPy8lBJdt/g31c=
++MD5-Digest: CwU7WRvYODMiRHmWxWylcw==
++SHA1-Digest: zmeQL5LH3Q0zo+cf3I8yTAjMVpI=
+
- Name: modules/Request.jsm
++Name: locale/ru-RU/requestpolicy.properties
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: lUR8ElEvzPH4RDQm8DPEDA==
- SHA1-Digest: 6yHySOrHqlMGLaP0hhmJSvtXyLI=
++MD5-Digest: ruzbqwxPm8ZqODwkS/EKAQ==
++SHA1-Digest: eaGKLWiLVSVWaIuvP4gVUuAQWUM=
+
- Name: modules/Subscription.jsm
++Name: locale/it/requestpolicy.dtd
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: lSdZx3x+99UbvDVla32m4g==
- SHA1-Digest: JLNZvzikKKygFH3qk77xw+Ab3nY=
++MD5-Digest: iuaTQG8IWWJHMEqm8/wzeg==
++SHA1-Digest: ADrzsCPiLkT1ClP6Xp+nKwsQu2M=
+
- Name: modules/DomainUtil.jsm
++Name: locale/it/requestpolicy.properties
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: STOWK+IdQ1RCUryKAPa2OA==
- SHA1-Digest: VvjO11Hsj2IzKrApy4LPUkPjZM8=
++MD5-Digest: WZRvu0vmoB1wEvXhzfEREA==
++SHA1-Digest: QFSNR3Lf9mdkyrPuB5sWFAnb8jQ=
+
- Name: modules/Services.jsm
++Name: locale/eo/requestpolicy.dtd
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: wstD9vYNy1Ma2Wv0Si+01Q==
- SHA1-Digest: VsfqNm8IBiV5S+UFq4FIwWnE4Vk=
++MD5-Digest: cpw0czZp2ImP3XqSkcOLew==
++SHA1-Digest: 8Hof6vgM3HoPcg+NbWGct5A5f+c=
+
- Name: modules/Ruleset.jsm
++Name: locale/eo/requestpolicy.properties
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: s0xfqgSshQVt3P9RX9RRbA==
- SHA1-Digest: 73wOZfnfe4Ya1Kj42SVmLp8hkc0=
++MD5-Digest: LSsj6eSm5ULIAzZCiWu2Mw==
++SHA1-Digest: hd7uYaOxFZML+MAIFd62Tm4P4Ko=
+
- Name: modules/Prompter.jsm
++Name: locale/sv-SE/requestpolicy.dtd
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: DiDa+VdsJm8FlBiTbIrO7w==
- SHA1-Digest: 5+E4XARzCJdwCKqDE2X2NtNsfc8=
++MD5-Digest: 9Oby/weV3RG1PoVJy+8Few==
++SHA1-Digest: B5ZC1vu7d2RT001lQ74d4lYDJ0A=
+
- Name: modules/GUILocation.jsm
++Name: locale/sv-SE/requestpolicy.properties
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: BEuqSgMUbSguyEPajYayUQ==
- SHA1-Digest: pN47sUo+1KfzzvNz3Cn72Qz0Hdo=
++MD5-Digest: NMq0OcAJm7qt3SpuvPouPQ==
++SHA1-Digest: RRebH1TdHjwvcUtWDWhIo+WKyZE=
+
- Name: modules/RequestUtil.jsm
++Name: locale/ja/requestpolicy.dtd
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: b3yLMzYE2OTnqqX5Ubhs4Q==
- SHA1-Digest: qArZ6/7VXDAAUToo6/WKg/d1k8E=
++MD5-Digest: D1qyBtMKfDUCxQ78Xclzew==
++SHA1-Digest: RsE6DQpQPXNKipnGtRGrVuYxvrU=
+
- Name: modules/RequestProcessor.jsm
++Name: locale/ja/requestpolicy.properties
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: utMjsmQuvMTK19RMHsbz2g==
- SHA1-Digest: iABr60ZwJOvsItcz39UE4DVDnQo=
++MD5-Digest: 2bdw7BU8M6kswpme/CDT4g==
++SHA1-Digest: rqFMKxiRqivkpHyq9YA7k5oGTeY=
+
- Name: modules/PolicyManager.jsm
++Name: locale/nl/requestpolicy.dtd
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: Eg7HOKz25baGb19Ui1oUYw==
- SHA1-Digest: yQUlzZoE5kPsIBnwjMGxElM6EHc=
++MD5-Digest: 8MG3dZttG264EPhnB/K3+Q==
++SHA1-Digest: ZeyaN7q1wX+om//sU36NpYYIU8M=
+
- Name: modules/JSON.jsm
++Name: locale/nl/requestpolicy.properties
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: 66tLBkirDbNGVScWVmuT0Q==
- SHA1-Digest: la4ccHi5IO/UdzZM1Sg2XGw4Euw=
++MD5-Digest: 5D97loMSiGuz4oM2Ei/h8w==
++SHA1-Digest: pyuahhoPx0PyMPdc0nmromZxTE0=
++
++Name: locale/zh-CN/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: T5Bqf7eJXZxkvt1kRf2WTw==
++SHA1-Digest: rsMS9bomSXElul3g2FIISZAq0CA=
++
++Name: locale/zh-CN/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: nDKYvUOrd+NdLAYAfXPF3A==
++SHA1-Digest: HaAotqsCbstWEndCRgNc3MEcWrc=
++
++Name: locale/zh-TW/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: PfmqQTFvc1tNhrhtngY7tQ==
++SHA1-Digest: MejY4QleZFjtH1FQRmulTz+bMcM=
++
++Name: locale/zh-TW/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 5Rly3fQBebz6oOIrZKJ2+Q==
++SHA1-Digest: 6v011Y6ZxypDMnM6FYJcdqnB/HM=
++
++Name: locale/pt-BR/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: ygevhxxjopSuy+jHRvTQ4w==
++SHA1-Digest: G9v9ugU1Ksv5F+OmOvg/xpxedQ4=
++
++Name: locale/pt-BR/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: k1fkORM2keBYpJ6kr6OTnQ==
++SHA1-Digest: GZ+YDVwukV68huPON54XjF6xDbQ=
++
++Name: locale/fr/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: cU13QRB7T8ukgCiSoJgkmw==
++SHA1-Digest: 8ASkBmqZ2Im5sU7XfRJmghOc+Yg=
++
++Name: locale/fr/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: iqn/g0UH6jQQ+Q7fyh8Iig==
++SHA1-Digest: wSHwbDvjWZFbeZOi5AeIblSIaf8=
++
++Name: locale/de/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: P3bt/Rvk7IFSK+FeLqrf2Q==
++SHA1-Digest: fi2ZzsQfYcSnMwVvzuMjKmj5ABA=
++
++Name: locale/de/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: MnDaGK7QCVcPaG9jbZV+gw==
++SHA1-Digest: WN71NIJjbJS5HFiuGPgxiSkNRAI=
++
++Name: locale/en-US/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: z9mL9ndqOIJDSki2OAliIA==
++SHA1-Digest: oI+05rIfs3LkbVj2A8/ZybgtDSQ=
++
++Name: locale/en-US/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: a3WLK0QWcnXjMu4aEQJ6qw==
++SHA1-Digest: NdKVZzdktdK8cLLAdJFFGfBVxTs=
++
++Name: locale/ko-KR/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: awJU2LVBkGH5531QM3WseQ==
++SHA1-Digest: 0CW1gSuTZoPHkUo8QaBSiEtPJ5Y=
++
++Name: locale/ko-KR/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: DPTq0986Db/Cp41y9d32UA==
++SHA1-Digest: Gggeop7zQ7maK20sRPpNCSCoJOc=
++
++Name: locale/lv-LV/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: F9pyEdjG26OezWX4uIvt3Q==
++SHA1-Digest: qU+7HD6MIJakGnFqLLJzRv6hPkA=
++
++Name: locale/lv-LV/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: WdvWgwuH1Gpu62pTq/u0PA==
++SHA1-Digest: Gjh9HSILzCMvAeHb8BUR4PDI/uU=
++
++Name: locale/es-MX/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: p4AB7nYZ/Kw6d9gfDi8IoQ==
++SHA1-Digest: zq7CXn+GYo7De4Xt91GOeOS3qZg=
++
++Name: locale/es-MX/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: bfkA7maKj2bJ3s8goBGSSw==
++SHA1-Digest: KcD1qZXKeSEhoESrlMsaKvejrMk=
++
++Name: skin/requestpolicy-statusbar-blocked.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: DLfnmMXcX11H83/x1l+PkA==
++SHA1-Digest: JwE7jJrdz9dY3SDzPDN5cXyuIPE=
++
++Name: skin/dot.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 6ZQiOl6YR5jZzvD/M9z8ig==
++SHA1-Digest: RDgRl3pigBseZY3o2FgGlA4lhe4=
++
++Name: skin/request-log.css
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 5WbDhFzsSSD+m7QrHUNnBQ==
++SHA1-Digest: vJPXdQg8S6qzNtqUS9wrTpThxas=
++
++Name: skin/requestpolicy-icon-32-blocked.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: Azv0YBgCQQPEOYE1H5FA1Q==
++SHA1-Digest: r/saE8YuaR1GmoXPLNiEEcWDyCQ=
++
++Name: skin/requestpolicy-statusbar-allowed.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 0folj9+9/KhWd2DmfBs3Fg==
++SHA1-Digest: v7VT838WMXI1DwX2lrSW3YH9jow=
++
++Name: skin/close.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 7h607/eRKeau7XntOi2fCw==
++SHA1-Digest: iTy8yNjtkoRcODVHdACukPyUdmk=
++
++Name: skin/requestpolicy-icon-32-allowed.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: ad8QX7maxSoOWvptubGk2w==
++SHA1-Digest: jJxLCde6TR+WUhbU+uADUvfaTe4=
++
++Name: skin/requestpolicy-icon-disabled.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: IbB/LJgfFQ0Bh//Ov/Pl1Q==
++SHA1-Digest: poh+kgwVdBewWFYWOXD6jMTnqUM=
++
++Name: skin/menu-other-origins.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: dUzdeO5c8zbWFl/zN6NwfQ==
++SHA1-Digest: wz+fvLxp7vdR4rq6/B3r6lZhVGM=
++
++Name: skin/requestpolicy-icon-24-allowed.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: zYXVJ1OS66hBNFm5yrFgkA==
++SHA1-Digest: C2IFm7adI+wabttSCm8NiLINjlo=
++
++Name: skin/toolbarbutton-seamonkey.css
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: M6P3PlhyQnPxYMH2Vu9jXg==
++SHA1-Digest: P5IZief2S7k8qxurgvu9pxCSh1M=
++
++Name: skin/menu-default.svg
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: YMrcua6M9KW8ha0v3Ye6rw==
++SHA1-Digest: UnW/mDqYtJjpsmaMDxseUmWZqbw=
++
++Name: skin/toolbarbutton.css
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: IoyZ/3BIN23q95ifOCWn4w==
++SHA1-Digest: 2/H3B53cKhiCaxLKA7onmCMh924=
++
++Name: skin/requestpolicy-icon-24-blocked.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: Wic5nAdiHv1e+ncHnNZhxg==
++SHA1-Digest: GsJl5v6HO7CUOS/8lB/4MMRPCuw=
++
++Name: skin/initial-setup.css
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: VR2u7MSsSw6xOHlM5bcNRg==
++SHA1-Digest: m2P4gSrSfOSWKgipRM3jlN8oulg=
++
++Name: skin/menu-allowed.svg
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: nV5VrjIu5Xtzzx12iVediw==
++SHA1-Digest: Xa8xm1qix3i/VH+dvzVvDwImgC8=
++
++Name: skin/requestpolicy-icon-32-disabled.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: B2n1zStlybgvbtikAtcGyg==
++SHA1-Digest: HfF/LBsrRVTUF3vqN4fH4OVoyVI=
++
++Name: skin/requestpolicy-icon-32.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 7cfrRo4hVSZjIitHKQJd6A==
++SHA1-Digest: L8b37XGnevF0DKRos7lxvVq7sTs=
++
++Name: skin/requestpolicy-icon-24-disabled.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: g/xzYn+NcYrBALsvLvCygA==
++SHA1-Digest: HgHArB9Dfe7ljls1nIrJNK6MHTg=
++
++Name: skin/menu-blocked.svg
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: n3DEgJZDT6gUSnxPKSq26g==
++SHA1-Digest: DWmR3LX2AnSv2Ns92RkP037LKr0=
++
++Name: skin/requestpolicy-icon-allowed.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: T7JlJKlYh0xouf5n3nXoIw==
++SHA1-Digest: 9Xd8pFqrBNRfiJaii6PuSQNwGGg=
++
++Name: skin/requestpolicy.css
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 0ul9T4Lr/ojP7uIIUTFWGw==
++SHA1-Digest: SkZdfTxj9zJvPneRclU4sczEoyw=
++
++Name: skin/requestpolicy-icon-blocked.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: H2bEnhsko2AFOyB/J0CoXg==
++SHA1-Digest: GKvD/D6A6XHVoYfW9J5LtkpFQis=
++
++Name: skin/requestpolicy-statusbar-disabled.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: AbAK6+qCdyzo9q3OA5Qt9w==
++SHA1-Digest: ulSmlBGaY0+PeWUM//y8TQryZ44=
+
+Name: chrome.manifest
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: EBLrFrZ0vxrC2c5gJA9fcg==
- SHA1-Digest: 11N6xQYHdmHq7xMuOq8FJ8alu10=
++MD5-Digest: 3cwuJSYo5KfrSeqmUWFMVA==
++SHA1-Digest: FXr45mmpMkL+fQ8WD9r1Z6ueH3w=
++
++Name: bootstrap.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: uQ87I+QLRvqjM2s/YsHYjA==
++SHA1-Digest: sIxtY5PqnaILCUCWmkRDEBCdPgU=
diff --cc META-INF/zigbert.rsa
index c068a5a,0000000..b72b7b9
mode 100644,000000..100644
Binary files differ
diff --cc META-INF/zigbert.sf
index ec40692,0000000..980a395
mode 100644,000000..100644
--- a/META-INF/zigbert.sf
+++ b/META-INF/zigbert.sf
@@@ -1,121 -1,0 +1,691 @@@
+Signature-Version: 1.0
+Created-By: Signtool (signtool 3.17.2 Basic ECC)
+Comments: PLEASE DO NOT EDIT THIS FILE. YOU WILL BREAK IT.
+Digest-Algorithms: MD5 SHA1
+MD5-Digest: Y542DeFadqxYpgFCAnJgFw==
+SHA1-Digest: gmQgtQLLpa+Pi8q+8RUrZjcjaEk=
+
- Name: chrome/requestpolicy.jar
++Name: content/main/pref-manager.jsm
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: 8fLUWC3/UA5+8Y1N7zNLUw==
- SHA1-Digest: 21n2gNs6kzOCuykqmbt1HRn25Iw=
++MD5-Digest: 4wQlO/3drh58AzIPIhir7g==
++SHA1-Digest: uvsvuYWOH6nXWMFO9OdEGC4DQBc=
+
- Name: defaults/preferences/defaults.js
++Name: content/main/about-uri.jsm
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: iiRycr/IN1ty3+fmYwj0rw==
- SHA1-Digest: sRD08BZUoK7iF1WnAomdvsmQL5M=
++MD5-Digest: xcaPyAfXLrwGTX7RsJnpQw==
++SHA1-Digest: 8G3cntGSGgnCraSUYk6GlO2gbuU=
+
- Name: README
++Name: content/main/requestpolicy-service.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: xl2R4jFMtwyre1/fGM89ug==
++SHA1-Digest: EorhyOSQVGKuVGfHQf9iCuVS0XU=
++
++Name: content/main/window-manager.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: qH2UwvProMYfc2T8FrbXNg==
++SHA1-Digest: /CeW/7yAxhaZ0q5rkVAYNatP8FU=
++
++Name: content/main/window-manager.listener.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: BTg+2p4f4U31k+rZcncqMw==
++SHA1-Digest: dzYbbgIMIIhvy1LbZz1j2YR8Wdw=
++
++Name: content/main/window-manager-toolbarbutton.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: Ls/+XISBUAmW04/A1AipNA==
++SHA1-Digest: Ytk4f/gZlzFDIpMg0i7AvylcLBQ=
++
++Name: content/main/default-pref-handler.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: RQ1e3JDuXio/nTfIf40wqg==
++SHA1-Digest: BGkHxjM82nHb6/OV/rf2kw35N6U=
++
++Name: content/main/content-policy.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: YWBH6DyQRzkkEN1xRhomJw==
++SHA1-Digest: WyiayvkQ0PHlJs6+ZyXsMTWSzsM=
++
++Name: content/ui/overlay.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 6SmPqRD+JwagCs03Pelq0A==
++SHA1-Digest: orV8ZFgq+uZsVXFlZ0s7e7wzrec=
++
++Name: content/ui/frame.dom-content-loaded.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: /YM3h1/L8lxQZMwv31NVVQ==
++SHA1-Digest: KEFaD/a0RugV/Smn8iO5l7W2f2M=
++
++Name: content/ui/frame.blocked-content.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: fUJCr27uoPVkFF+UBnqVvw==
++SHA1-Digest: vZnCjRPjEemYptXaNQzo+xTmVos=
++
++Name: content/ui/request-log.xul
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: RdI6dKfsm4EUoC0Fe8xgWw==
++SHA1-Digest: UboElBQcsO+JXapPwJb+h8pyUh0=
++
++Name: content/ui/request-log.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: CIuzubfthWsYurE3Ca+GnQ==
++SHA1-Digest: WheXOhEblQc456jLqicL1Q+gSII=
++
++Name: content/ui/frame.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: ohMQ/8alOh0cxvUuQ2c2Aw==
++SHA1-Digest: RpEwXYvKJUJvHjsogmRJKuxkiy4=
++
++Name: content/ui/request-log.interface.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: IpnjxFC8fNhbuyv4IkSdXQ==
++SHA1-Digest: Yb3H2uI7qmLYO/RGXipN+wZ2oXs=
++
++Name: content/ui/request-log.tree-view.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: yp9KA09Hp1fvmuTlrY+h/A==
++SHA1-Digest: AjguzG9oTtVUoE5z3lCTtWvNVhM=
++
++Name: content/ui/request-log.filtering.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: rwVwz9QOXpmH1IldyRsvWA==
++SHA1-Digest: UuOWIBWDbwiSwmbEoszC3oVa8eA=
++
++Name: content/ui/xul-trees.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: TBoK+zuHS2U/E8ErdEX/jg==
++SHA1-Digest: NZliW0mUdfyQDBZ7K3ACw5ehlBs=
++
++Name: content/ui/menu.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: Tv6ygxux3qQW6CHNpISOqg==
++SHA1-Digest: kaGJPdl2sE/uwXlxYeJ1/Zl9LY4=
++
++Name: content/ui/classicmenu.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: aGkkX+nasqxd3P+d+FnaXg==
++SHA1-Digest: WO96DQg9xTP05jVCCwyRkugrJ+I=
++
++Name: content/lib/ruleset-storage.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: uDWdTZjGoJRNTzideoyXXw==
++SHA1-Digest: VamwXTBqdkbJKy8i4p1ozRAn52I=
++
++Name: content/lib/policy-manager.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: kRFXDCJmEdnRP6mmdncE+g==
++SHA1-Digest: llM5wsKJ3wACRALYVWOpo+4EF2Y=
++
++Name: content/lib/manager-for-message-listeners.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: gcCLrBbXaha8LHApPxquyw==
++SHA1-Digest: 2XRS02P1b6QqX1E5v6YnxkiyltM=
++
++Name: content/lib/ruleset.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: jcaRWtDMUvdgRvJToDC0YA==
++SHA1-Digest: AhoCIsULPrL5AI6yT1ohxn4HOp0=
++
++Name: content/lib/prefs.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: oggU6BoF8r6EG7UQuS/dpA==
++SHA1-Digest: Zh73svpbEawoeZ6q57+FfR7D43Q=
++
++Name: content/lib/gui-location.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 1yKkN1QNuxzNfSQNJFYAJw==
++SHA1-Digest: hgI+syo6HkByh5iBhgZvIAYfvWQ=
++
++Name: content/lib/request.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: BLDkn5ZBBI6DhdbflU+o6w==
++SHA1-Digest: YagHhlcdBOMOP0Z5vq7CnpmhrEc=
++
++Name: content/lib/observer-manager.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 7W9XF+z3UDWdRiYl6rRPJw==
++SHA1-Digest: iOM606s31SirOgLCsQK66nLU0W4=
++
++Name: content/lib/environment.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: Xp1JNaDyuhIYK3VggBQBrA==
++SHA1-Digest: MG6roaq36h80oR6i6RfWCgwe9Bw=
++
++Name: content/lib/request-processor.redirects.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: QIF0xy3ZU2xgLEhV/scb1g==
++SHA1-Digest: Y81bgYb3Ps5zU+JegfdrdtfTvgs=
++
++Name: content/lib/request-result.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: +MWgmqGC8u9owqYXOQqbnA==
++SHA1-Digest: BGOMS3msgHW+3lnLND30tJ1nolg=
++
++Name: content/lib/utils/domains.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: ZtmdMuVUWTRhe6PEK3iuOA==
++SHA1-Digest: nLfygM6U/ykxs1x4kcqkmpJkwHs=
++
++Name: content/lib/utils/files.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: Y8Jbs2vtsJKlcBZ/naj0fw==
++SHA1-Digest: Q08vjPDDtekVgSVhe/jSJj8nwOo=
++
++Name: content/lib/utils/windows.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: EHhillCS27JpPJlT+R/B/Q==
++SHA1-Digest: EnY/BgDDd6PbVgYtsfeMQlkJvG0=
++
++Name: content/lib/utils/constants.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: WCgO+vTkkoOp/gPINZ5vCA==
++SHA1-Digest: IRFlPQijKpzeXXqq/I/MACcfREw=
++
++Name: content/lib/utils/xul.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: CfW1AbpJeS0NSdzMkFkhSg==
++SHA1-Digest: keTepkVBk7m+UP88JprggZYnYos=
++
++Name: content/lib/utils/strings.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: /m1YoiGU1zSplYCYkIJlyw==
++SHA1-Digest: A7TpJ3Fyc6lsCwhuCuKiKeyr9Qg=
++
++Name: content/lib/utils/dom.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 3vsBjO3mJohsD+fgsZiHDw==
++SHA1-Digest: nwpRiD9D/w2aXXWNFV1qGCF/k4o=
++
++Name: content/lib/utils/observers.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: AOjBGaE3MQZpcejWCNHWeQ==
++SHA1-Digest: xAYw8fPUALwiLdk8pWftdIk4+8A=
++
++Name: content/lib/subscription.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 5NW7uUtsBIk6gOjreskqfg==
++SHA1-Digest: D1+C7kvWFR+VQLPpOK+snFj12Rc=
++
++Name: content/lib/utils.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: Ooajcj+l7+ecFq5QhPICQQ==
++SHA1-Digest: ORM3ElSUrxfiG0IIaEWlUExg7jc=
++
++Name: content/lib/default-preferences.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: cDKS/RobAypkph652OWNtA==
++SHA1-Digest: PyEWIiQw/2eCRldFkPNUT4BVlQg=
++
++Name: content/lib/request-set.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: y7fyUS6n11zqAi/+rczxog==
++SHA1-Digest: TEdLH9F7754mH8jmfOLgbkJhpiA=
++
++Name: content/lib/policy-manager.alias-functions.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: ENNhUWFGUnTVwJ/+W/9Zyw==
++SHA1-Digest: d+sKGtodZPWTIUW9GCq6WujH+Us=
++
++Name: content/lib/logger.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: DSDyBumes2oEwrxkjYpf+g==
++SHA1-Digest: bRPiQAZUwDQwaThHDYnvbpTSfpk=
++
++Name: content/lib/request-processor.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: TUdViZd0P3+NlACJyRFyAQ==
++SHA1-Digest: vamjP/DfPg+iZ//rl/mZTUysXq4=
++
++Name: content/lib/framescript-to-overlay-communication.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: MKuwy/LMM+3CbazMxO0ajQ==
++SHA1-Digest: ra0mTuEb1wUkh6I1ybZx5vCCTk4=
++
++Name: content/lib/http-response.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: IZMuGSSLEZ3L34hxQSpJYg==
++SHA1-Digest: XN+3aCzZ8DOpmwT8DhS/XS0sspQ=
++
++Name: content/lib/manager-for-event-listeners.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: DqfZ6x1OsQCREg2RdoR5aA==
++SHA1-Digest: jrwTDeFdFjGCPytcsVE749XqnBg=
++
++Name: content/lib/environment.process.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: TnGyo7rXA20ODcCChs2QzQ==
++SHA1-Digest: PoierHXICptkybXNbqpR7PhuB60=
++
++Name: content/lib/request-processor.compat.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: ukxqqFrk518y5DUGozjdOg==
++SHA1-Digest: kT3LpbjjRRfP5hqb6nvoCLJzrog=
++
++Name: content/lib/script-loader.jsm
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: wL11uTz60v8ctchex/I8LA==
++SHA1-Digest: +CgOyDC6KYwHX2gHhepURJ36UqA=
++
++Name: content/settings/subscriptions.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: YcWhcrIxuBIWZTVkHgmOrw==
++SHA1-Digest: eRSM9z8QzcSmlFhEu1yMtmbnqTc=
++
++Name: content/settings/setup.css
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: QoWEmx22j94AQUpObNsAEg==
++SHA1-Digest: xNrenrNUc7QEKSSzKTan1fBsNOE=
++
++Name: content/settings/common.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 94NfxdMeKZidYoVwzhAvaA==
++SHA1-Digest: 73+v4CQ9ZRA96SZ43xJXTZBzZnk=
++
++Name: content/settings/advancedprefs.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: AvRFDRcR9c8iUdu6gzTrNQ==
++SHA1-Digest: IiBxP63t1vdTyjmNNcvuGU895bo=
++
++Name: content/settings/setup.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: kzFvLO3H+zDrMI1cR+to1g==
++SHA1-Digest: 8uy+iBbRwx69cjuCh4xRy5QkgE8=
++
++Name: content/settings/subscriptions.html
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: Q26Srm41597PDoMSwadPhQ==
++SHA1-Digest: qG9Ah3y3SulyUhUaJn0H/EnBHIk=
++
++Name: content/settings/basicprefs.html
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: vjYo9kJzdSfnS3fQVNWtXA==
++SHA1-Digest: Dk+OmN1+eY6FU7fN+ccnccftGOE=
++
++Name: content/settings/defaultpolicy.html
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: 4IF/FQ/CPR+dJ4KpixbQpA==
- SHA1-Digest: Y0R7rsNx4KaWYKrecWY+X/KVPJc=
++MD5-Digest: JJwj96RJnM0eOS3QtYA4Kg==
++SHA1-Digest: 9oMKkKuWhWmuw84mM4rwPY5yWGA=
+
- Name: components/requestpolicyService.js
++Name: content/settings/yourpolicy.js
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: jzE5C1cGlEFZz/05esgRcg==
- SHA1-Digest: 5+4AixpMF5i1HYAjVO4AUz87Vxk=
++MD5-Digest: o1N5OFaK7YdxothTUvjO+Q==
++SHA1-Digest: QiaOHceJhp2DQBKmAG1/HMnXC8o=
++
++Name: content/settings/oldrules.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: SYh4JVTlMdtUauo9b6h5eA==
++SHA1-Digest: rKkRlajO35PzciV1R3THvdTr8Eo=
++
++Name: content/settings/jquery.min.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: pCi1Mlsw4okPUXKBP4wW8A==
++SHA1-Digest: vxN6G5Y9UzhA5TUYKjhaw/wJH74=
++
++Name: content/settings/defaultpolicy.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: SlyoXA8yCOxQ5fvMT6b2Rg==
++SHA1-Digest: zWN3s2oNW3rTyXchl2rl01jKmDg=
++
++Name: content/settings/yourpolicy.html
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 8ghuv1FRZkvDA7UVMcDQSg==
++SHA1-Digest: n1dlolToBHvO+424IlKzNM1AhUM=
++
++Name: content/settings/oldrules.html
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: bBOaFHWiTDCFsRCcHlmLLg==
++SHA1-Digest: ufeUuEamMZlMlyq4cPQVIZMGbW4=
++
++Name: content/settings/setup.html
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: zguwTJ/EUPtIraafhEprpg==
++SHA1-Digest: x+ieG7MD3Rx+Y077FMC0veP9/E8=
++
++Name: content/settings/settings.css
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: eOWZ/81MSLHmT+idCxjh7w==
++SHA1-Digest: t7VUu2R+hsw7zaUkJukPAXvdaAg=
++
++Name: content/settings/basicprefs.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 5u4dpCZYmtZVx6CBnksKEA==
++SHA1-Digest: U65psgfnNZHR8yLdIOIWdoyDWCc=
++
++Name: content/settings/advancedprefs.html
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: +YJhgHvT7b8qQfYxQt8b7Q==
++SHA1-Digest: 5fkbAd+onHReUQ5sFV3J4W7quu0=
++
++Name: README
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: TgYM0sWvnUeqz8pGEC26QQ==
++SHA1-Digest: GPu/AAhbBA3U6gizB3T1f0jsodw=
+
+Name: install.rdf
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: ufrQFCUKe4Rs6H7pLm5syQ==
- SHA1-Digest: IJTwN4o/0S0hud5FpboFagVpVww=
++MD5-Digest: UmK828c4wUyxz+X1ZP4c3w==
++SHA1-Digest: TrQqT1V605eAc9dplTu6+aN+gis=
+
+Name: LICENSE
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: rq48gHVxmUTeD2ju1xqW6Q==
- SHA1-Digest: rZMp//tjtjReS6q4HHErfYsR8VI=
++MD5-Digest: ehUwRgZtsc5jhoDdyK393w==
++SHA1-Digest: n1A9Ct7K+QhfyjdwgdxpLAXTOBc=
++
++Name: locale/tr/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: Ex6SjuZC0vn1y4EeJ3vN9g==
++SHA1-Digest: ejuIKSlvLvr3q+0GKDSoi6nHq9k=
++
++Name: locale/tr/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: zX9TFP2qUEm41TqYVU4+4g==
++SHA1-Digest: ZiEyAQzsL6ejodw0hjFzNZOdf6s=
++
++Name: locale/uk-UA/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: Vr6DOgp2+m/If0ZdlCdmQA==
++SHA1-Digest: xaPVOd+5iwJUiPXdpOKyGqFi/Ec=
++
++Name: locale/uk-UA/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: Pj8O7QDgc6MzAO6T+EFQ2g==
++SHA1-Digest: ZYEHC18plXZEvSJOYf689PXe5JQ=
+
- Name: modules/RequestResult.jsm
++Name: locale/eu/requestpolicy.dtd
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: zPG3K21DT5Fipia6uCNXmw==
- SHA1-Digest: Ojv8OJgnVHTOd115E6OXO2oP3ks=
++MD5-Digest: 1DovZOZyq0+/t+Zk/iJX8Q==
++SHA1-Digest: feYCbktaGbMvEGrRNrUXvoSevTE=
+
- Name: modules/Logger.jsm
++Name: locale/eu/requestpolicy.properties
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: 8xUhks96ohRxgV1LazuevQ==
- SHA1-Digest: 8EYimHiaLlN5tgpU78sIdGeKbTk=
++MD5-Digest: 5LeYpJXucoW4s2KkQyuM3w==
++SHA1-Digest: ORwJSlunt/FCjaozqBf0IQFjd3w=
+
- Name: modules/Util.jsm
++Name: locale/sk-SK/requestpolicy.dtd
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: IX1NlqFneJBOzTX7yBMoYQ==
- SHA1-Digest: eu7ac11oMFd3/NkchfRjz1zv3k0=
++MD5-Digest: 6fM57lPnruOjY71DVdPJDg==
++SHA1-Digest: 760uHEeNhO0Ou12RNTBwT4NUqQQ=
+
- Name: modules/RulesetStorage.jsm
++Name: locale/sk-SK/requestpolicy.properties
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: M2cI6O4DI7BVgHFVvB/Jyw==
- SHA1-Digest: P10qEuHE051TdLVbFvnzIhiOKZQ=
++MD5-Digest: Y3THL1PcRJufZbQATPCTcA==
++SHA1-Digest: 2iwI2bQczTRcxBnzJGh15aovmJc=
+
- Name: modules/FileUtil.jsm
++Name: locale/ru-RU/requestpolicy.dtd
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: Qo70/CuFJnFgvYIwAh9ysQ==
- SHA1-Digest: m1+BpUPw+k59bw8exrpCv09ygDM=
++MD5-Digest: IRMfvQ8wLsvdQDPk+U8iGw==
++SHA1-Digest: qFqWxONJMdM7QPNMgw/86Z/1G80=
+
- Name: modules/Request.jsm
++Name: locale/ru-RU/requestpolicy.properties
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: p/tSX2dZSZFjaZBt37wcWA==
- SHA1-Digest: CEtA3qsSF5113brBS9/zadGbgg8=
++MD5-Digest: Dj/ZZW1BRfzGr2fe8VsVkA==
++SHA1-Digest: trQgVpSuYUtmZbni4hzRgyybKao=
+
- Name: modules/Subscription.jsm
++Name: locale/it/requestpolicy.dtd
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: RWdO528ZfssMIn1is/5H+A==
- SHA1-Digest: uoVYQlOUzVyj40zZZwrAL3msAGI=
++MD5-Digest: smeGdTrCgnGmuYRKycDrsA==
++SHA1-Digest: +VnCaafzK9UbQfjOhRJstmL5NII=
+
- Name: modules/DomainUtil.jsm
++Name: locale/it/requestpolicy.properties
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: zrKkx2OYshdkT7J8AdV7rg==
- SHA1-Digest: rr8mBm7BYmuGgmUKQoSCZ5uLstI=
++MD5-Digest: GlUkT12Pmr9vcDF79LXRjw==
++SHA1-Digest: ngA7E5MqtwNu02AF9jqVs6nQkXM=
+
- Name: modules/Services.jsm
++Name: locale/eo/requestpolicy.dtd
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: lOmstAWKhmx/5L+TxMBUgg==
- SHA1-Digest: YpacGBejz84Hl1lCf7pf9q/fx7o=
++MD5-Digest: xtxyDw0AbV5L7ILcgrDlRA==
++SHA1-Digest: NiIYvlZ9GINT+3p6tCsfaDRCS7s=
+
- Name: modules/Ruleset.jsm
++Name: locale/eo/requestpolicy.properties
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: V/CYr7g9nhNP4QAIV8/DSg==
- SHA1-Digest: RHQIHFF+ZcM9qBQ9e+opADVhtPA=
++MD5-Digest: AsQstb26+U66VJC9EmC4Ew==
++SHA1-Digest: yYuubNoGIEU4WtI3hM6xZ16LRdA=
+
- Name: modules/Prompter.jsm
++Name: locale/sv-SE/requestpolicy.dtd
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: UV6JMdUK9TDsEEzaPHvtNQ==
- SHA1-Digest: A8+pdVdDMTKj8YQ0welKqOM8u04=
++MD5-Digest: DH/LFssK2rU7Y3R9MyqcbQ==
++SHA1-Digest: LSN6BCEu1qlutqvunIJqo07z30I=
+
- Name: modules/GUILocation.jsm
++Name: locale/sv-SE/requestpolicy.properties
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: ByIRIkIsJbFQdbzBxjqLkw==
- SHA1-Digest: uxehxf3Nod+JeHi+u7nX1VHlYQg=
++MD5-Digest: QXkaOSZgQLFl5Q6UZ2PYTQ==
++SHA1-Digest: tflXANCOB+Qnxhfe3hvMRBAYNvk=
+
- Name: modules/RequestUtil.jsm
++Name: locale/ja/requestpolicy.dtd
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: nOXO5TEAY5caW5WcpXAnng==
- SHA1-Digest: dvcRbnz2yeVwAxCffoLzslImcQE=
++MD5-Digest: aQoxdQCtoBwKIyG5lkJc7A==
++SHA1-Digest: pWyBgC+oxIW12WEOq9ShjVbo+Pc=
+
- Name: modules/RequestProcessor.jsm
++Name: locale/ja/requestpolicy.properties
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: GvnmMN0+LCt50VCDSTOQKg==
- SHA1-Digest: fy1TpePqyvfCGD66VAQtGpnvPh4=
++MD5-Digest: K18WbxcQTHtY+OIWGnklpw==
++SHA1-Digest: PdA3iU+ienZIC/F5pw8/wTMvDlI=
+
- Name: modules/PolicyManager.jsm
++Name: locale/nl/requestpolicy.dtd
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: v4WKj0QJ7+zSkJSB6mCO2A==
- SHA1-Digest: 3sQiDxAz1USZ6jb/UdwXuRJwc0A=
++MD5-Digest: kR8X6CFrYZlpc3bfG8btuQ==
++SHA1-Digest: 2mbW+7boAZR0dO9GwuxWmqzKKXs=
+
- Name: modules/JSON.jsm
++Name: locale/nl/requestpolicy.properties
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: 40J/UCMpeBexf0V7W2tPcA==
- SHA1-Digest: 5re99XCChGRNoWNYKpVnXsIxIqo=
++MD5-Digest: f10oLQjBYIklpIDUzBmO5Q==
++SHA1-Digest: i7NmSrOrJx2RXlzsVmPdSxsBJHk=
++
++Name: locale/zh-CN/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: XQPotTYcAVzsizvLFyKGOg==
++SHA1-Digest: vMtNT+AaiIwiXu5KbyaGPUfSUow=
++
++Name: locale/zh-CN/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: gQ4THovj5TAx5sWijmZA1A==
++SHA1-Digest: 9roHnZWU37Z7PzxKFSAIeJn4vms=
++
++Name: locale/zh-TW/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: kdk4p0kngU9Y63aMWpXpkw==
++SHA1-Digest: ngeGprUXqKEIe5Iiwb9Pela2PsM=
++
++Name: locale/zh-TW/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 1+Cc11UEA9dN/9JkAhhAFA==
++SHA1-Digest: i1NxgmcZtO9odYPxDDb/N9lGfxM=
++
++Name: locale/pt-BR/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 409PEUWhES0hPUlL1qNHTA==
++SHA1-Digest: 9fPf3+2mx3nqsLUKjQa/gdf9Xo0=
++
++Name: locale/pt-BR/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: PQ9wHFpbxl4SCXKCLzSyfg==
++SHA1-Digest: gyiUVFsH4qDt7Y7bk3Sb0CAdFRo=
++
++Name: locale/fr/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: iqLFrcKFbnLmvTKMdxQoyA==
++SHA1-Digest: WWteCdobl3ziBYl41EfEPrIKwZM=
++
++Name: locale/fr/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: ABHk4vpRL+Z0LR165WybFA==
++SHA1-Digest: QgFd14IHawakoI2FjyG2cp8JITk=
++
++Name: locale/de/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: T07jj7gAigGrTKHW2sX3BA==
++SHA1-Digest: S0tEYPakQiJFAFNVptALTdYeKN4=
++
++Name: locale/de/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 3D0j61Ox3XLEeaZjdU2/tA==
++SHA1-Digest: A+bx2c7s9ZBP5x84eMKG2chm9PQ=
++
++Name: locale/en-US/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: ExX48pvPFLYKMmLCcUFI2w==
++SHA1-Digest: hqsmBUSTCzWLviPTpLPlc2I8Ae8=
++
++Name: locale/en-US/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 9pyu7TtJlwyk9fzsi8vqHQ==
++SHA1-Digest: EBntLMUb2a38WhUi49YVZAioOzQ=
++
++Name: locale/ko-KR/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: sKrxqRKDzFjz8X29haDgVw==
++SHA1-Digest: eXZ3IrLxNF5eZXVtk9LFMD8uqlE=
++
++Name: locale/ko-KR/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 18K0GuQOozavVuyjMSgFvA==
++SHA1-Digest: 9koZjtWxYV6ac+aKwf8NwJheFQw=
++
++Name: locale/lv-LV/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: /FDRMcRFdwsRebTy7VgtEQ==
++SHA1-Digest: d5jCb62Qc2+ZwoxZBUClEKSjXqs=
++
++Name: locale/lv-LV/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: j3x+DlqlyEloB9uU/mzDRg==
++SHA1-Digest: XEwD8FM8Ynzlpy0hzPCzHRe+3WI=
++
++Name: locale/es-MX/requestpolicy.dtd
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 7IkGkmGEiuS5aABRztcYbA==
++SHA1-Digest: 3sCeWdo6tYJ8oAJlEE3vTHNHngk=
++
++Name: locale/es-MX/requestpolicy.properties
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: NauvaJVJWEHrIuYjTd2CSg==
++SHA1-Digest: Fu/siar4lAQks6+gFfg6OoQ41XU=
++
++Name: skin/requestpolicy-statusbar-blocked.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: FRT3wgqPekWPzNGkY1BHuQ==
++SHA1-Digest: 9rGBl3oNOcl5nAT/+ka0PQw39Sc=
++
++Name: skin/dot.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: ZVQUzeOwErCtpS613LI4Sw==
++SHA1-Digest: D1ht/xn+OstSN5ZX1cVjoXJvOqU=
++
++Name: skin/request-log.css
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: J7G+gU2P16zfC6sUwfSK8Q==
++SHA1-Digest: JjS/LOhkM3gKHgLhufhAFeWIT3Q=
++
++Name: skin/requestpolicy-icon-32-blocked.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: f/Eg+9mmWPKitMH+7Yc1zA==
++SHA1-Digest: EkKGAQF7x4ajt++teFD7C28kvNE=
++
++Name: skin/requestpolicy-statusbar-allowed.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: xEe1GaeU14Qn9vecTpVN6g==
++SHA1-Digest: HApwwIGPGPvPeDmIYF8cY7NXV1Q=
++
++Name: skin/close.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: ULn22sAvNhzEfLZ1IrrGNA==
++SHA1-Digest: nrun822JA28GZ1qSuAVyg/psrFY=
++
++Name: skin/requestpolicy-icon-32-allowed.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: xTzq3/ElD2dQD68SLztK4A==
++SHA1-Digest: SNsRmVfGKAmdCv0DFUYSt/QYMLM=
++
++Name: skin/requestpolicy-icon-disabled.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: bodpzwuJ86wtzqMDdSd9aw==
++SHA1-Digest: KaNPBDoyHkH1oFHOfX0PihGp3DU=
++
++Name: skin/menu-other-origins.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: laOkW8yr6GHe7ZN4vz3oog==
++SHA1-Digest: zkV/iXv120HBNR+lCcRIsA+7vY0=
++
++Name: skin/requestpolicy-icon-24-allowed.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: G7A7ajTea2Ks2oHchbzgsg==
++SHA1-Digest: yUcxP3wWa3Huu2k7MJYbjWzkrqY=
++
++Name: skin/toolbarbutton-seamonkey.css
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: QLs3wUB7vlSLbY/hgSPqLw==
++SHA1-Digest: 4ZCQAKM/WvnxIEzXt0SVYFtKFGU=
++
++Name: skin/menu-default.svg
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: FxDUStQr3wcxRg/vw8hBNg==
++SHA1-Digest: PVzmI4T9Y+NKP7FniXnv7/uiKcQ=
++
++Name: skin/toolbarbutton.css
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 6ADISm8YfcMSMGdN5cJwYA==
++SHA1-Digest: VA1+jBA79I7s0JQ3KGYC13nfo1k=
++
++Name: skin/requestpolicy-icon-24-blocked.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: glQdQUi6B6P4Ft7DqvJIGA==
++SHA1-Digest: tuRrmHqmIUHE+TYw3LjqYyAHiNg=
++
++Name: skin/initial-setup.css
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: ntHRarzJQpJEyBJetklfKA==
++SHA1-Digest: 0J6pGvfARhVYtHT56dDsHkcSbdk=
++
++Name: skin/menu-allowed.svg
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: mFSaK6rxV0vlor1Luv2fdw==
++SHA1-Digest: SXr2n9FDTimW5srNWc/ZpIsDujY=
++
++Name: skin/requestpolicy-icon-32-disabled.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: X8vCUb2sv5csVnyFCp547g==
++SHA1-Digest: qEwWDsn8/oV8bleeb+/UMC5vzgk=
++
++Name: skin/requestpolicy-icon-32.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: AFWBmzMO4ftSLLMUbzFFxA==
++SHA1-Digest: jVr+aWjHB7q82yeG+E0jkgm2d14=
++
++Name: skin/requestpolicy-icon-24-disabled.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: GQX1n7q3WAcCGMetDLsS5g==
++SHA1-Digest: 8ROPk1eycusSR517LGveT5V6e20=
++
++Name: skin/menu-blocked.svg
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 5Tcapu+ja7Ws3yq8IDmPKA==
++SHA1-Digest: LMrd4fhkOrajGx2RP6GnSnveXyo=
++
++Name: skin/requestpolicy-icon-allowed.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: WQWkoBcDTA1noDd7veYq7A==
++SHA1-Digest: CEqeyLzIojGigFA17s65FYjcaUo=
++
++Name: skin/requestpolicy.css
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 1S7DJ1iIFNRTHoyP77ZWIw==
++SHA1-Digest: tqAnt8pa+W0QYzrhhmIVDFmfRHQ=
++
++Name: skin/requestpolicy-icon-blocked.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: GjPJ45n0DrIKxNhfXMm7UQ==
++SHA1-Digest: qq0aQYfm9a8GGlkj0s0SwnGWcwE=
++
++Name: skin/requestpolicy-statusbar-disabled.png
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: 4Wkv8zWU2BRESXv0NKayJg==
++SHA1-Digest: ++eAfx6faAE4JcuuelIjr5wj+Ew=
+
+Name: chrome.manifest
+Digest-Algorithms: MD5 SHA1
- MD5-Digest: jcHQF0iTVo0GPb5z0KMyYg==
- SHA1-Digest: U2QasCJhKrRFJA3dYOE7LU7toSQ=
++MD5-Digest: 8XvJuhmfJ9aWfCQEA0mU6Q==
++SHA1-Digest: udtOxdKZIUoybi7idl/s4dqWi3U=
++
++Name: bootstrap.js
++Digest-Algorithms: MD5 SHA1
++MD5-Digest: IiCmgt1HnfrsHRXo+QqKqA==
++SHA1-Digest: Z/TdmFzQUz+6u/sdv1IR6Sy8lFM=
diff --cc bootstrap.js
index 0000000,c22a6f9..d11d7b8
mode 000000,100644..100644
--- a/bootstrap.js
+++ b/bootstrap.js
@@@ -1,0 -1,77 +1,78 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ var mod = {};
+ const envURI = "chrome://requestpolicy/content/lib/environment.jsm";
+
+ /**
+ * If any Exception gets into bootstrap.js, it will be a severe error.
+ * The Logger can't be used, as it might not be available.
+ */
+ function logSevereError(msg, e) {
+ dump("[RequestPolicy] [SEVERE] [ERROR] " + msg + " " + e +
+ (e.stack ? ", stack was: " + e.stack : "") + "\n");
+ Cu.reportError(e);
+ }
+
+ function startup(data, reason) {
+ try {
+ // Import the ProcessEnvironment and call its startup() function.
+ Cu.import(envURI, mod);
+ mod.ProcessEnvironment.startup(arguments);
+ } catch(e) {
+ logSevereError("startup() failed!", e);
+ }
+ }
+
+ function shutdown(data, reason) {
+
+ try {
+ // shutdown, unset and unload.
+ mod.ProcessEnvironment.shutdown(arguments);
+ mod = {};
+ Cu.unload(envURI);
+ } catch(e) {
+ logSevereError("shutdown() failed!", e);
+ }
+ }
+
+ function install(data, reason) {
+ // note: the addon might be not activated when this function gets called
+
+ // HACK WARNING: The Addon Manager does not properly clear all addon
+ // related caches on update; in order to fully update
+ // images and locales, their caches are flushed.
+ // Note: Due to Bug 1144248 this has to be done in the
+ // `install` function.
+ Components.utils.import("resource://gre/modules/Services.jsm");
+ Services.obs.notifyObservers(null, "chrome-flush-caches", null);
+ }
+
+ function uninstall(data, reason) {
+ // note: the addon might be not activated when this function gets called
+ }
++
diff --cc content/lib/default-preferences.js
index c218b7d,b347b32..b3241be
--- a/content/lib/default-preferences.js
+++ b/content/lib/default-preferences.js
@@@ -28,6 -28,10 +28,7 @@@ pref("extensions.requestpolicy.menu.inf
pref("extensions.requestpolicy.lastVersion", "0.0");
pref("extensions.requestpolicy.lastAppVersion", "0.0");
-// #ifdef UNIT_TESTING
-pref("extensions.requestpolicy.unitTesting.errorCount", 0);
-// #endif
+
// Old prefs that are no longer used.
//pref("extensions.requestpolicy.allowedOrigins", "");
//pref("extensions.requestpolicy.allowedDestinations", "");
@@@ -35,3 -39,3 +36,4 @@@
//pref("extensions.requestpolicy.contextMenu", true);
//pref("extensions.requestpolicy.statusbarIcon", "standard");
//pref("extensions.requestpolicy.initialSetupDialogShown", false);
++
diff --cc content/lib/environment.jsm
index 0000000,963e8f2..b83d16f
mode 000000,100644..100644
--- a/content/lib/environment.jsm
+++ b/content/lib/environment.jsm
@@@ -1,0 -1,538 +1,517 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ let EXPORTED_SYMBOLS = [
+ "Environment",
+ "FrameScriptEnvironment",
+ "ProcessEnvironment"
+ ];
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ let globalScope = this;
+
+ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+ Cu.import("resource://gre/modules/Services.jsm");
+ Cu.import("resource://gre/modules/devtools/Console.jsm");
+
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+
+ ScriptLoader.defineLazyModuleGetters({
+ "lib/manager-for-event-listeners": ["ManagerForEventListeners"],
+ "lib/manager-for-message-listeners": ["ManagerForMessageListeners"],
+ "lib/utils/constants": ["C"],
+ "lib/observer-manager": ["ObserverManager"]
+ }, globalScope);
+
+
+
+
+ let ENV_STATES = {
+ "NOT_STARTED": 0,
+ "STARTING_UP": 1,
+ "STARTUP_DONE": 2,
+ "SHUTTING_DOWN": 3,
+ "SHUT_DOWN": 4
+ };
+
+ let LEVELS = {
+ // Essential functions do tasks that must be run first on startup and last
+ // on shutdown, that is they do tasks that are requirements for the Backend.
+ "ESSENTIAL": 1,
+ // Backend functions start up/shut down main parts of RequestPolicy, but
+ // they do not enable RequestPolicy at all.
+ "BACKEND": 2,
+ // Interface functions enable/disable external as well as internal interfaces,
+ // e.g. Event Listeners, Message Listeners, Observers, Factories.
+ "INTERFACE": 3,
+ // UI functions will enable/disable UI elements such as the menu.
+ "UI": 4
+ };
+
+ // a level can be entered, being processed, or finished being processed.
+ let LEVEL_STATES = {
+ "NOT_ENTERED": 0,
+ "PROCESSING": 1,
+ "FINISHED_PROCESSING": 2
+ };
+
+
+ let BOOTSTRAP = {
+ "startup": {
+ levelSequence: [
+ LEVELS.ESSENTIAL,
+ LEVELS.BACKEND,
+ LEVELS.INTERFACE,
+ LEVELS.UI
+ ],
+ lastLevel: LEVELS.UI,
+ envStates: {
+ "beforeProcessing": ENV_STATES.NOT_STARTED,
+ "duringProcessing": ENV_STATES.STARTING_UP,
+ "afterProcessing": ENV_STATES.STARTUP_DONE
+ },
+ functions: {
+ "beforeProcessing": function() {
+ // "this" will be an environment
+ let self = this;
+ self.register();
+ },
+ "afterProcessing": function() {}
+ }
+ },
+ "shutdown": {
+ levelSequence: [
+ LEVELS.UI,
+ LEVELS.INTERFACE,
+ LEVELS.BACKEND,
+ LEVELS.ESSENTIAL
+ ],
+ lastLevel: LEVELS.ESSENTIAL,
+ envStates: {
+ "beforeProcessing": ENV_STATES.STARTUP_DONE,
+ "duringProcessing": ENV_STATES.SHUTTING_DOWN,
+ "afterProcessing": ENV_STATES.SHUT_DOWN
+ },
+ functions: {
+ "beforeProcessing": function() {},
+ "afterProcessing": function() {
+ // "this" will be an environment
+ let self = this;
+ self.innerEnvs.length = 0;
+ self.unregister();
+ }
+ }
+ }
+ };
+ function getBootstrapMetadata(startupOrShutdown) {
+ return BOOTSTRAP[startupOrShutdown];
+ }
+
+
+ /**
+ * The `Environment` class can take care of the "startup" (=initialization)
+ * and "shutdown" of any environment.
+ *
+ * To each `Environment` instance, `startup` and `shutdown` functions can be
+ * added. As soon as the Environment starts up, e.g. via its startup()
+ * function, all those functions will be called. Equally the shutdown
+ * functions are called on the environment's shutdown.
+ *
+ * Both startup and shutdown functions will have Levels assigned. The levels
+ * of the functions determine in which sequence they are called.
+ *
+ * @constructor
+ * @param {Environment=} aOuterEnv - the Environment to which this environment
+ * will register itself. Inner environments shut down when its outer
+ * environment shuts down.
+ * @param {string=} aName - the Environment's name; only needed for debugging.
+ */
+ function Environment(aOuterEnv, aName="anonymous") {
+ let self = this;
+
+ self.envState = ENV_STATES.NOT_STARTED;
+
+ self.name = aName;
+
+ self.outerEnv = aOuterEnv instanceof Environment ? aOuterEnv : null;
+ self.innerEnvs = new Set();
+
+ self.levels = {
+ "startup": generateLevelObjects(),
+ "shutdown": generateLevelObjects()
+ };
+
+ // Define a Lazy Getter to get an ObserverManager for this Environment.
+ // Using that Getter is more convenient than doing it manually, as the
+ // Environment has to be created *before* the ObserverManager.
+ XPCOMUtils.defineLazyGetter(self, "obMan", function() {
+ return new ObserverManager(self);
+ });
+
+ // Define a Lazy Getter to get an instance of `ManagerForEventListeners` for
+ // this Environment.
+ XPCOMUtils.defineLazyGetter(self, "elManager", function() {
+ return new ManagerForEventListeners(self);
+ });
+
+ // generate an unique ID for debugging purposes
+ XPCOMUtils.defineLazyGetter(self, "uid", function() {
+ return Math.random().toString(36).substr(2, 5);
+ });
+
+ //console.debug('[RPC] created new Environment "'+self.name+'"');
+ }
+
+ Environment.LEVELS = LEVELS;
+
+
+ /**
+ * This function creates one "Level Object" for each level. Those objects
+ * mainly will hold the startup- or shutdown-functions of the corresponding
+ * level. All of the Level Objects are put together in another object which
+ * is then returned.
+ */
+ function generateLevelObjects() {
+ let obj = {};
+ for (let levelName in Environment.LEVELS) {
+ let level = Environment.LEVELS[levelName];
+ obj[level] = {"functions": [], "levelState": LEVEL_STATES.NOT_ENTERED};
+ }
+ return obj;
+ }
+
+
+ /**
+ * Registers the environment to its outer environment.
+ */
+ Environment.prototype.register = function() {
+ let self = this;
+ if (self.outerEnv) {
+ self.outerEnv.registerInnerEnvironment(self);
+ }
+ }
+ /**
+ * Unregisters the environment from its outer environment.
+ */
+ Environment.prototype.unregister = function() {
+ let self = this;
+ if (self.outerEnv) {
+ self.outerEnv.unregisterInnerEnvironment(self);
+ }
+ }
+ /**
+ * Function called by an inner environment when it starts up.
+ *
+ * @param {Environment} aEnv - the environment that wants to register itself.
+ */
+ Environment.prototype.registerInnerEnvironment = function(aEnv) {
+ let self = this;
+ if (self.envState === ENV_STATES.NOT_STARTED) {
+ console.warn("[RPC] registerInnerEnvironment() has been called, " +
+ "but the outer environment hasn't started up yet. " +
+ "Starting up now.");
+ self.startup();
+ }
+ //console.debug("[RPC] registering inner environment.");
+ self.innerEnvs.add(aEnv);
+ };
+ /**
+ * Function that is called each time an inner environment shuts down.
+ *
+ * @param {Environment} aEnv - the environment that is unregistering
+ */
+ Environment.prototype.unregisterInnerEnvironment = function(aEnv) {
+ let self = this;
+
+ if (self.innerEnvs.has(aEnv) === false) {
+ console.error("[RPC] it seems like an inner Environment " +
+ "did not register.");
+ } else {
+ self.innerEnvs.delete(aEnv);
+ }
+ };
+
+
+
+
+ /**
+ * Add a startup function to the environment.
+ */
+ Environment.prototype.addStartupFunction = function(aLevel, f) {
+ let self = this;
+ if (self.envState >= ENV_STATES.SHUTTING_DOWN) {
+ // the environment is shutting down or already shut down.
+ return;
+ }
+ if (self.levels["startup"][aLevel].levelState >= LEVEL_STATES.PROCESSING) {
+ // Either the startup functions of the same level as `aLevel` have
+ // already been processed
+ // OR they are currently being processed.
+ //
+ // ==> call the function immediately.
+ f();
+ } else {
+ // the startup process did not reach the function's level yet.
+ //
+ // ==> remember the function.
+ self.levels["startup"][aLevel].functions.push(f);
+ }
+ };
+
+ /**
+ * Add a shutdown function to the environment.
+ */
+ Environment.prototype.addShutdownFunction = function(aLevel, f) {
+ let self = this;
+ if (self.levels["shutdown"][aLevel].levelState >= LEVEL_STATES.PROCESSING) {
+ // Either the shutdown functions of the same level as `aLevel` have
+ // already been processed
+ // OR they are currently being processed.
+ //
+ // ==> call the function immediately.
+ f();
+ //console.debug('[RPC] calling shutdown function immediately: "' +
+ // (f.name || "anonymous") + '" (' + self.name + ')');
+ } else {
+ // The opposite, i.e. the startup process did not reach the function's
+ // level yet.
+ //
+ // ==> remember the function.
+ self.levels["shutdown"][aLevel].functions.push(f);
+ }
+ };
+
+
+ // have a scope/closure for private functions specific to
+ // startup() and shutdown().
+ (function createMethods_StartupAndShutdown(Environment) {
+ /**
+ * Iterates all levels of either the startup or the shutdown
+ * sequence and calls a function for each level.
+ *
+ * @param {string} aStartupOrShutdown
+ * @param {function()} aFn - the function to call
+ * @param {integer=} aUntilLevel - if specified, iteration stops
+ * after that level.
+ */
+ function iterateLevels(aStartupOrShutdown, aFn, aUntilLevel=null) {
+ let sequence = BOOTSTRAP[aStartupOrShutdown].levelSequence;
+
+ for (let i = 0, len = sequence.length; i < len; ++i) {
+ // Note: It's necessary to use for(;;) instead of for(..of..)
+ // because the order/sequence must be exactly the same as in the
+ // array. for(..of..) doesn't guarantee that the elements are
+ // called in order.
+
+ let level = sequence[i];
+ aFn(level);
+
+ if (level === aUntilLevel) {
+ // Stop after aUntilLevel
+ break;
+ }
+ }
+ }
+
+
+ /**
+ * This function calls all functions in an array.
+ *
+ * @param {Array.<function()>} aFunctions
+ * @param {Array} aBootstrapArgs - the arguments to apply
+ */
+ function callFunctions(aFunctions, aBootstrapArgs) {
+ // process the Array as long as it contains elements
+ while (aFunctions.length > 0) {
+ // The following is either `fnArray.pop()` or `fnArray.shift()`
+ // depending on `sequence`.
+ let f = aFunctions.pop();
+
+ // call the function
+ f.apply(null, aBootstrapArgs);
+ //console.debug("[RPC] function called! (" + fnArray.length +
+ // " functions left)");
+ }
+ };
+
+
+ /**
+ * Process a level independently of the environment's states and
+ * independently of the other levels' states.
+ *
+ * @this {Environment}
+ * @param {string} aStartupOrShutdown - either "startup" or "shutdown"
+ * @param {integer} aLevel
+ */
+ function processLevel(aStartupOrShutdown, aLevel, aBootstrapArgs) {
+ let self = this;
+
+ let levelObj = self.levels[aStartupOrShutdown][aLevel];
+
+ if (levelObj.levelState === LEVEL_STATES.NOT_ENTERED) {
+ levelObj.levelState = LEVEL_STATES.PROCESSING;
+
+ if (aStartupOrShutdown === "shutdown") {
+ // shut down all inner environments
+ self.innerEnvs.forEach(function(innerEnv) {
+ innerEnv.shutdown(aBootstrapArgs, aLevel);
+ });
+ }
+
+ callFunctions(levelObj.functions, aBootstrapArgs);
+
+ levelObj.levelState = LEVEL_STATES.FINISHED_PROCESSING;
+ }
+ }
+
+
+ /**
+ * Iterate levels and call processLevel() for each level.
+ *
+ * @this {Environment}
+ * @param {string} aStartupOrShutdown
+ * @param {Array} aBootstrapArgs
+ * @param {integer=} aUntilLevel
+ */
+ function processLevels(aStartupOrShutdown, aBootstrapArgs, aUntilLevel) {
+ let self = this;
+ iterateLevels(aStartupOrShutdown, function (level) {
+ processLevel.call(self, aStartupOrShutdown, level, aBootstrapArgs);
+ }, aUntilLevel);
+ }
+
+
+ /**
+ * Return some information about an environment.
+ *
+ * @param {Environment} env
+ * @return {string}
+ */
+ function getEnvInfo(env) {
+ return "'" + env.name + "' (" + env.uid + ")";
+ }
+
- // #ifdef LOG_ENVIRONMENT
- /**
- * Log some debug information on startup or shutdown.
- *
- * @this {Environment}
- * @param {string} aStartupOrShutdown
- */
- function logStartupOrShutdown(aStartupOrShutdown) {
- let self = this;
- console.log("[RPC] " + aStartupOrShutdown + ": " + getEnvInfo(self) + "." +
- (self.outerEnv ?
- " OuterEnv is " + getEnvInfo(self.outerEnv) + "." :
- " No OuterEnv."));
- }
- // #endif
+
+ /**
+ * Actual body of the functions startup() and shutdown().
+ *
+ * @this {Environment}
+ * @param {string} aStartupOrShutdown - either "startup" or "shutdown"
+ * @param {Array} aBootstrapArgs
+ * @param {integer=} aUntilLevel - The level after which the startup
+ * (or shutdown) processing is stopped.
+ */
+ function bootstrap(aStartupOrShutdown,
+ aBootstrapArgs,
+ aUntilLevel=BOOTSTRAP[aStartupOrShutdown].lastLevel) {
+ let self = this;
+
+ let {
+ lastLevel,
+ envStates,
+ functions
+ } = getBootstrapMetadata(aStartupOrShutdown);
+
+ if (self.envState === envStates["beforeProcessing"]) {
- // #ifdef LOG_ENVIRONMENT
- logStartupOrShutdown.call(self, aStartupOrShutdown);
- // #endif
+ functions["beforeProcessing"].call(self);
+
+ self.envState = envStates["duringProcessing"];
+ }
+
+ if (self.envState === envStates["duringProcessing"]) {
+ processLevels.call(self, aStartupOrShutdown, aBootstrapArgs, aUntilLevel);
+
+ if (aUntilLevel === lastLevel) {
+ self.envState = envStates["afterProcessing"];
+ functions["afterProcessing"].call(self);
+ }
+ }
+ };
+
+ Environment.prototype.startup = function(aBootstrapArgs, aUntilLevel) {
+ let self = this;
+ bootstrap.call(self, "startup", aBootstrapArgs, aUntilLevel);
+ };
+
+ Environment.prototype.shutdown = function(aBootstrapArgs, aUntilLevel) {
+ let self = this;
+ bootstrap.call(self, "shutdown", aBootstrapArgs, aUntilLevel);
+ };
+
+ })(Environment);
+
+ /**
+ * Tell the Environment to shut down when an EventTarget's
+ * "unload" event occurres.
+ *
+ * @param {EventTarget} aEventTarget - an object having the functions
+ * addEventListener() and removeEventListener().
+ */
+ Environment.prototype.shutdownOnUnload = function(aEventTarget) {
+ let self = this;
+ self.elManager.addListener(aEventTarget, "unload", function() {
- // #ifdef LOG_ENVIRONMENT
- console.log("[RPC] an EventTarget's `unload` function has been called. " +
- 'Going to shut down Environment "' + self.name + '"');
- // #endif
+ self.shutdown();
+ });
+ };
+
+
+ /**
+ * @constructor
+ * @extends {Environment}
+ * @param {ContentFrameMessageManager} aMM
+ * @param {string=} aName - the environment's name, passed to the superclass.
+ */
+ function FrameScriptEnvironment(aMM, aName="frame script environment") {
+ let self = this;
+
+ // The outer Environment will be either ChildProcessEnvironment
+ // or ParentProcessEnvironment.
+ let _outerEnv = ProcessEnvironment;
+
+ Environment.call(self, _outerEnv, aName);
+
+ self.mm = aMM;
+
+ self.addStartupFunction(LEVELS.INTERFACE, function() {
+ // shut down the framescript on the message manager's
+ // `unload`. That event will occur when the browsing context
+ // (e.g. the tab) has been closed.
+ self.shutdownOnUnload(self.mm);
+ });
+
+ // a "MessageListener"-Manager for this environment
+ XPCOMUtils.defineLazyGetter(self, "mlManager", function() {
+ return new ManagerForMessageListeners(self, self.mm);
+ });
+ }
+ FrameScriptEnvironment.prototype = Object.create(Environment.prototype);
+ FrameScriptEnvironment.prototype.constructor = Environment;
+
+
+ // Load the "ProcessEnvironment"
+ Services.scriptloader.loadSubScript("chrome://requestpolicy/content/" +
+ "lib/environment.process.js");
++
diff --cc content/lib/environment.process.js
index 0000000,7333366..00b5a88
mode 000000,100644..100644
--- a/content/lib/environment.process.js
+++ b/content/lib/environment.process.js
@@@ -1,0 -1,216 +1,217 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+
+ // ProcessEnvironment is either a ParentProcessEnvironment or
+ // a ChildProcessEnvironment.
+ let ProcessEnvironment = (function() {
+
+
+ // determine if this is the main process
+ let isMainProcess = (function isMainProcess() {
+ let xulRuntime = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULRuntime);
+ // The "default" type means that it's the main process,
+ // the chrome process. This is relevant for multiprocess
+ // firefox aka Electrolysis (e10s).
+ return xulRuntime.processType === xulRuntime.PROCESS_TYPE_DEFAULT;
+ }());
+
+
+ const shutdownMessage = C.MM_PREFIX + "shutdown";
+
+ /**
+ * @constructor
+ * @extends {Environment}
+ * @param {string=} aName - the environment's name, passed to the superclass.
+ */
+ function ProcessEnvironmentBase(aName="Process Environment") {
+ let self = this;
+
+ // Process environments are the outermost environment in each process.
+ let outerEnv = null;
+
+ Environment.call(self, outerEnv, aName);
+
+ self.isMainProcess = isMainProcess;
+ }
+ ProcessEnvironmentBase.prototype = Object.create(Environment.prototype);
+ ProcessEnvironmentBase.prototype.constructor = Environment;
+
+
+
+
+ /**
+ * @constructor
+ * @extends {ProcessEnvironmentBase}
+ * @param {string=} aName - the environment's name, passed to the superclass.
+ */
+ function ParentProcessEnvironment(aName="Parent Process Environment") {
+ let self = this;
+ ProcessEnvironmentBase.call(self, aName);
+
+
+ function sendShutdownMessageToChildren() {
+ let parentMM = Cc["@mozilla.org/parentprocessmessagemanager;1"]
+ .getService(Ci.nsIMessageBroadcaster);
+ parentMM.broadcastAsyncMessage(shutdownMessage);
+ };
+
+ // Very important: The shutdown message must be sent *after*
+ // calling `removeDelayedFrameScript`, which is done in
+ // the LEVELS.INTERFACE level.
+ self.addShutdownFunction(Environment.LEVELS.BACKEND,
+ sendShutdownMessageToChildren);
+ }
+ ParentProcessEnvironment.prototype = Object.create(ProcessEnvironmentBase.prototype);
+ ParentProcessEnvironment.prototype.constructor = ProcessEnvironmentBase;
+
+
+
+ /**
+ * @override
+ */
+ ParentProcessEnvironment.prototype.startup = function() {
+ let self = this;
+
+ // Create a dummy scope for modules that have to be imported
+ // but not remembered. As the scope is a local variable,
+ // it will be removed after the function has finished.
+ // However, the main modules register their startup and
+ // shutdown functions anyway.
+ let dummyScope = {};
+
+
+ /**
+ * The following section is not optimal – read on…
+ */
+ {
+ // Load and init PrefManager before anything else is loaded!
+ // The reason is that the Logger, which is imported by many modules,
+ // expects the prefs to be initialized and available already.
+ let {PrefManager} = ScriptLoader.importModule("main/pref-manager");
+ PrefManager.init();
+
+ // TODO: use the Browser Console for logging, see #563.
+ // *Then* it's no longer necessary to load and init PrefManager
+ // first. PrefManager will then be loaded and initialized when all
+ // other back end modules are loaded / initialized.
+ }
+
+ // import main modules:
+ ScriptLoader.importModules([
+ "main/requestpolicy-service",
+ "main/content-policy",
+ "main/window-manager",
+ "main/about-uri"
+ ], dummyScope);
+
+ ProcessEnvironmentBase.prototype.startup.apply(self, arguments);
+ };
+
+
+ /**
+ * @override
+ */
+ ParentProcessEnvironment.prototype.shutdown = function() {
+ let self = this;
+
+ ProcessEnvironmentBase.prototype.shutdown.apply(self, arguments);
+
+ ScriptLoader.doShutdownTasks();
+ Cu.unload("chrome://requestpolicy/content/lib/script-loader.jsm");
+ };
+
+
+
+
+ /**
+ * @constructor
+ * @extends {ProcessEnvironmentBase}
+ * @param {string=} aName - the environment's name, passed to the superclass.
+ */
+ function ChildProcessEnvironment(aName="Child Process Environment") {
+ let self = this;
+ ProcessEnvironmentBase.call(self, aName);
+
+ let childMM = Cc["@mozilla.org/childprocessmessagemanager;1"]
+ .getService(Ci.nsISyncMessageSender);
+
+ /**
+ * This function will be called when the paren process
+ * sends the shutdown message. After this function has
+ * finished, RequestPolicy has cleaned up itself from
+ * that child process.
+ */
+ function receiveShutdownMessage() {
+ childMM.removeMessageListener(shutdownMessage, receiveShutdownMessage);
+ self.shutdown();
+
+ // Unloading `environment.jsm` has to be the last task.
+ // After that task, any global object, such as
+ // `Environment` or `Cu` is not available anymore.
+ //console.debug("unloading environment.jsm");
+ Cu.unload("chrome://requestpolicy/content/lib/environment.jsm");
+ };
+
+ childMM.addMessageListener(shutdownMessage, receiveShutdownMessage);
+ }
+ ChildProcessEnvironment.prototype = Object.create(ProcessEnvironmentBase.prototype);
+ ChildProcessEnvironment.prototype.constructor = ProcessEnvironmentBase;
+
+
+ /**
+ * @override
+ */
+ ChildProcessEnvironment.prototype.shutdown = function() {
+ let self = this;
+
+ ProcessEnvironmentBase.prototype.shutdown.apply(self, arguments);
+
+ ScriptLoader.doShutdownTasks();
+ Cu.unload("chrome://requestpolicy/content/lib/script-loader.jsm");
+ };
+
+ ChildProcessEnvironment.prototype.registerInnerEnvironment = function(aEnv) {
+ let self = this;
+ if (self.envState === ENV_STATES.NOT_STARTED) {
+ // The child Process Environment needs to start up when
+ // the first framescript in that child is loading.
+ //console.debug("[RPC] Going to start up Child Process Environment.");
+ self.startup();
+ }
+ ProcessEnvironmentBase.prototype.registerInnerEnvironment.apply(self,
+ arguments);
+ };
+
+
+
+
+ if (isMainProcess === true) {
+ return new ParentProcessEnvironment();
+ } else {
+ return new ChildProcessEnvironment();
+ }
+
+ })();
++
diff --cc content/lib/framescript-to-overlay-communication.jsm
index 0000000,f1ba449..97f2264
mode 000000,100644..100644
--- a/content/lib/framescript-to-overlay-communication.jsm
+++ b/content/lib/framescript-to-overlay-communication.jsm
@@@ -1,0 -1,165 +1,166 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ let EXPORTED_SYMBOLS = ["FramescriptToOverlayCommunication"];
+
+ let globalScope = this;
+
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ ScriptLoader.importModules([
+ "lib/environment",
+ "lib/logger",
+ "lib/utils/constants"
+ ], globalScope);
+
+
+ /**
+ * The states of the communication channel to with the overlay.
+ * @enum {number}
+ */
+ let States = Object.freeze({
+ "WAITING": 0,
+ "RUNNING": 1,
+ "STOPPED": 2
+ });
+
+
+ /**
+ * Sometimes the framescript loads and starts up faster than the
+ * Overlay of the corresponding window. This is due to the async
+ * nature of framescripts. As a result, the overlay does not
+ * receive those early messages from the framescript.
+ *
+ * This class helps to ensure that any message to the overlay is
+ * actually being received. Instances take functions ("runnables")
+ * that will be called as soon as the overlay is ready. If the
+ * overlay is already in the "RUNNING" state, the function will be
+ * called immediately.
+ *
+ * @param {Environment} aEnv - The environment to which the
+ * communication channel's lifetime will be bound to.
+ * @constructor
+ */
+ function FramescriptToOverlayCommunication(aEnv) {
+ let self = this;
+
+ /**
+ * A queue of runnables that wait for the overlay to be ready.
+ * As it's a queue, the functions `pop` and `shift` have to be
+ * used.
+ * @type {Array.<function>}
+ */
+ self.waitingRunnables = [];
+
+ /**
+ * The state of the communication
+ * @type {States}
+ */
+ self.state = States.WAITING;
+
+ /**
+ * @type {Environment}
+ */
+ self.env = aEnv;
+
+ self.env.addStartupFunction(Environment.LEVELS.INTERFACE,
+ startCommNowOrLater.bind(null, self));
+ self.env.addShutdownFunction(Environment.LEVELS.INTERFACE,
+ stopCommunication.bind(null, self));
+ }
+
+ function dump(self, msg) {
+ Logger.dump(self.env.uid + ": " + msg);
+ }
+
+ /**
+ * Check whether the Overlay is ready. If it is, start the
+ * communication. If not, wait for the overlay to be ready.
+ *
+ * @param {FramescriptToOverlayCommunication} self
+ */
+ function startCommNowOrLater(self) {
+ let answers = self.env.mm.sendSyncMessage(C.MM_PREFIX + "isOverlayReady");
+ if (answers.length > 0 && answers[0] === true) {
+ startCommunication(self);
+ } else {
+ // The Overlay is not ready yet, so listen for the message.
+ // Add the listener immediately.
+ self.env.mlManager.addListener("overlayIsReady",
+ startCommunication.bind(null, self),
+ true);
+ }
+ }
+
+ /**
+ * The overlay is ready.
+ *
+ * @param {FramescriptToOverlayCommunication} self
+ */
+ function startCommunication(self) {
+ if (self.state === States.WAITING) {
+ //dump(self, "The Overlay is ready!");
+ self.state = States.RUNNING;
+
+ while (self.waitingRunnables.length !== 0) {
+ let runnable = self.waitingRunnables.shift();
+ //dump(self, "Lazily running function.");
+ runnable.call(null);
+ }
+ }
+ }
+
+ /**
+ * @param {FramescriptToOverlayCommunication} self
+ */
+ function stopCommunication(self) {
+ self.state = States.STOPPED;
+ }
+
+ /**
+ * Add a function that will be called now or later.
+ *
+ * @param {function} aRunnable
+ */
+ FramescriptToOverlayCommunication.prototype.run = function(aRunnable) {
+ let self = this;
+ switch (self.state) {
+ case States.RUNNING:
+ //dump(self, "Immediately running function.");
+ aRunnable.call(null);
+ break;
+
+ case States.WAITING:
+ //dump(self, "Remembering runnable.");
+ self.waitingRunnables.push(aRunnable);
+ break;
+
+ default:
+ //dump(self, "Ignoring runnable.");
+ break;
+ }
+ };
++
diff --cc content/lib/gui-location.jsm
index 59d01a8,ade6912..a1a9f97
--- a/content/lib/gui-location.jsm
+++ b/content/lib/gui-location.jsm
@@@ -263,3 -254,3 +254,4 @@@ GUILocationProperties.merge = function
return newObj;
};
++
diff --cc content/lib/http-response.jsm
index 0000000,c10b59c..e38cc5d
mode 000000,100644..100644
--- a/content/lib/http-response.jsm
+++ b/content/lib/http-response.jsm
@@@ -1,0 -1,181 +1,182 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ let EXPORTED_SYMBOLS = [
+ "HttpResponse"
+ ];
+
+ Cu.import("resource://gre/modules/Services.jsm");
+ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ ScriptLoader.importModules([
+ "lib/logger",
+ "lib/utils/domains",
+ "lib/utils/windows"
+ ], this);
+
+
+
+ function HttpResponse(aHttpChannel) {
+ this.httpChannel = aHttpChannel;
+
+ this.containsRedirection = undefined;
+ this.redirHeaderType = undefined;
+ this.redirHeaderValue = undefined;
+
+ // initialize
+ determineHttpHeader.call(this);
+ XPCOMUtils.defineLazyGetter(this, "rawDestString", getRawDestString);
+ XPCOMUtils.defineLazyGetter(this, "destURI", getDestURI);
+ XPCOMUtils.defineLazyGetter(this, "originURI", getOriginURI);
+
+ XPCOMUtils.defineLazyGetter(this, "loadContext", getLoadContext);
+ XPCOMUtils.defineLazyGetter(this, "browser", getBrowser);
+ }
+
+ HttpResponse.headerTypes = ["Location", "Refresh"];
+
+ HttpResponse.prototype.removeResponseHeader = function() {
+ this.httpChannel.setResponseHeader(this.redirHeaderType, "", false);
+ };
+
+
+
+
+
+ /**
+ * This function calls getResponseHeader(headerType). If there is no such
+ * header, that function will throw NS_ERROR_NOT_AVAILABLE.
+ */
+ function determineHttpHeader() {
+ this.containsRedirection = true;
+
+ for (let i in HttpResponse.headerTypes) {
+ try {
+ this.redirHeaderType = HttpResponse.headerTypes[i];
+ this.redirHeaderValue = this.httpChannel.getResponseHeader(this.redirHeaderType);
+ // In case getResponseHeader() didn't throw NS_ERROR_NOT_AVAILABLE,
+ // the header-type exists, so we return:
+ return;
+ } catch (e) {
+ }
+ }
+
+ // The following will be executed when there is no redirection:
+ this.containsRedirection = false;
+ this.redirHeaderType = null;
+ this.redirHeaderValue = null;
+ }
+
+ function getRawDestString() {
+ switch (this.redirHeaderType) {
+ case "Location":
+ return this.redirHeaderValue;
+
+ case "Refresh":
+ try {
+ // We can ignore the delay because we aren't manually doing
+ // the refreshes. Allowed refreshes we still leave to the browser.
+ // The rawDestString may be empty if the origin is what should be refreshed.
+ // This will be handled by DomainUtil.determineRedirectUri().
+ return DomainUtil.parseRefresh(this.redirHeaderValue).destURI;
+ } catch (e) {
+ Logger.warning(Logger.TYPE_HEADER_REDIRECT,
+ "Invalid refresh header: <" + this.redirHeaderValue + ">");
+ if (!Prefs.isBlockingDisabled()) {
+ this.removeResponseHeader();
+ }
+ return null;
+ }
+
+ default:
+ return null;
+ }
+ }
+
+ function getDestURI() {
+ return Services.io.newURI(this.rawDestString, null, this.originURI);
+ }
+
+ function getOriginURI() {
+ return Services.io.newURI(this.httpChannel.name, null, null);
+ }
+
+ function getLoadContext() {
+ // more info on the load context:
+ // https://developer.mozilla.org/en-US/Firefox/Releases/3.5/Updating_extensions
+
+ /* start - be careful when editing here */
+ try {
+ return this.httpChannel.notificationCallbacks
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsILoadContext);
+ } catch (ex) {
+ try {
+ return this.httpChannel.loadGroup
+ .notificationCallbacks
+ .getInterface(Ci.nsILoadContext);
+ } catch (ex2) {
+ // fixme: the Load Context can't be found in case a favicon
+ // request is redirected, that is, the server responds
+ // with a 'Location' header when the server's
+ // `favicon.ico` is requested.
+ Logger.warning(Logger.TYPE_HEADER_REDIRECT, "The redirection's " +
+ "Load Context couldn't be found! " + ex2);
+ return null;
+ }
+ }
+ /* end - be careful when editing here */
+ }
+
+ /**
+ * Get the <browser> (nsIDOMXULElement) related to this request.
+ */
+ function getBrowser() {
+ let loadContext = this.loadContext;
+
+ if (loadContext === null) {
+ return null;
+ }
+
+ try {
+ if (loadContext.topFrameElement) {
+ // the top frame element should be already the browser element
+ return loadContext.topFrameElement;
+ } else {
+ // we hope the associated window is available. in multiprocessor
+ // firefox it's not available.
+ return WindowUtils.getBrowserForWindow(loadContext.topWindow);
+ }
+ } catch (e) {
+ Logger.warning(Logger.TYPE_HEADER_REDIRECT, "The browser for " +
+ "the redirection's Load Context couldn't be " +
+ "found! " + e);
+ return null;
+ }
+ }
++
diff --cc content/lib/logger.jsm
index 0000000,3ccd6e2..df1e33c
mode 000000,100644..100644
--- a/content/lib/logger.jsm
+++ b/content/lib/logger.jsm
@@@ -1,0 -1,201 +1,191 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ let EXPORTED_SYMBOLS = ["Logger"];
+
+ Cu.import("resource://gre/modules/Services.jsm");
+ Cu.import("resource://gre/modules/devtools/Console.jsm");
+
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ ScriptLoader.importModules([
+ "lib/environment",
+ "lib/prefs"
+ ], this);
+
+
+ /**
+ * Provides logging methods
+ */
+ let Logger = (function() {
+
+ let self = {
+ TYPE_CONTENT: 1, // content whose origin isn't known more specifically
+ TYPE_META_REFRESH: 2, // info related to meta refresh
+ TYPE_HEADER_REDIRECT: 4, // info related to header redirects
+ TYPE_INTERNAL: 8, // internal happenings of the extension
+ TYPE_ERROR: 16, // errors
+ TYPE_POLICY: 32, // Policy changes, storage, etc.
+ TYPE_ALL: 0x0 - 1, // all
+
+ LEVEL_OFF: Number.MAX_VALUE, // no logging
+ LEVEL_SEVERE: 1000,
+ LEVEL_WARNING: 900,
+ LEVEL_INFO: 800,
+ LEVEL_DEBUG: 700,
+ LEVEL_ALL: Number.MIN_VALUE, // log everything
+ };
+
+ self._TYPE_NAMES = {};
+ self._TYPE_NAMES[self.TYPE_CONTENT.toString()] = "CONTENT";
+ self._TYPE_NAMES[self.TYPE_META_REFRESH.toString()] = "META_REFRESH";
+ self._TYPE_NAMES[self.TYPE_HEADER_REDIRECT.toString()] = "HEADER_REDIRECT";
+ self._TYPE_NAMES[self.TYPE_INTERNAL.toString()] = "INTERNAL";
+ self._TYPE_NAMES[self.TYPE_ERROR.toString()] = "ERROR";
+ self._TYPE_NAMES[self.TYPE_POLICY.toString()] = "POLICY";
+
+ self._LEVEL_NAMES = {};
+ self._LEVEL_NAMES[self.LEVEL_SEVERE.toString()] = "SEVERE";
+ self._LEVEL_NAMES[self.LEVEL_WARNING.toString()] = "WARNING";
+ self._LEVEL_NAMES[self.LEVEL_INFO.toString()] = "INFO";
+ self._LEVEL_NAMES[self.LEVEL_DEBUG.toString()] = "DEBUG";
+
+ // function to use to print out the log
+ self.printFunc = dump;
+
+
+
+
+ let initialized = false;
+
+ // initially, enable logging. later the logging preferences of the user will
+ // will be loaded.
+ let enabled = true;
+ // These can be set to change logging level, what types of messages are
+ // logged, and to enable/disable logging.
+ let level = self.LEVEL_INFO;
+ let types = self.TYPE_ALL;
+
+ function updateLoggingSettings(rp) {
+ enabled = rpPrefBranch.getBoolPref("log");
+ level = rpPrefBranch.getIntPref("log.level");
+ types = rpPrefBranch.getIntPref("log.types");
+ }
+
+ /**
+ * init() is called by doLog() until initialization was successful.
+ * For the case that nothing is logged at all, init is registered as a
+ * startup-function.
+ */
+ function init() {
+ if (initialized === true) {
+ // don't initialize several times
+ return;
+ }
+
+ // rpPrefBranch is available now.
+ ProcessEnvironment.obMan.observeRPPref(
+ ["log"],
+ function(subject, topic) {
+ if (topic === "nsPref:changed") {
+ updateLoggingSettings();
+ }
+ });
+ updateLoggingSettings();
+
+ // don't call init() anymore when doLog() is called
+ doLog = log;
+
+ initialized = true;
+ }
+
+ ProcessEnvironment.addStartupFunction(Environment.LEVELS.ESSENTIAL, init);
+
+
+
+
+ /**
+ * This function will be called in case Logger isn't fully initialized yet.
+ */
+ let initialLog = function() {
+ init();
+ log.apply(this, arguments);
+ };
+
+ let log = function(aLevel, aType, aMessage, aError) {
+ let shouldLog = (enabled && aLevel >= level && types & aType);
+
- // #ifdef UNIT_TESTING
- if (aType === self.TYPE_ERROR || aLevel === self.LEVEL_SEVERE) {
- // increment the error count
- let errorCount = rpPrefBranch.getIntPref("unitTesting.errorCount");
- rpPrefBranch.setIntPref("unitTesting.errorCount", ++errorCount);
- Services.prefs.savePrefFile(null);
-
- // log even if logging is disabled
- shouldLog = true;
- }
- // #endif
+
+ if (shouldLog) {
+ let levelName = self._LEVEL_NAMES[aLevel.toString()];
+ let typeName = self._TYPE_NAMES[aType.toString()];
+
+ let stack = (aError && aError.stack) ?
+ ", stack was:\n" + aError.stack : "";
+ self.printFunc("[RequestPolicy] [" + levelName + "] [" + typeName + "] "
+ + aMessage + stack + "\n");
+ }
+ };
+
+ /**
+ * Initially call initialLog() on doLog().
+ * After initialization it will be log().
+ */
+ let doLog = initialLog;
+
+
+
+ self.severe = doLog.bind(self, self.LEVEL_SEVERE);
+ self.severeError = doLog.bind(self, self.LEVEL_SEVERE, self.TYPE_ERROR);
+ self.warning = doLog.bind(self, self.LEVEL_WARNING);
+ self.info = doLog.bind(self, self.LEVEL_INFO);
+ self.debug = doLog.bind(self, self.LEVEL_DEBUG);
+ self.dump = doLog.bind(self, self.LEVEL_DEBUG, self.TYPE_INTERNAL);
+
+ self.vardump = function(obj, name, ignoreFunctions) {
+ if (name != undefined) {
+ self.dump(name + " : " + obj);
+ } else {
+ self.dump(obj);
+ }
+ for (var i in obj) {
+ try {
+ if (typeof obj[i] == 'function') {
+ if (!ignoreFunctions) {
+ self.dump(" => key: " + i + " / value: instanceof Function");
+ }
+ } else {
+ self.dump(" => key: " + i + " / value: " + obj[i]);
+ }
+ } catch (e) {
+ self.dump(" => key: " + i + " / value: [unable to access value]");
+ }
+ }
+ };
+
+ return self;
+ }());
++
diff --cc content/lib/manager-for-event-listeners.jsm
index 0000000,55dbfed..fe023e0
mode 000000,100644..100644
--- a/content/lib/manager-for-event-listeners.jsm
+++ b/content/lib/manager-for-event-listeners.jsm
@@@ -1,0 -1,152 +1,153 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ let EXPORTED_SYMBOLS = ["ManagerForEventListeners"];
+
+ let globalScope = this;
+
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ ScriptLoader.importModules([
+ "lib/environment",
+ "lib/logger"
+ ], globalScope);
+
+
+ /**
+ * This class provides an interface to multiple "Event Targets" which takes
+ * care of adding/removing event listeners at startup/shutdown.
+ * Every instance of this class is bound to an environment.
+ */
+ function ManagerForEventListeners(aEnv) {
+ let self = this;
+
+ /**
+ * an object holding all listeners for removing them when unloading the page
+ */
+ self.listeners = [];
+
+ /**
+ * This variable tells if the listener handed over to `addListener` should
+ * be added immediately or not. It is set to true when the startup function
+ * is called.
+ */
+ self.addNewListenersImmediately = false;
+
+ self.environment = aEnv;
+
+ // Note: the startup functions have to be defined *last*, as they might get
+ // called immediately.
+ if (!!aEnv) {
+ self.environment.addStartupFunction(
+ Environment.LEVELS.INTERFACE,
+ function() {
+ Logger.dump('From now on new event listeners will be ' +
+ 'added immediately. Environment: "' +
+ self.environment.name + '"');
+ self.addNewListenersImmediately = true;
+ self.addAllListeners();
+ });
+ self.environment.addShutdownFunction(
+ Environment.LEVELS.INTERFACE,
+ function() {
+ // clean up when the environment shuts down
+ self.removeAllListeners();
+ });
+ } else {
+ // aEnv is not defined! Try to report an error.
+ if (!!Logger) {
+ Logger.warning(Logger.TYPE_INTERNAL, "No Environment was specified for " +
+ "a new ManagerForEventListeners! This means that the " +
+ "listeners won't be removed!");
+ }
+ }
+ }
+
+
+ function addEvLis(listener) {
+ listener.target.addEventListener(listener.eventType, listener.callback,
+ listener.useCapture);
+ listener.listening = true;
+ };
+
+
+ ManagerForEventListeners.prototype.addListener = function(aEventTarget,
+ aEventType,
+ aCallback,
+ aUseCapture) {
+ let self = this;
+ if (typeof aCallback !== 'function') {
+ Logger.warning(Logger.TYPE_ERROR, "The callback for an event listener" +
+ 'must be a function! Event type was "' + aEventType + '"');
+ return;
+ }
+ let listener = {
+ target: aEventTarget,
+ eventType: aEventType,
+ callback: aCallback,
+ useCapture: !!aUseCapture,
+ listening: false
+ };
+ if (self.addNewListenersImmediately) {
+ Logger.dump('Immediately adding event listener for "' +
+ listener.eventType + '". Environment: "' +
+ self.environment.name + '"');
+ addEvLis(listener);
+ }
+ self.listeners.push(listener);
+ };
+
+
+
+ /**
+ * The function will add all listeners already in the list.
+ */
+ ManagerForEventListeners.prototype.addAllListeners = function() {
+ let self = this;
+ for (let listener of self.listeners) {
+ if (listener.listening === false) {
+ Logger.dump('Lazily adding event listener for "' +
+ listener.eventType + '". Environment: "' +
+ self.environment.name + '"');
+ addEvLis(listener);
+ }
+ }
+ };
+
+ /**
+ * The function will remove all listeners.
+ */
+ ManagerForEventListeners.prototype.removeAllListeners = function() {
+ let self = this;
+ while (self.listeners.length > 0) {
+ let listener = self.listeners.pop();
+ Logger.dump('Removing event listener for "' + listener.eventType +
+ '". Environment: "' + self.environment.name + '"');
+ listener.target.removeEventListener(listener.eventType, listener.callback,
+ listener.useCapture);
+ }
+ };
++
diff --cc content/lib/manager-for-message-listeners.jsm
index 0000000,35a2830..6139df6
mode 000000,100644..100644
--- a/content/lib/manager-for-message-listeners.jsm
+++ b/content/lib/manager-for-message-listeners.jsm
@@@ -1,0 -1,186 +1,187 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ let EXPORTED_SYMBOLS = ["ManagerForMessageListeners"];
+
+ let globalScope = this;
+
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ ScriptLoader.importModules([
+ "lib/environment",
+ "lib/logger",
+ "lib/utils/constants"
+ ], globalScope);
+
+
+ /**
+ * This class provides an interface to a "Message Manager" which takes
+ * care of adding/removing message listeners at startup/shutdown.
+ * Every instance of this class is bound to an environment and a MessageManager.
+ */
+ function ManagerForMessageListeners(aEnv, aMM) {
+ let self = this;
+
+ /**
+ * an object holding all listeners for removing them when unloading the page
+ */
+ self.listeners = [];
+
+ /**
+ * This variable tells if the listener handed over to `addListener` should
+ * be added immediately or not. It is set to true when the startup function
+ * is called.
+ */
+ self.addNewListenersImmediately = false;
+
+ self.environment = aEnv;
+
+ // Note: the startup functions have to be defined *last*, as they might get
+ // called immediately.
+ if (!!aEnv) {
+ self.environment.addStartupFunction(
+ Environment.LEVELS.INTERFACE,
+ function() {
+ Logger.dump('From now on new message listeners will be ' +
+ 'added immediately. Environment: "' +
+ self.environment.name + '"');
+ self.addNewListenersImmediately = true;
+ self.addAllListeners();
+ });
+ self.environment.addShutdownFunction(
+ Environment.LEVELS.INTERFACE,
+ function() {
+ // clean up when the environment shuts down
+ self.removeAllListeners();
+ });
+ } else {
+ // aEnv is not defined! Try to report an error.
+ if (!!Logger) {
+ Logger.warning(Logger.TYPE_INTERNAL, "No Environment was specified for " +
+ "a new ManagerForMessageListeners! This means that the listeners " +
+ "won't be unregistered!");
+ }
+ }
+
+ self.mm = aMM;
+
+ if (!self.mm) {
+ if (!!Logger) {
+ Logger.warning(Logger.TYPE_INTERNAL, "No Message Manager was specified " +
+ "for a new ManagerForMessageListeners!");
+ }
+ }
+ }
+
+
+ /**
+ * Add a listener. The class will then take care about adding
+ * and removing that message listener.
+ *
+ * @param {string} aMessageName
+ * @param {function} aCallback
+ * @param {boolean} aAddImmediately - Whether the listener should be
+ * added immediately, i.e. without waiting for the environment
+ * to start up.
+ */
+ ManagerForMessageListeners.prototype.addListener = function(aMessageName,
+ aCallback,
+ aAddImmediately) {
+ let self = this;
+ if (typeof aCallback !== 'function') {
+ Logger.warning(Logger.TYPE_ERROR, "The callback for a message listener" +
+ 'must be a function! The message name was "' + aMessageName +
+ '"');
+ return;
+ }
+ if (aMessageName.indexOf(C.MM_PREFIX) === 0) {
+ Logger.warning(Logger.TYPE_INTERNAL,
+ "The message name that has been passed to " +
+ "`addListener()` contains the MM Prefix. " +
+ "Extracting the message name.");
+ aMessageName = aMessageName.substr(C.MM_PREFIX.length)
+ }
+
+ let listener = {
+ messageName: aMessageName,
+ messageID: C.MM_PREFIX + aMessageName,
+ callback: aCallback,
+ listening: false
+ };
+ if (aAddImmediately === true || self.addNewListenersImmediately) {
+ Logger.dump('Immediately adding message listener for "' +
+ listener.messageName + '". Environment: "' +
+ self.environment.name + '"');
+ self.mm.addMessageListener(listener.messageID, listener.callback);
+ listener.listening = true;
+ }
+ self.listeners.push(listener);
+ };
+
+
+
+ /**
+ * The function will add all listeners already in the list.
+ */
+ ManagerForMessageListeners.prototype.addAllListeners = function() {
+ let self = this;
+ for (let listener of self.listeners) {
+ if (listener.listening === false) {
+ Logger.dump('Lazily adding message listener for "' +
+ listener.messageName + '". Environment: "' +
+ self.environment.name + '"');
+ self.mm.addMessageListener(listener.messageID, listener.callback);
+ listener.listening = true;
+ }
+ }
+ };
+
+ /**
+ * The function will remove all listeners.
+ */
+ ManagerForMessageListeners.prototype.removeAllListeners = function() {
+ let self = this;
+ while (self.listeners.length > 0) {
+ let listener = self.listeners.pop();
+ //if (typeof listener.callback == 'undefined') {
+ // Logger.warning(Logger.TYPE_ERROR, "Can't remove message listener '" +
+ // 'for "' + listener.messageName + '", the callback ' +
+ // 'is undefined!');
+ // continue;
+ //}
+ Logger.dump('Removing message listener for "' + listener.messageName +
+ '".');
+ //try {
+ self.mm.removeMessageListener(listener.messageID, listener.callback);
+ //} catch (e) {
+ // Logger.warning(Logger.TYPE_ERROR, 'Failed to remove message listener ' +
+ // 'for "' + listener.messageName + '". ' +
+ // 'Env "' + self.environment.uid + '" (' +
+ // self.environment.name + '). Error was: ' + e, e);
+ //}
+ }
+ };
++
diff --cc content/lib/observer-manager.jsm
index 0000000,43e05f2..b934ae3
mode 000000,100644..100644
--- a/content/lib/observer-manager.jsm
+++ b/content/lib/observer-manager.jsm
@@@ -1,0 -1,183 +1,184 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ /* global Components */
+ /* exported EXPORTED_SYMBOLS, ObserverManager */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ let EXPORTED_SYMBOLS = ["ObserverManager"];
+
+ let globalScope = this;
+
+ /* global ScriptLoader */
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ ScriptLoader.importModules([
+ "lib/utils/observers", /* global SingleTopicObserver,
+ SinglePrefBranchObserver */
+ "lib/environment" /* global Environment, ProcessEnvironment */
+ ], globalScope);
+
+ ScriptLoader.defineLazyModuleGetters({
+ "lib/prefs": [
+ "rpPrefBranch", /* global rpPrefBranch */
+ "rootPrefBranch" /* global rootPrefBranch */
+ ]
+ }, globalScope);
+
+
+
+ // Load the Logger at startup-time, not at load-time!
+ // ( On load-time Logger might be null. )
+ let Logger;
+ ProcessEnvironment.addStartupFunction(Environment.LEVELS.BACKEND, function() {
+ Logger = ScriptLoader.importModule("lib/logger").Logger;
+ });
+
+
+
+
+ /**
+ * An ObserverManager provides an interface to `nsIObserverService` which takes
+ * care of unregistering the observed topics. Every ObserverManager is bound to
+ * an environment.
+ */
+ function ObserverManager(aEnv) {
+ let self = this;
+
+ self.environment = aEnv;
+
+ if (!!aEnv) {
+ self.environment.addShutdownFunction(
+ Environment.LEVELS.INTERFACE,
+ function() {
+ // unregister when the environment shuts down
+ self.unregisterAllObservers();
+ });
+ } else {
+ // aEnv is not defined! Try to report an error.
+ if (!!Logger) {
+ Logger.warning(Logger.TYPE_INTERNAL, "No Environment was specified for " +
+ "a new ObserverManager! This means that the observers " +
+ "won't be unregistered!");
+ }
+ }
+
+ // an object holding all observers for unregistering when unloading the page
+ self.observers = [];
+ }
+
+
+ /**
+ * Define 'observe' functions. Those function can be called from anywhere;
+ * the caller hands over an object with the keys being the "IDs" and the values
+ * being the function that should be called when the "ID" is observed.
+ *
+ * The "ID" for each function might be something different.
+ */
+ {
+ //
+ // functions using nsIObserverService
+ //
+
+ /**
+ * Observe one single topic by using nsIObserverService. Details:
+ * https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIObserverService#addObserver%28%29
+ *
+ * @param {string} aTopic - The topic to be observed.
+ * @param {Function} aCallback - The observer's callback function.
+ */
+ ObserverManager.prototype.observeSingleTopic = function(aTopic, aCallback) {
+ let self = this;
+ self.observers.push(new SingleTopicObserver(aTopic, aCallback));
+ };
+
+ /**
+ * Observe multiple topics.
+ *
+ * @param {Array} aTopics - A list of topics to be observed.
+ * @param {function} aCallback - the function to be called when one of the
+ * the topics is observed.
+ */
+ ObserverManager.prototype.observe = function(aTopics, aCallback) {
+ let self = this;
+ aTopics.forEach(function(topic) {
+ self.observeSingleTopic(topic, aCallback);
+ });
+ };
+
+ /**
+ * A shorthand for calling observe() with topic "requestpolicy-prefs-changed".
+ */
+ ObserverManager.prototype.observePrefChanges = function(aCallback) {
+ let self = this;
+ self.observeSingleTopic("requestpolicy-prefs-changed", aCallback);
+ };
+
+ //
+ // functions using nsIPrefBranch
+ //
+
+ /**
+ * Observe one single subdomain of a Pref Branch (using nsIPrefBranch).
+ * Details: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIPrefBranch#addObserver%28%29
+ *
+ * @param {string} aTopic - The topic to be observed.
+ * @param {Function} aCallback - The observer's callback function.
+ */
+ ObserverManager.prototype.observeSinglePrefBranch = function(aPrefBranch,
+ aDomain,
+ aCallback) {
+ let self = this;
+ let obs = new SinglePrefBranchObserver(aPrefBranch, aDomain, aCallback);
+ self.observers.push(obs);
+ };
+
+ ObserverManager.prototype.observeRPPref = function(aDomains, aCallback) {
+ let self = this;
+ aDomains.forEach(function(domain) {
+ self.observeSinglePrefBranch(rpPrefBranch, domain, aCallback);
+ });
+ };
+ ObserverManager.prototype.observeRootPref = function(aDomains, aCallback) {
+ let self = this;
+ aDomains.forEach(function(domain) {
+ self.observeSinglePrefBranch(rootPrefBranch, domain, aCallback);
+ });
+ };
+ }
+
+
+
+ /**
+ * The function will unregister all registered observers.
+ */
+ ObserverManager.prototype.unregisterAllObservers = function() {
+ let self = this;
+ while (self.observers.length > 0) {
+ let observer = self.observers.pop();
+ observer.unregister();
+ }
+ };
++
diff --cc content/lib/policy-manager.alias-functions.js
index 0000000,99de8cf..18dd2fe
mode 000000,100644..100644
--- a/content/lib/policy-manager.alias-functions.js
+++ b/content/lib/policy-manager.alias-functions.js
@@@ -1,0 -1,98 +1,99 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+
+ let PolicyManager = (function(self) {
+
+ self.addAllowRule = self.addRule.bind(this, C.RULE_ACTION_ALLOW);
+ self.addTemporaryAllowRule = self.addTemporaryRule.bind(this,
+ C.RULE_ACTION_ALLOW);
+ self.removeAllowRule = self.removeRule.bind(this, C.RULE_ACTION_ALLOW);
+ self.addDenyRule = self.addRule.bind(this, C.RULE_ACTION_DENY);
+ self.addTemporaryDenyRule = self.addTemporaryRule.bind(this,
+ C.RULE_ACTION_DENY);
+ self.removeDenyRule = self.removeRule.bind(this, C.RULE_ACTION_DENY);
+
+
+ function getRuleData(aOrigin, aDest) {
+ let ruleData = {};
+ if (aOrigin !== undefined) {
+ ruleData["o"] = {"h": aOrigin};
+ }
+ if (aDest !== undefined) {
+ ruleData["d"] = {"h": aDest};
+ }
+ return ruleData;
+ }
+
+
+ function allowOrigin(aOrigin, noStore) {
+ self.addAllowRule(getRuleData(aOrigin), noStore);
+ }
+ self.allowOrigin = function(aOrigin) {
+ allowOrigin(aOrigin, false);
+ };
+ self.allowOriginDelayStore = function(aOrigin) {
+ allowOrigin(aOrigin, true);
+ };
+
+
+ self.temporarilyAllowOrigin = function(aOrigin) {
+ PolicyManager.addTemporaryAllowRule(getRuleData(aOrigin));
+ };
+ self.temporarilyAllowDestination = function(aDest) {
+ self.addTemporaryAllowRule(getRuleData(undefined, aDest));
+ };
+
+
+ function allowDestination(aDest, noStore) {
+ self.addAllowRule(getRuleData(undefined, aDest), noStore);
+ }
+ self.allowDestination = function(aDest) {
+ allowDestination(aDest, false);
+ };
+ self.allowDestinationDelayStore = function(aDest) {
+ allowDestination(aDest, true);
+ };
+
+
+ function allowOriginToDestination(originIdentifier, destIdentifier, noStore) {
+ self.addAllowRule(getRuleData(originIdentifier, destIdentifier), noStore);
+ };
+ self.allowOriginToDestination = function(originIdentifier, destIdentifier) {
+ allowOriginToDestination(originIdentifier, destIdentifier, false);
+ };
+ self.allowOriginToDestinationDelayStore = function(originIdentifier,
+ destIdentifier) {
+ allowOriginToDestination(originIdentifier, destIdentifier, true);
+ };
+
+
+ self.temporarilyAllowOriginToDestination = function(originIdentifier,
+ destIdentifier) {
+ PolicyManager.addTemporaryAllowRule(getRuleData(originIdentifier,
+ destIdentifier));
+ };
+
+ return self;
+ }(PolicyManager || {}));
++
diff --cc content/lib/policy-manager.jsm
index 0057594,0035a41..c9a4627
--- a/content/lib/policy-manager.jsm
+++ b/content/lib/policy-manager.jsm
@@@ -337,6 -342,11 +342,12 @@@ let PolicyManager = (function(self)
}
}
return result;
- }
+ };
+
+ return self;
+ }(PolicyManager || {}));
+
+
+ Services.scriptloader.loadSubScript(
+ "chrome://requestpolicy/content/lib/policy-manager.alias-functions.js");
+
- };
diff --cc content/lib/prefs.jsm
index 0000000,fe103a0..db5f4f5
mode 000000,100644..100644
--- a/content/lib/prefs.jsm
+++ b/content/lib/prefs.jsm
@@@ -1,0 -1,149 +1,150 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ let EXPORTED_SYMBOLS = ['rpPrefBranch', 'rootPrefBranch', 'Prefs'];
+
+ Cu.import("resource://gre/modules/Services.jsm");
+
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ ScriptLoader.importModules(["lib/environment"], this);
+
+
+
+ let rpPrefBranch = Services.prefs.getBranch("extensions.requestpolicy.")
+ .QueryInterface(Ci.nsIPrefBranch2);
+ let rootPrefBranch = Services.prefs.getBranch("")
+ .QueryInterface(Ci.nsIPrefBranch2);
+
+
+
+ let Prefs = (function() {
+ let self = {};
+
+ let defaultAllow = true;
+ let defaultAllowSameDomain = true;
+ let blockingDisabled = false;
+
+
+
+ self.save = function() {
+ Services.prefs.savePrefFile(null);
+ };
+
+
+ function getRPBoolPref(aPrefName) {
+ return rpPrefBranch.getBoolPref(aPrefName);
+ }
+ function setRPBoolPref(aPrefName, aValue) {
+ rpPrefBranch.setBoolPref(aPrefName, aValue);
+ }
+ // not needed yet
+ //function getInvertedRPBoolPref(aPrefName) {
+ // return !rpPrefBranch.getBoolPref(aPrefName);
+ //}
+ //function setInvertedRPBoolPref(aPrefName, aValue) {
+ // rpPrefBranch.setBoolPref(aPrefName, !aValue);
+ //}
+
+ /**
+ * Define a list of pref aliases that will be available through
+ * `Prefs.getter_function_name()` and `Prefs.setter_function_name()`.
+ * Those functions will be added to `self` subsequently.
+ */
+ let rpPrefAliases = {
+ "bool": {
+ "defaultPolicy.allow": "DefaultAllow",
+ "defaultPolicy.allowSameDomain": "DefaultAllowSameDomain",
+
+ // As an example, this will become `isBlockingDisabled()` and
+ // `setBlockingDisabled()`:
+ "startWithAllowAllEnabled": "BlockingDisabled"
+ }
+ };
+
+ /**
+ * Dynamically create functions like `isDefaultAllow` or
+ * `setBlockingDisabled`.
+ */
+ {
+ for (let prefID in rpPrefAliases.bool) {
+ let prefName = rpPrefAliases.bool[prefID];
+
+ // define the pref's getter function to `self`
+ self["is"+prefName] = getRPBoolPref.bind(this, prefID);
+
+ // define the pref's getter function to `self`
+ self["set"+prefName] = setRPBoolPref.bind(this, prefID);
+ }
+ }
+
+ self.isPrefetchEnabled = function() {
+ return rootPrefBranch.getBoolPref("network.prefetch-next")
+ || !rootPrefBranch.getBoolPref("network.dns.disablePrefetch");
+ };
+
+ function isPrefEmpty(pref) {
+ try {
+ let value = rpPrefBranch.getComplexValue(pref, Ci.nsISupportsString).data;
+ return value == '';
+ } catch (e) {
+ return true;
+ }
+ }
+
+ self.oldRulesExist = function() {
+ return !(isPrefEmpty('allowedOrigins') &&
+ isPrefEmpty('allowedDestinations') &&
+ isPrefEmpty('allowedOriginsToDestinations'));
+ };
+
+
+
+ function observePref(subject, topic, data) {
+ if (topic == "nsPref:changed") {
+ // Send an observer notification that a pref that affects RP has been
+ // changed.
+ // TODO: also send the pref's name and its branch
+ Services.obs.notifyObservers(null, "requestpolicy-prefs-changed", null);
+ }
+ };
+
+ function registerPrefObserver() {
+ // observe everything on RP's pref branch
+ ProcessEnvironment.obMan.observeRPPref([""], observePref);
+
+ // observe what is needed else
+ ProcessEnvironment.obMan.observeRootPref([
+ "network.prefetch-next",
+ "network.dns.disablePrefetch"
+ ], observePref);
+ }
+ ProcessEnvironment.addStartupFunction(Environment.LEVELS.INTERFACE,
+ registerPrefObserver);
+
+ return self;
+ }());
++
diff --cc content/lib/request-processor.compat.js
index 0000000,4d63b30..d793dba
mode 000000,100644..100644
--- a/content/lib/request-processor.compat.js
+++ b/content/lib/request-processor.compat.js
@@@ -1,0 -1,320 +1,321 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ Cu.import("resource://gre/modules/AddonManager.jsm");
+
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ ScriptLoader.importModules([
+ "lib/logger",
+ "lib/utils",
+ "lib/environment"
+ ], this);
+
+
+
+ let RequestProcessor = (function(self) {
+ let internal = Utils.moduleInternal(self);
+
+
+ let conflictingExtensions = [];
+ let compatibilityRules = [];
+ let topLevelDocTranslationRules = {};
+
+ // TODO: update compatibility rules etc. when addons are enabled/disabled
+ let addonListener = {
+ onDisabling : function(addon, needsRestart) {},
+ onUninstalling : function(addon, needsRestart) {},
+ onOperationCancelled : function(addon, needsRestart) {}
+ };
+
+ function init() {
+ // Detect other installed extensions and the current application and do
+ // what is needed to allow their requests.
+ initializeExtensionCompatibility();
+ initializeApplicationCompatibility();
+
+ AddonManager.addAddonListener(addonListener);
+ }
+
+ // stop observers / listeners
+ function cleanup() {
+ AddonManager.removeAddonListener(addonListener);
+ }
+
+ ProcessEnvironment.addStartupFunction(Environment.LEVELS.BACKEND, init);
+ ProcessEnvironment.addShutdownFunction(Environment.LEVELS.BACKEND, cleanup);
+
+
+ function initializeExtensionCompatibility() {
+ if (compatibilityRules.length != 0) {
+ return;
+ }
+
+ var idArray = [];
+ idArray.push("greasefire at skrul.com"); // GreaseFire
+ idArray.push("{0f9daf7e-2ee2-4fcf-9d4f-d43d93963420}"); // Sage-Too
+ idArray.push("{899DF1F8-2F43-4394-8315-37F6744E6319}"); // NewsFox
+ idArray.push("brief at mozdev.org"); // Brief
+ idArray.push("foxmarks at kei.com"); // Xmarks Sync (a.k.a. Foxmarks)
+ // Norton Safe Web Lite Toolbar
+ idArray.push("{203FB6B2-2E1E-4474-863B-4C483ECCE78E}");
+ // Norton Toolbar (a.k.a. NIS Toolbar)
+ idArray.push("{0C55C096-0F1D-4F28-AAA2-85EF591126E7}");
+ // Norton Toolbar 2011.7.0.8
+ idArray.push("{2D3F3651-74B9-4795-BDEC-6DA2F431CB62}");
+ idArray.push("{c45c406e-ab73-11d8-be73-000a95be3b12}"); // Web Developer
+ idArray.push("{c07d1a49-9894-49ff-a594-38960ede8fb9}"); // Update Scanner
+ idArray.push("FirefoxAddon at similarWeb.com"); // SimilarWeb
+ idArray.push("{6614d11d-d21d-b211-ae23-815234e1ebb5}"); // Dr. Web Link Checker
+
+ for (let i in idArray) {
+ Logger.info(Logger.TYPE_INTERNAL, "Extension check: " + idArray[i]);
+ AddonManager.getAddonByID(idArray[i], initializeExtCompatCallback);
+ }
+ }
+
+
+
+
+ function initializeExtCompatCallback(ext) {
+ if (!ext) {
+ return;
+ }
+
+ if (ext.isActive === false) {
+ Logger.info(Logger.TYPE_INTERNAL, "Extension is not active: " + ext.name);
+ return;
+ }
+
+ switch (ext.id) {
+ case "greasefire at skrul.com" : // Greasefire
+ Logger.info(Logger.TYPE_INTERNAL,
+ "Using extension compatibility rules for: " + ext.name);
+ compatibilityRules.push(
+ ["file://", "http://userscripts.org/", ext.name]);
+ compatibilityRules.push(
+ ["file://", "http://static.userscripts.org/", ext.name]);
+ break;
+ case "{0f9daf7e-2ee2-4fcf-9d4f-d43d93963420}" : // Sage-Too
+ case "{899DF1F8-2F43-4394-8315-37F6744E6319}" : // NewsFox
+ case "brief at mozdev.org" : // Brief
+ Logger.info(Logger.TYPE_INTERNAL, "Conflicting extension: " + ext.name);
+ compatibilityRules.push(
+ ["resource://brief-content/", null, ext.name]);
+ conflictingExtensions.push({
+ "id" : ext.id,
+ "name" : ext.name,
+ "version" : ext.version
+ });
+ break;
+ case "foxmarks at kei.com" : // Xmarks Sync
+ Logger.info(Logger.TYPE_INTERNAL,
+ "Using extension compatibility rules for: " + ext.name);
+ compatibilityRules.push([
+ "https://login.xmarks.com/",
+ "https://static.xmarks.com/",
+ ext.name
+ ]);
+ break;
+ case "{203FB6B2-2E1E-4474-863B-4C483ECCE78E}" : // Norton Safe Web Lite
+ case "{0C55C096-0F1D-4F28-AAA2-85EF591126E7}" : // Norton NIS Toolbar
+ case "{2D3F3651-74B9-4795-BDEC-6DA2F431CB62}" : // Norton Toolbar
+ Logger.info(Logger.TYPE_INTERNAL,
+ "Using extension compatibility rules for: " + ext.name);
+ compatibilityRules.push([null, "symnst:", ext.name]);
+ compatibilityRules.push([null, "symres:", ext.name]);
+ break;
+ case "{c45c406e-ab73-11d8-be73-000a95be3b12}" : // Web Developer
+ Logger.info(Logger.TYPE_INTERNAL,
+ "Using extension compatibility rules for: " + ext.name);
+ compatibilityRules.push([
+ "about:blank",
+ "http://jigsaw.w3.org/css-validator/validator",
+ ext.name
+ ]);
+ compatibilityRules.push(
+ ["about:blank", "http://validator.w3.org/check", ext.name]);
+ break;
+ case "{c07d1a49-9894-49ff-a594-38960ede8fb9}" : // Update Scanner
+ Logger.info(Logger.TYPE_INTERNAL,
+ "Using extension compatibility rules for: " + ext.name);
+ var orig = "chrome://updatescan/content/diffPage.xul";
+ var translated = "data:text/html";
+ topLevelDocTranslationRules[orig] = translated;
+ break;
+ case "FirefoxAddon at similarWeb.com" : // SimilarWeb
+ Logger.info(Logger.TYPE_INTERNAL,
+ "Using extension compatibility rules for: " + ext.name);
+ compatibilityRules.push([
+ "http://api2.similarsites.com/",
+ "http://images2.similargroup.com/",
+ ext.name
+ ]);
+ compatibilityRules.push([
+ "http://www.similarweb.com/",
+ "http://go.similarsites.com/",
+ ext.name
+ ]);
+ break;
+ case "{6614d11d-d21d-b211-ae23-815234e1ebb5}" : // Dr. Web Link Checker
+ Logger.info(Logger.TYPE_INTERNAL,
+ "Using extension compatibility rules for: " + ext.name);
+ compatibilityRules.push([null, "http://st.drweb.com/", ext.name]);
+ break;
+ default :
+ Logger.severe(Logger.TYPE_INTERNAL,
+ "Unhandled extension (id typo?): " + ext.name);
+ break;
+ }
+ }
+
+
+
+
+ function initializeApplicationCompatibility() {
+ var appInfo = Cc["@mozilla.org/xre/app-info;1"].
+ getService(Ci.nsIXULAppInfo);
+
+ // Mozilla updates (doing this for all applications, not just individual
+ // applications from the Mozilla community that I'm aware of).
+ // At least the http url is needed for Firefox updates, adding the https
+ // one as well to be safe.
+ compatibilityRules.push(
+ ["http://download.mozilla.org/", null, appInfo.vendor]);
+ compatibilityRules.push(
+ ["https://download.mozilla.org/", null, appInfo.vendor]);
+ // There are redirects from 'addons' to 'releases' when installing addons
+ // from AMO. Adding the origin of 'releases' to be safe in case those
+ // start redirecting elsewhere at some point.
+ compatibilityRules.push(
+ ["http://addons.mozilla.org/", null, appInfo.vendor]);
+ compatibilityRules.push(
+ ["https://addons.mozilla.org/", null, appInfo.vendor]);
+ compatibilityRules.push(
+ ["http://releases.mozilla.org/", null, appInfo.vendor]);
+ compatibilityRules.push(
+ ["https://releases.mozilla.org/", null, appInfo.vendor]);
+ // Firefox 4 has the about:addons page open an iframe to the mozilla site.
+ // That opened page grabs content from other mozilla domains.
+ compatibilityRules.push([
+ "about:addons",
+ "https://services.addons.mozilla.org/",
+ appInfo.vendor
+ ]);
+ compatibilityRules.push([
+ "https://services.addons.mozilla.org/",
+ "https://static.addons.mozilla.net/",
+ appInfo.vendor
+ ]);
+ compatibilityRules.push([
+ "https://services.addons.mozilla.org/",
+ "https://addons.mozilla.org/",
+ appInfo.vendor]);
+ compatibilityRules.push([
+ "https://services.addons.mozilla.org/",
+ "https://www.mozilla.com/",
+ appInfo.vendor
+ ]);
+ compatibilityRules.push([
+ "https://services.addons.mozilla.org/",
+ "https://www.getpersonas.com/",
+ appInfo.vendor
+ ]);
+ compatibilityRules.push([
+ "https://services.addons.mozilla.org/",
+ "https://static-cdn.addons.mozilla.net/",
+ appInfo.vendor
+ ]);
+ compatibilityRules.push([
+ "https://services.addons.mozilla.org/",
+ "https://addons.cdn.mozilla.net/",
+ appInfo.vendor
+ ]);
+ // Firefox 4 uses an about:home page that is locally stored but can be
+ // the origin for remote requests. See bug #140 for more info.
+ compatibilityRules.push(["about:home", null, appInfo.vendor]);
+ // Firefox Sync uses a google captcha.
+ compatibilityRules.push([
+ "https://auth.services.mozilla.com/",
+ "https://api-secure.recaptcha.net/challenge?",
+ appInfo.vendor
+ ]);
+ compatibilityRules.push([
+ "https://api-secure.recaptcha.net/challenge?",
+ "https://www.google.com/recaptcha/api/challenge?",
+ appInfo.vendor
+ ]);
+ compatibilityRules.push([
+ "https://auth.services.mozilla.com/",
+ "https://www.google.com/recaptcha/api/",
+ appInfo.vendor
+ ]);
+ // Firefox 13 added links from about:newtab
+ compatibilityRules.push(["about:newtab", null, appInfo.vendor]);
+
+ // Flock
+ if (appInfo.ID == "{a463f10c-3994-11da-9945-000d60ca027b}") {
+ Logger.info(Logger.TYPE_INTERNAL,
+ "Application detected: " + appInfo.vendor);
+ compatibilityRules.push(
+ ["about:myworld", "http://www.flock.com/", appInfo.vendor]);
+ compatibilityRules.push(["about:flock", null, appInfo.vendor]);
+ compatibilityRules.push([
+ "http://www.flock.com/rss",
+ "http://feeds.feedburner.com/flock",
+ appInfo.vendor
+ ]);
+ compatibilityRules.push([
+ "http://feeds.feedburner.com/",
+ "http://www.flock.com/",
+ appInfo.vendor
+ ]);
+ }
+
+ // Seamonkey
+ if (appInfo.ID == "{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}") {
+ Logger.info(Logger.TYPE_INTERNAL, "Application detected: Seamonkey");
+ compatibilityRules.push(["mailbox:", null, "Seamonkey"]);
+ compatibilityRules.push([null, "mailbox:", "Seamonkey"]);
+ }
+ }
+
+
+
+
+ self.getCompatibilityRules = function() {
+ return compatibilityRules;
+ };
+
+ self.getConflictingExtensions = function() {
+ return conflictingExtensions;
+ };
+
+ self.getTopLevelDocTranslation = function(uri) {
+ // We're not sure if the array will be fully populated during init. This
+ // is especially a concern given the async addon manager API in Firefox 4.
+ return topLevelDocTranslationRules[uri] || null;
+ };
+
+ return self;
+ }(RequestProcessor || {}));
++
diff --cc content/lib/request-processor.jsm
index 0000000,71b54ad..e90e4f6
mode 000000,100644..100644
--- a/content/lib/request-processor.jsm
+++ b/content/lib/request-processor.jsm
@@@ -1,0 -1,1075 +1,1076 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cr = Components.results;
+ const Cu = Components.utils;
+
+ let EXPORTED_SYMBOLS = ["RequestProcessor"];
+
+ const CP_OK = Ci.nsIContentPolicy.ACCEPT;
+ const CP_REJECT = Ci.nsIContentPolicy.REJECT_SERVER;
+
+ // A value intended to not conflict with aExtra passed to shouldLoad() by any
+ // other callers. Was chosen randomly.
+ const CP_MAPPEDDESTINATION = 0x178c40bf;
+
+ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+ Cu.import("resource://gre/modules/Services.jsm");
+
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ ScriptLoader.importModules([
+ "lib/logger",
+ "lib/prefs",
+ "lib/policy-manager",
+ "lib/utils/domains",
+ "lib/utils",
+ "lib/request",
+ "lib/request-result",
+ "lib/request-set",
+ "lib/environment"
+ ], this);
+ ScriptLoader.defineLazyModuleGetters({
+ "main/content-policy": ["PolicyImplementation"]
+ }, this);
+
+
+
+ let RequestProcessor = (function(self) {
+ let internal = Utils.moduleInternal(self);
+
+
+ /**
+ * Number of elapsed milliseconds from the time of the last shouldLoad() call
+ * at which the cached results of the last shouldLoad() call are discarded.
+ *
+ * @type {number}
+ */
+ let lastShouldLoadCheckTimeout = 200;
+
+ // Calls to shouldLoad appear to be repeated, so successive repeated calls and
+ // their result (accept or reject) are tracked to avoid duplicate processing
+ // and duplicate logging.
+ /**
+ * Object that caches the last shouldLoad
+ * @type {Object}
+ */
+ let lastShouldLoadCheck = {
+ "origin" : null,
+ "destination" : null,
+ "time" : 0,
+ "result" : null
+ };
+
+ let historyRequests = {};
+
+ internal.submittedForms = {};
+ internal.submittedFormsReverse = {};
+
+ internal.clickedLinks = {};
+ internal.clickedLinksReverse = {};
+
+ internal.faviconRequests = {};
+
+ internal.mappedDestinations = {};
+
+ internal.requestObservers = [];
+
+
+
+
+
+
+
+
+
+
+ function notifyRequestObserversOfBlockedRequest(request) {
+ for (var i = 0; i < internal.requestObservers.length; i++) {
+ if (!internal.requestObservers[i]) {
+ continue;
+ }
+ internal.requestObservers[i].observeBlockedRequest(request.originURI,
+ request.destURI, request.requestResult);
+ }
+ }
+
+ function notifyRequestObserversOfAllowedRequest(originUri,
+ destUri, requestResult) {
+ for (var i = 0; i < internal.requestObservers.length; i++) {
+ if (!internal.requestObservers[i]) {
+ continue;
+ }
+ internal.requestObservers[i].observeAllowedRequest(originUri, destUri,
+ requestResult);
+ }
+ }
+
+
+
+
+
+
+
+ // We always call this from shouldLoad to reject a request.
+ function reject(reason, request) {
+ Logger.warning(Logger.TYPE_CONTENT, "** BLOCKED ** reason: " + reason +
+ ". " + request.detailsToString());
+
+ if (Prefs.isBlockingDisabled()) {
+ return CP_OK;
+ }
+
+ if (request.aContext) {
+ request.aContext.requestpolicyBlocked = true;
+ }
+
+ cacheShouldLoadResult(CP_REJECT, request.originURI, request.destURI);
+ internal.recordRejectedRequest(request);
+
+ if (Ci.nsIContentPolicy.TYPE_DOCUMENT === request.aContentType) {
+ // This was a blocked top-level document request. This may be due to
+ // a blocked attempt by javascript to set the document location.
+
+ let browser = request.getBrowser();
+ let window = request.getChromeWindow();
+
+ if (!browser || !window) {
+ Logger.warning(Logger.TYPE_CONTENT, "The user could not be notified " +
+ "about the blocked top-level document request!");
+ } else {
+ window.requestpolicy.overlay.observeBlockedTopLevelDocRequest(
+ browser, request.originURI, request.destURI);
+ }
+ }
+
+ return CP_REJECT;
+ }
+
+ internal.recordRejectedRequest = function(request) {
+ self._rejectedRequests.addRequest(request.originURI, request.destURI,
+ request.requestResult);
+ self._allowedRequests.removeRequest(request.originURI, request.destURI);
+ notifyRequestObserversOfBlockedRequest(request);
+ }
+
+ // We only call this from shouldLoad when the request was a remote request
+ // initiated by the content of a page. this is partly for efficiency. in other
+ // cases we just return CP_OK rather than return this function which
+ // ultimately returns CP_OK. Fourth param, "unforbidable", is set to true if
+ // this request shouldn't be recorded as an allowed request.
+ /**
+ * @param {string} reason
+ * @param {Request} request
+ * @param {boolean} unforbidable
+ */
+ function accept(reason, request, unforbidable) {
+ Logger.warning(Logger.TYPE_CONTENT, "** ALLOWED ** reason: " +
+ reason + ". " + request.detailsToString());
+
+ cacheShouldLoadResult(CP_OK, request.originURI, request.destURI);
+ // We aren't recording the request so it doesn't show up in the menu, but we
+ // want it to still show up in the request log.
+ if (unforbidable) {
+ notifyRequestObserversOfAllowedRequest(request.originURI, request.destURI,
+ request.requestResult);
+ } else {
+ internal.recordAllowedRequest(request.originURI, request.destURI, false,
+ request.requestResult);
+ }
+
+ return CP_OK;
+ }
+
+ function recordAllowedRequest(originUri, destUri, isInsert, requestResult) {
+ // Reset the accepted and rejected requests originating from this
+ // destination. That is, if this accepts a request to a uri that may itself
+ // originate further requests, reset the information about what that page is
+ // accepting and rejecting.
+ // If "isInsert" is set, then we don't want to clear the destUri info.
+ if (true !== isInsert) {
+ self._allowedRequests.removeOriginUri(destUri);
+ self._rejectedRequests.removeOriginUri(destUri);
+ }
+ self._rejectedRequests.removeRequest(originUri, destUri);
+ self._allowedRequests.addRequest(originUri, destUri, requestResult);
+ notifyRequestObserversOfAllowedRequest(originUri, destUri, requestResult);
+ }
+ internal.recordAllowedRequest = recordAllowedRequest;
+
+ function cacheShouldLoadResult(result, originUri, destUri) {
+ var date = new Date();
+ lastShouldLoadCheck.time = date.getTime();
+ lastShouldLoadCheck.destination = destUri;
+ lastShouldLoadCheck.origin = originUri;
+ lastShouldLoadCheck.result = result;
+ }
+
+ internal.checkByDefaultPolicy = function(originUri, destUri) {
+ if (Prefs.isDefaultAllow()) {
+ var result = new RequestResult(true,
+ REQUEST_REASON_DEFAULT_POLICY);
+ return result;
+ }
+
+ if (Prefs.isDefaultAllowSameDomain()) {
+ var originDomain = DomainUtil.getBaseDomain(originUri);
+ var destDomain = DomainUtil.getBaseDomain(destUri);
+
+ if (originDomain !== null && destDomain !== null) {
+ // apply this rule only if both origin and dest URIs
+ // do have a host.
+ return new RequestResult(originDomain === destDomain,
+ REQUEST_REASON_DEFAULT_SAME_DOMAIN);
+ }
+ }
+ // We probably want to allow requests from http:80 to https:443 of the same
+ // domain. However, maybe this is so uncommon it's not worth any extra
+ // complexity.
+ var originIdent = DomainUtil.getIdentifier(originUri, DomainUtil.LEVEL_SOP);
+ var destIdent = DomainUtil.getIdentifier(destUri, DomainUtil.LEVEL_SOP);
+ return new RequestResult(originIdent === destIdent,
+ REQUEST_REASON_DEFAULT_SAME_DOMAIN);
+ };
+
+ /**
+ * Determines if a request is a duplicate of the last call to shouldLoad().
+ * If it is, the cached result in lastShouldLoadCheck.result can be used.
+ * Using this simple cache of the last call to shouldLoad() keeps duplicates
+ * out of log data.
+ *
+ * Duplicate shouldLoad() calls can be produced for example by creating a
+ * page containing many <img> with the same image (same `src`).
+ *
+ * @param {Request} request
+ * @return {boolean} True if the request is a duplicate of the previous one.
+ */
+ function isDuplicateRequest(request) {
+ if (lastShouldLoadCheck.origin == request.originURI &&
+ lastShouldLoadCheck.destination == request.destURI) {
+ var date = new Date();
+ if (date.getTime() - lastShouldLoadCheck.time <
+ lastShouldLoadCheckTimeout) {
+ Logger.debug(Logger.TYPE_CONTENT,
+ "Using cached shouldLoad() result of " +
+ lastShouldLoadCheck.result + " for request to <" +
+ request.destURI + "> from <" + request.originURI + ">.");
+ return true;
+ } else {
+ Logger.debug(Logger.TYPE_CONTENT,
+ "shouldLoad() cache expired for result of " +
+ lastShouldLoadCheck.result + " for request to <" +
+ request.destURI + "> from <" + request.originURI + ">.");
+ }
+ }
+ return false;
+ }
+
+
+
+ function _getRequestsHelper(currentlySelectedOrigin, allRequestsOnDocument,
+ isAllowed) {
+ var result = new RequestSet();
+ var requests = allRequestsOnDocument.getAll();
+
+ // We're assuming ident is fullIdent (LEVEL_SOP). We plan to remove base
+ // domain and hostname levels.
+ for (var originUri in requests) {
+ if (DomainUtil.getBaseDomain(originUri) !== currentlySelectedOrigin) {
+ // only return requests from the given base domain
+ continue;
+ }
+ Logger.dump("test destBase: " + destBase);
+ for (var destBase in requests[originUri]) {
+ Logger.dump("test destBase: " + destBase);
+ for (var destIdent in requests[originUri][destBase]) {
+ Logger.dump("test destIdent: " + destIdent);
+ for (var destUri in requests[originUri][destBase][destIdent]) {
+ Logger.dump("test destUri: " + destUri);
+ var dest = requests[originUri][destBase][destIdent][destUri];
+ for (var i in dest) {
+ // TODO: This variable could have been created easily already in
+ // getAllRequestsInBrowser(). ==> rewrite RequestSet to
+ // contain a blocked list, an allowed list (and maybe a list
+ // of all requests).
+ if (isAllowed === dest[i].isAllowed) {
+ result.addRequest(originUri, destUri, dest[i]);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ // function _getOtherOriginsHelperFromDOM(document, reqSet) {
+ // var documentUri = DomainUtil
+ // .stripFragment(document.documentURI);
+ // Logger.dump("Looking for other origins within DOM of "
+ // + documentUri);
+ // // TODO: Check other elements besides iframes and frames?
+ // var frameTagTypes = {
+ // "iframe" : null,
+ // "frame" : null
+ // };
+ // for (var tagType in frameTagTypes) {
+ // var iframes = document.getElementsByTagName(tagType);
+ // for (var i = 0; i < iframes.length; i++) {
+ // var child = iframes[i];
+ // var childDocument = child.contentDocument;
+ // // Flock's special home page is about:myworld. It has (i)frames in it
+ // // that have no contentDocument. It's probably related to the fact that
+ // // that is an xul page, but I have no reason to fully understand the
+ // // problem in order to fix it.
+ // if (!childDocument) {
+ // continue;
+ // }
+ // var childUri = DomainUtil
+ // .stripFragment(childDocument.documentURI);
+ // if (childUri == "about:blank") {
+ // // iframe empty or not loaded yet, or maybe blocked.
+ // // childUri = child.src;
+ // // If it's not loaded or blocked, it's not the origin for anything
+ // // yet.
+ // continue;
+ // }
+ // Logger.dump("Found DOM child " + tagType
+ // + " with src <" + childUri + "> in document <" + documentUri + ">");
+ // //var childUriIdent = DomainUtil.getIdentifier(childUri,
+ // // DomainUtil.LEVEL_SOP);
+ // // if (!origins[childUriIdent]) {
+ // // origins[childUriIdent] = {};
+ // // }
+ // // origins[childUriIdent][childUri] = true;
+ // reqSet.addRequest(documentUri, childUri);
+ // _getOtherOriginsHelperFromDOM(childDocument, reqSet);
+ // }
+ // }
+ // },
+
+ function _addRecursivelyAllRequestsFromURI(originURI, reqSet,
+ checkedOrigins) {
+ Logger.dump("Looking for other origins within allowed requests from "
+ + originURI);
+ if (!checkedOrigins[originURI]) {
+ // this "if" is needed for the first call of this function.
+ checkedOrigins[originURI] = true;
+ }
+ _addAllDeniedRequestsFromURI(originURI, reqSet);
+ var allowedRequests = RequestProcessor._allowedRequests
+ .getOriginUri(originURI);
+ if (allowedRequests) {
+ for (var destBase in allowedRequests) {
+ for (var destIdent in allowedRequests[destBase]) {
+ for (var destURI in allowedRequests[destBase][destIdent]) {
+ Logger.dump("Found allowed request to <"
+ + destURI + "> from <" + originURI + ">");
+ reqSet.addRequest(originURI, destURI,
+ allowedRequests[destBase][destIdent][destURI]);
+
+ if (!checkedOrigins[destURI]) {
+ // only check the destination URI if it hasn't been checked yet.
+ checkedOrigins[destURI] = true;
+
+ _addRecursivelyAllRequestsFromURI(destURI, reqSet,
+ checkedOrigins);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ function _addAllDeniedRequestsFromURI(originURI, reqSet) {
+ Logger.dump("Looking for other origins within denied requests from " +
+ originURI);
+ var requests = RequestProcessor._rejectedRequests.getOriginUri(originURI);
+ if (requests) {
+ for (var destBase in requests) {
+ for (var destIdent in requests[destBase]) {
+ for (var destUri in requests[destBase][destIdent]) {
+ Logger.dump("Found denied request to <" + destUri + "> from <" +
+ originURI + ">");
+ reqSet.addRequest(originURI, destUri,
+ requests[destBase][destIdent][destUri]);
+ }
+ }
+ }
+ }
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+ // TODO: make them private
+ self._rejectedRequests = new RequestSet();
+ self._allowedRequests = new RequestSet();
+
+
+
+ /**
+ * Process a NormalRequest.
+ *
+ * @param {NormalRequest} request
+ */
+ self.process = function(request) {
+ // uncomment for debugging:
+ //Logger.dump("request: " +
+ // (request.aRequestOrigin ? request.aRequestOrigin.spec :
+ // "<unknown>") +
+ // " --> "+request.aContentLocation.spec);
+ //Logger.vardump(request.aRequestOrigin);
+ //Logger.vardump(request.aContentLocation);
+ try {
+ if (request.isInternal()) {
+ Logger.debug(Logger.TYPE_CONTENT,
+ "Allowing a request that seems to be internal. " +
+ "Origin: " + request.originURI + ", Dest: " +
+ request.destURI);
+ return CP_OK;
+ }
+
+ var originURI = request.originURI;
+ var destURI = request.destURI;
+
+ if (request.aRequestOrigin.scheme == "moz-nullprincipal") {
+ // Before RP has been forked, there was a hack: in case of a request
+ // with the origin's scheme being 'moz-nullprincipal', RequestPolicy
+ // used the documentURI of the request's context as the "real" origin
+ // URI.
+ // (Note: RP assuemed that the context is always a document, but this
+ // is in fact not always true.)
+ // The reason for using the context's documentURI was, according to
+ // @jsamuel's comment, that the request's origin was not always the
+ // correct URI; according to @jsamuel this was fixed in Firefox 16.
+ // Originally he wrote:
+ // > "[Since Fx 16] we should be able to count on the referrer
+ // > (aRequestOrigin) being set to something besides
+ // > moz-nullprincipal when there is a referrer."
+ // TODO: check whether the requests that are allowed by this case are
+ // *definitely* internal request. Is it possible to determine
+ // where this request originally came from?
+ //
+ // ### Links:
+ // * nsIPrincipal:
+ // -> https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIPrincipal
+ //
+ // * discussion about RequestPolicy with regard to detecting that
+ // something has been entered in the url-bar -- it's the Mozilla Bug
+ // about adding `aRequestPrincipal` to `shouldLoad()` and it's
+ // milestone was Firefox 16.
+ // -> https://bugzilla.mozilla.org/show_bug.cgi?id=767134#c15
+ if (request.aRequestPrincipal) {
+ Logger.warning(
+ Logger.TYPE_CONTENT,
+ "Allowing request that appears to be a URL entered in the " +
+ "location bar or some other good explanation: " + destURI);
+ return CP_OK;
+ }
+ }
+
+ if (request.aRequestOrigin.scheme == "view-source") {
+ var newOriginURI = originURI.split(":").slice(1).join(":");
+ Logger.info(Logger.TYPE_CONTENT,
+ "Considering view-source origin <"
+ + originURI + "> to be origin <" + newOriginURI + ">");
+ originURI = newOriginURI;
+ request.setOriginURI(originURI);
+ }
+
+ if (request.aContentLocation.scheme == "view-source") {
+ var newDestURI = destURI.split(":").slice(1).join(":");
+ if (newDestURI.indexOf("data:text/html") == 0) {
+ // "View Selection Source" has been clicked
+ Logger.info(Logger.TYPE_CONTENT,
+ "Allowing \"data:text/html\" view-source destination"
+ + " (Selection Source)");
+ return CP_OK;
+ } else {
+ Logger.info(Logger.TYPE_CONTENT,
+ "Considering view-source destination <"
+ + destURI + "> to be destination <" + newDestURI + ">");
+ destURI = newDestURI;
+ request.setDestURI(destURI);
+ }
+ }
+
+ if (originURI == "about:blank" && request.aContext) {
+ let domNode;
+ try {
+ domNode = request.aContext.QueryInterface(Ci.nsIDOMNode);
+ } catch (e if e.result == Cr.NS_ERROR_NO_INTERFACE) {}
+ if (domNode && domNode.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE) {
+ var newOriginURI;
+ if (request.aContext.documentURI &&
+ request.aContext.documentURI != "about:blank") {
+ newOriginURI = request.aContext.documentURI;
+ } else if (request.aContext.ownerDocument &&
+ request.aContext.ownerDocument.documentURI &&
+ request.aContext.ownerDocument.documentURI != "about:blank") {
+ newOriginURI = request.aContext.ownerDocument.documentURI;
+ }
+ if (newOriginURI) {
+ newOriginURI = DomainUtil.stripFragment(newOriginURI);
+ Logger.info(Logger.TYPE_CONTENT, "Considering origin <" +
+ originURI + "> to be origin <" + newOriginURI + ">");
+ originURI = newOriginURI;
+ request.setOriginURI(originURI);
+ }
+ }
+ }
+
+
+ if (isDuplicateRequest(request)) {
+ return lastShouldLoadCheck.result;
+ }
+
+ // Sometimes, clicking a link to a fragment will result in a request
+ // where the origin is the same as the destination, but none of the
+ // additional content of the page is again requested. The result is that
+ // nothing ends up showing for blocked or allowed destinations because
+ // all of that data was cleared due to the new request.
+ // Example to test with: Click on "expand all" at
+ // http://code.google.com/p/SOME_PROJECT/source/detail?r=SOME_REVISION
+ if (originURI == destURI) {
+ Logger.warning(Logger.TYPE_CONTENT,
+ "Allowing (but not recording) request "
+ + "where origin is the same as the destination: " + originURI);
+ return CP_OK;
+ }
+
+
+
+ if (request.aContext) {
+ let domNode;
+ try {
+ domNode = request.aContext.QueryInterface(Ci.nsIDOMNode);
+ } catch (e if e.result == Cr.NS_ERROR_NO_INTERFACE) {}
+
+ if (domNode && domNode.nodeName == "LINK" &&
+ (domNode.rel == "icon" || domNode.rel == "shortcut icon")) {
+ internal.faviconRequests[destURI] = true;
+ }
+ }
+
+
+
+ // Note: If changing the logic here, also make necessary changes to
+ // isAllowedRedirect).
+
+ // Checking for link clicks, form submissions, and history requests
+ // should be done before other checks. Specifically, when link clicks
+ // were done after allowed-origin and other checks, then links that
+ // were allowed due to other checks would end up recorded in the origin
+ // url's allowed requests, and woud then show up on one tab if link
+ // was opened in a new tab but that link would have been allowed
+ // regardless of the link click. The original tab would then show it
+ // in its menu.
+ if (internal.clickedLinks[originURI] &&
+ internal.clickedLinks[originURI][destURI]) {
+ // Don't delete the clickedLinks item. We need it for if the user
+ // goes back/forward through their history.
+ // delete internal.clickedLinks[originURI][destURI];
+
+ // We used to have this not be recorded so that it wouldn't cause us
+ // to forget blocked/allowed requests. However, when a policy change
+ // causes a page refresh after a link click, it looks like a link
+ // click again and so if we don't forget the previous blocked/allowed
+ // requests, the menu becomes inaccurate. Now the question is: what
+ // are we breaking by clearing the blocked/allowed requests here?
+ request.requestResult = new RequestResult(true,
+ REQUEST_REASON_LINK_CLICK);
+ return accept("User-initiated request by link click", request);
+
+ } else if (internal.submittedForms[originURI] &&
+ internal.submittedForms[originURI][destURI.split("?")[0]]) {
+ // Note: we dropped the query string from the destURI because form GET
+ // requests will have that added on here but the original action of
+ // the form may not have had it.
+ // Don't delete the clickedLinks item. We need it for if the user
+ // goes back/forward through their history.
+ // delete internal.submittedForms[originURI][destURI.split("?")[0]];
+
+ // See the note above for link clicks and forgetting blocked/allowed
+ // requests on refresh. I haven't tested if it's the same for forms
+ // but it should be so we're making the same change here.
+ request.requestResult = new RequestResult(true,
+ REQUEST_REASON_FORM_SUBMISSION);
+ return accept("User-initiated request by form submission", request);
+
+ } else if (historyRequests[destURI]) {
+ // When the user goes back and forward in their history, a request for
+ // the url comes through but is not followed by requests for any of
+ // the page's content. Therefore, we make sure that our cache of
+ // blocked requests isn't removed in this case.
+ delete historyRequests[destURI];
+ request.requestResult = new RequestResult(true,
+ REQUEST_REASON_HISTORY_REQUEST);
+ return accept("History request", request, true);
+ } else if (internal.userAllowedRedirects[originURI]
+ && internal.userAllowedRedirects[originURI][destURI]) {
+ // shouldLoad is called by location.href in overlay.js as of Fx
+ // 3.7a5pre and SeaMonkey 2.1a.
+ request.requestResult = new RequestResult(true,
+ REQUEST_REASON_USER_ALLOWED_REDIRECT);
+ return accept("User-allowed redirect", request, true);
+ }
+
+ if (request.aRequestOrigin.scheme == "chrome") {
+ if (request.aRequestOrigin.asciiHost == "browser") {
+ // "browser" origin shows up for favicon.ico and an address entered
+ // in address bar.
+ request.requestResult = new RequestResult(true,
+ REQUEST_REASON_USER_ACTION);
+ return accept(
+ "User action (e.g. address entered in address bar) or other good "
+ + "explanation (e.g. new window/tab opened)", request);
+ } else {
+ // TODO: It seems sketchy to allow all requests from chrome. If I
+ // had to put my money on a possible bug (in terms of not blocking
+ // requests that should be), I'd put it here. Doing this, however,
+ // saves a lot of blocking of legitimate requests from extensions
+ // that originate from their xul files. If you're reading this and
+ // you know of a way to use this to evade RequestPolicy, please let
+ // me know, I will be very grateful.
+ request.requestResult = new RequestResult(true,
+ REQUEST_REASON_USER_ACTION);
+ return accept(
+ "User action (e.g. address entered in address bar) or other good "
+ + "explanation (e.g. new window/tab opened)", request);
+ }
+ }
+
+ // This is mostly here for the case of popup windows where the user has
+ // allowed popups for the domain. In that case, the window.open() call
+ // that made the popup isn't calling the wrapped version of
+ // window.open() and we can't find a better way to register the source
+ // and destination before the request is made. This should be able to be
+ // removed if we can find a better solution for the allowed popup case.
+ if (request.aContext) {
+ let domNode;
+ try {
+ domNode = request.aContext.QueryInterface(Ci.nsIDOMNode);
+ } catch (e if e.result == Cr.NS_ERROR_NO_INTERFACE) {}
+
+ if (domNode && domNode.nodeName == "xul:browser" &&
+ domNode.currentURI && domNode.currentURI.spec == "about:blank") {
+ request.requestResult = new RequestResult(true,
+ REQUEST_REASON_NEW_WINDOW);
+ return accept("New window (should probably only be an allowed " +
+ "popup's initial request)", request, true);
+ }
+ }
+
+ // XMLHttpRequests made within chrome's context have these origins.
+ // Greasemonkey uses such a method to provide their cross-site xhr.
+ if (originURI == "resource://gre/res/hiddenWindow.html" ||
+ originURI == "resource://gre-resources/hiddenWindow.html") {
+ }
+
+ // Now that we have blacklists, a user could prevent themselves from
+ // being able to reload a page by blocking requests from * to the
+ // destination page. As a simple hack around this, for now we'll always
+ // allow request to the same origin. It would be nice to have a a better
+ // solution but I'm not sure what that solution is.
+ var originIdent = DomainUtil.getIdentifier(originURI);
+ var destIdent = DomainUtil.getIdentifier(destURI);
+ if (originIdent === destIdent &&
+ originIdent !== null && destIdent !== null) {
+ request.requestResult = new RequestResult(true,
+ REQUEST_REASON_IDENTICAL_IDENTIFIER);
+ return accept(
+ "Allowing request where origin protocol, host, and port are the" +
+ " same as the destination: " + originIdent, request);
+ }
+
+ request.requestResult = PolicyManager.checkRequestAgainstUserRules(
+ request.aRequestOrigin, request.aContentLocation);
+ for (var i = 0; i < request.requestResult.matchedDenyRules.length; i++) {
+ Logger.dump('Matched deny rules');
+ Logger.vardump(request.requestResult.matchedDenyRules[i]);
+ }
+ for (var i = 0; i < request.requestResult.matchedAllowRules.length; i++) {
+ Logger.dump('Matched allow rules');
+ Logger.vardump(request.requestResult.matchedAllowRules[i]);
+ }
+ // If there are both allow and deny rules, then fall back on the default
+ // policy. I believe this is effectively the same as giving precedence
+ // to allow rules when in default allow mode and giving precedence to
+ // deny rules when in default deny mode. It's just a different way of
+ // expressing the same logic. Now, whether that's the right logic we
+ // should be using to solve the problem of rule precedence and support
+ // for fine-grained rules overriding course-grained ones is a different
+ // question.
+ if (request.requestResult.allowRulesExist() &&
+ request.requestResult.denyRulesExist()) {
+ request.requestResult.resultReason =
+ REQUEST_REASON_DEFAULT_POLICY_INCONSISTENT_RULES;
+ if (Prefs.isDefaultAllow()) {
+ request.requestResult.isAllowed = true;
+ return accept("User policy indicates both allow and block. " +
+ "Using default allow policy", request);
+ } else {
+ request.requestResult.isAllowed = false;
+ return reject("User policy indicates both allow and block. " +
+ "Using default block policy", request);
+ }
+ }
+ if (request.requestResult.allowRulesExist()) {
+ request.requestResult.resultReason = REQUEST_REASON_USER_POLICY;
+ request.requestResult.isAllowed = true;
+ return accept("Allowed by user policy", request);
+ }
+ if (request.requestResult.denyRulesExist()) {
+ request.requestResult.resultReason = REQUEST_REASON_USER_POLICY;
+ request.requestResult.isAllowed = false;
+ return reject("Blocked by user policy", request);
+ }
+
+ request.requestResult = PolicyManager
+ .checkRequestAgainstSubscriptionRules(request.aRequestOrigin,
+ request.aContentLocation);
+ for (var i = 0; i < request.requestResult.matchedDenyRules.length; i++) {
+ Logger.dump('Matched deny rules');
+ Logger.vardump(
+ request.requestResult.matchedDenyRules[i]);
+ }
+ for (var i = 0; i < request.requestResult.matchedAllowRules.length; i++) {
+ Logger.dump('Matched allow rules');
+ Logger.vardump(
+ request.requestResult.matchedAllowRules[i]);
+ }
+ if (request.requestResult.allowRulesExist() &&
+ request.requestResult.denyRulesExist()) {
+ request.requestResult.resultReason =
+ REQUEST_REASON_DEFAULT_POLICY_INCONSISTENT_RULES;
+ if (Prefs.isDefaultAllow()) {
+ request.requestResult.isAllowed = true;
+ return accept(
+ "Subscription rules indicate both allow and block. " +
+ "Using default allow policy", request);
+ } else {
+ request.requestResult.isAllowed = false;
+ return reject("Subscription rules indicate both allow and block. " +
+ "Using default block policy", request);
+ }
+ }
+ if (request.requestResult.denyRulesExist()) {
+ request.requestResult.resultReason =
+ REQUEST_REASON_SUBSCRIPTION_POLICY;
+ request.requestResult.isAllowed = false;
+ return reject("Blocked by subscription policy", request);
+ }
+ if (request.requestResult.allowRulesExist()) {
+ request.requestResult.resultReason =
+ REQUEST_REASON_SUBSCRIPTION_POLICY;
+ request.requestResult.isAllowed = true;
+ return accept("Allowed by subscription policy", request);
+ }
+
+ let compatibilityRules = self.getCompatibilityRules();
+ for (var i = 0; i < compatibilityRules.length; i++) {
+ var rule = compatibilityRules[i];
+ var allowOrigin = rule[0] ? originURI.indexOf(rule[0]) == 0 : true;
+ var allowDest = rule[1] ? destURI.indexOf(rule[1]) == 0 : true;
+ if (allowOrigin && allowDest) {
+ request.requestResult = new RequestResult(true,
+ REQUEST_REASON_COMPATIBILITY);
+ return accept(
+ "Extension/application compatibility rule matched [" + rule[2] +
+ "]", request, true);
+ }
+ }
+
+ // If the destination has a mapping (i.e. it was originally a different
+ // destination but was changed into the current one), accept this
+ // request if the original destination would have been accepted.
+ // Check aExtra against CP_MAPPEDDESTINATION to stop further recursion.
+ if (request.aExtra != CP_MAPPEDDESTINATION &&
+ internal.mappedDestinations[destURI]) {
+ for (var mappedDest in internal.mappedDestinations[destURI]) {
+ var mappedDestUriObj = internal.mappedDestinations[destURI][mappedDest];
+ Logger.warning(Logger.TYPE_CONTENT,
+ "Checking mapped destination: " + mappedDest);
+ var mappedResult = PolicyImplementation.shouldLoad(
+ request.aContentType, mappedDestUriObj, request.aRequestOrigin,
+ request.aContext, request.aMimeTypeGuess, CP_MAPPEDDESTINATION);
+ if (mappedResult == CP_OK) {
+ return CP_OK;
+ }
+ }
+ }
+
+ request.requestResult = internal.checkByDefaultPolicy(originURI, destURI);
+ if (request.requestResult.isAllowed) {
+ return accept("Allowed by default policy", request);
+ } else {
+ // We didn't match any of the conditions in which to allow the request,
+ // so reject it.
+ return request.aExtra == CP_MAPPEDDESTINATION ? CP_REJECT :
+ reject("Denied by default policy", request);
+ }
+
+
+ } catch (e) {
+ Logger.severe(Logger.TYPE_ERROR,
+ "Fatal Error, " + e + ", stack was: " + e.stack);
+ Logger.severe(Logger.TYPE_CONTENT,
+ "Rejecting request due to internal error.");
+ return Prefs.isBlockingDisabled() ? CP_OK : CP_REJECT;
+ }
+ };
+
+ // RequestProcessor.finishProcessing = function(request, result) {
+ // request.shouldLoadResult = result;
+ // };
+
+
+
+
+
+ /**
+ * Called as a http request is made. The channel is available to allow you to
+ * modify headers and such.
+ *
+ * Currently this just looks for prefetch requests that are getting through
+ * which we currently can't stop.
+ */
+ let examineHttpRequest = function(aSubject) {
+ var httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
+ try {
+ // Determine if prefetch requests are slipping through.
+ if (httpChannel.getRequestHeader("X-moz") == "prefetch") {
+ // Seems to be too late to block it at this point. Calling the
+ // cancel(status) method didn't stop it.
+ Logger.warning(Logger.TYPE_CONTENT,
+ "Discovered prefetch request being sent to: " + httpChannel.name);
+ }
+ } catch (e) {
+ // No X-moz header.
+ }
+ };
+
+ ProcessEnvironment.obMan.observe(["http-on-modify-request"],
+ examineHttpRequest);
+
+
+
+
+
+
+ self.registerHistoryRequest = function(destinationUrl) {
+ destinationUrl = DomainUtil.ensureUriHasPath(
+ DomainUtil.stripFragment(destinationUrl));
+ historyRequests[destinationUrl] = true;
+ Logger.info(Logger.TYPE_INTERNAL,
+ "History item requested: <" + destinationUrl + ">.");
+ };
+
+ self.registerFormSubmitted = function(originUrl, destinationUrl) {
+ originUrl = DomainUtil.ensureUriHasPath(DomainUtil.stripFragment(originUrl));
+ destinationUrl = DomainUtil.ensureUriHasPath(
+ DomainUtil.stripFragment(destinationUrl));
+
+ Logger.info(Logger.TYPE_INTERNAL,
+ "Form submitted from <" + originUrl + "> to <" + destinationUrl + ">.");
+
+ // Drop the query string from the destination url because form GET requests
+ // will end up with a query string on them when shouldLoad is called, so
+ // we'll need to be dropping the query string there.
+ destinationUrl = destinationUrl.split("?")[0];
+
+ if (internal.submittedForms[originUrl] == undefined) {
+ internal.submittedForms[originUrl] = {};
+ }
+ if (internal.submittedForms[originUrl][destinationUrl] == undefined) {
+ // TODO: See timestamp note for registerLinkClicked.
+ internal.submittedForms[originUrl][destinationUrl] = true;
+ }
+
+ // Keep track of a destination-indexed map, as well.
+ if (internal.submittedFormsReverse[destinationUrl] == undefined) {
+ internal.submittedFormsReverse[destinationUrl] = {};
+ }
+ if (internal.submittedFormsReverse[destinationUrl][originUrl] == undefined) {
+ // TODO: See timestamp note for registerLinkClicked.
+ internal.submittedFormsReverse[destinationUrl][originUrl] = true;
+ }
+ };
+
+ self.registerLinkClicked = function(originUrl, destinationUrl) {
+ originUrl = DomainUtil.ensureUriHasPath(DomainUtil.stripFragment(originUrl));
+ destinationUrl = DomainUtil.ensureUriHasPath(
+ DomainUtil.stripFragment(destinationUrl));
+
+ Logger.info(Logger.TYPE_INTERNAL,
+ "Link clicked from <" + originUrl + "> to <" + destinationUrl + ">.");
+
+ if (internal.clickedLinks[originUrl] == undefined) {
+ internal.clickedLinks[originUrl] = {};
+ }
+ if (internal.clickedLinks[originUrl][destinationUrl] == undefined) {
+ // TODO: Possibly set the value to a timestamp that can be used elsewhere
+ // to determine if this is a recent click. This is probably necessary as
+ // multiple calls to shouldLoad get made and we need a way to allow
+ // multiple in a short window of time. Alternately, as it seems to always
+ // be in order (repeats are always the same as the last), the last one
+ // could be tracked and always allowed (or allowed within a small period
+ // of time). This would have the advantage that we could delete items from
+ // the clickedLinks object. One of these approaches would also reduce log
+ // clutter, which would be good.
+ internal.clickedLinks[originUrl][destinationUrl] = true;
+ }
+
+ // Keep track of a destination-indexed map, as well.
+ if (internal.clickedLinksReverse[destinationUrl] == undefined) {
+ internal.clickedLinksReverse[destinationUrl] = {};
+ }
+ if (internal.clickedLinksReverse[destinationUrl][originUrl] == undefined) {
+ // TODO: Possibly set the value to a timestamp, as described above.
+ internal.clickedLinksReverse[destinationUrl][originUrl] = true;
+ }
+ };
+
+ self.registerAllowedRedirect = function(originUrl, destinationUrl) {
+ originUrl = DomainUtil.ensureUriHasPath(DomainUtil.stripFragment(originUrl));
+ destinationUrl = DomainUtil.ensureUriHasPath(
+ DomainUtil.stripFragment(destinationUrl));
+
+ Logger.info(Logger.TYPE_INTERNAL, "User-allowed redirect from <" +
+ originUrl + "> to <" + destinationUrl + ">.");
+
+ if (internal.userAllowedRedirects[originUrl] == undefined) {
+ internal.userAllowedRedirects[originUrl] = {};
+ }
+ if (internal.userAllowedRedirects[originUrl][destinationUrl] == undefined) {
+ internal.userAllowedRedirects[originUrl][destinationUrl] = true;
+ }
+ };
+
+ /**
+ * Add an observer to be notified of all blocked and allowed requests. TODO:
+ * This should be made to accept instances of a defined interface.
+ *
+ * @param {Object} observer
+ */
+ self.addRequestObserver = function(observer) {
+ if (!("observeBlockedRequest" in observer)) {
+ throw "Observer passed to addRequestObserver does "
+ + "not have an observeBlockedRequest() method.";
+ }
+ Logger.debug(Logger.TYPE_INTERNAL,
+ "Adding request observer: " + observer.toString());
+ internal.requestObservers.push(observer);
+ };
+
+ /**
+ * Remove an observer added through addRequestObserver().
+ *
+ * @param {Object} observer
+ */
+ self.removeRequestObserver = function(observer) {
+ for (var i = 0; i < internal.requestObservers.length; i++) {
+ if (internal.requestObservers[i] == observer) {
+ Logger.debug(Logger.TYPE_INTERNAL,
+ "Removing request observer: " + observer.toString());
+ delete internal.requestObservers[i];
+ return;
+ }
+ }
+ Logger.warning(Logger.TYPE_INTERNAL,
+ "Could not find observer to remove " + "in removeRequestObserver()");
+ };
+
+
+
+ self.getDeniedRequests = function(currentlySelectedOrigin, allRequestsOnDocument) {
+ Logger.dump("## getDeniedRequests");
+ return _getRequestsHelper(currentlySelectedOrigin, allRequestsOnDocument,
+ false);
+ };
+
+ self.getAllowedRequests = function(currentlySelectedOrigin, allRequestsOnDocument) {
+ Logger.dump("## getAllowedRequests");
+ return _getRequestsHelper(currentlySelectedOrigin, allRequestsOnDocument,
+ true);
+ };
+
+ /**
+ * TODO: This comment is quite old. It might not be necessary anymore to
+ * check the DOM since all requests are recorded, like:
+ * RequestSet._origins[originURI][destBase][destIdent][destURI][i]
+ * Info: As soon as requests are saved per Tab, this function isn't needed
+ * anymore.
+ *
+ * This will look both at the DOM as well as the recorded allowed requests to
+ * determine which other origins exist within the document. This includes
+ * other origins that have the same domain.
+ *
+ * The reason for also
+ * needing to check the DOM is that some sites (like gmail) will make multiple
+ * requests to the same uri for different iframes and this will cause us to
+ * only have in the recorded requests from a source uri the destinations from
+ * the most recent iframe that loaded that source uri. It may also help in
+ * cases where the user has multiple tabs/windows open to the same page.
+ *
+ * @param {}
+ * browser
+ * @return {}
+ * RequestSet
+ */
+ self.getAllRequestsInBrowser = function(browser) {
+ //var origins = {};
+ var reqSet = new RequestSet();
+
+ // If we get these from the DOM, then we won't know the relevant
+ // rules that were involved with allowing/denying the request.
+ // Maybe just look up the allowed/blocked requests in the
+ // main allowed/denied request sets before adding them.
+ //_getOtherOriginsHelperFromDOM(document, reqSet);
+
+ var uri = DomainUtil.stripFragment(browser.currentURI.spec);
+ _addRecursivelyAllRequestsFromURI(uri, reqSet, {});
+ return reqSet;
+ };
+
+ return self;
+ }(RequestProcessor || {}));
+
+
+ Services.scriptloader.loadSubScript(
+ 'chrome://requestpolicy/content/lib/request-processor.redirects.js');
+ Services.scriptloader.loadSubScript(
+ 'chrome://requestpolicy/content/lib/request-processor.compat.js');
++
diff --cc content/lib/request-processor.redirects.js
index 0000000,a6ea9e4..33673af
mode 000000,100644..100644
--- a/content/lib/request-processor.redirects.js
+++ b/content/lib/request-processor.redirects.js
@@@ -1,0 -1,413 +1,414 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const HTTPS_EVERYWHERE_REWRITE_TOPIC = "https-everywhere-uri-rewrite";
+
+ Cu.import("resource://gre/modules/Services.jsm");
+ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ ScriptLoader.importModules([
+ "lib/logger",
+ "lib/prefs",
+ "lib/policy-manager",
+ "lib/utils/domains",
+ "lib/utils",
+ "lib/request",
+ "lib/request-result",
+ "lib/http-response",
+ "lib/environment"
+ ], this);
+
+
+
+ let RequestProcessor = (function(self) {
+ let internal = Utils.moduleInternal(self);
+
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cr = Components.results;
+ const Cu = Components.utils;
+
+
+ /**
+ * These are redirects that the user allowed when presented with a redirect
+ * notification.
+ */
+ internal.userAllowedRedirects = {};
+
+ internal.allowedRedirectsReverse = {};
+
+
+
+ ProcessEnvironment.obMan.observe(
+ ["http-on-examine-response"],
+ function(subject) {
+ examineHttpResponse(subject);
+ });
+ ProcessEnvironment.obMan.observe(
+ [HTTPS_EVERYWHERE_REWRITE_TOPIC],
+ function(subject, topic, data) {
+ handleHttpsEverywhereUriRewrite(subject, data);
+ });
+
+
+
+ function mapDestinations(origDestUri, newDestUri) {
+ origDestUri = DomainUtil.stripFragment(origDestUri);
+ newDestUri = DomainUtil.stripFragment(newDestUri);
+ Logger.info(Logger.TYPE_INTERNAL,
+ "Mapping destination <" + origDestUri + "> to <" + newDestUri + ">.");
+ if (!internal.mappedDestinations[newDestUri]) {
+ internal.mappedDestinations[newDestUri] = {};
+ }
+ internal.mappedDestinations[newDestUri][origDestUri] =
+ DomainUtil.getUriObject(origDestUri);
+ }
+
+ /**
+ * Handles observer notifications sent by the HTTPS Everywhere extension
+ * that inform us of URIs that extension has rewritten.
+ *
+ * @param nsIURI oldURI
+ * @param string newSpec
+ */
+ function handleHttpsEverywhereUriRewrite(oldURI, newSpec) {
+ oldURI = oldURI.QueryInterface(Ci.nsIURI);
+ mapDestinations(oldURI.spec, newSpec);
+ }
+
+ function checkRedirect(request) {
+ // TODO: Find a way to get rid of repitition of code between this and
+ // shouldLoad().
+
+ // Note: If changing the logic here, also make necessary changes to
+ // shouldLoad().
+
+ // This is not including link clicks, form submissions, and user-allowed
+ // redirects.
+
+ var originURI = request.originURI;
+ var destURI = request.destURI;
+
+ var originURIObj = DomainUtil.getUriObject(originURI);
+ var destURIObj = DomainUtil.getUriObject(destURI);
+
+ var result = PolicyManager.checkRequestAgainstUserRules(originURIObj,
+ destURIObj);
+ // For now, we always give priority to deny rules.
+ if (result.denyRulesExist()) {
+ result.isAllowed = false;
+ return result;
+ }
+ if (result.allowRulesExist()) {
+ result.isAllowed = true;
+ return result;
+ }
+
+ var result = PolicyManager.checkRequestAgainstSubscriptionRules(
+ originURIObj, destURIObj);
+ // For now, we always give priority to deny rules.
+ if (result.denyRulesExist()) {
+ result.isAllowed = false;
+ return result;
+ }
+ if (result.allowRulesExist()) {
+ result.isAllowed = true;
+ return result;
+ }
+
+ if (destURI[0] && destURI[0] == '/'
+ || destURI.indexOf(":") == -1) {
+ // Redirect is to a relative url.
+ // ==> allow.
+ return new RequestResult(true, REQUEST_REASON_RELATIVE_URL);
+ }
+
+ let compatibilityRules = self.getCompatibilityRules();
+ for (var i = 0; i < compatibilityRules.length; i++) {
+ var rule = compatibilityRules[i];
+ var allowOrigin = rule[0] ? originURI.indexOf(rule[0]) == 0 : true;
+ var allowDest = rule[1] ? destURI.indexOf(rule[1]) == 0 : true;
+ if (allowOrigin && allowDest) {
+ return new RequestResult(true, REQUEST_REASON_COMPATIBILITY);
+ }
+ }
+
+ var result = internal.checkByDefaultPolicy(originURI, destURI);
+ return result;
+ }
+
+
+ self.isAllowedRedirect = function(originURI, destURI) {
+ var request = new Request(originURI, destURI);
+ return (true === checkRedirect(request).isAllowed);
+ };
+
+
+
+ function processUrlRedirection(request) {
+ let httpResponse = request.httpResponse;
+ let httpChannel = httpResponse.httpChannel;
+ var originURI = request.originURI;
+ var destURI = request.destURI;
+ var headerType = httpResponse.redirHeaderType;
+
+ // Ignore redirects to javascript. The browser will ignore them, as well.
+ if (httpResponse.destURI.schemeIs("javascript")) {
+ Logger.warning(Logger.TYPE_HEADER_REDIRECT,
+ "Ignoring redirect to javascript URI <" + destURI + ">");
+ return;
+ }
+
+ request.requestResult = checkRedirect(request);
+ if (true === request.requestResult.isAllowed) {
+ Logger.warning(Logger.TYPE_HEADER_REDIRECT, "** ALLOWED ** '"
+ + headerType + "' header to <" + destURI + "> " + "from <" + originURI
+ + ">. Same hosts or allowed origin/destination.");
+ internal.recordAllowedRequest(originURI, destURI, false,
+ request.requestResult);
+ internal.allowedRedirectsReverse[destURI] = originURI;
+
+ // If this was a link click or a form submission, we register an
+ // additional click/submit with the original source but with a new
+ // destination of the target of the redirect. This is because future
+ // requests (such as using back/forward) might show up as directly from
+ // the initial origin to the ultimate redirected destination.
+ if (httpChannel.referrer) {
+ var realOrigin = httpChannel.referrer.spec;
+
+ if (internal.clickedLinks[realOrigin] &&
+ internal.clickedLinks[realOrigin][originURI]) {
+ Logger.warning(Logger.TYPE_HEADER_REDIRECT,
+ "This redirect was from a link click." +
+ " Registering an additional click to <" + destURI + "> " +
+ "from <" + realOrigin + ">");
+ self.registerLinkClicked(realOrigin, destURI);
+
+ } else if (internal.submittedForms[realOrigin]
+ && internal.submittedForms[realOrigin][originURI.split("?")[0]]) {
+ Logger.warning(Logger.TYPE_HEADER_REDIRECT,
+ "This redirect was from a form submission." +
+ " Registering an additional form submission to <" + destURI +
+ "> " + "from <" + realOrigin + ">");
+ self.registerFormSubmitted(realOrigin, destURI);
+ }
+ }
+
+ return;
+ }
+
+ // The header isn't allowed, so remove it.
+ try {
+ if (!Prefs.isBlockingDisabled()) {
+ httpResponse.removeResponseHeader();
+
+ let browser = request.browser;
+
+ if (browser !== null) {
+ // `browser` is null if it could not be found. One known
+ // example is a favicon request that is redirected.
+
+ // TODO: do not put data into the <browser> object. Maybe use
+ // Map instead?
+
+ // save all blocked redirects directly in the browser element. the
+ // blocked elements will be checked later when the DOM content
+ // finished loading.
+ browser.requestpolicy = browser.requestpolicy || {blockedRedirects: {}};
+ browser.requestpolicy.blockedRedirects[originURI] = destURI;
+ }
+
+ // Cancel the request. As of Fx 37, this causes the location bar to
+ // show the URL of the previously displayed page.
+ httpChannel.cancel(Cr.NS_BINDING_ABORTED);
+
+ // TODO: show the redirect notification *only* when
+ // a) a link has been clicked
+ // b) an url has been entered.
+ // In any other case the redirect should *not* cause a notific.
+ // bar to be displayed, because the redirect hasn't been caused by
+ // *explicit* user interaction.
+ // Examples for such other cases are inline elements whose
+ // destination causes a redirect (via a HTTP Header), e.g. <img>.
+ // Note: As soon as this is fixed, enable this mozmill test:
+ // tests/mozmill/tests/testRedirect/testInlineRedirect.js
+ showRedirectNotification(request) || Logger.warning(
+ Logger.TYPE_HEADER_REDIRECT,
+ "A redirect has been observed, but it was not possible to notify " +
+ "the user! The redirect was from page <" + request.originURI + "> " +
+ "to <" + request.destURI + ">.");
+
+ // We try to trace the blocked redirect back to a link click or form
+ // submission if we can. It may indicate, for example, a link that
+ // was to download a file but a redirect got blocked at some point.
+ var initialOrigin = originURI;
+ var initialDest = destURI;
+ // To prevent infinite loops, bound the number of iterations.
+ // Note that an apparent redirect loop doesn't mean a problem with a
+ // website as the site may be using other information, such as cookies
+ // that get set in the redirection process, to stop the redirection.
+ var iterations = 0;
+ const ASSUME_REDIRECT_LOOP = 100; // Chosen arbitrarily.
+ while (initialOrigin in internal.allowedRedirectsReverse &&
+ iterations++ < ASSUME_REDIRECT_LOOP) {
+ initialDest = initialOrigin;
+ initialOrigin = internal.allowedRedirectsReverse[initialOrigin];
+ }
+
+ if (initialOrigin in internal.clickedLinksReverse) {
+ for (var i in internal.clickedLinksReverse[initialOrigin]) {
+ // We hope there's only one possibility of a source page (that is,
+ // ideally there will be one iteration of this loop).
+ var sourcePage = i;
+ }
+
+ // Maybe we just record the clicked link and each step in between as
+ // an allowed request, and the final blocked one as a blocked request.
+ // That is, make it show up in the requestpolicy menu like anything
+ // else.
+ // We set the "isInsert" parameter so we don't clobber the existing
+ // info about allowed and deleted requests.
+ internal.recordAllowedRequest(sourcePage, initialOrigin, true,
+ request.requestResult);
+ }
+
+ // if (internal.submittedFormsReverse[initialOrigin]) {
+ // // TODO: implement for form submissions whose redirects are blocked
+ // }
+
+ internal.recordRejectedRequest(request);
+ }
+ Logger.warning(Logger.TYPE_HEADER_REDIRECT,
+ "** BLOCKED ** '" + headerType + "' header to <" + destURI + ">" +
+ " found in response from <" + originURI + ">");
+ } catch (e) {
+ Logger.severe(
+ Logger.TYPE_HEADER_REDIRECT, "Failed removing " +
+ "'" + headerType + "' header to <" + destURI + ">" +
+ " in response from <" + originURI + ">. " + e, e);
+ }
+ }
+
+ function showRedirectNotification(request) {
+ let browser = request.browser;
+ if (browser === null) {
+ return false;
+ }
+
+ var window = browser.ownerGlobal;
+
+ Utils.tryMultipleTimes(function() {
+ var showNotification = Utils.getObjectPath(window, 'requestpolicy',
+ 'overlay', '_showRedirectNotification');
+ if (!showNotification) {
+ return false;
+ }
+ return showNotification(browser, request.destURI, 0, request.originURI);
+ });
+ return true;
+ }
+
+
+
+
+
+ /**
+ * Called after a response has been received from the web server. Headers are
+ * available on the channel. The response can be accessed and modified via
+ * nsITraceableChannel.
+ */
+ function examineHttpResponse(aSubject) {
+ // Currently, if a user clicks a link to download a file and that link
+ // redirects and is subsequently blocked, the user will see the blocked
+ // destination in the menu. However, after they have allowed it from
+ // the menu and attempted the download again, they won't see the allowed
+ // request in the menu. Fixing that might be a pain and also runs the
+ // risk of making the menu cluttered and confusing with destinations of
+ // followed links from the current page.
+
+ // TODO: Make user aware of blocked headers so they can allow them if
+ // desired.
+
+ let httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
+ let httpResponse = new HttpResponse(httpChannel);
+
+ // the "raw" dest string might be a relative or absolute URI
+ let rawDestString = httpResponse.rawDestString;
+
+ if (httpResponse.containsRedirection === false || rawDestString === null) {
+ return;
+ }
+
+ let originString = httpResponse.originURI.specIgnoringRef;
+
+ // Allow redirects of requests from privileged code.
+ if (!isContentRequest(httpResponse)) {
+ // However, favicon requests that are redirected appear as non-content
+ // requests. So, check if the original request was for a favicon.
+ var originPath = httpResponse.originURI.path;
+ // We always have to check "/favicon.ico" because Firefox will use this
+ // as a default path and that request won't pass through shouldLoad().
+ if (originPath == "/favicon.ico" || internal.faviconRequests[originString]) {
+ // If the redirected request is allowed, we need to know that was a
+ // favicon request in case it is further redirected.
+ internal.faviconRequests[rawDestString] = true;
+ Logger.info(Logger.TYPE_HEADER_REDIRECT, "'" + httpResponse.redirHeaderType
+ + "' header to <" + rawDestString + "> " + "from <" + originString
+ + "> appears to be a redirected favicon request. "
+ + "This will be treated as a content request.");
+ } else {
+ Logger.warning(Logger.TYPE_HEADER_REDIRECT,
+ "** ALLOWED ** '" + httpResponse.redirHeaderType +
+ "' header to <" + rawDestString + "> " +
+ "from <" + originString +
+ ">. Original request is from privileged code.");
+ return;
+ }
+ }
+
+ var request = new RedirectRequest(httpResponse);
+ processUrlRedirection(request);
+ };
+
+
+
+ /**
+ * Checks whether a request is initiated by a content window. If it's from a
+ * content window, then it's from unprivileged code.
+ */
+ function isContentRequest(httpResponse) {
+ let loadContext = httpResponse.loadContext;
+
+ if (loadContext === null) {
+ return false;
+ }
+
+ return !!loadContext.isContent;
+ }
+
+
+ return self;
+ }(RequestProcessor || {}));
++
diff --cc content/lib/request-result.jsm
index 404dbcd,2ee236a..197f32a
--- a/content/lib/request-result.jsm
+++ b/content/lib/request-result.jsm
@@@ -99,3 -102,3 +102,4 @@@ RequestResult.prototype =
return this.isAllowed ? false : !this.isDefaultPolicyUsed();
}
};
++
diff --cc content/lib/request-set.jsm
index 0000000,a9870a8..9c2e470
mode 000000,100644..100644
--- a/content/lib/request-set.jsm
+++ b/content/lib/request-set.jsm
@@@ -1,0 -1,241 +1,242 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ let EXPORTED_SYMBOLS = ["RequestSet"];
+
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ ScriptLoader.importModules([
+ "lib/logger",
+ "lib/utils/domains",
+ "lib/request-result"
+ ], this);
+
+
+
+ function getUriIdentifier(uri) {
+ try {
+ return DomainUtil.getIdentifier(uri, DomainUtil.LEVEL_SOP);
+ } catch (e) {
+ var msg = "getUriIdentifier exception on uri <" + uri + "> " +
+ ". Exception was: " + e;
+ throw new Error(msg);
+ }
+ }
+
+
+ function RequestSet() {
+ this._origins = {};
+ }
+ RequestSet.prototype = {
+ _origins : null,
+
+ print : function(name) {
+ var log = Logger;
+ log.dump("-------------------------------------------------");
+ log.dump("== Request Set <" + name + "> ==");
+ // "Take that, Big-O!"
+ var origins = this._origins;
+ for (var oUri in origins) {
+ log.dump(" " + "Origin uri: <" + oUri + ">");
+ for (var dBase in origins[oUri]) {
+ var dests = origins[oUri];
+ log.dump(" " + "Dest base domain: <" + dBase + ">");
+ for (var dIdent in dests[dBase]) {
+ log.dump(" " + "Dest identifier: <" + dIdent + ">");
+ for (var dUri in dests[dBase][dIdent]) {
+ log.dump(" " + "Dest uri: <" + dUri + ">");
+ for (var i in dests[dBase][dIdent][dUri]) {
+ log.dump(" " + "#: " + i);
+ for (var ruleStr in dests[dBase][dIdent][dUri][i]) {
+ log.dump(" " + "Rule: <" + ruleStr + ">");
+ }
+ }
+ }
+ }
+ }
+ }
+ log.dump("-------------------------------------------------");
+ },
+
+ getAll : function() {
+ return this._origins;
+ },
+
+ // TODO: the name of this method, getAllMergedOrigins, is confusing. Is it
+ // getting all of the "merged origins" is it "getting all" and merging the
+ // origins when it does it?
+ getAllMergedOrigins : function() {
+ var result = {};
+ for (var originUri in this._origins) {
+ var dests = this._origins[originUri];
+ for (var destBase in dests) {
+ if (!result[destBase]) {
+ result[destBase] = {};
+ }
+ for (var destIdent in dests[destBase]) {
+ if (!result[destBase][destIdent]) {
+ result[destBase][destIdent] = {};
+ }
+ for (var destUri in dests[destBase][destIdent]) {
+ if (!result[destBase][destIdent][destUri]) {
+ result[destBase][destIdent][destUri] = dests[destBase][destIdent][destUri];
+ } else {
+ result[destBase][destIdent][destUri] =
+ result[destBase][destIdent][destUri]
+ .concat(dests[destBase][destIdent][destUri]);
+ }
+ }
+ }
+ }
+ }
+ return result;
+ },
+
+ getOriginUri : function(originUri) {
+ return this._origins[originUri] || {};
+ },
+
+ /**
+ * @param {Array} rules The rules that were triggered by this request.
+ */
+ addRequest : function(originUri, destUri, requestResult) {
+ if (requestResult == undefined) {
+ Logger.warning(Logger.TYPE_INTERNAL,
+ "addRequest() was called without a requestResult object!"
+ +" Creating a new one.\n"
+ +"\torigin: <"+originUri+">\n"
+ +"\tdestination: <"+destUri+">"
+ );
+ requestResult = new RequestResult();
+ }
+
+ if (!this._origins[originUri]) {
+ this._origins[originUri] = {};
+ }
+ var dests = this._origins[originUri];
+
+ var destBase = DomainUtil.getBaseDomain(destUri);
+ if (!dests[destBase]) {
+ dests[destBase] = {};
+ }
+
+ var destIdent = getUriIdentifier(destUri);
+ if (!dests[destBase][destIdent]) {
+ dests[destBase][destIdent] = {};
+ }
+
+ // if (typeof rules != "object") {
+ // throw "addRequest 'rules' argument must be an object where each " +
+ // "key/val is ruleStr/rule";
+ // }
+ /*
+ if (!dests[destBase][destIdent][destUri]) {
+ // TODO: this is a little sketchy. What if we clobber rules
+ // that were already here? Arguably if we are told to add the
+ // same origin and dest pair, this will happen. Is that supposed
+ // to be possible?
+ dests[destBase][destIdent][destUri] = requestResult;
+ } else {
+ // TODO: append rules, removing duplicates.
+ }
+ */
+ if (!dests[destBase][destIdent][destUri]) {
+ dests[destBase][destIdent][destUri] = [];
+ }
+ if (requestResult instanceof Array) {
+ dests[destBase][destIdent][destUri] =
+ dests[destBase][destIdent][destUri]
+ .concat(requestResult);
+ } else {
+ dests[destBase][destIdent][destUri].push(requestResult);
+ }
+ },
+
+ /**
+ */
+ removeRequest : function(originUri, destUri) {
+ if (!this._origins[originUri]) {
+ return;
+ }
+ var dests = this._origins[originUri];
+
+ var destBase = DomainUtil.getBaseDomain(destUri);
+ if (!dests[destBase]) {
+ return;
+ }
+
+ var destIdent = getUriIdentifier(destUri);
+ if (!dests[destBase][destIdent]) {
+ return;
+ }
+
+ if (!dests[destBase][destIdent][destUri]) {
+ return;
+ }
+ delete dests[destBase][destIdent][destUri];
+
+ if (Object.getOwnPropertyNames(dests[destBase][destIdent]).length > 0) {
+ return;
+ }
+ delete dests[destBase][destIdent];
+
+ if (Object.getOwnPropertyNames(dests[destBase]).length > 0) {
+ return;
+ }
+ delete dests[destBase];
+
+ if (Object.getOwnPropertyNames(dests).length > 0) {
+ return;
+ }
+ delete this._origins[originUri];
+ },
+
+ /**
+ */
+ removeOriginUri : function(originUri) {
+ delete this._origins[originUri];
+ },
+
+ containsBlockedRequests : function() {
+ var origins = this._origins
+ for (var originURI in origins) {
+ for (var destBase in origins[originURI]) {
+ for (var destIdent in origins[originURI][destBase]) {
+ for (var destURI in origins[originURI][destBase][destIdent]) {
+ for (var i in origins[originURI][destBase][destIdent][destURI]) {
+ if (true !==
+ origins[originURI][destBase][destIdent][destURI][i].isAllowed) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+ };
++
diff --cc content/lib/request.jsm
index 88d902e,313aff1..bc85f04
--- a/content/lib/request.jsm
+++ b/content/lib/request.jsm
@@@ -332,3 -278,3 +278,4 @@@ function RedirectRequest(httpResponse)
}
RedirectRequest.prototype = Object.create(Request.prototype);
RedirectRequest.prototype.constructor = Request;
++
diff --cc content/lib/ruleset-storage.jsm
index e4c1ee8,33e406a..206a1cc
--- a/content/lib/ruleset-storage.jsm
+++ b/content/lib/ruleset-storage.jsm
@@@ -68,3 -79,3 +79,4 @@@ let RulesetStorage =
}
};
++
diff --cc content/lib/ruleset.jsm
index 87386db,41e6401..373f401
--- a/content/lib/ruleset.jsm
+++ b/content/lib/ruleset.jsm
@@@ -1113,3 -1132,3 +1132,4 @@@ Ruleset.rawRuleToCanonicalString = func
// var secondStr = Ruleset.rawRuleToCanonicalString(second);
// return firstStr == secondStr;
// }
++
diff --cc content/lib/script-loader.jsm
index 0000000,6476ba2..8b6b42e
mode 000000,100644..100644
--- a/content/lib/script-loader.jsm
+++ b/content/lib/script-loader.jsm
@@@ -1,0 -1,209 +1,210 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+ const Cr = Components.results;
+
+ let EXPORTED_SYMBOLS = ["ScriptLoader"];
+
+ // import some modules
+ // NOTICE: This file should NOT import any of RP's modules when it is loaded!
+ // Doing so would be a bad practice, and might produce import() loops
+ // when the module to be imported wants to import ScriptLoader.
+ Cu.import("resource://gre/modules/Services.jsm");
+ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+ Cu.import("resource://gre/modules/devtools/Console.jsm");
+
+
+ const rpChromeContentURI = 'chrome://requestpolicy/content/';
+
+ function getModuleURI(id) {
+ return rpChromeContentURI + id + ".jsm";
+ }
+
+ /**
+ * If the ScriptLoader catches an Exception, it will be a severe error.
+ */
+ function logSevereError(msg, e) {
+ dump("[RequestPolicy] [SEVERE] [ERROR] " + msg + " " + e +
+ (e.stack ? ", stack was: " + e.stack : "") + "\n");
+ }
+
+
+
+ let ScriptLoader = (function() {
+
+ let importedModuleURIs = {};
+
+ // URIs in that variable will not be unloaded
+ let moduleUnloadExceptions = {};
+ // a module shouldn't unload itself
+ moduleUnloadExceptions[getModuleURI("lib/script-loader")] = true;
+ // EnvironmentManager has to be unloaded even later than ScriptLoader
+ moduleUnloadExceptions[getModuleURI("lib/environment")] = true;
+
+
+ // contains the module IDs that are currently being imported initially and
+ // have not finished importing yet.
+ let modulesCurrentlyBeingImported = {};
+
+
+ let self = {
+ /**
+ * Unload all modules that have been imported.
+ * See https://developer.mozilla.org/en-US/docs/Components.utils.unload
+ */
+ unloadAllModules: function() {
+ for (let uri in importedModuleURIs) {
+ if (importedModuleURIs.hasOwnProperty(uri) &&
+ moduleUnloadExceptions.hasOwnProperty(uri) === false) {
+ //console.debug("[RPC] Unloading module "+uri);
+ try {
+ Cu.unload(uri);
+ } catch(e) {
+ console.error("[RPC] Failed to unload module "+uri);
+ Components.utils.reportError(e);
+ }
+ delete importedModuleURIs[uri];
+ }
+ }
+ },
+
+ /**
+ * Function called by EnvironmentManager before ScriptLoader is being
+ * unloaded.
+ */
+ doShutdownTasks: function() {
+ self.unloadAllModules();
+ },
+
+ /**
+ * @param {Array} moduleID
+ * the moduleID of the module to import
+ * @param {Object} scope
+ * (optional) if not specified, one will be created.
+ *
+ * @return {Object} the scope
+ */
+ importModule: function(moduleID, scope) {
+ scope = scope || {};
+
+ // avoid import loops.
+ if (moduleID in modulesCurrentlyBeingImported) {
+ return scope;
+ }
+
+ //console.debug("[RPC] `importModule` called for "+moduleID);
+
+ let uri = getModuleURI(moduleID);
+ try {
+ if (!(uri in importedModuleURIs)) {
+ // the module hasn't been imported yet
+ modulesCurrentlyBeingImported[moduleID] = true;
+ //console.debug("[RPC] importing " + moduleID);
+ }
+
+ Cu.import(uri, scope);
+ importedModuleURIs[uri] = true;
+
+ if (moduleID in modulesCurrentlyBeingImported) {
+ delete modulesCurrentlyBeingImported[moduleID];
+ }
+ } catch (e if e.result === Cr.NS_ERROR_FILE_NOT_FOUND) {
+ logSevereError('Failed to import module with ID "' + moduleID +
+ '", the file was not found!', e);
+ } catch (e) {
+ logSevereError('Failed to import module with ID "' + moduleID +
+ '".', e);
+ }
+ return scope;
+ },
+
+ /**
+ * @param {Array} moduleIDs
+ * the moduleIDs of the modules to import
+ * @param {Object} scope
+ * (optional) if not specified, one will be created.
+ *
+ * @return {Object} the scope
+ */
+ importModules: function(moduleIDs, scope) {
+ scope = scope || {};
+
+ // caution: the modules should be imported in the order specified!
+ for (let i = 0, len = moduleIDs.length; i < len; ++i) {
+ self.importModule(moduleIDs[i], scope);
+ }
+
+ return scope;
+ },
+
+ /**
+ * @param {String} moduleID
+ * @param {Array} names
+ * the names of the symbols to import
+ * @param {Object} scope
+ * (optional) if not specified, one will be created.
+ *
+ * @return {Object} the scope
+ */
+ defineLazyModuleGetter: function(moduleID, names, scope) {
+ scope = scope || {};
+
+ //console.debug("[RPC] defining lazy module getter(s) for " + moduleID);
+ let uri = getModuleURI(moduleID);
+ for (let i in names) {
+ let name = names[i];
+ XPCOMUtils.defineLazyModuleGetter(scope, name, uri);
+ }
+ importedModuleURIs[uri] = true;
+
+ return scope;
+ },
+
+ /**
+ * @param {Object} modules
+ * An object with moduleID:names attributes which will be given to
+ * self.defineLazyModuleGetter()
+ * @param {Object} scope
+ * (optional) if not specified, one will be created.
+ *
+ * @return {Object} the scope
+ */
+ defineLazyModuleGetters: function(modules, scope) {
+ scope = scope || {};
+
+ for (let id in modules) {
+ if (modules.hasOwnProperty(id)) {
+ self.defineLazyModuleGetter(id, modules[id], scope);
+ }
+ }
+
+ return scope;
+ }
+ };
+
+ return self;
+ }());
++
diff --cc content/lib/subscription.jsm
index b1a3c24,a53103e..5aa7509
--- a/content/lib/subscription.jsm
+++ b/content/lib/subscription.jsm
@@@ -441,3 -442,3 +442,4 @@@ Subscription.prototype =
}
};
++
diff --cc content/lib/utils.jsm
index 0000000,10ff720..a96c6c7
mode 000000,100644..100644
--- a/content/lib/utils.jsm
+++ b/content/lib/utils.jsm
@@@ -1,0 -1,192 +1,193 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see {tag: "http"://www.gnu.org/licenses}.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ let EXPORTED_SYMBOLS = ["Utils"];
+
+ Cu.import("resource://gre/modules/Services.jsm");
+ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+ //Cu.import("resource://gre/modules/devtools/Console.jsm");
+
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ ScriptLoader.importModules([
+ "lib/prefs",
+ "lib/utils/constants",
+ "lib/environment"
+ ], this);
+
+ if (ProcessEnvironment.isMainProcess) {
+ Cu.import("resource://gre/modules/AddonManager.jsm");
+ }
+
+
+
+
+
+ let Utils = (function() {
+ let self = {};
+
+ /**
+ * Posts an action to the event queue of the current thread to run it
+ * asynchronously. Any additional parameters to this function are passed
+ * as parameters to the callback.
+ *
+ * @param {Function} callback
+ * @param {Object} thisPtr
+ */
+ self.runAsync = function(callback, thisPtr) {
+ //console.log("registering async execution. Caller is "+
+ // Components.stack.caller.filename);
+ let params = Array.prototype.slice.call(arguments, 2);
+ let runnable = {
+ run: function() {
+ callback.apply(thisPtr, params);
+ }
+ };
+ self.threadManager.currentThread.dispatch(runnable,
+ Ci.nsIEventTarget.DISPATCH_NORMAL);
+ };
+ XPCOMUtils.defineLazyServiceGetter(self, "categoryManager",
+ "@mozilla.org/categorymanager;1", "nsICategoryManager");
+ XPCOMUtils.defineLazyServiceGetter(self, "threadManager",
+ "@mozilla.org/thread-manager;1", "nsIThreadManager");
+
+
+ /**
+ * Calls a function multiple times until it succeeds. The
+ * function must return TRUE on success.
+ *
+ * @param {function():boolean} aFunction
+ * @param {number} aTries - The number of tries.
+ */
+ self.tryMultipleTimes = function(aFunction, aTries=10) {
+ if (aTries <= 0) {
+ //console.log("no more tries!");
+ return;
+ }
+ let triesLeft = aTries - 1;
+ self.runAsync(function() {
+ if (aFunction.call(null, triesLeft) !== true) {
+ self.tryMultipleTimes(aFunction, triesLeft);
+ }
+ });
+ };
+
+ /**
+ * Return a nested property or `undefined` if it does not exist.
+ * Any element in the object chain may be undefined.
+ *
+ * Other implementations at http://stackoverflow.com/questions/2631001/javascript-test-for-existence-of-nested-object-key
+ *
+ * @param {Object} object
+ * @param {...string} properties
+ */
+ self.getObjectPath = function(object, ...properties) {
+ return properties.reduce(self.getObjectProperty, object);
+ };
+
+ /**
+ * @private
+ */
+ self.getObjectProperty = function(object, property) {
+ if (!!object && object.hasOwnProperty(property)) {
+ return object[property];
+ }
+ return undefined;
+ };
+
+
+
+
+ self.info = {};
+
+ // bad smell...
+ // get/set last/current RP version
+ if (ProcessEnvironment.isMainProcess) {
+ self.info.lastRPVersion = rpPrefBranch.getCharPref("lastVersion");
+
+ self.info.curRPVersion = "0.0";
+ // curRPVersion needs to be set asynchronously
+ AddonManager.getAddonByID(C.EXTENSION_ID, function(addon) {
+ rpPrefBranch.setCharPref("lastVersion", addon.version);
+ self.info.curRPVersion = addon.version;
+ if (self.info.lastRPVersion != self.info.curRPVersion) {
+ Services.prefs.savePrefFile(null);
+ }
+ });
+ }
+
+ // bad smell...
+ // get/set last/current app (e.g. firefox) version
+ if (ProcessEnvironment.isMainProcess) {
+ self.info.lastAppVersion = rpPrefBranch.getCharPref("lastAppVersion");
+
+ let curAppVersion = Services.appinfo.version;
+ self.info.curAppVersion = curAppVersion;
+ rpPrefBranch.setCharPref("lastAppVersion", curAppVersion);
+
+ if (self.info.lastAppVersion != self.info.curAppVersion) {
+ Services.prefs.savePrefFile(null);
+ }
+ }
+
+ let appID = Services.appinfo.ID;
+ self.info.isFirefox = appID === C.FIREFOX_ID;
+ self.info.isSeamonkey = appID === C.SEAMONKEY_ID;
+ self.info.isAustralis = self.info.isFirefox &&
+ Services.vc.compare(Services.appinfo.platformVersion, '29') >= 0;
+
+
+
+ /**
+ * This function returns and eventually creates a module's `internal`
+ * variable. The `internal` can be accessed from all submodules of that
+ * module (which might be in different files).
+ *
+ * The `internal` is added to `self`, and as soon as all modules have been
+ * loaded, i.e. when the startup functions are called, the `internal` is
+ * removed from `self` (the module is „sealed“).
+ *
+ * This function can be used as follows:
+ * let MyModule = (function(self) {
+ * let internal = Utils.moduleInternal(self);
+ * }(MyModule || {}));
+ *
+ * @param {Object} aModuleScope
+ * @returns {Object} the module's `internal`
+ */
+ self.moduleInternal = function(aModuleScope) {
+ aModuleScope.internal = aModuleScope.internal || {};
+ function sealInternal() {
+ delete aModuleScope.internal;
+ };
+ ProcessEnvironment.addStartupFunction(Environment.LEVELS.ESSENTIAL,
+ sealInternal);
+ return aModuleScope.internal;
+ };
+
+ return self;
+ }());
++
diff --cc content/lib/utils/constants.jsm
index 0000000,ef81422..c32fc1c
mode 000000,100644..100644
--- a/content/lib/utils/constants.jsm
+++ b/content/lib/utils/constants.jsm
@@@ -1,0 -1,54 +1,55 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see {tag: "http"://www.gnu.org/licenses}.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ let EXPORTED_SYMBOLS = ["C"];
+
+ let C = {};
+
+ C.EXTENSION_ID = "requestpolicy at requestpolicy.com";
+ C.FIREFOX_ID = "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
+ C.SEAMONKEY_ID = "{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}";
+ C.MMID = C.EXTENSION_ID; // message manager ID
+ C.MM_PREFIX = C.MMID + ":";
+
+ // reason constants for startup(), shutdown(), install() and uninstall()
+ // see https://developer.mozilla.org/en-US/Add-ons/Bootstrapped_extensions#Reason_constants
+ C.APP_STARTUP = 1; // The application is starting up.
+ C.APP_SHUTDOWN = 2; // The application is shutting down.
+ C.ADDON_ENABLE = 3; // The add-on is being enabled.
+ C.ADDON_DISABLE = 4; // The add-on is being disabled. (Also sent during uninstallation)
+ C.ADDON_INSTALL = 5; // The add-on is being installed.
+ C.ADDON_UNINSTALL = 6; // The add-on is being uninstalled.
+ C.ADDON_UPGRADE = 7; // The add-on is being upgraded.
+ C.ADDON_DOWNGRADE = 8; // The add-on is being downgraded.
+
+ // content policy
+ C.CP_OK = Ci.nsIContentPolicy.ACCEPT;
+ C.CP_REJECT = Ci.nsIContentPolicy.REJECT_SERVER;
+
+ C.RULE_ACTION_ALLOW = 1;
+ C.RULE_ACTION_DENY = 2;
++
diff --cc content/lib/utils/dom.jsm
index 0000000,899f6fa..785739f
mode 000000,100644..100644
--- a/content/lib/utils/dom.jsm
+++ b/content/lib/utils/dom.jsm
@@@ -1,0 -1,50 +1,51 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see {tag: "http"://www.gnu.org/licenses}.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ Cu.import("resource://gre/modules/Services.jsm");
+
+ let EXPORTED_SYMBOLS = ["DOMUtils"];
+
+ let DOMUtils = {};
+
+ /**
+ * Function that takes a DOM Element or an Array of DOM elements and removes
+ * all their children.
+ */
+ DOMUtils.removeChildren = function(aElements) {
+ // If aElements is not an Array, put the element in an Array.
+ let elements = Array.isArray(aElements) ? aElements : [aElements];
+ // Note on `isArray` (above):
+ // using `instanceof` did not work. For details see
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
+
+ for (let el of elements) {
+ while (el.firstChild) {
+ el.removeChild(el.firstChild);
+ }
+ }
+ };
++
diff --cc content/lib/utils/domains.jsm
index cc3e50e,585bbfb..77badcc
--- a/content/lib/utils/domains.jsm
+++ b/content/lib/utils/domains.jsm
@@@ -398,3 -360,3 +360,4 @@@ DomainUtil.hasStandardPort = function(u
uri.port == 80 && uri.scheme == "http" ||
uri.port == 443 && uri.scheme == "https";
}
++
diff --cc content/lib/utils/files.jsm
index f6cb6f8,f8ca07c..a67f991
--- a/content/lib/utils/files.jsm
+++ b/content/lib/utils/files.jsm
@@@ -175,3 -202,3 +202,4 @@@ var FileUtil =
return file;
}
};
++
diff --cc content/lib/utils/observers.jsm
index 0000000,0906362..5ee484a
mode 000000,100644..100644
--- a/content/lib/utils/observers.jsm
+++ b/content/lib/utils/observers.jsm
@@@ -1,0 -1,92 +1,93 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ let EXPORTED_SYMBOLS = ["SingleTopicObserver", "SinglePrefBranchObserver"];
+
+ Cu.import("resource://gre/modules/Services.jsm");
+
+
+ /**
+ * Generic Observer class.
+ */
+ function Observer(aCallback) {
+ // As the `observe` function, take directly the parameter.
+ this.observe = aCallback;
+
+ // currently this obserer is not rgistered yet
+ this.isRegistered = false;
+
+ // register this observer
+ this.register();
+ }
+ Observer.prototype.register = function() {
+ if (!this.isRegistered) {
+ this._register();
+ this.isRegistered = true;
+ }
+ };
+ Observer.prototype.unregister = function() {
+ if (this.isRegistered) {
+ this._unregister();
+ this.isRegistered = false;
+ }
+ };
+
+
+ /**
+ * An instance of this class registers itself to `nsIObserverService` on behalf
+ * of some other object.
+ */
+ function SingleTopicObserver(aTopic, aCallback) {
+ this.topic = aTopic;
+ Observer.call(this, aCallback);
+ }
+ SingleTopicObserver.prototype = Object.create(Observer.prototype);
+ SingleTopicObserver.prototype.constructor = Observer;
+
+ SingleTopicObserver.prototype._register = function() {
+ Services.obs.addObserver(this, this.topic, false);
+ };
+ SingleTopicObserver.prototype._unregister = function() {
+ Services.obs.removeObserver(this, this.topic);
+ };
+
+
+ function SinglePrefBranchObserver(aBranch, aDomain, aCallback) {
+ this.branch = aBranch;
+ this.domain = aDomain;
+ Observer.call(this, aCallback);
+ }
+ SinglePrefBranchObserver.prototype = Object.create(Observer.prototype);
+ SinglePrefBranchObserver.prototype.constructor = Observer;
+
+ SinglePrefBranchObserver.prototype._register = function() {
+ this.branch.addObserver(this.domain, this, false);
+ };
+ SinglePrefBranchObserver.prototype._unregister = function() {
+ this.branch.removeObserver(this.domain, this);
+ };
++
diff --cc content/lib/utils/strings.jsm
index 0000000,0a657b9..3d5e986
mode 000000,100644..100644
--- a/content/lib/utils/strings.jsm
+++ b/content/lib/utils/strings.jsm
@@@ -1,0 -1,74 +1,75 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2009 Justin Samuel
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see {tag: "http"://www.gnu.org/licenses}.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ /**
+ * Note: The string utils are used also in the content process (see
+ * e10s/multiprocessor firefox), so this file shouldn't contain code which is
+ * limited to the chrome process.
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ let EXPORTED_SYMBOLS = ["StringUtils"];
+
+ Cu.import("resource://gre/modules/Services.jsm");
+ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+
+ let StringUtils = (function() {
+ let self = {};
+
+ XPCOMUtils.defineLazyGetter(self, "strbundle", function() {
+ return loadPropertiesFile(
+ "chrome://requestpolicy/locale/requestpolicy.properties");
+ });
+ // from https://developer.mozilla.org/en-US/Add-ons/
+ // How_to_convert_an_overlay_extension_to_restartless
+ // #Step_10.3A_Bypass_cache_when_loading_properties_files
+ function loadPropertiesFile(path)
+ {
+ /* HACK: The string bundle cache is cleared on addon shutdown, however it
+ * doesn't appear to do so reliably. Errors can erratically happen on next
+ * load of the same file in certain instances. (at minimum, when strings are
+ * added/removed) The apparently accepted solution to reliably load new
+ * versions is to always create bundles with a unique URL so as to bypass the
+ * cache. This is accomplished by passing a random number in a parameter after
+ * a '?'. (this random ID is otherwise ignored) The loaded string bundle is
+ * still cached on startup and should still be cleared out of the cache on
+ * addon shutdown. This just bypasses the built-in cache for repeated loads of
+ * the same path so that a newly installed update loads cleanly. */
+ return Services.strings.createBundle(path + "?" + Math.random());
+ }
+
+ self.$str = function(aName, aParams) {
+ if (!!aParams) {
+ return self.strbundle.formatStringFromName(aName, aParams,
+ aParams.length);
+ } else {
+ return self.strbundle.GetStringFromName(aName);
+ }
+ };
+
+ return self;
+ }());
++
diff --cc content/lib/utils/windows.jsm
index 0000000,9364d1e..629e3bd
mode 000000,100644..100644
--- a/content/lib/utils/windows.jsm
+++ b/content/lib/utils/windows.jsm
@@@ -1,0 -1,147 +1,148 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see {tag: "http"://www.gnu.org/licenses}.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ let EXPORTED_SYMBOLS = ["WindowUtils"];
+
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ ScriptLoader.importModules(["lib/prefs"], this);
+
+
+
+ let WindowUtils = (function() {
+ let self = {};
+
+ self.getChromeWindow = function(aContentWindow) {
+ return aContentWindow.top.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem)
+ .rootTreeItem
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow);
+ };
+
+ self.getBrowserForWindow = function(aContentWindow) {
+ let win = self.getChromeWindow(aContentWindow);
+ let tabs = self.getTabsForWindow(win);
+ for (let tab of tabs) {
+ if (tab.linkedBrowser.contentWindow === aContentWindow.top) {
+ return tab.linkedBrowser;
+ }
+ }
+ return null;
+ };
+
+ self.getChromeWindowForDocShell = function(aDocShell) {
+ return aDocShell.QueryInterface(Ci.nsIDocShellTreeItem)
+ .rootTreeItem
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow);
+ };
+
+ self.getTabBrowser = function(window) {
+ // bug 1009938 - may be null in SeaMonkey
+ return window.gBrowser || window.getBrowser();
+ };
+
+ self.getTabsForWindow = function(window) {
+ return self.getTabBrowser(window).tabContainer.children;
+ };
+
+ //
+ // Private Browsing
+ //
+
+ // depending on the Firefox vesion, create the `isWindowPrivate` function
+ self.isWindowPrivate = (function() {
+ try {
+ // Firefox 20+
+ Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+
+ return (function(aWindow) {
+ return PrivateBrowsingUtils.isWindowPrivate(aWindow)
+ });
+ } catch(e) {
+ // pre Firefox 20
+ try {
+ let pbs = Cc["@mozilla.org/privatebrowsing;1"]
+ .getService(Ci.nsIPrivateBrowsingService);
+
+ return (function(aWindow) {
+ return pbs.privateBrowsingEnabled;
+ });
+ } catch(e) {
+ Components.utils.reportError(e);
+ // It's uncertain if private browsing is possible at all, so assume
+ // that Private Browsing is not possible.
+ return (function(aWindow) {
+ return true;
+ });
+ }
+ }
+ }());
+
+ /**
+ * Should it be possible to add permanent rules in that window?
+ *
+ * @return {boolean}
+ */
+ self.mayPermanentRulesBeAdded = function(aWindow) {
+ return self.isWindowPrivate(aWindow) === false ||
+ rpPrefBranch.getBoolPref("privateBrowsingPermanentWhitelisting");
+ };
+
+
+ //
+ // Window & DOM utilities
+ //
+
+ /**
+ * Wait for a window to be loaded and then add a list of Elements „by ID“ to
+ * a scope. The scope is optional, but in any case will be returned.
+ *
+ * @return {Object} the scope of the elements
+ */
+ self.getElementsByIdOnLoad = function(aWindow, aElementIDs, aScope,
+ aCallback) {
+ let scope = aScope || {};
+ let document = aWindow.document;
+ let callback = function() {
+ aWindow.removeEventListener("load", callback);
+
+ for (let elementName in aElementIDs) {
+ scope[elementName] = document.getElementById(aElementIDs[elementName]);
+ }
+ if (aCallback) {
+ aCallback();
+ }
+ };
+ aWindow.addEventListener("load", callback);
+ return scope;
+ };
+
+ return self;
+ }());
++
diff --cc content/lib/utils/xul.jsm
index 0000000,877e98a..b119c88
mode 000000,100644..100644
--- a/content/lib/utils/xul.jsm
+++ b/content/lib/utils/xul.jsm
@@@ -1,0 -1,175 +1,176 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see {tag: "http"://www.gnu.org/licenses}.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ Cu.import("resource://gre/modules/Services.jsm");
+
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ ScriptLoader.importModules([
+ "lib/logger",
+ "lib/utils/strings",
+ "lib/utils/constants"
+ ], this);
+
+ let EXPORTED_SYMBOLS = ["XULUtils"];
+
+ let XULUtils = {};
+
+ let xulTrees = XULUtils.xulTrees = {};
+
+ let xulTreesScope = {
+ "exports": xulTrees,
+ "C": C,
+ "appID": Services.appinfo.ID
+ };
+
+ Services.scriptloader.loadSubScript(
+ 'chrome://requestpolicy/content/ui/xul-trees.js',
+ xulTreesScope);
+
+
+ function getParentElement(doc, element) {
+ if (!element.parent) {
+ return false;
+ } else {
+ if (element.parent.id) {
+ return doc.getElementById(element.parent.id);
+ } else if (element.parent.special) {
+ switch (element.parent.special.type) {
+ case "__window__":
+ return doc.querySelector("window");
+
+ case "subobject":
+ let subobjectTree = element.parent.special.tree;
+ let parentElement = doc.getElementById(element.parent.special.id);
+ for (let i = 0, len = subobjectTree.length; i < len; ++i) {
+ parentElement = parentElement[subobjectTree[i]];
+ }
+ return parentElement;
+ default:
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ }
+
+ function isAttribute(element, attr) {
+ return attr != "children" && attr != "parent" && attr != "tag" &&
+ element.hasOwnProperty(attr);
+ }
+
+ function getAttrValue(element, attr) {
+ if (!isAttribute(element, attr)) {
+ return false;
+ }
+ let value = element[attr];
+ if (value.charAt(0) == "&" && value.charAt(value.length-1) == ";") {
+ try {
+ value = StringUtils.$str(value.substr(1, value.length-2));
+ } catch (e) {
+ Logger.severe(Logger.TYPE_ERROR, e, e);
+ return false;
+ }
+ }
+ return value;
+ }
+
+ function setAttributes(node, element) {
+ for (let attr in element) {
+ let value = getAttrValue(element, attr);
+ if (value) {
+ node.setAttribute(attr, value);
+ }
+ }
+ }
+
+
+ function recursivelyAddXULElements(doc, elements, parentElement=null) {
+ let parentElementIsSet = !!parentElement;
+
+ for (let i in elements) {
+ let element = elements[i];
+
+ if (!element || !element.tag) {
+ continue;
+ }
+ parentElement = parentElementIsSet ? parentElement :
+ getParentElement(doc, element);
+ if (false === parentElement) {
+ continue;
+ }
+ if (parentElement === null) {
+ Logger.warning(Logger.TYPE_ERROR,
+ "parentElement of '"+element.id+"' is null!");
+ continue;
+ }
+
+ let newElem = doc.createElement(element.tag);
+ setAttributes(newElem, element);
+ if (element.children) {
+ recursivelyAddXULElements(doc, element.children, newElem);
+ }
+ parentElement.appendChild(newElem);
+ }
+ };
+
+ XULUtils.addTreeElementsToWindow = function(win, treeName) {
+ if (xulTrees.hasOwnProperty(treeName)) {
+ recursivelyAddXULElements(win.document, xulTrees[treeName]);
+ }
+ }
+
+ let elementIDsToRemove = {};
+
+ function getElementIDsToRemove(treeName) {
+ if (elementIDsToRemove.hasOwnProperty(treeName)) {
+ return elementIDsToRemove[treeName];
+ }
+ let ids = elementIDsToRemove[treeName] = [];
+ let tree = xulTrees[treeName];
+ for (let i in tree) {
+ ids.push(tree[i].id);
+ }
+ return ids;
+ }
+
+ XULUtils.removeTreeElementsFromWindow = function(win, treeName) {
+ if (!xulTrees.hasOwnProperty(treeName)) {
+ return;
+ }
+ let tree = xulTrees[treeName];
+ let elementIDs = getElementIDsToRemove(treeName);
+
+ for (let i in elementIDs) {
+ let id = elementIDs[i];
+ let node = win.document.getElementById(id);
+ if (node && node.parentNode) {
+ node.parentNode.removeChild(node);
+ }
+ }
+ }
++
diff --cc content/main/about-uri.jsm
index 0000000,3e0eb27..b60337f
mode 000000,100644..100644
--- a/content/main/about-uri.jsm
+++ b/content/main/about-uri.jsm
@@@ -1,0 -1,129 +1,130 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see {tag: "http"://www.gnu.org/licenses}.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+ const Cr = Components.results;
+
+ let EXPORTED_SYMBOLS = ["AboutRequestPolicy"];
+
+ Components.utils.import("resource://gre/modules/Services.jsm");
+ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+ let globalScope = this;
+
+
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ ScriptLoader.importModules([
+ "lib/environment",
+ "lib/utils"
+ ], globalScope);
+
+
+ var filenames = {
+ "basicprefs": "basicprefs.html",
+ "advancedprefs": "advancedprefs.html",
+ "yourpolicy": "yourpolicy.html",
+ "defaultpolicy": "defaultpolicy.html",
+ "subscriptions": "subscriptions.html",
+ "setup": "setup.html"
+ };
+
+ function getURI(aURI) {
+ let id;
+ let index = aURI.path.indexOf("?");
+ if (index >= 0 && aURI.path.length > index) {
+ id = aURI.path.substr(index+1);
+ }
+ if (!id || !(id in filenames)) {
+ id = "basicprefs";
+ }
+ return "chrome://requestpolicy/content/settings/" + filenames[id];
+ }
+
+
+
+ let AboutRequestPolicy = (function() {
+ let self = {
+ classDescription: "about:requestpolicy",
+ contractID: "@mozilla.org/network/protocol/about;1?what=requestpolicy",
+ classID: Components.ID("{ad30f46c-42a6-45cd-ad0b-08b37f87435a}"),
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]),
+
+ getURIFlags: function(aURI) {
+ return Ci.nsIAboutModule.ALLOW_SCRIPT;
+ },
+
+ newChannel: function(aURI) {
+ let uri = getURI(aURI)
+ let channel = Services.io.newChannel(uri, null, null);
+ channel.originalURI = aURI;
+ return channel;
+ },
+
+ //
+ // nsIFactory interface implementation
+ //
+
+ createInstance: function(outer, iid) {
+ if (outer) {
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+ }
+ return self.QueryInterface(iid);
+ }
+ };
+
+
+ function registerFactory() {
+ Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
+ .registerFactory(self.classID, self.classDescription,
+ self.contractID, self);
+ }
+
+ ProcessEnvironment.addStartupFunction(
+ Environment.LEVELS.INTERFACE,
+ function () {
+ try {
+ registerFactory();
+ } catch (e if e.result === Cr.NS_ERROR_FACTORY_EXISTS) {
+ // When upgrading restartless the old factory might still exist.
+ Utils.runAsync(registerFactory);
+ }
+ });
+
+ function unregisterFactory() {
+ let registrar = Components.manager
+ .QueryInterface(Ci.nsIComponentRegistrar);
+ let {Utils} = ScriptLoader.importModule("lib/utils");
+
+ // This needs to run asynchronously, see Mozilla bug 753687
+ Utils.runAsync(function() {
+ registrar.unregisterFactory(self.classID, self);
+ });
+ }
+ ProcessEnvironment.addShutdownFunction(Environment.LEVELS.INTERFACE,
+ unregisterFactory);
+
+ return self;
+ }());
++
diff --cc content/main/content-policy.jsm
index 0000000,9b91249..ff03185
mode 000000,100644..100644
--- a/content/main/content-policy.jsm
+++ b/content/main/content-policy.jsm
@@@ -1,0 -1,187 +1,188 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+ const Cr = Components.results;
+
+ let EXPORTED_SYMBOLS = ["PolicyImplementation"];
+
+ let globalScope = this;
+
+ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+ Cu.import("resource://gre/modules/Services.jsm");
+
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ ScriptLoader.importModules([
+ "lib/utils/constants",
+ "lib/logger",
+ "lib/request",
+ "lib/utils",
+ "lib/request-processor",
+ "lib/environment"
+ ], globalScope);
+
+
+ // TODO: implement nsIChannelEventSink to catch redirects as Adblock Plus does.
+ let PolicyImplementation = (function() {
+ let xpcom_categories = ["content-policy"];
+
+ let self = {
+ classDescription: "RequestPolicy JavaScript XPCOM Component",
+ classID: Components.ID("{14027e96-1afb-4066-8846-e6c89b5faf3b}"),
+ contractID: "@requestpolicy.com/requestpolicy-service;1"
+ };
+
+ /**
+ * Registers the content policy on startup.
+ */
+ function register() {
+ Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
+ .registerFactory(self.classID, self.classDescription,
+ self.contractID, self);
+
+ let catMan = Utils.categoryManager;
+ for (let category of xpcom_categories) {
+ catMan.addCategoryEntry(category, self.contractID, self.contractID, false,
+ true);
+ }
+
+ if (!self.mimeService) {
+ // self.rejectCode = typeof(/ /) == "object" ? -4 : -3;
+ self.rejectCode = Ci.nsIContentPolicy.REJECT_SERVER;
+ self.mimeService =
+ Cc['@mozilla.org/uriloader/external-helper-app-service;1']
+ .getService(Ci.nsIMIMEService);
+ }
+ }
+
+ ProcessEnvironment.addStartupFunction(
+ Environment.LEVELS.INTERFACE,
+ function () {
+ try {
+ register();
+ } catch (e if e.result === Cr.NS_ERROR_FACTORY_EXISTS) {
+ // When upgrading restartless the old factory might still exist.
+ Utils.runAsync(register);
+ }
+ });
+
+
+ function unregister() {
+ Logger.dump("shutting down PolicyImplementation...");
+
+ // Below the shouldLoad function is replaced by a new one
+ // which always allows *all* requests.
+ //
+ // What's the reason?
+ // ------------------
+ // The function for unregistering, which is `unregisterFactory`,
+ // has to be called async; this means that the unregistering
+ // will be done at a later time. However, it's necessary to
+ // disable blocking *right now*.
+ //
+ // Why is that necessary?
+ // ----------------------
+ // It's possible (or always true) that async functions
+ // get called *after* the addon finished shutting down.
+ // After the shutdown RequestPolicy's modules and
+ // functions can't be used anymore, the modules have
+ // been unloaded already. There might be still some
+ // objects or closures, but it's unreliable to use
+ // them.
+ // However, the shouldLoad function needs many of
+ // RequestPolicy's other modules and functions. So any
+ // call to RP's `shouldLoad` might cause exceptions,
+ // given that the call happens between now and the
+ // time when the factory is actually unregistered.
+
+ // Before defining the new shouldLoad ...
+ // ... save the return value in the closure of this function.
+ // Similarly like described above this is necessary
+ // because the `C` variable is not available anymore
+ // after RP has been shut down.
+ var finalReturnValue = C.CP_OK;
+
+ // Actually create the final function, as it is described
+ // above.
+ self.shouldLoad = () => finalReturnValue;
+
+ let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+ let catMan = Utils.categoryManager;
+
+ for (let category of xpcom_categories) {
+ catMan.deleteCategoryEntry(category, self.contractID, false);
+ }
+
+ // This needs to run asynchronously, see bug 753687
+ Utils.runAsync(function() {
+ registrar.unregisterFactory(self.classID, self);
+ });
+ }
+
+ ProcessEnvironment.addShutdownFunction(Environment.LEVELS.INTERFACE,
+ unregister);
+
+ //
+ // nsISupports interface implementation
+ //
+
+ self.QueryInterface = XPCOMUtils.generateQI([Ci.nsIContentPolicy,
+ Ci.nsIObserver,
+ Ci.nsIFactory,
+ Ci.nsISupportsWeakReference]);
+
+ //
+ // nsIContentPolicy interface implementation
+ //
+
+ // https://developer.mozilla.org/en/nsIContentPolicy
+
+ self.shouldLoad = function(aContentType, aContentLocation, aRequestOrigin,
+ aContext, aMimeTypeGuess, aExtra, aRequestPrincipal) {
+ var request = new NormalRequest(
+ aContentType, aContentLocation, aRequestOrigin, aContext,
+ aMimeTypeGuess, aExtra, aRequestPrincipal);
+ return RequestProcessor.process(request);
+ // TODO: implement the following
+ // return request.shouldLoad(aContentType, aContentLocation, aRequestOrigin,
+ // aContext, aMimeTypeGuess, aExtra, aRequestPrincipal);
+ };
+
+ self.shouldProcess = (() => C.CP_OK);
+
+ //
+ // nsIFactory interface implementation
+ //
+
+ self.createInstance = function(outer, iid) {
+ if (outer) {
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+ }
+ return self.QueryInterface(iid);
+ };
+
+ return self;
+ }());
++
diff --cc content/main/default-pref-handler.js
index 0000000,77a62c7..17bc980
mode 000000,100644..100644
--- a/content/main/default-pref-handler.js
+++ b/content/main/default-pref-handler.js
@@@ -1,0 -1,114 +1,115 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2011 Justin Samuel
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ // This file has to be called only once. It handles the default preferences [1],
+ // so it has to be called quite early at the extension startup.
+ //
+ // Note that this script may *only* be loaded from the main process!
+ // Also note that if possible this script shouldn't import any other of RP's
+ // modules, e.g. to prevent import() loops.
+ //
+ // [1] https://developer.mozilla.org/en-US/Add-ons/How_to_convert_an_overlay_extension_to_restartless#Step_4.3A_Manually_handle_default_preferences
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ Cu.import("resource://gre/modules/Services.jsm");
+
+
+
+ let prefInitFunctions = {
+ getGenericPref: function(branch, prefName) {
+ switch (branch.getPrefType(prefName)) {
+ case 32:
+ // PREF_STRING
+ return this.getUCharPref(prefName, branch);
+
+ case 64:
+ // PREF_INT
+ return branch.getIntPref(prefName);
+
+ case 128:
+ // PREF_BOOL
+ return branch.getBoolPref(prefName);
+
+ case 0:
+ default:
+ // PREF_INVALID
+ return undefined;
+ }
+ },
+
+ setGenericPref: function(branch, prefName, prefValue) {
+ switch (typeof prefValue) {
+ case "string":
+ this.setUCharPref(prefName, prefValue, branch);
+ return;
+ case "number":
+ branch.setIntPref(prefName, prefValue);
+ return;
+ case "boolean":
+ branch.setBoolPref(prefName, prefValue);
+ return;
+ }
+ },
+
+ setDefaultPref: function(prefName, prefValue) {
+ var defaultBranch = Services.prefs.getDefaultBranch(null);
+ this.setGenericPref(defaultBranch, prefName, prefValue);
+ },
+
+ getUCharPref: function(prefName, branch) { // Unicode getCharPref
+ branch = branch || Services.prefs;
+ return branch.getComplexValue(prefName, Ci.nsISupportsString).data;
+ },
+
+ setUCharPref: function(prefName, text, branch) { // Unicode setCharPref
+ var string = Cc["@mozilla.org/supports-string;1"]
+ .createInstance(Ci.nsISupportsString);
+ string.data = text;
+ branch = branch || Services.prefs;
+ branch.setComplexValue(prefName, Ci.nsISupportsString, string);
+ }
+ };
+
+ let defaultPrefScriptScope = {
+ pref: prefInitFunctions.setDefaultPref,
+ setGenericPref: prefInitFunctions.setGenericPref,
+ setUCharPref: prefInitFunctions.setUCharPref
+ };
+
+
+ //
+ // Load default preferences (if necessary)
+ //
+
+ try {
+ // this is necessary for restartless extensions:
+ // ( See https://developer.mozilla.org/en-US/Add-ons/
+ // How_to_convert_an_overlay_extension_to_restartless
+ // #Step_4.3A_Manually_handle_default_preferences )
+ Services.scriptloader.loadSubScript(
+ "chrome://requestpolicy/content/lib/default-preferences.js",
+ defaultPrefScriptScope);
+ } catch (e) {}
++
diff --cc content/main/pref-manager.jsm
index 0000000,74cb485..a6b8c52
mode 000000,100644..100644
--- a/content/main/pref-manager.jsm
+++ b/content/main/pref-manager.jsm
@@@ -1,0 -1,145 +1,146 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ let EXPORTED_SYMBOLS = ["PrefManager"];
+
+ let globalScope = this;
+
+ Cu.import("resource://gre/modules/Services.jsm");
+ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+ Cu.import("resource://gre/modules/devtools/Console.jsm");
+
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ ScriptLoader.importModules([
+ "lib/utils/constants",
+ "lib/environment"
+ ], globalScope);
+
+ XPCOMUtils.defineLazyGetter(globalScope, "rpPrefBranch", function() {
+ return Services.prefs.getBranch("extensions.requestpolicy.")
+ .QueryInterface(Ci.nsIPrefBranch2);
+ });
+ XPCOMUtils.defineLazyGetter(globalScope, "rootPrefBranch", function() {
+ return Services.prefs.getBranch("").QueryInterface(Ci.nsIPrefBranch2);
+ });
+
+
+
+
+
+ let PrefManager = (function() {
+ let self = {};
+
+
+ // TODO: move to bootstrap.js
+ function handleUninstallOrDisable() {
+ var resetLinkPrefetch = rpPrefBranch.getBoolPref(
+ "prefetch.link.restoreDefaultOnUninstall");
+ var resetDNSPrefetch = rpPrefBranch.getBoolPref(
+ "prefetch.dns.restoreDefaultOnUninstall");
+
+ if (resetLinkPrefetch) {
+ if (rootPrefBranch.prefHasUserValue("network.prefetch-next")) {
+ rootPrefBranch.clearUserPref("network.prefetch-next");
+ }
+ }
+ if (resetDNSPrefetch) {
+ if (rootPrefBranch.prefHasUserValue("network.dns.disablePrefetch")) {
+ rootPrefBranch.clearUserPref("network.dns.disablePrefetch");
+ }
+ }
+ Services.prefs.savePrefFile(null);
+ }
+
+
+ self.init = function() {
+ // ================================
+ // manually handle RP's default preferences
+ // ----------------------------------------
+ // The following script needs to be called because bootsrapped addons have
+ // to handle their default preferences manually, see Mozilla Bug 564675:
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=564675
+ // The scope of that script doesn't need to be remembered.
+ Services.scriptloader.loadSubScript(
+ "chrome://requestpolicy/content/main/default-pref-handler.js",
+ {});
+
+
+ // ================================
+ // Link/DNS prefetching
+ // --------------------
+ // Disable link prefetch.
+ if (rpPrefBranch.getBoolPref("prefetch.link.disableOnStartup")) {
+ if (rootPrefBranch.getBoolPref("network.prefetch-next")) {
+ rootPrefBranch.setBoolPref("network.prefetch-next", false);
+ console.info("Disabled link prefetch.");
+ }
+ }
+ // Disable DNS prefetch.
+ if (rpPrefBranch.getBoolPref("prefetch.dns.disableOnStartup")) {
+ // network.dns.disablePrefetch only exists starting in Firefox 3.1 (and it
+ // doesn't have a default value, at least in 3.1b2, but if and when it
+ // does have a default it will be false).
+ if (!rootPrefBranch.prefHasUserValue("network.dns.disablePrefetch") ||
+ !rootPrefBranch.getBoolPref("network.dns.disablePrefetch")) {
+ rootPrefBranch.setBoolPref("network.dns.disablePrefetch", true);
+ console.info("Disabled DNS prefetch.");
+ }
+ }
+
+
+ // ================================
+ // Clean up old, unused prefs (removed in 0.2.0).
+ // ----------------------------------------------
+ let deletePrefs = [
+ "temporarilyAllowedOrigins",
+ "temporarilyAllowedDestinations",
+ "temporarilyAllowedOriginsToDestinations"
+ ];
+ for (var i = 0; i < deletePrefs.length; i++) {
+ if (rpPrefBranch.prefHasUserValue(deletePrefs[i])) {
+ rpPrefBranch.clearUserPref(deletePrefs[i]);
+ }
+ }
+
+
+ Services.prefs.savePrefFile(null);
+ };
+
+
+ function eventuallyHandleUninstallOrDisable(data, reason) {
+ if (reason == C.ADDON_DISABLE || reason == C.ADDON_UNINSTALL) {
+ // TODO: Handle uninstallation in bootstrap.js, not here, RP might be
+ // disabled when being uninstalled.
+ handleUninstallOrDisable();
+ }
+ }
+ ProcessEnvironment.addShutdownFunction(Environment.LEVELS.BACKEND,
+ eventuallyHandleUninstallOrDisable);
+
+ return self;
+ }());
++
diff --cc content/main/requestpolicy-service.jsm
index 0000000,25e683e..718b3e5
mode 000000,100644..100644
--- a/content/main/requestpolicy-service.jsm
+++ b/content/main/requestpolicy-service.jsm
@@@ -1,0 -1,233 +1,234 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ let EXPORTED_SYMBOLS = ["rpService"];
+
+ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+ Cu.import("resource://gre/modules/Services.jsm");
+
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ ScriptLoader.importModules([
+ "lib/logger",
+ "lib/prefs",
+ "lib/utils/domains",
+ "lib/policy-manager",
+ "lib/subscription",
+ "lib/utils",
+ "lib/utils/constants",
+ "lib/environment"
+ ], this);
+
+
+
+ let rpService = (function() {
+ let self = {};
+
+ // /////////////////////////////////////////////////////////////////////////
+ // Internal Data
+ // /////////////////////////////////////////////////////////////////////////
+
+ let subscriptions = null;
+
+
+ // /////////////////////////////////////////////////////////////////////////
+ // Utility
+ // /////////////////////////////////////////////////////////////////////////
+
+
+ function loadConfigAndRules() {
+ subscriptions = new UserSubscriptions();
+ PolicyManager.loadUserRules();
+
+ var defaultPolicy = Prefs.isDefaultAllow() ? "allow" : "deny";
+
+ var failures = PolicyManager.loadSubscriptionRules(
+ subscriptions.getSubscriptionInfo(defaultPolicy));
+ // TODO: check a preference that indicates the last time we checked for
+ // updates. Don't do it if we've done it too recently.
+ // TODO: Maybe we should probably ship snapshot versions of the official
+ // rulesets so that they can be available immediately after installation.
+ var serials = {};
+ for (var listName in failures) {
+ serials[listName] = {};
+ for (var subName in failures[listName]) {
+ serials[listName][subName] = -1;
+ }
+ }
+ var loadedSubs = PolicyManager.getSubscriptionRulesets();
+ for (var listName in loadedSubs) {
+ for (var subName in loadedSubs[listName]) {
+ if (!serials[listName]) {
+ serials[listName] = {};
+ }
+ var rawRuleset = loadedSubs[listName][subName].rawRuleset;
+ serials[listName][subName] = rawRuleset._metadata['serial'];
+ }
+ }
+ function updateCompleted(result) {
+ Logger.info(Logger.TYPE_INTERNAL,
+ 'Subscription updates completed: ' + result);
+ }
+ subscriptions.update(updateCompleted, serials, defaultPolicy);
+ }
+
+ // TODO: move to window manager
+ function showWelcomeWindow() {
+ if (!rpPrefBranch.getBoolPref("welcomeWindowShown")) {
+ var url = "about:requestpolicy?setup";
+
+ var wm = Cc['@mozilla.org/appshell/window-mediator;1'].
+ getService(Ci.nsIWindowMediator);
+ var windowtype = 'navigator:browser';
+ var mostRecentWindow = wm.getMostRecentWindow(windowtype);
+
+ // the gBrowser object of the firefox window
+ var _gBrowser = mostRecentWindow.getBrowser();
+
+ if (typeof(_gBrowser.addTab) != "function") return;
+
+ _gBrowser.selectedTab = _gBrowser.addTab(url);
+
+ rpPrefBranch.setBoolPref("welcomeWindowShown", true);
+ Services.prefs.savePrefFile(null);
+ }
+ }
+
+
+
+
+
+ // /////////////////////////////////////////////////////////////////////////
+ // startup and shutdown functions
+ // /////////////////////////////////////////////////////////////////////////
+
+ // prepare back-end
+ ProcessEnvironment.addStartupFunction(Environment.LEVELS.BACKEND,
+ loadConfigAndRules);
+
+ function registerObservers() {
+ ProcessEnvironment.obMan.observe([
+ "sessionstore-windows-restored",
+ SUBSCRIPTION_UPDATED_TOPIC,
+ SUBSCRIPTION_ADDED_TOPIC,
+ SUBSCRIPTION_REMOVED_TOPIC,
+
+ // support for old browsers (Firefox <20)
+ // TODO: support per-window temporary rules
+ // see https://github.com/RequestPolicyContinued/requestpolicy/issues/533#issuecomment-68851396
+ "private-browsing"
+ ], self.observe);
+ }
+ ProcessEnvironment.addStartupFunction(Environment.LEVELS.INTERFACE,
+ registerObservers);
+
+ ProcessEnvironment.addStartupFunction(
+ Environment.LEVELS.UI,
+ function(data, reason) {
+ if (reason !== C.APP_STARTUP) {
+ // In case of the app's startup `showWelcomeWindow()` will be
+ // called when "sessionstore-windows-restored" is observed.
+ showWelcomeWindow();
+ }
+ });
+
+
+
+
+
+ self.getSubscriptions = function() {
+ return subscriptions;
+ };
+
+
+
+
+ // /////////////////////////////////////////////////////////////////////////
+ // nsIObserver interface
+ // /////////////////////////////////////////////////////////////////////////
+
+ self.observe = function(subject, topic, data) {
+ switch (topic) {
+ case SUBSCRIPTION_UPDATED_TOPIC:
+ Logger.debug(Logger.TYPE_INTERNAL, 'XXX updated: ' + data);
+ // TODO: check if the subscription is enabled. The user might have
+ // disabled it between the time the update started and when it
+ // completed.
+ var subInfo = JSON.parse(data);
+ var failures = PolicyManager.loadSubscriptionRules(subInfo);
+ break;
+
+ case SUBSCRIPTION_ADDED_TOPIC:
+ Logger.debug(Logger.TYPE_INTERNAL, 'XXX added: ' + data);
+ var subInfo = JSON.parse(data);
+ var failures = PolicyManager.loadSubscriptionRules(subInfo);
+ var failed = false;
+ for (var listName in failures) {
+ failed = true;
+ }
+ if (failed) {
+ var serials = {};
+ for (var listName in subInfo) {
+ if (!serials[listName]) {
+ serials[listName] = {};
+ }
+ for (var subName in subInfo[listName]) {
+ serials[listName][subName] = -1;
+ }
+ }
+ let updateCompleted = function(result) {
+ Logger.info(Logger.TYPE_INTERNAL,
+ 'Subscription update completed: ' + result);
+ }
+ subscriptions.update(updateCompleted, serials);
+ }
+ break;
+
+ case SUBSCRIPTION_REMOVED_TOPIC:
+ Logger.debug(Logger.TYPE_INTERNAL, 'YYY: ' + data);
+ var subInfo = JSON.parse(data);
+ var failures = PolicyManager.unloadSubscriptionRules(subInfo);
+ break;
+
+ case "sessionstore-windows-restored":
+ showWelcomeWindow();
+ break;
+
+ // support for old browsers (Firefox <20)
+ case "private-browsing" :
+ if (data == "exit") {
+ PolicyManager.revokeTemporaryRules();
+ }
+ break;
+
+ default :
+ Logger.warning(Logger.TYPE_ERROR, "unknown topic observed: " + topic);
+ }
+ };
+
+ return self;
+ }());
++
diff --cc content/main/window-manager-toolbarbutton.js
index 0000000,e8c4210..10a2339
mode 000000,100644..100644
--- a/content/main/window-manager-toolbarbutton.js
+++ b/content/main/window-manager-toolbarbutton.js
@@@ -1,0 -1,152 +1,153 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see {tag: "http"://www.gnu.org/licenses}.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ const toolbarButtonId = "requestpolicyToolbarButton";
+
+
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ ScriptLoader.importModules(["lib/utils/xul", "lib/utils", "lib/logger"], this);
+
+ if (Utils.info.isAustralis) {
+ Components.utils.import("resource:///modules/CustomizableUI.jsm");
+ }
+
+
+
+ let rpWindowManager = (function(self) {
+
+ let isAustralis = Utils.info.isAustralis;
+
+ //
+ // Case 1: Australis (Firefox >= 29)
+ //
+
+ if (isAustralis) {
+ ProcessEnvironment.addStartupFunction(Environment.LEVELS.UI,
+ addToolbarButtonToAustralis);
+ ProcessEnvironment.addShutdownFunction(Environment.LEVELS.UI,
+ removeToolbarButtonFromAustralis);
+ }
+
+ function removeToolbarButtonFromAustralis() {
+ let tbb = XULUtils.xulTrees.toolbarbutton[0];
+ CustomizableUI.destroyWidget(tbb.id);
+ }
+
+ function addToolbarButtonToAustralis() {
+ let tbb = XULUtils.xulTrees.toolbarbutton[0];
+ CustomizableUI.createWidget({
+ id: tbb.id,
+ defaultArea: CustomizableUI.AREA_NAVBAR,
+ label: tbb.label,
+ tooltiptext: tbb.tooltiptext,
+ onCommand : function(aEvent) {
+ // Bad smell
+ let win = aEvent.target.ownerDocument.defaultView;
+ win.requestpolicy.overlay.openToolbarPopup(aEvent.target);
+ }
+ });
+ }
+
+
+ //
+ // Case 2: Gecko < 29
+ //
+
+
+ // this function can be deleted if Gecko < 29 isn't supported anymore
+ self.addToolbarButtonToWindow = function(win) {
+ if (!isAustralis) {
+ XULUtils.addTreeElementsToWindow(win, "toolbarbutton");
+ addToolbarButtonToNavBar(win);
+ }
+ };
+
+ self.removeToolbarButtonFromWindow = function(win) {
+ if (!isAustralis) {
+ XULUtils.removeTreeElementsFromWindow(win, "toolbarbutton");
+ }
+ };
+
+
+ function addToolbarButtonToNavBar(win) {
+ // SeaMonkey users have to use a toolbar button now. At the moment I can't
+ // justify a bunch of special cases to support the statusbar when such a
+ // tiny number of users have seamonkey and I can't even be sure that many of
+ // those users want a statusbar icon.
+ //if (!Utils.info.isFirefox) {
+ // Logger.info(Logger.TYPE_INTERNAL,
+ // "Not performing toolbar button check: not Firefox.");
+ // return;
+ //}
+ let doc = win.document;
+
+ let isFirstRun = false;
+ if (Services.vc.compare(Utils.info.lastAppVersion, "0.0") <= 0) {
+ Logger.info(Logger.TYPE_INTERNAL, "This is the first run.");
+ isFirstRun = true;
+ }
+
+ let id = toolbarButtonId;
+
+ // find the toolbar in which the button has been placed by the user
+ let toolbarSelector = "[currentset^='" + id + ",'],[currentset*='," + id +
+ ",'],[currentset$='," + id + "']";
+ let toolbar = doc.querySelector(toolbarSelector);
+
+ if (!toolbar) {
+ // The button is in none of the toolbar "currentset"s. Either this is
+ // the first run or the button has been removed by the user (through
+ // customizing)
+ if (isFirstRun) {
+ toolbar = doc.getElementById("nav-bar");
+ toolbar.insertItem(id);
+ toolbar.setAttribute("currentset", toolbar.currentSet);
+ doc.persist(toolbar.id, "currentset");
+ }
+ } else {
+ // find the position of the button and insert.
+ let currentset = toolbar.getAttribute("currentset").split(",");
+ let index = currentset.indexOf(id);
+
+ let before = null;
+ for (let i = index + 1, len = currentset.length; i < len; i++) {
+ before = doc.getElementById(currentset[i]);
+ if (before) {
+ toolbar.insertItem(id, before);
+ break;
+ }
+ }
+ if (!before) {
+ toolbar.insertItem(id);
+ }
+ }
+ }
+
+
+ return self;
+ }(rpWindowManager || {}));
++
diff --cc content/main/window-manager.jsm
index 0000000,ba15538..16ec8c5
mode 000000,100644..100644
--- a/content/main/window-manager.jsm
+++ b/content/main/window-manager.jsm
@@@ -1,0 -1,237 +1,238 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see {tag: "http"://www.gnu.org/licenses}.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ let EXPORTED_SYMBOLS = ["rpWindowManager"];
+
+ let globalScope = this;
+
+
+ let rpWindowManager = (function(self) {
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ Cu.import("resource://gre/modules/Services.jsm", globalScope);
+ Cu.import("resource://gre/modules/XPCOMUtils.jsm", globalScope);
+
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm", globalScope);
+ ScriptLoader.importModules([
+ "lib/utils",
+ "lib/utils/xul",
+ "lib/utils/constants",
+ "lib/environment"
+ ], globalScope);
+
+ // import the WindowListener
+ Services.scriptloader.loadSubScript(
+ "chrome://requestpolicy/content/main/window-manager.listener.js",
+ globalScope);
+
+ let styleSheets = [
+ "chrome://requestpolicy/skin/requestpolicy.css"
+ ];
+ if (Utils.info.isSeamonkey) {
+ styleSheets.push("chrome://requestpolicy/skin/toolbarbutton-seamonkey.css");
+ } else {
+ styleSheets.push("chrome://requestpolicy/skin/toolbarbutton.css");
+ }
+
+ let frameScriptURI = "chrome://requestpolicy/content/ui/frame.js?" +
+ Math.random();
+
+
+
+ function loadIntoWindow(window) {
+ // ==================================
+ // # 1 : add all XUL elements
+ // --------------------------
+ try {
+ XULUtils.addTreeElementsToWindow(window, "mainTree");
+ } catch (e) {
+ Logger.warning(Logger.TYPE_ERROR,
+ "Couldn't add tree elements to window. " + e, e);
+ }
+
+
+ // ==================================
+ // # 2 : create a scope variable for RP for this window
+ // ----------------------------------------------------
+ window.requestpolicy = {};
+
+
+ // ==================================
+ // # 3 : load the overlay's and menu's javascript
+ // ----------------------------------------------
+ try {
+ Services.scriptloader.loadSubScript(
+ "chrome://requestpolicy/content/ui/overlay.js",
+ window);
+ Services.scriptloader.loadSubScript(
+ "chrome://requestpolicy/content/ui/menu.js",
+ window);
+ Services.scriptloader.loadSubScript(
+ "chrome://requestpolicy/content/ui/classicmenu.js",
+ window);
+ } catch (e) {
+ Logger.warning(Logger.TYPE_ERROR,
+ "Error loading subscripts for window: " + e, e);
+ }
+
+
+ // ==================================
+ // # 4 : toolbar button
+ // --------------------
+ try {
+ self.addToolbarButtonToWindow(window);
+ } catch (e) {
+ Logger.warning(Logger.TYPE_ERROR, "Error while adding the toolbar " +
+ "button to the window: "+e, e);
+ }
+
+
+ // ==================================
+ // # 5 : init the overlay
+ // ----------------------
+ try {
+ // init must be called last, because it assumes that
+ // everything else is ready
+ window.requestpolicy.overlay.init();
+ } catch (e) {
+ Logger.warning(Logger.TYPE_ERROR,
+ "An error occurred while initializing the overlay: "+e, e);
+ }
+ }
+
+ function unloadFromWindow(window) {
+ // # 5 : the overlay cares itself about shutdown.
+ // nothing to do here.
+
+
+ // # 4 : remove the toolbarbutton
+ // ------------------------------
+ self.removeToolbarButtonFromWindow(window);
+
+
+ // # 3 and 2 : remove the `requestpolicy` variable from the window
+ // ---------------------------------------------------------
+ // This wouldn't be needed when the window is closed, but this has to be
+ // done when RP is being disabled.
+ delete window.requestpolicy;
+
+
+ // # 1 : remove all XUL elements
+ XULUtils.removeTreeElementsFromWindow(window, "mainTree");
+ }
+
+
+
+
+
+ ProcessEnvironment.addStartupFunction(
+ Environment.LEVELS.INTERFACE,
+ function(data, reason) {
+ forEachOpenWindow(loadIntoWindow);
+ WindowListener.setLoadFunction(loadIntoWindow);
+ WindowListener.setUnloadFunction(unloadFromWindow);
+ WindowListener.startListening();
+
+ // Load the framescript into all existing tabs.
+ // Also tell the globalMM to load it into each new
+ // tab from now on.
+ var globalMM = Cc["@mozilla.org/globalmessagemanager;1"]
+ .getService(Ci.nsIMessageListenerManager);
+ globalMM.loadFrameScript(frameScriptURI, true);
+ });
+
+ ProcessEnvironment.addStartupFunction(Environment.LEVELS.UI, loadStyleSheets);
+
+ ProcessEnvironment.addShutdownFunction(
+ Environment.LEVELS.INTERFACE,
+ function() {
+ // Stop loading framescripts into new tabs.
+ // --------------------------
+ // Note that it's not necessary to tell the framescripts'
+ // environments to shut down. Instead:
+ // - In case the window is closed, the framescript will shut
+ // down on the ContentFrameMessageManager's "unload" event.
+ // - In case the addon is being disabled or firefox gets quit,
+ // the ParentProcessEnvironment will send a message to all
+ // children.
+ var globalMM = Cc["@mozilla.org/globalmessagemanager;1"]
+ .getService(Ci.nsIMessageListenerManager);
+ globalMM.removeDelayedFrameScript(frameScriptURI);
+
+ forEachOpenWindow(unloadFromWindow);
+ WindowListener.stopListening();
+ });
+
+ ProcessEnvironment.addShutdownFunction(Environment.LEVELS.UI,
+ unloadStyleSheets);
+
+
+
+
+
+ function loadStyleSheets() {
+ let styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"]
+ .getService(Ci.nsIStyleSheetService);
+
+ for (let i = 0, len = styleSheets.length; i < len; i++) {
+ let styleSheetURI = Services.io.newURI(styleSheets[i], null, null);
+ styleSheetService.loadAndRegisterSheet(styleSheetURI,
+ styleSheetService.USER_SHEET);
+ }
+ }
+ function unloadStyleSheets() {
+ // Unload stylesheets
+ let styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"]
+ .getService(Ci.nsIStyleSheetService);
+
+ for (let i = 0, len = styleSheets.length; i < len; i++) {
+ let styleSheetURI = Services.io.newURI(styleSheets[i], null, null);
+ if (styleSheetService.sheetRegistered(styleSheetURI,
+ styleSheetService.USER_SHEET)) {
+ styleSheetService.unregisterSheet(styleSheetURI,
+ styleSheetService.USER_SHEET);
+ }
+ }
+ }
+
+ function forEachOpenWindow(functionToCall) {
+ // Apply a function to all open browser windows
+ let windows = Services.wm.getEnumerator("navigator:browser");
+ while (windows.hasMoreElements()) {
+ functionToCall(windows.getNext().QueryInterface(Ci.nsIDOMWindow));
+ }
+ }
+
+
+ return self;
+ }(rpWindowManager || {}));
+
+
+ // extend rpWindowManager
+ Services.scriptloader.loadSubScript(
+ "chrome://requestpolicy/content/main/window-manager-toolbarbutton.js",
+ globalScope);
++
diff --cc content/main/window-manager.listener.js
index 0000000,ba6fb4d..79b7fa6
mode 000000,100644..100644
--- a/content/main/window-manager.listener.js
+++ b/content/main/window-manager.listener.js
@@@ -1,0 -1,150 +1,151 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see {tag: "http"://www.gnu.org/licenses}.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ let WindowListener = (function() {
+ let self = {};
+
+ let nextWinID = 0;
+ let listeners = {};
+
+
+ let addEvLis = function(eventName, winID) {
+ if ((typeof listeners[winID]) !== 'undefined' &&
+ listeners[winID][eventName] !== null) {
+ listeners[winID].window.addEventListener(eventName,
+ listeners[winID][eventName],
+ false);
+ }
+ };
+
+ let removeEvLis = function(eventName, winID) {
+ if (typeof listeners[winID] !== 'undefined' &&
+ listeners[winID][eventName] !== null) {
+ listeners[winID].window.removeEventListener(eventName,
+ listeners[winID][eventName]);
+ if (eventName == 'unload') {
+ // when removing the 'unload' listener, also remove the 'load'
+ // listener and then delete listener[winID].
+ removeEvLis("load", winID);
+ // cleaning up -- listeners[winID] is not needed anymore
+ delete listeners[winID];
+ }
+ }
+ };
+
+
+
+ let addEventListenersToWindow = function(window) {
+ let winID = nextWinID++;
+
+ // ===========================
+ // create new functions specific for each window.
+ // ----------------------------------------------
+ let onLoad = function(event) {
+ removeEvLis("load", winID);
+
+ if (window.document.documentElement.getAttribute("windowtype") ==
+ "navigator:browser") {
+ if (!!externalLoadFunction) {
+ externalLoadFunction(window);
+ }
+ } else {
+ removeEvLis("unload", winID);
+ }
+ };
+ let onUnload = function(event) {
+ removeEvLis("unload", onUnload);
+
+ if (window.document.documentElement.getAttribute("windowtype") ==
+ "navigator:browser") {
+ if (!!externalUnloadFunction) {
+ externalUnloadFunction(window);
+ }
+ }
+ };
+ // ===========================
+
+ listeners[winID] = {window: window, load: onLoad, unload: onUnload};
+
+ // Event handler for when the window is closed. We listen for "unload"
+ // rather than "close" because "close" will fire when a print preview
+ // opened from this window is closed.
+ addEvLis("unload", winID);
+
+ // Registers event handlers for documents loaded in the window.
+ addEvLis("load", winID);
+
+ return winID;
+ };
+
+
+ function removeAllEventListeners() {
+ for (let winID in listeners) {
+ removeEvLis("load", winID);
+ removeEvLis("unload", winID);
+ }
+ listeners = {};
+ nextWinID = 0;
+ }
+
+
+
+ // external functions to be called on "load" or "unload" events
+ let externalLoadFunction = null;
+ let externalUnloadFunction = null;
+ self.setLoadFunction = function(f) {
+ externalLoadFunction = f;
+ };
+ self.setUnloadFunction = function(f) {
+ externalUnloadFunction = f;
+ };
+
+
+ let listening = false;
+ self.startListening = function() {
+ if (listening === false) {
+ Services.wm.addListener(WindowListener);
+ listening = true;
+ }
+ };
+ self.stopListening = function() {
+ if (listening === true) {
+ Services.wm.removeListener(WindowListener);
+ listening = false;
+ }
+ // remove all "load" and "unload" event listeners.
+ removeAllEventListeners();
+ };
+
+
+
+ self.onOpenWindow = function(xulWindow) {
+ let window = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow);
+ addEventListenersToWindow(window);
+ };
+ self.onCloseWindow = function(xulWindow) {};
+ self.onWindowTitleChange = function(xulWindow, newTitle) {};
+
+ return self;
+ }());
++
diff --cc content/settings/advancedprefs.js
index 0000000,e8ac116..92fc69c
mode 000000,100644..100644
--- a/content/settings/advancedprefs.js
+++ b/content/settings/advancedprefs.js
@@@ -1,0 -1,133 +1,134 @@@
+ var PAGE_STRINGS = [
+ 'basic',
+ 'advanced',
+ 'advancedPreferences',
+ 'linkPrefetching',
+ 'dnsPrefetching',
+ 'enabled',
+ 'disableOnStartup',
+ 'restoreDefaultOnUninstall',
+ 'menuPreferences',
+ 'menuSorting',
+ 'sortByNumRequests',
+ 'sortByDestName',
+ 'noSorting',
+ 'hint',
+ 'menuSortingHint',
+ 'menuIndicatedInformation',
+ 'menuIndicateNumRequests'
+ ];
+
+ $(function () {
+ common.localize(PAGE_STRINGS);
+ });
+
+ Cu.import("resource://gre/modules/Services.jsm");
+
+
+ function updateDisplay() {
+ // Link prefetch.
+ $id('pref-linkPrefetch').checked =
+ rootPrefBranch.getBoolPref('network.prefetch-next');
+
+ $id('pref-prefetch.link.disableOnStartup').checked =
+ rpPrefBranch.getBoolPref('prefetch.link.disableOnStartup');
+
+ $id('pref-prefetch.link.restoreDefaultOnUninstall').checked =
+ rpPrefBranch.getBoolPref('prefetch.link.restoreDefaultOnUninstall');
+
+ // DNS prefetch.
+ $id('pref-dnsPrefetch').checked =
+ !rootPrefBranch.getBoolPref('network.dns.disablePrefetch');
+
+ $id('pref-prefetch.dns.disableOnStartup').checked =
+ rpPrefBranch.getBoolPref('prefetch.dns.disableOnStartup');
+
+ $id('pref-prefetch.dns.restoreDefaultOnUninstall').checked =
+ rpPrefBranch.getBoolPref('prefetch.dns.restoreDefaultOnUninstall');
+
+ // TODO: Create a class which acts as an API for preferences and which ensures
+ // that the returned value is always a valid value for "string" preferences.
+ var sorting = rpPrefBranch.getCharPref('menu.sorting');
+
+ if (sorting == $id('sortByNumRequests').value) {
+ $id('sortByNumRequests').checked = true;
+ $id('sortByDestName').checked = false;
+ $id('noSorting').checked = false;
+ } else if (sorting == $id('noSorting').value) {
+ $id('sortByNumRequests').checked = false;
+ $id('sortByDestName').checked = false;
+ $id('noSorting').checked = true;
+ } else {
+ $id('sortByNumRequests').checked = false;
+ $id('sortByDestName').checked = true;
+ $id('noSorting').checked = false;
+ }
+
+ $id('menu.info.showNumRequests').checked =
+ rpPrefBranch.getBoolPref('menu.info.showNumRequests');
+ }
+
+
+ function onload() {
+ updateDisplay();
+
+ // Link prefetch.
+ elManager.addListener($id('pref-linkPrefetch'), 'change', function (event) {
+ rootPrefBranch.setBoolPref('network.prefetch-next', event.target.checked);
+ Services.prefs.savePrefFile(null);
+ });
+
+ elManager.addListener(
+ $id('pref-prefetch.link.disableOnStartup'), 'change',
+ function (event) {
+ rpPrefBranch.setBoolPref('prefetch.link.disableOnStartup',
+ event.target.checked);
+ Services.prefs.savePrefFile(null);
+ });
+
+ elManager.addListener(
+ $id('pref-prefetch.link.restoreDefaultOnUninstall'), 'change',
+ function (event) {
+ rpPrefBranch.setBoolPref('prefetch.link.restoreDefaultOnUninstall', event.target.checked);
+ Services.prefs.savePrefFile(null);
+ });
+
+ // DNS prefetch.
+ elManager.addListener($id('pref-dnsPrefetch'), 'change', function (event) {
+ rootPrefBranch.setBoolPref('network.dns.disablePrefetch', !event.target.checked);
+ Services.prefs.savePrefFile(null);
+ });
+
+ elManager.addListener(
+ $id('pref-prefetch.dns.disableOnStartup'), 'change',
+ function (event) {
+ rpPrefBranch.setBoolPref('prefetch.dns.disableOnStartup', event.target.checked);
+ Services.prefs.savePrefFile(null);
+ });
+
+ elManager.addListener(
+ $id('pref-prefetch.dns.restoreDefaultOnUninstall'), 'change',
+ function (event) {
+ rpPrefBranch.setBoolPref('prefetch.dns.restoreDefaultOnUninstall', event.target.checked);
+ Services.prefs.savePrefFile(null);
+ });
+
+ var sortingListener = function (event) {
+ rpPrefBranch.setCharPref('menu.sorting', event.target.value);
+ Services.prefs.savePrefFile(null);
+ };
+ elManager.addListener($id('sortByNumRequests'), 'change', sortingListener);
+ elManager.addListener($id('sortByDestName'), 'change', sortingListener);
+ elManager.addListener($id('noSorting'), 'change', sortingListener);
+
+ elManager.addListener(
+ $id('menu.info.showNumRequests'), 'change',
+ function (event) {
+ rpPrefBranch.setBoolPref('menu.info.showNumRequests', event.target.checked);
+ Services.prefs.savePrefFile(null);
+ });
+
+ // call updateDisplay() every time a preference gets changed
+ WinEnv.obMan.observePrefChanges(updateDisplay);
+ }
++
diff --cc content/settings/basicprefs.js
index 0000000,34011d9..7d67c0d
mode 000000,100644..100644
--- a/content/settings/basicprefs.js
+++ b/content/settings/basicprefs.js
@@@ -1,0 -1,79 +1,80 @@@
+ var PAGE_STRINGS = [
+ 'basic',
+ 'advanced',
+ 'webPages',
+ 'indicateBlockedImages',
+ 'dontIndicateBlacklisted',
+ 'autoReload',
+ 'menu',
+ 'allowAddingNonTemporaryRulesInPBM'
+ ];
+
+ $(function () {
+ common.localize(PAGE_STRINGS);
+ });
+
+ Cu.import("resource://gre/modules/Services.jsm");
+
+
+ function updateDisplay() {
+ var indicate = rpPrefBranch.getBoolPref('indicateBlockedObjects');
+ $id('pref-indicateBlockedObjects').checked = indicate;
+ $id('indicateBlockedImages-details').hidden = !indicate;
+
+ $id('pref-dontIndicateBlacklistedObjects').checked =
+ !rpPrefBranch.getBoolPref('indicateBlacklistedObjects');
+
+ $id('pref-autoReload').checked =
+ rpPrefBranch.getBoolPref('autoReload');
+
+ $id('pref-privateBrowsingPermanentWhitelisting').checked =
+ rpPrefBranch.getBoolPref('privateBrowsingPermanentWhitelisting');
+
+ // if (rpPrefBranch.getBoolPref('defaultPolicy.allow')) {
+ // var word = 'allow';
+ // } else {
+ // var word = 'block';
+ // }
+ // $id('defaultpolicyword').innerHTML = word;
+ }
+
+
+ function onload() {
+ updateDisplay();
+
+ elManager.addListener(
+ $id('pref-indicateBlockedObjects'), 'change',
+ function (event) {
+ rpPrefBranch.setBoolPref('indicateBlockedObjects', event.target.checked);
+ Services.prefs.savePrefFile(null);
+ updateDisplay();
+ });
+
+ elManager.addListener(
+ $id('pref-dontIndicateBlacklistedObjects'), 'change',
+ function (event) {
+ rpPrefBranch.setBoolPref('indicateBlacklistedObjects',
+ !event.target.checked);
+ Services.prefs.savePrefFile(null);
+ updateDisplay();
+ });
+
+ elManager.addListener($id('pref-autoReload'), 'change', function(event) {
+ rpPrefBranch.setBoolPref('autoReload', event.target.checked);
+ Services.prefs.savePrefFile(null);
+ updateDisplay();
+ });
+
+ elManager.addListener(
+ $id('pref-privateBrowsingPermanentWhitelisting'), 'change',
+ function (event) {
+ rpPrefBranch.setBoolPref('privateBrowsingPermanentWhitelisting',
+ event.target.checked);
+ Services.prefs.savePrefFile(null);
+ updateDisplay();
+ });
+
+ // call updateDisplay() every time a preference gets changed
+ WinEnv.obMan.observePrefChanges(updateDisplay);
+ }
++
diff --cc content/settings/common.js
index f88cb55,0919023..1c580ca
--- a/content/settings/common.js
+++ b/content/settings/common.js
@@@ -243,3 -228,3 +228,4 @@@ common.localize = function(stringNames
$(function() {
common.localize(COMMON_STRINGS);
});
++
diff --cc content/settings/defaultpolicy.js
index 0000000,b8c7fdd..68ba2dc
mode 000000,100644..100644
--- a/content/settings/defaultpolicy.js
+++ b/content/settings/defaultpolicy.js
@@@ -1,0 -1,79 +1,80 @@@
+ var PAGE_STRINGS = [
+ 'yourPolicy',
+ 'defaultPolicy',
+ 'subscriptions',
+ 'allowRequestsByDefault',
+ 'blockRequestsByDefault',
+ 'defaultPolicyDefinition',
+ 'learnMore',
+ 'allowRequestsToTheSameDomain',
+ 'differentSubscriptionsAreAvailable',
+ 'manageSubscriptions'
+ ];
+
+ $(function () {
+ common.localize(PAGE_STRINGS);
+ });
+
+ Cu.import("resource://gre/modules/Services.jsm");
+
+
+ function updateDisplay() {
+ var defaultallow = rpPrefBranch.getBoolPref('defaultPolicy.allow');
+ if (defaultallow) {
+ $id('defaultallow').checked = true;
+ $id('defaultdenysetting').hidden = true;
+ } else {
+ $id('defaultdeny').checked = true;
+ $id('defaultdenysetting').hidden = false;
+ }
+
+ var allowsamedomain = rpPrefBranch.getBoolPref('defaultPolicy.allowSameDomain');
+ $id('allowsamedomain').checked = allowsamedomain;
+ }
+
+ function showManageSubscriptionsLink() {
+ $id('subscriptionschanged').style.display = 'block';
+ }
+
+ function onload() {
+ updateDisplay();
+
+ elManager.addListener(
+ $id('defaultallow'), 'change',
+ function (event) {
+ var allow = event.target.checked;
+ rpPrefBranch.setBoolPref('defaultPolicy.allow', allow);
+ Services.prefs.savePrefFile(null);
+ // Reload all subscriptions because it's likely that different
+ // subscriptions will now be active.
+ common.switchSubscriptionPolicies();
+ updateDisplay();
+ showManageSubscriptionsLink();
+ });
+
+ elManager.addListener(
+ $id('defaultdeny'), 'change',
+ function (event) {
+ var deny = event.target.checked;
+ rpPrefBranch.setBoolPref('defaultPolicy.allow', !deny);
+ Services.prefs.savePrefFile(null);
+ // Reload all subscriptions because it's likely that different
+ // subscriptions will now be active.
+ common.switchSubscriptionPolicies();
+ updateDisplay();
+ showManageSubscriptionsLink();
+ });
+
+ elManager.addListener(
+ $id('allowsamedomain'), 'change',
+ function (event) {
+ var allowSameDomain = event.target.checked;
+ rpPrefBranch.setBoolPref('defaultPolicy.allowSameDomain',
+ allowSameDomain);
+ Services.prefs.savePrefFile(null);
+ });
+
+ // call updateDisplay() every time a preference gets changed
+ WinEnv.obMan.observePrefChanges(updateDisplay);
+ }
++
diff --cc content/settings/oldrules.js
index 514336e,4eadd11..e015191
--- a/content/settings/oldrules.js
+++ b/content/settings/oldrules.js
@@@ -108,3 -108,3 +108,4 @@@ function onload()
populateRuleTable();
$('#addhostwildcards').change(handleAddHostWildcardsChange);
}
++
diff --cc content/settings/setup.js
index a24e91e,dd6ad4c..438dd73
--- a/content/settings/setup.js
+++ b/content/settings/setup.js
@@@ -134,3 -155,3 +155,4 @@@ function onload()
$('input[name=subscriptions]').change(handleSubscriptionsChange);
$('#allowsamedomain').change(handleAllowSameDomainChange);
}
++
diff --cc content/settings/subscriptions.js
index 6854cc0,d6d873c..5f47095
--- a/content/settings/subscriptions.js
+++ b/content/settings/subscriptions.js
@@@ -124,14 -121,20 +121,21 @@@ function onload()
Logger.dump('Skipping unexpected official subName: ' + subName);
continue;
}
- el.addEventListener('change', handleSubscriptionCheckboxChange);
+ elManager.addListener(el, 'change', handleSubscriptionCheckboxChange);
}
- prefsChangedObserver = new common.PrefsChangedObserver(
- function(subject, topic, data) {
- updateDisplay();
- });
- window.addEventListener("beforeunload", function(event) {
- prefsChangedObserver.unregister();
- });
+ var selector = '[data-defaultpolicy=' +
+ (Prefs.isDefaultAllow() ? 'deny' : 'allow') + ']';
+ var matches = document.body.querySelectorAll(selector);
+ var hideElements = Array.prototype.slice.call(matches);
+ for (var i = 0; i < hideElements.length; i++) {
+ hideElements[i].style.display = 'none';
+ }
+
+ // call updateDisplay() every time a subscription is added or removed
+ WinEnv.obMan.observe([
+ SUBSCRIPTION_ADDED_TOPIC,
+ SUBSCRIPTION_REMOVED_TOPIC
+ ], updateDisplay);
}
++
diff --cc content/settings/yourpolicy.js
index 5ddcf97,a9e1a6a..4f331f6
--- a/content/settings/yourpolicy.js
+++ b/content/settings/yourpolicy.js
@@@ -237,12 -231,13 +231,14 @@@ function onload()
}, SEARCH_DELAY);
}, false);
populateRuleTable(search.value);
- if (rpService.oldRulesExist()) {
- $('#oldrulesexist').show();
+ if (Prefs.oldRulesExist()) {
+ $id("oldrulesexist").hidden = false;
}
- rulesChangedObserver = new RulesChangedObserver();
- window.addEventListener("beforeunload", function(event) {
- rulesChangedObserver.unregister();
+ // observe rule changes and update the table then
+ WinEnv.obMan.observe(["requestpolicy-rules-changed"], function() {
+ var search = $id('rulesearch');
+ populateRuleTable(search.value);
});
}
++
diff --cc content/ui/classicmenu.js
index 0fe5832,0915048..4c2da18
--- a/content/ui/classicmenu.js
+++ b/content/ui/classicmenu.js
@@@ -100,28 -105,27 +105,28 @@@ requestpolicy.classicmenu = (function(
var command = "requestpolicy.overlay.allowOriginToDestination('"
+ requestpolicy.menu._sanitizeJsFunctionArg(originHost) + "', '"
+ requestpolicy.menu._sanitizeJsFunctionArg(destHost) + "');";
- var item = this.addMenuItem(menu, label, command);
+ var item = self.addMenuItem(menu, label, command);
item.setAttribute("class", "requestpolicyAllowOriginToDest");
return item;
- },
+ };
- addMenuItemTemporarilyAllowDest : function(menu, destHost) {
- var label = requestpolicy.menu._strbundle.
- getFormattedString("allowDestinationTemporarily", [destHost]);
+ self.addMenuItemTemporarilyAllowDest = function(menu, destHost) {
+ var label = StringUtils.$str("allowDestinationTemporarily", [destHost]);
var command = "requestpolicy.overlay.temporarilyAllowDestination('"
+ requestpolicy.menu._sanitizeJsFunctionArg(destHost) + "');";
- var item = this.addMenuItem(menu, label, command);
+ var item = self.addMenuItem(menu, label, command);
item.setAttribute("class", "requestpolicyTemporary");
return item;
- },
+ };
- addMenuItemAllowDest : function(menu, destHost) {
- var label = requestpolicy.menu._strbundle.
- getFormattedString("allowDestination", [destHost]);
+ self.addMenuItemAllowDest = function(menu, destHost) {
+ var label = StringUtils.$str("allowDestination", [destHost]);
var command = "requestpolicy.overlay.allowDestination('"
+ requestpolicy.menu._sanitizeJsFunctionArg(destHost) + "');";
- return this.addMenuItem(menu, label, command);
- }
+ return self.addMenuItem(menu, label, command);
+ };
+
+ return self;
+ }());
+
- }
diff --cc content/ui/frame.blocked-content.js
index 0000000,88290e7..61a7a12
mode 000000,100644..100644
--- a/content/ui/frame.blocked-content.js
+++ b/content/ui/frame.blocked-content.js
@@@ -1,0 -1,96 +1,97 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2011 Justin Samuel
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ let ManagerForBlockedContent = (function() {
+ let self = {};
+
+
+ let missingImageDataUri = "data:image/png;base64,"
+ + "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c"
+ + "6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0"
+ + "SU1FB9gMFRANL5LXnioAAAJWSURBVDjLnZI/ixtXFMV/972ZNzPSrmTtalex"
+ + "lsWBGMfEYOzaVciXyKdIkW/hFKnS22WafIDUxk0g2AQSgm0csIPWK42ktaSR"
+ + "NPP+pRBK5SLOqS7cew7ccw4xxrPJ+8XdHx4+7AE8e3Cj++zLm71fvrqT8x+Q"
+ + "AK35dJr2n/x89urTa+eDm/cS+eI2y3eT+Lx/bt8u1vNqfDH++teXdk/6ThAf"
+ + "UUBIgL9ku75z/8WL7LOlhXIGJ0Pyw75wMcnGv//xSQ2DH4ddu9k01dXWsWzc"
+ + "ofhYaiiViLjiWi9UWQa1gzcjWF7hgfzzW5ydnXB62JLjg0PTLfJertNepnQS"
+ + "IA+gE4Cs03UuNYYQYP4e5jPogmSG9vA6rrjC+0AxN2i5Qk0DpXVJhCQB0EVR"
+ + "rzqdFgB1DZfvCDHixiV2NqO6LHHKIKnQMoaWbFBgIrQVgIXaDc+JCHgP5QRZ"
+ + "r4jzGWFbo6yncRYviiiQKUhBRch3Lyix4bgPWsAkcDkmZAV2OiE0DaI1WoES"
+ + "hRKF3sWnmt01pFBnJydEpZDEwHSGt47lYsls43AIXjTWV9R1Qx0DGahqLyAh"
+ + "bqrj0/ib0nRzXNoyCo0Kkor2llV0eKOwdUMg4pSQA7JPQXvnJv1B+GlwOvrG"
+ + "laXB6fV2lb5t6qOtike56DSJgYDGBQcOAsQAfueBMeHR48fhadb1j/58HWAR"
+ + "dt6yBv7+/vpBe2o5OogxlcaKdt5aKCNsk309W0WxKQjmQ33/9mJVAdWHdmo/"
+ + "tNvtRZIkfCz+ZQwGg6rT6Zj/LTAajTbD4bD5WIF/AAseEisPFO8uAAAAAElF"
+ + "TkSuQmCC";
+
+ let transparentImageDataUri = "data:image/gif;base64,R0lGODlhAQABAIAAA"
+ + "AAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
+
+ self.indicateBlockedVisibleObjects = function(doc, blockedURIs) {
+ if (Object.getOwnPropertyNames(blockedURIs).length == 0) {
+ // there are no blocked uris
+ return;
+ }
+
+ let images = doc.getElementsByTagName("img");
+
+ // Ideally, want the image to be a broken image so that the alt text
+ // shows. By default, the blocked image will just not show up at all.
+ // Setting img.src to a broken resource:// causes "save page as" to fail
+ // for some earlier Fx 3.x versions. Also, using a broken resource://
+ // causes our width setting to be ignored, as does using null for img.src.
+ // With Firefox 4, setting img.src to null doesn't work reliably for
+ // having the rest of the styles (e.g. background and border) applied.
+ // So, for now we're punting on trying to display alt text. We'll just use
+ // a transparent image as the replacement image.
+ // Note that with our changes to the image here, "save page as" works but
+ // different data is saved depending on what type of "save page as" the
+ // user performs. With "save all files", the saved source includes the
+ // original, blocked image src. With "web page, complete" the saved source
+ // has changes we make here to show the blocked request indicator.
+
+ for (var i = 0; i < images.length; i++) {
+ var img = images[i];
+ // Note: we're no longer checking img.requestpolicyBlocked here.
+ if (!img.requestpolicyIdentified && img.src in blockedURIs) {
+ img.requestpolicyIdentified = true;
+ img.style.border = "solid 1px #fcc";
+ img.style.backgroundRepeat = "no-repeat";
+ img.style.backgroundPosition = "center center";
+ img.style.backgroundImage = "url('" + missingImageDataUri + "')";
+ if (!img.width) {
+ img.width = 50;
+ }
+ if (!img.height) {
+ img.height = 50;
+ }
+ img.title = "[" + blockedURIs[img.src].identifier + "]"
+ + (img.title ? " " + img.title : "")
+ + (img.alt ? " " + img.alt : "");
+ img.src = transparentImageDataUri;
+ }
+ }
+ };
+
+ return self;
+ }());
++
diff --cc content/ui/frame.dom-content-loaded.js
index 0000000,9809156..5ffc929
mode 000000,100644..100644
--- a/content/ui/frame.dom-content-loaded.js
+++ b/content/ui/frame.dom-content-loaded.js
@@@ -1,0 -1,315 +1,316 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2011 Justin Samuel
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+
+ let ManagerForDOMContentLoaded = (function() {
+ let self = {};
+
+ let {DomainUtil} = ScriptLoader.importModule("lib/utils/domains");
+ let {Utils} = ScriptLoader.importModule("lib/utils");
+
+
+ function htmlAnchorTagClicked(event) {
+ // Notify the main thread that a link has been clicked.
+ // Note: The <a> element is `currentTarget`! See:
+ // https://developer.mozilla.org/en-US/docs/Web/API/Event.currentTarget
+ overlayComm.run(function() {
+ mm.sendSyncMessage(C.MM_PREFIX + "notifyLinkClicked",
+ {origin: event.currentTarget.ownerDocument.URL,
+ dest: event.currentTarget.href});
+ });
+ }
+
+
+ /**
+ * Determines if documentToCheck is the main document loaded in the currently
+ * active tab.
+ *
+ * @param {document} documentToCheck
+ * @return {Boolean}
+ */
+ function isActiveTopLevelDocument(documentToCheck) {
+ return documentToCheck === content.document;
+ }
+
+ /**
+ * Things to do when a page has loaded (after images, etc., have been loaded).
+ *
+ * @param {Event} event
+ */
+ function onDOMContentLoaded(event) {
+ // TODO: This is getting called multiple times for a page, should only be
+ // called once.
+ // <--- the above comment is very old – is it still true that
+ // onDOMContentLoaded is eventually called multiple times?
+ var doc = event.originalTarget;
+ if (doc.nodeName != "#document") {
+ // only documents
+ return;
+ }
+
+ onDocumentLoaded(doc);
+
+ overlayComm.run(function() {
+ let answers = mm.sendSyncMessage(C.MM_PREFIX + "notifyDocumentLoaded",
+ {documentURI: doc.documentURI});
+ if (answers.length === 0) {
+ Logger.warning(Logger.TYPE_ERROR, 'There seems to be no message ' +
+ 'listener for "notifyDocumentLoaded".');
+ } else {
+ // take only one answer. If there are more answers, they are ignored
+ // ==> there must be only one listener for 'notifyDocumentLoaded'
+ let answer = answers[0];
+
+ var blockedURIs = answer.blockedURIs;
+ //console.debug("Received " +
+ // Object.getOwnPropertyNames(blockedURIs).length +
+ // " blocked URIs.");
+
+ // Indicating blocked visible objects isn't an urgent task, so this should
+ // be done async.
+ Utils.runAsync(function() {
+ ManagerForBlockedContent.indicateBlockedVisibleObjects(doc, blockedURIs);
+ });
+ }
+
+ if (isActiveTopLevelDocument(doc)) {
+ mm.sendAsyncMessage(C.MM_PREFIX + "notifyTopLevelDocumentLoaded");
+ }
+ });
+ }
+
+ /**
+ * Things to do when a page or a frame within the page has loaded.
+ *
+ * @param {Event} event
+ */
+ function onDOMFrameContentLoaded(event) {
+ // TODO: This only works for (i)frames that are direct children of the main
+ // document, not (i)frames within those (i)frames.
+ var iframe = event.target;
+ // Flock's special home page is about:myworld. It has (i)frames in it
+ // that have no contentDocument. It's probably related to the fact that
+ // that is an xul page.
+ if (iframe.contentDocument === undefined) {
+ return;
+ }
+
+ // TODO: maybe this can check if the iframe's documentURI is in the other
+ // origins of the current document, and that way not just be limited to
+ // direct children of the main document. That would require building the
+ // other origins every time an iframe is loaded. Maybe, then, this should
+ // use a timeout like observerBlockedRequests does.
+ if (isActiveTopLevelDocument(iframe.ownerDocument)) {
+ overlayComm.run(function() {
+ mm.sendAsyncMessage(C.MM_PREFIX + "notifyDOMFrameContentLoaded");
+ });
+ }
+ }
+
+
+
+
+ /**
+ * Perform the actions required once the DOM is loaded. This may be being
+ * called for more than just the page content DOM. It seems to work for now.
+ *
+ * @param {Event} event
+ */
+ function onDocumentLoaded(doc) {
+ // Create a new Environment for this Document and shut it down when
+ // the document is unloaded.
+ let DocEnv = new Environment(framescriptEnv, "DocEnv");
+ DocEnv.shutdownOnUnload(doc.defaultView);
+ // start up the Environment immediately, as it won't have any startup
+ // functions.
+ DocEnv.startup();
+
+
+ let documentURI = doc.documentURI;
+
+ let metaRefreshes = [];
+
+ // Find all meta redirects.
+ var metaTags = doc.getElementsByTagName("meta");
+ for (var i = 0; i < metaTags.length; i++) {
+ let metaTag = metaTags[i];
+ if (!metaTag.httpEquiv || metaTag.httpEquiv.toLowerCase() != "refresh") {
+ continue;
+ }
+
+ let originalDestURI = null;
+
+ // TODO: Register meta redirects so we can tell which blocked requests
+ // were meta redirects in the statusbar menu.
+ // TODO: move this logic to the requestpolicy service.
+
+ // The dest may be empty if the origin is what should be refreshed. This
+ // will be handled by DomainUtil.determineRedirectUri().
+ let {delay, destURI} = DomainUtil.parseRefresh(metaTag.content);
+
+ // If destURI isn't a valid uri, assume it's a relative uri.
+ if (!DomainUtil.isValidUri(destURI)) {
+ originalDestURI = destURI;
+ destURI = doc.documentURIObject.resolve(destURI);
+ }
+
+ metaRefreshes.push({delay: delay, destURI: destURI,
+ originalDestURI: originalDestURI});
+ }
+
+ if (metaRefreshes.length > 0) {
+ // meta refreshes have been found.
+
+ Logger.info(Logger.TYPE_META_REFRESH,
+ "Number of meta refreshes found: " + metaRefreshes.length);
+
+
+ var docShell = doc.defaultView
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell);
+ if (!docShell.allowMetaRedirects) {
+ Logger.warning(Logger.TYPE_META_REFRESH,
+ "Another extension disabled docShell.allowMetaRedirects.");
+ }
+
+ overlayComm.run(function() {
+ mm.sendAsyncMessage(C.MM_PREFIX + "handleMetaRefreshes",
+ {documentURI: documentURI, metaRefreshes: metaRefreshes});
+ });
+ }
+
+ // Find all anchor tags and add click events (which also fire when enter
+ // is pressed while the element has focus).
+ // This seems to be a safe approach in that the MDC states that javascript
+ // can't be used to initiate a click event on a link:
+ // http://developer.mozilla.org/en/DOM/element.click
+ // We keep this even though we have the document looking for clicks because
+ // for certain links the target will not be the link (and we can't use the
+ // currentTarget in the other case it seems, as we can here). There probably
+ // is some solution when handling the click events at the document level,
+ // but I just don't know what it is. For now, there remains the risk of
+ // dynamically added links whose target of the click event isn't the anchor
+ // tag.
+ // TODO: is it possible to implement this differently?
+ var anchorTags = doc.getElementsByTagName("a");
+ for (let anchorTag of anchorTags) {
+ anchorTag.addEventListener("click", htmlAnchorTagClicked, false);
+ }
+ DocEnv.addShutdownFunction(Environment.LEVELS.INTERFACE, function() {
+ for (let anchorTag of anchorTags) {
+ anchorTag.removeEventListener("click", htmlAnchorTagClicked, false);
+ }
+ });
+
+ // maybe not needed?
+ //wrapWindowFunctions(doc.defaultView);
+ //DocEnv.addShutdownFunction(Environment.LEVELS.INTERFACE, function() {
+ // unwrapWindowFunctions(doc.defaultView);
+ //});
+ }
+
+ /**
+ * This function wraps an existing method of a window object.
+ * If that method is being called after being wrapped, first the custom
+ * function will be called and then the original function.
+ *
+ * @param {Window} aWindow
+ * @param {String} aFunctionName The name of the window's method.
+ * @param {Function} aNewFunction
+ */
+ function wrapWindowFunction(aWindow, aFunctionName, aNewFunction) {
+ aWindow.rpOriginalFunctions = aWindow.rpOriginalFunctions || {};
+ let originals = aWindow.rpOriginalFunctions;
+
+ if (!(aFunctionName in originals)) {
+ originals[aFunctionName] = aWindow[aFunctionName];
+ aWindow[aFunctionName] = function() {
+ aNewFunction.apply(aWindow, arguments);
+ return originals[aFunctionName].apply(aWindow, arguments);
+ }
+ }
+ }
+ function unwrapWindowFunction(aWindow, aFunctionName) {
+ if (typeof aWindow.rpOriginalFunctions !== 'object') {
+ return;
+ }
+ let originals = aWindow.rpOriginalFunctions;
+
+ if (aFunctionName in originals) {
+ aWindow[aFunctionName] =
+ originals[aFunctionName];
+ delete originals[aFunctionName];
+ }
+ }
+
+ /**
+ * Wraps the window's open() and openDialog() methods so that RequestPolicy
+ * can know the origin and destination URLs of the window being opened. Assume
+ * that if window.open() calls have made it this far, it's a window the user
+ * wanted open (e.g. they have allowed the popup). Unfortunately, this method
+ * (or our timing of doing self) doesn't seem to work for popups that are
+ * allowed popups (the user has allowed popups from the domain). So, the
+ * workaround was to also add the 'if(aContext.nodeName == "xul:browser" &&
+ * aContext.currentURI && aContext.currentURI.spec == "about:blank")' to
+ * shouldLoad().
+ *
+ * @param {Window} aWindow
+ */
+ function wrapWindowFunctions(aWindow) {
+ wrapWindowFunction(aWindow, "open",
+ function(url, windowName, windowFeatures) {
+ overlayComm.run(function() {
+ mm.sendSyncMessage(C.MM_PREFIX + "notifyLinkClicked",
+ {origin: aWindow.document.documentURI,
+ dest: url});
+ });
+ });
+
+ wrapWindowFunction(aWindow, "openDialog",
+ function() {
+ // openDialog(url, name, features, arg1, arg2, ...)
+ overlayComm.run(function() {
+ mm.sendSyncMessage(C.MM_PREFIX + "notifyLinkClicked",
+ {origin: aWindow.document.documentURI,
+ dest: arguments[0]});
+ });
+ });
+ }
+ function unwrapWindowFunctions(aWindow) {
+ unwrapWindowFunction(aWindow, "open");
+ unwrapWindowFunction(aWindow, "openDialog");
+ delete aWindow.rpOriginalFunctions;
+ }
+
+
+ framescriptEnv.elManager.addListener(mm, "DOMContentLoaded",
+ onDOMContentLoaded, true);
+
+ // DOMFrameContentLoaded is same DOMContentLoaded but also fires for
+ // enclosed frames.
+ framescriptEnv.elManager.addListener(mm, "DOMFrameContentLoaded",
+ onDOMFrameContentLoaded, true);
+
+ return self;
+ }());
++
diff --cc content/ui/frame.js
index 0000000,b661ec3..bfc0889
mode 000000,100644..100644
--- a/content/ui/frame.js
+++ b/content/ui/frame.js
@@@ -1,0 -1,164 +1,165 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2011 Justin Samuel
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ Components.utils.import("resource://gre/modules/Services.jsm");
+ Components.utils.import("resource://gre/modules/devtools/Console.jsm");
+
+ /**
+ * This anonymous function is needed because of Mozilla Bug 673569, fixed in
+ * Firefox 29 / Gecko 29.
+ * The bug means that all frame scripts run in the same shared scope. The
+ * anonymous function ensures that the framescripts do not overwrite
+ * one another.
+ */
+ (function () {
+ //console.debug('[RPC] new framescript loading...');
+
+ // the ContentFrameMessageManager of this framescript
+ let mm = this;
+
+ const Cu = Components.utils;
+
+ // import some modules
+ let {ScriptLoader} = Cu.import(
+ "chrome://requestpolicy/content/lib/script-loader.jsm", {});
+ let mod = {};
+ ScriptLoader.importModules([
+ "lib/utils/constants",
+ "lib/logger",
+ "lib/environment",
+ "lib/framescript-to-overlay-communication"
+ ], mod);
+ let {C, Logger, Environment, FrameScriptEnvironment,
+ FramescriptToOverlayCommunication} = mod;
+
+
+ let framescriptEnv = new FrameScriptEnvironment(mm);
+ let mlManager = framescriptEnv.mlManager;
+ let overlayComm = new FramescriptToOverlayCommunication(framescriptEnv);
+
+
+ // Create a scope for the sub-scripts, which also can
+ // be removed easily when the framescript gets unloaded.
+ var framescriptScope = {
+ "mm": mm,
+ "content": mm.content,
+ "Components": mm.Components,
+
+ "Ci": mm.Components.interfaces,
+ "Cc": mm.Components.classes,
+ "Cu": mm.Components.utils,
+
+ "ScriptLoader": ScriptLoader,
+ "C": C,
+ "Logger": Logger,
+ "console": console,
+ "Environment": Environment,
+
+ "framescriptEnv": framescriptEnv,
+ "mlManager": mlManager,
+ "overlayComm": overlayComm
+ };
+
+ function loadSubScripts() {
+ Services.scriptloader.loadSubScript(
+ 'chrome://requestpolicy/content/ui/frame.blocked-content.js',
+ framescriptScope);
+ Services.scriptloader.loadSubScript(
+ 'chrome://requestpolicy/content/ui/frame.dom-content-loaded.js',
+ framescriptScope);
+ }
+ framescriptEnv.addStartupFunction(Environment.LEVELS.ESSENTIAL,
+ loadSubScripts);
+
+ framescriptEnv.addShutdownFunction(Environment.LEVELS.ESSENTIAL, function() {
+ //console.debug("removing framescriptScope '" + framescriptEnv.uid + "'");
+ framescriptScope = null;
+ });
+
+
+
+
+ function reloadDocument() {
+ content.document.location.reload(false);
+ }
+ mlManager.addListener("reload", reloadDocument);
+
+ function setLocation(aUri) {
+ content.document.location.href = aUri;
+ }
+ mlManager.addListener("setLocation", function (message) {
+ setLocation(message.data.uri);
+ });
+
+
+
+ // Listen for click events so that we can allow requests that result from
+ // user-initiated link clicks and form submissions.
+ function mouseClicked(event) {
+ // If mozInputSource is undefined or zero, then this was a javascript-generated event.
+ // If there is a way to forge mozInputSource from javascript, then that could be used
+ // to bypass RequestPolicy.
+ if (!event.mozInputSource) {
+ return;
+ }
+ // The following show up as button value 0 for links and form input submit buttons:
+ // * left-clicks
+ // * enter key while focused
+ // * space bar while focused (no event sent for links in this case)
+ if (event.button != 0) {
+ return;
+ }
+ // Link clicked.
+ // I believe an empty href always gets filled in with the current URL so
+ // it will never actually be empty. However, I don't know this for certain.
+ if (event.target.nodeName.toLowerCase() == "a" && event.target.href) {
+ overlayComm.run(function() {
+ sendSyncMessage(C.MM_PREFIX + "notifyLinkClicked",
+ {origin: event.target.ownerDocument.URL,
+ dest: event.target.href});
+ });
+ return;
+ }
+ // Form submit button clicked. This can either be directly (e.g. mouseclick,
+ // enter/space while the the submit button has focus) or indirectly (e.g.
+ // pressing enter when a text input has focus).
+ if (event.target.nodeName.toLowerCase() == "input" &&
+ event.target.type.toLowerCase() == "submit" &&
+ event.target.form && event.target.form.action) {
+ overlayComm.run(function() {
+ sendSyncMessage(C.MM_PREFIX + "registerFormSubmitted",
+ {origin: event.target.ownerDocument.URL,
+ dest: event.target.form.action});
+ });
+ return;
+ }
+ };
+
+ framescriptEnv.addStartupFunction(Environment.LEVELS.INTERFACE, function() {
+ framescriptEnv.elManager.addListener(mm, "click", mouseClicked, true);
+ });
+
+
+ // start up the framescript's environment
+ framescriptEnv.startup();
+ }());
++
diff --cc content/ui/menu.js
index 0000000,d31fedd..675e451
mode 000000,100644..100644
--- a/content/ui/menu.js
+++ b/content/ui/menu.js
@@@ -1,0 -1,1215 +1,1216 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+
+ requestpolicy.menu = (function() {
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+
+ let {ScriptLoader} = (function() {
+ let mod = {};
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm", mod);
+ return mod;
+ }());
+ // iMod: Alias for ScriptLoader.importModule
+ let iMod = ScriptLoader.importModule;
+ let {Logger} = iMod("lib/logger");
+ let {rpPrefBranch, Prefs} = iMod("lib/prefs");
+ let {RequestProcessor} = iMod("lib/request-processor");
+ let {PolicyManager} = iMod("lib/policy-manager");
+ let {DomainUtil} = iMod("lib/utils/domains");
+ let {Ruleset} = iMod("lib/ruleset");
+ let {GUIOrigin, GUIDestination,
+ GUILocation, GUILocationProperties} = iMod("lib/gui-location");
+ let {StringUtils} = iMod("lib/utils/strings");
+ let {DOMUtils} = iMod("lib/utils/dom");
+ let {WindowUtils} = iMod("lib/utils/windows");
+ let {C} = iMod("lib/utils/constants");
+
+ let gBrowser = WindowUtils.getTabBrowser(window);
+
+
+ let $id = document.getElementById.bind(document);
+
+
+ let initialized = false;
+
+
+ let self = {
+ addedMenuItems : [],
+
+ _originItem : null,
+ _originDomainnameItem : null,
+ _originNumRequestsItem : null,
+
+ _otherOriginsList : null,
+ _blockedDestinationsList : null,
+ _mixedDestinationsList : null,
+ _allowedDestinationsList : null,
+ _removeRulesList : null,
+ _addRulesList : null,
+
+ _isCurrentlySelectedDestBlocked : null,
+ _isCurrentlySelectedDestAllowed : null,
+
+ _ruleChangeQueues : {}
+ };
+
+ self.init = function() {
+ if (initialized === false) {
+ initialized = true;
+
+ self._originItem = document.getElementById("rp-origin");
+ self._originDomainnameItem = $id('rp-origin-domainname');
+ self._originNumRequestsItem = $id('rp-origin-num-requests');
+
+ self._otherOriginsList = $id("rp-other-origins-list");
+ self._blockedDestinationsList = $id("rp-blocked-destinations-list");
+ self._mixedDestinationsList = $id("rp-mixed-destinations-list");
+ self._allowedDestinationsList = $id("rp-allowed-destinations-list");
+ self._addRulesList = $id("rp-rules-add");
+ self._removeRulesList = $id("rp-rules-remove");
+
+ var conflictCount = RequestProcessor.getConflictingExtensions().length;
+ var hideConflictInfo = (conflictCount == 0);
+ }
+ };
+
+
+ self.prepareMenu = function() {
+ try {
+ var disabled = Prefs.isBlockingDisabled();
+ $id('rp-link-enable-blocking').hidden = !disabled;
+ $id('rp-link-disable-blocking').hidden = disabled;
+
+ $id('rp-revoke-temporary-permissions').hidden =
+ !PolicyManager.temporaryRulesExist();
+
+ self._currentUri = requestpolicy.overlay.getTopLevelDocumentUri();
+
+ try {
+ self._currentBaseDomain = DomainUtil.getBaseDomain(self._currentUri);
+ if (self._currentBaseDomain === null) {
+ Logger.info(Logger.TYPE_INTERNAL, "Unable to prepare menu because " +
+ "the current uri has no host: " + self._currentUri);
+ self._populateMenuForUncontrollableOrigin();
+ return;
+ }
+ } catch (e) {
+ Logger.info(Logger.TYPE_INTERNAL, "Unable to prepare menu because " +
+ "base domain can't be determined: " + self._currentUri);
+ self._populateMenuForUncontrollableOrigin();
+ return;
+ }
+
+ self._currentIdentifier = requestpolicy.overlay
+ .getTopLevelDocumentUriIdentifier();
+
+ //Logger.info(Logger.TYPE_POLICY,
+ // "self._currentUri: " + self._currentUri);
+ self._currentUriObj = DomainUtil.getUriObject(self._currentUri);
+
+ self._isChromeUri = self._currentUriObj.scheme == "chrome";
+ //self._currentUriIsHttps = self._currentUriObj.scheme == "https";
+
+ Logger.info(Logger.TYPE_INTERNAL,
+ "self._currentUri: " + self._currentUri);
+
+ if (self._isChromeUri) {
+ self._populateMenuForUncontrollableOrigin();
+ return;
+ }
+
+ // The fact that getAllRequestsInBrowser uses currentURI.spec directly
+ // from the browser is important because getTopLevelDocumentUri will
+ // not return the real URI if there is an applicable
+ // top-level document translation rule (these are used sometimes
+ // for extension compatibility). For example, this is essential to the
+ // menu showing relevant info when using the Update Scanner extension.
+ self._allRequestsOnDocument = RequestProcessor
+ .getAllRequestsInBrowser(gBrowser.selectedBrowser);
+ self._allRequestsOnDocument.print("_allRequestsOnDocument");
+
+ self._setPrivateBrowsingStyles();
+
+ // var hidePrefetchInfo = !Prefs.isPrefetchEnabled();
+ // self._itemPrefetchWarning.hidden = hidePrefetchInfo;
+ // self._itemPrefetchWarningSeparator.hidden = hidePrefetchInfo;
+ //
+ // if (isChromeUri) {
+ // self._itemUnrestrictedOrigin.setAttribute("label",
+ // StringUtils.$str("unrestrictedOrigin", ["chrome://"]));
+ // self._itemUnrestrictedOrigin.hidden = false;
+ // return;
+ // }
+
+ self._populateOrigin();
+ self._populateOtherOrigins();
+ self._activateOriginItem($id("rp-origin"));
+
+ } catch (e) {
+ Logger.severe(Logger.TYPE_ERROR,
+ "Fatal Error, " + e + ", stack was: " + e.stack);
+ Logger.severe(Logger.TYPE_ERROR, "Unable to prepare menu due to error.");
+ throw e;
+ }
+ };
+
+ self._populateMenuForUncontrollableOrigin = function() {
+ self._originDomainnameItem.setAttribute('value',
+ StringUtils.$str('noOrigin'));
+ self._originNumRequestsItem.setAttribute('value', '');
+ self._originItem.removeAttribute("default-policy");
+ self._originItem.removeAttribute("requests-blocked");
+
+ DOMUtils.removeChildren([
+ self._otherOriginsList,
+ self._blockedDestinationsList,
+ self._mixedDestinationsList,
+ self._allowedDestinationsList,
+ self._removeRulesList,
+ self._addRulesList]);
+ $id('rp-other-origins').hidden = true;
+ $id('rp-blocked-destinations').hidden = true;
+ $id('rp-mixed-destinations').hidden = true;
+ $id('rp-allowed-destinations').hidden = true;
+ // TODO: show some message about why the menu is empty.
+ };
+
+ self._populateList = function(list, values) {
+ DOMUtils.removeChildren(list);
+
+ // check whether there are objects of GUILocation or just strings
+ var guiLocations = values[0] && (values[0] instanceof GUILocation);
+
+ if (true === guiLocations) {
+ // get prefs
+ var sorting = rpPrefBranch.getCharPref('menu.sorting');
+ var showNumRequests = rpPrefBranch.getBoolPref('menu.info.showNumRequests');
+
+ if (sorting == "numRequests") {
+ values.sort(GUILocation.sortByNumRequestsCompareFunction);
+ } else if (sorting == "destName") {
+ values.sort(GUILocation.compareFunction);
+ }
+
+ for (var i in values) {
+ var guiLocation = values[i];
+ var props = guiLocation.properties;
+
+ var num = undefined;
+ if (true === showNumRequests) {
+ num = props.numRequests;
+ if (props.numAllowedRequests > 0 && props.numBlockedRequests > 0) {
+ num += " (" + props.numBlockedRequests +
+ "+" + props.numAllowedRequests + ")";
+ }
+ }
+ var newitem = self._addListItem(list, 'rp-od-item', guiLocation, num);
+
+ newitem.setAttribute("default-policy",
+ (props.numDefaultPolicyRequests > 0 ? "true" : "false"));
+ newitem.setAttribute("requests-blocked",
+ (props.numBlockedRequests > 0 ? "true" : "false"));
+ }
+ } else {
+ values.sort();
+ for (var i in values) {
+ self._addListItem(list, 'rp-od-item', values[i]);
+ }
+ }
+ };
+
+ self._populateOrigin = function() {
+ self._originDomainnameItem.setAttribute("value", self._currentBaseDomain);
+
+ var showNumRequests = rpPrefBranch
+ .getBoolPref('menu.info.showNumRequests');
+
+ var props = self._getOriginGUILocationProperties();
+
+ var numRequests = '';
+ if (true === showNumRequests) {
+ if (props.numAllowedRequests > 0 && props.numBlockedRequests > 0) {
+ numRequests = props.numRequests + " (" +
+ props.numBlockedRequests + "+" + props.numAllowedRequests + ")";
+ } else {
+ numRequests = props.numRequests;
+ }
+ }
+ self._originNumRequestsItem.setAttribute("value", numRequests);
+
+ self._originItem.setAttribute("default-policy",
+ (props.numDefaultPolicyRequests > 0 ? "true" : "false"));
+ self._originItem.setAttribute("requests-blocked",
+ (props.numBlockedRequests > 0 ? "true" : "false"));
+ };
+
+ self._populateOtherOrigins = function() {
+ var guiOrigins = self._getOtherOriginsAsGUILocations();
+ self._populateList(self._otherOriginsList, guiOrigins);
+ $id('rp-other-origins').hidden = guiOrigins.length == 0;
+ };
+
+ self._populateDestinations = function(originIdentifier) {
+ var destsWithBlockedRequests = self._getBlockedDestinationsAsGUILocations();
+ var destsWithAllowedRequests = self._getAllowedDestinationsAsGUILocations();
+
+ var destsWithSolelyBlockedRequests = [];
+ var destsMixed = [];
+ var destsWithSolelyAllowedRequests = [];
+
+ // Set operations would be nice. These are small arrays, so keep it simple.
+ for (var i = 0; i < destsWithBlockedRequests.length; i++) {
+ let blockedGUIDest = destsWithBlockedRequests[i];
+
+ if (false === GUILocation.existsInArray(blockedGUIDest,
+ destsWithAllowedRequests)) {
+ destsWithSolelyBlockedRequests.push(blockedGUIDest);
+ } else {
+ destsMixed.push(blockedGUIDest);
+ }
+ }
+ for (var i = 0; i < destsWithAllowedRequests.length; i++) {
+ let allowedGUIDest = destsWithAllowedRequests[i];
+
+ var indexRawBlocked = GUIDestination.
+ indexOfDestInArray(allowedGUIDest, destsWithBlockedRequests);
+ var destsMixedIndex = GUIDestination.
+ indexOfDestInArray(allowedGUIDest, destsMixed);
+
+ if (indexRawBlocked == -1) {
+ destsWithSolelyAllowedRequests.push(allowedGUIDest);
+ } else {
+ if (destsMixedIndex != -1) {
+ Logger.info(Logger.TYPE_INTERNAL,
+ "Merging dest: <" + allowedGUIDest + ">");
+ destsMixed[destsMixedIndex] = GUIDestination.merge(
+ allowedGUIDest, destsMixed[destsMixedIndex]);
+ } else {
+ // If the allowedGUIDest is in destsWithBlockedRequests and
+ // destsWithAllowedRequests, but not in destsMixed.
+ // This should never happen, the destsMixed destination should have
+ // been added in the destsWithBlockedRequests-loop.
+ Logger.warning(Logger.TYPE_INTERNAL, "mixed dest was" +
+ " not added to `destsMixed` list: <" + dest.dest + ">");
+ destsMixed.push(allowedGUIDest);
+ }
+ }
+ }
+
+ self._populateList(self._blockedDestinationsList,
+ destsWithSolelyBlockedRequests);
+ $id('rp-blocked-destinations').hidden =
+ destsWithSolelyBlockedRequests.length == 0;
+
+ self._populateList(self._mixedDestinationsList, destsMixed);
+ $id('rp-mixed-destinations').hidden = destsMixed.length == 0;
+
+ self._populateList(self._allowedDestinationsList,
+ destsWithSolelyAllowedRequests);
+ $id('rp-allowed-destinations').hidden =
+ destsWithSolelyAllowedRequests.length == 0;
+ };
+
+ self._populateDetails = function() {
+ var origin = self._currentlySelectedOrigin;
+ var dest = self._currentlySelectedDest;
+ DOMUtils.removeChildren([self._removeRulesList, self._addRulesList]);
+
+ var ruleData = {
+ 'o' : {
+ 'h' : self._addWildcard(origin)
+ }
+ };
+
+ let mayPermRulesBeAdded = WindowUtils.mayPermanentRulesBeAdded(window);
+
+ // Note: in PBR we'll need to still use the old string for the temporary
+ // rule. We won't be able to use just "allow temporarily".
+
+ if (!self._currentlySelectedDest) {
+ if (Prefs.isDefaultAllow()) {
+ // It seems pretty rare that someone will want to add a rule to block all
+ // requests from a given origin.
+ //if (mayPermRulesBeAdded === true) {
+ // var item = self._addMenuItemDenyOrigin(
+ // self._addRulesList, ruleData);
+ //}
+ //var item = self._addMenuItemTempDenyOrigin(self._addRulesList, ruleData);
+ } else {
+ if (mayPermRulesBeAdded === true) {
+ var item = self._addMenuItemAllowOrigin(self._addRulesList, ruleData);
+ }
+ var item = self._addMenuItemTempAllowOrigin(self._addRulesList, ruleData);
+ }
+ }
+
+ if (dest) {
+ ruleData['d'] = {
+ 'h' : self._addWildcard(dest)
+ };
+ var destOnlyRuleData = {
+ 'd' : {
+ 'h' : self._addWildcard(dest)
+ }
+ };
+ //if (Prefs.isDefaultAllow()) {
+ if (self._isCurrentlySelectedDestAllowed ||
+ (!PolicyManager.ruleExists(C.RULE_ACTION_DENY, ruleData) &&
+ !PolicyManager.ruleExists(C.RULE_ACTION_DENY, destOnlyRuleData))) {
+ // show "Block requests" if the destination was allowed
+ // OR if there's no blocking rule (i.e. the request was blocked "by default")
+ // -- this enables support for blacklisting.
+ if (!PolicyManager.ruleExists(C.RULE_ACTION_ALLOW, ruleData) &&
+ !PolicyManager.ruleExists(C.RULE_ACTION_DENY, ruleData)) {
+ if (mayPermRulesBeAdded === true) {
+ var item = self._addMenuItemDenyOriginToDest(
+ self._addRulesList, ruleData);
+ }
+ var item = self._addMenuItemTempDenyOriginToDest(
+ self._addRulesList, ruleData);
+ }
+
+ if (!PolicyManager.ruleExists(C.RULE_ACTION_ALLOW, destOnlyRuleData) &&
+ !PolicyManager.ruleExists(C.RULE_ACTION_DENY, destOnlyRuleData)) {
+ if (mayPermRulesBeAdded === true) {
+ var item = self._addMenuItemDenyDest(
+ self._addRulesList, destOnlyRuleData);
+ }
+ var item = self._addMenuItemTempDenyDest(
+ self._addRulesList, destOnlyRuleData);
+ }
+ }
+ if (self._isCurrentlySelectedDestBlocked ||
+ (!PolicyManager.ruleExists(C.RULE_ACTION_ALLOW, ruleData) &&
+ !PolicyManager.ruleExists(C.RULE_ACTION_ALLOW, destOnlyRuleData))) {
+ // show "Allow requests" if the destination was blocked
+ // OR if there's no allow-rule (i.e. the request was allowed "by default")
+ // -- this enables support for whitelisting.
+ if (!PolicyManager.ruleExists(C.RULE_ACTION_ALLOW, ruleData) &&
+ !PolicyManager.ruleExists(C.RULE_ACTION_DENY, ruleData)) {
+ if (mayPermRulesBeAdded === true) {
+ var item = self._addMenuItemAllowOriginToDest(
+ self._addRulesList, ruleData);
+ }
+ var item = self._addMenuItemTempAllowOriginToDest(
+ self._addRulesList, ruleData);
+ }
+
+ if (!PolicyManager.ruleExists(C.RULE_ACTION_ALLOW, destOnlyRuleData) &&
+ !PolicyManager.ruleExists(C.RULE_ACTION_DENY, destOnlyRuleData)) {
+ if (mayPermRulesBeAdded === true) {
+ var item = self._addMenuItemAllowDest(
+ self._addRulesList, destOnlyRuleData);
+ }
+ var item = self._addMenuItemTempAllowDest(
+ self._addRulesList, destOnlyRuleData);
+ }
+ }
+ }
+
+ if (self._currentlySelectedDest) {
+ if (!Prefs.isDefaultAllow() &&
+ !Prefs.isDefaultAllowSameDomain()) {
+ self._populateDetailsAddSubdomainAllowRules(self._addRulesList);
+ }
+ }
+
+ self._populateDetailsRemoveAllowRules(self._removeRulesList);
+ self._populateDetailsRemoveDenyRules(self._removeRulesList);
+ };
+
+ self._addListItem = function(list, cssClass, value, numRequests) {
+ var hbox = document.createElement("hbox");
+ hbox.setAttribute("class", cssClass);
+ hbox.setAttribute("onclick", 'requestpolicy.menu.itemSelected(event);');
+ list.insertBefore(hbox, null);
+
+ var destLabel = document.createElement("label");
+ destLabel.setAttribute("value", value);
+ destLabel.setAttribute("class", "domainname");
+ destLabel.setAttribute("flex", "2");
+ hbox.insertBefore(destLabel, null);
+
+ if (numRequests) {
+ var numReqLabel = document.createElement("label");
+ numReqLabel.setAttribute("value", numRequests);
+ numReqLabel.setAttribute("class", "numRequests");
+ hbox.insertBefore(numReqLabel, null);
+ }
+
+ return hbox;
+ };
+
+ self._disableIfNoChildren = function(el) {
+ // TODO: this isn't working.
+ el.hidden = el.firstChild ? false : true;
+ };
+
+ self._setPrivateBrowsingStyles = function() {
+ let mayPermRulesBeAdded = WindowUtils.mayPermanentRulesBeAdded(window);
+ let val = mayPermRulesBeAdded === true ? '' : 'privatebrowsing';
+ $id('rp-details').setAttribute('class', val);
+ };
+
+ self._resetSelectedOrigin = function() {
+ self._originItem.setAttribute('selected-origin', 'false');
+ for (var i = 0; i < self._otherOriginsList.childNodes.length; i++) {
+ var child = self._otherOriginsList.childNodes[i];
+ child.setAttribute('selected-origin', 'false');
+ }
+ };
+
+ self._resetSelectedDest = function() {
+ for (var i = 0; i < self._blockedDestinationsList.childNodes.length; i++) {
+ var child = self._blockedDestinationsList.childNodes[i];
+ child.setAttribute('selected-dest', 'false');
+ }
+ for (var i = 0; i < self._mixedDestinationsList.childNodes.length; i++) {
+ var child = self._mixedDestinationsList.childNodes[i];
+ child.setAttribute('selected-dest', 'false');
+ }
+ for (var i = 0; i < self._allowedDestinationsList.childNodes.length; i++) {
+ var child = self._allowedDestinationsList.childNodes[i];
+ child.setAttribute('selected-dest', 'false');
+ }
+ };
+
+ self._activateOriginItem = function(item) {
+ if (item.id == 'rp-origin') {
+ // it's _the_ origin
+ self._currentlySelectedOrigin = self._originDomainnameItem.value;
+ } else if (item.parentNode.id == 'rp-other-origins-list') {
+ // it's an otherOrigin
+ self._currentlySelectedOrigin = item.getElementsByClassName("domainname")[0].value;
+ }
+ self._currentlySelectedDest = null;
+ // TODO: if the document's origin (rather than an other origin) is being
+ // activated, then regenerate the other origins list, as well.
+ self._resetSelectedOrigin();
+ item.setAttribute('selected-origin', 'true');
+ self._populateDestinations();
+ self._resetSelectedDest();
+ self._populateDetails();
+ };
+
+ self._activateDestinationItem = function(item) {
+ self._currentlySelectedDest = item.getElementsByClassName("domainname")[0].value;
+
+ if (item.parentNode.id == 'rp-blocked-destinations-list') {
+ self._isCurrentlySelectedDestBlocked = true;
+ self._isCurrentlySelectedDestAllowed = false;
+ } else if (item.parentNode.id == 'rp-allowed-destinations-list') {
+ self._isCurrentlySelectedDestBlocked = false;
+ self._isCurrentlySelectedDestAllowed = true;
+ } else {
+ self._isCurrentlySelectedDestBlocked = true;
+ self._isCurrentlySelectedDestAllowed = true;
+ }
+
+ self._resetSelectedDest();
+ item.setAttribute('selected-dest', 'true');
+ self._populateDetails();
+ };
+
+
+ self.itemSelected = function(event) {
+ var item = event.target;
+ // TODO: rather than compare IDs, this should probably compare against
+ // the elements we already have stored in variables. That is, assuming
+ // equality comparisons work that way here.
+ if (item.nodeName == "label" && item.parentNode.nodeName == "hbox") {
+ // item should be the <hbox>
+ item = item.parentNode;
+ }
+ if (item.id == 'rp-origin' ||
+ item.parentNode.id == 'rp-other-origins-list') {
+ self._activateOriginItem(item);
+ } else if (item.parentNode.id == 'rp-blocked-destinations-list' ||
+ item.parentNode.id == 'rp-mixed-destinations-list' ||
+ item.parentNode.id == 'rp-allowed-destinations-list') {
+ self._activateDestinationItem(item);
+ } else if (item.parentNode.id == 'rp-rule-options' ||
+ item.parentNode.id == 'rp-rules-remove' ||
+ item.parentNode.id == 'rp-rules-add') {
+ self._processRuleSelection(item);
+ } else {
+ Logger.severe(Logger.TYPE_ERROR,
+ 'Unable to figure out which item type was selected.');
+ }
+ };
+
+ self._processRuleSelection = function(item) {
+ var ruleData = item.requestpolicyRuleData;
+ var ruleAction = item.requestpolicyRuleAction;
+
+ if (item.getAttribute('selected-rule') == 'true') {
+ item.setAttribute('selected-rule', 'false');
+ var undo = true;
+ } else {
+ item.setAttribute('selected-rule', 'true');
+ var undo = false;
+ }
+
+ if (!ruleData) {
+ Logger.severe(Logger.TYPE_ERROR,
+ 'ruleData is empty in menu._processRuleSelection()');
+ return;
+ }
+ if (!ruleAction) {
+ Logger.severe(Logger.TYPE_ERROR,
+ 'ruleAction is empty in menu._processRuleSelection()');
+ return;
+ }
+
+ var canonicalRule = Ruleset.rawRuleToCanonicalString(ruleData);
+ Logger.dump("ruleData: " + canonicalRule);
+ Logger.dump("ruleAction: " + ruleAction);
+ Logger.dump("undo: " + undo);
+
+ // TODO: does all of this get replaced with a generic rule processor that
+ // only cares whether it's an allow/deny and temporary and drops the ruleData
+ // argument straight into the ruleset?
+ var origin, dest;
+ if (ruleData['o'] && ruleData['o']['h']) {
+ origin = ruleData['o']['h'];
+ }
+ if (ruleData['d'] && ruleData['d']['h']) {
+ dest = ruleData['d']['h'];
+ }
+
+ if (!self._ruleChangeQueues[ruleAction]) {
+ self._ruleChangeQueues[ruleAction] = {};
+ }
+
+ if (undo) {
+ delete self._ruleChangeQueues[ruleAction][canonicalRule];
+ } else {
+ self._ruleChangeQueues[ruleAction][canonicalRule] = ruleData;
+ }
+ };
+
+ self.processQueuedRuleChanges = function() {
+ var rulesChanged = false;
+ for (var ruleAction in self._ruleChangeQueues) {
+ for (var canonicalRule in self._ruleChangeQueues[ruleAction]) {
+ var ruleData = self._ruleChangeQueues[ruleAction][canonicalRule];
+ self._processRuleChange(ruleAction, ruleData);
+ var rulesChanged = true;
+ }
+ }
+
+ self._ruleChangeQueues = {};
+ return rulesChanged;
+ };
+
+ self._processRuleChange = function(ruleAction, ruleData) {
+
+ switch (ruleAction) {
+ case 'allow':
+ PolicyManager.addAllowRule(ruleData);
+ break;
+ case 'allow-temp':
+ PolicyManager.addTemporaryAllowRule(ruleData);
+ break;
+ case 'stop-allow':
+ PolicyManager.removeAllowRule(ruleData);
+ break;
+ case 'deny':
+ PolicyManager.addDenyRule(ruleData);
+ break;
+ case 'deny-temp':
+ PolicyManager.addTemporaryDenyRule(ruleData);
+ break;
+ case 'stop-deny':
+ PolicyManager.removeDenyRule(ruleData);
+ break;
+ default:
+ throw 'action not implemented: ' + ruleAction;
+ break;
+ }
+ };
+
+
+ // Note by @jsamuel:
+ // „It's been too long since I looked at some of the new code.
+ // I think I may have assumed that I'd get rid of the different strictness
+ // levels and just use what is currently called LEVEL_SOP. If using anything
+ // else there will be errors from within getDeniedRequests().“
+
+
+ self._getBlockedDestinationsAsGUILocations = function() {
+ var reqSet = RequestProcessor.getDeniedRequests(
+ self._currentlySelectedOrigin, self._allRequestsOnDocument);
+ var requests = reqSet.getAllMergedOrigins();
+
+ var result = [];
+ for (var destBase in requests) {
+ var properties = new GUILocationProperties();
+ properties.accumulate(requests[destBase], C.RULE_ACTION_DENY);
+ result.push(new GUIDestination(destBase, properties));
+ }
+ return result;
+ };
+
+ self._getAllowedDestinationsAsGUILocations = function() {
+ var reqSet = RequestProcessor.getAllowedRequests(
+ self._currentlySelectedOrigin, self._allRequestsOnDocument);
+ var requests = reqSet.getAllMergedOrigins();
+
+ var result = [];
+ for (var destBase in requests) {
+ // For everybody except users with default deny who are not allowing all
+ // requests to the same domain:
+ // Ignore the selected origin's domain when listing destinations.
+ if (Prefs.isDefaultAllow() || Prefs.isDefaultAllowSameDomain()) {
+ if (destBase == self._currentlySelectedOrigin) {
+ continue;
+ }
+ }
+
+ var properties = new GUILocationProperties();
+ properties.accumulate(requests[destBase], C.RULE_ACTION_ALLOW);
+ result.push(new GUIDestination(destBase, properties));
+ }
+ return result;
+ };
+
+ /**
+ * TODO: optimize this for performance (_getOriginGUILocationProperties and
+ * _getOtherOriginsAsGUILocations could be merged.)
+ *
+ * @return {GUILocationProperties}
+ * the properties of the "main" origin (the one in the location bar).
+ */
+ self._getOriginGUILocationProperties = function() {
+ var allRequests = self._allRequestsOnDocument.getAll();
+
+ var allowSameDomain = Prefs.isDefaultAllow() ||
+ Prefs.isDefaultAllowSameDomain();
+
+ var properties = new GUILocationProperties();
+
+ for (var originUri in allRequests) {
+ var originBase = DomainUtil.getBaseDomain(originUri);
+ if (originBase !== self._currentBaseDomain) {
+ continue;
+ }
+
+ for (var destBase in allRequests[originUri]) {
+ properties.accumulate(allRequests[originUri][destBase]);
+ }
+ }
+ return properties;
+ };
+
+ self._getOtherOriginsAsGUILocations = function() {
+ var allRequests = self._allRequestsOnDocument.getAll();
+
+ var allowSameDomain = Prefs.isDefaultAllow() ||
+ Prefs.isDefaultAllowSameDomain();
+
+ var guiOrigins = [];
+ for (var originUri in allRequests) {
+ var originBase = DomainUtil.getBaseDomain(originUri);
+ if (originBase === self._currentBaseDomain) {
+ continue;
+ }
+
+ // TODO: we should prevent chrome://browser/ URLs from getting anywhere
+ // near here in the first place.
+ // Is this an issue anymore? This may have been slipping through due to
+ // a bug that has since been fixed. Disabling for now.
+ //if (originBase == 'browser') {
+ // continue;
+ //}
+
+ var guiOriginsIndex = GUIOrigin.indexOfOriginInArray(originBase,
+ guiOrigins);
+ var properties;
+ if (guiOriginsIndex == -1) {
+ properties = new GUILocationProperties();
+ } else {
+ properties = guiOrigins[guiOriginsIndex].properties;
+ }
+ var addThisOriginBase = false;
+
+ for (var destBase in allRequests[originUri]) {
+ // Search for a destBase which wouldn't be allowed by the default policy.
+ // TODO: some users might want to know those "other origins" as well.
+ // this should be made possible.
+
+ // For everybody except users with default deny who are not allowing all
+ // guiOrigins to the same domain:
+ // Only list other origins where there is a destination from that origin
+ // that is at a different domain, not just a different subdomain.
+ if (allowSameDomain && destBase == originBase) {
+ continue;
+ }
+ addThisOriginBase = true;
+ properties.accumulate(allRequests[originUri][destBase]);
+ }
+
+ if (addThisOriginBase && guiOriginsIndex == -1) {
+ guiOrigins.push(new GUIOrigin(originBase, properties));
+ }
+ }
+ return guiOrigins;
+ };
+
+ self._sanitizeJsFunctionArg = function(str) {
+ // strip single quotes and backslashes
+ return str.replace(/['\\]/g, "");
+ };
+
+ self._isIPAddressOrSingleName = function(hostname) {
+ return DomainUtil.isIPAddress(hostname) ||
+ hostname.indexOf(".") == -1;
+ };
+
+ self._addWildcard = function(hostname) {
+ if (self._isIPAddressOrSingleName(hostname)) {
+ return hostname;
+ } else {
+ return "*." + hostname;
+ }
+ };
+
+ // TODO: the 12 _addMenuItem* functions below hopefully can be refactored.
+
+ // Stop allowing
+
+ self._addMenuItemStopAllowingOrigin = function(list, ruleData, subscriptionOverride) {
+ var originHost = ruleData["o"]["h"];
+ var ruleAction = subscriptionOverride ? 'deny' : 'stop-allow';
+ return self._addMenuItemHelper(list, ruleData, 'stopAllowingOrigin', [originHost], ruleAction, 'rp-stop-rule rp-stop-allow');
+ };
+
+ self._addMenuItemStopAllowingDest = function(list, ruleData, subscriptionOverride) {
+ var destHost = ruleData["d"]["h"];
+ var ruleAction = subscriptionOverride ? 'deny' : 'stop-allow';
+ return self._addMenuItemHelper(list, ruleData, 'stopAllowingDestination', [destHost], ruleAction, 'rp-stop-rule rp-stop-allow');
+ };
+
+ self._addMenuItemStopAllowingOriginToDest = function(list, ruleData, subscriptionOverride) {
+ var originHost = ruleData["o"]["h"];
+ var destHost = ruleData["d"]["h"];
+ var ruleAction = subscriptionOverride ? 'deny' : 'stop-allow';
+ return self._addMenuItemHelper(list, ruleData, 'stopAllowingOriginToDestination', [originHost, destHost], ruleAction, 'rp-stop-rule rp-stop-allow');
+ };
+
+ // Allow
+
+ self._addMenuItemAllowOrigin = function(list, ruleData) {
+ var originHost = ruleData["o"]["h"];
+ return self._addMenuItemHelper(list, ruleData, 'allowOrigin', [originHost], 'allow', 'rp-start-rule rp-allow');
+ };
+
+ self._addMenuItemAllowDest = function(list, ruleData) {
+ var destHost = ruleData["d"]["h"];
+ return self._addMenuItemHelper(list, ruleData, 'allowDestination', [destHost], 'allow', 'rp-start-rule rp-allow');
+ };
+
+ self._addMenuItemAllowOriginToDest = function(list, ruleData) {
+ var originHost = ruleData["o"]["h"];
+ var destHost = ruleData["d"]["h"];
+ return self._addMenuItemHelper(list, ruleData, 'allowOriginToDestination', [originHost, destHost], 'allow', 'rp-start-rule rp-allow');
+ };
+
+ // Allow temp
+
+ self._addMenuItemTempAllowOrigin = function(list, ruleData) {
+ var originHost = ruleData["o"]["h"];
+ return self._addMenuItemHelper(list, ruleData, 'allowOriginTemporarily', [originHost], 'allow-temp', 'rp-start-rule rp-allow rp-temporary');
+ };
+
+ self._addMenuItemTempAllowDest = function(list, ruleData) {
+ var destHost = ruleData["d"]["h"];
+ return self._addMenuItemHelper(list, ruleData, 'allowDestinationTemporarily', [destHost], 'allow-temp', 'rp-start-rule rp-allow rp-temporary');
+ };
+
+ self._addMenuItemTempAllowOriginToDest = function(list, ruleData) {
+ var originHost = ruleData["o"]["h"];
+ var destHost = ruleData["d"]["h"];
+ return self._addMenuItemHelper(list, ruleData, 'allowOriginToDestinationTemporarily', [originHost, destHost], 'allow-temp', 'rp-start-rule rp-allow rp-temporary');
+ };
+
+ // Stop denying
+
+ self._addMenuItemStopDenyingOrigin = function(list, ruleData, subscriptionOverride) {
+ var originHost = ruleData["o"]["h"];
+ var ruleAction = subscriptionOverride ? 'allow' : 'stop-deny';
+ return self._addMenuItemHelper(list, ruleData, 'stopDenyingOrigin', [originHost], ruleAction, 'rp-stop-rule rp-stop-deny');
+ };
+
+ self._addMenuItemStopDenyingDest = function(list, ruleData, subscriptionOverride) {
+ var destHost = ruleData["d"]["h"];
+ var ruleAction = subscriptionOverride ? 'allow' : 'stop-deny';
+ return self._addMenuItemHelper(list, ruleData, 'stopDenyingDestination', [destHost], ruleAction, 'rp-stop-rule rp-stop-deny');
+ };
+
+ self._addMenuItemStopDenyingOriginToDest = function(list, ruleData, subscriptionOverride) {
+ var originHost = ruleData["o"]["h"];
+ var destHost = ruleData["d"]["h"];
+ var ruleAction = subscriptionOverride ? 'allow' : 'stop-deny';
+ return self._addMenuItemHelper(list, ruleData, 'stopDenyingOriginToDestination', [originHost, destHost], ruleAction, 'rp-stop-rule rp-stop-deny');
+ };
+
+ // Deny
+
+ self._addMenuItemDenyOrigin = function(list, ruleData) {
+ var originHost = ruleData["o"]["h"];
+ return self._addMenuItemHelper(list, ruleData, 'denyOrigin', [originHost], 'deny', 'rp-start-rule rp-deny');
+ };
+
+ self._addMenuItemDenyDest = function(list, ruleData) {
+ var destHost = ruleData["d"]["h"];
+ return self._addMenuItemHelper(list, ruleData, 'denyDestination', [destHost], 'deny', 'rp-start-rule rp-deny');
+ };
+
+ self._addMenuItemDenyOriginToDest = function(list, ruleData) {
+ var originHost = ruleData["o"]["h"];
+ var destHost = ruleData["d"]["h"];
+ return self._addMenuItemHelper(list, ruleData, 'denyOriginToDestination', [originHost, destHost], 'deny', 'rp-start-rule rp-deny');
+ };
+
+ // Deny temp
+
+ self._addMenuItemTempDenyOrigin = function(list, ruleData) {
+ var originHost = ruleData["o"]["h"];
+ return self._addMenuItemHelper(list, ruleData, 'denyOriginTemporarily', [originHost], 'deny-temp', 'rp-start-rule rp-deny rp-temporary');
+ };
+
+ self._addMenuItemTempDenyDest = function(list, ruleData) {
+ var destHost = ruleData["d"]["h"];
+ return self._addMenuItemHelper(list, ruleData, 'denyDestinationTemporarily', [destHost], 'deny-temp', 'rp-start-rule rp-deny rp-temporary');
+ };
+
+ self._addMenuItemTempDenyOriginToDest = function(list, ruleData) {
+ var originHost = ruleData["o"]["h"];
+ var destHost = ruleData["d"]["h"];
+ return self._addMenuItemHelper(list, ruleData,
+ 'denyOriginToDestinationTemporarily', [originHost, destHost],
+ 'deny-temp', 'rp-start-rule rp-deny rp-temporary');
+ };
+
+ self._addMenuItemHelper = function(list, ruleData, fmtStrName, fmtStrArgs,
+ ruleAction, cssClass) {
+ var label = StringUtils.$str(fmtStrName, fmtStrArgs);
+ var item = self._addListItem(list, 'rp-od-item', label);
+ item.requestpolicyRuleData = ruleData;
+ item.requestpolicyRuleAction = ruleAction;
+ //var statustext = ''; // TODO
+ item.setAttribute('class', 'rp-od-item ' + cssClass);
+ var canonicalRule = Ruleset.rawRuleToCanonicalString(ruleData);
+ if (self._ruleChangeQueues[ruleAction]) {
+ if (self._ruleChangeQueues[ruleAction][canonicalRule]) {
+ item.setAttribute('selected-rule', 'true');
+ }
+ }
+ return item;
+ };
+
+ self._ruleDataPartToDisplayString = function(ruleDataPart) {
+ var str = "";
+ if (ruleDataPart["s"]) {
+ str += ruleDataPart["s"] + "://";
+ }
+ str += ruleDataPart["h"] || "*";
+ if (ruleDataPart["port"]) {
+ str += ":" + ruleDataPart["port"];
+ }
+ // TODO: path
+ return str;
+ };
+
+ self._ruleDataToFormatVariables = function(rawRule) {
+ var fmtVars = [];
+ if (rawRule["o"]) {
+ fmtVars.push(self._ruleDataPartToDisplayString(rawRule["o"]));
+ }
+ if (rawRule["d"]) {
+ fmtVars.push(self._ruleDataPartToDisplayString(rawRule["d"]));
+ }
+ return fmtVars;
+ };
+
+ self._addMenuItemRemoveAllowRule = function(list, rawRule, subscriptionOverride) {
+ var fmtVars = self._ruleDataToFormatVariables(rawRule);
+
+ if (rawRule["o"] && rawRule["d"]) {
+ return self._addMenuItemStopAllowingOriginToDest(list, rawRule, subscriptionOverride);
+ } else if (rawRule["o"]) {
+ return self._addMenuItemStopAllowingOrigin(list, rawRule, subscriptionOverride);
+ } else if (rawRule["d"]) {
+ return self._addMenuItemStopAllowingDest(list, rawRule, subscriptionOverride);
+ } else {
+ throw "Invalid rule data: no origin or destination parts.";
+ }
+ };
+
+ self._addMenuItemRemoveDenyRule = function(list, rawRule, subscriptionOverride) {
+ var fmtVars = self._ruleDataToFormatVariables(rawRule);
+
+ if (rawRule["o"] && rawRule["d"]) {
+ return self._addMenuItemStopDenyingOriginToDest(list, rawRule, subscriptionOverride);
+ } else if (rawRule["o"]) {
+ return self._addMenuItemStopDenyingOrigin(list, rawRule, subscriptionOverride);
+ } else if (rawRule["d"]) {
+ return self._addMenuItemStopDenyingDest(list, rawRule, subscriptionOverride);
+ } else {
+ throw "Invalid rule data: no origin or destination parts.";
+ }
+ };
+
+ self._populateDetailsRemoveAllowRules = function(list) {
+ // TODO: can we avoid calling getAllowedRequests here and reuse a result
+ // from calling it earlier?
+
+ var reqSet = RequestProcessor.getAllowedRequests(
+ self._currentlySelectedOrigin, self._allRequestsOnDocument);
+ var requests = reqSet.getAllMergedOrigins();
+
+ //var rules = {};
+
+ var userRules = {};
+ var subscriptionRules = {};
+
+ //reqSet.print('allowedRequests');
+
+ // TODO: there is no dest if no dest is selected (origin only).
+ //var destBase = DomainUtil.getBaseDomain(
+ // self._currentlySelectedDest);
+
+ for (var destBase in requests) {
+
+ if (self._currentlySelectedDest &&
+ self._currentlySelectedDest != destBase) {
+ continue;
+ }
+
+ for (var destIdent in requests[destBase]) {
+
+ var destinations = requests[destBase][destIdent];
+ for (var destUri in destinations) {
+
+ // This will be null when the request was denied because of a default
+ // allow rule. However about any other time?
+ // TODO: we at least in default allow mode, we need to give an option
+ // to add a deny rule for these requests.
+ if (!destinations[destUri]) {
+ Logger.dump("destinations[destUri] is null or undefined for " +
+ "destUri: " + destUri);
+ continue;
+ }
+
+
+ var results = destinations[destUri][0]; // TODO: Do not look only
+ // at the first RequestResult object, but at all. (there might be
+ // several requests with identical origin and destination URI.)
+
+ for (var i in results.matchedAllowRules) {
+
+ var ruleset, match;
+ [ruleset, match] = results.matchedAllowRules[i];
+ var rawRule = Ruleset.matchToRawRule(match);
+
+ if (!self._currentlySelectedDest) {
+ if (rawRule['d'] && rawRule['d']['h']) {
+ continue;
+ }
+ }
+
+ var rawRuleStr = Ruleset.rawRuleToCanonicalString(rawRule);
+ //Logger.info(Logger.TYPE_POLICY,
+ // "matched allow rule: " + rawRuleStr);
+ // This is how we remove duplicates: if two rules have the same
+ // canonical string, they'll have in the same key.
+ if (ruleset.userRuleset) {
+ userRules[rawRuleStr] = rawRule;
+ } else {
+ subscriptionRules[rawRuleStr] = rawRule;
+ }
+ }
+ }
+ }
+ }
+
+ for (var i in userRules) {
+ self._addMenuItemRemoveAllowRule(list, userRules[i], false);
+ }
+ // TODO: for subscription rules, we need the effect of the menu item to be
+ // adding a deny rule instead of removing an allow rule. However, the text
+ // used for the item needs to be the same as removing an allow rule.
+ for (var i in subscriptionRules) {
+ self._addMenuItemRemoveAllowRule(list, subscriptionRules[i], true);
+ }
+ };
+
+ self._populateDetailsRemoveDenyRules = function(list) {
+ // TODO: can we avoid calling getDeniedRequests here and reuse a result
+ // from calling it earlier?
+
+ var reqSet = RequestProcessor.getDeniedRequests(
+ self._currentlySelectedOrigin, self._allRequestsOnDocument);
+ var requests = reqSet.getAllMergedOrigins();
+
+ //var rules = {};
+
+ var userRules = {};
+ var subscriptionRules = {};
+
+ reqSet.print('deniedRequests');
+
+ // TODO: there is no dest if no dest is selected (origin only).
+ //var destBase = DomainUtil.getBaseDomain(
+ // self._currentlySelectedDest);
+
+ for (var destBase in requests) {
+
+ if (self._currentlySelectedDest &&
+ self._currentlySelectedDest != destBase) {
+ continue;
+ }
+
+ for (var destIdent in requests[destBase]) {
+
+ var destinations = requests[destBase][destIdent];
+ for (var destUri in destinations) {
+
+ // This will be null when the request was denied because of a default
+ // deny rule. However about any other time?
+ // TODO: we at least in default deny mode, we need to give an option
+ // to add a allow rule for these requests.
+ if (!destinations[destUri]) {
+ Logger.dump("destinations[destUri] is null or undefined for destUri: " + destUri);
+ continue;
+ }
+
+ var results = destinations[destUri][0]; // TODO: Do not look only
+ // at the first RequestResult object, but at all. (there may be
+ // several requests with identical origin and destination URI.)
+
+ for (var i in results.matchedDenyRules) {
+
+ var ruleset, match;
+ [ruleset, match] = results.matchedDenyRules[i];
+ var rawRule = Ruleset.matchToRawRule(match);
+
+ if (!self._currentlySelectedDest) {
+ if (rawRule['d'] && rawRule['d']['h']) {
+ continue;
+ }
+ }
+
+ var rawRuleStr = Ruleset.rawRuleToCanonicalString(rawRule);
+ //Logger.info(Logger.TYPE_POLICY,
+ // "matched allow rule: " + rawRuleStr);
+ // This is how we remove duplicates: if two rules have the same
+ // canonical string, they'll have in the same key.
+ if (ruleset.userRuleset) {
+ userRules[rawRuleStr] = rawRule;
+ } else {
+ subscriptionRules[rawRuleStr] = rawRule;
+ }
+ }
+ }
+ }
+ }
+
+ for (var i in userRules) {
+ self._addMenuItemRemoveDenyRule(list, userRules[i], false);
+ }
+ // TODO: for subscription rules, we need the effect of the menu item to be
+ // adding an allow rule instead of removing a deny rule. However, the text
+ // used for the item needs to be the same as removing a deny rule.
+ for (var i in subscriptionRules) {
+ self._addMenuItemRemoveDenyRule(list, subscriptionRules[i], true);
+ }
+ };
+
+ self._populateDetailsAddSubdomainAllowRules = function(list) {
+ var origin = self._currentlySelectedOrigin;
+
+ // TODO: can we avoid calling getDeniedRequests here and reuse a result
+ // from calling it earlier?
+
+ var reqSet = RequestProcessor.getDeniedRequests(
+ self._currentlySelectedOrigin, self._allRequestsOnDocument);
+ var requests = reqSet.getAllMergedOrigins();
+
+ var destHosts = {};
+
+ for (var destBase in requests) {
+ if (self._currentlySelectedDest &&
+ self._currentlySelectedDest != destBase) {
+ continue;
+ }
+ for (var destIdent in requests[destBase]) {
+ var destinations = requests[destBase][destIdent];
+ for (var destUri in destinations) {
+ destHosts[DomainUtil.getHost(destUri)] = null;
+ }
+ }
+ }
+
+ let mayPermRulesBeAdded = WindowUtils.mayPermanentRulesBeAdded(window);
+
+ for (var destHost in destHosts) {
+ var ruleData = {
+ 'o' : {
+ 'h' : self._addWildcard(origin)
+ },
+ 'd' : {
+ 'h': destHost
+ }
+ };
+ if (!PolicyManager.ruleExists(C.RULE_ACTION_ALLOW, ruleData) &&
+ !PolicyManager.ruleExists(C.RULE_ACTION_DENY, ruleData)) {
+ if (mayPermRulesBeAdded === true) {
+ var item = self._addMenuItemAllowOriginToDest(list, ruleData);
+ }
+ var item = self._addMenuItemTempAllowOriginToDest(list, ruleData);
+ }
+
+ var destOnlyRuleData = {
+ 'd' : {
+ 'h': destHost
+ }
+ };
+ if (!PolicyManager.ruleExists(C.RULE_ACTION_ALLOW, destOnlyRuleData) &&
+ !PolicyManager.ruleExists(C.RULE_ACTION_DENY, destOnlyRuleData)) {
+ if (mayPermRulesBeAdded === true) {
+ var item = self._addMenuItemAllowDest(list, destOnlyRuleData);
+ }
+ var item = self._addMenuItemTempAllowDest(list, destOnlyRuleData);
+ }
+ }
+ };
+
+ return self;
+ }());
++
diff --cc content/ui/overlay.js
index 0000000,da44344..d407c86
mode 000000,100644..100644
--- a/content/ui/overlay.js
+++ b/content/ui/overlay.js
@@@ -1,0 -1,1224 +1,1225 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+
+ /**
+ * Provides functionality for the overlay. An instance of this class exists for
+ * each tab/window.
+ */
+ requestpolicy.overlay = (function() {
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ let {ScriptLoader, XPCOMUtils} = (function() {
+ let mod = {};
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm", mod);
+ Cu.import("resource://gre/modules/XPCOMUtils.jsm", mod);
+ return mod;
+ }());
+
+ // iMod: Alias for ScriptLoader.importModule
+ let iMod = ScriptLoader.importModule;
+ let {Environment, ProcessEnvironment} = iMod("lib/environment");
+ let {ManagerForMessageListeners} = iMod("lib/manager-for-message-listeners");
+ let {Logger} = iMod("lib/logger");
+ let {rpPrefBranch, Prefs} = iMod("lib/prefs");
+ let {RequestProcessor} = iMod("lib/request-processor");
+ let {PolicyManager} = iMod("lib/policy-manager");
+ let {DomainUtil} = iMod("lib/utils/domains");
+ let {StringUtils} = iMod("lib/utils/strings");
+ let {DOMUtils} = iMod("lib/utils/dom");
+ let {WindowUtils} = iMod("lib/utils/windows");
+ let {C} = iMod("lib/utils/constants");
+
+ let gBrowser = WindowUtils.getTabBrowser(window);
+
+ let $id = document.getElementById.bind(document);
+
+ //let _extensionConflictInfoUri = "http://www.requestpolicy.com/conflict?ext=";
+
+ //let _prefetchInfoUri = "http://www.requestpolicy.com/help/prefetch.html";
+ //let _prefetchDisablingInstructionsUri = "http://www.requestpolicy.com/help/prefetch.html#disable";
+
+
+ // create an environment for this overlay.
+ let OverlayEnvironment = new Environment(ProcessEnvironment, "OverlayEnv");
+ // manage this overlay's message listeners:
+ let mlManager = new ManagerForMessageListeners(OverlayEnvironment,
+ window.messageManager);
+
+
+ let initialized = false;
+
+ let toolbarButtonId = "requestpolicyToolbarButton";
+
+ let overlayId = 0;
+
+ let blockedContentStateUpdateDelay = 250; // milliseconds
+ let blockedContentCheckTimeoutId = null;
+ let blockedContentCheckMinWaitOnObservedBlockedRequest = 500;
+ let blockedContentCheckLastTime = 0;
+
+ let popupElement = null;
+
+ //let statusbar = null;
+
+ // TODO: get back entry in context menu
+ // https://github.com/RequestPolicyContinued/requestpolicy/issues/353
+ //let rpContextMenu = null;
+
+ let toolbox = null;
+
+ let isFennec = false;
+
+
+
+ let self = {
+ // This is set by request-log.js when it is initialized. We don't need to worry
+ // about setting it here.
+ requestLog: null
+ };
+
+
+ self.toString = function() {
+ return "[requestpolicy.overlay " + overlayId + "]";
+ };
+
+ /**
+ * Initialize the object. This must be done after the DOM is loaded.
+ */
+ self.init = function() {
+ try {
+ if (initialized === false) {
+ initialized = true;
+ overlayId = (new Date()).getTime();
+
+ requestpolicy.menu.init();
+
+ popupElement = $id("rp-popup");
+
+ //statusbar = $id("status-bar");
+ //rpContextMenu = $id("requestpolicyContextMenu");
+ toolbox = $id("navigator-toolbox");
+
+ var appInfo = Components.classes["@mozilla.org/xre/app-info;1"]
+ .getService(Components.interfaces.nsIXULAppInfo);
+ isFennec = (appInfo.ID === "{a23983c0-fd0e-11dc-95ff-0800200c9a66}");
+
+ if (isFennec) {
+ Logger.dump("Detected Fennec.");
+ // Set an attribute for CSS usage.
+ popupElement.setAttribute("fennec", "true");
+ popupElement.setAttribute("position", "after_end");
+ }
+
+ // Register this window with the requestpolicy service so that we can be
+ // notified of blocked requests. When blocked requests happen, this
+ // object's observerBlockedRequests() method will be called.
+ RequestProcessor.addRequestObserver(self);
+
+ //self.setContextMenuEnabled(rpPrefBranch.getBoolPref("contextMenu"));
+
+ OverlayEnvironment.shutdownOnUnload(window);
+ OverlayEnvironment.startup();
+
+ // Tell the framescripts that the overlay is ready. The
+ // listener must be added immediately.
+ mlManager.addListener("isOverlayReady", function() {
+ return true;
+ });
+ window.messageManager.broadcastAsyncMessage(C.MM_PREFIX +
+ "overlayIsReady", true);
+ }
+ } catch (e) {
+ Logger.severe(Logger.TYPE_ERROR,
+ "Fatal Error, " + e + ", stack was: " + e.stack);
+ Logger.severe(Logger.TYPE_ERROR,
+ "Unable to initialize requestpolicy.overlay.");
+ throw e;
+ }
+ };
+
+ //setContextMenuEnabled : function(isEnabled) {
+ // rpContextMenu.setAttribute("hidden", !isEnabled);
+ //},
+
+ OverlayEnvironment.addShutdownFunction(
+ Environment.LEVELS.INTERFACE,
+ function() {
+ RequestProcessor.removeRequestObserver(self);
+ self._unwrapAddTab();
+ self._removeHistoryObserver();
+ self._removeLocationObserver();
+ });
+
+ OverlayEnvironment.addShutdownFunction(
+ Environment.LEVELS.UI,
+ function() {
+ let requestLog = $id("requestpolicy-requestLog");
+
+ // If the request log is found and is opened.
+ // The XUL elements of the request log might have already
+ // been removed.
+ if (!!requestLog && requestLog.hidden === false) {
+ self.toggleRequestLog();
+ }
+ });
+
+ function addAppcontentTabSelectListener() {
+ // Info on detecting page load at:
+ // http://developer.mozilla.org/En/Code_snippets/On_page_load
+ var appcontent = $id("appcontent"); // browser
+ if (appcontent) {
+ if (isFennec) {
+ OverlayEnvironment.elManager.addListener(appcontent, "TabSelect",
+ self.tabChanged, false);
+ }
+ }
+ }
+ OverlayEnvironment.addStartupFunction(Environment.LEVELS.INTERFACE,
+ addAppcontentTabSelectListener);
+
+ /**
+ * Add an event listener for when the contentAreaContextMenu (generally
+ * the right-click menu within the document) is shown.
+ */
+ function addContextMenuListener() {
+ var contextMenu = $id("contentAreaContextMenu");
+ if (contextMenu) {
+ OverlayEnvironment.elManager.addListener(contextMenu, "popupshowing",
+ self._contextMenuOnPopupShowing,
+ false);
+ }
+ }
+ OverlayEnvironment.addStartupFunction(Environment.LEVELS.INTERFACE,
+ addContextMenuListener);
+
+ function addTabContainerTabSelectListener() {
+ // We consider the default place for the popup to be attached to the
+ // context menu, so attach it there.
+ //self._attachPopupToContextMenu();
+
+ // Listen for the user changing tab so we can update any notification or
+ // indication of blocked requests.
+ if (!isFennec) {
+ var container = gBrowser.tabContainer;
+
+ let tabSelectCallback = function(event) {
+ self.tabChanged();
+ };
+
+ OverlayEnvironment.elManager.addListener(container, "TabSelect",
+ tabSelectCallback, false);
+
+ self._wrapAddTab();
+ self._addLocationObserver();
+ self._addHistoryObserver();
+ }
+ }
+ OverlayEnvironment.addStartupFunction(Environment.LEVELS.INTERFACE,
+ addTabContainerTabSelectListener);
+
+
+
+
+ mlManager.addListener("notifyDocumentLoaded", function(message) {
+ let {documentURI} = message.data;
+
+ // the <browser> element of the corresponding tab.
+ let browser = message.target;
+
+ let blockedURIs = {};
+
+ if (rpPrefBranch.getBoolPref("indicateBlockedObjects")) {
+ var indicateBlacklisted = rpPrefBranch
+ .getBoolPref("indicateBlacklistedObjects");
+
+ var rejectedRequests = RequestProcessor._rejectedRequests
+ .getOriginUri(documentURI);
+ for (var destBase in rejectedRequests) {
+ for (var destIdent in rejectedRequests[destBase]) {
+ for (var destUri in rejectedRequests[destBase][destIdent]) {
+ // case 1: indicateBlacklisted == true
+ // ==> indicate the object has been blocked
+ //
+ // case 2: indicateBlacklisted == false
+ // case 2a: all requests have been blocked because of a blacklist
+ // ==> do *not* indicate
+ //
+ // case 2b: at least one of the blocked (identical) requests has been
+ // blocked by a rule *other than* the blacklist
+ // ==> *do* indicate
+ let requests = rejectedRequests[destBase][destIdent][destUri];
+ if (indicateBlacklisted ||
+ self._containsNonBlacklistedRequests(requests)) {
+ blockedURIs[destUri] = blockedURIs[destUri] ||
+ {identifier: DomainUtil.getIdentifier(destUri)};
+ }
+ }
+ }
+ }
+ }
+
+ if ("requestpolicy" in browser &&
+ documentURI in browser.requestpolicy.blockedRedirects) {
+ // bad smell: do not save blocked requests in the <browser> obj
+ var dest = browser.requestpolicy.blockedRedirects[documentURI];
+ Logger.warning(Logger.TYPE_HEADER_REDIRECT,
+ "Showing notification for blocked redirect. To <" + dest +
+ "> " + "from <" + documentURI + ">");
+ self._showRedirectNotification(browser, dest);
+
+ delete browser.requestpolicy.blockedRedirects[documentURI];
+ }
+
+ // send the list of blocked URIs back to the frame script
+ return {blockedURIs: blockedURIs};
+ });
+
+
+
+ mlManager.addListener("notifyTopLevelDocumentLoaded", function (message) {
+ // Clear any notifications that may have been present.
+ self._setContentBlockedState(false);
+ // We don't do this immediately anymore because slow systems might have
+ // this slow down the loading of the page, which is noticable
+ // especially with CSS loading delays (it's not unlikely that slow
+ // webservers have a hand in this, too).
+ // Note that the change to _updateBlockedContentStateAfterTimeout seems to have
+ // added a bug where opening a blank tab and then quickly switching back
+ // to the original tab can cause the original tab's blocked content
+ // notification to be cleared. A simple compensation was to decrease
+ // the timeout from 1000ms to 250ms, making it much less likely the tab
+ // switch can be done in time for a blank opened tab. This isn't a real
+ // solution, though.
+ self._updateBlockedContentStateAfterTimeout();
+ });
+
+
+
+ mlManager.addListener("notifyDOMFrameContentLoaded", function (message) {
+ // This has an advantage over just relying on the
+ // observeBlockedRequest() call in that this will clear a blocked
+ // content notification if there no longer blocked content. Another way
+ // to solve this would be to observe allowed requests as well as blocked
+ // requests.
+ blockedContentCheckLastTime = (new Date()).getTime();
+ self._stopBlockedContentCheckTimeout();
+ self._updateBlockedContentState(message.target);
+ });
+
+
+
+ mlManager.addListener("handleMetaRefreshes", function(message) {
+ self.handleMetaRefreshes(message);
+ });
+
+
+
+ mlManager.addListener("notifyLinkClicked", function (message) {
+ RequestProcessor.registerLinkClicked(message.data.origin,
+ message.data.dest);
+ });
+
+
+
+ mlManager.addListener("notifyFormSubmitted", function (message) {
+ RequestProcessor.registerFormSubmitted(message.data.origin,
+ message.data.dest);
+ });
+
+
+
+ self.handleMetaRefreshes = function(message) {
+ Logger.dump("Handling meta refreshes...");
+
+ let {documentURI, metaRefreshes} = message.data;
+ let browser = message.target;
+
+ for (let i = 0, len = metaRefreshes.length; i < len; ++i) {
+ let {delay, destURI, originalDestURI} = metaRefreshes[i];
+
+ Logger.info(Logger.TYPE_META_REFRESH, "meta refresh to <" +
+ destURI + "> (" + delay + " second delay) found in document at <" +
+ documentURI + ">");
+
+ if (originalDestURI) {
+ Logger.info(Logger.TYPE_META_REFRESH,
+ "meta refresh destination <" + originalDestURI + "> " +
+ "appeared to be relative to <" + documentURI + ">, so " +
+ "it has been resolved to <" + destURI + ">");
+ }
+
+ // We don't automatically perform any allowed redirects. Instead, we
+ // just detect when they will be blocked and show a notification. If
+ // the docShell has allowMetaRedirects disabled, it will be respected.
+ if (!Prefs.isBlockingDisabled() &&
+ !RequestProcessor.isAllowedRedirect(documentURI, destURI)) {
+ // Ignore redirects to javascript. The browser will ignore them, as well.
+ if (DomainUtil.getUriObject(destURI).schemeIs("javascript")) {
+ Logger.warning(Logger.TYPE_META_REFRESH,
+ "Ignoring redirect to javascript URI <" + destURI + ">");
+ continue;
+ }
+ // The request will be blocked by shouldLoad.
+ self._showRedirectNotification(browser, destURI, delay);
+ }
+ }
+ };
+
+
+ /**
+ * Takes an URI, crops it if necessary, and returns it.
+ * It's ensured that the returned URI isn't longer than a specified length,
+ * but the prePath is never cropped, so that the resulting string might be
+ * longer than aMaxLength.
+ *
+ * (There doesn't seem to be a way to use the xul crop attribute with the
+ * notification.)
+ *
+ * @param {String} aUri
+ * @param {Int} aMaxLength
+ *
+ * @returns {String} the URI, eventually cropped
+ *
+ */
+ function cropUri(aUri, aMaxLength) {
+ if (aUri.length < aMaxLength) {
+ return aUri;
+ } else {
+ let prePathLength = DomainUtil.getPrePath(aUri).length + 1;
+ let len = Math.max(prePathLength, aMaxLength);
+ return aUri.substring(0, len) + "...";
+ }
+ }
+
+
+ /**
+ * Shows a notification that a redirect was requested by a page (meta refresh
+ * or with headers).
+ *
+ * @param {<browser> element} browser
+ * @param {string} redirectTargetUri
+ * @param {number} delay
+ * @param {string=} redirectOriginUri
+ * @return {boolean} whether showing the notification succeeded
+ */
+ // TODO, bad smell: Instead of the <browser> etc. hand over a `Request`
+ // object that contains everything. This requires
+ // e.g. a `MetaRedirectRequest` class.
+ self._showRedirectNotification = function(browser, redirectTargetUri, delay,
+ redirectOriginUri) {
+ // TODO: Do something with the delay. Not sure what the best thing to do is
+ // without complicating the UI.
+
+ // TODO: The following error seems to be resulting when the notification
+ // goes away with a redirect, either after clicking "allow" or if the
+ // redirect is allowed and happens automatically.
+ //
+ // Source file: chrome://browser/content/browser.js
+ // Line: 3704
+ // ----------
+ // Error: self._closedNotification.parentNode is null
+ // Source file: chrome://global/content/bindings/notification.xml
+ // Line: 260
+
+ // redirectOriginUri is optional and is not necessary for <meta> redirects.
+ let isOriginUndefined = redirectOriginUri === undefined;
+ redirectOriginUri = redirectOriginUri || self.getTopLevelDocumentUri();
+
+ if (isFennec) {
+ Logger.warning(Logger.TYPE_INTERNAL,
+ "Should have shown redirect notification to <" + redirectTargetUri +
+ ">, but it's not implemented yet on Fennec.");
+ return false;
+ }
+
+ var notificationBox = gBrowser.getNotificationBox(browser);
+ var notificationValue = "request-policy-meta-redirect";
+
+ // prepare the notification's label
+ let notificationLabel;
+ if (isOriginUndefined) {
+ notificationLabel = StringUtils.$str("redirectNotification",
+ [cropUri(redirectTargetUri, 50)]);
+ } else {
+ notificationLabel = StringUtils.$str("redirectNotificationWithOrigin",
+ [cropUri(redirectOriginUri, 50), cropUri(redirectTargetUri, 50)]);
+ }
+
+
+ var addRuleMenuName = "requestpolicyRedirectAddRuleMenu";
+ var addRulePopup = $id(addRuleMenuName);
+ DOMUtils.removeChildren(addRulePopup);
+
+ let m = requestpolicy.menu;
+ var originBaseDomain = DomainUtil.getBaseDomain(redirectOriginUri);
+ var destBaseDomain = DomainUtil.getBaseDomain(redirectTargetUri);
+
+ var origin = null, dest = null;
+ if (originBaseDomain !== null) {
+ origin = m._addWildcard(originBaseDomain);
+ }
+ if (destBaseDomain !== null) {
+ dest = m._addWildcard(destBaseDomain);
+ }
+
+ let mayPermRulesBeAdded = WindowUtils.mayPermanentRulesBeAdded(window);
+
+ let cm = requestpolicy.classicmenu;
+
+ if (destBaseDomain !== null) {
+ cm.addMenuItemTemporarilyAllowDest(addRulePopup, dest);
+ if (mayPermRulesBeAdded) {
+ cm.addMenuItemAllowDest(addRulePopup, dest);
+ }
+ }
+
+ if (originBaseDomain !== null && destBaseDomain !== null) {
+ cm.addMenuSeparator(addRulePopup);
+ }
+
+ if (originBaseDomain !== null) {
+ cm.addMenuItemTemporarilyAllowOrigin(addRulePopup, origin);
+ if (mayPermRulesBeAdded) {
+ cm.addMenuItemAllowOrigin(addRulePopup, origin);
+ }
+ }
+
+ if (originBaseDomain !== null && destBaseDomain !== null) {
+ cm.addMenuSeparator(addRulePopup);
+
+ cm.addMenuItemTemporarilyAllowOriginToDest(addRulePopup, origin, dest);
+ if (mayPermRulesBeAdded) {
+ cm.addMenuItemAllowOriginToDest(addRulePopup, origin, dest);
+ }
+ }
+
+
+
+
+ var notification = notificationBox
+ .getNotificationWithValue(notificationValue);
+ if (notification) {
+ notification.label = notificationLabel;
+ } else {
+ var buttons = [
+ {
+ label: StringUtils.$str("allow"),
+ accessKey: StringUtils.$str("allow.accesskey"),
+ popup: null,
+ callback: function() {
+ // Fx 3.7a5+ calls shouldLoad for location.href changes.
+
+ // TODO: currently the allow button ignores any additional
+ // HTTP response headers [1]. Maybe there is a way to take
+ // those headers into account (e.g. `Set-Cookie`?), or maybe
+ // this is not necessary at all.
+ // [1] https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Response_fields
+
+ RequestProcessor.registerAllowedRedirect(
+ browser.documentURI.specIgnoringRef, redirectTargetUri);
+
+ browser.messageManager.sendAsyncMessage(C.MM_PREFIX + "setLocation",
+ {uri: redirectTargetUri});
+ }
+ },
+ {
+ label: StringUtils.$str("deny"),
+ accessKey: StringUtils.$str("deny.accesskey"),
+ popup: null,
+ callback: function() {
+ // Do nothing. The notification closes when this is called.
+ }
+ },
+ {
+ label: StringUtils.$str("addRule"),
+ accessKey: StringUtils.$str("addRule.accesskey"),
+ popup: addRuleMenuName,
+ callback: null
+ }
+ // TODO: add a "read more about URL redirection" button, targetting to
+ // https://en.wikipedia.org/wiki/URL_redirection
+ ];
+ const priority = notificationBox.PRIORITY_WARNING_MEDIUM;
+ notificationBox.appendNotification(notificationLabel, notificationValue,
+ "chrome://browser/skin/Info.png", priority, buttons);
+ }
+ return true;
+ };
+
+
+ /**
+ * Performs actions required to be performed after a tab change.
+ */
+ self.tabChanged = function() {
+ // TODO: verify the Fennec and all supported browser versions update the
+ // status bar properly with only the ProgressListener. Once verified,
+ // remove calls to tabChanged();
+ // self._updateBlockedContentState(content.document);
+ };
+
+ self._containsNonBlacklistedRequests = function(requests) {
+ for (let i = 0, len = requests.length; i < len; i++) {
+ if (!requests[i].isOnBlacklist()) {
+ // This request has not been blocked by the blacklist
+ return true;
+ }
+ }
+ return false;
+ };
+
+ /**
+ * Checks if the document has blocked content and shows appropriate
+ * notifications.
+ */
+ self._updateBlockedContentState = function() {
+ try {
+ let browser = gBrowser.selectedBrowser;
+ let uri = DomainUtil.stripFragment(browser.currentURI.spec);
+ Logger.debug(Logger.TYPE_INTERNAL,
+ "Checking for blocked requests from page <" + uri + ">");
+
+ // TODO: this needs to be rewritten. checking if there is blocked
+ // content could be done much more efficiently.
+ let documentContainsBlockedContent = RequestProcessor
+ .getAllRequestsInBrowser(browser).containsBlockedRequests();
+ self._setContentBlockedState(documentContainsBlockedContent);
+
+ let logText = documentContainsBlockedContent ?
+ "Requests have been blocked." :
+ "No requests have been blocked.";
+ Logger.debug(Logger.TYPE_INTERNAL, logText);
+ } catch (e) {
+ Logger.severeError(
+ "Unable to complete _updateBlockedContentState actions: " + e, e);
+ }
+ };
+
+ /**
+ * Sets the blocked content notifications visible to the user.
+ */
+ self._setContentBlockedState = function(isContentBlocked) {
+ var button = $id(toolbarButtonId);
+ if (button) {
+ button.setAttribute("requestpolicyBlocked", isContentBlocked);
+ }
+ };
+
+ /**
+ * Update RP's "permissive" status, which is to true or false.
+ */
+ function updatePermissiveStatus() {
+ var button = $id(toolbarButtonId);
+ if (button) {
+ let isPermissive = Prefs.isBlockingDisabled();
+ button.setAttribute("requestpolicyPermissive", isPermissive);
+ }
+ }
+ /**
+ * register a pref observer
+ */
+ function updatePermissiveStatusOnPrefChanges() {
+ OverlayEnvironment.obMan.observeRPPref(
+ ["startWithAllowAllEnabled"],
+ function(subject, topic, data) {
+ if (topic === "nsPref:changed") {
+ updatePermissiveStatus();
+ }
+ });
+ }
+ OverlayEnvironment.addStartupFunction(Environment.LEVELS.INTERFACE,
+ updatePermissiveStatusOnPrefChanges);
+ // initially set the Permissive Status
+ OverlayEnvironment.addStartupFunction(Environment.LEVELS.UI,
+ updatePermissiveStatus);
+
+ /**
+ * This function is called when any allowed requests happen. This must be as
+ * fast as possible because request processing blocks until this function
+ * returns.
+ *
+ * @param {}
+ * originUri
+ * @param {}
+ * destUri
+ */
+ self.observeAllowedRequest = function(originUri, destUri) {
+ if (self.requestLog) {
+ self.requestLog.addAllowedRequest(originUri, destUri);
+ }
+ };
+
+ /**
+ * This function is called when any blocked requests happen. This must be as
+ * fast as possible because request processing blocks until this function
+ * returns.
+ *
+ * @param {}
+ * originUri
+ * @param {}
+ * destUri
+ */
+ self.observeBlockedRequest = function(originUri, destUri) {
+ self._updateNotificationDueToBlockedContent();
+ if (self.requestLog) {
+ self.requestLog.addBlockedRequest(originUri, destUri);
+ }
+ };
+
+ /**
+ * This function gets called when a top-level document request has been
+ * blocked.
+ * This function is called during shouldLoad(). As shouldLoad shoudn't be
+ * blocked, it's better to set a timeout here.
+ */
+ self.observeBlockedTopLevelDocRequest = function (browser, originUri,
+ destUri) {
+ // This function is called during shouldLoad() so set a timeout to
+ // avoid blocking shouldLoad.
+ window.setTimeout(function() {
+ requestpolicy.overlay._showRedirectNotification(browser, destUri, 0);
+ }, 0);
+ };
+
+ // TODO: observeBlockedFormSubmissionRedirect
+
+ self._updateNotificationDueToBlockedContent = function() {
+ if (!blockedContentCheckTimeoutId) {
+ self._updateBlockedContentStateAfterTimeout();
+ }
+ };
+
+ self._updateBlockedContentStateAfterTimeout = function() {
+ const browser = gBrowser.selectedBrowser;
+ blockedContentCheckTimeoutId = window.setTimeout(function() {
+ requestpolicy.overlay._updateBlockedContentState(browser);
+ }, blockedContentStateUpdateDelay);
+ };
+
+ self._stopBlockedContentCheckTimeout = function() {
+ if (blockedContentCheckTimeoutId) {
+ window.clearTimeout(blockedContentCheckTimeoutId);
+ blockedContentCheckTimeoutId = null;
+ }
+ };
+
+ /**
+ * Called as an event listener when popupshowing fires on the
+ * contentAreaContextMenu.
+ */
+ self._contextMenuOnPopupShowing = function() {
+ requestpolicy.overlay._wrapOpenLink();
+ /*requestpolicy.overlay._attachPopupToContextMenu();*/
+ };
+
+ /**
+ * Wraps (overrides) the following methods of gContextMenu
+ * - openLink()
+ * - openLinkInPrivateWindow()
+ * - openLinkInCurrent()
+ * so that RequestPolicy can register a link-click.
+ *
+ * The original methods are defined in Firefox' nsContextMenu.js:
+ * http://mxr.mozilla.org/mozilla-central/source/browser/base/content/nsContextMenu.js
+ *
+ * The openLinkInTab() method doesn't need to be wrapped because new tabs are already
+ * recognized by tabAdded(), which is wrapped elsewhere. The tabAdded() function ends up
+ * being called when openLinkInTab() is called.
+ *
+ * TODO: There are even more similar methods in gContextMenu (frame-specific),
+ * and perhaps the number will increase in future. Frame-specific contextMenu-
+ * entries are working, but are registered e.g. as "new window opened" by
+ * the subsequent shouldLoad() call.
+ */
+ self._wrapOpenLink = function() {
+ if (!gContextMenu.requestpolicyMethodsOverridden) {
+ gContextMenu.requestpolicyMethodsOverridden = true;
+
+ gContextMenu.openLink = function() {
+ RequestProcessor.registerLinkClicked(this.target.ownerDocument.URL, this.linkURL);
+ return this.__proto__.openLink.call(this); // call the overridden method
+ };
+
+ // Below, we check whether the functions exist before overriding it, because
+ // those functions have been introduced in later versions of Firefox than openLink().
+
+ if (gContextMenu.openLinkInPrivateWindow) {
+ gContextMenu.openLinkInPrivateWindow = function() {
+ RequestProcessor.registerLinkClicked(this.target.ownerDocument.URL, this.linkURL);
+ return this.__proto__.openLinkInPrivateWindow.call(this);
+ };
+ }
+
+ if (gContextMenu.openLinkInCurrent) {
+ gContextMenu.openLinkInCurrent = function() {
+ RequestProcessor.registerLinkClicked(this.target.ownerDocument.URL, this.linkURL);
+ return this.__proto__.openLinkInCurrent.call(this);
+ };
+ }
+ }
+ };
+
+ /**
+ * Modifies the addTab() function so that RequestPolicy can be aware of the
+ * tab being opened. Assume that if the tab is being opened, it was an action
+ * the user wanted (e.g. the equivalent of a link click). Using a TabOpen
+ * event handler, I was unable to determine the referrer, so that approach
+ * doesn't seem to be an option. This doesn't actually wrap addTab because the
+ * extension TabMixPlus modifies the function rather than wraps it, so
+ * wrapping it will break tabs if TabMixPlus is installed.
+ */
+ self._wrapAddTab = function() {
+ if (!gBrowser.requestpolicyAddTabModified) {
+ gBrowser.requestpolicyAddTabModified = true;
+
+ // For reference, the addTab() function signature looks like this:
+ // function addTab(aURI, aReferrerURI, aCharset, aPostData, aOwner,
+ // aAllowThirdPartyFixup) {";
+ // where it's possible that only two arguments are used and aReferrerURI
+ // is a hash of the other arguments as well as new ones.
+ // See https://github.com/RequestPolicyContinued/requestpolicy/issues/38
+
+ // In order to keep our code from breaking if the signature of addTab
+ // changes (even just a change in variable names, for example), we'll
+ // simply insert our own line right after the first curly brace in the
+ // string representation of the addTab function.
+ var addTabString = gBrowser.addTab.toString();
+ var firstCurlyBrace = addTabString.indexOf("{");
+ var addTabParts = [];
+ // Includes the '{'
+ addTabParts[0] = addTabString.substring(0, firstCurlyBrace + 1);
+ // Starts after the '{'
+ addTabParts[1] = addTabString.substring(firstCurlyBrace + 1);
+
+ // We use 'arguments' so that we aren't dependent on the names of two
+ // parameters, as it seems not unlikely that these could change due to
+ // the second parameter's purpose having been changed.
+ var newFirstCodeLine = "\n requestpolicy.overlay.tabAdded(arguments[0], arguments[1]);";
+ // Finally, add our line to the beginning of the addTab function.
+ eval("gBrowser.addTab = " + addTabParts[0] + newFirstCodeLine +
+ addTabParts[1]);
+ }
+ };
+
+
+
+ self._unwrapAddTab = function() {
+ if (gBrowser.requestpolicyAddTabModified === true) {
+ // get the addTab() function as a string
+ let addTabString = gBrowser.addTab.toString();
+
+ // define the regular expression that should find the existing code
+ // line that RequestPolicy added.
+ let codeLineRE = /(\n )?requestpolicy\.overlay\.tabAdded\(arguments\[0\], arguments\[1\]\);/;
+
+ // use the regular expression
+ let newAddTabString = addTabString.replace(codeLineRE, "");
+
+ // apply the changes
+ eval("gBrowser.addTab = " + newAddTabString);
+
+ delete gBrowser.requestpolicyAddTabModified;
+ }
+ };
+
+ /**
+ * This is called by the modified addTab().
+ *
+ * @param {String}
+ * url
+ * @param {nsIURI/hash}
+ * referrerURI
+ */
+ self.tabAdded = function(url, referrerURI) {
+ // The second argument to addTab was changed to a hash.
+ // See https://github.com/RequestPolicyContinued/requestpolicy/issues/38
+ if (referrerURI && !(referrerURI instanceof Components.interfaces.nsIURI)) {
+ if ("referrerURI" in referrerURI) {
+ referrerURI = referrerURI.referrerURI;
+ } else {
+ referrerURI = null;
+ }
+ }
+ if (referrerURI) {
+ RequestProcessor.registerLinkClicked(referrerURI.spec, url);
+ }
+ };
+
+ self._addLocationObserver = function() {
+ self.locationListener = {
+ onLocationChange : function(aProgress, aRequest, aURI) {
+ // This gets called both for tab changes and for history navigation.
+ // The timer is running on the main window, not the document's window,
+ // so we want to stop the timer when the tab is changed.
+ requestpolicy.overlay._stopBlockedContentCheckTimeout();
+ requestpolicy.overlay
+ ._updateBlockedContentState(gBrowser.selectedBrowser);
+ },
+
+ QueryInterface: XPCOMUtils.generateQI(["nsIWebProgressListener",
+ "nsISupportsWeakReference"])
+ };
+
+ // https://developer.mozilla.org/en/Code_snippets/Progress_Listeners
+ gBrowser.addProgressListener(self.locationListener);
+ };
+
+ self._removeLocationObserver = function() {
+ gBrowser.removeProgressListener(self.locationListener);
+ };
+
+ self._addHistoryObserver = function() {
+ // Implements nsISHistoryListener (and nsISupportsWeakReference)
+ self.historyListener = {
+ OnHistoryGoBack : function(backURI) {
+ RequestProcessor.registerHistoryRequest(backURI.asciiSpec);
+ return true;
+ },
+
+ OnHistoryGoForward : function(forwardURI) {
+ RequestProcessor.registerHistoryRequest(forwardURI.asciiSpec);
+ return true;
+ },
+
+ OnHistoryGotoIndex : function(index, gotoURI) {
+ RequestProcessor.registerHistoryRequest(gotoURI.asciiSpec);
+ return true;
+ },
+
+ OnHistoryNewEntry : function(newURI) {
+ },
+
+ OnHistoryPurge : function(numEntries) {
+ return true;
+ },
+
+ OnHistoryReload : function(reloadURI, reloadFlags) {
+ return true;
+ },
+
+ QueryInterface : function(aIID, aResult) {
+ if (aIID.equals(Components.interfaces.nsISHistoryListener) ||
+ aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
+ aIID.equals(Components.interfaces.nsISupports)) {
+ return this;
+ }
+ throw Components.results.NS_NOINTERFACE;
+ },
+
+ GetWeakReference : function() {
+ return Components.classes["@mozilla.org/appshell/appShellService;1"]
+ .createInstance(Components.interfaces.nsIWeakReference);
+ }
+ };
+
+ // there seems to be a bug in Firefox ESR 24 -- the session history is
+ // null. After waiting a few miliseconds it's available. To be sure this
+ let tries = 0, waitTime = 20, maxTries = 10;
+ let tryAddingSHistoryListener = function() {
+ ++tries;
+ try {
+ let sHistory = gBrowser.webNavigation.sessionHistory;
+ sHistory.addSHistoryListener(self.historyListener);
+ return;
+ } catch (e) {
+ if (tries >= maxTries) {
+ Logger.severeError("Can't add session history listener, even " +
+ "after " + tries + " tries. "+e, e);
+ return;
+ }
+ // call this function again in a few miliseconds.
+ setTimeout(tryAddingSHistoryListener, waitTime);
+ }
+ };
+ tryAddingSHistoryListener();
+ };
+
+ self._removeHistoryObserver = function() {
+ var sHistory = gBrowser.webNavigation.sessionHistory;
+ try {
+ sHistory.removeSHistoryListener(self.historyListener);
+ } catch (e) {
+ // When closing the last window in a session where additional windows
+ // have been opened and closed, this will sometimes fail (bug #175).
+ }
+ };
+
+ /**
+ * Called before the popup menu is shown.
+ *
+ * @param {Event}
+ * event
+ */
+ self.onPopupShowing = function(event) {
+ // if (event.currentTarget != event.originalTarget) {
+ // return;
+ // }
+ requestpolicy.menu.prepareMenu();
+ };
+
+ /**
+ * Called after the popup menu has been hidden.
+ *
+ * @param {Event}
+ * event
+ */
+ self.onPopupHidden = function(event) {
+ var rulesChanged = requestpolicy.menu.processQueuedRuleChanges();
+ if (rulesChanged || self._needsReloadOnMenuClose) {
+ if (rpPrefBranch.getBoolPref("autoReload")) {
+ let mm = gBrowser.selectedBrowser.messageManager;
+ mm.sendAsyncMessage(C.MM_PREFIX + "reload");
+ }
+ }
+ self._needsReloadOnMenuClose = false;
+ // if (event.currentTarget != event.originalTarget) {
+ // return;
+ // }
+ // Leave the popup attached to the context menu, as we consider that the
+ // default location for it.
+ //self._attachPopupToContextMenu();
+ };
+
+ /**
+ * Determines the top-level document's uri identifier based on the current
+ * identifier level setting.
+ *
+ * @return {String} The current document's identifier.
+ */
+ self.getTopLevelDocumentUriIdentifier = function() {
+ return DomainUtil.getIdentifier(self.getTopLevelDocumentUri());
+ };
+
+ /**
+ * Get the top-level document's uri.
+ */
+ self.getTopLevelDocumentUri = function() {
+ let uri = gBrowser.selectedBrowser.currentURI.spec;
+ return RequestProcessor.getTopLevelDocTranslation(uri) ||
+ DomainUtil.stripFragment(uri);
+ };
+
+ /**
+ * Toggles disabling of all blocking for the current session.
+ *
+ * @param {Event}
+ * event
+ */
+ self.toggleTemporarilyAllowAll = function(event) {
+ var disabled = !Prefs.isBlockingDisabled();
+ Prefs.setBlockingDisabled(disabled);
+
+ // Change the link displayed in the menu.
+ $id("rp-link-enable-blocking").hidden = !disabled;
+ $id("rp-link-disable-blocking").hidden = disabled;
+ };
+
+ /**
+ * Allows requests from the specified origin to any destination for the
+ * duration of the browser session.
+ */
+ self.temporarilyAllowOrigin = function(originHost) {
+ PolicyManager.temporarilyAllowOrigin(originHost);
+ };
+
+ /**
+ * Allows the current document's origin to request from any destination for
+ * the duration of the browser session.
+ *
+ * @param {Event}
+ * event
+ */
+ self.temporarilyAllowCurrentOrigin = function(event) {
+ // Note: the available variable "content" is different than the avaialable
+ // "window.target".
+ var host = self.getTopLevelDocumentUriIdentifier();
+ PolicyManager.temporarilyAllowOrigin(host);
+ };
+
+ /**
+ * Allows a destination to be requested from any origin for the duration of
+ * the browser session.
+ *
+ * @param {String}
+ * destHost
+ */
+ self.temporarilyAllowDestination = function(destHost) {
+ PolicyManager.temporarilyAllowDestination(destHost);
+ };
+
+ /**
+ * Allows a destination to be requested from a single origin for the duration
+ * of the browser session.
+ *
+ * @param {String}
+ * originHost
+ * @param {String}
+ * destHost
+ */
+ self.temporarilyAllowOriginToDestination = function(originHost, destHost) {
+ PolicyManager.temporarilyAllowOriginToDestination(originHost, destHost);
+ };
+
+ /**
+ * Allows requests from an origin, including in future browser sessions.
+ */
+ self.allowOrigin = function(originHost) {
+ PolicyManager.allowOrigin(originHost);
+ };
+
+ /**
+ * Allows the current document's origin to request from any destination,
+ * including in future browser sessions.
+ *
+ * @param {Event}
+ * event
+ */
+ self.allowCurrentOrigin = function(event) {
+ var host = self.getTopLevelDocumentUriIdentifier();
+ PolicyManager.allowOrigin(host);
+ };
+
+ /**
+ * Allows requests to a destination, including in future browser sessions.
+ *
+ * @param {String}
+ * destHost
+ */
+ self.allowDestination = function(destHost) {
+ PolicyManager.allowDestination(destHost);
+ };
+
+ /**
+ * Allows requests to a destination from a single origin, including in future
+ * browser sessions.
+ *
+ * @param {String}
+ * originHost
+ * @param {String}
+ * destHost
+ */
+ self.allowOriginToDestination = function(originHost, destHost) {
+ PolicyManager.allowOriginToDestination(originHost, destHost);
+ };
+
+ /**
+ * Revokes all temporary permissions granted during the current session.
+ *
+ * @param {Event}
+ * event
+ */
+ self.revokeTemporaryPermissions = function(event) {
+ PolicyManager.revokeTemporaryRules();
+ self._needsReloadOnMenuClose = true;
+ popupElement.hidePopup();
+ };
+
+ self._openInNewTab = function(uri) {
+ gBrowser.selectedTab = gBrowser.addTab(uri);
+ };
+
+ self.openMenuByHotkey = function() {
+ // Ideally we'd put the popup in its normal place based on the rp toolbar
+ // button but let's not count on that being visible. So, we'll be safe and
+ // anchor it within the content element. However, there's no good way to
+ // right-align a popup. So, we can either let it be left aligned or we can
+ // figure out where we think the top-left corner should be. And that's what
+ // we do.
+ // The first time the width will be 0. The default value is determined by
+ // logging it or you can probably figure it out from the CSS which doesn't
+ // directly specify the width of the entire popup.
+ //Logger.dump('popup width: ' + popup.clientWidth);
+ var popupWidth = popupElement.clientWidth ? 730 : popupElement.clientWidth;
+ var anchor = $id("content");
+ var contentWidth = anchor.clientWidth;
+ // Take a few pixels off so it doesn't cover the browser chrome's border.
+ var xOffset = contentWidth - popupWidth - 2;
+ popupElement.openPopup(anchor, "overlap", xOffset);
+ };
+
+ // showExtensionConflictInfo : function() {
+ // var ext = RequestProcessor.getConflictingExtensions();
+ // var extJson = JSON.stringify(ext);
+ // self._openInNewTab(self._extensionConflictInfoUri
+ // + encodeURIComponent(extJson));
+ // },
+
+ // showPrefetchInfo : function() {
+ // self._openInNewTab(self._prefetchInfoUri);
+ // },
+ //
+ // showPrefetchDisablingInstructions : function() {
+ // self._openInNewTab(self._prefetchDisablingInstructionsUri);
+ // },
+
+ self.openToolbarPopup = function(anchor) {
+ // requestpolicy.overlay._toolbox.insertBefore(requestpolicy.overlay.popupElement,
+ // null);
+ popupElement.openPopup(anchor, "after_start", 0, 0, true, true);
+ };
+
+ function openLinkInNewTab(url, relatedToCurrent) {
+ window.openUILinkIn(url, "tab", {relatedToCurrent: !!relatedToCurrent});
+ popupElement.hidePopup();
+ }
+
+ self.openPrefs = openLinkInNewTab.bind(this, "about:requestpolicy", true);
+ self.openPolicyManager = openLinkInNewTab.bind(this,
+ "about:requestpolicy?yourpolicy", true);
+ self.openHelp = openLinkInNewTab.bind(this,
+ "https://github.com/RequestPolicyContinued/requestpolicy/wiki/Help-and-Support");
+
+
+ self.clearRequestLog = function() {
+ self.requestLog.clear();
+ };
+
+ self.toggleRequestLog = function() {
+ var requestLog = $id("requestpolicy-requestLog");
+ var requestLogSplitter = $id("requestpolicy-requestLog-splitter");
+ var requestLogFrame = $id("requestpolicy-requestLog-frame");
+ //var openRequestLog = $id("requestpolicyOpenRequestLog");
+
+ // TODO: figure out how this should interact with the new menu.
+ //var closeRequestLog = $id("requestpolicyCloseRequestLog");
+ var closeRequestLog = {};
+
+ if (requestLog.hidden) {
+ requestLogFrame.setAttribute("src",
+ "chrome://requestpolicy/content/ui/request-log.xul");
+ requestLog.hidden = requestLogSplitter.hidden = closeRequestLog.hidden = false;
+ //openRequestLog.hidden = true;
+ } else {
+ requestLogFrame.setAttribute("src", "about:blank");
+ requestLog.hidden = requestLogSplitter.hidden = closeRequestLog.hidden = true;
+ //openRequestLog.hidden = false;
+ self.requestLog = null;
+ }
+ };
+
+ return self;
+ }());
++
diff --cc content/ui/request-log.filtering.js
index 0000000,2908378..9326f2a
mode 000000,100644..100644
--- a/content/ui/request-log.filtering.js
+++ b/content/ui/request-log.filtering.js
@@@ -1,0 -1,110 +1,111 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+
+ window.requestpolicy.requestLog = (function (self) {
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ let {ScriptLoader, Services} = (function() {
+ let mod = {};
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm", mod);
+ Cu.import("resource://gre/modules/Services.jsm", mod);
+ return mod;
+ }());
+ let {WindowUtils} = ScriptLoader.importModule("lib/utils/windows");
+
+ let filterText = null;
+
+ // TODO: use the Window Environment instead
+ let elements = WindowUtils.getElementsByIdOnLoad(window, {
+ filterTextbox: "requestpolicy-requestLog-requestFilter",
+ clearFilterButton: "requestpolicy-requestLog-clearFilter"
+ });
+
+ self.filterChanged = function() {
+ let filterValue = elements.filterTextbox.value;
+
+ // create a new regular expression
+ filterText = filterValue.length === 0 ? null : new RegExp(filterValue, 'i');
+ // enable/disable the "Clear Filter" button
+ elements.clearFilterButton.disabled = filterValue.length === 0;
+
+ loadTable();
+ };
+
+ self.clearFilter = function() {
+ elements.filterTextbox.value = "";
+ elements.filterTextbox.focus();
+ self.filterChanged();
+ }
+
+
+
+
+ /**
+ * Check if the row should be displayed or filtered out.
+ *
+ * This function searches the first two columns for the filterText.
+ *
+ * @param {Array} aRow
+ */
+ self.isRowFilteredOut = function(aRow) {
+ if (filterText === null) {
+ return false;
+ }
+ // The row is filtered out in case *all* searches in the columns *failed*.
+ return aRow[0].search(filterText) === -1 &&
+ aRow[1].search(filterText) === -1;
+ };
+
+ function addRowOrFilterOut(aRow) {
+ if (self.isRowFilteredOut(aRow)) {
+ return;
+ }
+ self.visibleRows.push(aRow);
+ }
+
+ // This function is called every time the tree is sorted, filtered or reloaded
+ function loadTable() {
+ let oldRowCount = self.treeView.rowCount;
+
+ if (!filterText) {
+ // there's no filter ==> show all rows
+ self.visibleRows = self.rows;
+ } else {
+ // filter out the rows we want to display
+ self.visibleRows = [];
+ self.rows.forEach(addRowOrFilterOut);
+ }
+
+ // notify that the table rows has changed
+ let newRowCount = self.treeView.rowCount;
+ self.treebox.rowCountChanged(0, newRowCount-oldRowCount);
+ }
+
+
+ return self;
+ }(window.requestpolicy.requestLog || {}));
++
diff --cc content/ui/request-log.interface.js
index 0000000,941346b..1daef7d
mode 000000,100644..100644
--- a/content/ui/request-log.interface.js
+++ b/content/ui/request-log.interface.js
@@@ -1,0 -1,134 +1,135 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ window.requestpolicy = window.requestpolicy || {};
+
+ window.requestpolicy.requestLog = (function (self) {
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ let {ScriptLoader, Services} = (function() {
+ let mod = {};
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm", mod);
+ Cu.import("resource://gre/modules/Services.jsm", mod);
+ return mod;
+ }());
+ let {DomainUtil} = ScriptLoader.importModule("lib/utils/domains");
+ let {StringUtils} = ScriptLoader.importModule("lib/utils/strings");
+ let {Utils} = ScriptLoader.importModule("lib/utils");
+
+
+
+
+ self.clear = function() {
+ var count = self.treeView.rowCount;
+ if (count == 0) {
+ return;
+ }
+ self.rows = [];
+ self.visibleRows = [];
+ if (!self.treebox) {
+ return;
+ }
+ self.treebox.rowCountChanged(0, -count);
+ };
+
+ /**
+ * Copy the content of a cell to the clipboard. The row used will be the one
+ * selected when the context menu was opened.
+ */
+ self.copyToClipboard = function(columnName) {
+ var content = self.treeView.getCellText(self.tree.currentIndex,
+ self.tree.columns.getNamedColumn(columnName));
+
+ const clipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"]
+ .getService(Components.interfaces.nsIClipboardHelper);
+ clipboardHelper.copyString(content);
+ };
+
+ /**
+ * Open the content of a cell in a new tab. The row used will be the one
+ * selected when the context menu was opened.
+ */
+ self.openInNewTab = function(columnName) {
+ var content = self.treeView.getCellText(self.tree.currentIndex,
+ self.tree.columns.getNamedColumn(columnName));
+
+ var forbidden = true;
+ try {
+ var uri = DomainUtil.getUriObject(content);
+ if (uri.scheme == 'http' || uri.scheme == 'https' || uri.scheme == 'ftp') {
+ forbidden = false;
+ }
+ } catch (e) {
+ }
+
+ if (forbidden) {
+ var alertTitle = StringUtils.$str("actionForbidden");
+ var alertText = StringUtils.$str("urlCanOnlyBeCopiedToClipboard");
+ Services.prompt.alert(null, alertTitle, alertText);
+ return;
+ }
+
+ window.top.openUILinkIn(content, "tab", {relatedToCurrent: true});
+ };
+
+
+
+
+ function addRow(aRow) {
+ self.rows.push(aRow);
+
+ if (!self.isRowFilteredOut(aRow)) {
+ if (self.isEmptyMessageDisplayed) {
+ // If this were to be called in a multithreaded manner, there's probably
+ // a race condition here.
+ self.visibleRows.shift();
+ self.isEmptyMessageDisplayed = false;
+ self.treebox.rowCountChanged(0, -1);
+ }
+
+ self.visibleRows.push(aRow);
+
+ if (!self.treebox) {
+ return;
+ }
+
+ self.treebox.rowCountChanged(0, 1);
+ }
+ }
+
+ self.addAllowedRequest = function(originURI, destURI) {
+ addRow([originURI, destURI, false, (new Date()).toLocaleTimeString()]);
+ };
+
+ self.addBlockedRequest = function(originURI, destURI) {
+ addRow([originURI, destURI, true, (new Date()).toLocaleTimeString()]);
+ };
+
+
+
+ return self;
+ }(window.requestpolicy.requestLog || {}));
++
diff --cc content/ui/request-log.js
index 0000000,a60714e..6a02c1d
mode 000000,100644..100644
--- a/content/ui/request-log.js
+++ b/content/ui/request-log.js
@@@ -1,0 -1,81 +1,82 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ window.requestpolicy = window.requestpolicy || {};
+
+ window.requestpolicy.requestLog = (function (self) {
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+ let {ScriptLoader} = (function() {
+ let mod = {};
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm", mod);
+ return mod;
+ }());
+ let {StringUtils} = ScriptLoader.importModule("lib/utils/strings");
+ let {WindowUtils} = ScriptLoader.importModule("lib/utils/windows");
+ let {Environment,
+ ProcessEnvironment} = ScriptLoader.importModule("lib/environment");
+
+ // create a new Environment for this window
+ var WinEnv = new Environment(ProcessEnvironment, "WinEnv");
+ // The Environment has to be shut down when the content window gets unloaded.
+ WinEnv.shutdownOnUnload(window);
+ // start up right now, as there won't be any startup functions
+ WinEnv.startup();
+
+ let $id = window.document.getElementById.bind(window.document);
+
+
+ self.isEmptyMessageDisplayed = true;
+ self.rows = [];
+ self.visibleRows = [];
+
+
+
+ function init() {
+ self.tree = $id("requestpolicy-requestLog-tree")
+
+ self.tree.view = self.treeView;
+
+ showLogIsEmptyMessage();
+
+ // Give the requestpolicy overlay direct access to the the request log.
+ window.parent.requestpolicy.overlay.requestLog = self;
+ }
+ function showLogIsEmptyMessage() {
+ var message = StringUtils.$str("requestLogIsEmpty");
+ var directions = StringUtils.$str("requestLogDirections");
+ self.visibleRows.push([message, directions, false, ""]);
+ self.treebox.rowCountChanged(0, 1);
+ }
+
+ // call init() on the window's "load" event
+ WinEnv.elManager.addListener(window, "load", init, false);
+
+
+
+ return self;
+ }(window.requestpolicy.requestLog || {}));
++
diff --cc content/ui/request-log.tree-view.js
index 0000000,6f87f0d..5f6b98c
mode 000000,100644..100644
--- a/content/ui/request-log.tree-view.js
+++ b/content/ui/request-log.tree-view.js
@@@ -1,0 -1,185 +1,186 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ window.requestpolicy = window.requestpolicy || {};
+
+ window.requestpolicy.requestLog = (function (self) {
+
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+ const Cu = Components.utils;
+
+
+ let {ScriptLoader} = (function() {
+ let mod = {};
+ Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm", mod);
+ return mod;
+ }());
+ let {StringUtils} = ScriptLoader.importModule("lib/utils/strings");
+
+
+
+
+ self.treebox = null;
+
+ self.columnNameToIndexMap = {
+ "requestpolicy-requestLog-origin" : 0,
+ "requestpolicy-requestLog-destination" : 1,
+ "requestpolicy-requestLog-blocked" : 2,
+ "requestpolicy-requestLog-time" : 3
+ };
+
+ let aserv = Cc["@mozilla.org/atom-service;1"].getService(Ci.nsIAtomService);
+
+
+ function getVisibleRowAtIndex(index) {
+ return self.visibleRows[self.visibleRows.length - index - 1];
+ };
+
+
+
+ //
+ // the interface.
+ // see https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/Tutorial/Custom_Tree_Views
+ //
+
+ self.treeView = {
+ /**
+ * "This property should be set to the total number of rows in the tree."
+ * (getter function)
+ */
+ get rowCount () {
+ return self.visibleRows.length;
+ },
+
+ /**
+ * "This method should return the text contents at the specified row and
+ * column."
+ */
+ setTree: function(aTreebox) {
+ self.treebox = aTreebox;
+ },
+
+ /**
+ * This method is called once to set the tree element on the view.
+ */
+ getCellText: function(aIndex, aColumn) {
+ // Row 0 is actually the last element in the array so that we don't have to
+ // unshift() the array and can just push().
+ // TODO: Do an actual speed test with push vs. unshift to see if it matters
+ // with this javascript array implementation, though I'm assuming it does.
+ var columnIndex = self.columnNameToIndexMap[aColumn.id];
+ if (columnIndex != 2) {
+ return getVisibleRowAtIndex(aIndex)[self.columnNameToIndexMap[aColumn.id]];
+ }
+ },
+
+ isContainer: function(aIndex) {
+ return false;
+ },
+
+ isContainerOpen: function(aIndex) {
+ return false;
+ },
+
+ isContainerEmpty: function(aIndex) {
+ return false;
+ },
+
+ isSeparator: function(aIndex) {
+ return false;
+ },
+
+ isSorted: function() {
+ return false;
+ },
+
+ isEditable: function(aIndex, aColumn) {
+ return false;
+ },
+
+ getParentIndex: function(aIndex) {
+ return -1;
+ },
+
+ getLevel: function(aIndex) {
+ return 0;
+ },
+
+ hasNextSibling: function(aIndex, aAfter) {
+ return false;
+ },
+
+ toggleOpenState: function(aIndex) {},
+
+ getImageSrc: function(aIndex, aColumn) {
+ if (self.columnNameToIndexMap[aColumn.id] == 2) {
+ if (getVisibleRowAtIndex(aIndex)[2]) {
+ return "chrome://requestpolicy/skin/dot.png";
+ }
+ }
+ },
+
+ getProgressMode: function(aIndex, aColumn) {},
+ getCellValue: function(aIndex, aColumn) {},
+ cycleHeader: function(col, elem) {},
+ selectionChanged: function() {},
+ cycleCell: function(aIndex, aColumn) {},
+ performAction: function(action) {},
+ performActionOnCell: function(action, aIndex, aColumn) {},
+
+ getRowProperties: function(aIndex, aProps) {
+ var returnValue = (getVisibleRowAtIndex(aIndex)[2]) ? "blocked" : "allowed";
+
+ if (aProps) {
+ // Gecko version < 22
+ aProps.AppendElement(aserv.getAtom(returnValue));
+ } else {
+ // Gecko version >= 22
+ return returnValue;
+ }
+ },
+
+ getCellProperties: function(aIndex, aColumn, aProps) {
+ if (self.columnNameToIndexMap[aColumn.id] == 2) {
+ if (getVisibleRowAtIndex(aIndex)[2]) {
+ if (aProps) {
+ // Gecko version < 22
+ aProps.AppendElement(aserv.getAtom("blocked"));
+ } else {
+ // Gecko version >= 22
+ return "blocked";
+ }
+ }
+ }
+ },
+
+ getColumnProperties: function(aColumn, aProps) {
+ if (!aProps) {
+ return "";
+ }
+ }
+ };
+
+ return self;
+ }(window.requestpolicy.requestLog || {}));
++
diff --cc content/ui/xul-trees.js
index 0000000,fab25d1..58b8fb8
mode 000000,100644..100644
--- a/content/ui/xul-trees.js
+++ b/content/ui/xul-trees.js
@@@ -1,0 -1,185 +1,186 @@@
+ /*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see {tag: "http"://www.gnu.org/licenses}.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+ // differences in seamonkey:
+ // https://developer.mozilla.org/en-US/Add-ons/SeaMonkey_2
+ let isSeamonkey = appID === C.SEAMONKEY_ID;
+
+
+ exports.toolbarbutton = [
+ {parent: {special: {type: "subobject", id: "navigator-toolbox",
+ tree: ["palette"]}}, // ("#navigator-toolbox".palette)
+ tag: "toolbarbutton", id: "requestpolicyToolbarButton",
+ label: "RequestPolicy", tooltiptext: "RequestPolicy",
+ popup: "rp-popup"
+ }
+ ];
+
+ exports.mainTree = [
+ {parent: {id: (isSeamonkey ? "taskPopup" : "menu_ToolsPopup")},
+ tag: "menu", id: "requestpolicyToolsMenuPopup", label: "RequestPolicy",
+ accesskey: "r",
+ children: [
+ {tag: "menupopup",
+ children: [
+ {tag: "menuitem", label: "&rp.menu.managePolicies;", accesskey: "m",
+ oncommand: "requestpolicy.overlay.openPolicyManager();"},
+ {tag: "menuitem", label: "&rp.requestLog.title;", accesskey: "l",
+ oncommand: "requestpolicy.overlay.toggleRequestLog(event);"},
+ {tag: "menuitem", label: "&rp.menu.preferences;", accesskey: "p",
+ oncommand: "requestpolicy.overlay.openPrefs(event);"}
+ ]}
+ ]},
+
+
+ {parent: {special: {type: "__window__"}},
+ tag: "keyset", id: "requestpolicyKeyset",
+ children: [
+ {tag: "key", key: "r", modifiers: "accel alt",
+ oncommand: "requestpolicy.overlay.openMenuByHotkey()"}
+ ]},
+
+
+ {parent: {special: {type: "__window__"}},
+ tag: "popupset", id: "requestpolicyPopupset",
+ children: [
+ {tag: "menupopup", id: "requestpolicyRedirectAddRuleMenu"},
+ {tag: "menupopup", id: "rp-popup", noautohide: "true",
+ position: "after_start",
+ onpopupshowing: "requestpolicy.overlay.onPopupShowing(event);",
+ onpopuphidden: "requestpolicy.overlay.onPopupHidden(event);",
+ children: [
+ {tag: "vbox", id: "rp-contents",
+ children: [
+ {tag: "hbox", id: "rp-main",
+ children: [
+ {tag: "vbox", id: "rp-origins-destinations",
+ children: [
+ {tag: "hbox", id: "rp-origin", "class": "rp-od-item",
+ onclick: "requestpolicy.menu.itemSelected(event);",
+ children: [
+ {tag: "label", id: "rp-origin-domainname", "class": "domainname",
+ flex: "2"},
+ {tag: "label", id: "rp-origin-num-requests",
+ "class": "numRequests"}
+ ]},
+ {tag: "vbox", id: "rp-other-origins",
+ children: [
+ {tag: "label", id: "rp-other-origins-title",
+ value: "&rp.menu.otherOrigins;"},
+ {tag: "vbox", id: "rp-other-origins-list",
+ "class": "rp-label-list"}
+ ]},
+ {tag: "vbox", id: "rp-blocked-destinations",
+ children: [
+ {tag: "label", id: "rp-blocked-destinations-title",
+ value: "&rp.menu.blockedDestinations;"},
+ {tag: "vbox", id: "rp-blocked-destinations-list",
+ "class": "rp-label-list"}
+ ]},
+ {tag: "vbox", id: "rp-mixed-destinations",
+ children: [
+ {tag: "label", id: "rp-mixed-destinations-title",
+ value: "&rp.menu.mixedDestinations;"},
+ {tag: "vbox", id: "rp-mixed-destinations-list",
+ "class": "rp-label-list"}
+ ]},
+ {tag: "vbox", id: "rp-allowed-destinations",
+ children: [
+ {tag: "label", id: "rp-allowed-destinations-title",
+ value: "&rp.menu.allowedDestinations;"},
+ {tag: "vbox", id: "rp-allowed-destinations-list",
+ "class": "rp-label-list"}
+ ]}
+ ]},
+ {tag: "vbox", id: "rp-details",
+ children: [
+ {tag: "vbox", id: "rp-rules-remove"},
+ {tag: "vbox", id: "rp-rules-add"},
+ {tag: "vbox", id: "rp-rules-info"}
+ ]}
+ ]},
+ {tag: "hbox", id: "rp-revoke-temporary-permissions", hidden: "true",
+ children: [
+ {tag: "label", value: "&rp.menu.revokeTemporaryPermissions;",
+ onclick: "requestpolicy.overlay.revokeTemporaryPermissions();"}
+ ]},
+ {tag: "hbox", id: "rp-footer",
+ children: [
+ {tag: "hbox", id: "rp-footer-links",
+ children: [
+ {tag: "label", id: "rp-link-enable-blocking",
+ "class": "rp-footer-link", value: "&rp.menu.enableBlocking;",
+ onclick: "requestpolicy.overlay.toggleTemporarilyAllowAll();"},
+ {tag: "label", id: "rp-link-disable-blocking",
+ "class": "rp-footer-link", value: "&rp.menu.disableBlocking;",
+ onclick: "requestpolicy.overlay.toggleTemporarilyAllowAll();"},
+ {tag: "label", id: "rp-link-help", "class": "rp-footer-link",
+ value: "&rp.menu.help;",
+ onclick: "requestpolicy.overlay.openHelp();"},
+ {tag: "label", id: "rp-link-prefs", "class": "rp-footer-link",
+ value: "&rp.menu.preferences;",
+ onclick: "requestpolicy.overlay.openPrefs(event);"},
+ {tag: "label", id: "rp-link-policies", "class": "rp-footer-link",
+ value: "&rp.menu.managePolicies;",
+ onclick: "requestpolicy.overlay.openPolicyManager();"},
+ {tag: "label", id: "rp-link-request-log", "class": "rp-footer-link",
+ value: "&rp.requestLog.title;",
+ onclick: "requestpolicy.overlay.toggleRequestLog(event);"}
+ ]}
+ ]}
+ ]}
+ ]}
+ ]},
+
+
+ {parent: {id: "appcontent"},
+ tag: "splitter", id: "requestpolicy-requestLog-splitter", hidden: "true"},
+
+ {parent: {id: "appcontent"},
+ tag: "vbox", id: "requestpolicy-requestLog", height: "300",
+ hidden: "true", persist: "height",
+ children: [
+ {tag: "toolbox", id: "requestpolicy-requestLog-header",
+ children: [
+ {tag: "toolbar", id: "requestpolicy-requestLog-toolbar",
+ align: "center",
+ children: [
+ {tag: "label", id: "requestpolicy-requestLog-title",
+ control: "requestpolicy-requestLog-frame",
+ value: "&rp.requestLog.title;", crop: "end"},
+ {tag: "button", id: "requestpolicy-requestLog-clear",
+ label: "&rp.requestLog.clear;",
+ oncommand: "requestpolicy.overlay.clearRequestLog();"},
+ {tag: "vbox", flex: "1"},
+ {tag: "toolbarbutton", id: "requestpolicy-requestLog-close",
+ align: "right",
+ oncommand: "requestpolicy.overlay.toggleRequestLog()"}
+ ]}
+ ]},
+ // The src of this iframe is set to
+ // chrome://requestpolicy/content/ui/request-log.xul in overlay.js
+ {tag: "iframe", id: "requestpolicy-requestLog-frame", type: "chrome",
+ flex: "1"}
+ ]}
+ ];
++
diff --cc locale/de/requestpolicy.dtd
index 0000000,fd07f0b..fd07f0b
mode 000000,100644..100644
--- a/locale/de/requestpolicy.dtd
+++ b/locale/de/requestpolicy.dtd
diff --cc locale/de/requestpolicy.properties
index 0000000,7c93d2d..7c93d2d
mode 000000,100644..100644
--- a/locale/de/requestpolicy.properties
+++ b/locale/de/requestpolicy.properties
diff --cc locale/en-US/requestpolicy.dtd
index 0000000,547de3e..547de3e
mode 000000,100644..100644
--- a/locale/en-US/requestpolicy.dtd
+++ b/locale/en-US/requestpolicy.dtd
diff --cc locale/eo/requestpolicy.dtd
index 0000000,13c6291..13c6291
mode 000000,100644..100644
--- a/locale/eo/requestpolicy.dtd
+++ b/locale/eo/requestpolicy.dtd
diff --cc locale/es-MX/requestpolicy.dtd
index 0000000,1c406c2..1c406c2
mode 000000,100644..100644
--- a/locale/es-MX/requestpolicy.dtd
+++ b/locale/es-MX/requestpolicy.dtd
diff --cc locale/eu/requestpolicy.dtd
index 0000000,1649291..1649291
mode 000000,100644..100644
--- a/locale/eu/requestpolicy.dtd
+++ b/locale/eu/requestpolicy.dtd
diff --cc locale/fr/requestpolicy.dtd
index 0000000,408f3b3..408f3b3
mode 000000,100644..100644
--- a/locale/fr/requestpolicy.dtd
+++ b/locale/fr/requestpolicy.dtd
diff --cc locale/it/requestpolicy.dtd
index 0000000,989ee26..989ee26
mode 000000,100644..100644
--- a/locale/it/requestpolicy.dtd
+++ b/locale/it/requestpolicy.dtd
diff --cc locale/ja/requestpolicy.dtd
index 0000000,e5bdb56..e5bdb56
mode 000000,100644..100644
--- a/locale/ja/requestpolicy.dtd
+++ b/locale/ja/requestpolicy.dtd
diff --cc locale/ja/requestpolicy.properties
index 0000000,c05e9c4..c05e9c4
mode 000000,100644..100644
--- a/locale/ja/requestpolicy.properties
+++ b/locale/ja/requestpolicy.properties
diff --cc locale/ko-KR/requestpolicy.dtd
index 0000000,5e305dc..5e305dc
mode 000000,100644..100644
--- a/locale/ko-KR/requestpolicy.dtd
+++ b/locale/ko-KR/requestpolicy.dtd
diff --cc locale/lv-LV/requestpolicy.dtd
index 0000000,fb2e6c0..fb2e6c0
mode 000000,100644..100644
--- a/locale/lv-LV/requestpolicy.dtd
+++ b/locale/lv-LV/requestpolicy.dtd
diff --cc locale/nl/requestpolicy.dtd
index 0000000,7f226c7..7f226c7
mode 000000,100644..100644
--- a/locale/nl/requestpolicy.dtd
+++ b/locale/nl/requestpolicy.dtd
diff --cc locale/pt-BR/requestpolicy.dtd
index 0000000,b12563e..b12563e
mode 000000,100644..100644
--- a/locale/pt-BR/requestpolicy.dtd
+++ b/locale/pt-BR/requestpolicy.dtd
diff --cc locale/ru-RU/requestpolicy.dtd
index 0000000,c37616a..c37616a
mode 000000,100644..100644
--- a/locale/ru-RU/requestpolicy.dtd
+++ b/locale/ru-RU/requestpolicy.dtd
diff --cc locale/sk-SK/requestpolicy.dtd
index 0000000,2531d8d..2531d8d
mode 000000,100644..100644
--- a/locale/sk-SK/requestpolicy.dtd
+++ b/locale/sk-SK/requestpolicy.dtd
diff --cc locale/sv-SE/requestpolicy.dtd
index 0000000,12538bf..12538bf
mode 000000,100644..100644
--- a/locale/sv-SE/requestpolicy.dtd
+++ b/locale/sv-SE/requestpolicy.dtd
diff --cc locale/tr/requestpolicy.dtd
index 0000000,5e04879..5e04879
mode 000000,100644..100644
--- a/locale/tr/requestpolicy.dtd
+++ b/locale/tr/requestpolicy.dtd
diff --cc locale/uk-UA/requestpolicy.dtd
index 0000000,1dd6ad3..1dd6ad3
mode 000000,100644..100644
--- a/locale/uk-UA/requestpolicy.dtd
+++ b/locale/uk-UA/requestpolicy.dtd
diff --cc locale/zh-CN/requestpolicy.dtd
index 0000000,6be29ff..6be29ff
mode 000000,100644..100644
--- a/locale/zh-CN/requestpolicy.dtd
+++ b/locale/zh-CN/requestpolicy.dtd
diff --cc locale/zh-TW/requestpolicy.dtd
index 0000000,2ff4e42..2ff4e42
mode 000000,100644..100644
--- a/locale/zh-TW/requestpolicy.dtd
+++ b/locale/zh-TW/requestpolicy.dtd
diff --cc skin/close.png
index 6d37d63,6d37d63..6d37d63
Binary files differ
diff --cc skin/dot.png
index b5c4988,b5c4988..b5c4988
Binary files differ
diff --cc skin/menu-other-origins.png
index b0b6d08,b0b6d08..b0b6d08
Binary files differ
diff --cc skin/requestpolicy-icon-24-allowed.png
index cd092a9,cd092a9..cd092a9
Binary files differ
diff --cc skin/requestpolicy-icon-24-blocked.png
index 6af940c,6af940c..6af940c
Binary files differ
diff --cc skin/requestpolicy-icon-24-disabled.png
index 64e79e6,64e79e6..64e79e6
Binary files differ
diff --cc skin/requestpolicy-icon-32-allowed.png
index 4d6e274,4d6e274..4d6e274
Binary files differ
diff --cc skin/requestpolicy-icon-32-blocked.png
index e63147d,e63147d..e63147d
Binary files differ
diff --cc skin/requestpolicy-icon-32-disabled.png
index 8575dde,8575dde..8575dde
Binary files differ
diff --cc skin/requestpolicy-icon-32.png
index 73c7d7f,73c7d7f..73c7d7f
Binary files differ
diff --cc skin/requestpolicy-icon-allowed.png
index 67cdf00,67cdf00..67cdf00
mode 100644,100755..100755
Binary files differ
diff --cc skin/requestpolicy-icon-blocked.png
index 75a4e61,75a4e61..75a4e61
mode 100644,100755..100755
Binary files differ
diff --cc skin/requestpolicy-icon-disabled.png
index 1731917,1731917..1731917
mode 100644,100755..100755
Binary files differ
diff --cc skin/requestpolicy-statusbar-allowed.png
index b57cc75,b57cc75..b57cc75
mode 100644,100755..100755
Binary files differ
diff --cc skin/requestpolicy-statusbar-blocked.png
index 28280ca,28280ca..28280ca
mode 100644,100755..100755
Binary files differ
diff --cc skin/requestpolicy-statusbar-disabled.png
index 983b886,983b886..983b886
mode 100644,100755..100755
Binary files differ
diff --cc skin/toolbarbutton-seamonkey.css
index 0000000,cbbe439..cbbe439
mode 000000,100644..100644
--- a/skin/toolbarbutton-seamonkey.css
+++ b/skin/toolbarbutton-seamonkey.css
diff --cc skin/toolbarbutton.css
index 0000000,370cc00..370cc00
mode 000000,100644..100644
--- a/skin/toolbarbutton.css
+++ b/skin/toolbarbutton.css
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-mozext/requestpolicy.git
More information about the Pkg-mozext-commits
mailing list