diff --git a/mozilla/docshell/base/nsWebShell.cpp b/mozilla/docshell/base/nsWebShell.cpp new file mode 100644 index 00000000000..864cf8aaf44 --- /dev/null +++ b/mozilla/docshell/base/nsWebShell.cpp @@ -0,0 +1,1037 @@ +/* -*- 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 "nsRepository.h" +#include "nsCRT.h" +#include "nsVoidArray.h" +#include "nsString.h" +#include "nsWidgetsCID.h" +#include "nsGfxCIID.h" +#include "plevent.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: + 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 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 SetParent(nsIWebShell* aParent); + NS_IMETHOD GetParent(nsIWebShell*& aParent); + NS_IMETHOD GetChildCount(PRInt32& aResult); + NS_IMETHOD AddChild(nsIWebShell* aChild, PRBool aRelationship = PR_TRUE); + 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(nsIStreamObserver* aObserver); + NS_IMETHOD Forward(nsIStreamObserver* aObserver); + NS_IMETHOD LoadURL(const nsString& aURLSpec, + nsIStreamObserver* aObserver, + nsIPostData* aPostData=nsnull); + NS_IMETHOD GoTo(PRInt32 aHistoryIndex, nsIStreamObserver* aObserver); + NS_IMETHOD GetHistoryIndex(PRInt32& 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 Init(); + 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); + + // nsWebShell + void HandleLinkClickEvent(const nsString& aURLSpec, + const nsString& aTargetSpec, + nsIPostData* aPostDat = 0); + + void ShowHistory(); + + nsIWebShell* GetRootWebShell(); + + nsIWebShell* GetTarget(const nsString& aName); + + static nsEventStatus PR_CALLBACK HandleEvent(nsGUIEvent *aEvent); + +protected: + nsIWebShellContainer* mContainer; + nsIContentViewer* mContentViewer; + nsIDeviceContext* mDeviceContext; + nsIWidget* mWindow; + nsIDocumentLoader* mDocLoader; + + nsIWebShell* mParent; + nsVoidArray mChildren; + nsString mName; + + nsVoidArray mHistory; + PRInt32 mHistoryIndex; + + nsString mOverURL; + nsString mOverTarget; + + void ReleaseChildren(); +}; + +//---------------------------------------------------------------------- + +// 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(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); + +// XXX not sure +static NS_DEFINE_IID(kILinkHandlerIID, NS_ILINKHANDLER_IID); + +//---------------------------------------------------------------------- + +NS_WEB nsresult +NS_NewWebShell(nsIWebShell*& aResult) +{ + nsIWebShell* it = new nsWebShell(); + if (nsnull == it) { + return NS_ERROR_OUT_OF_MEMORY; + } + it->AddRef(); + aResult = it; + return NS_OK; +} + +// Note: operator new zeros our memory +nsWebShell::nsWebShell() +{ + NS_INIT_REFCNT(); + mHistoryIndex = -1; +} + +nsWebShell::~nsWebShell() +{ + NS_IF_RELEASE(mContentViewer); + NS_IF_RELEASE(mContainer); + + // 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) + +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(kISupportsIID)) { + *aInstancePtr = (void*)(nsISupports*)(nsIWebShell*)this; + AddRef(); + return NS_OK; + } + 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; + } + 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); + if (NS_OK == rv) { + mContentViewer->Show(); + } + + // Now that we have switch documents, forget all of our children + ReleaseChildren(); + + return rv; +} + +NS_IMETHODIMP +nsWebShell::Init(nsNativeWidget aNativeParent, + const nsRect& aBounds, + nsScrollPreference aScrolling) +{ + WEB_TRACE(WEB_TRACE_CALLS, + ("nsWebShell::Init: this=%p", this)); + + nsresult rv = NSRepository::CreateInstance(kDocumentLoaderCID, nsnull, + kIDocumentLoaderIID, + (void**)&mDocLoader); + + NS_PRECONDITION(nsnull != aNativeParent, "null Parent Window"); + if (nsnull == aNativeParent) { + rv = NS_ERROR_NULL_POINTER; + 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, nsnull, kIWidgetIID, + (void**)&mWindow); + if (NS_OK != rv) { + goto done; + } + mWindow->Create(aNativeParent, aBounds, nsWebShell::HandleEvent, + mDeviceContext, nsnull); + +done: + 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::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, PRBool aRelationship) +{ + 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(nsIStreamObserver* aObserver) +{ + return GoTo(mHistoryIndex - 1, aObserver); +} + +NS_IMETHODIMP +nsWebShell::Forward(nsIStreamObserver* aObserver) +{ + return GoTo(mHistoryIndex + 1, aObserver); +} + +NS_IMETHODIMP +nsWebShell::LoadURL(const nsString& aURLSpec, + nsIStreamObserver* aObserver, + 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; + } + } + rv = mDocLoader->LoadURL(urlSpec, // URL string + nsnull, // Command + this, // Container + aPostData, // Post Data + nsnull, // Extra Info... + aObserver); // Observer + return rv; +} + +NS_IMETHODIMP +nsWebShell::GoTo(PRInt32 aHistoryIndex, nsIStreamObserver* aObserver) +{ + 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; + } + } + rv = mDocLoader->LoadURL(urlSpec, // URL string + nsnull, // Command + this, // Container + nsnull, // Post Data + nsnull, // Extra Info... + aObserver); // Observer + } + return rv; +} + +NS_IMETHODIMP +nsWebShell::GetHistoryIndex(PRInt32& aResult) +{ + aResult = mHistoryIndex; + return NS_OK; +} + +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 +} + +//---------------------------------------------------------------------- + +// 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::Init() +{ + return NS_OK; +} + +NS_IMETHODIMP +nsWebShell::OnLinkClick(nsIFrame* aFrame, + const nsString& aURLSpec, + const nsString& aTargetSpec, + nsIPostData* aPostData) +{ + new OnLinkClickEvent(this, aURLSpec, aTargetSpec, aPostData); + return NS_OK; +} + +// Find the web shell at the top of our tree +nsIWebShell* +nsWebShell::GetRootWebShell() +{ + nsIWebShell* top = this; + for (;;) { + nsIWebShell* parent; + top->GetParent(parent); + if (nsnull == parent) { + break; + } + top = parent; + } + return top; +} + +// 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 Where should this logic live? _blank requires the app to + // get involved + NS_ASSERTION(0, "not implemented yet"); + target = this; + } + else if (aName.EqualsIgnoreCase("_self")) { + target = this; + } + else if (aName.EqualsIgnoreCase("_parent")) { + if (nsnull == mParent) { + target = this; + } + else { + target = mParent; + } + } + else if (aName.EqualsIgnoreCase("_top")) { + target = GetRootWebShell(); + } + else { + // Look from the top of the tree downward + nsIWebShell* top = GetRootWebShell(); + top->FindChildWithName(aName, target); + if (nsnull == target) { + target = this; + } + } + if (target == this) { + NS_ADDREF(this); + } + return target; +} + +void +nsWebShell::HandleLinkClickEvent(const nsString& aURLSpec, + const nsString& aTargetSpec, + nsIPostData* aPostData) +{ + nsIWebShell* shell = GetTarget(aTargetSpec); + if (nsnull != shell) { + shell->LoadURL(aURLSpec, nsnull, 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; +} + +//---------------------------------------------------------------------- + +// Factory code for creating nsWebShell's + +class nsWebShellFactory : public nsIFactory +{ +public: + // 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); + + nsWebShellFactory(const nsCID &aClass); + ~nsWebShellFactory(); + +private: + nsrefcnt mRefCnt; + nsCID mClassID; +}; + +nsWebShellFactory::nsWebShellFactory(const nsCID &aClass) +{ + mRefCnt = 0; + mClassID = aClass; +} + +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) +{ + if (aResult == NULL) { + return NS_ERROR_NULL_POINTER; + } + + *aResult = NULL; + + nsWebShell *inst = nsnull; + + if (mClassID.Equals(kWebShellCID)) { + inst = new nsWebShell(); + } + + if (inst == NULL) { + return NS_ERROR_OUT_OF_MEMORY; + } + + nsresult res = inst->QueryInterface(aIID, aResult); + + if (res != NS_OK) { + // We didn't get the right interface, so clean up + delete inst; + } + + return res; +} + +nsresult +nsWebShellFactory::LockFactory(PRBool aLock) +{ + // Not implemented in simplest case. + return NS_OK; +} + +//---------------------------------------------------------------------- + +// return the proper factory to the caller +extern "C" NS_WEB nsresult +NSGetFactory(const nsCID &aClass, nsIFactory **aFactory) +{ + nsresult rv = NS_OK; + + if (nsnull == aFactory) { + return NS_ERROR_NULL_POINTER; + } + + if (aClass.Equals(kWebShellCID)) { + nsIFactory* inst = new nsWebShellFactory(aClass); + NS_IF_ADDREF(inst); + *aFactory = inst; + if (nsnull == inst) { + rv = NS_ERROR_OUT_OF_MEMORY; + } + } + else if (aClass.Equals(kDocumentLoaderCID)) { + rv = NS_NewDocumentLoaderFactory(aFactory); + } + + return rv; +} diff --git a/mozilla/webshell/public/nsIContentViewer.h b/mozilla/webshell/public/nsIContentViewer.h new file mode 100644 index 00000000000..78f5daeb239 --- /dev/null +++ b/mozilla/webshell/public/nsIContentViewer.h @@ -0,0 +1,71 @@ +/* -*- 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. + */ +#ifndef nsIContentViewer_h___ +#define nsIContentViewer_h___ + +#include "nsweb.h" +#include "nsIWidget.h" +#include "nsIScrollableView.h" + +// Forward declarations... +class nsIDeviceContext; +class nsString; +struct nsRect; +class nsIContentViewerContainer; + +// IID for the nsIContentViewer interface +#define NS_ICONTENT_VIEWER_IID \ + { 0xa6cf9056, 0x15b3, 0x11d2,{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}} + +/** + * Content viewer interface. When a stream of data is to be presented + * visually to the user, a content viewer is found for the underlying + * mime-type associated with the data in the stream. This interface + * defines the capabilities of the viewer. + */ +class nsIContentViewer : public nsISupports +{ +public: + /** + * Initialize the content viewer. Make it a child of the + * the given native parent widget. + */ + NS_IMETHOD Init(nsNativeWidget aNativeParent, + nsIDeviceContext* aDeviceContext, + const nsRect& aBounds, + nsScrollPreference aScrolling = nsScrollPreference_kAuto)=0; + + NS_IMETHOD BindToDocument(nsISupports* aDoc, const char* aCommand) = 0; + + NS_IMETHOD SetContainer(nsIContentViewerContainer* aContainer) = 0; + + NS_IMETHOD GetContainer(nsIContentViewerContainer*& aContainerResult) = 0; + + virtual nsRect GetBounds() = 0; + + virtual void SetBounds(const nsRect& aBounds) = 0; + + virtual void Move(PRInt32 aX, PRInt32 aY) = 0; + + virtual void Show() = 0; + + virtual void Hide() = 0; +}; + +#endif /* nsIContentViewer_h___ */ diff --git a/mozilla/webshell/public/nsIContentViewerContainer.h b/mozilla/webshell/public/nsIContentViewerContainer.h new file mode 100644 index 00000000000..9953edc6c84 --- /dev/null +++ b/mozilla/webshell/public/nsIContentViewerContainer.h @@ -0,0 +1,46 @@ +/* -*- 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. + */ +#ifndef nsIContentViewerContainer_h___ +#define nsIContentViewerContainer_h___ + +#include "nsweb.h" +#include "prtypes.h" +#include "nsISupports.h" + +class nsIContentViewer; + +#define NS_ICONTENT_VIEWER_CONTAINER_IID \ + { 0xa6cf9055, 0x15b3, 0x11d2,{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}} + +/* + * The primary container interface for objects that can contain + * implementations of nsIContentViewer. + */ +class nsIContentViewerContainer : public nsISupports +{ +public: + + NS_IMETHOD QueryCapability(const nsIID &aIID, void** aResult) = 0; + + NS_IMETHOD Embed(nsIContentViewer* aDocViewer, + const char* aCommand, + nsISupports* aExtraInfo) = 0; +}; + +#endif /* nsIContentViewerContainer_h___ */ diff --git a/mozilla/webshell/public/nsIDocumentViewer.h b/mozilla/webshell/public/nsIDocumentViewer.h new file mode 100644 index 00000000000..b58facf0616 --- /dev/null +++ b/mozilla/webshell/public/nsIDocumentViewer.h @@ -0,0 +1,54 @@ +/* -*- 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. + */ +#ifndef nsIDocumentViewer_h___ +#define nsIDocumentViewer_h___ + +#include "nsIContentViewer.h" + +class nsIDocument; +class nsIPresContext; +class nsIPresShell; +class nsIStyleSheet; + +#define NS_IDOCUMENT_VIEWER_IID \ + { 0xa6cf9057, 0x15b3, 0x11d2,{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}} + +/** + * A document viewer is a kind of content viewer that uses NGLayout + * to manage the presentation of the content. + */ +class nsIDocumentViewer : public nsIContentViewer +{ +public: + NS_IMETHOD SetUAStyleSheet(nsIStyleSheet* aUAStyleSheet) = 0; + + NS_IMETHOD GetDocument(nsIDocument*& aResult) = 0; + + NS_IMETHOD GetPresShell(nsIPresShell*& aResult) = 0; + + NS_IMETHOD GetPresContext(nsIPresContext*& aResult) = 0; + + // XXX Add a method to create a new doc viewer on the same content + // model with a specified type-of/instance-of presentation-context +}; + +// XXX temporary +extern "C" NS_WEB nsresult NS_NewDocumentViewer(nsIDocumentViewer*& aViewer); + +#endif /* nsIDocumentViewer_h___ */ diff --git a/mozilla/webshell/public/nsIWebShell.h b/mozilla/webshell/public/nsIWebShell.h new file mode 100644 index 00000000000..6af51eb4c74 --- /dev/null +++ b/mozilla/webshell/public/nsIWebShell.h @@ -0,0 +1,119 @@ +/* -*- 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. + */ +#ifndef nsIWebShell_h___ +#define nsIWebShell_h___ + +#include "nsIWidget.h" +#include "nsIScrollableView.h" +#include "nsIContentViewerContainer.h" + +class nsIPostData; +class nsIStreamObserver; +class nsIWebShell; +class nsIWebShellContainer; + +// Interface ID for nsIWebShell +#define NS_IWEB_SHELL_IID \ + { 0xa6cf9058, 0x15b3, 0x11d2,{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}} + +// Interface ID for nsIWebShellContainer +#define NS_IWEB_SHELL_CONTAINER_IID \ + { 0xa6cf905a, 0x15b3, 0x11d2,{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}} + +// Class ID for an implementation of nsIWebShell +#define NS_WEB_SHELL_CID \ + { 0xa6cf9059, 0x15b3, 0x11d2,{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}} + +//---------------------------------------------------------------------- + +// Container for web shell's +class nsIWebShellContainer : public nsISupports { +public: + // History control + NS_IMETHOD WillLoadURL(nsIWebShell* aShell, const nsString& aURL) = 0; + NS_IMETHOD BeginLoadURL(nsIWebShell* aShell, const nsString& aURL) = 0; + + // XXX not yet implemented; should we? + NS_IMETHOD EndLoadURL(nsIWebShell* aShell, const nsString& aURL) = 0; + + // Chrome control +// NS_IMETHOD SetHistoryIndex(PRInt32 aIndex, PRInt32 aMaxIndex) = 0; + + // Link traversing control +}; + +//---------------------------------------------------------------------- + +/** + * The web shell is a container for implementations of nsIContentViewer. + * It is a content-viewer-container and also knows how to delegate certain + * behavior to an nsIWebShellContainer. + * + * Web shells can be arranged in a tree. + * + * Web shells are also nsIWebShellContainer's + */ +class nsIWebShell : public nsIContentViewerContainer { +public: + NS_IMETHOD Init(nsNativeWidget aNativeParent, + const nsRect& aBounds, + nsScrollPreference aScrolling = nsScrollPreference_kAuto)=0; + + NS_IMETHOD GetBounds(nsRect& aResult) = 0; + + NS_IMETHOD SetBounds(const nsRect& aBounds) = 0; + + NS_IMETHOD MoveTo(PRInt32 aX, PRInt32 aY) = 0; + + NS_IMETHOD Show() = 0; + + NS_IMETHOD Hide() = 0; + + NS_IMETHOD GetContentViewer(nsIContentViewer*& aResult) = 0; + // XXX SetContentViewer? + + NS_IMETHOD SetContainer(nsIWebShellContainer* aContainer) = 0; + NS_IMETHOD GetContainer(nsIWebShellContainer*& aResult) = 0; + + NS_IMETHOD SetParent(nsIWebShell* aParent) = 0; + NS_IMETHOD GetParent(nsIWebShell*& aParent) = 0; + NS_IMETHOD GetChildCount(PRInt32& aResult) = 0; + NS_IMETHOD AddChild(nsIWebShell* aChild, PRBool aRelationship = PR_TRUE)=0; + NS_IMETHOD ChildAt(PRInt32 aIndex, nsIWebShell*& aResult) = 0; + NS_IMETHOD GetName(nsString& aName) = 0; + NS_IMETHOD SetName(const nsString& aName) = 0; + NS_IMETHOD FindChildWithName(const nsString& aName, + nsIWebShell*& aResult) = 0; + + // History api's + NS_IMETHOD Back(nsIStreamObserver* aObserver) = 0; + NS_IMETHOD Forward(nsIStreamObserver* aObserver) = 0; + NS_IMETHOD LoadURL(const nsString& aURLSpec, + nsIStreamObserver* aObserver, + nsIPostData* aPostData=nsnull) = 0; + NS_IMETHOD GoTo(PRInt32 aHistoryIndex, nsIStreamObserver* aObserver) = 0; + NS_IMETHOD GetHistoryIndex(PRInt32& aResult) = 0; + + // XXX chrome api's + // SetToolBar + // SetMenuBar + // SetStatusBar +}; + +#endif /* nsIWebShell_h___ */ diff --git a/mozilla/webshell/src/nsDocumentViewer.cpp b/mozilla/webshell/src/nsDocumentViewer.cpp new file mode 100644 index 00000000000..dde4f938c5e --- /dev/null +++ b/mozilla/webshell/src/nsDocumentViewer.cpp @@ -0,0 +1,509 @@ +/* -*- 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.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 "nscore.h" +#include "nsCRT.h" +#include "nsString.h" +#include "nsISupports.h" +#include "nsIContentViewerContainer.h" +#include "nsIDocumentViewer.h" + +#include "nsIDocument.h" +#include "nsIPresContext.h" +#include "nsIPresShell.h" +#include "nsIStyleSet.h" +#include "nsIStyleSheet.h" + +#include "nsILinkHandler.h" + +#include "nsViewsCID.h" +#include "nsWidgetsCID.h" +#include "nsIDeviceContext.h" +#include "nsIViewManager.h" +#include "nsIView.h" + +#include "nsIURL.h" + +class DocumentViewerImpl : public nsIDocumentViewer +{ +public: + DocumentViewerImpl(); + + void* operator new(size_t sz) { + void* rv = new char[sz]; + nsCRT::zero(rv, sz); + return rv; + } + + // nsISupports interface... + NS_DECL_ISUPPORTS + + // nsIContentViewer interface... + NS_IMETHOD Init(nsNativeWidget aParent, + nsIDeviceContext* aDeviceContext, + const nsRect& aBounds, + nsScrollPreference aScrolling = nsScrollPreference_kAuto); + + NS_IMETHOD BindToDocument(nsISupports* aDoc, const char* aCommand); + NS_IMETHOD SetContainer(nsIContentViewerContainer* aContainer); + NS_IMETHOD GetContainer(nsIContentViewerContainer*& aContainerResult); + + virtual nsRect GetBounds(); + virtual void SetBounds(const nsRect& aBounds); + virtual void Move(PRInt32 aX, PRInt32 aY); + virtual void Show(); + virtual void Hide(); + + + // nsIDocumentViewer interface... + NS_IMETHOD Init(nsNativeWidget aParent, + const nsRect& aBounds, + nsIDocument* aDocument, + nsIPresContext* aPresContext, + nsScrollPreference aScrolling = nsScrollPreference_kAuto); + + NS_IMETHOD SetUAStyleSheet(nsIStyleSheet* aUAStyleSheet); + + NS_IMETHOD GetDocument(nsIDocument*& aResult); + + NS_IMETHOD GetPresShell(nsIPresShell*& aResult); + + NS_IMETHOD GetPresContext(nsIPresContext*& aResult); + +protected: + virtual ~DocumentViewerImpl(); + +private: + void ForceRefresh(void); + nsresult CreateStyleSet(nsIDocument* aDocument, nsIStyleSet** aStyleSet); + nsresult MakeWindow(nsNativeWidget aNativeParent, + const nsRect& aBounds, + nsScrollPreference aScrolling); + +protected: + nsIViewManager* mViewManager; + nsIView* mView; + nsIWidget* mWindow; + nsIContentViewerContainer* mContainer; + + nsIDocument* mDocument; + nsIPresContext* mPresContext; + nsIPresShell* mPresShell; + nsIStyleSheet* mUAStyleSheet; + +}; + +//Class IDs +static NS_DEFINE_IID(kViewManagerCID, NS_VIEW_MANAGER_CID); +static NS_DEFINE_IID(kScrollingViewCID, NS_SCROLLING_VIEW_CID); +static NS_DEFINE_IID(kWidgetCID, NS_CHILD_CID); + + +// Interface IDs +static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); +static NS_DEFINE_IID(kIDocumentIID, NS_IDOCUMENT_IID); +static NS_DEFINE_IID(kIViewManagerIID, NS_IVIEWMANAGER_IID); +static NS_DEFINE_IID(kIViewIID, NS_IVIEW_IID); +static NS_DEFINE_IID(kScrollViewIID, NS_ISCROLLABLEVIEW_IID); +static NS_DEFINE_IID(kIContentViewerIID, NS_ICONTENT_VIEWER_IID); +static NS_DEFINE_IID(kIDocumentViewerIID, NS_IDOCUMENT_VIEWER_IID); +static NS_DEFINE_IID(kILinkHandlerIID, NS_ILINKHANDLER_IID); + + +// Note: operator new zeros our memory +DocumentViewerImpl::DocumentViewerImpl() +{ + NS_INIT_REFCNT(); +} + +// ISupports implementation... +NS_IMPL_ADDREF(DocumentViewerImpl) +NS_IMPL_RELEASE(DocumentViewerImpl) + +nsresult DocumentViewerImpl::QueryInterface(REFNSIID aIID, void** aInstancePtr) +{ + if (NULL == aInstancePtr) { + return NS_ERROR_NULL_POINTER; + } + + if (aIID.Equals(kIContentViewerIID)) { + *aInstancePtr = (void*)(nsIContentViewer*)this; + AddRef(); + return NS_OK; + } + if (aIID.Equals(kIDocumentViewerIID)) { + *aInstancePtr = (void*)(nsIDocumentViewer*)this; + AddRef(); + return NS_OK; + } + if (aIID.Equals(kISupportsIID)) { + *aInstancePtr = (void*)(nsISupports*)(nsIContentViewer*)this; + AddRef(); + return NS_OK; + } + return NS_NOINTERFACE; +} + + +DocumentViewerImpl::~DocumentViewerImpl() +{ + // Release windows and views + if (nsnull != mViewManager) { + mViewManager->SetRootView(nsnull); + mViewManager->SetRootWindow(nsnull); + NS_RELEASE(mViewManager); + } + NS_IF_RELEASE(mWindow); + NS_IF_RELEASE(mView); + + NS_IF_RELEASE(mDocument); + + // Note: release context then shell + NS_IF_RELEASE(mPresContext); + if (nsnull != mPresShell) { + // Break circular reference first + mPresShell->EndObservingDocument(); + + // Then release the shell + NS_RELEASE(mPresShell); + } + + NS_IF_RELEASE(mUAStyleSheet); + NS_IF_RELEASE(mContainer); +} + + + +/* + * This method is called by the Document Loader once a document has + * been created for a particular data stream... The content viewer + * must cache this document for later use when Init(...) is called. + */ +NS_IMETHODIMP +DocumentViewerImpl::BindToDocument(nsISupports *aDoc, const char *aCommand) +{ + nsresult rv; + + NS_PRECONDITION(nsnull == mDocument, "Viewer is already bound to a document!"); + +#ifdef NS_DEBUG + printf("DocumentViewerImpl::BindToDocument\n"); +#endif + + rv = aDoc->QueryInterface(kIDocumentIID, (void**)&mDocument); + return rv; +} + +NS_IMETHODIMP +DocumentViewerImpl::SetContainer(nsIContentViewerContainer* aContainer) +{ + NS_IF_RELEASE(mContainer); + mContainer = aContainer; + + if (nsnull != aContainer) { + if (nsnull != mPresContext) { + mPresContext->SetContainer(aContainer); + } + NS_ADDREF(mContainer); + } + + return NS_OK; +} + +NS_IMETHODIMP +DocumentViewerImpl::GetContainer(nsIContentViewerContainer*& aResult) +{ + aResult = mContainer; + NS_IF_ADDREF(mContainer); + return NS_OK; +} + + +NS_IMETHODIMP +DocumentViewerImpl::Init(nsNativeWidget aNativeParent, + nsIDeviceContext* aDeviceContext, + const nsRect& aBounds, + nsScrollPreference aScrolling) +{ + nsresult rv; + + if (nsnull == mDocument) { + return NS_ERROR_NULL_POINTER; + } + + // Create presentation context + rv = NS_NewGalleyContext(&mPresContext); + if (NS_OK != rv) { + return rv; + } + + mPresContext->Init(aDeviceContext); + rv = Init(aNativeParent, aBounds, mDocument, mPresContext, aScrolling); + + // Init(...) will addref the Presentation Context... + if (NS_OK == rv) { + mPresContext->Release(); + } + return rv; +} + + +NS_IMETHODIMP +DocumentViewerImpl::Init(nsNativeWidget aNativeParent, + const nsRect& aBounds, + nsIDocument* aDocument, + nsIPresContext* aPresContext, + nsScrollPreference aScrolling) +{ + nsresult rv; + nsRect bounds; + + NS_PRECONDITION(nsnull != aPresContext, "null ptr"); + NS_PRECONDITION(nsnull != aDocument, "null ptr"); + if ((nsnull == aPresContext) || (nsnull == aDocument)) { + rv = NS_ERROR_NULL_POINTER; + goto done; + } + + mPresContext = aPresContext; + NS_ADDREF(mPresContext); + + if (nsnull != mContainer) { + nsILinkHandler* linkHandler = nsnull; + + mContainer->QueryCapability(kILinkHandlerIID, (void**)&linkHandler); + mPresContext->SetContainer(mContainer); + mPresContext->SetLinkHandler(linkHandler); + NS_IF_RELEASE(linkHandler); + } + + // Create the ViewManager and Root View... + MakeWindow(aNativeParent, aBounds, aScrolling); + + // Create the style set... + nsIStyleSet* styleSet; + rv = CreateStyleSet(aDocument, &styleSet); + if (NS_OK != rv) { + goto done; + } + + // Now make the shell for the document + rv = aDocument->CreateShell(mPresContext, mViewManager, styleSet, + &mPresShell); + NS_RELEASE(styleSet); + if (NS_OK != rv) { + goto done; + } + + // Now that we have a presentation shell trigger a reflow so we + // create a frame model + mWindow->GetBounds(bounds); + if (nsnull != mPresShell) { + nscoord width = bounds.width; + nscoord height = bounds.height; + width = NS_TO_INT_ROUND(width * mPresContext->GetPixelsToTwips()); + height = NS_TO_INT_ROUND(height * mPresContext->GetPixelsToTwips()); + mViewManager->SetWindowDimensions(width, height); + } + ForceRefresh(); + +done: + return rv; +} + +NS_IMETHODIMP +DocumentViewerImpl::SetUAStyleSheet(nsIStyleSheet* aUAStyleSheet) +{ + NS_IF_RELEASE(mUAStyleSheet); + mUAStyleSheet = aUAStyleSheet; + NS_IF_ADDREF(mUAStyleSheet); + + return NS_OK; +} + +NS_IMETHODIMP +DocumentViewerImpl::GetDocument(nsIDocument*& aResult) +{ + aResult = mDocument; + NS_IF_ADDREF(mDocument); + return NS_OK; +} + +NS_IMETHODIMP +DocumentViewerImpl::GetPresShell(nsIPresShell*& aResult) +{ + aResult = mPresShell; + NS_IF_ADDREF(mPresShell); + return NS_OK; +} + +NS_IMETHODIMP +DocumentViewerImpl::GetPresContext(nsIPresContext*& aResult) +{ + aResult = mPresContext; + NS_IF_ADDREF(mPresContext); + return NS_OK; +} + + +nsRect DocumentViewerImpl::GetBounds() +{ + NS_PRECONDITION(nsnull != mWindow, "null window"); + nsRect zr(0, 0, 0, 0); + if (nsnull != mWindow) { + mWindow->GetBounds(zr); + } + return zr; +} + + +void DocumentViewerImpl::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); + } +} + + +void DocumentViewerImpl::Move(PRInt32 aX, PRInt32 aY) +{ + NS_PRECONDITION(nsnull != mWindow, "null window"); + if (nsnull != mWindow) { + mWindow->Move(aX, aY); + } +} + +void DocumentViewerImpl::Show() +{ + NS_PRECONDITION(nsnull != mWindow, "null window"); + if (nsnull != mWindow) { + mWindow->Show(PR_TRUE); + } +} + +void DocumentViewerImpl::Hide() +{ + NS_PRECONDITION(nsnull != mWindow, "null window"); + if (nsnull != mWindow) { + mWindow->Show(PR_FALSE); + } +} + + + +void DocumentViewerImpl::ForceRefresh() +{ + mWindow->Invalidate(PR_TRUE); +} + +nsresult DocumentViewerImpl::CreateStyleSet(nsIDocument* aDocument, nsIStyleSet** aStyleSet) +{ // this should eventually get expanded to allow for creating different sets for different media + nsresult rv; + + if (nsnull == mUAStyleSheet) { + NS_WARNING("unable to load UA style sheet"); + } + + rv = NS_NewStyleSet(aStyleSet); + if (NS_OK == rv) { + PRInt32 count = aDocument->GetNumberOfStyleSheets(); + + for (PRInt32 index = 0; index < count; index++) { + nsIStyleSheet* sheet = aDocument->GetStyleSheetAt(index); + (*aStyleSet)->AppendDocStyleSheet(sheet); + NS_RELEASE(sheet); + } + if (nsnull != mUAStyleSheet) { + (*aStyleSet)->AppendBackstopStyleSheet(mUAStyleSheet); + } + } + return rv; +} + + +nsresult DocumentViewerImpl::MakeWindow(nsNativeWidget aNativeParent, + const nsRect& aBounds, + nsScrollPreference aScrolling) +{ + nsresult rv; + + rv = NSRepository::CreateInstance(kViewManagerCID, + nsnull, + kIViewManagerIID, + (void **)&mViewManager); + + if ((NS_OK != rv) || (NS_OK != mViewManager->Init(mPresContext))) { + return rv; + } + + nsRect tbounds = aBounds; + tbounds *= mPresContext->GetPixelsToTwips(); + + // Create a child window of the parent that is our "root view/window" + // Create a view + rv = NSRepository::CreateInstance(kScrollingViewCID, + nsnull, + kIViewIID, + (void **)&mView); + static NS_DEFINE_IID(kWidgetCID, NS_CHILD_CID); + if ((NS_OK != rv) || (NS_OK != mView->Init(mViewManager, + tbounds, + nsnull, + &kWidgetCID, + nsnull, + aNativeParent))) { + return rv; + } + + nsIScrollableView* scrollView; + rv = mView->QueryInterface(kScrollViewIID, (void**)&scrollView); + if (NS_OK == rv) { + scrollView->SetScrollPreference(aScrolling); + NS_RELEASE(scrollView); + } + else { + NS_ASSERTION(0, "invalid scrolling view"); + return rv; + } + + // Setup hierarchical relationship in view manager + mViewManager->SetRootView(mView); + mWindow = mView->GetWidget(); + if (nsnull != mWindow) { + mViewManager->SetRootWindow(mWindow); + } + + //set frame rate to 25 fps + mViewManager->SetFrameRate(25); + + return rv; +} + +NS_WEB nsresult NS_NewDocumentViewer(nsIDocumentViewer*& aViewer) +{ + aViewer = new DocumentViewerImpl(); + if (nsnull == aViewer) { + return NS_ERROR_OUT_OF_MEMORY; + } + NS_ADDREF(aViewer); + return NS_OK; +} diff --git a/mozilla/webshell/src/nsWebShell.cpp b/mozilla/webshell/src/nsWebShell.cpp new file mode 100644 index 00000000000..864cf8aaf44 --- /dev/null +++ b/mozilla/webshell/src/nsWebShell.cpp @@ -0,0 +1,1037 @@ +/* -*- 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 "nsRepository.h" +#include "nsCRT.h" +#include "nsVoidArray.h" +#include "nsString.h" +#include "nsWidgetsCID.h" +#include "nsGfxCIID.h" +#include "plevent.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: + 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 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 SetParent(nsIWebShell* aParent); + NS_IMETHOD GetParent(nsIWebShell*& aParent); + NS_IMETHOD GetChildCount(PRInt32& aResult); + NS_IMETHOD AddChild(nsIWebShell* aChild, PRBool aRelationship = PR_TRUE); + 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(nsIStreamObserver* aObserver); + NS_IMETHOD Forward(nsIStreamObserver* aObserver); + NS_IMETHOD LoadURL(const nsString& aURLSpec, + nsIStreamObserver* aObserver, + nsIPostData* aPostData=nsnull); + NS_IMETHOD GoTo(PRInt32 aHistoryIndex, nsIStreamObserver* aObserver); + NS_IMETHOD GetHistoryIndex(PRInt32& 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 Init(); + 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); + + // nsWebShell + void HandleLinkClickEvent(const nsString& aURLSpec, + const nsString& aTargetSpec, + nsIPostData* aPostDat = 0); + + void ShowHistory(); + + nsIWebShell* GetRootWebShell(); + + nsIWebShell* GetTarget(const nsString& aName); + + static nsEventStatus PR_CALLBACK HandleEvent(nsGUIEvent *aEvent); + +protected: + nsIWebShellContainer* mContainer; + nsIContentViewer* mContentViewer; + nsIDeviceContext* mDeviceContext; + nsIWidget* mWindow; + nsIDocumentLoader* mDocLoader; + + nsIWebShell* mParent; + nsVoidArray mChildren; + nsString mName; + + nsVoidArray mHistory; + PRInt32 mHistoryIndex; + + nsString mOverURL; + nsString mOverTarget; + + void ReleaseChildren(); +}; + +//---------------------------------------------------------------------- + +// 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(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); + +// XXX not sure +static NS_DEFINE_IID(kILinkHandlerIID, NS_ILINKHANDLER_IID); + +//---------------------------------------------------------------------- + +NS_WEB nsresult +NS_NewWebShell(nsIWebShell*& aResult) +{ + nsIWebShell* it = new nsWebShell(); + if (nsnull == it) { + return NS_ERROR_OUT_OF_MEMORY; + } + it->AddRef(); + aResult = it; + return NS_OK; +} + +// Note: operator new zeros our memory +nsWebShell::nsWebShell() +{ + NS_INIT_REFCNT(); + mHistoryIndex = -1; +} + +nsWebShell::~nsWebShell() +{ + NS_IF_RELEASE(mContentViewer); + NS_IF_RELEASE(mContainer); + + // 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) + +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(kISupportsIID)) { + *aInstancePtr = (void*)(nsISupports*)(nsIWebShell*)this; + AddRef(); + return NS_OK; + } + 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; + } + 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); + if (NS_OK == rv) { + mContentViewer->Show(); + } + + // Now that we have switch documents, forget all of our children + ReleaseChildren(); + + return rv; +} + +NS_IMETHODIMP +nsWebShell::Init(nsNativeWidget aNativeParent, + const nsRect& aBounds, + nsScrollPreference aScrolling) +{ + WEB_TRACE(WEB_TRACE_CALLS, + ("nsWebShell::Init: this=%p", this)); + + nsresult rv = NSRepository::CreateInstance(kDocumentLoaderCID, nsnull, + kIDocumentLoaderIID, + (void**)&mDocLoader); + + NS_PRECONDITION(nsnull != aNativeParent, "null Parent Window"); + if (nsnull == aNativeParent) { + rv = NS_ERROR_NULL_POINTER; + 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, nsnull, kIWidgetIID, + (void**)&mWindow); + if (NS_OK != rv) { + goto done; + } + mWindow->Create(aNativeParent, aBounds, nsWebShell::HandleEvent, + mDeviceContext, nsnull); + +done: + 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::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, PRBool aRelationship) +{ + 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(nsIStreamObserver* aObserver) +{ + return GoTo(mHistoryIndex - 1, aObserver); +} + +NS_IMETHODIMP +nsWebShell::Forward(nsIStreamObserver* aObserver) +{ + return GoTo(mHistoryIndex + 1, aObserver); +} + +NS_IMETHODIMP +nsWebShell::LoadURL(const nsString& aURLSpec, + nsIStreamObserver* aObserver, + 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; + } + } + rv = mDocLoader->LoadURL(urlSpec, // URL string + nsnull, // Command + this, // Container + aPostData, // Post Data + nsnull, // Extra Info... + aObserver); // Observer + return rv; +} + +NS_IMETHODIMP +nsWebShell::GoTo(PRInt32 aHistoryIndex, nsIStreamObserver* aObserver) +{ + 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; + } + } + rv = mDocLoader->LoadURL(urlSpec, // URL string + nsnull, // Command + this, // Container + nsnull, // Post Data + nsnull, // Extra Info... + aObserver); // Observer + } + return rv; +} + +NS_IMETHODIMP +nsWebShell::GetHistoryIndex(PRInt32& aResult) +{ + aResult = mHistoryIndex; + return NS_OK; +} + +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 +} + +//---------------------------------------------------------------------- + +// 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::Init() +{ + return NS_OK; +} + +NS_IMETHODIMP +nsWebShell::OnLinkClick(nsIFrame* aFrame, + const nsString& aURLSpec, + const nsString& aTargetSpec, + nsIPostData* aPostData) +{ + new OnLinkClickEvent(this, aURLSpec, aTargetSpec, aPostData); + return NS_OK; +} + +// Find the web shell at the top of our tree +nsIWebShell* +nsWebShell::GetRootWebShell() +{ + nsIWebShell* top = this; + for (;;) { + nsIWebShell* parent; + top->GetParent(parent); + if (nsnull == parent) { + break; + } + top = parent; + } + return top; +} + +// 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 Where should this logic live? _blank requires the app to + // get involved + NS_ASSERTION(0, "not implemented yet"); + target = this; + } + else if (aName.EqualsIgnoreCase("_self")) { + target = this; + } + else if (aName.EqualsIgnoreCase("_parent")) { + if (nsnull == mParent) { + target = this; + } + else { + target = mParent; + } + } + else if (aName.EqualsIgnoreCase("_top")) { + target = GetRootWebShell(); + } + else { + // Look from the top of the tree downward + nsIWebShell* top = GetRootWebShell(); + top->FindChildWithName(aName, target); + if (nsnull == target) { + target = this; + } + } + if (target == this) { + NS_ADDREF(this); + } + return target; +} + +void +nsWebShell::HandleLinkClickEvent(const nsString& aURLSpec, + const nsString& aTargetSpec, + nsIPostData* aPostData) +{ + nsIWebShell* shell = GetTarget(aTargetSpec); + if (nsnull != shell) { + shell->LoadURL(aURLSpec, nsnull, 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; +} + +//---------------------------------------------------------------------- + +// Factory code for creating nsWebShell's + +class nsWebShellFactory : public nsIFactory +{ +public: + // 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); + + nsWebShellFactory(const nsCID &aClass); + ~nsWebShellFactory(); + +private: + nsrefcnt mRefCnt; + nsCID mClassID; +}; + +nsWebShellFactory::nsWebShellFactory(const nsCID &aClass) +{ + mRefCnt = 0; + mClassID = aClass; +} + +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) +{ + if (aResult == NULL) { + return NS_ERROR_NULL_POINTER; + } + + *aResult = NULL; + + nsWebShell *inst = nsnull; + + if (mClassID.Equals(kWebShellCID)) { + inst = new nsWebShell(); + } + + if (inst == NULL) { + return NS_ERROR_OUT_OF_MEMORY; + } + + nsresult res = inst->QueryInterface(aIID, aResult); + + if (res != NS_OK) { + // We didn't get the right interface, so clean up + delete inst; + } + + return res; +} + +nsresult +nsWebShellFactory::LockFactory(PRBool aLock) +{ + // Not implemented in simplest case. + return NS_OK; +} + +//---------------------------------------------------------------------- + +// return the proper factory to the caller +extern "C" NS_WEB nsresult +NSGetFactory(const nsCID &aClass, nsIFactory **aFactory) +{ + nsresult rv = NS_OK; + + if (nsnull == aFactory) { + return NS_ERROR_NULL_POINTER; + } + + if (aClass.Equals(kWebShellCID)) { + nsIFactory* inst = new nsWebShellFactory(aClass); + NS_IF_ADDREF(inst); + *aFactory = inst; + if (nsnull == inst) { + rv = NS_ERROR_OUT_OF_MEMORY; + } + } + else if (aClass.Equals(kDocumentLoaderCID)) { + rv = NS_NewDocumentLoaderFactory(aFactory); + } + + return rv; +}