--- chimera/NSBrowserView.mm 2002/01/31 20:18:48 1.1 +++ chimera/NSBrowserView.mm 2002/02/25 08:44:17 1.22 @@ -36,6 +36,8 @@ * ***** END LICENSE BLOCK ***** */ #import "NSBrowserView.h" +#import "ProgressDlgController.h" +#import "FindDlgController.h" #import "nsCocoaBrowserService.h" // Embedding includes @@ -55,11 +57,41 @@ #include "nsXPIDLString.h" #include "nsCOMPtr.h" +// Printing +#include "nsIWebBrowserPrint.h" +#include "nsIPrintSettings.h" + +// Saving of links/images/docs +#include "nsIWebBrowserFocus.h" +#include "nsIDOMHTMLDocument.h" +#include "nsIDOMNSDocument.h" +#include "nsIDOMLocation.h" +#include "nsIURL.h" +#include "nsIWebBrowserPersist.h" +#include "nsIProperties.h" +#include "nsIRequest.h" +#include "nsIChannel.h" +#include "nsIHttpChannel.h" +#include "nsIPref.h" +#include "nsIMIMEService.h" +#include "nsIMIMEInfo.h" +#include "nsISHistory.h" +#include "nsIHistoryEntry.h" +#include "nsISHEntry.h" +#include "nsNetUtil.h" + +// Cut/copy/paste +#include "nsIClipboardCommands.h" +#include "nsIInterfaceRequestorUtils.h" + +const char* persistContractID = "@mozilla.org/embedding/browser/nsWebBrowserPersist;1"; +const char* dirServiceContractID = "@mozilla.org/file/directory_service;1"; class nsCocoaBrowserListener : public nsSupportsWeakReference, public nsIInterfaceRequestor, - public nsIWebBrowserChrome, - public nsIEmbeddingSiteWindow, + public nsIWebBrowserChrome, + public nsIWindowCreator, + public nsIEmbeddingSiteWindow, public nsIWebProgressListener { public: @@ -69,6 +101,7 @@ public: NS_DECL_ISUPPORTS NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSIWEBBROWSERCHROME + NS_DECL_NSIWINDOWCREATOR NS_DECL_NSIEMBEDDINGSITEWINDOW NS_DECL_NSIWEBPROGRESSLISTENER @@ -100,12 +133,13 @@ nsCocoaBrowserListener::~nsCocoaBrowserL } } -NS_IMPL_ISUPPORTS5(nsCocoaBrowserListener, +NS_IMPL_ISUPPORTS6(nsCocoaBrowserListener, nsIInterfaceRequestor, nsIWebBrowserChrome, + nsIWindowCreator, nsIEmbeddingSiteWindow, nsIWebProgressListener, - nsISupportsWeakReference) + nsISupportsWeakReference) // Implementation of nsIInterfaceRequestor NS_IMETHODIMP @@ -120,6 +154,36 @@ nsCocoaBrowserListener::GetInterface(con return QueryInterface(aIID, aInstancePtr); } +// Implementation of nsIWindowCreator. The CocoaBrowserService forwards requests +// for a new window that have a parent to us, and we take over from there. +/* nsIWebBrowserChrome createChromeWindow (in nsIWebBrowserChrome parent, in PRUint32 chromeFlags); */ +NS_IMETHODIMP +nsCocoaBrowserListener::CreateChromeWindow(nsIWebBrowserChrome *parent, + PRUint32 chromeFlags, + nsIWebBrowserChrome **_retval) +{ + if (parent != this) { + printf("Mismatch in nsCocoaBrowserListener::CreateChromeWindow. We should be the owning parent.\n"); + return NS_ERROR_FAILURE; + } + + NSBrowserView* childView = [mContainer createBrowserWindow: chromeFlags]; + if (!childView) { + printf("No NSBrowserView hooked up for a newly created window yet.\n"); + return NS_ERROR_FAILURE; + } + + nsCocoaBrowserListener* listener = [childView getCocoaBrowserListener]; + if (!listener) { + printf("Uh-oh! No listener yet for a newly created window (nsCocoaBrowserlistener)\n"); + return NS_ERROR_FAILURE; + } + + *_retval = listener; + NS_IF_ADDREF(*_retval); + return NS_OK; +} + // Implementation of nsIWebBrowserChrome /* void setStatus (in unsigned long statusType, in wstring status); */ NS_IMETHODIMP @@ -225,10 +289,10 @@ nsCocoaBrowserListener::ShowAsModal() } mIsModal = PR_TRUE; - int result = [NSApp runModalForWindow:window]; + //int result = [NSApp runModalForWindow:window]; mIsModal = PR_FALSE; - return (nsresult)result; + return NS_OK; } /* boolean isWindowModal (); */ @@ -246,7 +310,7 @@ nsCocoaBrowserListener::IsWindowModal(PR NS_IMETHODIMP nsCocoaBrowserListener::ExitModalEventLoop(nsresult aStatus) { - [NSApp stopModalWithCode:(int)aStatus]; +// [NSApp stopModalWithCode:(int)aStatus]; return NS_OK; } @@ -533,6 +597,17 @@ nsCocoaBrowserListener::OnStatusChange(n nsresult aStatus, const PRUnichar *aMessage) { + nsCAutoString msg; msg.AssignWithConversion(aMessage); + + NSString* str = [NSString stringWithCString:msg.get()]; + + NSEnumerator* enumerator = [mListeners objectEnumerator]; + id obj; + + while ((obj = [enumerator nextObject])) { + [obj onStatusChange: str]; + } + return NS_OK; } @@ -567,6 +642,277 @@ nsCocoaBrowserListener::SetContainer(id [mContainer retain]; } +// Implementation of a header sniffer class that is used when saving Web pages and images. +class nsHeaderSniffer : public nsIWebProgressListener +{ +public: + nsHeaderSniffer(nsIWebBrowserPersist* aPersist, nsIFile* aFile, nsIURI* aURL, + nsIDOMDocument* aDocument, nsIInputStream* aPostData, PRBool aBypassCache, + NSView* aFilterView, NSPopUpButton* aFilterList) + { + NS_INIT_REFCNT(); + mPersist = aPersist; + mTmpFile = aFile; + mURL = aURL; + mDocument = aDocument; + mPostData = aPostData; + mBypassCache = aBypassCache; + mFilterView = aFilterView; + mFilterList = aFilterList; + } + + virtual ~nsHeaderSniffer() + { + }; + + NS_DECL_ISUPPORTS + NS_DECL_NSIWEBPROGRESSLISTENER + +protected: + void PerformSave(); + +private: + nsIWebBrowserPersist* mPersist; // Weak. It owns us as a listener. + nsCOMPtr mTmpFile; + nsCOMPtr mURL; + nsCOMPtr mDocument; + nsCOMPtr mPostData; + PRBool mBypassCache; + nsCString mContentType; + nsCString mContentDisposition; + NSView* mFilterView; + NSPopUpButton* mFilterList; +}; + +NS_IMPL_ISUPPORTS1(nsHeaderSniffer, nsIWebProgressListener) + +// Implementation of nsIWebProgressListener +/* void onStateChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long aStateFlags, in unsigned long aStatus); */ +NS_IMETHODIMP +nsHeaderSniffer::OnStateChange(nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + PRInt32 aStateFlags, + PRUint32 aStatus) +{ + if (aStateFlags & nsIWebProgressListener::STATE_START) { + nsCOMPtr channel(do_QueryInterface(aRequest)); + nsXPIDLCString contentType; + channel->GetContentType(getter_Copies(contentType)); + mContentType = contentType; + + // Get the content-disposition if we're an HTTP channel. + nsCOMPtr httpChannel(do_QueryInterface(channel)); + if (httpChannel) { + nsXPIDLCString disp; + httpChannel->GetResponseHeader("content-disposition", getter_Copies(disp)); + mContentDisposition = disp; + } + + mPersist->CancelSave(); + PRBool exists; + mTmpFile->Exists(&exists); + if (exists) + mTmpFile->Remove(PR_FALSE); + PerformSave(); + } + return NS_OK; +} + +void nsHeaderSniffer::PerformSave() +{ + // Are we an HTML document? If so, we will want to append an accessory view to + // the save dialog to provide the user with the option of doing a complete + // save vs. a single file save. + PRBool isHTML = (mDocument && mContentType.Equals("text/html") || + mContentType.Equals("text/xml") || + mContentType.Equals("application/xhtml+xml")); + + // Next find out the directory that we should start in. + nsCOMPtr prefs(do_GetService("@mozilla.org/preferences-service;1")); + if (!prefs) + return; + nsCOMPtr dirBranch; + prefs->GetBranch("browser.download.", getter_AddRefs(dirBranch)); + PRInt32 filterIndex = 0; + if (dirBranch) { + nsresult rv = dirBranch->GetIntPref("save_converter_index", &filterIndex); + if (NS_FAILED(rv)) + filterIndex = 0; + } + if (mFilterList) + [mFilterList selectItemAtIndex: filterIndex]; + + // We need to figure out what file name to use. + nsCAutoString defaultFileName; + + if (!mContentDisposition.IsEmpty()) { + // (1) Use the HTTP header suggestion. + PRInt32 index = mContentDisposition.Find("filename="); + if (index >= 0) { + // Take the substring following the prefix. + index += 9; + nsCAutoString filename; + mContentDisposition.Right(filename, mContentDisposition.Length() - index); + defaultFileName = filename; + } + } + + if (defaultFileName.IsEmpty()) { + nsCOMPtr url(do_QueryInterface(mURL)); + if (url) { + nsXPIDLCString fileName; + url->GetFileName(getter_Copies(fileName)); + defaultFileName = fileName; // (2) For file URLs, use the file name. + } + } + + if (defaultFileName.IsEmpty() && mDocument && isHTML) { + nsCOMPtr htmlDoc(do_QueryInterface(mDocument)); + nsAutoString title; + if (htmlDoc) + htmlDoc->GetTitle(title); // (3) Use the title of the document. + defaultFileName.AssignWithConversion(title); + } + + if (defaultFileName.IsEmpty()) { + // (4) Use the caller provided name. XXXdwh + } + + if (defaultFileName.IsEmpty()) { + // (5) Use the host. + nsXPIDLCString host; + mURL->GetHost(getter_Copies(host)); + defaultFileName = host; + } + + // One last case to handle about:blank and other fruity untitled pages. + if (defaultFileName.IsEmpty()) + defaultFileName = "untitled"; + + // Validate the file name to ensure legality. + for (PRUint32 i = 0; i < defaultFileName.Length(); i++) + if (defaultFileName[i] == ':' || defaultFileName[i] == '/') + defaultFileName.SetCharAt(i, ' '); + + // Make sure the appropriate extension is appended to the suggested file name. + nsCOMPtr fileURI(do_CreateInstance("@mozilla.org/network/standard-url;1")); + nsCOMPtr fileURL(do_QueryInterface(fileURI)); + if (!fileURL) + return; + fileURL->SetFilePath(defaultFileName.get()); + + nsXPIDLCString fileEx; + fileURL->GetFileExtension(getter_Copies(fileEx)); + nsDependentCString fileExtension(fileEx.get()); + + PRBool setExtension = PR_FALSE; + if (mContentType.Equals("text/html")) { + if (fileExtension.IsEmpty() || (!fileExtension.Equals("htm") && !fileExtension.Equals("html"))) { + defaultFileName += ".html"; + setExtension = PR_TRUE; + } + } + + if (!setExtension && fileExtension.IsEmpty()) { + nsCOMPtr mimeService(do_GetService("@mozilla.org/mime;1")); + if (!mimeService) + return; + nsCOMPtr mimeInfo; + mimeService->GetFromMIMEType(mContentType.get(), getter_AddRefs(mimeInfo)); + + PRUint32 extCount; + char** extList; + mimeInfo->GetFileExtensions(&extCount, &extList); + + if (extCount > 0) { + defaultFileName += "."; + defaultFileName += extList[0]; + } + } + + // Now it's time to pose the save dialog. + NSSavePanel* savePanel = [NSSavePanel savePanel]; + NSString* file = nil; + if (!defaultFileName.IsEmpty()) + file = [[NSString alloc] initWithCString: defaultFileName.get()]; + + if (isHTML) + [savePanel setAccessoryView: mFilterView]; + + if ([savePanel runModalForDirectory: nil file: file] == NSFileHandlingPanelCancelButton) + return; + + // Release the file string. + [file release]; + + // Update the filter index. + if (isHTML && mFilterList) { + filterIndex = [mFilterList indexOfSelectedItem]; + dirBranch->SetIntPref("save_converter_index", filterIndex); + } + + // Convert the content type to text/plain if it was selected in the filter. + if (isHTML && filterIndex == 2) + mContentType = "text/plain"; + + nsCOMPtr sourceData; + if (isHTML && filterIndex != 1) + sourceData = do_QueryInterface(mDocument); + else + sourceData = do_QueryInterface(mURL); + + nsCOMPtr webPersist(do_CreateInstance(persistContractID)); + ProgressDlgController* progressDialog = [[ProgressDlgController alloc] initWithWindowNibName: @"ProgressDialog"]; + [progressDialog setWebPersist: webPersist + source: sourceData.get() + destination: [savePanel filename] + contentType: mContentType.get() + postData: mPostData + bypassCache: mBypassCache]; + + [progressDialog showWindow: progressDialog]; +} + +/* void onProgressChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long aCurSelfProgress, in long aMaxSelfProgress, in long aCurTotalProgress, in long aMaxTotalProgress); */ +NS_IMETHODIMP +nsHeaderSniffer::OnProgressChange(nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + PRInt32 aCurSelfProgress, + PRInt32 aMaxSelfProgress, + PRInt32 aCurTotalProgress, + PRInt32 aMaxTotalProgress) +{ + return NS_OK; +} + +/* void onLocationChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsIURI location); */ +NS_IMETHODIMP +nsHeaderSniffer::OnLocationChange(nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + nsIURI *location) +{ + return NS_OK; +} + +/* void onStatusChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsresult aStatus, in wstring aMessage); */ +NS_IMETHODIMP +nsHeaderSniffer::OnStatusChange(nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + nsresult aStatus, + const PRUnichar *aMessage) +{ + return NS_OK; +} + +/* void onSecurityChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long state); */ +NS_IMETHODIMP +nsHeaderSniffer::OnSecurityChange(nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + PRInt32 state) +{ + return NS_OK; +} + @implementation NSBrowserView @@ -608,12 +954,21 @@ nsCocoaBrowserListener::SetContainer(id return self; } +- (void)destroyWebBrowser +{ + nsCOMPtr baseWin = do_QueryInterface(_webBrowser); + baseWin->Destroy(); +} + - (void)dealloc { NS_RELEASE(_listener); NS_IF_RELEASE(_webBrowser); - nsCocoaBrowserService::TermEmbedding(); - + + nsCocoaBrowserService::BrowserClosed(); + + printf("NSBrowserView died.\n"); + [super dealloc]; } @@ -779,6 +1134,11 @@ nsCocoaBrowserListener::SetContainer(id return url; } +- (nsCocoaBrowserListener*)getCocoaBrowserListener +{ + return _listener; +} + - (nsIWebBrowser*)getWebBrowser { NS_IF_ADDREF(_webBrowser); @@ -805,5 +1165,195 @@ nsCocoaBrowserListener::SetContainer(id } +-(void) saveInternal: (nsIURI*)aURI + withDocument: (nsIDOMDocument*)aDocument + bypassCache: (BOOL)aBypassCache + filterView: (NSView*)aFilterView + filterList: (NSPopUpButton*)aFilterList +{ + // Create our web browser persist object. This is the object that knows + // how to actually perform the saving of the page (and of the images + // on the page). + nsCOMPtr webPersist(do_CreateInstance(persistContractID)); + if (!webPersist) + return; + + // Make a temporary file object that we can save to. + nsCOMPtr dirService(do_GetService(dirServiceContractID)); + if (!dirService) + return; + nsCOMPtr tmpFile; + dirService->Get("TmpD", NS_GET_IID(nsIFile), getter_AddRefs(tmpFile)); + static short unsigned int tmpRandom = 0; + nsCAutoString tmpNo; tmpNo.AssignWithConversion(tmpRandom++); + nsCAutoString saveFile("-sav"); + saveFile += tmpNo; + saveFile += "tmp"; + tmpFile->Append(saveFile.get()); + + // Get the post data if we're an HTML doc. + nsCOMPtr webNav(do_QueryInterface(_webBrowser)); + nsCOMPtr sessionHistory; + webNav->GetSessionHistory(getter_AddRefs(sessionHistory)); + nsCOMPtr entry; + PRInt32 sindex; + sessionHistory->GetIndex(&sindex); + sessionHistory->GetEntryAtIndex(sindex, PR_FALSE, getter_AddRefs(entry)); + nsCOMPtr shEntry(do_QueryInterface(entry)); + nsCOMPtr postData; + if (shEntry) + shEntry->GetPostData(getter_AddRefs(postData)); + + nsHeaderSniffer* sniffer = new nsHeaderSniffer(webPersist, tmpFile, aURI, + aDocument, postData, aBypassCache, + aFilterView, aFilterList); + if (!sniffer) + return; + webPersist->SetProgressListener(sniffer); + webPersist->SaveURI(aURI, nsnull, tmpFile); +} + +-(void)printDocument +{ + nsCOMPtr domWindow; + _webBrowser->GetContentDOMWindow(getter_AddRefs(domWindow)); + nsCOMPtr ir(do_QueryInterface(domWindow)); + nsCOMPtr print; + ir->GetInterface(NS_GET_IID(nsIWebBrowserPrint), getter_AddRefs(print)); + print->Print(nsnull, nsnull); +} + +-(void)findInPage +{ + FindDlgController* findDialog = [[FindDlgController alloc] initWithWindowNibName: @"FindDialog"]; + nsCOMPtr wbf(do_QueryInterface(_webBrowser)); + nsCOMPtr rootWindow; + nsCOMPtr focusedWindow; + _webBrowser->GetContentDOMWindow(getter_AddRefs(rootWindow)); + wbf->GetFocusedWindow(getter_AddRefs(focusedWindow)); + if (!focusedWindow) + focusedWindow = rootWindow; + nsCOMPtr webFind(do_GetInterface(_webBrowser)); + nsCOMPtr framesFind(do_QueryInterface(webFind)); + framesFind->SetRootSearchFrame(rootWindow); + framesFind->SetCurrentSearchFrame(focusedWindow); + + [findDialog setFind: webFind]; + [findDialog showWindow: findDialog]; +} + +-(void)findAgain +{ +} + +-(void)saveDocument: (NSView*)aFilterView filterList: (NSPopUpButton*)aFilterList +{ + nsCOMPtr wbf(do_QueryInterface(_webBrowser)); + nsCOMPtr domWindow; + wbf->GetFocusedWindow(getter_AddRefs(domWindow)); + if (!domWindow) + _webBrowser->GetContentDOMWindow(getter_AddRefs(domWindow)); + if (!domWindow) + return; + + nsCOMPtr domDocument; + domWindow->GetDocument(getter_AddRefs(domDocument)); + if (!domDocument) + return; + nsCOMPtr nsDoc(do_QueryInterface(domDocument)); + if (!nsDoc) + return; + nsCOMPtr location; + nsDoc->GetLocation(getter_AddRefs(location)); + if (!location) + return; + nsAutoString urlStr; + location->GetHref(urlStr); + nsCAutoString urlCStr; urlCStr.AssignWithConversion(urlStr); + nsCOMPtr url; + nsresult rv = NS_NewURI(getter_AddRefs(url), urlCStr.get()); + if (NS_FAILED(rv)) + return; + +#if 0 + nsCOMPtr doc(do_QueryInterface(domDocument)); + if (!doc) + return; + nsCOMPtr url; + doc->GetDocumentURL(getter_AddRefs(url)); +#endif + + [self saveInternal: url.get() + withDocument: domDocument + bypassCache: NO + filterView: aFilterView + filterList: aFilterList]; +} + + +-(IBAction)cut:(id)aSender +{ + nsCOMPtr clipboard(do_GetInterface(_webBrowser)); + clipboard->CutSelection(); +} + +-(IBAction)copy:(id)aSender +{ + nsCOMPtr clipboard(do_GetInterface(_webBrowser)); + clipboard->CopySelection(); +} + +-(IBAction)paste:(id)aSender +{ + nsCOMPtr clipboard(do_GetInterface(_webBrowser)); + clipboard->Paste(); +} + +-(IBAction)clear:(id)aSender +{ + nsCOMPtr clipboard(do_GetInterface(_webBrowser)); + clipboard->SelectNone(); +} + +-(IBAction)selectAll:(id)aSender +{ + nsCOMPtr clipboard(do_GetInterface(_webBrowser)); + clipboard->SelectAll(); +} + +-(NSString*)getCurrentURLSpec +{ + NSString* empty = @""; + nsCOMPtr domWindow; + _webBrowser->GetContentDOMWindow(getter_AddRefs(domWindow)); + if (!domWindow) + return empty; + + nsCOMPtr domDocument; + domWindow->GetDocument(getter_AddRefs(domDocument)); + if (!domDocument) + return empty; + nsCOMPtr nsDoc(do_QueryInterface(domDocument)); + if (!nsDoc) + return empty; + nsCOMPtr location; + nsDoc->GetLocation(getter_AddRefs(location)); + if (!location) + return empty; + nsAutoString urlStr; + location->GetHref(urlStr); + nsCAutoString urlCStr; urlCStr.AssignWithConversion(urlStr); + + return [NSString stringWithCString: urlCStr.get()]; +} + +- (void)setActive: (BOOL)aIsActive +{ + nsCOMPtr wbf(do_QueryInterface(_webBrowser)); + if (aIsActive) + wbf->Activate(); + else + wbf->Deactivate(); +} @end