diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/suite/components/permissions | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/suite/components/permissions')
7 files changed, 1271 insertions, 0 deletions
diff --git a/comm/suite/components/permissions/content/cookieViewer.js b/comm/suite/components/permissions/content/cookieViewer.js new file mode 100644 index 0000000000..d864f72908 --- /dev/null +++ b/comm/suite/components/permissions/content/cookieViewer.js @@ -0,0 +1,531 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); +const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm"); + +// cookies and permissions list +var cookies = []; +var permissions = []; +var allCookies = []; +var deletedCookies = []; +var deletedPermissions = []; + +var cookieBundle; +var gUpdatingBatch = ""; +var lastCookieSortColumn; +var lastCookieSortAscending; +var lastPermissionSortColumn; +var lastPermissionSortAscending; + +function Startup() { + + // arguments passed to this routine: + // cookieManager + + // intialize string bundle + cookieBundle = document.getElementById("cookieBundle"); + + // load in the cookies and permissions + cookiesTree = document.getElementById("cookiesTree"); + lastCookieSortAscending = (cookiesTree.getAttribute("sortAscending") == "true"); + lastCookieSortColumn = cookiesTree.getAttribute("sortColumn"); + permissionsTree = document.getElementById("permissionsTree"); + lastPermissionSortAscending = (permissionsTree.getAttribute("sortAscending") == "true"); + lastPermissionSortColumn = permissionsTree.getAttribute("sortColumn"); + loadCookies(); + loadPermissions(); + + // be prepared to reload the display if anything changes + Services.obs.addObserver(cookieReloadDisplay, "cookie-changed", false); + Services.obs.addObserver(cookieReloadDisplay, "perm-changed", false); + + // filter the table if requested by caller + if (window.arguments && + window.arguments[0] && + window.arguments[0].filterString) + setFilter(window.arguments[0].filterString); + + document.getElementById("filter").focus(); +} + +function Shutdown() { + Services.obs.removeObserver(cookieReloadDisplay, "cookie-changed"); + Services.obs.removeObserver(cookieReloadDisplay, "perm-changed"); +} + +function PromptConfirm(title, msg, yes) { + var flags = + ((Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) + + (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1) + + Services.prompt.BUTTON_POS_1_DEFAULT) + return Services.prompt.confirmEx(window, + cookieBundle.getString(title), + cookieBundle.getString(msg), + flags, + cookieBundle.getString(yes), + null, null, null, {value:0}); +} + +var cookieReloadDisplay = { + observe: function(subject, topic, state) { + if (topic == gUpdatingBatch) + return; + if (topic == "cookie-changed") { + allCookies.length = 0; + loadCookies(); + } else if (topic == "perm-changed") { + permissions.length = 0; + loadPermissions(); + } + } +} + +function doSelectAll() { + var elem = document.commandDispatcher.focusedElement; + if (elem && "treeBoxObject" in elem) + elem.view.selection.selectAll(); +} + +/*** =================== COOKIES CODE =================== ***/ + +var cookiesTreeView = { + rowCount : 0, + setTree : function(tree){}, + getImageSrc : function(row,column) {}, + getProgressMode : function(row,column) {}, + getCellValue : function(row,column) {}, + getCellText : function(row,column){ return cookies[row][column.id]; }, + isSeparator : function(index) {return false;}, + isSorted: function() { return false; }, + isContainer : function(index) {return false;}, + cycleHeader : function(aCol) {}, + getRowProperties : function(row) { return ""; }, + getColumnProperties : function(column) { return ""; }, + getCellProperties : function(row, column) { return ""; } +}; +var cookiesTree; + +function Cookie(id, host, name, path, originAttributes, value, + isDomain, rawHost, isSecure, expires) { + this.id = id; + this.host = host; + this.name = name; + this.path = path; + this.originAttributes = originAttributes; + this.value = value; + this.isDomain = isDomain; + this.rawHost = rawHost; + this.isSecure = isSecure; + this.expires = GetExpiresString(expires); + this.expiresSortValue = expires; +} + +function loadCookies() { + // load cookies into a table + var enumerator = Services.cookies.enumerator; + var count = 0; + while (enumerator.hasMoreElements()) { + var nextCookie = enumerator.getNext(); + if (!nextCookie) break; + nextCookie = nextCookie.QueryInterface(Ci.nsICookie); + var host = nextCookie.host; + allCookies.push( + new Cookie(count++, host, nextCookie.name, + nextCookie.path, nextCookie.originAttributes, + nextCookie.value, nextCookie.isDomain, + host.charAt(0)=="." ? host.slice(1) : host, + nextCookie.isSecure, nextCookie.expires)); + } + + // filter, sort and display the table + cookiesTree.view = cookiesTreeView; + filter(document.getElementById("filter").value); +} + +function GetExpiresString(expires) { + if (expires) { + var date = new Date(1000*expires); + + // if a server manages to set a really long-lived cookie, the dateservice + // can't cope with it properly, so we'll just return a blank string + // see bug 238045 for details + var expiry = ""; + try { + const dateTimeFormatter = new Services.intl.DateTimeFormat(undefined, { + dateStyle: "full", timeStyle: "long" }); + expiry = dateTimeFormatter.format(date); + } catch(ex) { + // do nothing + } + return expiry; + } + return cookieBundle.getString("expireAtEndOfSession"); +} + +function CookieSelected() { + var selections = GetTreeSelections(cookiesTree); + if (selections.length) { + document.getElementById("removeCookie").removeAttribute("disabled"); + } else { + document.getElementById("removeCookie").setAttribute("disabled", "true"); + ClearCookieProperties(); + return true; + } + + var idx = selections[0]; + if (idx >= cookies.length) { + // Something got out of synch. See bug 119812 for details + dump("Tree and viewer state are out of sync! " + + "Help us figure out the problem in bug 119812"); + return false; + } + + var props = [ + {id: "ifl_name", value: cookies[idx].name}, + {id: "ifl_value", value: cookies[idx].value}, + {id: "ifl_isDomain", + value: cookies[idx].isDomain ? + cookieBundle.getString("domainColon") : cookieBundle.getString("hostColon")}, + {id: "ifl_host", value: cookies[idx].host}, + {id: "ifl_path", value: cookies[idx].path}, + {id: "ifl_isSecure", + value: cookies[idx].isSecure ? + cookieBundle.getString("forSecureOnly") : + cookieBundle.getString("forAnyConnection")}, + {id: "ifl_expires", value: cookies[idx].expires} + ]; + + var value; + var field; + + for (let lProp of props) + { + field = document.getElementById(lProp.id); + if ((selections.length > 1) && (lProp.id != "ifl_isDomain")) { + value = ""; // clear field if multiple selections + } else { + value = lProp.value; + } + field.value = value; + } + return true; +} + +function ClearCookieProperties() { + var properties = + ["ifl_name","ifl_value","ifl_host","ifl_path","ifl_isSecure","ifl_expires"]; + for (let prop of properties) { + document.getElementById(prop).value = ""; + } +} + +function DeleteCookie() { + if (cookiesTreeView.selection.count > 1 && + PromptConfirm("deleteSelectedCookiesTitle", + "deleteSelectedCookies", + "deleteSelectedCookiesYes") == 1) { + return; + } + DeleteSelectedItemFromTree(cookiesTree, cookiesTreeView, + cookies, deletedCookies, + "removeCookie", "removeAllCookies"); + if (document.getElementById("filter").value) { + // remove selected cookies from unfiltered set + for (let cookie of deletedCookies) { + allCookies.splice(allCookies.indexOf(cookie), 1); + } + } + if (!cookies.length) { + ClearCookieProperties(); + } + FinalizeCookieDeletions(); +} + +function DeleteAllCookies() { + if (PromptConfirm("deleteAllCookiesTitle", + "deleteAllCookies", + "deleteAllCookiesYes") == 1) { + return; + } + + ClearCookieProperties(); + DeleteAllFromTree(cookiesTree, cookiesTreeView, + cookies, deletedCookies, + "removeCookie", "removeAllCookies"); + allCookies.length = 0; + FinalizeCookieDeletions(); +} + +function FinalizeCookieDeletions() { + gUpdatingBatch = "cookie-changed"; + for (let delCookie of deletedCookies) { + Services.cookies.remove(delCookie.host, + delCookie.name, + delCookie.path, + document.getElementById("checkbox").checked, + delCookie.originAttributes); + } + deletedCookies.length = 0; + gUpdatingBatch = ""; +} + +function HandleCookieKeyPress(e) { + if (e.keyCode == KeyEvent.DOM_VK_DELETE || + (AppConstants.platform == "macosx" && + e.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) { + DeleteCookie(); + } +} + +function CookieColumnSort(column, updateSelection) { + lastCookieSortAscending = + SortTree(cookiesTree, cookiesTreeView, cookies, + column, lastCookieSortColumn, lastCookieSortAscending, + updateSelection); + lastCookieSortColumn = column; + + SetSortDirection(cookiesTree, column, lastCookieSortAscending); +} + +/*** =================== PERMISSIONS CODE =================== ***/ + +var permissionsTreeView = { + rowCount : 0, + setTree : function(tree){}, + getImageSrc : function(row,column) {}, + getProgressMode : function(row,column) {}, + getCellValue : function(row,column) {}, + getCellText : function(row,column) { return permissions[row][column.id]; }, + isSeparator : function(index) {return false;}, + isSorted: function() { return false; }, + isContainer : function(index) {return false;}, + cycleHeader : function(aCol) {}, + getRowProperties : function(row) { return ""; }, + getColumnProperties : function(column) { return ""; }, + getCellProperties : function(row, column) { return ""; } +}; +var permissionsTree; + +function Permission(id, principal, type, capability) { + this.id = id; + this.principal = principal; + this.host = principal.URI.hostPort; + this.scheme = principal.URI.scheme; + this.type = type; + this.capability = capability; +} + +function loadPermissions() { + // load permissions into a table + var enumerator = Services.perms.enumerator; + var canStr = cookieBundle.getString("can"); + var canSessionStr = cookieBundle.getString("canSession"); + var cannotStr = cookieBundle.getString("cannot"); + var capability; + var count = 0; + var permission; + while (enumerator.hasMoreElements()) { + permission = enumerator.getNext().QueryInterface(Ci.nsIPermission); + // We are only interested in cookie permissions in this code. + if (permission.type == "cookie") { + // It is currently possible to add a cookie permission for about:xxx + // and other internal pages. They are probably invalid and will be + // ignored for now. + // Test if the permission has a host. + try { + permission.principal.URI.host; + } + catch (e) { + Cu.reportError("Invalid permission found: " + + permission.principal.origin + " " + permission.type); + continue; + } + + switch (permission.capability) { + case Ci.nsIPermissionManager.ALLOW_ACTION: + capability = canStr; + break; + case Ci.nsIPermissionManager.DENY_ACTION: + capability = cannotStr; + break; + case Ci.nsICookiePermission.ACCESS_SESSION: + capability = canSessionStr; + break; + default: + continue; + } + permissions.push(new Permission(count++, + permission.principal, + permission.type, + capability)); + } + } + permissionsTreeView.rowCount = permissions.length; + + // sort and display the table + permissionsTree.view = permissionsTreeView; + permissionsTreeView.selection.clearSelection(); + SortTree(permissionsTree, permissionsTreeView, permissions, + lastPermissionSortColumn, lastPermissionSortColumn, + !lastPermissionSortAscending); + + // disable "remove all" button if there are no cookies + document.getElementById("removeAllPermissions").disabled = permissions.length == 0; +} + +function DeletePermission() { + if (permissionsTreeView.selection.count > 1 && + PromptConfirm("deleteSelectedSitesTitle", + "deleteSelectedCookiesSites", + "deleteSelectedSitesYes") == 1) { + return; + } + DeleteSelectedItemFromTree(permissionsTree, permissionsTreeView, + permissions, deletedPermissions, + "removePermission", "removeAllPermissions"); + FinalizePermissionDeletions(); +} + +function setCookiePermissions(action) { + var site = document.getElementById("cookie-site"); + + // let the backend do the validation + try { + var url = new URL(site.value); + } catch (e) { + // show an error if URL is invalid + window.alert(cookieBundle.getString("allowedURLSchemes")); + return; + } + + try { + var uri = Services.io.newURI(url); + } catch (e) { + // show an error if URI can not be constructed or adding it failed + window.alert(cookieBundle.getString("errorAddPermission")); + return; + } + // only allow a few schemes here + // others like file:// would produce an invalid entry in the database + if (uri.scheme != "http" && + uri.scheme != "https") { + // show an error if uri uses invalid scheme + window.alert(uri.scheme + ": " + cookieBundle.getString("allowedURLSchemes")); + return; + } + + if (Services.perms.testPermission(uri, "cookie") != action) + Services.perms.add(uri, "cookie", action); + + site.focus(); + site.value = ""; +} + +function DeleteAllPermissions() { + if (PromptConfirm("deleteAllSitesTitle", + "deleteAllCookiesSites", + "deleteAllSitesYes") == 1) { + return; + } + + DeleteAllFromTree(permissionsTree, permissionsTreeView, + permissions, deletedPermissions, + "removePermission", "removeAllPermissions"); + FinalizePermissionDeletions(); +} + +function FinalizePermissionDeletions() { + if (!deletedPermissions.length) + return; + + gUpdatingBatch = "perm-changed"; + for (let permission of deletedPermissions) + Services.perms.removeFromPrincipal(permission.principal, permission.type); + deletedPermissions.length = 0; + gUpdatingBatch = ""; +} + +function HandlePermissionKeyPress(e) { + if (e.keyCode == KeyEvent.DOM_VK_DELETE || + (AppConstants.platform == "macosx" && + e.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) { + DeletePermission(); + } +} + +function PermissionColumnSort(column, updateSelection) { + lastPermissionSortAscending = + SortTree(permissionsTree, permissionsTreeView, permissions, + column, lastPermissionSortColumn, lastPermissionSortAscending, + updateSelection); + lastPermissionSortColumn = column; + + SetSortDirection(permissionsTree, column, lastPermissionSortAscending); +} + +/*** ============ CODE FOR HELP BUTTON =================== ***/ + +function doHelpButton() +{ + var selTab = document.getElementById("tabbox").selectedTab; + var key = selTab.getAttribute("help"); + openHelp(key, "chrome://communicator/locale/help/suitehelp.rdf"); +} + +/*** =================== FILTER CODE =================== ***/ + +function filterCookies(aFilterValue) +{ + var filterSet = []; + for (let cookie of allCookies) { + if (cookie.rawHost.includes(aFilterValue) || + cookie.name.includes(aFilterValue) || + cookie.value.includes(aFilterValue)) + filterSet.push(cookie); + } + return filterSet; +} + +function filter(filter) +{ + // clear the display + var oldCount = cookiesTreeView.rowCount; + cookiesTreeView.rowCount = 0; + cookiesTree.treeBoxObject.rowCountChanged(0, -oldCount); + + // set up the display + cookies = filter ? filterCookies(filter) : allCookies; + cookiesTreeView.rowCount = cookies.length; + cookiesTree.treeBoxObject.rowCountChanged(0, cookiesTreeView.rowCount); + + // sort the tree according to the last sort parameters + SortTree(cookiesTree, cookiesTreeView, cookies, lastCookieSortColumn, + lastCookieSortColumn, !lastCookieSortAscending); + + // disable Remove All Cookies button if the view is filtered or there are no cookies + if (filter || !cookies.length) + document.getElementById("removeAllCookies").setAttribute("disabled", "true"); + else + document.getElementById("removeAllCookies").removeAttribute("disabled"); + + // if the view is filtered and not empty then select the first item + if (filter && cookies.length) + cookiesTreeView.selection.select(0); +} + +function setFilter(aFilterString) +{ + document.getElementById("filter").value = aFilterString; + filter(aFilterString); +} + +function focusFilterBox() +{ + var filterBox = document.getElementById("filter"); + filterBox.focus(); + filterBox.select(); +} diff --git a/comm/suite/components/permissions/content/cookieViewer.xul b/comm/suite/components/permissions/content/cookieViewer.xul new file mode 100644 index 0000000000..62a9db9ad3 --- /dev/null +++ b/comm/suite/components/permissions/content/cookieViewer.xul @@ -0,0 +1,225 @@ +<?xml version="1.0"?> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<!-- CHANGE THIS WHEN MOVING FILES --> +<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?> + +<!-- CHANGE THIS WHEN MOVING FILES --> +<!DOCTYPE dialog SYSTEM "chrome://communicator/locale/permissions/cookieViewer.dtd" > + +<dialog id="cookieviewer" + buttons="help" + title="&windowtitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + windowtype="mozilla:cookieviewer" + style="width: 65ch; height: 42em;" + onload="Startup()" + onunload="Shutdown()" + ondialoghelp="doHelpButton();" + persist="screenX screenY width height"> + + <script src="chrome://communicator/content/permissions/cookieViewer.js"/> + <script src="chrome://communicator/content/permissions/permissionsUtils.js"/> + <script src="chrome://global/content/treeUtils.js"/> + <script src="chrome://help/content/contextHelp.js" /> + + <keyset id="dialogKeys"> + <key key="&focusSearch.key;" + modifiers="accel" + oncommand="focusFilterBox();"/> + <key key="&selectAll.key;" + modifiers="accel" + oncommand="doSelectAll();"/> + </keyset> + <stringbundle id="cookieBundle" + src="chrome://communicator/locale/permissions/cookieViewer.properties"/> + + <tabbox id="tabbox" flex="1"> + <tabs> + <tab id="cookiesTab" label="&tab.cookiesonsystem.label;" help="cookies_stored"/> + <tab id="permissionsTab" label="&tab.bannedservers.label;" help="cookie_sites"/> + </tabs> + <tabpanels id="panel" flex="1"> + <vbox class="tabpanel" id="system" flex="1"> + <vbox id="dummyContainer" flex="1"> + <!-- filter --> + <hbox align="center"> + <textbox id="filter" + flex="1" + type="search" + aria-controls="cookiesTree" + placeholder="&search.placeholder;" + oncommand="filter(this.value);"/> + </hbox> + <separator class="thin"/> + <label value="&div.cookiesonsystem.label;" control="cookiesTree"/> + <separator class="thin"/> + <tree id="cookiesTree" flex="1" style="height: 10em;" + onkeypress="HandleCookieKeyPress(event);" + onselect="CookieSelected();" + sortAscending="true" + sortColumn="rawHost" + persist="sortAscending sortColumn"> + <treecols> + <treecol id="rawHost" + label="&treehead.cookiedomain.label;" + flex="5" + onclick="CookieColumnSort(this.id, true);" + sortDirection="ascending" + persist="width hidden"/> + <splitter class="tree-splitter"/> + <treecol id="name" + label="&treehead.cookiename.label;" + flex="5" + onclick="CookieColumnSort(this.id, true);" + persist="width hidden"/> + <splitter class="tree-splitter"/> + <treecol id="expires" + label="&treehead.cookieexpires.label;" + flex="10" + hidden="true" + onclick="CookieColumnSort(this.id, true);" + persist="width hidden"/> + </treecols> + <treechildren/> + </tree> + <groupbox> + <caption label="&treehead.infoselected.label;"/> + <!-- labels --> + <grid flex="1"> + <columns> + <column/> + <column flex="1"/> + </columns> + <rows> + + <row align="center"> + <hbox align="center" pack="end"> + <label value="&props.name.label;" control="ifl_name"/> + </hbox> + <textbox id="ifl_name" readonly="true" class="plain"/> + </row> + + <row align="center"> + <hbox align="center" pack="end"> + <label value="&props.value.label;" control="ifl_value"/> + </hbox> + <textbox id="ifl_value" readonly="true" class="plain"/> + </row> + + <row align="center"> + <hbox align="center" pack="end"> + <label id="ifl_isDomain" value="&props.domain.label;" control="ifl_host"/> + </hbox> + <textbox id="ifl_host" readonly="true" class="plain"/> + </row> + + <row align="center"> + <hbox align="center" pack="end"> + <label value="&props.path.label;" control="ifl_path"/> + </hbox> + <textbox id="ifl_path" readonly="true" class="plain"/> + </row> + + <row align="center"> + <hbox align="center" pack="end"> + <label value="&props.secure.label;" control="ifl_isSecure"/> + </hbox> + <textbox id="ifl_isSecure" readonly="true" class="plain"/> + </row> + + <row align="center"> + <hbox align="center" pack="end"> + <label value="&props.expires.label;" control="ifl_expires"/> + </hbox> + <textbox id="ifl_expires" readonly="true" class="plain"/> + </row> + + </rows> + </grid> + </groupbox> + <hbox> + <button id="removeCookie" disabled="true" + label="&button.removecookie.label;" + accesskey="&button.removecookie.accesskey;" + oncommand="DeleteCookie();"/> + <button id="removeAllCookies" + label="&button.removeallcookies.label;" + accesskey="&button.removeallcookies.accesskey;" + oncommand="DeleteAllCookies();"/> + <!-- todo: <button id="restoreCookies" class="dialog push" disabled="true" label="&button.restorecookie.label;" oncommand="RestoreCookies();"/> --> + </hbox> + <separator class="thin"/> + <hbox align="start"> + <checkbox id="checkbox" label="&futureCookies.label;" accesskey="&futureCookies.accesskey;" persist="checked"/> + </hbox> + </vbox> + </vbox> + + <vbox id="servers" flex="1"> + <description id="permissionsText">&div.bannedservers.label;</description> + <separator class="thin"/> + <hbox> + <textbox id="cookie-site" + flex="1" + oninput="handleHostInput(this.value);"/> + <button id="btnBlock" label="&blockSite.label;" disabled="true" + accesskey="&blockSite.accesskey;" + oncommand="setCookiePermissions(Ci.nsIPermissionManager.DENY_ACTION);"/> + <button id="btnSession" label="&allowSiteSession.label;" disabled="true" + accesskey="&allowSiteSession.accesskey;" + oncommand="setCookiePermissions(Ci.nsICookiePermission.ACCESS_SESSION);"/> + <button id="btnAllow" label="&allowSite.label;" disabled="true" + accesskey="&allowSite.accesskey;" + oncommand="setCookiePermissions(Ci.nsIPermissionManager.ALLOW_ACTION);"/> + </hbox> + <separator class="thin"/> + <tree id="permissionsTree" + flex="1" + style="height: 10em;" + hidecolumnpicker="true" + onkeypress="HandlePermissionKeyPress(event);" + onselect="PermissionSelected(this);" + sortAscending="true" + sortColumn="host" + persist="sortAscending sortColumn"> + <treecols> + <treecol id="host" + label="&treehead.sitename.label;" + flex="5" + onclick="PermissionColumnSort(this.id, true);" + sortDirection="ascending" + persist="width"/> + <splitter class="tree-splitter"/> + <treecol id="scheme" + label="&treehead.scheme.label;" + flex="5" + onclick="PermissionColumnSort(this.id, true);" + persist="width"/> + <splitter class="tree-splitter"/> + <treecol id="capability" + label="&treehead.status.label;" + flex="5" + onclick="PermissionColumnSort(this.id, true);" + persist="width"/> + </treecols> + <treechildren/> + </tree> + <hbox> + <button id="removePermission" + disabled="true" + label="&removepermission.label;" + accesskey="&removepermission.accesskey;" + oncommand="DeletePermission();"/> + <button id="removeAllPermissions" + label="&removeallpermissions.label;" + accesskey="&removeallpermissions.accesskey;" + oncommand="DeleteAllPermissions();"/> + </hbox> + </vbox> + + </tabpanels> + </tabbox> +</dialog> diff --git a/comm/suite/components/permissions/content/permissionsManager.js b/comm/suite/components/permissions/content/permissionsManager.js new file mode 100644 index 0000000000..9004dc3cda --- /dev/null +++ b/comm/suite/components/permissions/content/permissionsManager.js @@ -0,0 +1,287 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); +const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm"); + +var permissions = []; +var removals = []; + +var sortColumn; +var sortAscending; + +var permissionsTreeView = { + rowCount: 0, + setTree: function(tree) {}, + getImageSrc: function(row, column) {}, + getProgressMode: function(row, column) {}, + getCellValue: function(row, column) {}, + getCellText: function(row, column) { return permissions[row][column.id]; }, + isSeparator: function(index) { return false; }, + isSorted: function() { return false; }, + isContainer: function(index) { return false; }, + cycleHeader: function(column) {}, + getRowProperties: function(row, column) { return ""; }, + getColumnProperties: function(column) { return ""; }, + getCellProperties: function(row, column) { return ""; } + }; + +var permissionsTree; +var permissionType = "popup"; +var gManageCapability; + +var permissionsBundle; + +function Startup() { + var introText, windowTitle; + + permissionsTree = document.getElementById("permissionsTree"); + + permissionsBundle = document.getElementById("permissionsBundle"); + + sortAscending = (permissionsTree.getAttribute("sortAscending") == "true"); + sortColumn = permissionsTree.getAttribute("sortColumn"); + + var params = { blockVisible : true, + sessionVisible : true, + allowVisible : true, + manageCapability : true + }; + + if (window.arguments && window.arguments[0]) { + params = window.arguments[0]; + setHost(params.prefilledHost); + permissionType = params.permissionType; + gManageCapability = params.manageCapability; + introText = params.introText; + windowTitle = params.windowTitle; + } + + document.getElementById("btnBlock").hidden = !params.blockVisible; + document.getElementById("btnSession").hidden = !params.sessionVisible; + document.getElementById("btnAllow").hidden = !params.allowVisible; + + document.getElementById("permissionsText").textContent = introText || + permissionsBundle.getString(permissionType + "permissionstext"); + + document.title = windowTitle || + permissionsBundle.getString(permissionType + "permissionstitle"); + + var dialogElement = document.getElementById("permissionsManager"); + dialogElement.setAttribute("windowtype", "permissions-" + permissionType); + + var urlFieldVisible = params.blockVisible || + params.sessionVisible || + params.allowVisible; + + document.getElementById("url").hidden = !urlFieldVisible; + document.getElementById("urlLabel").hidden = !urlFieldVisible; + + handleHostInput(document.getElementById("url").value); + loadPermissions(); +} + +function onAccept() { + finalizeChanges(); + reInitialize(); + + // Don't close the window. + return false; +} + +function onCancel() { + reInitialize(); + + // Don't close the window. + return false; +} + +function reInitialize() { + permissions = []; + removals = []; + + // loadPermissions will reverse the sort direction so flip it now. + sortAscending = !sortAscending; + + // Reload permissions tree. + loadPermissions(); +} + +function setHost(aHost) { + document.getElementById("url").value = aHost; +} + +function Permission(id, principal, host, type, capability, perm) { + this.id = id; + this.principal = principal; + this.host = host; + this.rawHost = host.replace(/^\./, ""); + this.type = type; + this.capability = capability; + this.perm = perm; +} + +function loadPermissions() { + var enumerator = Services.perms.enumerator; + var count = 0; + var permission; + + try { + while (enumerator.hasMoreElements()) { + permission = enumerator.getNext().QueryInterface(Ci.nsIPermission); + if (permission.type == permissionType && + (!gManageCapability || permission.capability == gManageCapability)) { + permissions.push(new Permission(count++, + permission.principal, + permission.principal.URI.host, + permission.type, + capabilityString(permission.capability), + permission.capability)); + } + } + } catch(ex) { + } + + permissionsTreeView.rowCount = permissions.length; + + // sort and display the table + permissionsTree.view = permissionsTreeView; + permissionColumnSort(sortColumn, false); + + // disable "remove all" button if there are none + document.getElementById("removeAllPermissions").disabled = + permissions.length == 0; +} + +function capabilityString(aCapability) { + var capability = null; + switch (aCapability) { + case Ci.nsIPermissionManager.ALLOW_ACTION: + capability = "can"; + break; + case Ci.nsIPermissionManager.DENY_ACTION: + capability = "cannot"; + break; + // we should only ever hit this for cookies + case Ci.nsICookiePermission.ACCESS_SESSION: + capability = "canSession"; + break; + default: + break; + } + return permissionsBundle.getString(capability); +} + +function permissionColumnSort(aColumn, aUpdateSelection) { + sortAscending = + SortTree(permissionsTree, permissionsTreeView, permissions, + aColumn, sortColumn, sortAscending, aUpdateSelection); + sortColumn = aColumn; + + SetSortDirection(permissionsTree, aColumn, sortAscending); +} + +function deletePermissions() { + DeleteSelectedItemFromTree(permissionsTree, permissionsTreeView, + permissions, removals, + "removePermission", "removeAllPermissions"); +} + +function deleteAllPermissions() { + DeleteAllFromTree(permissionsTree, permissionsTreeView, permissions, + removals, "removePermission", "removeAllPermissions"); +} + +function finalizeChanges() { + let p; + + for (let i in permissions) { + p = permissions[i]; + try { + // Principal is null so a permission we just added in this session. + if (p.principal == null) { + let uri = Services.io.newURI("https://" + p.host); + Services.perms.add(uri, p.type, p.perm); + } + } catch(ex) { + } + } + + for (let i in removals) { + p = removals[i]; + try { + // Principal is not null so not a permission we just added in this + // session. + if (p.principal) { + Services.perms.removeFromPrincipal(p.principal, + p.type); + } + } catch(ex) { + } + } +} + +function handlePermissionKeyPress(e) { + if (e.keyCode == KeyEvent.DOM_VK_DELETE || + (AppConstants.platform == "macosx" && + e.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) { + deletePermissions(); + } +} + +function addPermission(aPermission) { + var textbox = document.getElementById("url"); + // trim any leading and trailing spaces and scheme + var host = trimSpacesAndScheme(textbox.value); + try { + let uri = Services.io.newURI("https://" + host); + host = uri.host; + } catch(ex) { + var message = permissionsBundle.getFormattedString("alertInvalid", [host]); + var title = permissionsBundle.getString("alertInvalidTitle"); + Services.prompt.alert(window, title, message); + textbox.value = ""; + textbox.focus(); + handleHostInput(""); + return; + } + + // we need this whether the perm exists or not + var stringCapability = capabilityString(aPermission); + + // check whether the permission already exists, if not, add it + var exists = false; + for (var i in permissions) { + if (permissions[i].rawHost == host) { + // Avoid calling the permission manager if the capability settings are + // the same. Otherwise allow the call to the permissions manager to + // update the listbox for us. + exists = permissions[i].perm == aPermission; + break; + } + } + + if (!exists) { + permissions.push(new Permission(permissions.length, null, host, + permissionType, stringCapability, + aPermission)); + + permissionsTreeView.rowCount = permissions.length; + permissionsTree.treeBoxObject.rowCountChanged(permissions.length - 1, 1); + permissionsTree.treeBoxObject.ensureRowIsVisible(permissions.length - 1); + } + textbox.value = ""; + textbox.focus(); + + // covers a case where the site exists already, so the buttons don't disable + handleHostInput(""); + + // enable "remove all" button as needed + document.getElementById("removeAllPermissions").disabled = permissions.length == 0; +} + +function doHelpButton() { + openHelp(permissionsBundle.getString(permissionType + "permissionshelp"), "chrome://communicator/locale/help/suitehelp.rdf"); + return true; +} diff --git a/comm/suite/components/permissions/content/permissionsManager.xul b/comm/suite/components/permissions/content/permissionsManager.xul new file mode 100644 index 0000000000..4231fd1a96 --- /dev/null +++ b/comm/suite/components/permissions/content/permissionsManager.xul @@ -0,0 +1,81 @@ +<?xml version="1.0"?> + +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?> + +<!DOCTYPE dialog SYSTEM "chrome://communicator/locale/permissions/permissionsManager.dtd" > + +<dialog id="permissionsManager" + buttons="accept,cancel,help" + windowtype="exceptions" + title="&windowtitle.label;" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + style="width:32em; height:42em;" + persist="width height screenX screenY" + onload="Startup();" + ondialogaccept="return onAccept();" + ondialogcancel="return onCancel();" + ondialoghelp="return doHelpButton();"> + + <script src="chrome://communicator/content/permissions/permissionsManager.js"/> + <script src="chrome://communicator/content/permissions/permissionsUtils.js"/> + <script src="chrome://global/content/treeUtils.js"/> + <script src="chrome://help/content/contextHelp.js"/> + + <stringbundle id="permissionsBundle" + src="chrome://communicator/locale/permissions/permissionsManager.properties"/> + + <description id="permissionsText"/> + <separator class="thin"/> + <label id="urlLabel" + value="&address.label;" + accesskey="&address.accesskey;" + control="url"/> + <hbox align="start"> + <textbox id="url" flex="1" oninput="handleHostInput(event.target.value);"/> + </hbox> + <hbox pack="end"> + <button id="btnBlock" disabled="true" accesskey="&block.accesskey;" + label="&block.label;" oncommand="addPermission(Ci.nsIPermissionManager.DENY_ACTION);"/> + <button id="btnSession" disabled="true" accesskey="&session.accesskey;" + label="&session.label;" oncommand="addPermission(Ci.nsICookiePermission.ACCESS_SESSION);"/> + <button id="btnAllow" disabled="true" accesskey="&allow.accesskey;" + label="&allow.label;" oncommand="addPermission(Ci.nsIPermissionManager.ALLOW_ACTION);"/> + </hbox> + <separator class="thin"/> + <tree id="permissionsTree" flex="1" style="height: 18em;" + hidecolumnpicker="true" + onkeypress="handlePermissionKeyPress(event)" + onselect="PermissionSelected(this);" + sortAscending="false" + sortColumn="rawHost" + persist="sortAscending sortColumn"> + <treecols> + <treecol id="rawHost" + label="&treehead.sitename.label;" + flex="3" + onclick="permissionColumnSort(this.id, true);" + sortDirection="descending" + persist="width"/> + <splitter class="tree-splitter"/> + <treecol id="capability" + label="&treehead.status.label;" + flex="1" + onclick="permissionColumnSort(this.id, true);" + persist="width"/> + </treecols> + <treechildren/> + </tree> + <separator class="thin"/> + <hbox> + <button id="removePermission" disabled="true" + label="&remove.label;" accesskey="&remove.accesskey;" + oncommand="deletePermissions();"/> + <button id="removeAllPermissions" + label="&removeall.label;" accesskey="&removeall.accesskey;" + oncommand="deleteAllPermissions();"/> + </hbox> +</dialog> diff --git a/comm/suite/components/permissions/content/permissionsUtils.js b/comm/suite/components/permissions/content/permissionsUtils.js new file mode 100644 index 0000000000..4b208f87da --- /dev/null +++ b/comm/suite/components/permissions/content/permissionsUtils.js @@ -0,0 +1,130 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function DeleteAllFromTree + (tree, view, table, deletedTable, removeButton, removeAllButton) { + + gTreeUtils.deleteAll(tree, view, table, deletedTable); + + // disable buttons + document.getElementById(removeButton).setAttribute("disabled", "true") + document.getElementById(removeAllButton).setAttribute("disabled","true"); +} + +function DeleteSelectedItemFromTree + (tree, view, table, deletedTable, removeButton, removeAllButton) { + + gTreeUtils.deleteSelectedItems(tree, view, table, deletedTable); + + // disable buttons if nothing left in the table + if (!table.length) { + document.getElementById(removeButton).setAttribute("disabled", "true") + document.getElementById(removeAllButton).setAttribute("disabled","true"); + } +} + +function GetTreeSelections(tree) { + var selections = []; + var select = tree.view.selection; + if (select) { + var count = select.getRangeCount(); + var min = new Object(); + var max = new Object(); + for (var i=0; i<count; i++) { + select.getRangeAt(i, min, max); + for (var k=min.value; k<=max.value; k++) { + if (k != -1) { + selections[selections.length] = k; + } + } + } + } + return selections; +} + +function SortTree(tree, view, table, column, lastSortColumn, lastSortAscending, updateSelection) { + + // remember which item was selected so we can restore it after the sort + var selections = GetTreeSelections(tree); + var selectedNumber = selections.length ? table[selections[0]].id : -1; + + // do the sort or re-sort + // this is a temporary hack for 1.7, we should implement + // display and sort variables here for trees in general + var sortColumn; + var comparator; + if (column == "expires") { + sortColumn = "expiresSortValue"; + comparator = function compare(a, b) { return a - b; }; + } else { + sortColumn = column; + comparator = function compare(a, b) { + return a.toLowerCase().localeCompare(b.toLowerCase()); + }; + } + if (lastSortColumn == "expires") { + lastSortColumn = "expiresSortValue"; + } + var ascending = gTreeUtils.sort(tree, view, table, sortColumn, comparator, + lastSortColumn, lastSortAscending); + + // restore the selection + if (selectedNumber >= 0 && updateSelection) { + var selectedRow = -1; + for (var s = 0; s < table.length; s++) { + if (table[s].id == selectedNumber) { + selectedRow = s; + break; + } + } + + if (selectedRow > 0) { + // update selection and display the results + tree.view.selection.select(selectedRow); + tree.treeBoxObject.invalidate(); + tree.treeBoxObject.ensureRowIsVisible(selectedRow); + } + } + + return ascending; +} + +function handleHostInput(aValue) { + // trim any leading and trailing spaces and scheme + // and set buttons appropiately + btnDisable(!trimSpacesAndScheme(aValue)); +} + +function trimSpacesAndScheme(aString) { + if (!aString) + return ""; + return aString.trim().replace(/([-\w]*:\/+)?/, ""); +} + +function btnDisable(aDisabled) { + document.getElementById("btnSession").disabled = aDisabled; + document.getElementById("btnBlock").disabled = aDisabled; + document.getElementById("btnAllow").disabled = aDisabled; +} + +function PermissionSelected(tree) { + var hasSelection = tree.view.selection.count > 0; + document.getElementById("removePermission").disabled = !hasSelection; +} + +function SetSortDirection(tree, column, ascending) { + // first we need to get the right elements + for (let col of tree.getElementsByTagName("treecol")) { + if (col.id == column) { + // set the sortDirection attribute to get the styling going + col.setAttribute("sortDirection", ascending ? "ascending" : "descending"); + } + else { + // clear out the sortDirection attribute on the rest of the columns + col.removeAttribute("sortDirection"); + } + } +} diff --git a/comm/suite/components/permissions/jar.mn b/comm/suite/components/permissions/jar.mn new file mode 100644 index 0000000000..15784fc80b --- /dev/null +++ b/comm/suite/components/permissions/jar.mn @@ -0,0 +1,10 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +comm.jar: + content/communicator/permissions/cookieViewer.js (content/cookieViewer.js) + content/communicator/permissions/cookieViewer.xul (content/cookieViewer.xul) + content/communicator/permissions/permissionsManager.js (content/permissionsManager.js) + content/communicator/permissions/permissionsManager.xul (content/permissionsManager.xul) + content/communicator/permissions/permissionsUtils.js (content/permissionsUtils.js) diff --git a/comm/suite/components/permissions/moz.build b/comm/suite/components/permissions/moz.build new file mode 100644 index 0000000000..d988c0ff9b --- /dev/null +++ b/comm/suite/components/permissions/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +JAR_MANIFESTS += ["jar.mn"] |