1530 lines
46 KiB
C++
1530 lines
46 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape 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/NPL/
|
|
*
|
|
* 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 Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 2001 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*/
|
|
|
|
//#define USEWEAKREFS // (haven't quite figured that out yet)
|
|
|
|
#include "nsWindowWatcher.h"
|
|
|
|
#include "jscntxt.h"
|
|
#include "nsAutoLock.h"
|
|
#include "nsCRT.h"
|
|
#include "nsEscape.h"
|
|
#include "nsWWJSUtils.h"
|
|
#include "nsNetUtil.h"
|
|
#include "plstr.h"
|
|
|
|
#include "nsIBaseWindow.h"
|
|
#include "nsICharsetConverterManager.h"
|
|
#include "nsIDocShell.h"
|
|
#include "nsIDocShellLoadInfo.h"
|
|
#include "nsIDocShellTreeItem.h"
|
|
#include "nsIDocShellTreeOwner.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsIDOMDocument.h"
|
|
#include "nsIDOMScreen.h"
|
|
#include "nsIDOMWindow.h"
|
|
#include "nsIDOMWindowInternal.h"
|
|
#include "nsIEventQueue.h"
|
|
#include "nsIEventQueueService.h"
|
|
#include "nsIGenericFactory.h"
|
|
#include "nsIJSContextStack.h"
|
|
#include "nsIObserverService.h"
|
|
#include "nsIScriptGlobalObject.h"
|
|
#include "nsIScriptObjectOwner.h"
|
|
#include "nsIScriptSecurityManager.h"
|
|
#include "nsIURI.h"
|
|
#include "nsIWebBrowser.h"
|
|
#include "nsIWebBrowserChrome.h"
|
|
#include "nsIWebNavigation.h"
|
|
#include "nsIWindowCreator.h"
|
|
#ifdef USEWEAKREFS
|
|
#include "nsIWeakReference.h"
|
|
#endif
|
|
|
|
#define NOTIFICATION_OPENED NS_LITERAL_STRING("domwindowopened")
|
|
#define NOTIFICATION_CLOSED NS_LITERAL_STRING("domwindowclosed")
|
|
|
|
static const char *sJSStackContractID="@mozilla.org/js/xpc/ContextStack;1";
|
|
|
|
/****************************************************************
|
|
************************* WindowInfo ***************************
|
|
****************************************************************/
|
|
|
|
class nsWindowWatcher;
|
|
|
|
struct WindowInfo {
|
|
|
|
WindowInfo(nsIDOMWindow* inWindow) {
|
|
#ifdef USEWEAKREFS
|
|
mWindow = getter_AddRefs(NS_GetWeakReference(inWindow));
|
|
#else
|
|
mWindow = inWindow;
|
|
#endif
|
|
ReferenceSelf();
|
|
}
|
|
~WindowInfo() {}
|
|
|
|
void InsertAfter(WindowInfo *inOlder);
|
|
void Unlink();
|
|
void ReferenceSelf();
|
|
|
|
#ifdef USEWEAKREFS
|
|
nsCOMPtr<nsIWeakReference> mWindow;
|
|
#else // still not an owning ref
|
|
nsIDOMWindow *mWindow;
|
|
#endif
|
|
// each struct is in a circular, doubly-linked list
|
|
WindowInfo *mYounger, // next younger in sequence
|
|
*mOlder;
|
|
};
|
|
|
|
void WindowInfo::InsertAfter(WindowInfo *inOlder)
|
|
{
|
|
if (inOlder) {
|
|
mOlder = inOlder;
|
|
mYounger = inOlder->mYounger;
|
|
mOlder->mYounger = this;
|
|
if (mOlder->mOlder == mOlder)
|
|
mOlder->mOlder = this;
|
|
mYounger->mOlder = this;
|
|
if (mYounger->mYounger == mYounger)
|
|
mYounger->mYounger = this;
|
|
}
|
|
}
|
|
|
|
void WindowInfo::Unlink() {
|
|
|
|
mOlder->mYounger = mYounger;
|
|
mYounger->mOlder = mOlder;
|
|
ReferenceSelf();
|
|
}
|
|
|
|
void WindowInfo::ReferenceSelf() {
|
|
|
|
mYounger = this;
|
|
mOlder = this;
|
|
}
|
|
|
|
/****************************************************************
|
|
********************* nsWindowEnumerator ***********************
|
|
****************************************************************/
|
|
|
|
class nsWindowEnumerator : public nsISimpleEnumerator {
|
|
|
|
public:
|
|
nsWindowEnumerator(nsWindowWatcher *inWatcher);
|
|
virtual ~nsWindowEnumerator();
|
|
NS_IMETHOD HasMoreElements(PRBool *retval);
|
|
NS_IMETHOD GetNext(nsISupports **retval);
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
private:
|
|
friend class nsWindowWatcher;
|
|
|
|
WindowInfo *FindNext();
|
|
void WindowRemoved(WindowInfo *inInfo);
|
|
|
|
nsWindowWatcher *mWindowWatcher;
|
|
WindowInfo *mCurrentPosition;
|
|
};
|
|
|
|
NS_IMPL_ADDREF(nsWindowEnumerator);
|
|
NS_IMPL_RELEASE(nsWindowEnumerator);
|
|
NS_IMPL_QUERY_INTERFACE1(nsWindowEnumerator, nsISimpleEnumerator);
|
|
|
|
nsWindowEnumerator::nsWindowEnumerator(nsWindowWatcher *inWatcher)
|
|
: mWindowWatcher(inWatcher),
|
|
mCurrentPosition(inWatcher->mOldestWindow)
|
|
{
|
|
NS_INIT_REFCNT();
|
|
mWindowWatcher->AddEnumerator(this);
|
|
mWindowWatcher->AddRef();
|
|
}
|
|
|
|
nsWindowEnumerator::~nsWindowEnumerator()
|
|
{
|
|
mWindowWatcher->RemoveEnumerator(this);
|
|
mWindowWatcher->Release();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWindowEnumerator::HasMoreElements(PRBool *retval)
|
|
{
|
|
if (!retval)
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
*retval = mCurrentPosition? PR_TRUE : PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWindowEnumerator::GetNext(nsISupports **retval)
|
|
{
|
|
if (!retval)
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
*retval = NULL;
|
|
|
|
#ifdef USEWEAKREFS
|
|
while (mCurrentPosition) {
|
|
CallQueryReferent(mCurrentPosition->mWindow, retval);
|
|
if (*retval) {
|
|
mCurrentPosition = FindNext();
|
|
break;
|
|
} else // window is gone!
|
|
mWindowWatcher->RemoveWindow(mCurrentPosition);
|
|
}
|
|
NS_IF_ADDREF(*retval);
|
|
#else
|
|
if (mCurrentPosition) {
|
|
CallQueryInterface(mCurrentPosition->mWindow, retval);
|
|
mCurrentPosition = FindNext();
|
|
}
|
|
#endif
|
|
return NS_OK;
|
|
}
|
|
|
|
WindowInfo *
|
|
nsWindowEnumerator::FindNext()
|
|
{
|
|
WindowInfo *info;
|
|
|
|
if (!mCurrentPosition)
|
|
return 0;
|
|
|
|
info = mCurrentPosition->mYounger;
|
|
return info == mWindowWatcher->mOldestWindow ? 0 : info;
|
|
}
|
|
|
|
// if a window is being removed adjust the iterator's current position
|
|
void nsWindowEnumerator::WindowRemoved(WindowInfo *inInfo) {
|
|
|
|
if (mCurrentPosition == inInfo)
|
|
mCurrentPosition = mCurrentPosition != inInfo->mYounger ?
|
|
inInfo->mYounger : 0;
|
|
}
|
|
|
|
/****************************************************************
|
|
********************* EventQueueAutoPopper *********************
|
|
****************************************************************/
|
|
|
|
class EventQueueAutoPopper {
|
|
public:
|
|
EventQueueAutoPopper();
|
|
~EventQueueAutoPopper();
|
|
|
|
nsresult Push();
|
|
|
|
protected:
|
|
nsCOMPtr<nsIEventQueueService> mService;
|
|
nsCOMPtr<nsIEventQueue> mQueue;
|
|
};
|
|
|
|
EventQueueAutoPopper::EventQueueAutoPopper() : mQueue(nsnull)
|
|
{
|
|
}
|
|
|
|
EventQueueAutoPopper::~EventQueueAutoPopper()
|
|
{
|
|
if(mQueue)
|
|
mService->PopThreadEventQueue(mQueue);
|
|
}
|
|
|
|
nsresult EventQueueAutoPopper::Push()
|
|
{
|
|
if (mQueue) // only once
|
|
return NS_ERROR_FAILURE;
|
|
|
|
mService = do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID);
|
|
if(mService)
|
|
mService->PushThreadEventQueue(getter_AddRefs(mQueue));
|
|
return mQueue ? NS_OK : NS_ERROR_FAILURE;
|
|
}
|
|
|
|
/****************************************************************
|
|
********************** JSContextAutoPopper *********************
|
|
****************************************************************/
|
|
|
|
class JSContextAutoPopper {
|
|
public:
|
|
JSContextAutoPopper();
|
|
~JSContextAutoPopper();
|
|
|
|
nsresult Push();
|
|
JSContext *get() { return mContext; }
|
|
|
|
protected:
|
|
nsCOMPtr<nsIThreadJSContextStack> mService;
|
|
JSContext *mContext;
|
|
};
|
|
|
|
JSContextAutoPopper::JSContextAutoPopper() : mContext(nsnull)
|
|
{
|
|
}
|
|
|
|
JSContextAutoPopper::~JSContextAutoPopper()
|
|
{
|
|
JSContext *cx;
|
|
nsresult rv;
|
|
|
|
if(mContext) {
|
|
rv = mService->Pop(&cx);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv) && cx == mContext, "JSContext push/pop mismatch");
|
|
}
|
|
}
|
|
|
|
nsresult JSContextAutoPopper::Push()
|
|
{
|
|
nsresult rv;
|
|
|
|
if (mContext) // only once
|
|
return NS_ERROR_FAILURE;
|
|
|
|
mService = do_GetService(sJSStackContractID);
|
|
if(mService) {
|
|
rv = mService->GetSafeJSContext(&mContext);
|
|
if (NS_SUCCEEDED(rv) && mContext) {
|
|
rv = mService->Push(mContext);
|
|
if (NS_FAILED(rv))
|
|
mContext = 0;
|
|
}
|
|
}
|
|
return mContext ? NS_OK : NS_ERROR_FAILURE;
|
|
}
|
|
|
|
/****************************************************************
|
|
*********************** nsWindowWatcher ************************
|
|
****************************************************************/
|
|
|
|
NS_IMPL_ADDREF(nsWindowWatcher);
|
|
NS_IMPL_RELEASE(nsWindowWatcher);
|
|
NS_IMPL_QUERY_INTERFACE2(nsWindowWatcher, nsIWindowWatcher, nsPIWindowWatcher)
|
|
|
|
nsWindowWatcher::nsWindowWatcher() :
|
|
mEnumeratorList(),
|
|
mOldestWindow(0),
|
|
mActiveWindow(0),
|
|
mListLock(0)
|
|
{
|
|
NS_INIT_REFCNT();
|
|
}
|
|
|
|
nsWindowWatcher::~nsWindowWatcher()
|
|
{
|
|
// delete data
|
|
while (mOldestWindow)
|
|
RemoveWindow(mOldestWindow);
|
|
|
|
if (mListLock)
|
|
PR_DestroyLock(mListLock);
|
|
}
|
|
|
|
nsresult
|
|
nsWindowWatcher::Init()
|
|
{
|
|
mListLock = PR_NewLock();
|
|
if (!mListLock)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWindowWatcher::OpenWindow(nsIDOMWindow *aParent,
|
|
const PRUnichar *aUrl,
|
|
const PRUnichar *aName,
|
|
const PRUnichar *aFeatures,
|
|
nsIDOMWindow **_retval)
|
|
{
|
|
return OpenWindowJS(aParent, aUrl, aName, aFeatures, PR_FALSE, 0, 0, _retval);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWindowWatcher::OpenWindowJS(nsIDOMWindow *aParent,
|
|
const PRUnichar *aUrl,
|
|
const PRUnichar *aName,
|
|
const PRUnichar *aFeatures,
|
|
PRBool aDialog,
|
|
PRUint32 argc,
|
|
jsval *argv,
|
|
nsIDOMWindow **_retval)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
PRBool nameSpecified,
|
|
featuresSpecified,
|
|
windowIsNew = PR_FALSE,
|
|
windowIsModal = PR_FALSE;
|
|
PRUint32 chromeFlags;
|
|
nsAutoString name; // string version of aName
|
|
nsCString features; // string version of aFeatures
|
|
nsCOMPtr<nsIURI> uriToLoad; // from aUrl, if any
|
|
nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner; // from the parent window, if any
|
|
nsCOMPtr<nsIDocShellTreeItem> newDocShellItem; // from the new window
|
|
EventQueueAutoPopper queueGuard;
|
|
JSContextAutoPopper contextGuard;
|
|
|
|
NS_ENSURE_ARG_POINTER(_retval);
|
|
*_retval = 0;
|
|
|
|
if (aParent)
|
|
GetWindowTreeOwner(aParent, getter_AddRefs(parentTreeOwner));
|
|
|
|
if (aUrl)
|
|
rv = URIfromURL(aUrl, aParent, getter_AddRefs(uriToLoad));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
nameSpecified = PR_FALSE;
|
|
if (aName) {
|
|
name.Assign(aName);
|
|
CheckWindowName(name);
|
|
nameSpecified = PR_TRUE;
|
|
}
|
|
|
|
featuresSpecified = PR_FALSE;
|
|
if (aFeatures) {
|
|
features.AssignWithConversion(aFeatures);
|
|
featuresSpecified = PR_TRUE;
|
|
}
|
|
|
|
chromeFlags = CalculateChromeFlags(features, featuresSpecified, aDialog);
|
|
|
|
// try to find an extant window with the given name
|
|
if (nameSpecified) {
|
|
/* Oh good. special target names are now handled in multiple places:
|
|
Here and within FindItemWithName, just below. I put _top here because
|
|
here it's able to do what it should: get the topmost shell of the same
|
|
(content/chrome) type as the docshell. treeOwner is always chrome, so
|
|
this scheme doesn't work there, where a lot of other special case
|
|
targets are handled. (treeOwner is, however, a good place to look
|
|
for browser windows by name, as it does.)
|
|
*/
|
|
if (aParent) {
|
|
if (name.EqualsIgnoreCase("_self")) {
|
|
GetWindowTreeItem(aParent, getter_AddRefs(newDocShellItem));
|
|
} else if (name.EqualsIgnoreCase("_top")) {
|
|
nsCOMPtr<nsIDocShellTreeItem> shelltree;
|
|
GetWindowTreeItem(aParent, getter_AddRefs(shelltree));
|
|
if (shelltree)
|
|
shelltree->GetSameTypeRootTreeItem(getter_AddRefs(newDocShellItem));
|
|
} else
|
|
parentTreeOwner->FindItemWithName(name.GetUnicode(), nsnull,
|
|
getter_AddRefs(newDocShellItem));
|
|
} else
|
|
FindItemWithName(name.GetUnicode(), getter_AddRefs(newDocShellItem));
|
|
}
|
|
|
|
// no extant window? make a new one.
|
|
if (!newDocShellItem) {
|
|
windowIsNew = PR_TRUE;
|
|
|
|
// is the parent (if any) modal? if so, we must be, too.
|
|
PRBool weAreModal = PR_FALSE;
|
|
if (parentTreeOwner) {
|
|
nsCOMPtr<nsIInterfaceRequestor> parentRequestor(do_QueryInterface(parentTreeOwner));
|
|
if (parentRequestor) {
|
|
nsCOMPtr<nsIWebBrowserChrome> parentChrome;
|
|
parentRequestor->GetInterface(NS_GET_IID(nsIWebBrowserChrome), getter_AddRefs(parentChrome));
|
|
if (parentChrome)
|
|
parentChrome->IsWindowModal(&weAreModal);
|
|
}
|
|
}
|
|
if (weAreModal || (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL)) {
|
|
rv = queueGuard.Push();
|
|
if (NS_SUCCEEDED(rv)) {
|
|
windowIsModal = PR_TRUE;
|
|
// in case we added this because weAreModal
|
|
chromeFlags |= nsIWebBrowserChrome::CHROME_MODAL | nsIWebBrowserChrome::CHROME_DEPENDENT;
|
|
}
|
|
}
|
|
if (parentTreeOwner)
|
|
parentTreeOwner->GetNewWindow(chromeFlags, getter_AddRefs(newDocShellItem));
|
|
else if (mWindowCreator) {
|
|
nsCOMPtr<nsIWebBrowserChrome> newChrome;
|
|
mWindowCreator->CreateChromeWindow(0, chromeFlags, getter_AddRefs(newChrome));
|
|
if (newChrome) {
|
|
nsCOMPtr<nsIInterfaceRequestor> thing(do_QueryInterface(newChrome));
|
|
if (thing) {
|
|
/* It might be a chrome nsXULWindow, in which case it won't have
|
|
an nsIDOMWindow (primary content shell). But in that case, it'll
|
|
be able to hand over an nsIDocShellTreeItem directly. */
|
|
// XXX got the order right?
|
|
nsCOMPtr<nsIDOMWindow> newWindow;
|
|
thing->GetInterface(NS_GET_IID(nsIDOMWindow), getter_AddRefs(newWindow));
|
|
if (newWindow)
|
|
GetWindowTreeItem(newWindow, getter_AddRefs(newDocShellItem));
|
|
if (!newDocShellItem)
|
|
thing->GetInterface(NS_GET_IID(nsIDocShellTreeItem), getter_AddRefs(newDocShellItem));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// better have a window to use by this point
|
|
if (!newDocShellItem)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
rv = ReadyOpenedDocShellItem(newDocShellItem, aParent, _retval);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
/* disable persistence of size/position in popups (determined by
|
|
determining whether the features parameter specifies width or height
|
|
in any way). We consider any overriding of the window's size or position
|
|
in the open call as disabling persistence of those attributes.
|
|
Popup windows (which should not persist size or position) generally set
|
|
the size. */
|
|
if (windowIsNew) {
|
|
/* at the moment, the strings "height=" or "width=" never happen
|
|
outside a size specification, so we can do this the Q&D way. */
|
|
|
|
if (PL_strcasestr(features, "width=") || PL_strcasestr(features, "height=")) {
|
|
|
|
nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
|
|
newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner));
|
|
if (newTreeOwner)
|
|
newTreeOwner->SetPersistence(PR_FALSE, PR_FALSE, PR_FALSE);
|
|
}
|
|
}
|
|
|
|
if (aDialog && argc > 0)
|
|
AttachArguments(*_retval, argc, argv);
|
|
|
|
nsCOMPtr<nsIScriptSecurityManager> secMan;
|
|
|
|
if (uriToLoad) {
|
|
/* Get security manager, check to see if URI is allowed.
|
|
Don't call CheckLoadURI for dialogs - see bug 56851
|
|
The security of this function depends on window.openDialog being
|
|
inaccessible from web scripts */
|
|
JSContext *cx;
|
|
nsCOMPtr<nsIScriptContext> scriptCX;
|
|
cx = GetExtantJSContext(aParent);
|
|
if (!cx) {
|
|
rv = contextGuard.Push();
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
cx = contextGuard.get();
|
|
}
|
|
#if 0
|
|
// better than trying so hard to find a script object? or just wrong?
|
|
nsJSUtils::nsGetDynamicScriptContext(cx, getter_AddRefs(scriptCX));
|
|
#else
|
|
JSObject *scriptObject = GetWindowScriptObject(aParent ? aParent : *_retval);
|
|
nsWWJSUtils::nsGetStaticScriptContext(cx, scriptObject,
|
|
getter_AddRefs(scriptCX));
|
|
#endif
|
|
if (!scriptCX ||
|
|
NS_FAILED(scriptCX->GetSecurityManager(getter_AddRefs(secMan))) ||
|
|
((!aDialog && NS_FAILED(secMan->CheckLoadURIFromScript(cx, uriToLoad)))))
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
newDocShellItem->SetName(nameSpecified ? name.GetUnicode() : nsnull);
|
|
|
|
nsCOMPtr<nsIDocShell> newDocShell(do_QueryInterface(newDocShellItem));
|
|
if (uriToLoad) { // Get script principal and pass to docshell
|
|
nsCOMPtr<nsIPrincipal> principal;
|
|
if (NS_FAILED(secMan->GetSubjectPrincipal(getter_AddRefs(principal))))
|
|
return NS_ERROR_FAILURE;
|
|
|
|
nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
|
|
newDocShell->CreateLoadInfo(getter_AddRefs(loadInfo));
|
|
NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);
|
|
|
|
if (principal) {
|
|
nsCOMPtr<nsISupports> owner(do_QueryInterface(principal));
|
|
loadInfo->SetOwner(owner);
|
|
}
|
|
|
|
// Get the calling context off the JS context stack
|
|
nsCOMPtr<nsIJSContextStack> stack = do_GetService(sJSStackContractID);
|
|
|
|
JSContext* ccx = nsnull;
|
|
|
|
if (stack && NS_SUCCEEDED(stack->Peek(&ccx)) && ccx) {
|
|
JSObject *global = ::JS_GetGlobalObject(ccx);
|
|
|
|
if (global) {
|
|
JSClass* jsclass = ::JS_GetClass(ccx, global);
|
|
|
|
// Check if the global object on the calling context has
|
|
// nsISupports * private data
|
|
if (jsclass &&
|
|
!((~jsclass->flags) & (JSCLASS_HAS_PRIVATE |
|
|
JSCLASS_PRIVATE_IS_NSISUPPORTS))) {
|
|
nsISupports* sup = (nsISupports *)::JS_GetPrivate(ccx, global);
|
|
|
|
nsCOMPtr<nsIDOMWindow> w(do_QueryInterface(sup));
|
|
|
|
if (w) {
|
|
nsCOMPtr<nsIDOMDocument> document;
|
|
|
|
// Get the document from the window.
|
|
w->GetDocument(getter_AddRefs(document));
|
|
|
|
nsCOMPtr<nsIDocument> doc(do_QueryInterface(document));
|
|
|
|
if (doc) {
|
|
nsCOMPtr<nsIURI> uri(dont_AddRef(doc->GetDocumentURL()));
|
|
|
|
// Set the referrer
|
|
loadInfo->SetReferrer(uri);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
newDocShell->LoadURI(uriToLoad, loadInfo,
|
|
nsIWebNavigation::LOAD_FLAGS_NONE);
|
|
}
|
|
|
|
if (windowIsNew)
|
|
SizeOpenedDocShellItem(newDocShellItem, aParent, features, chromeFlags);
|
|
|
|
if (windowIsModal) {
|
|
nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
|
|
nsCOMPtr<nsIInterfaceRequestor> newRequestor;
|
|
nsCOMPtr<nsIWebBrowserChrome> newChrome;
|
|
|
|
newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner));
|
|
if (newTreeOwner)
|
|
newRequestor = do_QueryInterface(newTreeOwner);
|
|
if (newRequestor)
|
|
newRequestor->GetInterface(NS_GET_IID(nsIWebBrowserChrome), getter_AddRefs(newChrome));
|
|
|
|
if (newChrome)
|
|
newChrome->ShowAsModal();
|
|
NS_ASSERTION(newChrome, "show modal window failed: no available chrome");
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWindowWatcher::RegisterNotification(nsIObserver *aObserver)
|
|
{
|
|
// just a convenience method; it delegates to nsIObserverService
|
|
nsresult rv;
|
|
|
|
if (!aObserver)
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
nsCOMPtr<nsIObserverService> os(do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv));
|
|
if (os) {
|
|
rv = os->AddObserver(aObserver, NOTIFICATION_OPENED.get());
|
|
if (NS_SUCCEEDED(rv))
|
|
rv = os->AddObserver(aObserver, NOTIFICATION_CLOSED.get());
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWindowWatcher::UnregisterNotification(nsIObserver *aObserver)
|
|
{
|
|
// just a convenience method; it delegates to nsIObserverService
|
|
nsresult rv;
|
|
|
|
if (!aObserver)
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
nsCOMPtr<nsIObserverService> os(do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv));
|
|
if (os) {
|
|
os->RemoveObserver(aObserver, NOTIFICATION_OPENED.get());
|
|
os->RemoveObserver(aObserver, NOTIFICATION_CLOSED.get());
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWindowWatcher::GetWindowEnumerator(nsISimpleEnumerator** _retval)
|
|
{
|
|
if (!_retval)
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
nsAutoLock lock(mListLock);
|
|
nsWindowEnumerator *enumerator = new nsWindowEnumerator(this);
|
|
if (enumerator)
|
|
return CallQueryInterface(enumerator, _retval);
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWindowWatcher::SetWindowCreator(nsIWindowCreator *creator)
|
|
{
|
|
mWindowCreator = creator; // it's an nsCOMPtr, so this is an ownership ref
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWindowWatcher::GetActiveWindow(nsIDOMWindow **aActiveWindow)
|
|
{
|
|
if (!aActiveWindow)
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
*aActiveWindow = mActiveWindow;
|
|
NS_IF_ADDREF(mActiveWindow);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWindowWatcher::SetActiveWindow(nsIDOMWindow *aActiveWindow)
|
|
{
|
|
mActiveWindow = aActiveWindow;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWindowWatcher::AddWindow(nsIDOMWindow *aWindow)
|
|
{
|
|
nsresult rv;
|
|
|
|
if (!aWindow)
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
// create window info struct and add to list of windows
|
|
WindowInfo* info = new WindowInfo(aWindow);
|
|
if (!info)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
nsAutoLock lock(mListLock);
|
|
if (mOldestWindow)
|
|
info->InsertAfter(mOldestWindow->mOlder);
|
|
else
|
|
mOldestWindow = info;
|
|
|
|
// a window being added to us signifies a newly opened window.
|
|
// send notifications.
|
|
nsCOMPtr<nsIObserverService> os(do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv));
|
|
if (os) {
|
|
nsCOMPtr<nsISupports> domwin(do_QueryInterface(aWindow));
|
|
rv = os->Notify(domwin, NOTIFICATION_OPENED.get(), 0);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWindowWatcher::RemoveWindow(nsIDOMWindow *aWindow)
|
|
{
|
|
// find the corresponding WindowInfo, remove it
|
|
WindowInfo *info,
|
|
*listEnd;
|
|
#ifdef USEWEAKREFS
|
|
nsresult rv;
|
|
PRBool found;
|
|
#endif
|
|
|
|
if (!aWindow)
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
info = mOldestWindow;
|
|
listEnd = 0;
|
|
#ifdef USEWEAKREFS
|
|
rv = NS_OK;
|
|
found = PR_FALSE;
|
|
while (info != listEnd && NS_SUCCEEDED(rv)) {
|
|
nsCOMPtr<nsIDOMWindow> infoWindow(do_QueryReferent(info->mWindow));
|
|
if (!infoWindow)
|
|
rv = RemoveWindow(info);
|
|
else if (infoWindow.get() == aWindow) {
|
|
found = PR_TRUE;
|
|
rv = RemoveWindow(info);
|
|
}
|
|
info = info->mYounger;
|
|
listEnd = mOldestWindow;
|
|
}
|
|
return found ? rv : NS_ERROR_INVALID_ARG;
|
|
#else
|
|
while (info != listEnd) {
|
|
if (info->mWindow == aWindow)
|
|
return RemoveWindow(info);
|
|
info = info->mYounger;
|
|
listEnd = mOldestWindow;
|
|
}
|
|
NS_WARNING("requested removal of nonexistent window\n");
|
|
return NS_ERROR_INVALID_ARG;
|
|
#endif
|
|
}
|
|
|
|
nsresult nsWindowWatcher::RemoveWindow(WindowInfo *inInfo)
|
|
{
|
|
PRInt32 ctr,
|
|
count = mEnumeratorList.Count();
|
|
nsresult rv;
|
|
|
|
{
|
|
// notify the enumerators
|
|
nsAutoLock lock(mListLock);
|
|
for (ctr = 0; ctr < count; ++ctr)
|
|
((nsWindowEnumerator*)mEnumeratorList[ctr])->WindowRemoved(inInfo);
|
|
|
|
// remove the element from the list
|
|
if (inInfo == mOldestWindow)
|
|
mOldestWindow = inInfo->mYounger == mOldestWindow ? 0 : inInfo->mYounger;
|
|
inInfo->Unlink();
|
|
}
|
|
|
|
// a window being removed from us signifies a newly closed window.
|
|
// send notifications.
|
|
nsCOMPtr<nsIObserverService> os(do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv));
|
|
if (os) {
|
|
#ifdef USEWEAKREFS
|
|
nsCOMPtr<nsISupports> domwin(do_QueryReferent(inInfo->mWindow));
|
|
if (domwin)
|
|
rv = os->Notify(domwin, NOTIFICATION_CLOSED.get(), 0);
|
|
// else bummer. since the window is gone, there's nothing to notify with.
|
|
#else
|
|
nsCOMPtr<nsISupports> domwin(do_QueryInterface(inInfo->mWindow));
|
|
rv = os->Notify(domwin, NOTIFICATION_CLOSED.get(), 0);
|
|
#endif
|
|
}
|
|
|
|
delete inInfo;
|
|
return NS_OK;
|
|
}
|
|
|
|
PRBool
|
|
nsWindowWatcher::AddEnumerator(nsWindowEnumerator* inEnumerator)
|
|
{
|
|
// (requires a lock; assumes it's called by someone holding the lock)
|
|
return mEnumeratorList.AppendElement(inEnumerator);
|
|
}
|
|
|
|
PRBool
|
|
nsWindowWatcher::RemoveEnumerator(nsWindowEnumerator* inEnumerator)
|
|
{
|
|
// (requires a lock; assumes it's called by someone holding the lock)
|
|
return mEnumeratorList.RemoveElement(inEnumerator);
|
|
}
|
|
|
|
// stolen from GlobalWindowImpl
|
|
nsresult
|
|
nsWindowWatcher::Escape(const nsAReadableString& aStr, nsAWritableString& aReturn,
|
|
nsIDOMWindow *aWindow)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
nsCOMPtr<nsIUnicodeEncoder> encoder;
|
|
nsAutoString charset;
|
|
|
|
// Get the document character set
|
|
charset.AssignWithConversion("UTF-8"); // default to utf-8
|
|
if (aWindow) { // or use aWindow's, if possible
|
|
nsCOMPtr<nsIDOMDocument> domDoc;
|
|
aWindow->GetDocument(getter_AddRefs(domDoc));
|
|
if (domDoc) {
|
|
nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
|
|
if (doc)
|
|
rv = doc->GetDocumentCharacterSet(charset);
|
|
}
|
|
}
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
nsCOMPtr<nsICharsetConverterManager>
|
|
ccm(do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID));
|
|
if (!ccm)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
// Get an encoder for the character set
|
|
rv = ccm->GetUnicodeEncoder(&charset, getter_AddRefs(encoder));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
rv = encoder->Reset();
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
PRInt32 maxByteLen, srcLen;
|
|
srcLen = aStr.Length();
|
|
|
|
nsPromiseFlatString flatSrc(aStr);
|
|
const PRUnichar* src = flatSrc.get();
|
|
|
|
// Get the expected length of result string
|
|
rv = encoder->GetMaxLength(src, srcLen, &maxByteLen);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
// Allocate a buffer of the maximum length
|
|
char* dest = (char *) nsMemory::Alloc(maxByteLen + 1);
|
|
PRInt32 destLen2, destLen = maxByteLen;
|
|
if (!dest)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
// Convert from unicode to the character set
|
|
rv = encoder->Convert(src, &srcLen, dest, &destLen);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
// Allow the encoder to finish the conversion
|
|
destLen2 = maxByteLen - destLen;
|
|
encoder->Finish(dest + destLen, &destLen2);
|
|
dest[destLen + destLen2] = '\0';
|
|
|
|
// Escape the string
|
|
char *outBuf =
|
|
nsEscape(dest, nsEscapeMask(url_XAlphas | url_XPAlphas | url_Path));
|
|
CopyASCIItoUCS2(nsLiteralCString(outBuf), aReturn);
|
|
nsMemory::Free(outBuf);
|
|
}
|
|
|
|
nsMemory::Free(dest);
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsWindowWatcher::URIfromURL(const PRUnichar *aURL,
|
|
nsIDOMWindow *aParent,
|
|
nsIURI **aURI)
|
|
{
|
|
*aURI = 0;
|
|
|
|
nsresult rv = NS_OK;
|
|
nsAutoString unescapedURL(aURL);
|
|
nsAutoString escapedURL;
|
|
|
|
// fix bug 35076
|
|
// if the URL contains non ASCII, then escape from the first non ASCII char
|
|
if (unescapedURL.IsASCII())
|
|
escapedURL = unescapedURL;
|
|
else {
|
|
const PRUnichar *pt = unescapedURL.GetUnicode();
|
|
PRUint32 len = unescapedURL.Length();
|
|
PRUint32 i;
|
|
for (i = 0; i < len; i++)
|
|
if (0xFF80 & *pt++)
|
|
break;
|
|
|
|
nsAutoString right, escapedRight;
|
|
unescapedURL.Left(escapedURL, i);
|
|
unescapedURL.Right(right, len - i);
|
|
if (NS_SUCCEEDED(Escape(right, escapedRight, aParent)))
|
|
escapedURL.Append(escapedRight);
|
|
else
|
|
escapedURL = unescapedURL;
|
|
}
|
|
|
|
if (!escapedURL.IsEmpty()) {
|
|
nsAutoString absoluteURL;
|
|
nsCOMPtr<nsIDocument> doc;
|
|
nsCOMPtr<nsIURI> baseURI;
|
|
|
|
if (aParent) {
|
|
nsCOMPtr<nsIDOMDocument> domDoc;
|
|
aParent->GetDocument(getter_AddRefs(domDoc));
|
|
if (domDoc)
|
|
doc = do_QueryInterface(domDoc);
|
|
}
|
|
if (doc) {
|
|
// build absolute URI relative to this document.
|
|
baseURI = dont_AddRef(doc->GetDocumentURL());
|
|
rv = NS_MakeAbsoluteURI(absoluteURL, escapedURL, baseURI);
|
|
}
|
|
if (NS_FAILED(rv) || !baseURI) {
|
|
/* probably no document, probably because the context window's
|
|
URL hasn't finished loading. (don't call this function with
|
|
no context window and a relative URL!). All we can do is hope
|
|
the URL we've been given is absolute. */
|
|
absoluteURL = escapedURL;
|
|
}
|
|
// make URI; if absoluteURL is relative (or otherwise bogus) this will fail.
|
|
rv = NS_NewURI(aURI, absoluteURL);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/* Check for an illegal name e.g. frame3.1
|
|
This just prints a warning message an continues; we open the window anyway,
|
|
(see bug 32898). */
|
|
void nsWindowWatcher::CheckWindowName(nsString& aName)
|
|
{
|
|
nsReadingIterator<PRUnichar> scan;
|
|
nsReadingIterator<PRUnichar> endScan;
|
|
|
|
aName.EndReading(endScan);
|
|
for (aName.BeginReading(scan); scan != endScan; ++scan)
|
|
if (!nsCRT::IsAsciiAlpha(*scan) && !nsCRT::IsAsciiDigit(*scan) &&
|
|
*scan != '_') {
|
|
|
|
// Don't use js_ReportError as this will cause the application
|
|
// to shut down (JS_ASSERT calls abort()) See bug 32898
|
|
nsAutoString warn;
|
|
warn.AssignWithConversion("Illegal character in window name ");
|
|
warn.Append(aName);
|
|
char *cp = warn.ToNewCString();
|
|
NS_WARNING(cp);
|
|
nsCRT::free(cp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calculate the chrome bitmask from a string list of features.
|
|
* @param aFeatures a string containing a list of named chrome features
|
|
* @param aNullFeatures true if aFeatures was a null pointer (which fact
|
|
* is lost by its conversion to a string in the caller)
|
|
* @param aDialog affects the assumptions made about unnamed features
|
|
* @return the chrome bitmask
|
|
*/
|
|
PRUint32 nsWindowWatcher::CalculateChromeFlags(const char *aFeatures,
|
|
PRBool aFeaturesSpecified,
|
|
PRBool aDialog)
|
|
{
|
|
if(!aFeaturesSpecified || !aFeatures) {
|
|
if(aDialog)
|
|
return nsIWebBrowserChrome::CHROME_ALL |
|
|
nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
|
|
nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
|
|
else
|
|
return nsIWebBrowserChrome::CHROME_ALL;
|
|
}
|
|
|
|
/* This function has become complicated since browser windows and
|
|
dialogs diverged. The difference is, browser windows assume all
|
|
chrome not explicitly mentioned is off, if the features string
|
|
is not null. Exceptions are some OS border chrome new with Mozilla.
|
|
Dialogs interpret a (mostly) empty features string to mean
|
|
"OS's choice," and also support an "all" flag explicitly disallowed
|
|
in the standards-compliant window.(normal)open. */
|
|
|
|
PRUint32 chromeFlags = 0;
|
|
PRBool presenceFlag = PR_FALSE;
|
|
|
|
chromeFlags = nsIWebBrowserChrome::CHROME_WINDOW_BORDERS;
|
|
if (aDialog && WinHasOption(aFeatures, "all", 0, &presenceFlag))
|
|
chromeFlags = nsIWebBrowserChrome::CHROME_ALL;
|
|
|
|
/* Next, allow explicitly named options to override the initial settings */
|
|
|
|
chromeFlags |= WinHasOption(aFeatures, "titlebar", 0, &presenceFlag)
|
|
? nsIWebBrowserChrome::CHROME_TITLEBAR : 0;
|
|
chromeFlags |= WinHasOption(aFeatures, "close", 0, &presenceFlag)
|
|
? nsIWebBrowserChrome::CHROME_WINDOW_CLOSE : 0;
|
|
chromeFlags |= WinHasOption(aFeatures, "toolbar", 0, &presenceFlag)
|
|
? nsIWebBrowserChrome::CHROME_TOOLBAR : 0;
|
|
chromeFlags |= WinHasOption(aFeatures, "location", 0, &presenceFlag)
|
|
? nsIWebBrowserChrome::CHROME_LOCATIONBAR : 0;
|
|
chromeFlags |= (WinHasOption(aFeatures, "directories", 0, &presenceFlag) ||
|
|
WinHasOption(aFeatures, "personalbar", 0, &presenceFlag))
|
|
? nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR : 0;
|
|
chromeFlags |= WinHasOption(aFeatures, "status", 0, &presenceFlag)
|
|
? nsIWebBrowserChrome::CHROME_STATUSBAR : 0;
|
|
chromeFlags |= WinHasOption(aFeatures, "menubar", 0, &presenceFlag)
|
|
? nsIWebBrowserChrome::CHROME_MENUBAR : 0;
|
|
chromeFlags |= WinHasOption(aFeatures, "scrollbars", 0, &presenceFlag)
|
|
? nsIWebBrowserChrome::CHROME_SCROLLBARS : 0;
|
|
chromeFlags |= WinHasOption(aFeatures, "resizable", 0, &presenceFlag)
|
|
? nsIWebBrowserChrome::CHROME_WINDOW_RESIZE : 0;
|
|
chromeFlags |= WinHasOption(aFeatures, "minimizable", 0, &presenceFlag)
|
|
? nsIWebBrowserChrome::CHROME_WINDOW_MIN : 0;
|
|
|
|
/* OK.
|
|
Normal browser windows, in spite of a stated pattern of turning off
|
|
all chrome not mentioned explicitly, will want the new OS chrome (window
|
|
borders, titlebars, closebox) on, unless explicitly turned off.
|
|
Dialogs, on the other hand, take the absence of any explicit settings
|
|
to mean "OS' choice." */
|
|
|
|
// default titlebar and closebox to "on," if not mentioned at all
|
|
if (!PL_strcasestr(aFeatures, "titlebar"))
|
|
chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR;
|
|
if (!PL_strcasestr(aFeatures, "close"))
|
|
chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE;
|
|
|
|
if (aDialog && !presenceFlag)
|
|
chromeFlags = nsIWebBrowserChrome::CHROME_DEFAULT;
|
|
|
|
/* Finally, once all the above normal chrome has been divined, deal
|
|
with the features that are more operating hints than appearance
|
|
instructions. (Note modality implies dependence.) */
|
|
|
|
if (WinHasOption(aFeatures, "alwaysLowered", 0, nsnull) ||
|
|
WinHasOption(aFeatures, "z-lock", 0, nsnull))
|
|
chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_LOWERED;
|
|
else if (WinHasOption(aFeatures, "alwaysRaised", 0, nsnull))
|
|
chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_RAISED;
|
|
|
|
chromeFlags |= WinHasOption(aFeatures, "chrome", 0, nsnull) ?
|
|
nsIWebBrowserChrome::CHROME_OPENAS_CHROME : 0;
|
|
chromeFlags |= WinHasOption(aFeatures, "centerscreen", 0, nsnull) ?
|
|
nsIWebBrowserChrome::CHROME_CENTER_SCREEN : 0;
|
|
chromeFlags |= WinHasOption(aFeatures, "dependent", 0, nsnull) ?
|
|
nsIWebBrowserChrome::CHROME_DEPENDENT : 0;
|
|
chromeFlags |= WinHasOption(aFeatures, "modal", 0, nsnull) ?
|
|
(nsIWebBrowserChrome::CHROME_MODAL | nsIWebBrowserChrome::CHROME_DEPENDENT) : 0;
|
|
chromeFlags |= WinHasOption(aFeatures, "dialog", 0, nsnull) ?
|
|
nsIWebBrowserChrome::CHROME_OPENAS_DIALOG : 0;
|
|
|
|
/* and dialogs need to have the last word. assume dialogs are dialogs,
|
|
and opened as chrome, unless explicitly told otherwise. */
|
|
if (aDialog) {
|
|
if (!PL_strcasestr(aFeatures, "dialog"))
|
|
chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG;
|
|
if (!PL_strcasestr(aFeatures, "chrome"))
|
|
chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
|
|
}
|
|
|
|
/* missing
|
|
chromeFlags->copy_history
|
|
*/
|
|
|
|
/* Allow disabling of commands only if there is no menubar */
|
|
/*if(!chromeFlags & NS_CHROME_MENU_BAR_ON) {
|
|
chromeFlags->disable_commands = !WinHasOption(aFeatures, "hotkeys");
|
|
if(XP_STRCASESTR(aFeatures,"hotkeys")==NULL)
|
|
chromeFlags->disable_commands = FALSE;
|
|
}
|
|
*/
|
|
|
|
//Check security state for use in determing window dimensions
|
|
nsCOMPtr<nsIScriptSecurityManager>
|
|
securityManager(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
|
|
NS_ENSURE_TRUE(securityManager, NS_ERROR_FAILURE);
|
|
|
|
PRBool enabled;
|
|
nsresult res =
|
|
securityManager->IsCapabilityEnabled("UniversalBrowserWrite", &enabled);
|
|
|
|
res = securityManager->IsCapabilityEnabled("UniversalBrowserWrite", &enabled);
|
|
|
|
if (NS_FAILED(res) || !enabled) {
|
|
//If priv check fails, set all elements to minimum reqs., else leave them alone.
|
|
chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR;
|
|
chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_LOWERED;
|
|
chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_RAISED;
|
|
//XXX Temporarily removing this check to allow modal dialogs to be
|
|
//raised from script. A more complete security based fix is needed.
|
|
//chromeFlags &= ~nsIWebBrowserChrome::CHROME_MODAL;
|
|
}
|
|
|
|
return chromeFlags;
|
|
}
|
|
|
|
PRInt32
|
|
nsWindowWatcher::WinHasOption(const char *aOptions, const char *aName,
|
|
PRInt32 aDefault, PRBool *aPresenceFlag)
|
|
{
|
|
if (!aOptions)
|
|
return 0;
|
|
|
|
char *comma, *equal;
|
|
PRInt32 found = 0;
|
|
|
|
while (PR_TRUE) {
|
|
while (nsCRT::IsAsciiSpace(*aOptions))
|
|
aOptions++;
|
|
|
|
comma = PL_strchr(aOptions, ',');
|
|
if (comma)
|
|
*comma = '\0';
|
|
equal = PL_strchr(aOptions, '=');
|
|
if (equal)
|
|
*equal = '\0';
|
|
if (nsCRT::strcasecmp(aOptions, aName) == 0) {
|
|
if (aPresenceFlag)
|
|
*aPresenceFlag = PR_TRUE;
|
|
if (equal)
|
|
if (*(equal + 1) == '*')
|
|
found = aDefault;
|
|
else if (nsCRT::strcasecmp(equal + 1, "yes") == 0)
|
|
found = 1;
|
|
else
|
|
found = atoi(equal + 1);
|
|
else
|
|
found = 1;
|
|
}
|
|
if (equal)
|
|
*equal = '=';
|
|
if (comma)
|
|
*comma = ',';
|
|
if (found || !comma)
|
|
break;
|
|
aOptions = comma + 1;
|
|
}
|
|
return found;
|
|
}
|
|
|
|
/* try to find an nsIDocShellTreeItem with the given name in any
|
|
known open window. a failure to find the item will not
|
|
necessarily return a failure method value. check aFoundItem.
|
|
*/
|
|
nsresult
|
|
nsWindowWatcher::FindItemWithName(
|
|
const PRUnichar* aName,
|
|
nsIDocShellTreeItem** aFoundItem)
|
|
{
|
|
PRBool more;
|
|
nsresult rv;
|
|
|
|
*aFoundItem = 0;
|
|
|
|
nsCOMPtr<nsISimpleEnumerator> windows;
|
|
GetWindowEnumerator(getter_AddRefs(windows));
|
|
if (!windows)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
rv = NS_OK;
|
|
do {
|
|
windows->HasMoreElements(&more);
|
|
if (!more)
|
|
break;
|
|
nsCOMPtr<nsISupports> nextSupWindow;
|
|
windows->GetNext(getter_AddRefs(nextSupWindow));
|
|
if (nextSupWindow) {
|
|
nsCOMPtr<nsIDOMWindow> nextWindow(do_QueryInterface(nextSupWindow));
|
|
if (nextWindow) {
|
|
nsCOMPtr<nsIDocShellTreeItem> treeItem;
|
|
GetWindowTreeItem(nextWindow, getter_AddRefs(treeItem));
|
|
if (treeItem) {
|
|
rv = treeItem->FindItemWithName(aName, treeItem, aFoundItem);
|
|
if (NS_FAILED(rv) || *aFoundItem)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} while(1);
|
|
|
|
return rv;
|
|
}
|
|
|
|
/* Fetch the nsIDOMWindow corresponding to the given nsIDocShellTreeItem.
|
|
This forces the creation of a script context, if one has not already
|
|
been created. Note it also sets the window's opener to the parent,
|
|
if applicable -- because it's just convenient, that's all. null aParent
|
|
is acceptable. */
|
|
nsresult
|
|
nsWindowWatcher::ReadyOpenedDocShellItem(nsIDocShellTreeItem *aOpenedItem,
|
|
nsIDOMWindow *aParent,
|
|
nsIDOMWindow **aOpenedWindow)
|
|
{
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
|
|
*aOpenedWindow = 0;
|
|
nsCOMPtr<nsIScriptGlobalObject> globalObject(do_GetInterface(aOpenedItem));
|
|
if (globalObject) {
|
|
if (aParent) {
|
|
nsCOMPtr<nsIDOMWindowInternal> internalParent(do_QueryInterface(aParent));
|
|
globalObject->SetOpenerWindow(internalParent); // damnit
|
|
}
|
|
rv = CallQueryInterface(globalObject, aOpenedWindow);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
nsWindowWatcher::SizeOpenedDocShellItem(nsIDocShellTreeItem *aDocShellItem,
|
|
nsIDOMWindow *aParent,
|
|
const char *aFeatures,
|
|
PRUint32 aChromeFlags)
|
|
{
|
|
PRInt32 chromeX = 0, chromeY = 0, chromeCX = 100, chromeCY = 100;
|
|
PRInt32 contentCX = 100, contentCY = 100;
|
|
|
|
// Use sizes from the parent window, if any, as our default
|
|
if (aParent) {
|
|
nsCOMPtr<nsIDocShellTreeItem> item;
|
|
|
|
GetWindowTreeItem(aParent, getter_AddRefs(item));
|
|
if (item) {
|
|
// if we are content, we may need the content sizes
|
|
nsCOMPtr<nsIBaseWindow> win(do_QueryInterface(item));
|
|
win->GetSize(&contentCX, &contentCY);
|
|
|
|
// now the main window
|
|
nsCOMPtr<nsIDocShellTreeOwner> owner;
|
|
item->GetTreeOwner(getter_AddRefs(owner));
|
|
if (owner) {
|
|
nsCOMPtr<nsIBaseWindow> basewin(do_QueryInterface(owner));
|
|
if (basewin)
|
|
basewin->GetPositionAndSize(&chromeX, &chromeY,
|
|
&chromeCX, &chromeCY);
|
|
}
|
|
}
|
|
}
|
|
|
|
PRBool present = PR_FALSE;
|
|
PRBool positionSpecified = PR_FALSE;
|
|
PRInt32 temp;
|
|
|
|
if ((temp = WinHasOption(aFeatures, "left", 0, &present)) || present)
|
|
chromeX = temp;
|
|
else if ((temp = WinHasOption(aFeatures, "screenX", 0, &present)) || present)
|
|
chromeX = temp;
|
|
|
|
if (present)
|
|
positionSpecified = PR_TRUE;
|
|
|
|
present = PR_FALSE;
|
|
|
|
if ((temp = WinHasOption(aFeatures, "top", 0, &present)) || present)
|
|
chromeY = temp;
|
|
else if ((temp = WinHasOption(aFeatures, "screenY", 0, &present)) || present)
|
|
chromeY = temp;
|
|
|
|
if (present)
|
|
positionSpecified = PR_TRUE;
|
|
|
|
present = PR_FALSE;
|
|
|
|
PRBool sizeChrome = PR_FALSE;
|
|
PRBool sizeSpecified = PR_FALSE;
|
|
|
|
if ((temp = WinHasOption(aFeatures, "outerWidth", chromeCX, &present)) ||
|
|
present) {
|
|
chromeCX = temp;
|
|
sizeChrome = PR_TRUE;
|
|
sizeSpecified = PR_TRUE;
|
|
}
|
|
|
|
present = PR_FALSE;
|
|
|
|
if ((temp = WinHasOption(aFeatures, "outerHeight", chromeCY, &present)) ||
|
|
present) {
|
|
chromeCY = temp;
|
|
sizeChrome = PR_TRUE;
|
|
sizeSpecified = PR_TRUE;
|
|
}
|
|
|
|
// We haven't switched to chrome sizing so we need to get the content area
|
|
if (!sizeChrome) {
|
|
if ((temp = WinHasOption(aFeatures, "width", chromeCX, &present)) ||
|
|
present) {
|
|
contentCX = temp;
|
|
sizeSpecified = PR_TRUE;
|
|
}
|
|
else if ((temp = WinHasOption(aFeatures, "innerWidth", chromeCX, &present))
|
|
|| present) {
|
|
contentCX = temp;
|
|
sizeSpecified = PR_TRUE;
|
|
}
|
|
|
|
if ((temp = WinHasOption(aFeatures, "height", chromeCY, &present)) ||
|
|
present) {
|
|
contentCY = temp;
|
|
sizeSpecified = PR_TRUE;
|
|
}
|
|
else if ((temp = WinHasOption(aFeatures, "innerHeight", chromeCY, &present))
|
|
|| present) {
|
|
contentCY = temp;
|
|
sizeSpecified = PR_TRUE;
|
|
}
|
|
}
|
|
|
|
nsresult res;
|
|
PRBool enabled = PR_FALSE;
|
|
PRInt32 screenWidth = 0, screenHeight = 0;
|
|
PRInt32 winWidth, winHeight;
|
|
|
|
// Check security state for use in determing window dimensions
|
|
nsCOMPtr<nsIScriptSecurityManager>
|
|
securityManager(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
|
|
if (securityManager) {
|
|
res = securityManager->IsCapabilityEnabled("UniversalBrowserWrite", &enabled);
|
|
if (NS_FAILED(res))
|
|
enabled = PR_FALSE;
|
|
}
|
|
|
|
if (!enabled) {
|
|
// Security check failed. Ensure all args meet minimum reqs.
|
|
if (sizeSpecified) {
|
|
if (sizeChrome) {
|
|
chromeCX = chromeCX < 100 ? 100 : chromeCX;
|
|
chromeCY = chromeCY < 100 ? 100 : chromeCY;
|
|
}
|
|
else {
|
|
contentCX = contentCX < 100 ? 100 : contentCX;
|
|
contentCY = contentCY < 100 ? 100 : contentCY;
|
|
}
|
|
}
|
|
|
|
if (positionSpecified) {
|
|
// We'll also need the screen dimensions
|
|
// XXX This should use nsIScreenManager once it's fully fleshed out.
|
|
nsCOMPtr<nsIDOMScreen> screen;
|
|
if (aParent) {
|
|
nsCOMPtr<nsIDOMWindowInternal> intparent(do_QueryInterface(aParent));
|
|
if (intparent)
|
|
intparent->GetScreen(getter_AddRefs(screen));
|
|
} else {
|
|
// XXX hmmph. try the new window.
|
|
}
|
|
if (screen) {
|
|
screen->GetAvailWidth(&screenWidth);
|
|
screen->GetAvailHeight(&screenHeight);
|
|
}
|
|
|
|
// This isn't strictly true but close enough
|
|
winWidth = sizeSpecified ? (sizeChrome ? chromeCX : contentCX) : 100;
|
|
winHeight = sizeSpecified ? (sizeChrome ? chromeCY : contentCY) : 100;
|
|
|
|
chromeX =
|
|
screenWidth < chromeX + winWidth ? screenWidth - winWidth : chromeX;
|
|
chromeX = chromeX < 0 ? 0 : chromeX;
|
|
chromeY = screenHeight < chromeY + winHeight
|
|
? screenHeight - winHeight
|
|
: chromeY;
|
|
chromeY = chromeY < 0 ? 0 : chromeY;
|
|
}
|
|
}
|
|
|
|
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
|
|
aDocShellItem->GetTreeOwner(getter_AddRefs(treeOwner));
|
|
nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(treeOwner));
|
|
if (treeOwnerAsWin) {
|
|
if (sizeChrome) {
|
|
if (positionSpecified && sizeSpecified)
|
|
treeOwnerAsWin->SetPositionAndSize(chromeX, chromeY, chromeCX,
|
|
chromeCY, PR_FALSE);
|
|
else {
|
|
if (sizeSpecified)
|
|
treeOwnerAsWin->SetSize(chromeCX, chromeCY, PR_FALSE);
|
|
if (positionSpecified)
|
|
treeOwnerAsWin->SetPosition(chromeX, chromeY);
|
|
}
|
|
}
|
|
else {
|
|
if (positionSpecified)
|
|
treeOwnerAsWin->SetPosition(chromeX, chromeY);
|
|
if (sizeSpecified)
|
|
treeOwner->SizeShellTo(aDocShellItem, contentCX, contentCY);
|
|
}
|
|
treeOwnerAsWin->SetVisibility(PR_TRUE);
|
|
}
|
|
}
|
|
|
|
// attach the given array of JS values to the given window, as a property array
|
|
// named "arguments"
|
|
void
|
|
nsWindowWatcher::AttachArguments(nsIDOMWindow *aWindow,
|
|
PRUint32 argc, jsval *argv)
|
|
{
|
|
if (argc == 0)
|
|
return;
|
|
|
|
// copy the extra parameters into a JS Array and attach it
|
|
nsCOMPtr<nsIScriptGlobalObject> scriptGlobal(do_QueryInterface(aWindow));
|
|
if (scriptGlobal) {
|
|
nsCOMPtr<nsIScriptContext> scriptContext;
|
|
scriptGlobal->GetContext(getter_AddRefs(scriptContext));
|
|
if (scriptContext) {
|
|
JSContext *cx;
|
|
cx = (JSContext *) scriptContext->GetNativeContext();
|
|
nsCOMPtr<nsIScriptObjectOwner> owner(do_QueryInterface(aWindow));
|
|
if (owner) {
|
|
JSObject *scriptObject;
|
|
owner->GetScriptObject(scriptContext, (void **) &scriptObject);
|
|
JSObject *args;
|
|
args = ::JS_NewArrayObject(cx, argc, argv);
|
|
if (args) {
|
|
jsval argsVal = OBJECT_TO_JSVAL(args);
|
|
// ::JS_DefineProperty(cx, scriptObject, "arguments",
|
|
// argsVal, NULL, NULL, JSPROP_PERMANENT);
|
|
::JS_SetProperty(cx, scriptObject, "arguments", &argsVal);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsWindowWatcher::GetWindowTreeItem(nsIDOMWindow *inWindow,
|
|
nsIDocShellTreeItem **outTreeItem)
|
|
{
|
|
*outTreeItem = 0;
|
|
|
|
nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(inWindow));
|
|
if (sgo) {
|
|
nsCOMPtr<nsIDocShell> docshell;
|
|
sgo->GetDocShell(getter_AddRefs(docshell));
|
|
if (docshell)
|
|
CallQueryInterface(docshell, outTreeItem);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsWindowWatcher::GetWindowTreeOwner(nsIDOMWindow *inWindow,
|
|
nsIDocShellTreeOwner **outTreeOwner)
|
|
{
|
|
*outTreeOwner = 0;
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> treeItem;
|
|
GetWindowTreeItem(inWindow, getter_AddRefs(treeItem));
|
|
if (treeItem)
|
|
treeItem->GetTreeOwner(outTreeOwner);
|
|
}
|
|
|
|
JSContext *
|
|
nsWindowWatcher::GetExtantJSContext(nsIDOMWindow *aWindow)
|
|
{
|
|
JSContext *cx;
|
|
|
|
// given a window, we'll use its
|
|
cx = 0;
|
|
if (aWindow) {
|
|
nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(aWindow));
|
|
if (sgo) {
|
|
nsCOMPtr<nsIScriptContext> scx;
|
|
sgo->GetContext(getter_AddRefs(scx));
|
|
if (scx)
|
|
cx = (JSContext *) scx->GetNativeContext();
|
|
}
|
|
/* (off-topic note:) the nsIScriptContext can be retrieved by
|
|
nsCOMPtr<nsIScriptContext> scx;
|
|
nsJSUtils::nsGetDynamicScriptContext(cx, getter_AddRefs(scx));
|
|
*/
|
|
}
|
|
|
|
// still no JSContext? try pulling one from the stack.
|
|
if (!cx) {
|
|
nsCOMPtr<nsIThreadJSContextStack> cxStack(do_GetService(sJSStackContractID));
|
|
if (cxStack)
|
|
cxStack->Peek(&cx);
|
|
/* We explicitly do not use GetSafeJSContext to force one if Peek
|
|
finds nothing. That's done, if necessary, by a helper class which
|
|
knows how to clean up after itself.
|
|
*/
|
|
}
|
|
|
|
return cx;
|
|
}
|
|
|
|
JSObject *
|
|
nsWindowWatcher::GetWindowScriptObject(nsIDOMWindow *inWindow)
|
|
{
|
|
JSObject *object = 0;
|
|
|
|
nsCOMPtr<nsIScriptGlobalObject> scriptGlobal(do_QueryInterface(inWindow));
|
|
if (scriptGlobal) {
|
|
nsCOMPtr<nsIScriptContext> scriptContext;
|
|
scriptGlobal->GetContext(getter_AddRefs(scriptContext));
|
|
|
|
nsCOMPtr<nsIScriptObjectOwner> owner(do_QueryInterface(inWindow));
|
|
if (owner)
|
|
owner->GetScriptObject(scriptContext, (void **) &object);
|
|
}
|
|
return object;
|
|
}
|
|
|