1/* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5var EXPORTED_SYMBOLS = [ "SitePermissions" ]; 6 7const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); 8const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); 9 10var gStringBundle = 11 Services.strings.createBundle("chrome://communicator/locale/sitePermissions.properties"); 12 13/** 14 * A helper module to manage temporarily blocked permissions. 15 * 16 * Permissions are keyed by browser, so methods take a Browser 17 * element to identify the corresponding permission set. 18 * 19 * This uses a WeakMap to key browsers, so that entries are 20 * automatically cleared once the browser stops existing 21 * (once there are no other references to the browser object); 22 */ 23var TemporaryBlockedPermissions = { 24 // This is a three level deep map with the following structure: 25 // 26 // Browser => { 27 // <prePath>: { 28 // <permissionID>: {Number} <timeStamp> 29 // } 30 // } 31 // 32 // Only the top level browser elements are stored via WeakMap. The WeakMap 33 // value is an object with URI prePaths as keys. The keys of that object 34 // are ids that identify permissions that were set for the specific URI. 35 // The final value is an object containing the timestamp of when the permission 36 // was set (in order to invalidate after a certain amount of time has passed). 37 _stateByBrowser: new WeakMap(), 38 39 // Private helper method that bundles some shared behavior for 40 // get() and getAll(), e.g. deleting permissions when they have expired. 41 _get(entry, prePath, id, timeStamp) { 42 if (timeStamp == null) { 43 delete entry[prePath][id]; 44 return null; 45 } 46 if (timeStamp + SitePermissions.temporaryPermissionExpireTime < Date.now()) { 47 delete entry[prePath][id]; 48 return null; 49 } 50 return {id, state: SitePermissions.BLOCK, scope: SitePermissions.SCOPE_TEMPORARY}; 51 }, 52 53 // Sets a new permission for the specified browser. 54 set(browser, id) { 55 if (!browser) { 56 return; 57 } 58 if (!this._stateByBrowser.has(browser)) { 59 this._stateByBrowser.set(browser, {}); 60 } 61 let entry = this._stateByBrowser.get(browser); 62 let prePath = browser.currentURI.prePath; 63 if (!entry[prePath]) { 64 entry[prePath] = {}; 65 } 66 entry[prePath][id] = Date.now(); 67 }, 68 69 // Removes a permission with the specified id for the specified browser. 70 remove(browser, id) { 71 if (!browser) { 72 return; 73 } 74 let entry = this._stateByBrowser.get(browser); 75 let prePath = browser.currentURI.prePath; 76 if (entry && entry[prePath]) { 77 delete entry[prePath][id]; 78 } 79 }, 80 81 // Gets a permission with the specified id for the specified browser. 82 get(browser, id) { 83 if (!browser || !browser.currentURI) { 84 return null; 85 } 86 let entry = this._stateByBrowser.get(browser); 87 let prePath = browser.currentURI.prePath; 88 if (entry && entry[prePath]) { 89 let permission = entry[prePath][id]; 90 return this._get(entry, prePath, id, permission); 91 } 92 return null; 93 }, 94 95 // Gets all permissions for the specified browser. 96 // Note that only permissions that apply to the current URI 97 // of the passed browser element will be returned. 98 getAll(browser) { 99 let permissions = []; 100 let entry = this._stateByBrowser.get(browser); 101 let prePath = browser.currentURI.prePath; 102 if (entry && entry[prePath]) { 103 let timeStamps = entry[prePath]; 104 for (let id of Object.keys(timeStamps)) { 105 let permission = this._get(entry, prePath, id, timeStamps[id]); 106 // _get() returns null when the permission has expired. 107 if (permission) { 108 permissions.push(permission); 109 } 110 } 111 } 112 return permissions; 113 }, 114 115 // Clears all permissions for the specified browser. 116 // Unlike other methods, this does NOT clear only for 117 // the currentURI but the whole browser state. 118 clear(browser) { 119 this._stateByBrowser.delete(browser); 120 }, 121 122 // Copies the temporary permission state of one browser 123 // into a new entry for the other browser. 124 copy(browser, newBrowser) { 125 let entry = this._stateByBrowser.get(browser); 126 if (entry) { 127 this._stateByBrowser.set(newBrowser, entry); 128 } 129 }, 130}; 131 132/** 133 * A module to manage permanent and temporary permissions 134 * by URI and browser. 135 * 136 * Some methods have the side effect of dispatching a "PermissionStateChange" 137 * event on changes to temporary permissions, as mentioned in the respective docs. 138 */ 139var SitePermissions = { 140 // Permission states. 141 // PROMPT_HIDE state is only used to show the "Hide Prompt" state in the 142 // identity panel for the "plugin:flash" permission and not in pageinfo. 143 UNKNOWN: Services.perms.UNKNOWN_ACTION, 144 ALLOW: Services.perms.ALLOW_ACTION, 145 BLOCK: Services.perms.DENY_ACTION, 146 PROMPT: Services.perms.PROMPT_ACTION, 147 ALLOW_COOKIES_FOR_SESSION: Ci.nsICookiePermission.ACCESS_SESSION, 148 PROMPT_HIDE: Ci.nsIObjectLoadingContent.PLUGIN_PERMISSION_PROMPT_ACTION_QUIET, 149 150 // Permission scopes. 151 SCOPE_REQUEST: "{SitePermissions.SCOPE_REQUEST}", 152 SCOPE_TEMPORARY: "{SitePermissions.SCOPE_TEMPORARY}", 153 SCOPE_SESSION: "{SitePermissions.SCOPE_SESSION}", 154 SCOPE_PERSISTENT: "{SitePermissions.SCOPE_PERSISTENT}", 155 156 _defaultPrefBranch: Services.prefs.getBranch("permissions.default."), 157 158 /** 159 * Deprecated! Please use getAllByPrincipal(principal) instead. 160 * Gets all custom permissions for a given URI. 161 * Install addon permission is excluded, check bug 1303108. 162 * 163 * @return {Array} a list of objects with the keys: 164 * - id: the permissionId of the permission 165 * - scope: the scope of the permission (e.g. SitePermissions.SCOPE_TEMPORARY) 166 * - state: a constant representing the current permission state 167 * (e.g. SitePermissions.ALLOW) 168 */ 169 getAllByURI(uri) { 170 let principal = uri ? Services.scriptSecurityManager.createCodebasePrincipal(uri, {}) : null; 171 return this.getAllByPrincipal(principal); 172 }, 173 174 /** 175 * Gets all custom permissions for a given principal. 176 * Install addon permission is excluded, check bug 1303108. 177 * 178 * @return {Array} a list of objects with the keys: 179 * - id: the permissionId of the permission 180 * - scope: the scope of the permission (e.g. SitePermissions.SCOPE_TEMPORARY) 181 * - state: a constant representing the current permission state 182 * (e.g. SitePermissions.ALLOW) 183 */ 184 getAllByPrincipal(principal) { 185 let result = []; 186 if (!this.isSupportedPrincipal(principal)) { 187 return result; 188 } 189 190 let permissions = Services.perms.getAllForPrincipal(principal); 191 while (permissions.hasMoreElements()) { 192 let permission = permissions.getNext(); 193 194 // filter out unknown permissions 195 if (gPermissionObject[permission.type]) { 196 // XXX Bug 1303108 - Control Center should only show non-default permissions 197 if (permission.type == "install") { 198 continue; 199 } 200 let scope = this.SCOPE_PERSISTENT; 201 if (permission.expireType == Services.perms.EXPIRE_SESSION) { 202 scope = this.SCOPE_SESSION; 203 } 204 result.push({ 205 id: permission.type, 206 scope, 207 state: permission.capability, 208 }); 209 } 210 } 211 212 return result; 213 }, 214 215 /** 216 * Returns all custom permissions for a given browser. 217 * 218 * To receive a more detailed, albeit less performant listing see 219 * SitePermissions.getAllPermissionDetailsForBrowser(). 220 * 221 * @param {Browser} browser 222 * The browser to fetch permission for. 223 * 224 * @return {Array} a list of objects with the keys: 225 * - id: the permissionId of the permission 226 * - state: a constant representing the current permission state 227 * (e.g. SitePermissions.ALLOW) 228 * - scope: a constant representing how long the permission will 229 * be kept. 230 */ 231 getAllForBrowser(browser) { 232 let permissions = {}; 233 234 for (let permission of TemporaryBlockedPermissions.getAll(browser)) { 235 permission.scope = this.SCOPE_TEMPORARY; 236 permissions[permission.id] = permission; 237 } 238 239 for (let permission of this.getAllByPrincipal(browser.contentPrincipal)) { 240 permissions[permission.id] = permission; 241 } 242 243 return Object.values(permissions); 244 }, 245 246 /** 247 * Returns a list of objects with detailed information on all permissions 248 * that are currently set for the given browser. 249 * 250 * @param {Browser} browser 251 * The browser to fetch permission for. 252 * 253 * @return {Array<Object>} a list of objects with the keys: 254 * - id: the permissionID of the permission 255 * - state: a constant representing the current permission state 256 * (e.g. SitePermissions.ALLOW) 257 * - scope: a constant representing how long the permission will 258 * be kept. 259 * - label: the localized label 260 */ 261 getAllPermissionDetailsForBrowser(browser) { 262 return this.getAllForBrowser(browser).map(({id, scope, state}) => 263 ({id, scope, state, label: this.getPermissionLabel(id)})); 264 }, 265 266 /** 267 * Deprecated! Please use isSupportedPrincipal(principal) instead. 268 * Checks whether a UI for managing permissions should be exposed for a given 269 * URI. 270 * 271 * @param {nsIURI} uri 272 * The URI to check. 273 * 274 * @return {boolean} if the URI is supported. 275 */ 276 isSupportedURI(uri) { 277 return uri && ["file", "http", "https", "moz-extension"].includes(uri.scheme); 278 }, 279 280 /** 281 * Checks whether a UI for managing permissions should be exposed for a given 282 * principal. 283 * 284 * @param {nsIPrincipal} principal 285 * The principal to check. 286 * 287 * @return {boolean} if the principal is supported. 288 */ 289 isSupportedPrincipal(principal) { 290 return principal && principal.URI && 291 ["file", "http", "https", "moz-extension"].includes(principal.URI.scheme); 292 }, 293 294 /** 295 * Gets an array of all permission IDs. 296 * 297 * @return {Array<String>} an array of all permission IDs. 298 */ 299 listPermissions() { 300 return Object.keys(gPermissionObject); 301 }, 302 303 /** 304 * Returns an array of permission states to be exposed to the user for a 305 * permission with the given ID. 306 * 307 * @param {string} permissionID 308 * The ID to get permission states for. 309 * 310 * @return {Array<SitePermissions state>} an array of all permission states. 311 */ 312 getAvailableStates(permissionID) { 313 if (permissionID in gPermissionObject && 314 gPermissionObject[permissionID].states) 315 return gPermissionObject[permissionID].states; 316 317 /* Since the permissions we are dealing with have adopted the convention 318 * of treating UNKNOWN == PROMPT, we only include one of either UNKNOWN 319 * or PROMPT in this list, to avoid duplicating states. */ 320 if (this.getDefault(permissionID) == this.UNKNOWN) 321 return [ SitePermissions.UNKNOWN, SitePermissions.ALLOW, SitePermissions.BLOCK ]; 322 323 return [ SitePermissions.PROMPT, SitePermissions.ALLOW, SitePermissions.BLOCK ]; 324 }, 325 326 /** 327 * Returns the default state of a particular permission. 328 * 329 * @param {string} permissionID 330 * The ID to get the default for. 331 * 332 * @return {SitePermissions.state} the default state. 333 */ 334 getDefault(permissionID) { 335 // If the permission has custom logic for getting its default value, 336 // try that first. 337 if (permissionID in gPermissionObject && 338 gPermissionObject[permissionID].getDefault) 339 return gPermissionObject[permissionID].getDefault(); 340 341 // Otherwise try to get the default preference for that permission. 342 return this._defaultPrefBranch.getIntPref(permissionID, this.UNKNOWN); 343 }, 344 345 /** 346 * Set the default state of a particular permission. 347 * 348 * @param {string} permissionID 349 * The ID to set the default for. 350 * 351 * @param {string} state 352 * The state to set. 353 */ 354 setDefault(permissionID, state) { 355 if (permissionID in gPermissionObject && 356 gPermissionObject[permissionID].setDefault) { 357 return gPermissionObject[permissionID].setDefault(state); 358 } 359 let key = "permissions.default." + permissionID; 360 return Services.prefs.setIntPref(key, state); 361 }, 362 /** 363 * Returns the state and scope of a particular permission for a given URI. 364 * 365 * This method will NOT dispatch a "PermissionStateChange" event on the specified 366 * browser if a temporary permission was removed because it has expired. 367 * 368 * @param {nsIURI} uri 369 * The URI to check. 370 * @param {String} permissionID 371 * The id of the permission. 372 * @param {Browser} browser (optional) 373 * The browser object to check for temporary permissions. 374 * 375 * @return {Object} an object with the keys: 376 * - state: The current state of the permission 377 * (e.g. SitePermissions.ALLOW) 378 * - scope: The scope of the permission 379 * (e.g. SitePermissions.SCOPE_PERSISTENT) 380 */ 381 get(uri, permissionID, browser) { 382 let principal = uri ? Services.scriptSecurityManager.createCodebasePrincipal(uri, {}) : null; 383 return this.getForPrincipal(principal, permissionID, browser); 384 }, 385 386 /** 387 * Returns the state and scope of a particular permission for a given 388 * principal. 389 * 390 * This method will NOT dispatch a "PermissionStateChange" event on the 391 * specified browser if a temporary permission was removed because it has 392 * expired. 393 * 394 * @param {nsIPrincipal} principal 395 * The principal to check. 396 * @param {String} permissionID 397 * The id of the permission. 398 * @param {Browser} browser (optional) 399 * The browser object to check for temporary permissions. 400 * 401 * @return {Object} an object with the keys: 402 * - state: The current state of the permission 403 * (e.g. SitePermissions.ALLOW) 404 * - scope: The scope of the permission 405 * (e.g. SitePermissions.SCOPE_PERSISTENT) 406 */ 407 getForPrincipal(principal, permissionID, browser) { 408 let defaultState = this.getDefault(permissionID); 409 let result = { state: defaultState, scope: this.SCOPE_PERSISTENT }; 410 if (this.isSupportedPrincipal(principal)) { 411 let permission = null; 412 if (permissionID in gPermissionObject && 413 gPermissionObject[permissionID].exactHostMatch) { 414 permission = Services.perms.getPermissionObject(principal, permissionID, true); 415 } else { 416 permission = Services.perms.getPermissionObject(principal, permissionID, false); 417 } 418 419 if (permission) { 420 result.state = permission.capability; 421 if (permission.expireType == Services.perms.EXPIRE_SESSION) { 422 result.scope = this.SCOPE_SESSION; 423 } 424 } 425 } 426 427 if (result.state == defaultState) { 428 // If there's no persistent permission saved, check if we have something 429 // set temporarily. 430 let value = TemporaryBlockedPermissions.get(browser, permissionID); 431 432 if (value) { 433 result.state = value.state; 434 result.scope = this.SCOPE_TEMPORARY; 435 } 436 } 437 438 return result; 439 }, 440 441 /** 442 * Deprecated! Use setForPrincipal(...) instead. 443 * Sets the state of a particular permission for a given URI or browser. 444 * This method will dispatch a "PermissionStateChange" event on the specified 445 * browser if a temporary permission was set 446 * 447 * @param {nsIURI} uri 448 * The URI to set the permission for. 449 * Note that this will be ignored if the scope is set to SCOPE_TEMPORARY 450 * @param {String} permissionID 451 * The id of the permission. 452 * @param {SitePermissions state} state 453 * The state of the permission. 454 * @param {SitePermissions scope} scope (optional) 455 * The scope of the permission. Defaults to SCOPE_PERSISTENT. 456 * @param {Browser} browser (optional) 457 * The browser object to set temporary permissions on. 458 * This needs to be provided if the scope is SCOPE_TEMPORARY! 459 */ 460 set(uri, permissionID, state, scope = this.SCOPE_PERSISTENT, browser = null) { 461 let principal = uri ? Services.scriptSecurityManager.createCodebasePrincipal(uri, {}) : null; 462 return this.setForPrincipal(principal, permissionID, state, scope, browser); 463 }, 464 465 /** 466 * Sets the state of a particular permission for a given principal or browser. 467 * This method will dispatch a "PermissionStateChange" event on the specified 468 * browser if a temporary permission was set 469 * 470 * @param {nsIPrincipal} principal 471 * The principal to set the permission for. 472 * Note that this will be ignored if the scope is set to SCOPE_TEMPORARY 473 * @param {String} permissionID 474 * The id of the permission. 475 * @param {SitePermissions state} state 476 * The state of the permission. 477 * @param {SitePermissions scope} scope (optional) 478 * The scope of the permission. Defaults to SCOPE_PERSISTENT. 479 * @param {Browser} browser (optional) 480 * The browser object to set temporary permissions on. 481 * This needs to be provided if the scope is SCOPE_TEMPORARY! 482 */ 483 setForPrincipal(principal, permissionID, state, 484 scope = this.SCOPE_PERSISTENT, browser = null) { 485 if (state == this.UNKNOWN || state == this.getDefault(permissionID)) { 486 // Because they are controlled by two prefs with many states that do not 487 // correspond to the classical ALLOW/DENY/PROMPT model, we want to always 488 // allow the user to add exceptions to their cookie rules without 489 // removing them. 490 if (permissionID != "cookie") { 491 this.removeFromPrincipal(principal, permissionID, browser); 492 return; 493 } 494 } 495 496 if (state == this.ALLOW_COOKIES_FOR_SESSION && permissionID != "cookie") { 497 throw "ALLOW_COOKIES_FOR_SESSION can only be set on the cookie permission"; 498 } 499 500 // Save temporary permissions. 501 if (scope == this.SCOPE_TEMPORARY) { 502 // We do not support setting temp ALLOW for security reasons. 503 // In its current state, this permission could be exploited by subframes 504 // on the same page. This is because for BLOCK we ignore the request 505 // principal and only consider the current browser principal, to avoid 506 // notification spamming. 507 // 508 // If you ever consider removing this line, you likely want to implement 509 // a more fine-grained TemporaryBlockedPermissions that temporarily blocks for the 510 // entire browser, but temporarily allows only for specific frames. 511 if (state != this.BLOCK) { 512 throw "'Block' is the only permission we can save temporarily on a browser"; 513 } 514 515 if (!browser) { 516 throw "TEMPORARY scoped permissions require a browser object"; 517 } 518 519 TemporaryBlockedPermissions.set(browser, permissionID); 520 521 browser.dispatchEvent(new browser.ownerGlobal 522 .CustomEvent("PermissionStateChange")); 523 } else if (this.isSupportedPrincipal(principal)) { 524 let perms_scope = Services.perms.EXPIRE_NEVER; 525 if (scope == this.SCOPE_SESSION) { 526 perms_scope = Services.perms.EXPIRE_SESSION; 527 } 528 529 Services.perms.addFromPrincipal(principal, permissionID, state, perms_scope); 530 } 531 }, 532 533 /** 534 * Deprecated! Please use removeFromPrincipal(principal, permissionID, browser). 535 * Removes the saved state of a particular permission for a given URI and/or browser. 536 * This method will dispatch a "PermissionStateChange" event on the specified 537 * browser if a temporary permission was removed. 538 * 539 * @param {nsIURI} uri 540 * The URI to remove the permission for. 541 * @param {String} permissionID 542 * The id of the permission. 543 * @param {Browser} browser (optional) 544 * The browser object to remove temporary permissions on. 545 */ 546 remove(uri, permissionID, browser) { 547 let principal = uri ? Services.scriptSecurityManager.createCodebasePrincipal(uri, {}) : null; 548 return this.removeFromPrincipal(principal, permissionID, browser); 549 }, 550 551 /** 552 * Removes the saved state of a particular permission for a given principal 553 * and/or browser. 554 * This method will dispatch a "PermissionStateChange" event on the specified 555 * browser if a temporary permission was removed. 556 * 557 * @param {nsIPrincipal} principal 558 * The principal to remove the permission for. 559 * @param {String} permissionID 560 * The id of the permission. 561 * @param {Browser} browser (optional) 562 * The browser object to remove temporary permissions on. 563 */ 564 removeFromPrincipal(principal, permissionID, browser) { 565 if (this.isSupportedPrincipal(principal)) 566 Services.perms.removeFromPrincipal(principal, permissionID); 567 568 // TemporaryBlockedPermissions.get() deletes expired permissions automatically, 569 if (TemporaryBlockedPermissions.get(browser, permissionID)) { 570 // If it exists but has not expired, remove it explicitly. 571 TemporaryBlockedPermissions.remove(browser, permissionID); 572 // Send a PermissionStateChange event only if the permission hasn't expired. 573 browser.dispatchEvent(new browser.ownerGlobal 574 .CustomEvent("PermissionStateChange")); 575 } 576 }, 577 578 /** 579 * Clears all permissions that were temporarily saved. 580 * 581 * @param {Browser} browser 582 * The browser object to clear. 583 */ 584 clearTemporaryPermissions(browser) { 585 TemporaryBlockedPermissions.clear(browser); 586 }, 587 588 /** 589 * Copy all permissions that were temporarily saved on one 590 * browser object to a new browser. 591 * 592 * @param {Browser} browser 593 * The browser object to copy from. 594 * @param {Browser} newBrowser 595 * The browser object to copy to. 596 */ 597 copyTemporaryPermissions(browser, newBrowser) { 598 TemporaryBlockedPermissions.copy(browser, newBrowser); 599 }, 600 601 /** 602 * Returns the localized label for the permission with the given ID, to be 603 * used in a UI for managing permissions. 604 * 605 * @param {string} permissionID 606 * The permission to get the label for. 607 * 608 * @return {String} the localized label. 609 */ 610 getPermissionLabel(permissionID) { 611 let labelID = gPermissionObject[permissionID].labelID || permissionID; 612 return gStringBundle.GetStringFromName("permission." + labelID + ".label"); 613 }, 614 615 /** 616 * Returns the localized label for the given permission state, to be used in 617 * a UI for managing permissions. 618 * 619 * @param {string} permissionID 620 * The permission to get the label for. 621 * 622 * @param {SitePermissions state} state 623 * The state to get the label for. 624 * 625 * @return {String|null} the localized label or null if an 626 * unknown state was passed. 627 */ 628 getMultichoiceStateLabel(permissionID, state) { 629 // If the permission has custom logic for getting its default value, 630 // try that first. 631 if (permissionID in gPermissionObject && 632 gPermissionObject[permissionID].getMultichoiceStateLabel) { 633 return gPermissionObject[permissionID].getMultichoiceStateLabel(state); 634 } 635 636 switch (state) { 637 case this.UNKNOWN: 638 case this.PROMPT: 639 return gStringBundle.GetStringFromName("state.multichoice.alwaysAsk"); 640 case this.ALLOW: 641 return gStringBundle.GetStringFromName("state.multichoice.allow"); 642 case this.ALLOW_COOKIES_FOR_SESSION: 643 return gStringBundle.GetStringFromName("state.multichoice.allowForSession"); 644 case this.BLOCK: 645 return gStringBundle.GetStringFromName("state.multichoice.block"); 646 default: 647 return null; 648 } 649 }, 650 651 /** 652 * Returns the localized label for a permission's current state. 653 * 654 * @param {SitePermissions state} state 655 * The state to get the label for. 656 * @param {string} id 657 * The permission to get the state label for. 658 * @param {SitePermissions scope} scope (optional) 659 * The scope to get the label for. 660 * 661 * @return {String|null} the localized label or null if an 662 * unknown state was passed. 663 */ 664 getCurrentStateLabel(state, id, scope = null) { 665 // We try to avoid a collision between SitePermissions.PROMPT_HIDE and 666 // SitePermissions.ALLOW_COOKIES_FOR_SESSION which share the same const 667 // value. 668 if (id.startsWith("plugin") && state == SitePermissions.PROMPT_HIDE) { 669 return gStringBundle.GetStringFromName("state.current.hide"); 670 } 671 switch (state) { 672 case this.PROMPT: 673 return gStringBundle.GetStringFromName("state.current.prompt"); 674 case this.ALLOW: 675 if (scope && scope != this.SCOPE_PERSISTENT) 676 return gStringBundle.GetStringFromName("state.current.allowedTemporarily"); 677 return gStringBundle.GetStringFromName("state.current.allowed"); 678 case this.ALLOW_COOKIES_FOR_SESSION: 679 return gStringBundle.GetStringFromName("state.current.allowedForSession"); 680 case this.BLOCK: 681 if (scope && scope != this.SCOPE_PERSISTENT) 682 return gStringBundle.GetStringFromName("state.current.blockedTemporarily"); 683 return gStringBundle.GetStringFromName("state.current.blocked"); 684 default: 685 return null; 686 } 687 }, 688}; 689 690var gPermissionObject = { 691 /* Holds permission ID => options pairs. 692 * 693 * Supported options: 694 * 695 * - exactHostMatch 696 * Allows sub domains to have their own permissions. 697 * Defaults to false. 698 * 699 * - getDefault 700 * Called to get the permission's default state. 701 * Defaults to UNKNOWN, indicating that the user will be asked each time 702 * a page asks for that permissions. 703 * 704 * - labelID 705 * Use the given ID instead of the permission name for looking up strings. 706 * e.g. "desktop-notification2" to use permission.desktop-notification2.label 707 * 708 * - states 709 * Array of permission states to be exposed to the user. 710 * Defaults to ALLOW, BLOCK and the default state (see getDefault). 711 * The PROMPT_HIDE state is deliberately excluded from "plugin:flash" 712 * since we don't want to expose a "Hide Prompt" button to the user 713 * through pageinfo. 714 * 715 * - getMultichoiceStateLabel 716 * Allows for custom logic for getting its default value 717 */ 718 719 "image": { 720 states: [ 721 SitePermissions.ALLOW, 722 SitePermissions.PROMPT, 723 SitePermissions.BLOCK 724 ], 725 getMultichoiceStateLabel(state) { 726 switch (state) { 727 case SitePermissions.ALLOW: 728 return gStringBundle.GetStringFromName("state.multichoice.allow"); 729 // Equates to BEHAVIOR_NOFOREIGN from nsContentBlocker.cpp 730 case SitePermissions.PROMPT: 731 return gStringBundle.GetStringFromName("state.multichoice.allowForSameDomain"); 732 case SitePermissions.BLOCK: 733 return gStringBundle.GetStringFromName("state.multichoice.block"); 734 } 735 throw new Error(`Unknown state: ${state}`); 736 }, 737 }, 738 739 "cookie": { 740 states: [ SitePermissions.ALLOW, SitePermissions.ALLOW_COOKIES_FOR_SESSION, SitePermissions.BLOCK ], 741 getDefault() { 742 if (Services.prefs.getIntPref("network.cookie.cookieBehavior") == Ci.nsICookieService.BEHAVIOR_REJECT) 743 return SitePermissions.BLOCK; 744 745 if (Services.prefs.getIntPref("network.cookie.lifetimePolicy") == Ci.nsICookieService.ACCEPT_SESSION) 746 return SitePermissions.ALLOW_COOKIES_FOR_SESSION; 747 748 return SitePermissions.ALLOW; 749 } 750 }, 751 752 "desktop-notification": { 753 exactHostMatch: true, 754 labelID: "desktop-notification2", 755 }, 756 757 "camera": { 758 exactHostMatch: true, 759 }, 760 761 "microphone": { 762 exactHostMatch: true, 763 }, 764 765 "screen": { 766 exactHostMatch: true, 767 states: [ SitePermissions.UNKNOWN, SitePermissions.BLOCK ], 768 }, 769 770 "popup": { 771 getDefault() { 772 return Services.prefs.getBoolPref("dom.disable_open_during_load") ? 773 SitePermissions.BLOCK : SitePermissions.ALLOW; 774 }, 775 states: [ SitePermissions.ALLOW, SitePermissions.BLOCK ], 776 }, 777 778 "install": { 779 getDefault() { 780 return Services.prefs.getBoolPref("xpinstall.whitelist.required") ? 781 SitePermissions.BLOCK : SitePermissions.ALLOW; 782 }, 783 states: [ SitePermissions.ALLOW, SitePermissions.BLOCK ], 784 }, 785 786 "geo": { 787 exactHostMatch: true 788 }, 789 790 "focus-tab-by-prompt": { 791 exactHostMatch: true, 792 states: [ SitePermissions.UNKNOWN, SitePermissions.ALLOW ], 793 }, 794 795 "persistent-storage": { 796 exactHostMatch: true 797 }, 798 799 "plugin:flash": { 800 labelID: "flash-plugin", 801 states: [ SitePermissions.UNKNOWN, SitePermissions.ALLOW, SitePermissions.BLOCK ], 802 } 803}; 804 805// Delete this entry while being pre-off 806// or the persistent-storage permission would appear in Page info's Permission section 807if (!Services.prefs.getBoolPref("browser.storageManager.enabled")) { 808 delete gPermissionObject["persistent-storage"]; 809} 810 811XPCOMUtils.defineLazyPreferenceGetter(SitePermissions, "temporaryPermissionExpireTime", 812 "privacy.temporary_permission_expire_time_ms", 3600 * 1000); 813