pjs/docshell/base/nsWebShell.cpp

1295 строки
31 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.0 (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 Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are Copyright (C) 1998
* Netscape Communications Corporation. All Rights Reserved.
*/
#include "nsIWebShell.h"
#include "nsIDocumentLoader.h"
#include "nsIContentViewer.h"
#include "nsIDeviceContext.h"
#include "nsILinkHandler.h"
#include "nsIStreamListener.h"
#include "nsIScriptGlobalObject.h"
#include "nsIScriptContextOwner.h"
#include "nsRepository.h"
#include "nsCRT.h"
#include "nsVoidArray.h"
#include "nsString.h"
#include "nsWidgetsCID.h"
#include "nsGfxCIID.h"
#include "plevent.h"
#include "nsplugin.h"
#include "nsIPluginHost.h"
#include "nsPluginsCID.h"
#include "prlog.h"
//XXX for nsIPostData; this is wrong; we shouldn't see the nsIDocument type
#include "nsIDocument.h"
#ifdef NS_DEBUG
/**
* Note: the log module is created during initialization which
* means that you cannot perform logging before then.
*/
static PRLogModuleInfo* gLogModule = PR_NewLogModule("webwidget");
#endif
#define WEB_TRACE_CALLS 0x1
#define WEB_TRACE_HISTORY 0x2
#define WEB_LOG_TEST(_lm,_bit) (PRIntn((_lm)->level) & (_bit))
#ifdef NS_DEBUG
#define WEB_TRACE(_bit,_args) \
PR_BEGIN_MACRO \
if (WEB_LOG_TEST(gLogModule,_bit)) { \
PR_LogPrint _args; \
} \
PR_END_MACRO
#else
#define WEB_TRACE(_bit,_args)
#endif
//----------------------------------------------------------------------
class nsWebShell : public nsIWebShell,
public nsIWebShellContainer,
public nsILinkHandler,
public nsIScriptContextOwner
{
public:
nsWebShell();
virtual ~nsWebShell();
void* operator new(size_t sz) {
void* rv = new char[sz];
nsCRT::zero(rv, sz);
return rv;
}
// nsISupports
NS_DECL_ISUPPORTS
// nsIContentViewerContainer
NS_IMETHOD QueryCapability(const nsIID &aIID, void** aResult);
NS_IMETHOD Embed(nsIContentViewer* aDocViewer,
const char* aCommand,
nsISupports* aExtraInfo);
// nsIWebShell
NS_IMETHOD Init(nsNativeWidget aNativeParent,
const nsRect& aBounds,
nsScrollPreference aScrolling = nsScrollPreference_kAuto);
NS_IMETHOD Destroy(void);
NS_IMETHOD GetBounds(nsRect& aResult);
NS_IMETHOD SetBounds(const nsRect& aBounds);
NS_IMETHOD MoveTo(PRInt32 aX, PRInt32 aY);
NS_IMETHOD Show();
NS_IMETHOD Hide();
NS_IMETHOD GetContentViewer(nsIContentViewer*& aResult);
NS_IMETHOD SetContainer(nsIWebShellContainer* aContainer);
NS_IMETHOD GetContainer(nsIWebShellContainer*& aResult);
NS_IMETHOD SetObserver(nsIStreamObserver* anObserver);
NS_IMETHOD GetObserver(nsIStreamObserver*& aResult);
NS_IMETHOD GetDocumentLoader(nsIDocumentLoader*& aResult);
NS_IMETHOD GetRootWebShell(nsIWebShell*& aResult);
NS_IMETHOD SetParent(nsIWebShell* aParent);
NS_IMETHOD GetParent(nsIWebShell*& aParent);
NS_IMETHOD GetChildCount(PRInt32& aResult);
NS_IMETHOD AddChild(nsIWebShell* aChild);
NS_IMETHOD ChildAt(PRInt32 aIndex, nsIWebShell*& aResult);
NS_IMETHOD GetName(nsString& aName);
NS_IMETHOD SetName(const nsString& aName);
NS_IMETHOD FindChildWithName(const nsString& aName,
nsIWebShell*& aResult);
NS_IMETHOD Back(void);
NS_IMETHOD Forward(void);
NS_IMETHOD LoadURL(const nsString& aURLSpec,
nsIPostData* aPostData=nsnull);
NS_IMETHOD GoTo(PRInt32 aHistoryIndex);
NS_IMETHOD GetHistoryIndex(PRInt32& aResult);
NS_IMETHOD GetURL(PRInt32 aHistoryIndex, nsString& aURLResult);
NS_IMETHOD SetTitle(const nsString& aTitle);
NS_IMETHOD GetTitle(nsString& aResult);
// nsIWebShellContainer
NS_IMETHOD WillLoadURL(nsIWebShell* aShell, const nsString& aURL);
NS_IMETHOD BeginLoadURL(nsIWebShell* aShell, const nsString& aURL);
NS_IMETHOD EndLoadURL(nsIWebShell* aShell, const nsString& aURL);
// nsILinkHandler
NS_IMETHOD OnLinkClick(nsIFrame* aFrame,
const nsString& aURLSpec,
const nsString& aTargetSpec,
nsIPostData* aPostData = 0);
NS_IMETHOD OnOverLink(nsIFrame* aFrame,
const nsString& aURLSpec,
const nsString& aTargetSpec);
NS_IMETHOD GetLinkState(const nsString& aURLSpec, nsLinkState& aState);
// nsIScriptContextOwner
NS_IMETHOD GetScriptContext(nsIScriptContext **aContext);
NS_IMETHOD GetScriptGlobalObject(nsIScriptGlobalObject **aGlobal);
NS_IMETHOD ReleaseScriptContext(nsIScriptContext *aContext);
// nsWebShell
void HandleLinkClickEvent(const nsString& aURLSpec,
const nsString& aTargetSpec,
nsIPostData* aPostDat = 0);
void ShowHistory();
nsIWebShell* GetTarget(const nsString& aName);
static nsEventStatus PR_CALLBACK HandleEvent(nsGUIEvent *aEvent);
static nsresult CreatePluginHost(void);
protected:
nsIScriptGlobalObject *mScriptGlobal;
nsIScriptContext* mScriptContext;
nsIWebShellContainer* mContainer;
nsIContentViewer* mContentViewer;
nsIDeviceContext* mDeviceContext;
nsIWidget* mWindow;
nsISupports* mInnerWindow;
nsIDocumentLoader* mDocLoader;
nsIStreamObserver* mObserver;
nsIWebShell* mParent;
nsVoidArray mChildren;
nsString mName;
nsVoidArray mHistory;
PRInt32 mHistoryIndex;
nsString mTitle;
nsString mOverURL;
nsString mOverTarget;
nsScrollPreference mScrollPref;
void ReleaseChildren();
static nsIPluginHost *mPluginHost;
static nsIPluginManager *mPluginManager;
};
//----------------------------------------------------------------------
// Class IID's
static NS_DEFINE_IID(kChildCID, NS_CHILD_CID);
static NS_DEFINE_IID(kDeviceContextCID, NS_DEVICE_CONTEXT_CID);
static NS_DEFINE_IID(kDocumentLoaderCID, NS_DOCUMENTLOADER_CID);
static NS_DEFINE_IID(kWebShellCID, NS_WEB_SHELL_CID);
// IID's
static NS_DEFINE_IID(kIContentViewerContainerIID,
NS_ICONTENT_VIEWER_CONTAINER_IID);
static NS_DEFINE_IID(kIDeviceContextIID, NS_IDEVICE_CONTEXT_IID);
static NS_DEFINE_IID(kIDocumentLoaderIID, NS_IDOCUMENTLOADER_IID);
static NS_DEFINE_IID(kIFactoryIID, NS_IFACTORY_IID);
static NS_DEFINE_IID(kIScriptContextOwnerIID, NS_ISCRIPTCONTEXTOWNER_IID);
static NS_DEFINE_IID(kIStreamObserverIID, NS_ISTREAMOBSERVER_IID);
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
static NS_DEFINE_IID(kIWebShellIID, NS_IWEB_SHELL_IID);
static NS_DEFINE_IID(kIWidgetIID, NS_IWIDGET_IID);
static NS_DEFINE_IID(kIPluginManagerIID, NS_IPLUGINMANAGER_IID);
static NS_DEFINE_IID(kIPluginHostIID, NS_IPLUGINHOST_IID);
static NS_DEFINE_IID(kCPluginHostCID, NS_PLUGIN_HOST_CID);
// XXX not sure
static NS_DEFINE_IID(kILinkHandlerIID, NS_ILINKHANDLER_IID);
nsIPluginHost *nsWebShell::mPluginHost = nsnull;
nsIPluginManager *nsWebShell::mPluginManager = nsnull;
nsresult nsWebShell::CreatePluginHost(void)
{
nsresult rv = NS_ERROR_FAILURE;
if (nsnull == mPluginManager)
{
rv = NSRepository::CreateInstance(kCPluginHostCID, nsnull,
kIPluginManagerIID,
(void**)&mPluginManager);
if (NS_OK == rv)
{
if (NS_OK == mPluginManager->QueryInterface(kIPluginHostIID,
(void **)&mPluginHost))
{
mPluginHost->Init();
mPluginHost->LoadPlugins();
}
}
}
return rv;
}
//----------------------------------------------------------------------
// Note: operator new zeros our memory
nsWebShell::nsWebShell()
{
NS_INIT_REFCNT();
mHistoryIndex = -1;
mScrollPref = nsScrollPreference_kAuto;
mScriptGlobal = nsnull;
mScriptContext = nsnull;
}
nsWebShell::~nsWebShell()
{
// Stop any pending document loads and destroy the loader...
mDocLoader->Stop();
NS_IF_RELEASE(mDocLoader);
NS_IF_RELEASE(mInnerWindow);
NS_IF_RELEASE(mContentViewer);
NS_IF_RELEASE(mContainer);
NS_IF_RELEASE(mObserver);
NS_IF_RELEASE(mScriptGlobal);
NS_IF_RELEASE(mScriptContext);
// Release references on our children
ReleaseChildren();
// Free up history memory
PRInt32 i, n = mHistory.Count();
for (i = 0; i < n; i++) {
nsString* s = (nsString*) mHistory.ElementAt(i);
delete s;
}
}
void
nsWebShell::ReleaseChildren()
{
PRInt32 i, n = mChildren.Count();
for (i = 0; i < n; i++) {
nsIWebShell* shell = (nsIWebShell*) mChildren.ElementAt(i);
shell->SetParent(nsnull);
NS_RELEASE(shell);
}
mChildren.Clear();
}
NS_IMPL_ADDREF(nsWebShell)
NS_IMPL_RELEASE(nsWebShell)
//XXX missing nsIWebShellContainer!!!
nsresult
nsWebShell::QueryInterface(REFNSIID aIID, void** aInstancePtr)
{
if (NULL == aInstancePtr) {
return NS_ERROR_NULL_POINTER;
}
if (aIID.Equals(kIWebShellIID)) {
*aInstancePtr = (void*)(nsIWebShell*)this;
AddRef();
return NS_OK;
}
if (aIID.Equals(kIContentViewerContainerIID)) {
*aInstancePtr = (void*)(nsIContentViewerContainer*)this;
AddRef();
return NS_OK;
}
if (aIID.Equals(kIScriptContextOwnerIID)) {
*aInstancePtr = (void*)(nsIScriptContextOwner*)this;
AddRef();
return NS_OK;
}
if (aIID.Equals(kISupportsIID)) {
*aInstancePtr = (void*)(nsISupports*)(nsIWebShell*)this;
AddRef();
return NS_OK;
}
if (nsnull != mInnerWindow) {
return mInnerWindow->QueryInterface(aIID, aInstancePtr);
}
return NS_NOINTERFACE;
}
NS_IMETHODIMP
nsWebShell::QueryCapability(const nsIID &aIID, void** aInstancePtr)
{
if (nsnull == aInstancePtr) {
return NS_ERROR_NULL_POINTER;
}
if (aIID.Equals(kILinkHandlerIID)) {
*aInstancePtr = (void*) ((nsILinkHandler*)this);
AddRef();
return NS_OK;
}
if (aIID.Equals(kIScriptContextOwnerIID)) {
*aInstancePtr = (void*) ((nsIScriptContextOwner*)this);
AddRef();
return NS_OK;
}
//XXX this seems a little wrong. MMP
if (nsnull != mPluginManager)
return mPluginManager->QueryInterface(aIID, aInstancePtr);
return NS_NOINTERFACE;
}
NS_IMETHODIMP
nsWebShell::Embed(nsIContentViewer* aContentViewer,
const char* aCommand,
nsISupports* aExtraInfo)
{
nsresult rv;
nsRect bounds;
WEB_TRACE(WEB_TRACE_CALLS,
("nsWebShell::Embed: this=%p aDocViewer=%p aCommand=%s aExtraInfo=%p",
this, aContentViewer, aCommand ? aCommand : "", aExtraInfo));
NS_IF_RELEASE(mContentViewer);
mContentViewer = aContentViewer;
NS_ADDREF(aContentViewer);
mWindow->GetBounds(bounds);
bounds.x = bounds.y = 0;
rv = mContentViewer->Init(mWindow->GetNativeData(NS_NATIVE_WIDGET),
mDeviceContext,
bounds,
mScrollPref);
if (NS_OK == rv) {
mContentViewer->Show();
}
// Now that we have switched documents, forget all of our children
ReleaseChildren();
return rv;
}
NS_IMETHODIMP
nsWebShell::Init(nsNativeWidget aNativeParent,
const nsRect& aBounds,
nsScrollPreference aScrolling)
{
//XXX make sure plugins have started up. this really needs to
//be associated with the nsIContentViewerContainer interfaces,
//not the nsIWebShell interfaces. this is a hack. MMP
CreatePluginHost();
mScrollPref = aScrolling;
WEB_TRACE(WEB_TRACE_CALLS,
("nsWebShell::Init: this=%p", this));
nsresult rv = NS_OK;
// Initial error checking...
NS_PRECONDITION(nsnull != aNativeParent, "null Parent Window");
if (nsnull == aNativeParent) {
rv = NS_ERROR_NULL_POINTER;
goto done;
}
// Create a document loader...
if (nsnull != mParent) {
nsIDocumentLoader* parentLoader;
// Create a child document loader...
mParent->GetDocumentLoader(parentLoader);
if (NS_OK == rv) {
rv = parentLoader->CreateDocumentLoader(&mDocLoader);
NS_RELEASE(parentLoader);
}
} else {
rv = NSRepository::CreateInstance(kDocumentLoaderCID,
nsnull,
kIDocumentLoaderIID,
(void**)&mDocLoader);
}
if (NS_OK != rv) {
goto done;
}
// Create device context
rv = NSRepository::CreateInstance(kDeviceContextCID, nsnull,
kIDeviceContextIID,
(void **)&mDeviceContext);
if (NS_OK != rv) {
goto done;
}
mDeviceContext->Init(aNativeParent);
mDeviceContext->SetDevUnitsToAppUnits(mDeviceContext->GetDevUnitsToTwips());
mDeviceContext->SetAppUnitsToDevUnits(mDeviceContext->GetTwipsToDevUnits());
mDeviceContext->SetGamma(1.7f);
// Create a Native window for the shell container...
rv = NSRepository::CreateInstance(kChildCID,
(nsISupports*)((nsIWebShell*)this),
kISupportsIID,
(void**)&mInnerWindow);
if (NS_OK != rv) {
goto done;
}
mInnerWindow->QueryInterface(kIWidgetIID, (void**) &mWindow);
if (NS_OK != rv) {
NS_RELEASE(mInnerWindow);
}
else {
mWindow->Create(aNativeParent, aBounds, nsWebShell::HandleEvent,
mDeviceContext, nsnull);
// Get rid of extra reference count
mWindow->Release();
}
done:
return rv;
}
NS_IMETHODIMP
nsWebShell::Destroy()
{
nsresult rv = NS_OK;
// Stop any URLs that are currently being loaded...
mDocLoader->Stop();
SetContainer(nsnull);
SetObserver(nsnull);
NS_IF_RELEASE(mContentViewer);
return rv;
}
NS_IMETHODIMP
nsWebShell::GetBounds(nsRect& aResult)
{
NS_PRECONDITION(nsnull != mWindow, "null window");
aResult.SetRect(0, 0, 0, 0);
if (nsnull != mWindow) {
mWindow->GetBounds(aResult);
}
return NS_OK;
}
NS_IMETHODIMP
nsWebShell::SetBounds(const nsRect& aBounds)
{
NS_PRECONDITION(nsnull != mWindow, "null window");
if (nsnull != mWindow) {
// Don't have the widget repaint. Layout will generate repaint requests
// during reflow
mWindow->Resize(aBounds.x, aBounds.y, aBounds.width, aBounds.height,
PR_FALSE);
}
if (nsnull != mContentViewer) {
nsRect rr(0, 0, aBounds.width, aBounds.height);
mContentViewer->SetBounds(rr);
}
return NS_OK;
}
NS_IMETHODIMP
nsWebShell::MoveTo(PRInt32 aX, PRInt32 aY)
{
NS_PRECONDITION(nsnull != mWindow, "null window");
if (nsnull != mWindow) {
mWindow->Move(aX, aY);
}
return NS_OK;
}
NS_IMETHODIMP
nsWebShell::Show()
{
NS_PRECONDITION(nsnull != mWindow, "null window");
if (nsnull != mWindow) {
mWindow->Show(PR_TRUE);
}
if (nsnull != mContentViewer) {
mContentViewer->Show();
}
return NS_OK;
}
NS_IMETHODIMP
nsWebShell::Hide()
{
NS_PRECONDITION(nsnull != mWindow, "null window");
if (nsnull != mWindow) {
mWindow->Show(PR_FALSE);
}
if (nsnull != mContentViewer) {
mContentViewer->Hide();
}
return NS_OK;
}
NS_IMETHODIMP
nsWebShell::GetContentViewer(nsIContentViewer*& aResult)
{
aResult = mContentViewer;
NS_IF_ADDREF(mContentViewer);
return NS_OK;
}
NS_IMETHODIMP
nsWebShell::SetContainer(nsIWebShellContainer* aContainer)
{
NS_IF_RELEASE(mContainer);
mContainer = aContainer;
NS_IF_ADDREF(aContainer);
return NS_OK;
}
NS_IMETHODIMP
nsWebShell::GetContainer(nsIWebShellContainer*& aResult)
{
aResult = mContainer;
NS_IF_ADDREF(mContainer);
return NS_OK;
}
nsEventStatus PR_CALLBACK
nsWebShell::HandleEvent(nsGUIEvent *aEvent)
{
return nsEventStatus_eIgnore;
}
NS_IMETHODIMP
nsWebShell::SetObserver(nsIStreamObserver* anObserver)
{
NS_IF_RELEASE(mObserver);
mObserver = anObserver;
NS_IF_ADDREF(mObserver);
return NS_OK;
}
NS_IMETHODIMP
nsWebShell::GetObserver(nsIStreamObserver*& aResult)
{
aResult = mObserver;
NS_IF_ADDREF(mObserver);
return NS_OK;
}
NS_IMETHODIMP
nsWebShell::GetDocumentLoader(nsIDocumentLoader*& aResult)
{
aResult = mDocLoader;
NS_IF_ADDREF(mDocLoader);
return (nsnull != mDocLoader) ? NS_OK : NS_ERROR_FAILURE;
}
nsresult
nsWebShell::GetRootWebShell(nsIWebShell*& aResult)
{
nsIWebShell* top = this;
for (;;) {
nsIWebShell* parent;
top->GetParent(parent);
if (nsnull == parent) {
break;
}
top = parent;
}
aResult = top;
NS_ADDREF(top);
return NS_OK;
}
NS_IMETHODIMP
nsWebShell::SetParent(nsIWebShell* aParent)
{
NS_IF_RELEASE(mParent);
mParent = aParent;
NS_IF_ADDREF(aParent);
return NS_OK;
}
NS_IMETHODIMP
nsWebShell::GetParent(nsIWebShell*& aParent)
{
aParent = mParent;
NS_IF_ADDREF(mParent);
return NS_OK;
}
NS_IMETHODIMP
nsWebShell::GetChildCount(PRInt32& aResult)
{
aResult = mChildren.Count();
return NS_OK;
}
NS_IMETHODIMP
nsWebShell::AddChild(nsIWebShell* aChild)
{
NS_PRECONDITION(nsnull != aChild, "null ptr");
if (nsnull == aChild) {
return NS_ERROR_NULL_POINTER;
}
mChildren.AppendElement(aChild);
aChild->SetParent(this);
NS_ADDREF(aChild);
return NS_OK;
}
NS_IMETHODIMP
nsWebShell::ChildAt(PRInt32 aIndex, nsIWebShell*& aResult)
{
if (PRUint32(aIndex) >= PRUint32(mChildren.Count())) {
aResult = nsnull;
}
else {
aResult = (nsIWebShell*) mChildren.ElementAt(aIndex);
NS_IF_ADDREF(aResult);
}
return NS_OK;
}
NS_IMETHODIMP
nsWebShell::GetName(nsString& aName)
{
aName = mName;
return NS_OK;
}
NS_IMETHODIMP
nsWebShell::SetName(const nsString& aName)
{
mName = aName;
return NS_OK;
}
NS_IMETHODIMP
nsWebShell::FindChildWithName(const nsString& aName,
nsIWebShell*& aResult)
{
aResult = nsnull;
nsAutoString childName;
PRInt32 i, n = mChildren.Count();
for (i = 0; i < n; i++) {
nsIWebShell* child = (nsIWebShell*) mChildren.ElementAt(i);
if (nsnull != child) {
child->GetName(childName);
if (childName.Equals(aName)) {
aResult = child;
NS_ADDREF(child);
break;
}
// See if child contains the shell with the given name
nsresult rv = child->FindChildWithName(aName, aResult);
if (NS_OK != rv) {
return rv;
}
if (nsnull != aResult) {
break;
}
}
}
return NS_OK;
}
//----------------------------------------
// History methods
NS_IMETHODIMP
nsWebShell::Back(void)
{
return GoTo(mHistoryIndex - 1);
}
NS_IMETHODIMP
nsWebShell::Forward(void)
{
return GoTo(mHistoryIndex + 1);
}
NS_IMETHODIMP
nsWebShell::LoadURL(const nsString& aURLSpec,
nsIPostData* aPostData)
{
nsresult rv;
// Give web-shell-container right of refusal
nsAutoString urlSpec(aURLSpec);
if (nsnull != mContainer) {
rv = mContainer->WillLoadURL(this, urlSpec);
if (NS_OK != rv) {
return rv;
}
}
// Discard part of history that is no longer reachable
PRInt32 i, n = mHistory.Count();
i = mHistoryIndex + 1;
while (--n >= i) {
nsString* u = (nsString*) mHistory.ElementAt(n);
delete u;
mHistory.RemoveElementAt(n);
}
// Tack on new url
nsString* url = new nsString(urlSpec);
mHistory.AppendElement(url);
mHistoryIndex++;
ShowHistory();
// Tell web-shell-container we are loading a new url
if (nsnull != mContainer) {
rv = mContainer->BeginLoadURL(this, urlSpec);
if (NS_OK != rv) {
return rv;
}
}
// Stop any documents that are currently being loaded...
mDocLoader->Stop();
rv = mDocLoader->LoadURL(urlSpec, // URL string
nsnull, // Command
this, // Container
aPostData, // Post Data
nsnull, // Extra Info...
mObserver); // Observer
return rv;
}
NS_IMETHODIMP
nsWebShell::GoTo(PRInt32 aHistoryIndex)
{
nsresult rv = NS_ERROR_ILLEGAL_VALUE;
if ((aHistoryIndex >= 0) &&
(aHistoryIndex <= mHistory.Count() - 1)) {
nsString* s = (nsString*) mHistory.ElementAt(aHistoryIndex);
// Give web-shell-container right of refusal
nsAutoString urlSpec(*s);
if (nsnull != mContainer) {
rv = mContainer->WillLoadURL(this, urlSpec);
if (NS_OK != rv) {
return rv;
}
}
printf("Goto %d\n", aHistoryIndex);
mHistoryIndex = aHistoryIndex;
ShowHistory();
// Tell web-shell-container we are loading a new url
if (nsnull != mContainer) {
rv = mContainer->BeginLoadURL(this, urlSpec);
if (NS_OK != rv) {
return rv;
}
}
// Stop any documents that are currently being loaded...
mDocLoader->Stop();
rv = mDocLoader->LoadURL(urlSpec, // URL string
nsnull, // Command
this, // Container
nsnull, // Post Data
nsnull, // Extra Info...
mObserver); // Observer
}
return rv;
}
NS_IMETHODIMP
nsWebShell::GetHistoryIndex(PRInt32& aResult)
{
aResult = mHistoryIndex;
return NS_OK;
}
NS_IMETHODIMP
nsWebShell::GetURL(PRInt32 aHistoryIndex, nsString& aURLResult)
{
nsresult rv = NS_ERROR_ILLEGAL_VALUE;
if ((aHistoryIndex >= 0) &&
(aHistoryIndex <= mHistory.Count() - 1)) {
aURLResult.Truncate();
nsString* s = (nsString*) mHistory.ElementAt(aHistoryIndex);
if (nsnull != s) {
aURLResult = *s;
}
rv = NS_OK;
}
return rv;
}
void
nsWebShell::ShowHistory()
{
#ifdef NS_DEBUG
if (WEB_LOG_TEST(gLogModule, WEB_TRACE_HISTORY)) {
PRInt32 i, n = mHistory.Count();
for (i = 0; i < n; i++) {
if (i == mHistoryIndex) {
printf("**");
}
else {
printf(" ");
}
nsString* u = (nsString*) mHistory.ElementAt(i);
fputs(*u, stdout);
printf("\n");
}
}
#endif
}
//----------------------------------------
// Chrome API's
NS_IMETHODIMP
nsWebShell::SetTitle(const nsString& aTitle)
{
// Record local title
mTitle = aTitle;
// Title's set on the top level web-shell are passed ont to the container
if (nsnull == mParent) {
if (nsnull != mContainer) {
mContainer->SetTitle(aTitle);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsWebShell::GetTitle(nsString& aResult)
{
aResult = mTitle;
return NS_OK;
}
//----------------------------------------------------------------------
// WebShell container implementation
NS_IMETHODIMP
nsWebShell::WillLoadURL(nsIWebShell* aShell, const nsString& aURL)
{
if (nsnull != mContainer) {
return mContainer->WillLoadURL(aShell, aURL);
}
return NS_OK;
}
NS_IMETHODIMP
nsWebShell::BeginLoadURL(nsIWebShell* aShell, const nsString& aURL)
{
if (nsnull != mContainer) {
return mContainer->BeginLoadURL(aShell, aURL);
}
return NS_OK;
}
NS_IMETHODIMP
nsWebShell::EndLoadURL(nsIWebShell* aShell, const nsString& aURL)
{
if (nsnull != mContainer) {
return mContainer->EndLoadURL(aShell, aURL);
}
return NS_OK;
}
//----------------------------------------------------------------------
// WebShell link handling
struct OnLinkClickEvent : public PLEvent {
OnLinkClickEvent(nsWebShell* aHandler, const nsString& aURLSpec,
const nsString& aTargetSpec, nsIPostData* aPostData = 0);
~OnLinkClickEvent();
void HandleEvent() {
mHandler->HandleLinkClickEvent(*mURLSpec, *mTargetSpec, mPostData);
}
nsWebShell* mHandler;
nsString* mURLSpec;
nsString* mTargetSpec;
nsIPostData* mPostData;
};
static void PR_CALLBACK HandlePLEvent(OnLinkClickEvent* aEvent)
{
aEvent->HandleEvent();
}
static void PR_CALLBACK DestroyPLEvent(OnLinkClickEvent* aEvent)
{
delete aEvent;
}
OnLinkClickEvent::OnLinkClickEvent(nsWebShell* aHandler,
const nsString& aURLSpec,
const nsString& aTargetSpec,
nsIPostData* aPostData)
{
mHandler = aHandler;
NS_ADDREF(aHandler);
mURLSpec = new nsString(aURLSpec);
mTargetSpec = new nsString(aTargetSpec);
mPostData = aPostData;
NS_IF_ADDREF(mPostData);
#ifdef XP_PC
PL_InitEvent(this, nsnull,
(PLHandleEventProc) ::HandlePLEvent,
(PLDestroyEventProc) ::DestroyPLEvent);
PLEventQueue* eventQueue = PL_GetMainEventQueue();
PL_PostEvent(eventQueue, this);
#endif
}
OnLinkClickEvent::~OnLinkClickEvent()
{
NS_IF_RELEASE(mHandler);
NS_IF_RELEASE(mPostData);
if (nsnull != mURLSpec) delete mURLSpec;
if (nsnull != mTargetSpec) delete mTargetSpec;
}
//----------------------------------------
NS_IMETHODIMP
nsWebShell::OnLinkClick(nsIFrame* aFrame,
const nsString& aURLSpec,
const nsString& aTargetSpec,
nsIPostData* aPostData)
{
OnLinkClickEvent* ev;
nsresult rv = NS_OK;
ev = new OnLinkClickEvent(this, aURLSpec, aTargetSpec, aPostData);
if (nsnull == ev) {
rv = NS_ERROR_OUT_OF_MEMORY;
}
return rv;
}
// Find the web shell in the entire tree that we can reach that the
// link click should go to.
// XXX This doesn't yet know how to target other windows with their
// own tree
nsIWebShell*
nsWebShell::GetTarget(const nsString& aName)
{
nsIWebShell* target = nsnull;
if (aName.EqualsIgnoreCase("_blank")) {
// XXX Need api in nsIWebShellContainer
NS_ASSERTION(0, "not implemented yet");
target = this;
NS_ADDREF(target);
}
else if (aName.EqualsIgnoreCase("_self")) {
target = this;
NS_ADDREF(target);
}
else if (aName.EqualsIgnoreCase("_parent")) {
if (nsnull == mParent) {
target = this;
}
else {
target = mParent;
}
NS_ADDREF(target);
}
else if (aName.EqualsIgnoreCase("_top")) {
GetRootWebShell(target);
}
else {
// Look from the top of the tree downward
nsIWebShell* top;
GetRootWebShell(top);
top->FindChildWithName(aName, target);
if (nsnull == target) {
target = this;
NS_ADDREF(target);
}
NS_RELEASE(top);
}
return target;
}
void
nsWebShell::HandleLinkClickEvent(const nsString& aURLSpec,
const nsString& aTargetSpec,
nsIPostData* aPostData)
{
nsIWebShell* shell = GetTarget(aTargetSpec);
if (nsnull != shell) {
shell->LoadURL(aURLSpec, aPostData);
}
}
NS_IMETHODIMP
nsWebShell::OnOverLink(nsIFrame* aFrame,
const nsString& aURLSpec,
const nsString& aTargetSpec)
{
if (!aURLSpec.Equals(mOverURL) || !aTargetSpec.Equals(mOverTarget)) {
fputs("Was '", stdout); fputs(mOverURL, stdout); fputs("' '", stdout); fputs(mOverTarget, stdout); fputs("'\n", stdout);
fputs("Over link '", stdout);
fputs(aURLSpec, stdout);
fputs("' '", stdout);
fputs(aTargetSpec, stdout);
fputs("'\n", stdout);
mOverURL = aURLSpec;
mOverTarget = aTargetSpec;
}
return NS_OK;
}
NS_IMETHODIMP
nsWebShell:: GetLinkState(const nsString& aURLSpec, nsLinkState& aState)
{
aState = eLinkState_Unvisited;
#ifdef NS_DEBUG
if (aURLSpec.Equals("http://visited/")) {
aState = eLinkState_Visited;
}
else if (aURLSpec.Equals("http://out-of-date/")) {
aState = eLinkState_OutOfDate;
}
else if (aURLSpec.Equals("http://active/")) {
aState = eLinkState_Active;
}
else if (aURLSpec.Equals("http://hover/")) {
aState = eLinkState_Hover;
}
#endif
return NS_OK;
}
//----------------------------------------------------------------------
nsresult
nsWebShell::GetScriptContext(nsIScriptContext** aContext)
{
NS_PRECONDITION(nsnull != aContext, "null arg");
nsresult res = NS_OK;
if (nsnull == mScriptGlobal) {
res = NS_NewScriptGlobalObject(&mScriptGlobal);
if (NS_OK != res) {
return res;
}
res = NS_CreateContext(mScriptGlobal, &mScriptContext);
if (NS_OK != res) {
return res;
}
}
*aContext = mScriptContext;
NS_ADDREF(mScriptContext);
return res;
}
nsresult
nsWebShell::GetScriptGlobalObject(nsIScriptGlobalObject** aGlobal)
{
NS_PRECONDITION(nsnull != aGlobal, "null arg");
nsresult res = NS_OK;
if (nsnull == mScriptGlobal) {
res = NS_NewScriptGlobalObject(&mScriptGlobal);
if (NS_OK != res) {
return res;
}
}
*aGlobal = mScriptGlobal;
NS_ADDREF(mScriptGlobal);
return res;
}
nsresult
nsWebShell::ReleaseScriptContext(nsIScriptContext *aContext)
{
// XXX Is this right? Why are we passing in a context?
NS_IF_RELEASE(aContext);
return NS_OK;
}
//----------------------------------------------------------------------
// Factory code for creating nsWebShell's
class nsWebShellFactory : public nsIFactory
{
public:
nsWebShellFactory();
virtual ~nsWebShellFactory();
// nsISupports methods
NS_IMETHOD QueryInterface(const nsIID &aIID, void **aResult);
NS_IMETHOD_(nsrefcnt) AddRef(void);
NS_IMETHOD_(nsrefcnt) Release(void);
// nsIFactory methods
NS_IMETHOD CreateInstance(nsISupports *aOuter,
const nsIID &aIID,
void **aResult);
NS_IMETHOD LockFactory(PRBool aLock);
private:
nsrefcnt mRefCnt;
};
nsWebShellFactory::nsWebShellFactory()
{
mRefCnt = 0;
}
nsWebShellFactory::~nsWebShellFactory()
{
NS_ASSERTION(mRefCnt == 0, "non-zero refcnt at destruction");
}
nsresult
nsWebShellFactory::QueryInterface(const nsIID &aIID, void **aResult)
{
if (aResult == NULL) {
return NS_ERROR_NULL_POINTER;
}
// Always NULL result, in case of failure
*aResult = NULL;
if (aIID.Equals(kISupportsIID)) {
*aResult = (void *)(nsISupports*)this;
} else if (aIID.Equals(kIFactoryIID)) {
*aResult = (void *)(nsIFactory*)this;
}
if (*aResult == NULL) {
return NS_NOINTERFACE;
}
AddRef(); // Increase reference count for caller
return NS_OK;
}
nsrefcnt
nsWebShellFactory::AddRef()
{
return ++mRefCnt;
}
nsrefcnt
nsWebShellFactory::Release()
{
if (--mRefCnt == 0) {
delete this;
return 0; // Don't access mRefCnt after deleting!
}
return mRefCnt;
}
nsresult
nsWebShellFactory::CreateInstance(nsISupports *aOuter,
const nsIID &aIID,
void **aResult)
{
nsresult rv;
nsWebShell *inst;
if (aResult == NULL) {
return NS_ERROR_NULL_POINTER;
}
*aResult = NULL;
if (nsnull != aOuter) {
rv = NS_ERROR_NO_AGGREGATION;
goto done;
}
inst = new nsWebShell();
if (inst == NULL) {
rv = NS_ERROR_OUT_OF_MEMORY;
goto done;
}
NS_ADDREF(inst);
rv = inst->QueryInterface(aIID, aResult);
NS_RELEASE(inst);
done:
return rv;
}
nsresult
nsWebShellFactory::LockFactory(PRBool aLock)
{
// Not implemented in simplest case.
return NS_OK;
}
extern "C" NS_WEB nsresult
NS_NewWebShellFactory(nsIFactory** aFactory)
{
nsresult rv = NS_OK;
nsIFactory* inst = new nsWebShellFactory();
if (nsnull == inst) {
rv = NS_ERROR_OUT_OF_MEMORY;
}
else {
NS_ADDREF(inst);
}
*aFactory = inst;
return rv;
}