Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | let Ci = Components.interfaces; |
michael@0 | 6 | let Cc = Components.classes; |
michael@0 | 7 | let Cu = Components.utils; |
michael@0 | 8 | |
michael@0 | 9 | Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
michael@0 | 10 | Cu.import("resource://gre/modules/Services.jsm"); |
michael@0 | 11 | Cu.import("resource://gre/modules/DownloadUtils.jsm"); |
michael@0 | 12 | Cu.import("resource://gre/modules/NetUtil.jsm"); |
michael@0 | 13 | Cu.import("resource://gre/modules/ForgetAboutSite.jsm"); |
michael@0 | 14 | |
michael@0 | 15 | XPCOMUtils.defineLazyModuleGetter(this, "PluralForm", |
michael@0 | 16 | "resource://gre/modules/PluralForm.jsm"); |
michael@0 | 17 | |
michael@0 | 18 | let gFaviconService = Cc["@mozilla.org/browser/favicon-service;1"]. |
michael@0 | 19 | getService(Ci.nsIFaviconService); |
michael@0 | 20 | |
michael@0 | 21 | let gPlacesDatabase = Cc["@mozilla.org/browser/nav-history-service;1"]. |
michael@0 | 22 | getService(Ci.nsPIPlacesDatabase). |
michael@0 | 23 | DBConnection. |
michael@0 | 24 | clone(true); |
michael@0 | 25 | |
michael@0 | 26 | let gSitesStmt = gPlacesDatabase.createAsyncStatement( |
michael@0 | 27 | "SELECT get_unreversed_host(rev_host) AS host " + |
michael@0 | 28 | "FROM moz_places " + |
michael@0 | 29 | "WHERE rev_host > '.' " + |
michael@0 | 30 | "AND visit_count > 0 " + |
michael@0 | 31 | "GROUP BY rev_host " + |
michael@0 | 32 | "ORDER BY MAX(frecency) DESC " + |
michael@0 | 33 | "LIMIT :limit"); |
michael@0 | 34 | |
michael@0 | 35 | let gVisitStmt = gPlacesDatabase.createAsyncStatement( |
michael@0 | 36 | "SELECT SUM(visit_count) AS count " + |
michael@0 | 37 | "FROM moz_places " + |
michael@0 | 38 | "WHERE rev_host = :rev_host"); |
michael@0 | 39 | |
michael@0 | 40 | /** |
michael@0 | 41 | * Permission types that should be tested with testExactPermission, as opposed |
michael@0 | 42 | * to testPermission. This is based on what consumers use to test these permissions. |
michael@0 | 43 | */ |
michael@0 | 44 | let TEST_EXACT_PERM_TYPES = ["geo", "camera", "microphone"]; |
michael@0 | 45 | |
michael@0 | 46 | /** |
michael@0 | 47 | * Site object represents a single site, uniquely identified by a host. |
michael@0 | 48 | */ |
michael@0 | 49 | function Site(host) { |
michael@0 | 50 | this.host = host; |
michael@0 | 51 | this.listitem = null; |
michael@0 | 52 | |
michael@0 | 53 | this.httpURI = NetUtil.newURI("http://" + this.host); |
michael@0 | 54 | this.httpsURI = NetUtil.newURI("https://" + this.host); |
michael@0 | 55 | } |
michael@0 | 56 | |
michael@0 | 57 | Site.prototype = { |
michael@0 | 58 | /** |
michael@0 | 59 | * Gets the favicon to use for the site. The callback only gets called if |
michael@0 | 60 | * a favicon is found for either the http URI or the https URI. |
michael@0 | 61 | * |
michael@0 | 62 | * @param aCallback |
michael@0 | 63 | * A callback function that takes a favicon image URL as a parameter. |
michael@0 | 64 | */ |
michael@0 | 65 | getFavicon: function Site_getFavicon(aCallback) { |
michael@0 | 66 | function invokeCallback(aFaviconURI) { |
michael@0 | 67 | try { |
michael@0 | 68 | // Use getFaviconLinkForIcon to get image data from the database instead |
michael@0 | 69 | // of using the favicon URI to fetch image data over the network. |
michael@0 | 70 | aCallback(gFaviconService.getFaviconLinkForIcon(aFaviconURI).spec); |
michael@0 | 71 | } catch (e) { |
michael@0 | 72 | Cu.reportError("AboutPermissions: " + e); |
michael@0 | 73 | } |
michael@0 | 74 | } |
michael@0 | 75 | |
michael@0 | 76 | // Try to find favicon for both URIs, but always prefer the https favicon. |
michael@0 | 77 | gFaviconService.getFaviconURLForPage(this.httpsURI, function (aURI) { |
michael@0 | 78 | if (aURI) { |
michael@0 | 79 | invokeCallback(aURI); |
michael@0 | 80 | } else { |
michael@0 | 81 | gFaviconService.getFaviconURLForPage(this.httpURI, function (aURI) { |
michael@0 | 82 | if (aURI) { |
michael@0 | 83 | invokeCallback(aURI); |
michael@0 | 84 | } |
michael@0 | 85 | }); |
michael@0 | 86 | } |
michael@0 | 87 | }.bind(this)); |
michael@0 | 88 | }, |
michael@0 | 89 | |
michael@0 | 90 | /** |
michael@0 | 91 | * Gets the number of history visits for the site. |
michael@0 | 92 | * |
michael@0 | 93 | * @param aCallback |
michael@0 | 94 | * A function that takes the visit count (a number) as a parameter. |
michael@0 | 95 | */ |
michael@0 | 96 | getVisitCount: function Site_getVisitCount(aCallback) { |
michael@0 | 97 | let rev_host = this.host.split("").reverse().join("") + "."; |
michael@0 | 98 | gVisitStmt.params.rev_host = rev_host; |
michael@0 | 99 | gVisitStmt.executeAsync({ |
michael@0 | 100 | handleResult: function(aResults) { |
michael@0 | 101 | let row = aResults.getNextRow(); |
michael@0 | 102 | let count = row.getResultByName("count") || 0; |
michael@0 | 103 | try { |
michael@0 | 104 | aCallback(count); |
michael@0 | 105 | } catch (e) { |
michael@0 | 106 | Cu.reportError("AboutPermissions: " + e); |
michael@0 | 107 | } |
michael@0 | 108 | }, |
michael@0 | 109 | handleError: function(aError) { |
michael@0 | 110 | Cu.reportError("AboutPermissions: " + aError); |
michael@0 | 111 | }, |
michael@0 | 112 | handleCompletion: function(aReason) { |
michael@0 | 113 | } |
michael@0 | 114 | }); |
michael@0 | 115 | }, |
michael@0 | 116 | |
michael@0 | 117 | /** |
michael@0 | 118 | * Gets the permission value stored for a specified permission type. |
michael@0 | 119 | * |
michael@0 | 120 | * @param aType |
michael@0 | 121 | * The permission type string stored in permission manager. |
michael@0 | 122 | * e.g. "cookie", "geo", "indexedDB", "popup", "image" |
michael@0 | 123 | * @param aResultObj |
michael@0 | 124 | * An object that stores the permission value set for aType. |
michael@0 | 125 | * |
michael@0 | 126 | * @return A boolean indicating whether or not a permission is set. |
michael@0 | 127 | */ |
michael@0 | 128 | getPermission: function Site_getPermission(aType, aResultObj) { |
michael@0 | 129 | // Password saving isn't a nsIPermissionManager permission type, so handle |
michael@0 | 130 | // it seperately. |
michael@0 | 131 | if (aType == "password") { |
michael@0 | 132 | aResultObj.value = this.loginSavingEnabled ? |
michael@0 | 133 | Ci.nsIPermissionManager.ALLOW_ACTION : |
michael@0 | 134 | Ci.nsIPermissionManager.DENY_ACTION; |
michael@0 | 135 | return true; |
michael@0 | 136 | } |
michael@0 | 137 | |
michael@0 | 138 | let permissionValue; |
michael@0 | 139 | if (TEST_EXACT_PERM_TYPES.indexOf(aType) == -1) { |
michael@0 | 140 | permissionValue = Services.perms.testPermission(this.httpURI, aType); |
michael@0 | 141 | } else { |
michael@0 | 142 | permissionValue = Services.perms.testExactPermission(this.httpURI, aType); |
michael@0 | 143 | } |
michael@0 | 144 | aResultObj.value = permissionValue; |
michael@0 | 145 | |
michael@0 | 146 | return permissionValue != Ci.nsIPermissionManager.UNKNOWN_ACTION; |
michael@0 | 147 | }, |
michael@0 | 148 | |
michael@0 | 149 | /** |
michael@0 | 150 | * Sets a permission for the site given a permission type and value. |
michael@0 | 151 | * |
michael@0 | 152 | * @param aType |
michael@0 | 153 | * The permission type string stored in permission manager. |
michael@0 | 154 | * e.g. "cookie", "geo", "indexedDB", "popup", "image" |
michael@0 | 155 | * @param aPerm |
michael@0 | 156 | * The permission value to set for the permission type. This should |
michael@0 | 157 | * be one of the constants defined in nsIPermissionManager. |
michael@0 | 158 | */ |
michael@0 | 159 | setPermission: function Site_setPermission(aType, aPerm) { |
michael@0 | 160 | // Password saving isn't a nsIPermissionManager permission type, so handle |
michael@0 | 161 | // it seperately. |
michael@0 | 162 | if (aType == "password") { |
michael@0 | 163 | this.loginSavingEnabled = aPerm == Ci.nsIPermissionManager.ALLOW_ACTION; |
michael@0 | 164 | return; |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | // Using httpURI is kind of bogus, but the permission manager stores the |
michael@0 | 168 | // permission for the host, so the right thing happens in the end. |
michael@0 | 169 | Services.perms.add(this.httpURI, aType, aPerm); |
michael@0 | 170 | }, |
michael@0 | 171 | |
michael@0 | 172 | /** |
michael@0 | 173 | * Clears a user-set permission value for the site given a permission type. |
michael@0 | 174 | * |
michael@0 | 175 | * @param aType |
michael@0 | 176 | * The permission type string stored in permission manager. |
michael@0 | 177 | * e.g. "cookie", "geo", "indexedDB", "popup", "image" |
michael@0 | 178 | */ |
michael@0 | 179 | clearPermission: function Site_clearPermission(aType) { |
michael@0 | 180 | Services.perms.remove(this.host, aType); |
michael@0 | 181 | }, |
michael@0 | 182 | |
michael@0 | 183 | /** |
michael@0 | 184 | * Gets cookies stored for the site. This does not return cookies stored |
michael@0 | 185 | * for the base domain, only the exact hostname stored for the site. |
michael@0 | 186 | * |
michael@0 | 187 | * @return An array of the cookies set for the site. |
michael@0 | 188 | */ |
michael@0 | 189 | get cookies() { |
michael@0 | 190 | let cookies = []; |
michael@0 | 191 | let enumerator = Services.cookies.getCookiesFromHost(this.host); |
michael@0 | 192 | while (enumerator.hasMoreElements()) { |
michael@0 | 193 | let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2); |
michael@0 | 194 | // getCookiesFromHost returns cookies for base domain, but we only want |
michael@0 | 195 | // the cookies for the exact domain. |
michael@0 | 196 | if (cookie.rawHost == this.host) { |
michael@0 | 197 | cookies.push(cookie); |
michael@0 | 198 | } |
michael@0 | 199 | } |
michael@0 | 200 | return cookies; |
michael@0 | 201 | }, |
michael@0 | 202 | |
michael@0 | 203 | /** |
michael@0 | 204 | * Removes a set of specific cookies from the browser. |
michael@0 | 205 | */ |
michael@0 | 206 | clearCookies: function Site_clearCookies() { |
michael@0 | 207 | this.cookies.forEach(function(aCookie) { |
michael@0 | 208 | Services.cookies.remove(aCookie.host, aCookie.name, aCookie.path, false); |
michael@0 | 209 | }); |
michael@0 | 210 | }, |
michael@0 | 211 | |
michael@0 | 212 | /** |
michael@0 | 213 | * Gets logins stored for the site. |
michael@0 | 214 | * |
michael@0 | 215 | * @return An array of the logins stored for the site. |
michael@0 | 216 | */ |
michael@0 | 217 | get logins() { |
michael@0 | 218 | let httpLogins = Services.logins.findLogins({}, this.httpURI.prePath, "", ""); |
michael@0 | 219 | let httpsLogins = Services.logins.findLogins({}, this.httpsURI.prePath, "", ""); |
michael@0 | 220 | return httpLogins.concat(httpsLogins); |
michael@0 | 221 | }, |
michael@0 | 222 | |
michael@0 | 223 | get loginSavingEnabled() { |
michael@0 | 224 | // Only say that login saving is blocked if it is blocked for both http and https. |
michael@0 | 225 | return Services.logins.getLoginSavingEnabled(this.httpURI.prePath) && |
michael@0 | 226 | Services.logins.getLoginSavingEnabled(this.httpsURI.prePath); |
michael@0 | 227 | }, |
michael@0 | 228 | |
michael@0 | 229 | set loginSavingEnabled(isEnabled) { |
michael@0 | 230 | Services.logins.setLoginSavingEnabled(this.httpURI.prePath, isEnabled); |
michael@0 | 231 | Services.logins.setLoginSavingEnabled(this.httpsURI.prePath, isEnabled); |
michael@0 | 232 | }, |
michael@0 | 233 | |
michael@0 | 234 | /** |
michael@0 | 235 | * Removes all data from the browser corresponding to the site. |
michael@0 | 236 | */ |
michael@0 | 237 | forgetSite: function Site_forgetSite() { |
michael@0 | 238 | ForgetAboutSite.removeDataFromDomain(this.host); |
michael@0 | 239 | } |
michael@0 | 240 | } |
michael@0 | 241 | |
michael@0 | 242 | /** |
michael@0 | 243 | * PermissionDefaults object keeps track of default permissions for sites based |
michael@0 | 244 | * on global preferences. |
michael@0 | 245 | * |
michael@0 | 246 | * Inspired by pageinfo/permissions.js |
michael@0 | 247 | */ |
michael@0 | 248 | let PermissionDefaults = { |
michael@0 | 249 | UNKNOWN: Ci.nsIPermissionManager.UNKNOWN_ACTION, // 0 |
michael@0 | 250 | ALLOW: Ci.nsIPermissionManager.ALLOW_ACTION, // 1 |
michael@0 | 251 | DENY: Ci.nsIPermissionManager.DENY_ACTION, // 2 |
michael@0 | 252 | SESSION: Ci.nsICookiePermission.ACCESS_SESSION, // 8 |
michael@0 | 253 | |
michael@0 | 254 | get password() { |
michael@0 | 255 | if (Services.prefs.getBoolPref("signon.rememberSignons")) { |
michael@0 | 256 | return this.ALLOW; |
michael@0 | 257 | } |
michael@0 | 258 | return this.DENY; |
michael@0 | 259 | }, |
michael@0 | 260 | set password(aValue) { |
michael@0 | 261 | let value = (aValue != this.DENY); |
michael@0 | 262 | Services.prefs.setBoolPref("signon.rememberSignons", value); |
michael@0 | 263 | }, |
michael@0 | 264 | |
michael@0 | 265 | // For use with network.cookie.* prefs. |
michael@0 | 266 | COOKIE_ACCEPT: 0, |
michael@0 | 267 | COOKIE_DENY: 2, |
michael@0 | 268 | COOKIE_NORMAL: 0, |
michael@0 | 269 | COOKIE_SESSION: 2, |
michael@0 | 270 | |
michael@0 | 271 | get cookie() { |
michael@0 | 272 | if (Services.prefs.getIntPref("network.cookie.cookieBehavior") == this.COOKIE_DENY) { |
michael@0 | 273 | return this.DENY; |
michael@0 | 274 | } |
michael@0 | 275 | |
michael@0 | 276 | if (Services.prefs.getIntPref("network.cookie.lifetimePolicy") == this.COOKIE_SESSION) { |
michael@0 | 277 | return this.SESSION; |
michael@0 | 278 | } |
michael@0 | 279 | return this.ALLOW; |
michael@0 | 280 | }, |
michael@0 | 281 | set cookie(aValue) { |
michael@0 | 282 | let value = (aValue == this.DENY) ? this.COOKIE_DENY : this.COOKIE_ACCEPT; |
michael@0 | 283 | Services.prefs.setIntPref("network.cookie.cookieBehavior", value); |
michael@0 | 284 | |
michael@0 | 285 | let lifetimeValue = aValue == this.SESSION ? this.COOKIE_SESSION : |
michael@0 | 286 | this.COOKIE_NORMAL; |
michael@0 | 287 | Services.prefs.setIntPref("network.cookie.lifetimePolicy", lifetimeValue); |
michael@0 | 288 | }, |
michael@0 | 289 | |
michael@0 | 290 | get geo() { |
michael@0 | 291 | if (!Services.prefs.getBoolPref("geo.enabled")) { |
michael@0 | 292 | return this.DENY; |
michael@0 | 293 | } |
michael@0 | 294 | // We always ask for permission to share location with a specific site, so |
michael@0 | 295 | // there is no global ALLOW. |
michael@0 | 296 | return this.UNKNOWN; |
michael@0 | 297 | }, |
michael@0 | 298 | set geo(aValue) { |
michael@0 | 299 | let value = (aValue != this.DENY); |
michael@0 | 300 | Services.prefs.setBoolPref("geo.enabled", value); |
michael@0 | 301 | }, |
michael@0 | 302 | |
michael@0 | 303 | get indexedDB() { |
michael@0 | 304 | if (!Services.prefs.getBoolPref("dom.indexedDB.enabled")) { |
michael@0 | 305 | return this.DENY; |
michael@0 | 306 | } |
michael@0 | 307 | // We always ask for permission to enable indexedDB storage for a specific |
michael@0 | 308 | // site, so there is no global ALLOW. |
michael@0 | 309 | return this.UNKNOWN; |
michael@0 | 310 | }, |
michael@0 | 311 | set indexedDB(aValue) { |
michael@0 | 312 | let value = (aValue != this.DENY); |
michael@0 | 313 | Services.prefs.setBoolPref("dom.indexedDB.enabled", value); |
michael@0 | 314 | }, |
michael@0 | 315 | |
michael@0 | 316 | get popup() { |
michael@0 | 317 | if (Services.prefs.getBoolPref("dom.disable_open_during_load")) { |
michael@0 | 318 | return this.DENY; |
michael@0 | 319 | } |
michael@0 | 320 | return this.ALLOW; |
michael@0 | 321 | }, |
michael@0 | 322 | set popup(aValue) { |
michael@0 | 323 | let value = (aValue == this.DENY); |
michael@0 | 324 | Services.prefs.setBoolPref("dom.disable_open_during_load", value); |
michael@0 | 325 | }, |
michael@0 | 326 | |
michael@0 | 327 | get fullscreen() { |
michael@0 | 328 | if (!Services.prefs.getBoolPref("full-screen-api.enabled")) { |
michael@0 | 329 | return this.DENY; |
michael@0 | 330 | } |
michael@0 | 331 | return this.UNKNOWN; |
michael@0 | 332 | }, |
michael@0 | 333 | set fullscreen(aValue) { |
michael@0 | 334 | let value = (aValue != this.DENY); |
michael@0 | 335 | Services.prefs.setBoolPref("full-screen-api.enabled", value); |
michael@0 | 336 | }, |
michael@0 | 337 | |
michael@0 | 338 | get camera() this.UNKNOWN, |
michael@0 | 339 | get microphone() this.UNKNOWN |
michael@0 | 340 | }; |
michael@0 | 341 | |
michael@0 | 342 | /** |
michael@0 | 343 | * AboutPermissions manages the about:permissions page. |
michael@0 | 344 | */ |
michael@0 | 345 | let AboutPermissions = { |
michael@0 | 346 | /** |
michael@0 | 347 | * Number of sites to return from the places database. |
michael@0 | 348 | */ |
michael@0 | 349 | PLACES_SITES_LIMIT: 50, |
michael@0 | 350 | |
michael@0 | 351 | /** |
michael@0 | 352 | * When adding sites to the dom sites-list, divide workload into intervals. |
michael@0 | 353 | */ |
michael@0 | 354 | LIST_BUILD_CHUNK: 5, // interval size |
michael@0 | 355 | LIST_BUILD_DELAY: 100, // delay between intervals |
michael@0 | 356 | |
michael@0 | 357 | /** |
michael@0 | 358 | * Stores a mapping of host strings to Site objects. |
michael@0 | 359 | */ |
michael@0 | 360 | _sites: {}, |
michael@0 | 361 | |
michael@0 | 362 | sitesList: null, |
michael@0 | 363 | _selectedSite: null, |
michael@0 | 364 | |
michael@0 | 365 | /** |
michael@0 | 366 | * For testing, track initializations so we can send notifications |
michael@0 | 367 | */ |
michael@0 | 368 | _initPlacesDone: false, |
michael@0 | 369 | _initServicesDone: false, |
michael@0 | 370 | |
michael@0 | 371 | /** |
michael@0 | 372 | * This reflects the permissions that we expose in the UI. These correspond |
michael@0 | 373 | * to permission type strings in the permission manager, PermissionDefaults, |
michael@0 | 374 | * and element ids in aboutPermissions.xul. |
michael@0 | 375 | * |
michael@0 | 376 | * Potential future additions: "sts/use", "sts/subd" |
michael@0 | 377 | */ |
michael@0 | 378 | _supportedPermissions: ["password", "cookie", "geo", "indexedDB", "popup", |
michael@0 | 379 | "fullscreen", "camera", "microphone"], |
michael@0 | 380 | |
michael@0 | 381 | /** |
michael@0 | 382 | * Permissions that don't have a global "Allow" option. |
michael@0 | 383 | */ |
michael@0 | 384 | _noGlobalAllow: ["geo", "indexedDB", "fullscreen", "camera", "microphone"], |
michael@0 | 385 | |
michael@0 | 386 | /** |
michael@0 | 387 | * Permissions that don't have a global "Deny" option. |
michael@0 | 388 | */ |
michael@0 | 389 | _noGlobalDeny: ["camera", "microphone"], |
michael@0 | 390 | |
michael@0 | 391 | _stringBundle: Services.strings. |
michael@0 | 392 | createBundle("chrome://browser/locale/preferences/aboutPermissions.properties"), |
michael@0 | 393 | |
michael@0 | 394 | /** |
michael@0 | 395 | * Called on page load. |
michael@0 | 396 | */ |
michael@0 | 397 | init: function() { |
michael@0 | 398 | this.sitesList = document.getElementById("sites-list"); |
michael@0 | 399 | |
michael@0 | 400 | this.getSitesFromPlaces(); |
michael@0 | 401 | |
michael@0 | 402 | this.enumerateServicesGenerator = this.getEnumerateServicesGenerator(); |
michael@0 | 403 | setTimeout(this.enumerateServicesDriver.bind(this), this.LIST_BUILD_DELAY); |
michael@0 | 404 | |
michael@0 | 405 | // Attach observers in case data changes while the page is open. |
michael@0 | 406 | Services.prefs.addObserver("signon.rememberSignons", this, false); |
michael@0 | 407 | Services.prefs.addObserver("network.cookie.", this, false); |
michael@0 | 408 | Services.prefs.addObserver("geo.enabled", this, false); |
michael@0 | 409 | Services.prefs.addObserver("dom.indexedDB.enabled", this, false); |
michael@0 | 410 | Services.prefs.addObserver("dom.disable_open_during_load", this, false); |
michael@0 | 411 | Services.prefs.addObserver("full-screen-api.enabled", this, false); |
michael@0 | 412 | |
michael@0 | 413 | Services.obs.addObserver(this, "perm-changed", false); |
michael@0 | 414 | Services.obs.addObserver(this, "passwordmgr-storage-changed", false); |
michael@0 | 415 | Services.obs.addObserver(this, "cookie-changed", false); |
michael@0 | 416 | Services.obs.addObserver(this, "browser:purge-domain-data", false); |
michael@0 | 417 | |
michael@0 | 418 | this._observersInitialized = true; |
michael@0 | 419 | Services.obs.notifyObservers(null, "browser-permissions-preinit", null); |
michael@0 | 420 | }, |
michael@0 | 421 | |
michael@0 | 422 | /** |
michael@0 | 423 | * Called on page unload. |
michael@0 | 424 | */ |
michael@0 | 425 | cleanUp: function() { |
michael@0 | 426 | if (this._observersInitialized) { |
michael@0 | 427 | Services.prefs.removeObserver("signon.rememberSignons", this, false); |
michael@0 | 428 | Services.prefs.removeObserver("network.cookie.", this, false); |
michael@0 | 429 | Services.prefs.removeObserver("geo.enabled", this, false); |
michael@0 | 430 | Services.prefs.removeObserver("dom.indexedDB.enabled", this, false); |
michael@0 | 431 | Services.prefs.removeObserver("dom.disable_open_during_load", this, false); |
michael@0 | 432 | Services.prefs.removeObserver("full-screen-api.enabled", this, false); |
michael@0 | 433 | |
michael@0 | 434 | Services.obs.removeObserver(this, "perm-changed"); |
michael@0 | 435 | Services.obs.removeObserver(this, "passwordmgr-storage-changed"); |
michael@0 | 436 | Services.obs.removeObserver(this, "cookie-changed"); |
michael@0 | 437 | Services.obs.removeObserver(this, "browser:purge-domain-data"); |
michael@0 | 438 | } |
michael@0 | 439 | |
michael@0 | 440 | gSitesStmt.finalize(); |
michael@0 | 441 | gVisitStmt.finalize(); |
michael@0 | 442 | gPlacesDatabase.asyncClose(null); |
michael@0 | 443 | }, |
michael@0 | 444 | |
michael@0 | 445 | observe: function (aSubject, aTopic, aData) { |
michael@0 | 446 | switch(aTopic) { |
michael@0 | 447 | case "perm-changed": |
michael@0 | 448 | // Permissions changes only affect individual sites. |
michael@0 | 449 | if (!this._selectedSite) { |
michael@0 | 450 | break; |
michael@0 | 451 | } |
michael@0 | 452 | // aSubject is null when nsIPermisionManager::removeAll() is called. |
michael@0 | 453 | if (!aSubject) { |
michael@0 | 454 | this._supportedPermissions.forEach(function(aType){ |
michael@0 | 455 | this.updatePermission(aType); |
michael@0 | 456 | }, this); |
michael@0 | 457 | break; |
michael@0 | 458 | } |
michael@0 | 459 | let permission = aSubject.QueryInterface(Ci.nsIPermission); |
michael@0 | 460 | // We can't compare selectedSite.host and permission.host here because |
michael@0 | 461 | // we need to handle the case where a parent domain was changed in a |
michael@0 | 462 | // way that affects the subdomain. |
michael@0 | 463 | if (this._supportedPermissions.indexOf(permission.type) != -1) { |
michael@0 | 464 | this.updatePermission(permission.type); |
michael@0 | 465 | } |
michael@0 | 466 | break; |
michael@0 | 467 | case "nsPref:changed": |
michael@0 | 468 | this._supportedPermissions.forEach(function(aType){ |
michael@0 | 469 | this.updatePermission(aType); |
michael@0 | 470 | }, this); |
michael@0 | 471 | break; |
michael@0 | 472 | case "passwordmgr-storage-changed": |
michael@0 | 473 | this.updatePermission("password"); |
michael@0 | 474 | if (this._selectedSite) { |
michael@0 | 475 | this.updatePasswordsCount(); |
michael@0 | 476 | } |
michael@0 | 477 | break; |
michael@0 | 478 | case "cookie-changed": |
michael@0 | 479 | if (this._selectedSite) { |
michael@0 | 480 | this.updateCookiesCount(); |
michael@0 | 481 | } |
michael@0 | 482 | break; |
michael@0 | 483 | case "browser:purge-domain-data": |
michael@0 | 484 | this.deleteFromSitesList(aData); |
michael@0 | 485 | break; |
michael@0 | 486 | } |
michael@0 | 487 | }, |
michael@0 | 488 | |
michael@0 | 489 | /** |
michael@0 | 490 | * Creates Site objects for the top-frecency sites in the places database and stores |
michael@0 | 491 | * them in _sites. The number of sites created is controlled by PLACES_SITES_LIMIT. |
michael@0 | 492 | */ |
michael@0 | 493 | getSitesFromPlaces: function() { |
michael@0 | 494 | gSitesStmt.params.limit = this.PLACES_SITES_LIMIT; |
michael@0 | 495 | gSitesStmt.executeAsync({ |
michael@0 | 496 | handleResult: function(aResults) { |
michael@0 | 497 | AboutPermissions.startSitesListBatch(); |
michael@0 | 498 | let row; |
michael@0 | 499 | while (row = aResults.getNextRow()) { |
michael@0 | 500 | let host = row.getResultByName("host"); |
michael@0 | 501 | AboutPermissions.addHost(host); |
michael@0 | 502 | } |
michael@0 | 503 | AboutPermissions.endSitesListBatch(); |
michael@0 | 504 | }, |
michael@0 | 505 | handleError: function(aError) { |
michael@0 | 506 | Cu.reportError("AboutPermissions: " + aError); |
michael@0 | 507 | }, |
michael@0 | 508 | handleCompletion: function(aReason) { |
michael@0 | 509 | // Notify oberservers for testing purposes. |
michael@0 | 510 | AboutPermissions._initPlacesDone = true; |
michael@0 | 511 | if (AboutPermissions._initServicesDone) { |
michael@0 | 512 | Services.obs.notifyObservers(null, "browser-permissions-initialized", null); |
michael@0 | 513 | } |
michael@0 | 514 | } |
michael@0 | 515 | }); |
michael@0 | 516 | }, |
michael@0 | 517 | |
michael@0 | 518 | /** |
michael@0 | 519 | * Drives getEnumerateServicesGenerator to work in intervals. |
michael@0 | 520 | */ |
michael@0 | 521 | enumerateServicesDriver: function() { |
michael@0 | 522 | if (this.enumerateServicesGenerator.next()) { |
michael@0 | 523 | // Build top sitesList items faster so that the list never seems sparse |
michael@0 | 524 | let delay = Math.min(this.sitesList.itemCount * 5, this.LIST_BUILD_DELAY); |
michael@0 | 525 | setTimeout(this.enumerateServicesDriver.bind(this), delay); |
michael@0 | 526 | } else { |
michael@0 | 527 | this.enumerateServicesGenerator.close(); |
michael@0 | 528 | this._initServicesDone = true; |
michael@0 | 529 | if (this._initPlacesDone) { |
michael@0 | 530 | Services.obs.notifyObservers(null, "browser-permissions-initialized", null); |
michael@0 | 531 | } |
michael@0 | 532 | } |
michael@0 | 533 | }, |
michael@0 | 534 | |
michael@0 | 535 | /** |
michael@0 | 536 | * Finds sites that have non-default permissions and creates Site objects for |
michael@0 | 537 | * them if they are not already stored in _sites. |
michael@0 | 538 | */ |
michael@0 | 539 | getEnumerateServicesGenerator: function() { |
michael@0 | 540 | let itemCnt = 1; |
michael@0 | 541 | |
michael@0 | 542 | let logins = Services.logins.getAllLogins(); |
michael@0 | 543 | logins.forEach(function(aLogin) { |
michael@0 | 544 | if (itemCnt % this.LIST_BUILD_CHUNK == 0) { |
michael@0 | 545 | yield true; |
michael@0 | 546 | } |
michael@0 | 547 | try { |
michael@0 | 548 | // aLogin.hostname is a string in origin URL format (e.g. "http://foo.com") |
michael@0 | 549 | let uri = NetUtil.newURI(aLogin.hostname); |
michael@0 | 550 | this.addHost(uri.host); |
michael@0 | 551 | } catch (e) { |
michael@0 | 552 | // newURI will throw for add-ons logins stored in chrome:// URIs |
michael@0 | 553 | } |
michael@0 | 554 | itemCnt++; |
michael@0 | 555 | }, this); |
michael@0 | 556 | |
michael@0 | 557 | let disabledHosts = Services.logins.getAllDisabledHosts(); |
michael@0 | 558 | disabledHosts.forEach(function(aHostname) { |
michael@0 | 559 | if (itemCnt % this.LIST_BUILD_CHUNK == 0) { |
michael@0 | 560 | yield true; |
michael@0 | 561 | } |
michael@0 | 562 | try { |
michael@0 | 563 | // aHostname is a string in origin URL format (e.g. "http://foo.com") |
michael@0 | 564 | let uri = NetUtil.newURI(aHostname); |
michael@0 | 565 | this.addHost(uri.host); |
michael@0 | 566 | } catch (e) { |
michael@0 | 567 | // newURI will throw for add-ons logins stored in chrome:// URIs |
michael@0 | 568 | } |
michael@0 | 569 | itemCnt++; |
michael@0 | 570 | }, this); |
michael@0 | 571 | |
michael@0 | 572 | let (enumerator = Services.perms.enumerator) { |
michael@0 | 573 | while (enumerator.hasMoreElements()) { |
michael@0 | 574 | if (itemCnt % this.LIST_BUILD_CHUNK == 0) { |
michael@0 | 575 | yield true; |
michael@0 | 576 | } |
michael@0 | 577 | let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission); |
michael@0 | 578 | // Only include sites with exceptions set for supported permission types. |
michael@0 | 579 | if (this._supportedPermissions.indexOf(permission.type) != -1) { |
michael@0 | 580 | this.addHost(permission.host); |
michael@0 | 581 | } |
michael@0 | 582 | itemCnt++; |
michael@0 | 583 | } |
michael@0 | 584 | } |
michael@0 | 585 | |
michael@0 | 586 | yield false; |
michael@0 | 587 | }, |
michael@0 | 588 | |
michael@0 | 589 | /** |
michael@0 | 590 | * Creates a new Site and adds it to _sites if it's not already there. |
michael@0 | 591 | * |
michael@0 | 592 | * @param aHost |
michael@0 | 593 | * A host string. |
michael@0 | 594 | */ |
michael@0 | 595 | addHost: function(aHost) { |
michael@0 | 596 | if (aHost in this._sites) { |
michael@0 | 597 | return; |
michael@0 | 598 | } |
michael@0 | 599 | let site = new Site(aHost); |
michael@0 | 600 | this._sites[aHost] = site; |
michael@0 | 601 | this.addToSitesList(site); |
michael@0 | 602 | }, |
michael@0 | 603 | |
michael@0 | 604 | /** |
michael@0 | 605 | * Populates sites-list richlistbox with data from Site object. |
michael@0 | 606 | * |
michael@0 | 607 | * @param aSite |
michael@0 | 608 | * A Site object. |
michael@0 | 609 | */ |
michael@0 | 610 | addToSitesList: function(aSite) { |
michael@0 | 611 | let item = document.createElement("richlistitem"); |
michael@0 | 612 | item.setAttribute("class", "site"); |
michael@0 | 613 | item.setAttribute("value", aSite.host); |
michael@0 | 614 | |
michael@0 | 615 | aSite.getFavicon(function(aURL) { |
michael@0 | 616 | item.setAttribute("favicon", aURL); |
michael@0 | 617 | }); |
michael@0 | 618 | aSite.listitem = item; |
michael@0 | 619 | |
michael@0 | 620 | // Make sure to only display relevant items when list is filtered |
michael@0 | 621 | let filterValue = document.getElementById("sites-filter").value.toLowerCase(); |
michael@0 | 622 | item.collapsed = aSite.host.toLowerCase().indexOf(filterValue) == -1; |
michael@0 | 623 | |
michael@0 | 624 | (this._listFragment || this.sitesList).appendChild(item); |
michael@0 | 625 | }, |
michael@0 | 626 | |
michael@0 | 627 | startSitesListBatch: function () { |
michael@0 | 628 | if (!this._listFragment) |
michael@0 | 629 | this._listFragment = document.createDocumentFragment(); |
michael@0 | 630 | }, |
michael@0 | 631 | |
michael@0 | 632 | endSitesListBatch: function () { |
michael@0 | 633 | if (this._listFragment) { |
michael@0 | 634 | this.sitesList.appendChild(this._listFragment); |
michael@0 | 635 | this._listFragment = null; |
michael@0 | 636 | } |
michael@0 | 637 | }, |
michael@0 | 638 | |
michael@0 | 639 | /** |
michael@0 | 640 | * Hides sites in richlistbox based on search text in sites-filter textbox. |
michael@0 | 641 | */ |
michael@0 | 642 | filterSitesList: function() { |
michael@0 | 643 | let siteItems = this.sitesList.children; |
michael@0 | 644 | let filterValue = document.getElementById("sites-filter").value.toLowerCase(); |
michael@0 | 645 | |
michael@0 | 646 | if (filterValue == "") { |
michael@0 | 647 | for (let i = 0; i < siteItems.length; i++) { |
michael@0 | 648 | siteItems[i].collapsed = false; |
michael@0 | 649 | } |
michael@0 | 650 | return; |
michael@0 | 651 | } |
michael@0 | 652 | |
michael@0 | 653 | for (let i = 0; i < siteItems.length; i++) { |
michael@0 | 654 | let siteValue = siteItems[i].value.toLowerCase(); |
michael@0 | 655 | siteItems[i].collapsed = siteValue.indexOf(filterValue) == -1; |
michael@0 | 656 | } |
michael@0 | 657 | }, |
michael@0 | 658 | |
michael@0 | 659 | /** |
michael@0 | 660 | * Removes all evidence of the selected site. The "forget this site" observer |
michael@0 | 661 | * will call deleteFromSitesList to update the UI. |
michael@0 | 662 | */ |
michael@0 | 663 | forgetSite: function() { |
michael@0 | 664 | this._selectedSite.forgetSite(); |
michael@0 | 665 | }, |
michael@0 | 666 | |
michael@0 | 667 | /** |
michael@0 | 668 | * Deletes sites for a host and all of its sub-domains. Removes these sites |
michael@0 | 669 | * from _sites and removes their corresponding elements from the DOM. |
michael@0 | 670 | * |
michael@0 | 671 | * @param aHost |
michael@0 | 672 | * The host string corresponding to the site to delete. |
michael@0 | 673 | */ |
michael@0 | 674 | deleteFromSitesList: function(aHost) { |
michael@0 | 675 | for each (let site in this._sites) { |
michael@0 | 676 | if (site.host.hasRootDomain(aHost)) { |
michael@0 | 677 | if (site == this._selectedSite) { |
michael@0 | 678 | // Replace site-specific interface with "All Sites" interface. |
michael@0 | 679 | this.sitesList.selectedItem = document.getElementById("all-sites-item"); |
michael@0 | 680 | } |
michael@0 | 681 | |
michael@0 | 682 | this.sitesList.removeChild(site.listitem); |
michael@0 | 683 | delete this._sites[site.host]; |
michael@0 | 684 | } |
michael@0 | 685 | } |
michael@0 | 686 | }, |
michael@0 | 687 | |
michael@0 | 688 | /** |
michael@0 | 689 | * Shows interface for managing site-specific permissions. |
michael@0 | 690 | */ |
michael@0 | 691 | onSitesListSelect: function(event) { |
michael@0 | 692 | if (event.target.selectedItem.id == "all-sites-item") { |
michael@0 | 693 | // Clear the header label value from the previously selected site. |
michael@0 | 694 | document.getElementById("site-label").value = ""; |
michael@0 | 695 | this.manageDefaultPermissions(); |
michael@0 | 696 | return; |
michael@0 | 697 | } |
michael@0 | 698 | |
michael@0 | 699 | let host = event.target.value; |
michael@0 | 700 | let site = this._selectedSite = this._sites[host]; |
michael@0 | 701 | document.getElementById("site-label").value = host; |
michael@0 | 702 | document.getElementById("header-deck").selectedPanel = |
michael@0 | 703 | document.getElementById("site-header"); |
michael@0 | 704 | |
michael@0 | 705 | this.updateVisitCount(); |
michael@0 | 706 | this.updatePermissionsBox(); |
michael@0 | 707 | }, |
michael@0 | 708 | |
michael@0 | 709 | /** |
michael@0 | 710 | * Shows interface for managing default permissions. This corresponds to |
michael@0 | 711 | * the "All Sites" list item. |
michael@0 | 712 | */ |
michael@0 | 713 | manageDefaultPermissions: function() { |
michael@0 | 714 | this._selectedSite = null; |
michael@0 | 715 | |
michael@0 | 716 | document.getElementById("header-deck").selectedPanel = |
michael@0 | 717 | document.getElementById("defaults-header"); |
michael@0 | 718 | |
michael@0 | 719 | this.updatePermissionsBox(); |
michael@0 | 720 | }, |
michael@0 | 721 | |
michael@0 | 722 | /** |
michael@0 | 723 | * Updates permissions interface based on selected site. |
michael@0 | 724 | */ |
michael@0 | 725 | updatePermissionsBox: function() { |
michael@0 | 726 | this._supportedPermissions.forEach(function(aType){ |
michael@0 | 727 | this.updatePermission(aType); |
michael@0 | 728 | }, this); |
michael@0 | 729 | |
michael@0 | 730 | this.updatePasswordsCount(); |
michael@0 | 731 | this.updateCookiesCount(); |
michael@0 | 732 | }, |
michael@0 | 733 | |
michael@0 | 734 | /** |
michael@0 | 735 | * Sets menulist for a given permission to the correct state, based on the |
michael@0 | 736 | * stored permission. |
michael@0 | 737 | * |
michael@0 | 738 | * @param aType |
michael@0 | 739 | * The permission type string stored in permission manager. |
michael@0 | 740 | * e.g. "cookie", "geo", "indexedDB", "popup", "image" |
michael@0 | 741 | */ |
michael@0 | 742 | updatePermission: function(aType) { |
michael@0 | 743 | let allowItem = document.getElementById(aType + "-" + PermissionDefaults.ALLOW); |
michael@0 | 744 | allowItem.hidden = !this._selectedSite && |
michael@0 | 745 | this._noGlobalAllow.indexOf(aType) != -1; |
michael@0 | 746 | let denyItem = document.getElementById(aType + "-" + PermissionDefaults.DENY); |
michael@0 | 747 | denyItem.hidden = !this._selectedSite && |
michael@0 | 748 | this._noGlobalDeny.indexOf(aType) != -1; |
michael@0 | 749 | |
michael@0 | 750 | let permissionMenulist = document.getElementById(aType + "-menulist"); |
michael@0 | 751 | let permissionValue; |
michael@0 | 752 | if (!this._selectedSite) { |
michael@0 | 753 | // If there is no selected site, we are updating the default permissions interface. |
michael@0 | 754 | permissionValue = PermissionDefaults[aType]; |
michael@0 | 755 | if (aType == "cookie") |
michael@0 | 756 | // cookie-9 corresponds to ALLOW_FIRST_PARTY_ONLY, which is reserved |
michael@0 | 757 | // for site-specific preferences only. |
michael@0 | 758 | document.getElementById("cookie-9").hidden = true; |
michael@0 | 759 | } else { |
michael@0 | 760 | if (aType == "cookie") |
michael@0 | 761 | document.getElementById("cookie-9").hidden = false; |
michael@0 | 762 | let result = {}; |
michael@0 | 763 | permissionValue = this._selectedSite.getPermission(aType, result) ? |
michael@0 | 764 | result.value : PermissionDefaults[aType]; |
michael@0 | 765 | } |
michael@0 | 766 | |
michael@0 | 767 | permissionMenulist.selectedItem = document.getElementById(aType + "-" + permissionValue); |
michael@0 | 768 | }, |
michael@0 | 769 | |
michael@0 | 770 | onPermissionCommand: function(event) { |
michael@0 | 771 | let permissionType = event.currentTarget.getAttribute("type"); |
michael@0 | 772 | let permissionValue = event.target.value; |
michael@0 | 773 | |
michael@0 | 774 | if (!this._selectedSite) { |
michael@0 | 775 | // If there is no selected site, we are setting the default permission. |
michael@0 | 776 | PermissionDefaults[permissionType] = permissionValue; |
michael@0 | 777 | } else { |
michael@0 | 778 | this._selectedSite.setPermission(permissionType, permissionValue); |
michael@0 | 779 | } |
michael@0 | 780 | }, |
michael@0 | 781 | |
michael@0 | 782 | updateVisitCount: function() { |
michael@0 | 783 | this._selectedSite.getVisitCount(function(aCount) { |
michael@0 | 784 | let visitForm = AboutPermissions._stringBundle.GetStringFromName("visitCount"); |
michael@0 | 785 | let visitLabel = PluralForm.get(aCount, visitForm) |
michael@0 | 786 | .replace("#1", aCount); |
michael@0 | 787 | document.getElementById("site-visit-count").value = visitLabel; |
michael@0 | 788 | }); |
michael@0 | 789 | }, |
michael@0 | 790 | |
michael@0 | 791 | updatePasswordsCount: function() { |
michael@0 | 792 | if (!this._selectedSite) { |
michael@0 | 793 | document.getElementById("passwords-count").hidden = true; |
michael@0 | 794 | document.getElementById("passwords-manage-all-button").hidden = false; |
michael@0 | 795 | return; |
michael@0 | 796 | } |
michael@0 | 797 | |
michael@0 | 798 | let passwordsCount = this._selectedSite.logins.length; |
michael@0 | 799 | let passwordsForm = this._stringBundle.GetStringFromName("passwordsCount"); |
michael@0 | 800 | let passwordsLabel = PluralForm.get(passwordsCount, passwordsForm) |
michael@0 | 801 | .replace("#1", passwordsCount); |
michael@0 | 802 | |
michael@0 | 803 | document.getElementById("passwords-label").value = passwordsLabel; |
michael@0 | 804 | document.getElementById("passwords-manage-button").disabled = (passwordsCount < 1); |
michael@0 | 805 | document.getElementById("passwords-manage-all-button").hidden = true; |
michael@0 | 806 | document.getElementById("passwords-count").hidden = false; |
michael@0 | 807 | }, |
michael@0 | 808 | |
michael@0 | 809 | /** |
michael@0 | 810 | * Opens password manager dialog. |
michael@0 | 811 | */ |
michael@0 | 812 | managePasswords: function() { |
michael@0 | 813 | let selectedHost = ""; |
michael@0 | 814 | if (this._selectedSite) { |
michael@0 | 815 | selectedHost = this._selectedSite.host; |
michael@0 | 816 | } |
michael@0 | 817 | |
michael@0 | 818 | let win = Services.wm.getMostRecentWindow("Toolkit:PasswordManager"); |
michael@0 | 819 | if (win) { |
michael@0 | 820 | win.setFilter(selectedHost); |
michael@0 | 821 | win.focus(); |
michael@0 | 822 | } else { |
michael@0 | 823 | window.openDialog("chrome://passwordmgr/content/passwordManager.xul", |
michael@0 | 824 | "Toolkit:PasswordManager", "", {filterString : selectedHost}); |
michael@0 | 825 | } |
michael@0 | 826 | }, |
michael@0 | 827 | |
michael@0 | 828 | updateCookiesCount: function() { |
michael@0 | 829 | if (!this._selectedSite) { |
michael@0 | 830 | document.getElementById("cookies-count").hidden = true; |
michael@0 | 831 | document.getElementById("cookies-clear-all-button").hidden = false; |
michael@0 | 832 | document.getElementById("cookies-manage-all-button").hidden = false; |
michael@0 | 833 | return; |
michael@0 | 834 | } |
michael@0 | 835 | |
michael@0 | 836 | let cookiesCount = this._selectedSite.cookies.length; |
michael@0 | 837 | let cookiesForm = this._stringBundle.GetStringFromName("cookiesCount"); |
michael@0 | 838 | let cookiesLabel = PluralForm.get(cookiesCount, cookiesForm) |
michael@0 | 839 | .replace("#1", cookiesCount); |
michael@0 | 840 | |
michael@0 | 841 | document.getElementById("cookies-label").value = cookiesLabel; |
michael@0 | 842 | document.getElementById("cookies-clear-button").disabled = (cookiesCount < 1); |
michael@0 | 843 | document.getElementById("cookies-manage-button").disabled = (cookiesCount < 1); |
michael@0 | 844 | document.getElementById("cookies-clear-all-button").hidden = true; |
michael@0 | 845 | document.getElementById("cookies-manage-all-button").hidden = true; |
michael@0 | 846 | document.getElementById("cookies-count").hidden = false; |
michael@0 | 847 | }, |
michael@0 | 848 | |
michael@0 | 849 | /** |
michael@0 | 850 | * Clears cookies for the selected site. |
michael@0 | 851 | */ |
michael@0 | 852 | clearCookies: function() { |
michael@0 | 853 | if (!this._selectedSite) { |
michael@0 | 854 | return; |
michael@0 | 855 | } |
michael@0 | 856 | let site = this._selectedSite; |
michael@0 | 857 | site.clearCookies(site.cookies); |
michael@0 | 858 | this.updateCookiesCount(); |
michael@0 | 859 | }, |
michael@0 | 860 | |
michael@0 | 861 | /** |
michael@0 | 862 | * Opens cookie manager dialog. |
michael@0 | 863 | */ |
michael@0 | 864 | manageCookies: function() { |
michael@0 | 865 | let selectedHost = ""; |
michael@0 | 866 | if (this._selectedSite) { |
michael@0 | 867 | selectedHost = this._selectedSite.host; |
michael@0 | 868 | } |
michael@0 | 869 | |
michael@0 | 870 | let win = Services.wm.getMostRecentWindow("Browser:Cookies"); |
michael@0 | 871 | if (win) { |
michael@0 | 872 | win.gCookiesWindow.setFilter(selectedHost); |
michael@0 | 873 | win.focus(); |
michael@0 | 874 | } else { |
michael@0 | 875 | window.openDialog("chrome://browser/content/preferences/cookies.xul", |
michael@0 | 876 | "Browser:Cookies", "", {filterString : selectedHost}); |
michael@0 | 877 | } |
michael@0 | 878 | } |
michael@0 | 879 | } |
michael@0 | 880 | |
michael@0 | 881 | // See nsPrivateBrowsingService.js |
michael@0 | 882 | String.prototype.hasRootDomain = function hasRootDomain(aDomain) { |
michael@0 | 883 | let index = this.indexOf(aDomain); |
michael@0 | 884 | if (index == -1) |
michael@0 | 885 | return false; |
michael@0 | 886 | |
michael@0 | 887 | if (this == aDomain) |
michael@0 | 888 | return true; |
michael@0 | 889 | |
michael@0 | 890 | let prevChar = this[index - 1]; |
michael@0 | 891 | return (index == (this.length - aDomain.length)) && |
michael@0 | 892 | (prevChar == "." || prevChar == "/"); |
michael@0 | 893 | } |