Back to index

lightning-sunbird  0.9+nobinonly
nsWebShell.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Travis Bogard <travis@netscape.com>
00024  *   Dan Rosen <dr@netscape.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsDocShell.h"
00041 #include "nsWebShell.h"
00042 #include "nsIWebBrowserChrome.h"
00043 #include "nsIInterfaceRequestor.h"
00044 #include "nsIInterfaceRequestorUtils.h"
00045 #include "nsIWebProgress.h"
00046 #include "nsIDocumentLoader.h"
00047 #include "nsIContentViewer.h"
00048 #include "nsIDocumentViewer.h"
00049 #include "nsIMarkupDocumentViewer.h"
00050 #include "nsIClipboardCommands.h"
00051 #include "nsILinkHandler.h"
00052 #include "nsIStreamListener.h"
00053 #include "nsIPrompt.h"
00054 #include "nsNetUtil.h"
00055 #include "nsIRefreshURI.h"
00056 #include "nsIScriptGlobalObject.h"
00057 #include "nsIScriptGlobalObjectOwner.h"
00058 #include "nsIDOMEvent.h"
00059 #include "nsPresContext.h"
00060 #include "nsIComponentManager.h"
00061 #include "nsIEventQueueService.h"
00062 #include "nsCRT.h"
00063 #include "nsVoidArray.h"
00064 #include "nsString.h"
00065 #include "nsReadableUtils.h"
00066 #include "plevent.h"
00067 #include "prprf.h"
00068 #include "nsIPluginHost.h"
00069 #include "nsplugin.h"
00070 #include "nsIPluginManager.h"
00071 #include "nsCDefaultURIFixup.h"
00072 #include "nsIContent.h"
00073 #include "prlog.h"
00074 #include "nsCOMPtr.h"
00075 #include "nsIPresShell.h"
00076 #include "nsIWebShellServices.h"
00077 #include "nsIGlobalHistory.h"
00078 #include "prmem.h"
00079 #include "prthread.h"
00080 #include "nsXPIDLString.h"
00081 #include "nsDOMError.h"
00082 #include "nsIDOMRange.h"
00083 #include "nsIURIContentListener.h"
00084 #include "nsIDOMDocument.h"
00085 #include "nsIDOMHTMLAnchorElement.h"
00086 #include "nsIBaseWindow.h"
00087 #include "nsIDocShell.h"
00088 #include "nsIDocShellTreeItem.h"
00089 #include "nsIDocShellTreeNode.h"
00090 #include "nsIDocShellTreeOwner.h"
00091 #include "nsCURILoader.h"
00092 #include "nsIDOMWindowInternal.h"
00093 #include "nsEscape.h"
00094 #include "nsIPlatformCharset.h"
00095 #include "nsICharsetConverterManager.h"
00096 #include "nsISocketTransportService.h"
00097 #include "nsTextFormatter.h"
00098 #include "nsPIDOMWindow.h"
00099 #include "nsPICommandUpdater.h"
00100 #include "nsIController.h"
00101 #include "nsIFocusController.h"
00102 #include "nsGUIEvent.h"
00103 #include "nsISHistoryInternal.h"
00104 
00105 #include "nsIHttpChannel.h"
00106 #include "nsIUploadChannel.h"
00107 #include "nsISeekableStream.h"
00108 
00109 #include "nsILocaleService.h"
00110 #include "nsIStringBundle.h"
00111 
00112 #include "nsICachingChannel.h"
00113 
00114 //XXX for nsIPostData; this is wrong; we shouldn't see the nsIDocument type
00115 #include "nsIDocument.h"
00116 #include "nsITextToSubURI.h"
00117 
00118 #include "nsIExternalProtocolService.h"
00119 #include "nsCExternalHandlerService.h"
00120 
00121 #include "nsIIDNService.h"
00122 
00123 #ifdef NS_DEBUG
00124 
00128 static PRLogModuleInfo* gLogModule = PR_NewLogModule("webshell");
00129 #endif
00130 
00131 #define WEB_TRACE_CALLS        0x1
00132 #define WEB_TRACE_HISTORY      0x2
00133 
00134 #define WEB_LOG_TEST(_lm,_bit) (PRIntn((_lm)->level) & (_bit))
00135 
00136 #ifdef NS_DEBUG
00137 #define WEB_TRACE(_bit,_args)            \
00138   PR_BEGIN_MACRO                         \
00139     if (WEB_LOG_TEST(gLogModule,_bit)) { \
00140       PR_LogPrint _args;                 \
00141     }                                    \
00142   PR_END_MACRO
00143 #else
00144 #define WEB_TRACE(_bit,_args)
00145 #endif
00146 
00147 //----------------------------------------------------------------------
00148 
00149 static PRBool
00150 IsOffline()
00151 {
00152     PRBool offline = PR_TRUE;
00153     nsCOMPtr<nsIIOService> ios(do_GetIOService());
00154     if (ios)
00155         ios->GetOffline(&offline);
00156     return offline;
00157 }
00158 
00159 //----------------------------------------------------------------------
00160 
00161 // Note: operator new zeros our memory
00162 nsWebShell::nsWebShell() : nsDocShell()
00163 {
00164 #ifdef DEBUG
00165   // We're counting the number of |nsWebShells| to help find leaks
00166   ++gNumberOfWebShells;
00167 #endif
00168 #ifdef DEBUG
00169     printf("++WEBSHELL == %ld\n", gNumberOfWebShells);
00170 #endif
00171 
00172   mThread = nsnull;
00173   InitFrameData();
00174   mItemType = typeContent;
00175   mCharsetReloadState = eCharsetReloadInit;
00176 }
00177 
00178 nsWebShell::~nsWebShell()
00179 {
00180    Destroy();
00181 
00182   ++mRefCnt; // following releases can cause this destructor to be called
00183              // recursively if the refcount is allowed to remain 0
00184 
00185   mContentViewer=nsnull;
00186   mDeviceContext=nsnull;
00187 
00188   InitFrameData();
00189 
00190 #ifdef DEBUG
00191   // We're counting the number of |nsWebShells| to help find leaks
00192   --gNumberOfWebShells;
00193 #endif
00194 #ifdef DEBUG
00195   printf("--WEBSHELL == %ld\n", gNumberOfWebShells);
00196 #endif
00197 }
00198 
00199 void nsWebShell::InitFrameData()
00200 {
00201   SetMarginWidth(-1);    
00202   SetMarginHeight(-1);
00203 }
00204 
00205 nsresult
00206 nsWebShell::EnsureCommandHandler()
00207 {
00208   if (!mCommandManager)
00209   {
00210     mCommandManager = do_CreateInstance("@mozilla.org/embedcomp/command-manager;1");
00211     if (!mCommandManager) return NS_ERROR_OUT_OF_MEMORY;
00212     
00213     nsCOMPtr<nsPICommandUpdater>       commandUpdater = do_QueryInterface(mCommandManager);
00214     if (!commandUpdater) return NS_ERROR_FAILURE;
00215     
00216     nsCOMPtr<nsIDOMWindow> domWindow = do_GetInterface(NS_STATIC_CAST(nsIInterfaceRequestor *, this));
00217 #ifdef DEBUG
00218     nsresult rv =
00219 #endif
00220     commandUpdater->Init(domWindow);
00221     NS_ASSERTION(NS_SUCCEEDED(rv), "Initting command manager failed");
00222   }
00223   
00224   return mCommandManager ? NS_OK : NS_ERROR_FAILURE;
00225 }
00226 
00227 
00228 
00229 NS_IMPL_ADDREF_INHERITED(nsWebShell, nsDocShell)
00230 NS_IMPL_RELEASE_INHERITED(nsWebShell, nsDocShell)
00231 
00232 NS_INTERFACE_MAP_BEGIN(nsWebShell)
00233    NS_INTERFACE_MAP_ENTRY(nsIWebShellServices)
00234    NS_INTERFACE_MAP_ENTRY(nsILinkHandler)
00235    NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands)
00236 NS_INTERFACE_MAP_END_INHERITING(nsDocShell)
00237 
00238 NS_IMETHODIMP
00239 nsWebShell::GetInterface(const nsIID &aIID, void** aInstancePtr)
00240 {
00241    NS_PRECONDITION(aInstancePtr, "null out param");
00242 
00243    *aInstancePtr = nsnull;
00244 
00245    if(aIID.Equals(NS_GET_IID(nsICommandManager)))
00246       {
00247       NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE);
00248       *aInstancePtr = mCommandManager;
00249       NS_ADDREF((nsISupports*) *aInstancePtr);
00250       return NS_OK;
00251       }
00252 
00253    return nsDocShell::GetInterface(aIID, aInstancePtr);
00254 }
00255 
00256 nsEventStatus PR_CALLBACK
00257 nsWebShell::HandleEvent(nsGUIEvent *aEvent)
00258 {
00259   return nsEventStatus_eIgnore;
00260 }
00261 
00262 
00263 //----------------------------------------------------------------------
00264 // Web Shell Services API
00265 
00266 //This functions is only called when a new charset is detected in loading a document. 
00267 //Its name should be changed to "CharsetReloadDocument"
00268 NS_IMETHODIMP
00269 nsWebShell::ReloadDocument(const char* aCharset,
00270                            PRInt32 aSource)
00271 {
00272 
00273   // XXX hack. kee the aCharset and aSource wait to pick it up
00274   nsCOMPtr<nsIContentViewer> cv;
00275   NS_ENSURE_SUCCESS(GetContentViewer(getter_AddRefs(cv)), NS_ERROR_FAILURE);
00276   if (cv)
00277   {
00278     nsCOMPtr<nsIMarkupDocumentViewer> muDV = do_QueryInterface(cv);  
00279     if (muDV)
00280     {
00281       PRInt32 hint;
00282       muDV->GetHintCharacterSetSource(&hint);
00283       if( aSource > hint ) 
00284       {
00285          muDV->SetHintCharacterSet(nsDependentCString(aCharset));
00286          muDV->SetHintCharacterSetSource(aSource);
00287          if(eCharsetReloadRequested != mCharsetReloadState) 
00288          {
00289             mCharsetReloadState = eCharsetReloadRequested;
00290             return Reload(LOAD_FLAGS_CHARSET_CHANGE);
00291          }
00292       }
00293     }
00294   }
00295   //return failer if this request is not accepted due to mCharsetReloadState
00296   return NS_ERROR_WEBSHELL_REQUEST_REJECTED;
00297 }
00298 
00299 
00300 NS_IMETHODIMP
00301 nsWebShell::StopDocumentLoad(void)
00302 {
00303   if(eCharsetReloadRequested != mCharsetReloadState) 
00304   {
00305     Stop(nsIWebNavigation::STOP_ALL);
00306     return NS_OK;
00307   }
00308   //return failer if this request is not accepted due to mCharsetReloadState
00309   return NS_ERROR_WEBSHELL_REQUEST_REJECTED;
00310 }
00311 
00312 NS_IMETHODIMP
00313 nsWebShell::SetRendering(PRBool aRender)
00314 {
00315   if(eCharsetReloadRequested != mCharsetReloadState) 
00316   {
00317     if (mContentViewer) {
00318        mContentViewer->SetEnableRendering(aRender);
00319        return NS_OK;
00320     }
00321   }
00322   //return failer if this request is not accepted due to mCharsetReloadState
00323   return NS_ERROR_WEBSHELL_REQUEST_REJECTED;
00324 }
00325 
00326 //----------------------------------------------------------------------
00327 
00328 // WebShell link handling
00329 
00330 struct OnLinkClickEvent : public PLEvent {
00331   OnLinkClickEvent(nsWebShell* aHandler, nsIContent* aContent,
00332                    nsLinkVerb aVerb, nsIURI* aURI,
00333                    const PRUnichar* aTargetSpec, nsIInputStream* aPostDataStream = 0, 
00334                    nsIInputStream* aHeadersDataStream = 0);
00335   ~OnLinkClickEvent();
00336 
00337   void HandleEvent() {
00338     nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mHandler->mScriptGlobal));
00339     nsAutoPopupStatePusher popupStatePusher(window, mPopupState);
00340 
00341     mHandler->OnLinkClickSync(mContent, mVerb, mURI,
00342                               mTargetSpec.get(), mPostDataStream,
00343                               mHeadersDataStream,
00344                               nsnull, nsnull);
00345   }
00346 
00347   nsWebShell*              mHandler;
00348   nsCOMPtr<nsIURI>         mURI;
00349   nsString                 mTargetSpec;
00350   nsCOMPtr<nsIInputStream> mPostDataStream;
00351   nsCOMPtr<nsIInputStream> mHeadersDataStream;
00352   nsCOMPtr<nsIContent>     mContent;
00353   nsLinkVerb               mVerb;
00354   PopupControlState        mPopupState;
00355 };
00356 
00357 static void PR_CALLBACK HandlePLEvent(OnLinkClickEvent* aEvent)
00358 {
00359   aEvent->HandleEvent();
00360 }
00361 
00362 static void PR_CALLBACK DestroyPLEvent(OnLinkClickEvent* aEvent)
00363 {
00364   delete aEvent;
00365 }
00366 
00367 OnLinkClickEvent::OnLinkClickEvent(nsWebShell* aHandler,
00368                                    nsIContent *aContent,
00369                                    nsLinkVerb aVerb,
00370                                    nsIURI* aURI,
00371                                    const PRUnichar* aTargetSpec,
00372                                    nsIInputStream* aPostDataStream,
00373                                    nsIInputStream* aHeadersDataStream)
00374 {
00375   mHandler = aHandler;
00376   NS_ADDREF(aHandler);
00377   mURI = aURI;
00378   mTargetSpec.Assign(aTargetSpec);
00379   mPostDataStream = aPostDataStream;
00380   mHeadersDataStream = aHeadersDataStream;
00381   mContent = aContent;
00382   mVerb = aVerb;
00383 
00384   nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mHandler->mScriptGlobal));
00385 
00386   mPopupState = window->GetPopupControlState();
00387 
00388   PL_InitEvent(this, nsnull,
00389                (PLHandleEventProc) ::HandlePLEvent,
00390                (PLDestroyEventProc) ::DestroyPLEvent);
00391 
00392   nsCOMPtr<nsIEventQueue> eventQueue;
00393   aHandler->GetEventQueue(getter_AddRefs(eventQueue));
00394   NS_ASSERTION(eventQueue, "no event queue");
00395   if (eventQueue)
00396     eventQueue->PostEvent(this);
00397 }
00398 
00399 OnLinkClickEvent::~OnLinkClickEvent()
00400 {
00401   NS_IF_RELEASE(mHandler);
00402 }
00403 
00404 //----------------------------------------
00405 
00406 NS_IMETHODIMP
00407 nsWebShell::OnLinkClick(nsIContent* aContent,
00408                         nsLinkVerb aVerb,
00409                         nsIURI* aURI,
00410                         const PRUnichar* aTargetSpec,
00411                         nsIInputStream* aPostDataStream,
00412                         nsIInputStream* aHeadersDataStream)
00413 {
00414   if (mFiredUnloadEvent) {
00415     return NS_OK;
00416   }
00417   
00418   OnLinkClickEvent* ev;
00419 
00420   ev = new OnLinkClickEvent(this, aContent, aVerb, aURI,
00421                             aTargetSpec, aPostDataStream, aHeadersDataStream);
00422   if (!ev) {
00423     return NS_ERROR_OUT_OF_MEMORY;
00424   }
00425   return NS_OK;
00426 }
00427 
00428 nsresult
00429 nsWebShell::GetEventQueue(nsIEventQueue **aQueue)
00430 {
00431   NS_ENSURE_ARG_POINTER(aQueue);
00432   *aQueue = 0;
00433 
00434   nsCOMPtr<nsIEventQueueService> eventService(do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID));
00435   if (eventService)
00436     eventService->GetThreadEventQueue(mThread, aQueue);
00437   return *aQueue ? NS_OK : NS_ERROR_FAILURE;
00438 }
00439 
00440 NS_IMETHODIMP
00441 nsWebShell::OnLinkClickSync(nsIContent *aContent,
00442                             nsLinkVerb aVerb,
00443                             nsIURI* aURI,
00444                             const PRUnichar* aTargetSpec,
00445                             nsIInputStream* aPostDataStream,
00446                             nsIInputStream* aHeadersDataStream,
00447                             nsIDocShell** aDocShell,
00448                             nsIRequest** aRequest)
00449 {
00450   // Initialize the DocShell / Request
00451   if (aDocShell) {
00452     *aDocShell = nsnull;
00453   }
00454   if (aRequest) {
00455     *aRequest = nsnull;
00456   }
00457 
00458   if (mFiredUnloadEvent) {
00459     return NS_OK;
00460   }
00461 
00462   {
00463     // defer to an external protocol handler if necessary...
00464     nsCOMPtr<nsIExternalProtocolService> extProtService = do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
00465     if (extProtService) {
00466       nsCAutoString scheme;
00467       aURI->GetScheme(scheme);
00468       if (!scheme.IsEmpty()) {
00469         // if the URL scheme does not correspond to an exposed protocol, then we
00470         // need to hand this link click over to the external protocol handler.
00471         PRBool isExposed;
00472         nsresult rv = extProtService->IsExposedProtocol(scheme.get(), &isExposed);
00473         if (NS_SUCCEEDED(rv) && !isExposed) {
00474           return extProtService->LoadUrl(aURI);
00475         }
00476       }
00477     }
00478   }
00479 
00480   nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aContent));
00481   NS_ENSURE_TRUE(node, NS_ERROR_UNEXPECTED);
00482 
00483   PRBool isJS = PR_FALSE;
00484   PRBool isData = PR_FALSE;
00485 
00486   aURI->SchemeIs("javascript", &isJS);
00487   aURI->SchemeIs("data", &isData);
00488 
00489   if (isJS || isData) {
00490     nsCOMPtr<nsIDocument> sourceDoc = aContent->GetDocument();
00491 
00492     if (!sourceDoc) {
00493       // The source is in a 'zombie' document, or not part of a
00494       // document any more. Don't let it execute any javascript in the
00495       // new document.
00496 
00497       return NS_OK;
00498     }
00499 
00500     nsCOMPtr<nsIPresShell> presShell;
00501     GetPresShell(getter_AddRefs(presShell));
00502     NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
00503 
00504     if (presShell->GetDocument() != sourceDoc) {
00505       // The source is not in the current document, don't let it
00506       // execute any javascript in the current document.
00507 
00508       return NS_OK;
00509     }
00510   }
00511 
00512   // Get the owner document of the link that was clicked, this will be
00513   // the document that the link is in, or the last document that the
00514   // link was in. From that document, we'll get the URI to use as the
00515   // referer, since the current URI in this webshell/docshell may be a
00516   // new document that we're in the process of loading.
00517   nsCOMPtr<nsIDOMDocument> refererOwnerDoc;
00518   node->GetOwnerDocument(getter_AddRefs(refererOwnerDoc));
00519 
00520   nsCOMPtr<nsIDocument> refererDoc(do_QueryInterface(refererOwnerDoc));
00521   NS_ENSURE_TRUE(refererDoc, NS_ERROR_UNEXPECTED);
00522 
00523   nsIURI *referer = refererDoc->GetDocumentURI();
00524 
00525   // referer could be null here in some odd cases, but that's ok,
00526   // we'll just load the link w/o sending a referer in those cases.
00527 
00528   nsAutoString target(aTargetSpec);
00529 
00530   // If this is an anchor element, grab its type property to use as a hint
00531   nsAutoString typeHint;
00532   nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(aContent));
00533   if (anchor) {
00534     anchor->GetType(typeHint);
00535   }
00536   
00537   switch(aVerb) {
00538     case eLinkVerb_New:
00539       NS_ASSERTION(target.IsEmpty(), "Losing window name information");
00540       target.AssignLiteral("_blank");
00541       // Fall into replace case
00542     case eLinkVerb_Undefined:
00543       // Fall through, this seems like the most reasonable action
00544     case eLinkVerb_Replace:
00545       {
00546         return InternalLoad(aURI,               // New URI
00547                             referer,            // Referer URI
00548                             nsnull,             // No onwer
00549                             INTERNAL_LOAD_FLAGS_INHERIT_OWNER, // Inherit owner from document
00550                             target.get(),       // Window target
00551                             NS_LossyConvertUCS2toASCII(typeHint).get(),
00552                             aPostDataStream,    // Post data stream
00553                             aHeadersDataStream, // Headers stream
00554                             LOAD_LINK,          // Load type
00555                             nsnull,             // No SHEntry
00556                             PR_TRUE,            // first party site
00557                             aDocShell,          // DocShell out-param
00558                             aRequest);          // Request out-param
00559       }
00560       break;
00561     case eLinkVerb_Embed:
00562       // XXX TODO Should be similar to the HTML IMG ALT attribute handling
00563       //          in NS 4.x
00564     default:
00565       NS_ABORT_IF_FALSE(0,"unexpected link verb");
00566       return NS_ERROR_UNEXPECTED;
00567   }
00568 }
00569 
00570 NS_IMETHODIMP
00571 nsWebShell::OnOverLink(nsIContent* aContent,
00572                        nsIURI* aURI,
00573                        const PRUnichar* aTargetSpec)
00574 {
00575   nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
00576   nsresult rv = NS_ERROR_FAILURE;
00577 
00578   if (browserChrome)  {
00579     nsCOMPtr<nsITextToSubURI> textToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
00580     if (NS_FAILED(rv)) return rv;
00581 
00582     // use url origin charset to unescape the URL
00583     nsCAutoString charset;
00584     rv = aURI->GetOriginCharset(charset);
00585     NS_ENSURE_SUCCESS(rv, rv);
00586 
00587     nsCAutoString spec;
00588     rv = aURI->GetSpec(spec);
00589     NS_ENSURE_SUCCESS(rv, rv);
00590 
00591     nsAutoString uStr;
00592     rv = textToSubURI->UnEscapeURIForUI(charset, spec, uStr);    
00593 
00594     if (NS_SUCCEEDED(rv))
00595       rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, uStr.get());
00596   }
00597   return rv;
00598 }
00599 
00600 NS_IMETHODIMP
00601 nsWebShell::OnLeaveLink()
00602 {
00603   nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
00604   nsresult rv = NS_ERROR_FAILURE;
00605 
00606   if (browserChrome)  {
00607       rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK,
00608                                     EmptyString().get());
00609   }
00610   return rv;
00611 }
00612 
00613 NS_IMETHODIMP
00614 nsWebShell::GetLinkState(nsIURI* aLinkURI, nsLinkState& aState)
00615 {
00616   if (!aLinkURI) {
00617     // No uri means not a link
00618     aState = eLinkState_NotLink;
00619     return NS_OK;
00620   }
00621     
00622   aState = eLinkState_Unvisited;
00623 
00624   // no history, leave state unchanged
00625   if (!mGlobalHistory)
00626     return NS_OK;
00627 
00628   PRBool isVisited;
00629   NS_ENSURE_SUCCESS(mGlobalHistory->IsVisited(aLinkURI, &isVisited),
00630                     NS_ERROR_FAILURE);
00631   if (isVisited)
00632     aState = eLinkState_Visited;
00633   
00634   return NS_OK;
00635 }
00636 
00637 //----------------------------------------------------------------------
00638 nsresult nsWebShell::EndPageLoad(nsIWebProgress *aProgress,
00639                                  nsIChannel* channel,
00640                                  nsresult aStatus)
00641 {
00642   nsresult rv = NS_OK;
00643 
00644   if(!channel)
00645     return NS_ERROR_NULL_POINTER;
00646     
00647   nsCOMPtr<nsIURI> url;
00648   rv = channel->GetURI(getter_AddRefs(url));
00649   if (NS_FAILED(rv)) return rv;
00650   
00651   // clean up reload state for meta charset
00652   if(eCharsetReloadRequested == mCharsetReloadState)
00653     mCharsetReloadState = eCharsetReloadStopOrigional;
00654   else 
00655     mCharsetReloadState = eCharsetReloadInit;
00656 
00657   // Save a pointer to the currently-loading history entry.
00658   // nsDocShell::EndPageLoad will clear mLSHE, but we may need this history
00659   // entry further down in this method.
00660   nsCOMPtr<nsISHEntry> loadingSHE = mLSHE;
00661   
00662   //
00663   // one of many safeguards that prevent death and destruction if
00664   // someone is so very very rude as to bring this window down
00665   // during this load handler.
00666   //
00667   nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
00668   nsDocShell::EndPageLoad(aProgress, channel, aStatus);
00669 
00670   // Test if this is the top frame or a subframe
00671   PRBool isTopFrame = PR_TRUE;
00672   nsCOMPtr<nsIDocShellTreeItem> targetParentTreeItem;
00673   rv = GetSameTypeParent(getter_AddRefs(targetParentTreeItem));
00674   if (NS_SUCCEEDED(rv) && targetParentTreeItem) 
00675   {
00676     isTopFrame = PR_FALSE;
00677   }
00678 
00679   //
00680   // If the page load failed, then deal with the error condition...
00681   // Errors are handled as follows:
00682   //   1. Check to see if it a file not found error.
00683   //   2. Send the URI to a keyword server (if enabled)
00684   //   3. If the error was DNS failure, then add www and .com to the URI
00685   //      (if appropriate).
00686   //   4. Throw an error dialog box...
00687   //
00688 
00689   if(url && NS_FAILED(aStatus)) {
00690     if (aStatus == NS_ERROR_FILE_NOT_FOUND) {
00691       DisplayLoadError(aStatus, url, nsnull, channel);
00692       return NS_OK;
00693     }  
00694 
00695     if (sURIFixup)
00696     {
00697       //
00698       // Try and make an alternative URI from the old one
00699       //
00700       nsCOMPtr<nsIURI> newURI;
00701 
00702       nsCAutoString oldSpec;
00703       url->GetSpec(oldSpec);
00704       
00705       //
00706       // First try keyword fixup
00707       //
00708       if (aStatus == NS_ERROR_UNKNOWN_HOST && mAllowKeywordFixup)
00709       {
00710         PRBool keywordsEnabled = PR_FALSE;
00711 
00712         if (mPrefs && NS_FAILED(mPrefs->GetBoolPref("keyword.enabled", &keywordsEnabled)))
00713             keywordsEnabled = PR_FALSE;
00714 
00715         nsCOMPtr<nsIURIFixup_MOZILLA_1_8_BRANCH> uriFix =
00716           do_QueryInterface(sURIFixup);
00717         if (!uriFix)
00718           keywordsEnabled = PR_FALSE;
00719 
00720         nsCAutoString host;
00721         url->GetHost(host);
00722 
00723         nsCAutoString scheme;
00724         url->GetScheme(scheme);
00725 
00726         PRInt32 dotLoc = host.FindChar('.');
00727 
00728         // we should only perform a keyword search under the following conditions:
00729         // (0) Pref keyword.enabled is true
00730         // (1) the url scheme is http (or https)
00731         // (2) the url does not have a protocol scheme
00732         // If we don't enforce such a policy, then we end up doing keyword searchs on urls
00733         // we don't intend like imap, file, mailbox, etc. This could lead to a security
00734         // problem where we send data to the keyword server that we shouldn't be. 
00735         // Someone needs to clean up keywords in general so we can determine on a per url basis
00736         // if we want keywords enabled...this is just a bandaid...
00737         if (keywordsEnabled && !scheme.IsEmpty() &&
00738            (scheme.Find("http") != 0)) {
00739             keywordsEnabled = PR_FALSE;
00740         }
00741 
00742         if(keywordsEnabled && (kNotFound == dotLoc)) {
00743           // only send non-qualified hosts to the keyword server
00744           //
00745           // If this string was passed through nsStandardURL by chance, then it
00746           // may have been converted from UTF-8 to ACE, which would result in a
00747           // completely bogus keyword query.  Here we try to recover the
00748           // original Unicode value, but this is not 100% correct since the
00749           // value may have been normalized per the IDN normalization rules.
00750           //
00751           // Since we don't have access to the exact original string that was
00752           // entered by the user, this will just have to do.
00753           //
00754           PRBool isACE;
00755           nsCAutoString utf8Host;
00756           nsCOMPtr<nsIIDNService> idnSrv =
00757               do_GetService(NS_IDNSERVICE_CONTRACTID);
00758           if (idnSrv &&
00759               NS_SUCCEEDED(idnSrv->IsACE(host, &isACE)) && isACE &&
00760               NS_SUCCEEDED(idnSrv->ConvertACEtoUTF8(host, utf8Host)))
00761             uriFix->KeywordToURI(utf8Host, getter_AddRefs(newURI));
00762           else
00763             uriFix->KeywordToURI(host, getter_AddRefs(newURI));
00764         } // end keywordsEnabled
00765       }
00766 
00767       //
00768       // Now try change the address, e.g. turn http://foo into http://www.foo.com
00769       //
00770       if (aStatus == NS_ERROR_UNKNOWN_HOST ||
00771           aStatus == NS_ERROR_NET_RESET)
00772       {
00773         PRBool doCreateAlternate = PR_TRUE;
00774         
00775         // Skip fixup for anything except a normal document load operation on
00776         // the topframe.
00777         
00778         if (mLoadType != LOAD_NORMAL || !isTopFrame)
00779         {
00780           doCreateAlternate = PR_FALSE;
00781         }
00782         else
00783         {
00784           // Test if keyword lookup produced a new URI or not
00785           if (newURI)
00786           {
00787             PRBool sameURI = PR_FALSE;
00788             url->Equals(newURI, &sameURI);
00789             if (!sameURI)
00790             {
00791               // Keyword lookup made a new URI so no need to try an
00792               // alternate one.
00793               doCreateAlternate = PR_FALSE;
00794             }
00795           }
00796         }
00797         if (doCreateAlternate)
00798         {
00799           newURI = nsnull;
00800           sURIFixup->CreateFixupURI(oldSpec,
00801               nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI, getter_AddRefs(newURI));
00802         }
00803       }
00804 
00805       //
00806       // Did we make a new URI that is different to the old one? If so load it.
00807       //
00808       if (newURI)
00809       {
00810         // Make sure the new URI is different from the old one, otherwise
00811         // there's little point trying to load it again.
00812         PRBool sameURI = PR_FALSE;
00813         url->Equals(newURI, &sameURI);
00814         if (!sameURI)
00815         {
00816           nsCAutoString newSpec;
00817           newURI->GetSpec(newSpec);
00818           NS_ConvertUTF8toUCS2 newSpecW(newSpec);
00819 
00820           // This seems evil, since it is modifying the original URL
00821           rv = url->SetSpec(newSpec);
00822           if (NS_FAILED(rv)) return rv;
00823 
00824           return LoadURI(newSpecW.get(),      // URI string
00825                          LOAD_FLAGS_NONE, // Load flags
00826                          nsnull,          // Referring URI
00827                          nsnull,          // Post data stream
00828                          nsnull);         // Headers stream
00829         }
00830       }
00831     }
00832 
00833     //
00834     // Well, fixup didn't work :-(
00835     // It is time to throw an error dialog box, and be done with it...
00836     //
00837 
00838     // Errors to be shown only on top-level frames
00839     if ((aStatus == NS_ERROR_UNKNOWN_HOST || 
00840          aStatus == NS_ERROR_CONNECTION_REFUSED ||
00841          aStatus == NS_ERROR_UNKNOWN_PROXY_HOST || 
00842          aStatus == NS_ERROR_PROXY_CONNECTION_REFUSED) &&
00843             (isTopFrame || mUseErrorPages)) {
00844       DisplayLoadError(aStatus, url, nsnull, channel);
00845     }
00846     // Errors to be shown for any frame
00847     else if (aStatus == NS_ERROR_NET_TIMEOUT ||
00848              aStatus == NS_ERROR_REDIRECT_LOOP ||
00849              aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE ||
00850              aStatus == NS_ERROR_NET_INTERRUPT ||
00851              aStatus == NS_ERROR_NET_RESET ||
00852              aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE) {
00853       DisplayLoadError(aStatus, url, nsnull, channel);
00854     }
00855     else if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) {
00856       /* A document that was requested to be fetched *only* from
00857        * the cache is not in cache. May be this is one of those 
00858        * postdata results. Throw a  dialog to the user,
00859        * saying that the page has expired from cache and ask if 
00860        * they wish to refetch the page from the net. Do this only
00861        * if the request is a form post.
00862        */
00863       nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
00864       nsCAutoString method;
00865       if (httpChannel)
00866         httpChannel->GetRequestMethod(method);
00867       if (method.Equals("POST") && !IsOffline()) {
00868         nsCOMPtr<nsIPrompt> prompter;
00869         PRBool repost;
00870         nsCOMPtr<nsIStringBundle> stringBundle;
00871         GetPromptAndStringBundle(getter_AddRefs(prompter), 
00872                                   getter_AddRefs(stringBundle));
00873    
00874         if (stringBundle && prompter) {
00875           nsXPIDLString messageStr;
00876           nsresult rv = stringBundle->GetStringFromName(NS_LITERAL_STRING("repost").get(), 
00877                                                         getter_Copies(messageStr));
00878             
00879           if (NS_SUCCEEDED(rv) && messageStr) {
00880             prompter->Confirm(nsnull, messageStr, &repost);
00881             /* If the user pressed cancel in the dialog, return. Don't
00882              * try to load the page with out the post data.
00883              */
00884             if (!repost)
00885               return NS_OK;
00886 
00887             // The user wants to repost the data to the server. 
00888             // If the page was loaded due to a back/forward/go
00889             // operation, update the session history index.
00890             // This is similar to the updating done in 
00891             // nsDocShell::OnNewURI() for regular pages          
00892             nsCOMPtr<nsISHistory> rootSH=mSessionHistory;
00893             if (!mSessionHistory) {
00894               nsCOMPtr<nsIDocShellTreeItem> root;
00895               //Get the root docshell
00896               GetSameTypeRootTreeItem(getter_AddRefs(root));
00897               if (root) {
00898                 // QI root to nsIWebNavigation
00899                 nsCOMPtr<nsIWebNavigation> rootAsWebnav(do_QueryInterface(root));
00900                 if (rootAsWebnav) {
00901                  // Get the handle to SH from the root docshell          
00902                  rootAsWebnav->GetSessionHistory(getter_AddRefs(rootSH));             
00903                 }
00904               }
00905             }  // mSessionHistory
00906 
00907             if (rootSH && (mLoadType & LOAD_CMD_HISTORY)) {
00908               nsCOMPtr<nsISHistoryInternal> shInternal(do_QueryInterface(rootSH));
00909               if (shInternal) {
00910                 rootSH->GetIndex(&mPreviousTransIndex);
00911                 shInternal->UpdateIndex();
00912                 rootSH->GetIndex(&mLoadedTransIndex);
00913 #ifdef DEBUG_PAGE_CACHE
00914                 printf("Previous index: %d, Loaded index: %d\n\n",
00915                        mPreviousTransIndex, mLoadedTransIndex);
00916 #endif
00917               }
00918             }
00919 
00920             // Make it look like we really did honestly finish loading the
00921             // history page we were loading, since the "reload" load we're
00922             // about to kick off will reload our current history entry.  This
00923             // is a bit of a hack, and if the force-load fails I think we'll
00924             // end up being confused about what page we're on... but we would
00925             // anyway, since we've updated the session history index above.
00926             SetHistoryEntry(&mOSHE, loadingSHE);
00927             
00928             /* The user does want to repost the data to the server.
00929              * Initiate a new load again.
00930              */
00931             /* Get the postdata if any from the channel */
00932             nsCOMPtr<nsIInputStream> inputStream;
00933             nsCOMPtr<nsIURI> referrer;
00934             if (channel) {
00935               nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
00936    
00937               if(httpChannel) {
00938                 httpChannel->GetReferrer(getter_AddRefs(referrer));
00939                 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(channel));
00940                 if (uploadChannel) {
00941                   uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
00942                 }
00943               }
00944             }
00945             nsCOMPtr<nsISeekableStream> postDataSeekable(do_QueryInterface(inputStream));
00946             if (postDataSeekable)
00947             {
00948                postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
00949             }
00950             InternalLoad(url,                               // URI
00951                          referrer,                          // Referring URI
00952                          nsnull,                            // Owner
00953                          INTERNAL_LOAD_FLAGS_INHERIT_OWNER, // Inherit owner
00954                          nsnull,                            // No window target
00955                          nsnull,                            // No type hint
00956                          inputStream,                       // Post data stream
00957                          nsnull,                            // No headers stream
00958                          LOAD_RELOAD_BYPASS_PROXY_AND_CACHE,// Load type
00959                          nsnull,                            // No SHEntry
00960                          PR_TRUE,                           // first party site
00961                          nsnull,                            // No nsIDocShell
00962                          nsnull);                           // No nsIRequest
00963           }
00964         }
00965       }
00966       else {
00967         DisplayLoadError(aStatus, url, nsnull, channel);
00968       }
00969     }
00970   } // if we have a host
00971 
00972   return NS_OK;
00973 }
00974 
00975 //
00976 // Routines for selection and clipboard
00977 //
00978 
00979 #ifdef XP_MAC
00980 #pragma mark -
00981 #endif
00982 
00983 nsresult
00984 nsWebShell::GetControllerForCommand ( const char * inCommand, nsIController** outController )
00985 {
00986   NS_ENSURE_ARG_POINTER(outController);
00987   *outController = nsnull;
00988   
00989   nsresult rv = NS_ERROR_FAILURE;
00990   
00991   nsCOMPtr<nsPIDOMWindow> window ( do_QueryInterface(mScriptGlobal) );
00992   if ( window ) {
00993     nsIFocusController *focusController = window->GetRootFocusController();
00994     if ( focusController )
00995       rv = focusController->GetControllerForCommand ( inCommand, outController );
00996   } // if window
00997 
00998   return rv;
00999   
01000 } // GetControllerForCommand
01001 
01002 
01003 nsresult
01004 nsWebShell::IsCommandEnabled ( const char * inCommand, PRBool* outEnabled )
01005 {
01006   NS_ENSURE_ARG_POINTER(outEnabled);
01007   *outEnabled = PR_FALSE;
01008 
01009   nsresult rv = NS_ERROR_FAILURE;
01010   
01011   nsCOMPtr<nsIController> controller;
01012   rv = GetControllerForCommand ( inCommand, getter_AddRefs(controller) );
01013   if ( controller )
01014     rv = controller->IsCommandEnabled(inCommand, outEnabled);
01015   
01016   return rv;
01017 }
01018 
01019 
01020 nsresult
01021 nsWebShell::DoCommand ( const char * inCommand )
01022 {
01023   nsresult rv = NS_ERROR_FAILURE;
01024   
01025   nsCOMPtr<nsIController> controller;
01026   rv = GetControllerForCommand ( inCommand, getter_AddRefs(controller) );
01027   if ( controller )
01028     rv = controller->DoCommand(inCommand);
01029   
01030   return rv;
01031 }
01032 
01033 
01034 NS_IMETHODIMP
01035 nsWebShell::CanCutSelection(PRBool* aResult)
01036 {
01037   return IsCommandEnabled ( "cmd_cut", aResult );
01038 }
01039 
01040 NS_IMETHODIMP
01041 nsWebShell::CanCopySelection(PRBool* aResult)
01042 {
01043   return IsCommandEnabled ( "cmd_copy", aResult );
01044 }
01045 
01046 NS_IMETHODIMP
01047 nsWebShell::CanCopyLinkLocation(PRBool* aResult)
01048 {
01049   return IsCommandEnabled ( "cmd_copyLink", aResult );
01050 }
01051 
01052 NS_IMETHODIMP
01053 nsWebShell::CanCopyImageLocation(PRBool* aResult)
01054 {
01055   return IsCommandEnabled ( "cmd_copyImageLocation",
01056                             aResult );
01057 }
01058 
01059 NS_IMETHODIMP
01060 nsWebShell::CanCopyImageContents(PRBool* aResult)
01061 {
01062   return IsCommandEnabled ( "cmd_copyImageContents",
01063                             aResult );
01064 }
01065 
01066 NS_IMETHODIMP
01067 nsWebShell::CanPaste(PRBool* aResult)
01068 {
01069   return IsCommandEnabled ( "cmd_paste", aResult );
01070 }
01071 
01072 NS_IMETHODIMP
01073 nsWebShell::CutSelection(void)
01074 {
01075   return DoCommand ( "cmd_cut" );
01076 }
01077 
01078 NS_IMETHODIMP
01079 nsWebShell::CopySelection(void)
01080 {
01081   return DoCommand ( "cmd_copy" );
01082 }
01083 
01084 NS_IMETHODIMP
01085 nsWebShell::CopyLinkLocation(void)
01086 {
01087   return DoCommand ( "cmd_copyLink" );
01088 }
01089 
01090 NS_IMETHODIMP
01091 nsWebShell::CopyImageLocation(void)
01092 {
01093   return DoCommand ( "cmd_copyImageLocation" );
01094 }
01095 
01096 NS_IMETHODIMP
01097 nsWebShell::CopyImageContents(void)
01098 {
01099   return DoCommand ( "cmd_copyImageContents" );
01100 }
01101 
01102 NS_IMETHODIMP
01103 nsWebShell::Paste(void)
01104 {
01105   return DoCommand ( "cmd_paste" );
01106 }
01107 
01108 NS_IMETHODIMP
01109 nsWebShell::SelectAll(void)
01110 {
01111   return DoCommand ( "cmd_selectAll" );
01112 }
01113 
01114 
01115 //
01116 // SelectNone
01117 //
01118 // Collapses the current selection, insertion point ends up at beginning
01119 // of previous selection.
01120 //
01121 NS_IMETHODIMP
01122 nsWebShell::SelectNone(void)
01123 {
01124   return DoCommand ( "cmd_selectNone" );
01125 }
01126 
01127 
01128 #ifdef XP_MAC
01129 #pragma mark -
01130 #endif
01131 
01132 //*****************************************************************************
01133 // nsWebShell::nsIBaseWindow
01134 //*****************************************************************************   
01135 
01136 NS_IMETHODIMP nsWebShell::Create()
01137 {
01138   if (mPrefs) {
01139     // We've already been created
01140     return NS_OK;
01141   }
01142   
01143   // Remember the current thread (in current and forseeable implementations,
01144   // it'll just be the unique UI thread)
01145   //
01146   // Since this call must be made on the UI thread, we know the Event Queue
01147   // will be associated with the current thread...
01148   //
01149   mThread = PR_GetCurrentThread();
01150 
01151   WEB_TRACE(WEB_TRACE_CALLS,
01152             ("nsWebShell::Init: this=%p", this));
01153 
01154   return nsDocShell::Create();
01155 }
01156 
01157 #ifdef DEBUG
01158 unsigned long nsWebShell::gNumberOfWebShells = 0;
01159 #endif