danm%netscape.com bda3afbc6a ignore width or height of 0 (as in 'width=') when opening windows. bug 67947 r=hyatt,joki
git-svn-id: svn://10.0.0.236/trunk@102222 18797224-902f-48f8-a5cc-f745e15eee43
2001-09-04 23:14:31 +00:00

1861 lines
54 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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 "nsWWJSUtils.h"
#include "nsNetUtil.h"
#include "nsPrompt.h"
#include "plstr.h"
#include "nsIBaseWindow.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 "nsIScriptContext.h"
#include "nsIEventQueue.h"
#include "nsIEventQueueService.h"
#include "nsIGenericFactory.h"
#include "nsIJSContextStack.h"
#include "nsIObserverService.h"
#include "nsIScriptGlobalObject.h"
#include "nsIScriptSecurityManager.h"
#include "nsISupportsArray.h"
#include "nsISupportsPrimitives.h"
#include "nsIURI.h"
#include "nsIWebBrowser.h"
#include "nsIWebBrowserChrome.h"
#include "nsIWebNavigation.h"
#include "nsIWindowCreator.h"
#include "nsIXPConnect.h"
#ifdef XP_UNIX
// please see bug 78421 for the eventual "right" fix for this
#define HAVE_LAME_APPSHELL
#endif
#ifdef HAVE_LAME_APPSHELL
#include "nsIAppShell.h"
// for NS_APPSHELL_CID
#include <nsWidgetsCID.h>
#endif
#ifdef USEWEAKREFS
#include "nsIWeakReference.h"
#endif
#define NOTIFICATION_OPENED NS_LITERAL_STRING("domwindowopened")
#define NOTIFICATION_CLOSED NS_LITERAL_STRING("domwindowclosed")
#ifdef HAVE_LAME_APPSHELL
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
#endif
static const char *sJSStackContractID="@mozilla.org/js/xpc/ContextStack;1";
/****************************************************************
******************** nsWatcherWindowEntry **********************
****************************************************************/
class nsWindowWatcher;
struct nsWatcherWindowEntry {
nsWatcherWindowEntry(nsIDOMWindow *inWindow, nsIWebBrowserChrome *inChrome) {
#ifdef USEWEAKREFS
mWindow = getter_AddRefs(NS_GetWeakReference(inWindow));
#else
mWindow = inWindow;
#endif
mChrome = inChrome;
ReferenceSelf();
}
~nsWatcherWindowEntry() {}
void InsertAfter(nsWatcherWindowEntry *inOlder);
void Unlink();
void ReferenceSelf();
#ifdef USEWEAKREFS
nsCOMPtr<nsIWeakReference> mWindow;
#else // still not an owning ref
nsIDOMWindow *mWindow;
#endif
nsIWebBrowserChrome *mChrome;
// each struct is in a circular, doubly-linked list
nsWatcherWindowEntry *mYounger, // next younger in sequence
*mOlder;
};
void nsWatcherWindowEntry::InsertAfter(nsWatcherWindowEntry *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 nsWatcherWindowEntry::Unlink() {
mOlder->mYounger = mYounger;
mYounger->mOlder = mOlder;
ReferenceSelf();
}
void nsWatcherWindowEntry::ReferenceSelf() {
mYounger = this;
mOlder = this;
}
/****************************************************************
****************** nsWatcherWindowEnumerator *******************
****************************************************************/
class nsWatcherWindowEnumerator : public nsISimpleEnumerator {
public:
nsWatcherWindowEnumerator(nsWindowWatcher *inWatcher);
virtual ~nsWatcherWindowEnumerator();
NS_IMETHOD HasMoreElements(PRBool *retval);
NS_IMETHOD GetNext(nsISupports **retval);
NS_DECL_ISUPPORTS
private:
friend class nsWindowWatcher;
nsWatcherWindowEntry *FindNext();
void WindowRemoved(nsWatcherWindowEntry *inInfo);
nsWindowWatcher *mWindowWatcher;
nsWatcherWindowEntry *mCurrentPosition;
};
NS_IMPL_ADDREF(nsWatcherWindowEnumerator);
NS_IMPL_RELEASE(nsWatcherWindowEnumerator);
NS_IMPL_QUERY_INTERFACE1(nsWatcherWindowEnumerator, nsISimpleEnumerator);
nsWatcherWindowEnumerator::nsWatcherWindowEnumerator(nsWindowWatcher *inWatcher)
: mWindowWatcher(inWatcher),
mCurrentPosition(inWatcher->mOldestWindow)
{
NS_INIT_REFCNT();
mWindowWatcher->AddEnumerator(this);
mWindowWatcher->AddRef();
}
nsWatcherWindowEnumerator::~nsWatcherWindowEnumerator()
{
mWindowWatcher->RemoveEnumerator(this);
mWindowWatcher->Release();
}
NS_IMETHODIMP
nsWatcherWindowEnumerator::HasMoreElements(PRBool *retval)
{
if (!retval)
return NS_ERROR_INVALID_ARG;
*retval = mCurrentPosition? PR_TRUE : PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsWatcherWindowEnumerator::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;
}
nsWatcherWindowEntry *
nsWatcherWindowEnumerator::FindNext()
{
nsWatcherWindowEntry *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 nsWatcherWindowEnumerator::WindowRemoved(nsWatcherWindowEntry *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;
#ifdef HAVE_LAME_APPSHELL
nsCOMPtr<nsIAppShell> mAppShell;
#endif
};
EventQueueAutoPopper::EventQueueAutoPopper() : mQueue(nsnull)
{
}
EventQueueAutoPopper::~EventQueueAutoPopper()
{
#ifdef HAVE_LAME_APPSHELL
if (mAppShell) {
if (mQueue)
mAppShell->ListenToEventQueue(mQueue, PR_FALSE);
mAppShell->Spindown();
mAppShell = nsnull;
}
#endif
if(mQueue)
mService->PopThreadEventQueue(mQueue);
}
nsresult EventQueueAutoPopper::Push()
{
if (mQueue) // only once
return NS_ERROR_FAILURE;
mService = do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID);
if (!mService)
return NS_ERROR_FAILURE;
// push a new queue onto it
mService->PushThreadEventQueue(getter_AddRefs(mQueue));
if (!mQueue)
return NS_ERROR_FAILURE;
#ifdef HAVE_LAME_APPSHELL
// listen to the event queue
mAppShell = do_CreateInstance(kAppShellCID);
if (!mAppShell)
return NS_ERROR_FAILURE;
mAppShell->Create(0, nsnull);
mAppShell->Spinup();
// listen to the new queue
mAppShell->ListenToEventQueue(mQueue, PR_TRUE);
#endif
return NS_OK;
}
/****************************************************************
********************** 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;
}
/****************************************************************
************************** AutoFree ****************************
****************************************************************/
class AutoFree {
public:
AutoFree(void *aPtr) : mPtr(aPtr) {
}
~AutoFree() {
if (mPtr)
nsMemory::Free(mPtr);
}
void Invalidate() {
mPtr = 0;
}
private:
void *mPtr;
};
/****************************************************************
*********************** 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 char *aUrl,
const char *aName,
const char *aFeatures,
nsISupports *aArguments,
nsIDOMWindow **_retval)
{
PRUint32 argc;
jsval *argv = nsnull;
nsresult rv;
rv = ConvertSupportsTojsvals(aParent, aArguments, &argc, &argv);
if (NS_SUCCEEDED(rv)) {
PRBool dialog = argc == 0 ? PR_FALSE : PR_TRUE;
rv = OpenWindowJS(aParent, aUrl, aName, aFeatures, dialog, argc, argv,
_retval);
}
if (argv) // Free goes to libc free(). so i'm assuming a bad libc.
nsMemory::Free(argv);
return rv;
}
NS_IMETHODIMP
nsWindowWatcher::OpenWindowJS(nsIDOMWindow *aParent,
const char *aUrl,
const char *aName,
const char *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.AssignWithConversion(aName);
CheckWindowName(name);
nameSpecified = PR_TRUE;
}
featuresSpecified = PR_FALSE;
if (aFeatures) {
features.Assign(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 {
/* parent is being simultaneously torn down (probably because of
the code that keeps an old docshell alive but disconnected while
we load a new one). not much to do but open the new window
without a parent. */
if (parentTreeOwner)
parentTreeOwner->FindItemWithName(name.get(), nsnull,
getter_AddRefs(newDocShellItem));
}
} else
FindItemWithName(name.get(), 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) {
rv = AttachArguments(*_retval, argc, argv);
if (NS_FAILED(rv)) {
return rv;
}
}
newDocShellItem->SetName(nameSpecified ? name.get() : nsnull);
nsCOMPtr<nsIDocShell> newDocShell(do_QueryInterface(newDocShellItem));
if (uriToLoad) { // get the script principal and pass it to docshell
// get the security manager
nsCOMPtr<nsIScriptSecurityManager> secMan;
JSContext *cx;
nsCOMPtr<nsIScriptContext> scriptCX;
cx = GetJSContextFromCallStack();
if (!cx)
cx = GetJSContextFromWindow(aParent);
if (!cx) {
rv = contextGuard.Push();
if (NS_FAILED(rv))
return rv;
cx = contextGuard.get();
}
JSObject *scriptObject = GetWindowScriptObject(aParent ? aParent : *_retval);
nsWWJSUtils::nsGetStaticScriptContext(cx, scriptObject,
getter_AddRefs(scriptCX));
if (!scriptCX ||
NS_FAILED(scriptCX->GetSecurityManager(getter_AddRefs(secMan))))
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
newDocShell->CreateLoadInfo(getter_AddRefs(loadInfo));
NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);
PRBool isChrome = PR_FALSE;
rv = uriToLoad->SchemeIs("chrome", &isChrome);
if (NS_FAILED(rv) || !isChrome) {
nsCOMPtr<nsIPrincipal> principal;
if (NS_FAILED(secMan->GetSubjectPrincipal(getter_AddRefs(principal))))
return 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) {
nsCOMPtr<nsIScriptGlobalObject> sgo;
nsWWJSUtils::nsGetStaticScriptGlobal(ccx, ::JS_GetGlobalObject(ccx),
getter_AddRefs(sgo));
nsCOMPtr<nsIDOMWindow> w(do_QueryInterface(sgo));
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;
doc->GetDocumentURL(getter_AddRefs(uri));
// 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);
nsWatcherWindowEnumerator *enumerator = new nsWatcherWindowEnumerator(this);
if (enumerator)
return CallQueryInterface(enumerator, _retval);
return NS_ERROR_OUT_OF_MEMORY;
}
NS_IMETHODIMP
nsWindowWatcher::GetNewPrompter(nsIDOMWindow *aParent, nsIPrompt **_retval)
{
return NS_NewPrompter(_retval, aParent);
}
NS_IMETHODIMP
nsWindowWatcher::GetNewAuthPrompter(nsIDOMWindow *aParent, nsIAuthPrompt **_retval)
{
return NS_NewAuthPrompter(_retval, aParent);
}
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, nsIWebBrowserChrome *aChrome)
{
nsresult rv;
if (!aWindow)
return NS_ERROR_INVALID_ARG;
nsWatcherWindowEntry *info;
nsAutoLock lock(mListLock);
// if we already have an entry for this window, adjust
// its chrome mapping and return
info = FindWindowEntry(aWindow);
if (info) {
info->mChrome = aChrome;
return NS_OK;
}
// create a window info struct and add it to the list of windows
info = new nsWatcherWindowEntry(aWindow, aChrome);
if (!info)
return NS_ERROR_OUT_OF_MEMORY;
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 nsWatcherWindowEntry, remove it
if (!aWindow)
return NS_ERROR_INVALID_ARG;
nsWatcherWindowEntry *info = FindWindowEntry(aWindow);
if (info) {
RemoveWindow(info);
return NS_OK;
}
NS_WARNING("requested removal of nonexistent window\n");
return NS_ERROR_INVALID_ARG;
}
nsWatcherWindowEntry *
nsWindowWatcher::FindWindowEntry(nsIDOMWindow *aWindow)
{
// find the corresponding nsWatcherWindowEntry
nsWatcherWindowEntry *info,
*listEnd;
#ifdef USEWEAKREFS
nsresult rv;
PRBool found;
#endif
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) // clean up dangling reference, while we're here
rv = RemoveWindow(info);
}
else if (infoWindow.get() == aWindow)
return info;
info = info->mYounger;
listEnd = mOldestWindow;
}
return 0;
#else
while (info != listEnd) {
if (info->mWindow == aWindow)
return info;
info = info->mYounger;
listEnd = mOldestWindow;
}
return 0;
#endif
}
nsresult nsWindowWatcher::RemoveWindow(nsWatcherWindowEntry *inInfo)
{
PRInt32 ctr,
count = mEnumeratorList.Count();
nsresult rv;
{
// notify the enumerators
nsAutoLock lock(mListLock);
for (ctr = 0; ctr < count; ++ctr)
((nsWatcherWindowEnumerator*)mEnumeratorList[ctr])->WindowRemoved(inInfo);
// remove the element from the list
if (inInfo == mOldestWindow)
mOldestWindow = inInfo->mYounger == mOldestWindow ? 0 : inInfo->mYounger;
inInfo->Unlink();
// clear the active window, if they're the same
if (mActiveWindow == inInfo->mWindow)
mActiveWindow = 0;
}
// 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;
}
NS_IMETHODIMP
nsWindowWatcher::GetChromeForWindow(nsIDOMWindow *aWindow, nsIWebBrowserChrome **_retval)
{
if (!aWindow || !_retval)
return NS_ERROR_INVALID_ARG;
*_retval = 0;
nsAutoLock lock(mListLock);
nsWatcherWindowEntry *info = FindWindowEntry(aWindow);
if (info) {
*_retval = info->mChrome;
NS_IF_ADDREF(*_retval);
}
return NS_OK;
}
PRBool
nsWindowWatcher::AddEnumerator(nsWatcherWindowEnumerator* inEnumerator)
{
// (requires a lock; assumes it's called by someone holding the lock)
return mEnumeratorList.AppendElement(inEnumerator);
}
PRBool
nsWindowWatcher::RemoveEnumerator(nsWatcherWindowEnumerator* inEnumerator)
{
// (requires a lock; assumes it's called by someone holding the lock)
return mEnumeratorList.RemoveElement(inEnumerator);
}
nsresult
nsWindowWatcher::URIfromURL(const char *aURL,
nsIDOMWindow *aParent,
nsIURI **aURI)
{
nsCOMPtr<nsIDOMWindow> baseWindow;
/* build the URI relative to the calling JS Context, if any.
(note this is the same context used to make the security check
in nsGlobalWindow.cpp.) */
JSContext *cx = GetJSContextFromCallStack();
if (cx) {
nsISupports *cxsup = (nsISupports *) JS_GetContextPrivate(cx);
nsCOMPtr<nsIScriptContext> scriptcx(do_QueryInterface(cxsup));
if (scriptcx) {
nsCOMPtr<nsIScriptGlobalObject> gobj;
scriptcx->GetGlobalObject(getter_AddRefs(gobj));
baseWindow = do_QueryInterface(gobj);
}
}
// failing that, build it relative to the parent window, if possible
if (!baseWindow)
baseWindow = aParent;
// failing that, use the given URL unmodified. It had better not be relative.
nsCOMPtr<nsIURI> baseURI;
// get baseWindow's document URI
if (baseWindow) {
nsCOMPtr<nsIDOMDocument> domDoc;
baseWindow->GetDocument(getter_AddRefs(domDoc));
if (domDoc) {
nsCOMPtr<nsIDocument> doc;
doc = do_QueryInterface(domDoc);
if (doc)
doc->GetDocumentURL(getter_AddRefs(baseURI));
}
}
// build and return the absolute URI
return NS_NewURI(aURI, aURL, baseURI);
}
/* 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;
PRBool sizeChrome = PR_FALSE;
PRBool sizeSpecified = PR_FALSE;
if (temp = WinHasOption(aFeatures, "outerWidth", chromeCX, nsnull)) {
chromeCX = temp;
sizeChrome = PR_TRUE;
sizeSpecified = PR_TRUE;
}
if (temp = WinHasOption(aFeatures, "outerHeight", chromeCY, nsnull)) {
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, nsnull)) {
contentCX = temp;
sizeSpecified = PR_TRUE;
} else if (temp = WinHasOption(aFeatures, "innerWidth", chromeCX, nsnull)) {
contentCX = temp;
sizeSpecified = PR_TRUE;
}
if (temp = WinHasOption(aFeatures, "height", chromeCY, nsnull)) {
contentCY = temp;
sizeSpecified = PR_TRUE;
} else if (temp = WinHasOption(aFeatures, "innerHeight", chromeCY, nsnull)) {
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"
nsresult
nsWindowWatcher::AttachArguments(nsIDOMWindow *aWindow,
PRUint32 argc, jsval *argv)
{
if (argc == 0)
return NS_OK;
// copy the extra parameters into a JS Array and attach it
nsCOMPtr<nsIScriptGlobalObject> scriptGlobal(do_QueryInterface(aWindow));
NS_ENSURE_TRUE(scriptGlobal, NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIScriptContext> scriptContext;
scriptGlobal->GetContext(getter_AddRefs(scriptContext));
if (scriptContext) {
JSContext *cx;
cx = (JSContext *)scriptContext->GetNativeContext();
nsresult rv;
nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
rv = xpc->WrapNative(cx, ::JS_GetGlobalObject(cx), aWindow,
NS_GET_IID(nsIDOMWindow), getter_AddRefs(wrapper));
NS_ENSURE_SUCCESS(rv, rv);
JSObject *window_obj;
rv = wrapper->GetJSObject(&window_obj);
NS_ENSURE_SUCCESS(rv, rv);
JSObject *args;
args = ::JS_NewArrayObject(cx, argc, argv);
if (args) {
jsval argsVal = OBJECT_TO_JSVAL(args);
// ::JS_DefineProperty(cx, window_obj, "arguments",
// argsVal, NULL, NULL, JSPROP_PERMANENT);
::JS_SetProperty(cx, window_obj, "arguments", &argsVal);
}
}
return NS_OK;
}
nsresult
nsWindowWatcher::ConvertSupportsTojsvals(nsIDOMWindow *aWindow,
nsISupports *aArgs,
PRUint32 *aArgc, jsval **aArgv)
{
nsresult rv = NS_OK;
*aArgv = nsnull;
*aArgc = 0;
// copy the elements in aArgsArray into the JS array
// window.arguments in the new window
if (!aArgs)
return NS_OK;
PRUint32 argCtr, argCount;
nsCOMPtr<nsISupportsArray> argsArray(do_QueryInterface(aArgs));
if (argsArray) {
argsArray->Count(&argCount);
if (argCount == 0)
return NS_OK;
} else
argCount = 1; // the nsISupports which is not an array
jsval *argv = NS_STATIC_CAST(jsval *, nsMemory::Alloc(argCount * sizeof(jsval)));
NS_ENSURE_TRUE(argv, NS_ERROR_OUT_OF_MEMORY);
AutoFree argvGuard(argv);
JSContext *cx;
JSContextAutoPopper contextGuard;
cx = GetJSContextFromWindow(aWindow);
if (!cx)
cx = GetJSContextFromCallStack();
if (!cx) {
rv = contextGuard.Push();
if (NS_FAILED(rv))
return rv;
cx = contextGuard.get();
}
if (argsArray)
for (argCtr = 0; argCtr < argCount && NS_SUCCEEDED(rv); argCtr++) {
nsCOMPtr<nsISupports> s(dont_AddRef(argsArray->ElementAt(argCtr)));
rv = AddSupportsTojsvals(s, cx, argv + argCtr);
}
else
rv = AddSupportsTojsvals(aArgs, cx, argv);
if (NS_FAILED(rv))
return rv;
argvGuard.Invalidate();
*aArgv = argv;
*aArgc = argCount;
return NS_OK;
}
nsresult
nsWindowWatcher::AddInterfaceTojsvals(nsISupports *aArg,
JSContext *cx,
jsval *aArgv)
{
nsresult rv;
nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
rv = xpc->WrapNative(cx, ::JS_GetGlobalObject(cx), aArg,
NS_GET_IID(nsISupports), getter_AddRefs(wrapper));
NS_ENSURE_SUCCESS(rv, rv);
JSObject *obj;
rv = wrapper->GetJSObject(&obj);
NS_ENSURE_SUCCESS(rv, rv);
*aArgv = OBJECT_TO_JSVAL(obj);
return NS_OK;
}
nsresult
nsWindowWatcher::AddSupportsTojsvals(nsISupports *aArg,
JSContext *cx, jsval *aArgv)
{
if (!aArg) {
*aArgv = JSVAL_NULL;
return NS_OK;
}
nsCOMPtr<nsISupportsPrimitive> argPrimitive(do_QueryInterface(aArg));
if (!argPrimitive)
return AddInterfaceTojsvals(aArg, cx, aArgv);
PRUint16 type;
argPrimitive->GetType(&type);
switch(type) {
case nsISupportsPrimitive::TYPE_STRING : {
nsCOMPtr<nsISupportsString> p(do_QueryInterface(argPrimitive));
NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
char *data;
p->GetData(&data);
JSString *str = ::JS_NewString(cx, data, nsCRT::strlen(data));
NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
*aArgv = STRING_TO_JSVAL(str);
break;
}
case nsISupportsPrimitive::TYPE_WSTRING : {
nsCOMPtr<nsISupportsWString> p(do_QueryInterface(argPrimitive));
NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
PRUnichar *data;
p->GetData(&data);
// cast is probably safe since wchar_t and jschar are expected
// to be equivalent; both unsigned 16-bit entities
JSString *str = ::JS_NewUCString(cx, NS_REINTERPRET_CAST(jschar *, data),
nsCRT::strlen(data));
NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
*aArgv = STRING_TO_JSVAL(str);
break;
}
case nsISupportsPrimitive::TYPE_PRBOOL : {
nsCOMPtr<nsISupportsPRBool> p(do_QueryInterface(argPrimitive));
NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
PRBool data;
p->GetData(&data);
*aArgv = BOOLEAN_TO_JSVAL(data);
break;
}
case nsISupportsPrimitive::TYPE_PRUINT8 : {
nsCOMPtr<nsISupportsPRUint8> p(do_QueryInterface(argPrimitive));
NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
PRUint8 data;
p->GetData(&data);
*aArgv = INT_TO_JSVAL(data);
break;
}
case nsISupportsPrimitive::TYPE_PRUINT16 : {
nsCOMPtr<nsISupportsPRUint16> p(do_QueryInterface(argPrimitive));
NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
PRUint16 data;
p->GetData(&data);
*aArgv = INT_TO_JSVAL(data);
break;
}
case nsISupportsPrimitive::TYPE_PRUINT32 : {
nsCOMPtr<nsISupportsPRUint32> p(do_QueryInterface(argPrimitive));
NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
PRUint32 data;
p->GetData(&data);
*aArgv = INT_TO_JSVAL(data);
break;
}
case nsISupportsPrimitive::TYPE_CHAR : {
nsCOMPtr<nsISupportsChar> p(do_QueryInterface(argPrimitive));
NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
char data;
p->GetData(&data);
JSString *str = ::JS_NewStringCopyN(cx, &data, 1);
NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
*aArgv = STRING_TO_JSVAL(str);
break;
}
case nsISupportsPrimitive::TYPE_PRINT16 : {
nsCOMPtr<nsISupportsPRInt16> p(do_QueryInterface(argPrimitive));
NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
PRInt16 data;
p->GetData(&data);
*aArgv = INT_TO_JSVAL(data);
break;
}
case nsISupportsPrimitive::TYPE_PRINT32 : {
nsCOMPtr<nsISupportsPRInt32> p(do_QueryInterface(argPrimitive));
NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
PRInt32 data;
p->GetData(&data);
*aArgv = INT_TO_JSVAL(data);
break;
}
case nsISupportsPrimitive::TYPE_FLOAT : {
nsCOMPtr<nsISupportsFloat> p(do_QueryInterface(argPrimitive));
NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
float data;
p->GetData(&data);
jsdouble *d = ::JS_NewDouble(cx, data);
*aArgv = DOUBLE_TO_JSVAL(d);
break;
}
case nsISupportsPrimitive::TYPE_DOUBLE : {
nsCOMPtr<nsISupportsDouble> p(do_QueryInterface(argPrimitive));
NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
double data;
p->GetData(&data);
jsdouble *d = ::JS_NewDouble(cx, data);
*aArgv = DOUBLE_TO_JSVAL(d);
break;
}
case nsISupportsPrimitive::TYPE_INTERFACE_POINTER : {
nsCOMPtr<nsISupportsInterfacePointer> p(do_QueryInterface(argPrimitive));
NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
nsCOMPtr<nsISupports> data;
nsIID *iid = nsnull;
p->GetData(getter_AddRefs(data));
p->GetDataIID(&iid);
NS_ENSURE_TRUE(iid, NS_ERROR_UNEXPECTED);
AutoFree iidGuard(iid); // Free iid upon destruction.
nsresult rv;
nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
rv = xpc->WrapNative(cx, ::JS_GetGlobalObject(cx), data,
*iid, getter_AddRefs(wrapper));
NS_ENSURE_SUCCESS(rv, rv);
JSObject *obj;
rv = wrapper->GetJSObject(&obj);
NS_ENSURE_SUCCESS(rv, rv);
*aArgv = OBJECT_TO_JSVAL(obj);
break;
}
case nsISupportsPrimitive::TYPE_ID :
case nsISupportsPrimitive::TYPE_PRUINT64 :
case nsISupportsPrimitive::TYPE_PRINT64 :
case nsISupportsPrimitive::TYPE_PRTIME :
case nsISupportsPrimitive::TYPE_VOID : {
NS_WARNING("Unsupported primitive type used");
*aArgv = JSVAL_NULL;
break;
}
default : {
NS_WARNING("Unknown primitive type used");
*aArgv = JSVAL_NULL;
break;
}
}
return NS_OK;
}
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::GetJSContextFromCallStack()
{
JSContext *cx = 0;
nsCOMPtr<nsIThreadJSContextStack> cxStack(do_GetService(sJSStackContractID));
if (cxStack)
cxStack->Peek(&cx);
return cx;
}
JSContext *
nsWindowWatcher::GetJSContextFromWindow(nsIDOMWindow *aWindow)
{
JSContext *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));
*/
}
return cx;
}
JSObject *
nsWindowWatcher::GetWindowScriptObject(nsIDOMWindow *inWindow)
{
nsCOMPtr<nsIScriptGlobalObject> scriptGlobal(do_QueryInterface(inWindow));
return scriptGlobal ? scriptGlobal->GetGlobalJSObject() : 0;
}