Bug 481603: Add a way to unload JS modules loaded with Components.utils.import. r=mrbkap

This commit is contained in:
Dave Townsend 2011-06-15 11:08:43 -07:00
parent 6d98f14885
commit 508ce88566
6 changed files with 170 additions and 4 deletions

View File

@ -48,14 +48,14 @@ struct JSObject;
[ptr] native JSObjectPtr(JSObject);
[scriptable, uuid(89da3673-e699-4f26-9ed7-11a528011434)]
[scriptable, uuid(3f945a8e-58ca-47ba-a789-82d022e837fd)]
interface xpcIJSModuleLoader : nsISupports
{
/**
* To be called from JavaScript only.
*
* Synchronously loads and evaluates the js file located at
* 'registryLocation' with a new, fully privileged global object.
* aResourceURI with a new, fully privileged global object.
*
* If 'targetObj' is specified and equal to null, returns the
* module's global object. Otherwise (if 'targetObj' is not
@ -85,11 +85,19 @@ interface xpcIJSModuleLoader : nsISupports
/* , [optional] in JSObject targetObj */);
/**
* Imports the JS module at 'registryLocation' to the JS object
* Imports the JS module at aResourceURI to the JS object
* 'targetObj' (if != null) as described for importModule() and
* returns the module's global object.
*/
[noscript] JSObjectPtr importInto(in AUTF8String aResourceURI,
in JSObjectPtr targetObj,
in nsAXPCNativeCallContextPtr cc);
/**
* Unloads the JS module at aResourceURI. Existing references to the module
* will continue to work but any subsequent import of the module will
* reload it and give new reference. If the JS module hasn't yet been imported
* then this method will do nothing.
*/
void unload(in AUTF8String aResourceURI);
};

View File

@ -123,7 +123,7 @@ interface nsIXPCComponents_utils_Sandbox : nsISupports
/**
* interface of Components.utils
*/
[scriptable, uuid(c1d616fa-1875-49ba-b7b8-5861dab31a42)]
[scriptable, uuid(5f0acf45-135a-48d1-976c-082ce3b24ead)]
interface nsIXPCComponents_Utils : nsISupports
{
@ -204,6 +204,16 @@ interface nsIXPCComponents_Utils : nsISupports
void /* JSObject */ import(in AUTF8String registryLocation
/*, [optional] in JSObject targetObj */);
/*
* Unloads the JS module at 'registryLocation'. Existing references to the
* module will continue to work but any subsequent import of the module will
* reload it and give new reference. If the JS module hasn't yet been
* imported then this method will do nothing.
*
* @param resourceURI A resource:// URI string to unload the module from.
*/
void unload(in AUTF8String registryLocation);
/*
* To be called from JS only.
*

View File

@ -1601,6 +1601,76 @@ mozJSComponentLoader::ImportInto(const nsACString & aLocation,
return NS_OK;
}
NS_IMETHODIMP
mozJSComponentLoader::Unload(const nsACString & aLocation)
{
nsresult rv;
if (!mInitialized) {
return NS_OK;
}
nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
NS_ENSURE_SUCCESS(rv, rv);
// Get the URI.
nsCOMPtr<nsIURI> resURI;
rv = ioService->NewURI(aLocation, nsnull, nsnull, getter_AddRefs(resURI));
NS_ENSURE_SUCCESS(rv, rv);
// figure out the resolved URI
nsCOMPtr<nsIChannel> scriptChannel;
rv = ioService->NewChannelFromURI(resURI, getter_AddRefs(scriptChannel));
NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
nsCOMPtr<nsIURI> resolvedURI;
rv = scriptChannel->GetURI(getter_AddRefs(resolvedURI));
NS_ENSURE_SUCCESS(rv, rv);
// get the JAR if there is one
nsCOMPtr<nsIJARURI> jarURI;
jarURI = do_QueryInterface(resolvedURI, &rv);
nsCOMPtr<nsIFileURL> baseFileURL;
nsCAutoString jarEntry;
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIURI> baseURI;
rv = jarURI->GetJARFile(getter_AddRefs(baseURI));
NS_ENSURE_SUCCESS(rv, rv);
baseFileURL = do_QueryInterface(baseURI, &rv);
NS_ENSURE_SUCCESS(rv, rv);
jarURI->GetJAREntry(jarEntry);
NS_ENSURE_SUCCESS(rv, rv);
} else {
baseFileURL = do_QueryInterface(resolvedURI, &rv);
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsIFile> sourceFile;
rv = baseFileURL->GetFile(getter_AddRefs(sourceFile));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsILocalFile> sourceLocalFile;
sourceLocalFile = do_QueryInterface(sourceFile, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString key;
if (jarEntry.IsEmpty()) {
rv = FileKey(sourceLocalFile, key);
} else {
rv = JarKey(sourceLocalFile, jarEntry, key);
}
NS_ENSURE_SUCCESS(rv, rv);
ModuleEntry* mod;
if (mImports.Get(key, &mod)) {
mImports.Remove(key);
}
return NS_OK;
}
NS_IMETHODIMP
mozJSComponentLoader::Observe(nsISupports *subject, const char *topic,
const PRUnichar *data)

View File

@ -3725,6 +3725,18 @@ nsXPCComponents_Utils::Import(const nsACString & registryLocation)
return moduleloader->Import(registryLocation);
}
/* unload (in AUTF8String registryLocation);
*/
NS_IMETHODIMP
nsXPCComponents_Utils::Unload(const nsACString & registryLocation)
{
nsCOMPtr<xpcIJSModuleLoader> moduleloader =
do_GetService(MOZJSCOMPONENTLOADER_CONTRACTID);
if (!moduleloader)
return NS_ERROR_FAILURE;
return moduleloader->Unload(registryLocation);
}
/* xpcIJSWeakReference getWeakReference (); */
NS_IMETHODIMP
nsXPCComponents_Utils::GetWeakReference(xpcIJSWeakReference **_retval)

View File

@ -0,0 +1,65 @@
/*
* ***** 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 code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Dave Townsend <dtownsend@oxymoronical.com> (original author)
*
* 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 ***** */
function run_test() {
var scope1 = {};
var global1 = Components.utils.import("resource://gre/modules/NetUtil.jsm", scope1);
var scope2 = {};
var global2 = Components.utils.import("resource://gre/modules/NetUtil.jsm", scope2);
do_check_true(global1 === global2);
do_check_true(scope1.NetUtil === scope2.NetUtil);
Components.utils.unload("resource://gre/modules/NetUtil.jsm");
var scope3 = {};
var global3 = Components.utils.import("resource://gre/modules/NetUtil.jsm", scope3);
do_check_false(global1 === global3);
do_check_false(scope1.NetUtil === scope3.NetUtil);
// Both instances should work
uri1 = scope1.NetUtil.newURI("http://www.example.com");
do_check_true(uri1 instanceof Components.interfaces.nsIURL);
var uri3 = scope3.NetUtil.newURI("http://www.example.com");
do_check_true(uri3 instanceof Components.interfaces.nsIURL);
do_check_true(uri1.equals(uri3));
}

View File

@ -15,3 +15,4 @@ tail =
[test_localeCompare.js]
[test_recursive_import.js]
[test_xpcomutils.js]
[test_unload.js]