diff --git a/extensions/xforms/jar.mn b/extensions/xforms/jar.mn index 8b0a6727743..6bbd81afa40 100755 --- a/extensions/xforms/jar.mn +++ b/extensions/xforms/jar.mn @@ -8,6 +8,8 @@ xforms.jar: * content/xforms/xforms-prefs.xul (resources/content/xforms-prefs.xul) * content/xforms/xforms-prefs-ui.xul (resources/content/xforms-prefs-ui.xul) * content/xforms/xforms-prefs.js (resources/content/xforms-prefs.js) +* content/xforms/xforms-permissions.xul (resources/content/xforms-permissions.xul) +* content/xforms/xforms-permissions.js (resources/content/xforms-permissions.js) content/xforms/widgets.xml (resources/content/widgets.xml) content/xforms/widgets-xhtml.xml (resources/content/widgets-xhtml.xml) content/xforms/xforms.xml (resources/content/xforms.xml) diff --git a/extensions/xforms/nsXFormsSubmissionElement.cpp b/extensions/xforms/nsXFormsSubmissionElement.cpp index f8ccb0ab0e0..6d3008e3ed1 100644 --- a/extensions/xforms/nsXFormsSubmissionElement.cpp +++ b/extensions/xforms/nsXFormsSubmissionElement.cpp @@ -888,63 +888,24 @@ nsXFormsSubmissionElement::CheckSameOrigin(nsIDocument *aBaseDocument, baseURI->SchemeIs("file", &allowSubmission); } - // let's check the permission manager - if (!allowSubmission) { - allowSubmission = CheckPermissionManager(baseURI); - } - // if none of the above checks have allowed the submission, we do a // same origin check. if (!allowSubmission) { - allowSubmission = nsXFormsUtils::CheckSameOrigin(aBaseDocument, aTestURI); + // replace instance is both a send and a load + PRUint8 mode; + if (mIsReplaceInstance) + mode = nsXFormsUtils::kXFormsActionLoadSend; + else + mode = nsXFormsUtils::kXFormsActionSend; + + allowSubmission = nsXFormsUtils::CheckSameOrigin(aBaseDocument, aTestURI, + mode); } } return allowSubmission; } -PRBool -nsXFormsSubmissionElement::CheckPermissionManager(nsIURI *aBaseURI) -{ - PRBool result = PR_FALSE; - - nsresult rv; - nsCOMPtr prefBranch = - do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); - - PRUint32 permission = nsIPermissionManager::UNKNOWN_ACTION; - - if (NS_SUCCEEDED(rv) && prefBranch) { - // check if the user has enabled the xforms cross domain preference - PRBool checkPermission = PR_FALSE; - prefBranch->GetBoolPref("xforms.crossdomain.enabled", &checkPermission); - - if (checkPermission) { - // if the user enabled the cross domain check, query the permission - // manager with the URI. It will return 1 if the URI was allowed by the - // user. - nsCOMPtr permissionManager = - do_GetService("@mozilla.org/permissionmanager;1"); - - nsCOMPtr domDoc; - mElement->GetOwnerDocument(getter_AddRefs(domDoc)); - - nsCOMPtr doc = do_QueryInterface(domDoc); - NS_ENSURE_STATE(doc); - - permissionManager->TestPermission(doc->GetDocumentURI(), - "xforms-xd", &permission); - } - } - - if (permission == nsIPermissionManager::ALLOW_ACTION) { - // not in the permission manager - result = PR_TRUE; - } - - return result; -} - nsresult nsXFormsSubmissionElement::AddNameSpaces(nsIDOMElement *aTarget, nsIDOMNode *aSource, diff --git a/extensions/xforms/nsXFormsSubmissionElement.h b/extensions/xforms/nsXFormsSubmissionElement.h index 004d0f8a7a4..cf8f5e1cc1e 100644 --- a/extensions/xforms/nsXFormsSubmissionElement.h +++ b/extensions/xforms/nsXFormsSubmissionElement.h @@ -161,7 +161,6 @@ private: * there is no need for a same origin check. */ PRBool CheckSameOrigin(nsIDocument *aBaseDocument, nsIURI *aTestURI); - PRBool CheckPermissionManager(nsIURI *aBaseURI); nsresult AddNameSpaces(nsIDOMElement* aTarget, nsIDOMNode* aSource, nsStringHashSet* aPrefixHash); nsresult GetIncludeNSPrefixesAttr(nsStringHashSet** aHash); diff --git a/extensions/xforms/nsXFormsUtils.cpp b/extensions/xforms/nsXFormsUtils.cpp index abc21b904f0..19ba23aa204 100644 --- a/extensions/xforms/nsXFormsUtils.cpp +++ b/extensions/xforms/nsXFormsUtils.cpp @@ -1301,7 +1301,8 @@ nsXFormsUtils::FindParentContext(nsIDOMElement *aElement, } /* static */ PRBool -nsXFormsUtils::CheckSameOrigin(nsIDocument *aBaseDocument, nsIURI *aTestURI) +nsXFormsUtils::CheckSameOrigin(nsIDocument *aBaseDocument, nsIURI *aTestURI, + PRUint8 aType) { nsresult rv; @@ -1342,9 +1343,12 @@ nsXFormsUtils::CheckSameOrigin(nsIDocument *aBaseDocument, nsIURI *aTestURI) if (NS_SUCCEEDED(rv)) { PRUint32 perm; - rv = permMgr->TestPermission(principalURI, "xforms-load", &perm); - if (NS_SUCCEEDED(rv) && perm == nsIPermissionManager::ALLOW_ACTION) - return PR_TRUE; + rv = permMgr->TestPermission(principalURI, "xforms-xd", &perm); + + if (NS_SUCCEEDED(rv) && perm != nsIPermissionManager::UNKNOWN_ACTION) { + if (perm == kXFormsActionLoadSend || perm == aType) + return PR_TRUE; + } } return PR_FALSE; diff --git a/extensions/xforms/nsXFormsUtils.h b/extensions/xforms/nsXFormsUtils.h index db57c22a65c..1084e867eab 100644 --- a/extensions/xforms/nsXFormsUtils.h +++ b/extensions/xforms/nsXFormsUtils.h @@ -372,11 +372,26 @@ public: PRInt32 *aContextPosition, PRInt32 *aContextSize); + /** CheckSameOrigin takes in the connection type. We either: + * - Send data, such as doing submission + * - Load data, such as getting external instance data + * - Send and Load data, which is replace instance + */ + static const PRUint8 kXFormsActionSend = 1; + static const PRUint8 kXFormsActionLoad = 2; + static const PRUint8 kXFormsActionLoadSend = 3; + /** - * @return true if aTestURI has the same origin as aBaseDocument + * @param aBaseDocument The document the XForms lives in + * @param aTestURI The uri we are trying to connect to + * @param aType The connection type (see above 3 consts) + * @return true if aTestURI has the same origin as aBaseDocument or + * the user has allowed the connection type using the permission + * manager. */ static NS_HIDDEN_(PRBool) CheckSameOrigin(nsIDocument *aBaseDocument, - nsIURI *aTestURI); + nsIURI *aTestURI, + PRUint8 aType = kXFormsActionLoad); /** * @return true if aNode is element, its namespace URI is diff --git a/extensions/xforms/resources/content/xforms-permissions.js b/extensions/xforms/resources/content/xforms-permissions.js new file mode 100644 index 00000000000..207fc8946ea --- /dev/null +++ b/extensions/xforms/resources/content/xforms-permissions.js @@ -0,0 +1,432 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is the Firefox Preferences System. +# +# The Initial Developer of the Original Code is +# Ben Goodger. +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Ben Goodger +# Blake Ross +# Doron Rosenberg +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +const nsIPermissionManager = Components.interfaces.nsIPermissionManager; + +function Permission(host, rawHost, type, capability, perm) +{ + this.host = host; + this.rawHost = rawHost; + this.type = type; + this.capability = capability; + this.perm = perm; +} + +var gPermissionManager = { + _type : "", + _permissions : [], + _pm : Components.classes["@mozilla.org/permissionmanager;1"] + .getService(Components.interfaces.nsIPermissionManager), + _bundle : null, + _xformsBundle : null, + _tree : null, + _loaded : false, + + _view: { + _rowCount: 0, + get rowCount() + { + return this._rowCount; + }, + getCellText: function (aRow, aColumn) + { + if (aColumn.id == "siteCol") + return gPermissionManager._permissions[aRow].rawHost; + else if (aColumn.id == "statusCol") + return gPermissionManager._permissions[aRow].capability; + return ""; + }, + + isSeparator: function(aIndex) { return false; }, + isSorted: function() { return false; }, + isContainer: function(aIndex) { return false; }, + setTree: function(aTree){}, + getImageSrc: function(aRow, aColumn) {}, + getProgressMode: function(aRow, aColumn) {}, + getCellValue: function(aRow, aColumn) {}, + cycleHeader: function(column) {}, + getRowProperties: function(row,prop){}, + getColumnProperties: function(column,prop){}, + getCellProperties: function(row,column,prop){} + }, + + _getCapabilityString: function (aCapability) + { + var stringKey = null; + switch (aCapability) { + case 1: + stringKey = "xformsXDPermissionSend"; + break; + case 2: + stringKey = "xformsXDPermissionLoad"; + break; + case 3: + stringKey = "xformsXDPermissionLoadSend"; + break; + } + + return this._xformsBundle.getString(stringKey); + }, + + addPermission: function (aIsSave) + { + var textbox = document.getElementById("url"); + var host = textbox.value.replace(/^\s*([-\w]*:\/+)?/, ""); // trim any leading space and scheme + try { + var ioService = Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService); + var uri = ioService.newURI("http://"+host, null, null); + host = uri.host; + } catch(ex) { + var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .getService(Components.interfaces.nsIPromptService); + var message = this._bundle.getString("invalidURI"); + var title = this._bundle.getString("invalidURITitle"); + promptService.alert(window, title, message); + return; + } + + var permission = 0; + var canLoad = document.getElementById("checkLoad").checked; + var canSend = document.getElementById("checkSend").checked; + + if (!canLoad && !canSend) { + // invalid + return; + } else if (canLoad && canSend) { + permission = 3; + } else { + permission = canSend ? 1 : 2; + } + + // check whether the permission already exists, if not, add it + var exists = false; + for (var i = 0; i < this._permissions.length; ++i) { + // check if the host and the permission matches + if (this._permissions[i].rawHost == host) { + var capabilityString = this._getCapabilityString(permission); + this._permissions[i].capability = capabilityString; + this._permissions[i].perm = permission; + + if (this._permissions[i].perm == permission) { + exists = true; + } + break; + } + } + + if (!exists) { + if (aIsSave) { + // if it doesn't exist, but we were saving, remove the entry we were + // editing. + var index = this._tree.currentIndex; + var oldurl = this._tree.view.getCellText(index, this._tree.columns.getFirstColumn()); + + if (oldurl != host) { + this.onPermissionDeleted(); + } + } + + host = (host.charAt(0) == ".") ? host.substring(1,host.length) : host; + var uri = ioService.newURI("http://" + host, null, null); + this._pm.add(uri, this._type, permission); + } + + // clear the checkboxes + document.getElementById("checkLoad").checked = false; + document.getElementById("checkSend").checked = false; + + this.clear(); + textbox.focus(); + + // covers a case where the site exists already, so the buttons don't disable + this.onHostInput(textbox); + + // enable "remove all" button as needed + document.getElementById("removeAllPermissions").disabled = this._permissions.length == 0; + }, + + onHostInput: function () + { + // if no checkbox selected, disable button. + if (!document.getElementById("checkLoad").checked && + !document.getElementById("checkSend").checked) { + document.getElementById("btnAdd").disabled = true; + document.getElementById("btnSave").disabled = true; + } else { + var input = document.getElementById("url"); + document.getElementById("btnAdd").disabled = !input.value; + document.getElementById("btnSave").disabled = !input.value; + } + }, + + onHostKeyPress: function (aEvent) + { + if (aEvent.keyCode == 13) + gPermissionManager.addPermission(); + }, + + onLoad: function () + { + this._bundle = document.getElementById("bundlePreferences"); + this._xformsBundle = document.getElementById("xformsBundle"); + + var params = window.arguments[0]; + this.init(params); + + // clear selection + this._tree.view.selection.clearSelection(); + + this._loaded = true; + }, + + init: function (aParams) + { + this._type = aParams.permissionType; + + var permissionsText = document.getElementById("permissionsText"); + while (permissionsText.hasChildNodes()) + permissionsText.removeChild(permissionsText.firstChild); + permissionsText.appendChild(document.createTextNode(aParams.introText)); + + document.title = aParams.windowTitle; + + var urlField = document.getElementById("url"); + this.onHostInput(urlField); + + var os = Components.classes["@mozilla.org/observer-service;1"] + .getService(Components.interfaces.nsIObserverService); + os.addObserver(this, "perm-changed", false); + + this._loadPermissions(); + + urlField.focus(); + }, + + uninit: function () + { + var os = Components.classes["@mozilla.org/observer-service;1"] + .getService(Components.interfaces.nsIObserverService); + os.removeObserver(this, "perm-changed"); + }, + + observe: function (aSubject, aTopic, aData) + { + if (aTopic == "perm-changed") { + var permission = aSubject.QueryInterface(Components.interfaces.nsIPermission); + if (aData == "added") { + this._addPermissionToList(permission); + ++this._view._rowCount; + this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, 1); + // Re-do the sort, since we inserted this new item at the end. + gTreeUtils.sort(this._tree, this._view, this._permissions, + this._lastPermissionSortColumn, + this._lastPermissionSortAscending); + } + else if (aData == "changed") { + for (var i = 0; i < this._permissions.length; ++i) { + if (this._permissions[i].host == permission.host) { + this._permissions[i].capability = this._getCapabilityString(permission.capability); + break; + } + } + // Re-do the sort, if the status changed from Block to Allow + // or vice versa, since if we're sorted on status, we may no + // longer be in order. + if (this._lastPermissionSortColumn.id == "statusCol") { + gTreeUtils.sort(this._tree, this._view, this._permissions, + this._lastPermissionSortColumn, + this._lastPermissionSortAscending); + } + this._tree.treeBoxObject.invalidate(); + } + // No UI other than this window causes this method to be sent a "deleted" + // notification, so we don't need to implement it since Delete is handled + // directly by the Permission Removal handlers. If that ever changes, those + // implementations will have to move into here. + } + }, + + onPermissionSelected: function () + { + var hasSelection = this._tree.view.selection.count > 0; + var hasRows = this._tree.view.rowCount > 0; + document.getElementById("removePermission").disabled = !hasRows || !hasSelection; + document.getElementById("removeAllPermissions").disabled = !hasRows; + + if (this._loaded && hasSelection && hasRows) { + document.getElementById("btnAdd").hidden = true; + document.getElementById("btnSave").hidden = false; + document.getElementById("btnSave").disabled = false; + + var index = this._tree.currentIndex; + if (index >= 0) { + var url = this._tree.view.getCellText(index, + this._tree.columns.getFirstColumn()); + document.getElementById("url").value = url; + + // look for the permission + for (var i = 0; i < this._permissions.length; ++i) { + if (this._permissions[i].rawHost == url) { + var perm = this._permissions[i].perm; + + document.getElementById("checkLoad").checked = !(perm == 1); + document.getElementById("checkSend").checked = !(perm == 2); + break; + } + } + } + } + }, + + onPermissionDeleted: function () + { + if (!this._view.rowCount) + return; + var removedPermissions = []; + gTreeUtils.deleteSelectedItems(this._tree, this._view, this._permissions, removedPermissions); + for (var i = 0; i < removedPermissions.length; ++i) { + var p = removedPermissions[i]; + this._pm.remove(p.host, p.type); + } + document.getElementById("removePermission").disabled = !this._permissions.length; + document.getElementById("removeAllPermissions").disabled = !this._permissions.length; + }, + + onAllPermissionsDeleted: function () + { + if (!this._view.rowCount) + return; + var removedPermissions = []; + gTreeUtils.deleteAll(this._tree, this._view, this._permissions, removedPermissions); + for (var i = 0; i < removedPermissions.length; ++i) { + var p = removedPermissions[i]; + this._pm.remove(p.host, p.type); + } + document.getElementById("removePermission").disabled = true; + document.getElementById("removeAllPermissions").disabled = true; + }, + + onPermissionKeyPress: function (aEvent) + { + if (aEvent.keyCode == 46) + this.onPermissionDeleted(); + }, + + _lastPermissionSortColumn: "", + _lastPermissionSortAscending: false, + + onPermissionSort: function (aColumn) + { + this._lastPermissionSortAscending = gTreeUtils.sort(this._tree, + this._view, + this._permissions, + aColumn, + this._lastPermissionSortColumn, + this._lastPermissionSortAscending); + this._lastPermissionSortColumn = aColumn; + }, + + _loadPermissions: function () + { + this._tree = document.getElementById("permissionsTree"); + this._permissions = []; + + // load permissions into a table + var count = 0; + var enumerator = this._pm.enumerator; + while (enumerator.hasMoreElements()) { + var nextPermission = enumerator.getNext().QueryInterface(Components.interfaces.nsIPermission); + this._addPermissionToList(nextPermission); + } + + this._view._rowCount = this._permissions.length; + + // sort and display the table + this._tree.treeBoxObject.view = this._view; + this.onPermissionSort("rawHost", false); + + // disable "remove all" button if there are none + document.getElementById("removeAllPermissions").disabled = this._permissions.length == 0; + }, + + _addPermissionToList: function (aPermission) + { + if (aPermission.type == this._type) { + var host = aPermission.host; + var capabilityString = this._getCapabilityString(aPermission.capability); + var p = new Permission(host, + (host.charAt(0) == ".") ? host.substring(1,host.length) : host, + aPermission.type, + capabilityString, + aPermission.capability); + this._permissions.push(p); + } + }, + + setHost: function (aHost) + { + document.getElementById("url").value = aHost; + }, + + clear: function () + { + document.getElementById("url").value = ""; + document.getElementById("checkLoad").checked = false; + document.getElementById("checkSend").checked = false; + document.getElementById("btnAdd").disabled = true; + + document.getElementById("btnAdd").hidden = false; + document.getElementById("btnSave").hidden = true; + document.getElementById("btnSave").disabled = false; + + this._tree.view.selection.clearSelection(); + } +}; + +function setHost(aHost) +{ + gPermissionManager.setHost(aHost); +} + +function initWithParams(aParams) +{ + gPermissionManager.init(aParams); +} + diff --git a/extensions/xforms/resources/content/xforms-permissions.xul b/extensions/xforms/resources/content/xforms-permissions.xul new file mode 100644 index 00000000000..45fd049ee54 --- /dev/null +++ b/extensions/xforms/resources/content/xforms-permissions.xul @@ -0,0 +1,133 @@ + +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Blake Ross. +# Portions created by the Initial Developer are Copyright (C) 2003 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Blake Ross +# Ben Goodger +# Doron Rosenberg +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + + + + + + %permissionDTD; + + %xformsDTD; +]> + + + +