mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 15:52:07 +00:00
Bug 472674 - Allow XPCNativeWrapper to unwrap SJOWs again. r+sr=jst
This commit is contained in:
parent
52f4f5da62
commit
60c9e5280e
@ -2271,7 +2271,6 @@ nsJSContext::GetGlobalObject()
|
||||
|
||||
if (!c || ((~c->flags) & (JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_PRIVATE_IS_NSISUPPORTS))) {
|
||||
NS_WARNING("Global is not an nsISupports.");
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
|
@ -155,7 +155,9 @@ ShouldBypassNativeWrapper(JSContext *cx, JSObject *obj)
|
||||
#define XPC_NW_BYPASS_BASE(cx, obj, code) \
|
||||
JS_BEGIN_MACRO \
|
||||
if (ShouldBypassNativeWrapper(cx, obj)) { \
|
||||
XPCWrappedNative *wn_ = XPCNativeWrapper::GetWrappedNative(obj); \
|
||||
/* Use SafeGetWrappedNative since obj can't be an explicit native \
|
||||
wrapper. */ \
|
||||
XPCWrappedNative *wn_ = XPCNativeWrapper::SafeGetWrappedNative(obj); \
|
||||
if (!wn_) { \
|
||||
return JS_TRUE; \
|
||||
} \
|
||||
@ -193,6 +195,45 @@ static inline
|
||||
JSBool
|
||||
EnsureLegalActivity(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
|
||||
if (!ssm) {
|
||||
// If there's no security manager, then we're not running in a browser
|
||||
// context: allow access.
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSStackFrame *fp;
|
||||
nsIPrincipal *subjectPrincipal = ssm->GetCxSubjectPrincipalAndFrame(cx, &fp);
|
||||
if (!subjectPrincipal || !fp) {
|
||||
// We must allow the access if there is no code running.
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
// This might be chrome code or content code with UniversalXPConnect.
|
||||
void *annotation = JS_GetFrameAnnotation(cx, fp);
|
||||
PRBool isPrivileged = PR_FALSE;
|
||||
nsresult rv = subjectPrincipal->IsCapabilityEnabled("UniversalXPConnect",
|
||||
annotation,
|
||||
&isPrivileged);
|
||||
if (NS_SUCCEEDED(rv) && isPrivileged) {
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
// We're in unprivileged code, ensure that we're allowed to access the
|
||||
// underlying object.
|
||||
XPCWrappedNative *wn = XPCNativeWrapper::SafeGetWrappedNative(obj);
|
||||
if (wn) {
|
||||
nsIPrincipal *objectPrincipal = wn->GetScope()->GetPrincipal();
|
||||
PRBool subsumes;
|
||||
if (NS_FAILED(subjectPrincipal->Subsumes(objectPrincipal, &subsumes)) ||
|
||||
!subsumes) {
|
||||
return ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx);
|
||||
}
|
||||
}
|
||||
|
||||
// The underlying object is accessible, but this might be the wrong
|
||||
// type of wrapper to access it through.
|
||||
// TODO This should just be an assertion now.
|
||||
jsval flags;
|
||||
|
||||
::JS_GetReservedSlot(cx, obj, 0, &flags);
|
||||
@ -201,35 +242,65 @@ EnsureLegalActivity(JSContext *cx, JSObject *obj)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSStackFrame *frame = nsnull;
|
||||
uint32 fileFlags = JS_GetTopScriptFilenameFlags(cx, NULL);
|
||||
if (!JS_FrameIterator(cx, &frame) ||
|
||||
fileFlags == JSFILENAME_NULL ||
|
||||
(fileFlags & JSFILENAME_SYSTEM)) {
|
||||
JSScript *script = JS_GetFrameScript(cx, fp);
|
||||
uint32 fileFlags = JS_GetScriptFilenameFlags(script);
|
||||
if (fileFlags == JSFILENAME_NULL || (fileFlags & JSFILENAME_SYSTEM)) {
|
||||
// We expect implicit native wrappers in system files.
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
|
||||
if (!ssm) {
|
||||
// If there's no security manager, then we're not running in a browser
|
||||
// context: allow access.
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
// A last ditch effort to allow access: if the currently-running code
|
||||
// has UniversalXPConnect privileges, then allow access.
|
||||
PRBool isPrivileged;
|
||||
nsresult rv = ssm->IsCapabilityEnabled("UniversalXPConnect", &isPrivileged);
|
||||
if (NS_SUCCEEDED(rv) && isPrivileged) {
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
// Otherwise, we're looking at a non-system file with a handle on an
|
||||
// implicit wrapper. This is a bug! Deny access.
|
||||
return ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx);
|
||||
}
|
||||
|
||||
// static
|
||||
JSBool
|
||||
XPCNativeWrapper::GetWrappedNative(JSContext *cx, JSObject *obj,
|
||||
XPCWrappedNative **aWrappedNative)
|
||||
{
|
||||
XPCWrappedNative *wn = static_cast<XPCWrappedNative *>(xpc_GetJSPrivate(obj));
|
||||
*aWrappedNative = wn;
|
||||
if (!wn) {
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
|
||||
if (!ssm) {
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSStackFrame *fp;
|
||||
nsIPrincipal *subjectPrincipal = ssm->GetCxSubjectPrincipalAndFrame(cx, &fp);
|
||||
if (!subjectPrincipal) {
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
if (fp) {
|
||||
void *annotation = JS_GetFrameAnnotation(cx, fp);
|
||||
|
||||
PRBool isPrivileged;
|
||||
nsresult rv =
|
||||
subjectPrincipal->IsCapabilityEnabled("UniversalXPConnect",
|
||||
annotation,
|
||||
&isPrivileged);
|
||||
if (NS_SUCCEEDED(rv) && isPrivileged) {
|
||||
return JS_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
XPCWrappedNativeScope *scope = wn->GetScope();
|
||||
nsIPrincipal *objectPrincipal = scope->GetPrincipal();
|
||||
|
||||
PRBool subsumes;
|
||||
nsresult rv = subjectPrincipal->Subsumes(objectPrincipal, &subsumes);
|
||||
if (NS_FAILED(rv) || !subsumes) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool
|
||||
XPC_NW_WrapFunction(JSContext* cx, JSObject* funobj, jsval *rval)
|
||||
{
|
||||
@ -373,7 +444,7 @@ XPC_NW_RewrapIfDeepWrapper(JSContext *cx, JSObject *obj, jsval v, jsval *rval)
|
||||
#ifdef DEBUG_XPCNativeWrapper
|
||||
printf("Rewrapping for deep explicit wrapper\n");
|
||||
#endif
|
||||
if (wrappedNative == XPCNativeWrapper::GetWrappedNative(obj)) {
|
||||
if (wrappedNative == XPCNativeWrapper::SafeGetWrappedNative(obj)) {
|
||||
// Already wrapped, return the wrapper.
|
||||
*rval = OBJECT_TO_JSVAL(obj);
|
||||
return JS_TRUE;
|
||||
@ -427,9 +498,11 @@ XPC_NW_FunctionWrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
// The real method we're going to call is the parent of this
|
||||
// function's JSObject.
|
||||
JSObject *methodToCallObj = STOBJ_GET_PARENT(funObj);
|
||||
XPCWrappedNative *wrappedNative = XPCNativeWrapper::GetWrappedNative(obj);
|
||||
XPCWrappedNative *wrappedNative;
|
||||
|
||||
if (!::JS_ObjectIsFunction(cx, methodToCallObj) || !wrappedNative) {
|
||||
if (!XPCNativeWrapper::GetWrappedNative(cx, obj, &wrappedNative) ||
|
||||
!::JS_ObjectIsFunction(cx, methodToCallObj) ||
|
||||
!wrappedNative) {
|
||||
return ThrowException(NS_ERROR_UNEXPECTED, cx);
|
||||
}
|
||||
|
||||
@ -469,7 +542,8 @@ XPC_NW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp,
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
XPCWrappedNative *wrappedNative = XPCNativeWrapper::GetWrappedNative(obj);
|
||||
// Protected by EnsureLegalActivity.
|
||||
XPCWrappedNative *wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj);
|
||||
|
||||
if (!wrappedNative) {
|
||||
return ThrowException(NS_ERROR_INVALID_ARG, cx);
|
||||
@ -548,7 +622,8 @@ XPC_NW_Enumerate(JSContext *cx, JSObject *obj)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
XPCWrappedNative *wn = XPCNativeWrapper::GetWrappedNative(obj);
|
||||
// Protected by EnsureLegalActivity.
|
||||
XPCWrappedNative *wn = XPCNativeWrapper::SafeGetWrappedNative(obj);
|
||||
if (!wn) {
|
||||
return JS_TRUE;
|
||||
}
|
||||
@ -585,7 +660,8 @@ XPC_NW_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
|
||||
// the wrapped native's object.
|
||||
|
||||
if (ShouldBypassNativeWrapper(cx, obj)) {
|
||||
XPCWrappedNative *wn = XPCNativeWrapper::GetWrappedNative(obj);
|
||||
// Protected by EnsureLegalActivity.
|
||||
XPCWrappedNative *wn = XPCNativeWrapper::SafeGetWrappedNative(obj);
|
||||
if (!wn) {
|
||||
return JS_TRUE;
|
||||
}
|
||||
@ -622,7 +698,8 @@ XPC_NW_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
|
||||
}
|
||||
}
|
||||
|
||||
XPCWrappedNative *wrappedNative = XPCNativeWrapper::GetWrappedNative(obj);
|
||||
// Protected by EnsureLegalActivity.
|
||||
XPCWrappedNative *wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj);
|
||||
|
||||
if (!wrappedNative) {
|
||||
// No wrapped native, no properties.
|
||||
@ -677,7 +754,8 @@ XPC_NW_CheckAccess(JSContext *cx, JSObject *obj, jsval id,
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
XPCWrappedNative *wrappedNative = XPCNativeWrapper::GetWrappedNative(obj);
|
||||
// This function does its own security checks.
|
||||
XPCWrappedNative *wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj);
|
||||
if (!wrappedNative) {
|
||||
return JS_TRUE;
|
||||
}
|
||||
@ -722,7 +800,12 @@ XPC_NW_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
|
||||
XPC_NW_BYPASS_TEST(cx, obj, construct, (cx, obj, argc, argv, rval));
|
||||
|
||||
XPCWrappedNative *wrappedNative = XPCNativeWrapper::GetWrappedNative(obj);
|
||||
if (!EnsureLegalActivity(cx, obj)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
// Protected by EnsureLegalActivity.
|
||||
XPCWrappedNative *wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj);
|
||||
if (!wrappedNative) {
|
||||
return JS_TRUE;
|
||||
}
|
||||
@ -802,11 +885,23 @@ XPCNativeWrapperCtor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
|
||||
JSObject *nativeObj = JSVAL_TO_OBJECT(native);
|
||||
|
||||
// Not allowed to go from more restrictive wrappers to less restrictive
|
||||
// wrappers.
|
||||
if (STOBJ_GET_CLASS(nativeObj) == &sXPC_XOW_JSClass.base ||
|
||||
STOBJ_GET_CLASS(nativeObj) == &sXPC_SJOW_JSClass.base) {
|
||||
return ThrowException(NS_ERROR_XPC_BAD_CONVERT_JS, cx);
|
||||
// Unwrap a cross origin wrapper, since we're more restrictive than it is.
|
||||
if (STOBJ_GET_CLASS(nativeObj) == &sXPC_XOW_JSClass.base) {
|
||||
jsval v;
|
||||
if (!::JS_GetReservedSlot(cx, nativeObj, XPCWrapper::sWrappedObjSlot, &v)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
// If v is primitive, allow nativeObj to remain a cross origin wrapper,
|
||||
// which will fail below (since it isn't a wrapped native).
|
||||
if (!JSVAL_IS_PRIMITIVE(v)) {
|
||||
nativeObj = JSVAL_TO_OBJECT(v);
|
||||
}
|
||||
} else if (STOBJ_GET_CLASS(nativeObj) == &sXPC_SJOW_JSClass.base) {
|
||||
// Also unwrap SJOWs.
|
||||
nativeObj = JS_GetParent(cx, nativeObj);
|
||||
if (!nativeObj) {
|
||||
return ThrowException(NS_ERROR_XPC_BAD_CONVERT_JS, cx);
|
||||
}
|
||||
}
|
||||
|
||||
XPCWrappedNative *wrappedNative;
|
||||
@ -819,7 +914,8 @@ XPCNativeWrapperCtor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
printf("Wrapping already wrapped object\n");
|
||||
#endif
|
||||
|
||||
wrappedNative = XPCNativeWrapper::GetWrappedNative(nativeObj);
|
||||
// It's always safe to re-wrap an object.
|
||||
wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(nativeObj);
|
||||
|
||||
if (!wrappedNative) {
|
||||
return ThrowException(NS_ERROR_INVALID_ARG, cx);
|
||||
@ -963,7 +1059,8 @@ XPCNativeWrapperCtor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
static void
|
||||
XPC_NW_Trace(JSTracer *trc, JSObject *obj)
|
||||
{
|
||||
XPCWrappedNative *wrappedNative = XPCNativeWrapper::GetWrappedNative(obj);
|
||||
// Untrusted code can't trigger this.
|
||||
XPCWrappedNative *wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj);
|
||||
|
||||
if (wrappedNative && wrappedNative->IsValid()) {
|
||||
JS_CALL_OBJECT_TRACER(trc, wrappedNative->GetFlatJSObject(),
|
||||
@ -978,13 +1075,18 @@ XPC_NW_Equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
|
||||
"Uh, we should only ever be called for XPCNativeWrapper "
|
||||
"objects!");
|
||||
|
||||
if (!EnsureLegalActivity(cx, obj)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (JSVAL_IS_PRIMITIVE(v)) {
|
||||
*bp = JS_FALSE;
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
XPCWrappedNative *wrappedNative = XPCNativeWrapper::GetWrappedNative(obj);
|
||||
// Protected by EnsureLegalActivity.
|
||||
XPCWrappedNative *wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj);
|
||||
|
||||
if (wrappedNative && wrappedNative->IsValid() &&
|
||||
NATIVE_HAS_FLAG(wrappedNative, WantEquality)) {
|
||||
@ -1020,7 +1122,8 @@ XPC_NW_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
XPCWrappedNative *wrappedNative = XPCNativeWrapper::GetWrappedNative(obj);
|
||||
// Protected by EnsureLegalActivity.
|
||||
XPCWrappedNative *wrappedNative = XPCNativeWrapper::SafeGetWrappedNative(obj);
|
||||
|
||||
if (!wrappedNative) {
|
||||
// toString() called on XPCNativeWrapper.prototype
|
||||
|
@ -61,11 +61,16 @@ public:
|
||||
return STOBJ_GET_CLASS(obj) == &sXPC_NW_JSClass.base;
|
||||
}
|
||||
|
||||
static XPCWrappedNative *GetWrappedNative(JSObject *obj)
|
||||
static JSBool GetWrappedNative(JSContext *cx, JSObject *obj,
|
||||
XPCWrappedNative **aWrappedNative);
|
||||
|
||||
// NB: Use the following carefully.
|
||||
static XPCWrappedNative *SafeGetWrappedNative(JSObject *obj)
|
||||
{
|
||||
return (XPCWrappedNative *)xpc_GetJSPrivate(obj);
|
||||
return static_cast<XPCWrappedNative *>(xpc_GetJSPrivate(obj));
|
||||
}
|
||||
|
||||
|
||||
static JSClass *GetJSClass()
|
||||
{
|
||||
return &sXPC_NW_JSClass.base;
|
||||
|
@ -535,8 +535,8 @@ xpc_qsUnwrapThisImpl(JSContext *cx,
|
||||
}
|
||||
else if(XPCNativeWrapper::IsNativeWrapperClass(clazz))
|
||||
{
|
||||
wrapper = XPCNativeWrapper::GetWrappedNative(cur);
|
||||
if(!wrapper)
|
||||
if(!XPCNativeWrapper::GetWrappedNative(cx, cur, &wrapper) ||
|
||||
!wrapper)
|
||||
goto next;
|
||||
}
|
||||
else if(IsXPCSafeJSObjectWrapperClass(clazz))
|
||||
|
@ -1444,8 +1444,8 @@ return_tearoff:
|
||||
|
||||
if(XPCNativeWrapper::IsNativeWrapperClass(clazz))
|
||||
{
|
||||
XPCWrappedNative* wrapper = XPCNativeWrapper::GetWrappedNative(cur);
|
||||
if(wrapper)
|
||||
XPCWrappedNative* wrapper;
|
||||
if(XPCNativeWrapper::GetWrappedNative(cx, cur, &wrapper) && wrapper)
|
||||
return GetWrappedNativeOfJSObject(cx, wrapper->GetFlatJSObject(),
|
||||
funobj, pobj2, pTearOff);
|
||||
}
|
||||
|
@ -736,7 +736,9 @@ XPC_GetIdentityObject(JSContext *cx, JSObject *obj)
|
||||
XPCWrappedNative *wrapper;
|
||||
|
||||
if(XPCNativeWrapper::IsNativeWrapper(obj))
|
||||
wrapper = XPCNativeWrapper::GetWrappedNative(obj);
|
||||
// Note: It's okay to use SafeGetWrappedNative here since we only do
|
||||
// identity checking on the returned object.
|
||||
wrapper = XPCNativeWrapper::SafeGetWrappedNative(obj);
|
||||
else
|
||||
wrapper = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj);
|
||||
|
||||
|
@ -52,6 +52,8 @@
|
||||
'SJOWs equality hook returns true correctly');
|
||||
}
|
||||
|
||||
ok(new XPCSafeJSObjectWrapper(window) == new XPCNativeWrapper(window),
|
||||
'SJOWs equality hook returns true correctly against XPCNW');
|
||||
ok(new XPCSafeJSObjectWrapper(window) == window,
|
||||
'SJOWs equality hook returns true correctly against XOW');
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=2 sw=2 et tw=79:
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
@ -66,6 +67,7 @@
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsIPrefBranch.h"
|
||||
#include "nsIWebNavigation.h"
|
||||
#include "nsIJSContextStack.h"
|
||||
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIScriptObjectPrincipal.h"
|
||||
@ -74,6 +76,8 @@
|
||||
// CIDs
|
||||
static NS_DEFINE_CID(kWindowMediatorCID, NS_WINDOWMEDIATOR_CID);
|
||||
|
||||
static const char *sJSStackContractID="@mozilla.org/js/xpc/ContextStack;1";
|
||||
|
||||
//*****************************************************************************
|
||||
//*** nsSiteWindow2 declaration
|
||||
//*****************************************************************************
|
||||
@ -780,6 +784,34 @@ NS_IMETHODIMP nsContentTreeOwner::SetTitle(const PRUnichar* aTitle)
|
||||
return mXULWindow->SetTitle(title.get());
|
||||
}
|
||||
|
||||
NS_STACK_CLASS class NullJSContextPusher {
|
||||
public:
|
||||
NullJSContextPusher() {
|
||||
mService = do_GetService(sJSStackContractID);
|
||||
if (mService) {
|
||||
#ifdef DEBUG
|
||||
nsresult rv =
|
||||
#endif
|
||||
mService->Push(nsnull);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "Mismatched push/pop");
|
||||
}
|
||||
}
|
||||
|
||||
~NullJSContextPusher() {
|
||||
if (mService) {
|
||||
#ifdef DEBUG
|
||||
JSContext *cx;
|
||||
nsresult rv =
|
||||
#endif
|
||||
mService->Pop(&cx);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv) && !cx, "Bad pop!");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIThreadJSContextStack> mService;
|
||||
};
|
||||
|
||||
//*****************************************************************************
|
||||
// nsContentTreeOwner: nsIWindowProvider
|
||||
//*****************************************************************************
|
||||
@ -878,10 +910,14 @@ nsContentTreeOwner::ProvideWindow(nsIDOMWindow* aParent,
|
||||
|
||||
*aWindowIsNew = (containerPref != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW);
|
||||
|
||||
// Get a new rendering area from the browserDOMWin. We don't want
|
||||
// to be starting any loads here, so get it with a null URI.
|
||||
return browserDOMWin->OpenURI(nsnull, aParent, containerPref,
|
||||
nsIBrowserDOMWindow::OPEN_NEW, aReturn);
|
||||
{
|
||||
NullJSContextPusher pusher;
|
||||
|
||||
// Get a new rendering area from the browserDOMWin. We don't want
|
||||
// to be starting any loads here, so get it with a null URI.
|
||||
return browserDOMWin->OpenURI(nsnull, aParent, containerPref,
|
||||
nsIBrowserDOMWindow::OPEN_NEW, aReturn);
|
||||
}
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
|
Loading…
Reference in New Issue
Block a user