Back to index

lightning-sunbird  0.9+nobinonly
nsWebShellWindow.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  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 
00040 #include "nsWebShellWindow.h"
00041 
00042 #include "nsLayoutCID.h"
00043 #include "nsContentCID.h"
00044 #include "nsIWeakReference.h"
00045 #include "nsIDocumentLoader.h"
00046 
00047 #include "nsIComponentManager.h"
00048 #include "nsIServiceManager.h"
00049 #include "nsIURL.h"
00050 #include "nsIIOService.h"
00051 #include "nsIURL.h"
00052 #include "nsNetCID.h"
00053 #include "nsIStringBundle.h"
00054 #include "nsIPref.h"
00055 #include "nsReadableUtils.h"
00056 
00057 #include "nsEscape.h"
00058 #include "nsIScriptGlobalObject.h"
00059 #include "nsIDOMWindowInternal.h"
00060 #include "nsPIDOMWindow.h"
00061 #include "nsIDOMEventTarget.h"
00062 #include "nsIDOMFocusListener.h"
00063 #include "nsIWebNavigation.h"
00064 #include "nsIWindowWatcher.h"
00065 
00066 #include "nsIDOMXULElement.h"
00067 
00068 #include "nsGUIEvent.h"
00069 #include "nsWidgetsCID.h"
00070 #include "nsIWidget.h"
00071 #include "nsIAppShell.h"
00072 
00073 #include "nsIAppShellService.h"
00074 
00075 #include "nsIDOMCharacterData.h"
00076 #include "nsIDOMNodeList.h"
00077 
00078 #include "nsIMenuBar.h"
00079 #include "nsIMenu.h"
00080 #include "nsIMenuItem.h"
00081 #include "nsIMenuListener.h"
00082 #include "nsITimer.h"
00083 
00084 // For JS Execution
00085 #include "nsIScriptGlobalObjectOwner.h"
00086 #include "nsIJSContextStack.h"
00087 
00088 #include "nsIEventQueueService.h"
00089 #include "plevent.h"
00090 #include "prmem.h"
00091 #include "prlock.h"
00092 
00093 #include "nsIDOMXULDocument.h"
00094 
00095 #include "nsIFocusController.h"
00096 
00097 #include "nsIWebProgress.h"
00098 #include "nsIWebProgressListener.h"
00099 
00100 #include "nsIDocumentViewer.h"
00101 #include "nsIDocument.h"
00102 #include "nsIDOMDocument.h"
00103 #include "nsIDOMNode.h"
00104 #include "nsIDOMElement.h"
00105 #include "nsIDocumentLoader.h"
00106 #include "nsIDocumentLoaderFactory.h"
00107 #include "nsIObserverService.h"
00108 #include "prprf.h"
00109 //#include "nsIDOMHTMLInputElement.h"
00110 //#include "nsIDOMHTMLImageElement.h"
00111 
00112 #include "nsIContent.h" // for menus
00113 
00114 // For calculating size
00115 #include "nsIFrame.h"
00116 #include "nsIPresShell.h"
00117 #include "nsPresContext.h"
00118 
00119 #include "nsIBaseWindow.h"
00120 #include "nsIDocShellTreeItem.h"
00121 #include "nsIDocShellTreeNode.h"
00122 
00123 #include "nsIMarkupDocumentViewer.h"
00124 
00125 
00126 // HACK for M4, should be removed by M5
00127 // ... its now M15
00128 #if defined(XP_MAC) || defined(XP_MACOSX)
00129 #include <Menus.h>
00130 #endif
00131 #include "nsIMenuItem.h"
00132 #include "nsIDOMXULDocument.h"
00133 // End hack
00134 
00135 #if defined(XP_MAC) || defined(XP_MACOSX)
00136 #define USE_NATIVE_MENUS
00137 #endif
00138 
00139 #include "nsIPopupSetFrame.h"
00140 
00141 /* Define Class IDs */
00142 static NS_DEFINE_CID(kWindowCID,           NS_WINDOW_CID);
00143 
00144 #include "nsWidgetsCID.h"
00145 static NS_DEFINE_CID(kMenuBarCID,          NS_MENUBAR_CID);
00146 
00147 #define SIZE_PERSISTENCE_TIMEOUT 500 // msec
00148 
00149 nsWebShellWindow::nsWebShellWindow() : nsXULWindow()
00150 {
00151   mSPTimerLock = PR_NewLock();
00152 }
00153 
00154 
00155 nsWebShellWindow::~nsWebShellWindow()
00156 {
00157   if (mWindow)
00158     mWindow->SetClientData(0);
00159   mWindow = nsnull; // Force release here.
00160 
00161   if (mSPTimerLock) {
00162     PR_Lock(mSPTimerLock);
00163     if (mSPTimer)
00164       mSPTimer->Cancel();
00165     PR_Unlock(mSPTimerLock);
00166     PR_DestroyLock(mSPTimerLock);
00167   }
00168 }
00169 
00170 NS_IMPL_ADDREF_INHERITED(nsWebShellWindow, nsXULWindow)
00171 NS_IMPL_RELEASE_INHERITED(nsWebShellWindow, nsXULWindow)
00172 
00173 NS_INTERFACE_MAP_BEGIN(nsWebShellWindow)
00174   NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
00175 NS_INTERFACE_MAP_END_INHERITING(nsXULWindow)
00176 
00177 nsresult nsWebShellWindow::Initialize(nsIXULWindow* aParent,
00178                                       nsIAppShell* aShell, nsIURI* aUrl, 
00179                                       PRInt32 aInitialWidth,
00180                                       PRInt32 aInitialHeight,
00181                                       PRBool aIsHiddenWindow,
00182                                       nsWidgetInitData& widgetInitData)
00183 {
00184   nsresult rv;
00185   nsCOMPtr<nsIWidget> parentWidget;
00186 
00187   mIsHiddenWindow = aIsHiddenWindow;
00188   
00189   // XXX: need to get the default window size from prefs...
00190   // Doesn't come from prefs... will come from CSS/XUL/RDF
00191   nsRect r(0, 0, aInitialWidth, aInitialHeight);
00192   
00193   // Create top level window
00194   mWindow = do_CreateInstance(kWindowCID, &rv);
00195   if (NS_OK != rv) {
00196     return rv;
00197   }
00198 
00199   /* This next bit is troublesome. We carry two different versions of a pointer
00200      to our parent window. One is the parent window's widget, which is passed
00201      to our own widget. The other is a weak reference we keep here to our
00202      parent WebShellWindow. The former is useful to the widget, and we can't
00203      trust its treatment of the parent reference because they're platform-
00204      specific. The latter is useful to this class.
00205        A better implementation would be one in which the parent keeps strong
00206      references to its children and closes them before it allows itself
00207      to be closed. This would mimic the behaviour of OSes that support
00208      top-level child windows in OSes that do not. Later.
00209   */
00210   nsCOMPtr<nsIBaseWindow> parentAsWin(do_QueryInterface(aParent));
00211   if (parentAsWin) {
00212     parentAsWin->GetMainWidget(getter_AddRefs(parentWidget));
00213     mParentWindow = do_GetWeakReference(aParent);
00214   }
00215 
00216   mWindow->SetClientData(this);
00217   mWindow->Create((nsIWidget *)parentWidget,          // Parent nsIWidget
00218                   r,                                  // Widget dimensions
00219                   nsWebShellWindow::HandleEvent,      // Event handler function
00220                   nsnull,                             // Device context
00221                   aShell,                             // Application shell
00222                   nsnull,                             // nsIToolkit
00223                   &widgetInitData);                   // Widget initialization data
00224   mWindow->GetClientBounds(r);
00225   mWindow->SetBackgroundColor(NS_RGB(192,192,192));
00226 
00227   // Create web shell
00228   mDocShell = do_CreateInstance("@mozilla.org/webshell;1");
00229   NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
00230 
00231   // Make sure to set the item type on the docshell _before_ calling
00232   // Create() so it knows what type it is.
00233   nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(do_QueryInterface(mDocShell));
00234   NS_ENSURE_TRUE(docShellAsItem, NS_ERROR_FAILURE);
00235   NS_ENSURE_SUCCESS(EnsureChromeTreeOwner(), NS_ERROR_FAILURE);
00236 
00237   docShellAsItem->SetTreeOwner(mChromeTreeOwner);
00238   docShellAsItem->SetItemType(nsIDocShellTreeItem::typeChrome);
00239 
00240   r.x = r.y = 0;
00241   nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell));
00242   NS_ENSURE_SUCCESS(docShellAsWin->InitWindow(nsnull, mWindow, 
00243    r.x, r.y, r.width, r.height), NS_ERROR_FAILURE);
00244   NS_ENSURE_SUCCESS(docShellAsWin->Create(), NS_ERROR_FAILURE);
00245 
00246   // Attach a WebProgress listener.during initialization...
00247   nsCOMPtr<nsIWebProgress> webProgress(do_GetInterface(mDocShell, &rv));
00248   if (webProgress) {
00249     webProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_NETWORK);
00250   }
00251 
00252   if (nsnull != aUrl)  {
00253     nsCAutoString tmpStr;
00254 
00255     rv = aUrl->GetSpec(tmpStr);
00256     if (NS_FAILED(rv)) return rv;
00257 
00258     NS_ConvertUTF8toUCS2 urlString(tmpStr);
00259     nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
00260     NS_ENSURE_TRUE(webNav, NS_ERROR_FAILURE);
00261     rv = webNav->LoadURI(urlString.get(),
00262                          nsIWebNavigation::LOAD_FLAGS_NONE,
00263                          nsnull,
00264                          nsnull,
00265                          nsnull);
00266     NS_ENSURE_SUCCESS(rv, rv);
00267   }
00268                      
00269   return rv;
00270 }
00271 
00272 
00273 /*
00274  * Toolbar
00275  */
00276 nsresult
00277 nsWebShellWindow::Toolbar()
00278 {
00279     nsCOMPtr<nsIXULWindow> kungFuDeathGrip(this);
00280     nsCOMPtr<nsIWebBrowserChrome> wbc(do_GetInterface(kungFuDeathGrip));
00281     if (!wbc)
00282       return NS_ERROR_UNEXPECTED;
00283 
00284     // rjc: don't use "nsIWebBrowserChrome::CHROME_EXTRA"
00285     //      due to components with multiple sidebar components
00286     //      (such as Mail/News, Addressbook, etc)... and frankly,
00287     //      Mac IE, OmniWeb, and other Mac OS X apps all work this way
00288 
00289     PRUint32    chromeMask = (nsIWebBrowserChrome::CHROME_TOOLBAR |
00290                               nsIWebBrowserChrome::CHROME_LOCATIONBAR |
00291                               nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR);
00292 
00293     PRUint32    chromeFlags, newChromeFlags = 0;
00294     wbc->GetChromeFlags(&chromeFlags);
00295     newChromeFlags = chromeFlags & chromeMask;
00296     if (!newChromeFlags)    chromeFlags |= chromeMask;
00297     else                    chromeFlags &= (~newChromeFlags);
00298     wbc->SetChromeFlags(chromeFlags);
00299     return NS_OK;
00300 }
00301 
00302 
00303 /*
00304  * Event handler function...
00305  *
00306  * This function is called to process events for the nsIWidget of the 
00307  * nsWebShellWindow...
00308  */
00309 nsEventStatus PR_CALLBACK
00310 nsWebShellWindow::HandleEvent(nsGUIEvent *aEvent)
00311 {
00312   nsEventStatus result = nsEventStatus_eIgnore;
00313   nsIDocShell* docShell = nsnull;
00314   nsWebShellWindow *eventWindow = nsnull;
00315 
00316   // Get the WebShell instance...
00317   if (nsnull != aEvent->widget) {
00318     void* data;
00319 
00320     aEvent->widget->GetClientData(data);
00321     if (data != nsnull) {
00322       eventWindow = NS_REINTERPRET_CAST(nsWebShellWindow *, data);
00323       docShell = eventWindow->mDocShell;
00324     }
00325   }
00326 
00327   if (docShell) {
00328     switch(aEvent->message) {
00329       /*
00330        * For size events, the DocShell must be resized to fill the entire
00331        * client area of the window...
00332        */
00333       case NS_MOVE: {
00334         // persist position, but not immediately, in case this OS is firing
00335         // repeated move events as the user drags the window
00336         eventWindow->SetPersistenceTimer(PAD_POSITION);
00337         break;
00338       }
00339       case NS_SIZE: {
00340         nsSizeEvent* sizeEvent = (nsSizeEvent*)aEvent;
00341         nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(docShell));
00342         shellAsWin->SetPositionAndSize(0, 0, sizeEvent->windowSize->width, 
00343           sizeEvent->windowSize->height, PR_FALSE);  
00344         // persist size, but not immediately, in case this OS is firing
00345         // repeated size events as the user drags the sizing handle
00346         if (!eventWindow->IsLocked())
00347           eventWindow->SetPersistenceTimer(PAD_SIZE | PAD_MISC);
00348         result = nsEventStatus_eConsumeNoDefault;
00349         break;
00350       }
00351       case NS_SIZEMODE: {
00352         nsSizeModeEvent* modeEvent = (nsSizeModeEvent*)aEvent;
00353 
00354         // an alwaysRaised (or higher) window will hide any newly opened
00355         // normal browser windows. here we just drop a raised window
00356         // to the normal zlevel if it's maximized. we make no provision
00357         // for automatically re-raising it when restored.
00358         if (modeEvent->mSizeMode == nsSizeMode_Maximized) {
00359           PRUint32 zLevel;
00360           eventWindow->GetZLevel(&zLevel);
00361           if (zLevel > nsIXULWindow::normalZ)
00362             eventWindow->SetZLevel(nsIXULWindow::normalZ);
00363         }
00364 
00365         aEvent->widget->SetSizeMode(modeEvent->mSizeMode);
00366 
00367         // persist mode, but not immediately, because in many (all?)
00368         // cases this will merge with the similar call in NS_SIZE and
00369         // write the attribute values only once.
00370         eventWindow->SetPersistenceTimer(PAD_MISC);
00371         result = nsEventStatus_eConsumeDoDefault;
00372 
00373         // Note the current implementation of SetSizeMode just stores
00374         // the new state; it doesn't actually resize. So here we store
00375         // the state and pass the event on to the OS. The day is coming
00376         // when we'll handle the event here, and the return result will
00377         // then need to be different.
00378 #ifdef XP_WIN
00379         // This is a nasty hack to get around the fact that win32 sends the kill focus
00380         // event in a different sequence than the deactivate depending on if you're
00381         // minimizing the window vs. just clicking in a different window to cause
00382         // the deactivation. Bug #82534
00383         if(modeEvent->mSizeMode == nsSizeMode_Minimized) {
00384           nsCOMPtr<nsPIDOMWindow> privateDOMWindow = do_GetInterface(docShell);
00385           if(privateDOMWindow) {
00386             nsIFocusController *focusController =
00387               privateDOMWindow->GetRootFocusController();
00388             if (focusController)
00389               focusController->RewindFocusState();
00390           }
00391         }
00392 #endif
00393         break;
00394       }
00395       case NS_OS_TOOLBAR: {
00396         nsCOMPtr<nsIXULWindow> kungFuDeathGrip(eventWindow);
00397         eventWindow->Toolbar();
00398         break;
00399       }
00400       case NS_XUL_CLOSE: {
00401         // Calling ExecuteCloseHandler may actually close the window
00402         // (it probably shouldn't, but you never know what the users JS 
00403         // code will do).  Therefore we add a death-grip to the window
00404         // for the duration of the close handler.
00405         nsCOMPtr<nsIXULWindow> kungFuDeathGrip(eventWindow);
00406         if (!eventWindow->ExecuteCloseHandler())
00407           eventWindow->Destroy();
00408         break;
00409       }
00410       /*
00411        * Notify the ApplicationShellService that the window is being closed...
00412        */
00413       case NS_DESTROY: {
00414         eventWindow->Destroy();
00415         break;
00416       }
00417 
00418       case NS_SETZLEVEL: {
00419         nsZLevelEvent *zEvent = (nsZLevelEvent *) aEvent;
00420 
00421         zEvent->mAdjusted = eventWindow->ConstrainToZLevel(zEvent->mImmediate,
00422                               &zEvent->mPlacement,
00423                               zEvent->mReqBelow, &zEvent->mActualBelow);
00424         break;
00425       }
00426 
00427       case NS_MOUSE_ACTIVATE:{
00428         break;
00429       }
00430       
00431       case NS_ACTIVATE: {
00432 #ifdef DEBUG_saari
00433         printf("nsWebShellWindow::NS_ACTIVATE\n");
00434 #endif
00435         nsCOMPtr<nsPIDOMWindow> privateDOMWindow = do_GetInterface(docShell);
00436         if (privateDOMWindow)
00437           privateDOMWindow->Activate();
00438 
00439         break;
00440       }
00441 
00442       case NS_DEACTIVATE: {
00443 #ifdef DEBUG_saari
00444         printf("nsWebShellWindow::NS_DEACTIVATE\n");
00445 #endif
00446 
00447         nsCOMPtr<nsPIDOMWindow> privateDOMWindow = do_GetInterface(docShell);
00448         if (privateDOMWindow) {
00449           nsIFocusController *focusController =
00450             privateDOMWindow->GetRootFocusController();
00451           if (focusController)
00452             focusController->SetActive(PR_FALSE);
00453 
00454           privateDOMWindow->Deactivate();
00455         }
00456         break;
00457       }
00458       
00459       case NS_GOTFOCUS: {
00460 #ifdef DEBUG_saari
00461         printf("nsWebShellWindow::GOTFOCUS\n");
00462 #endif
00463         nsCOMPtr<nsIDOMDocument> domDocument;
00464         nsCOMPtr<nsPIDOMWindow> piWin = do_GetInterface(docShell);
00465         if (!piWin) {
00466           break;
00467         }
00468         nsIFocusController *focusController = piWin->GetRootFocusController();
00469         if (focusController) {
00470           // This is essentially the first stage of activation (NS_GOTFOCUS is
00471           // followed by the DOM window getting activated (which is direct on Win32
00472           // and done through web shell window via an NS_ACTIVATE message on the
00473           // other platforms).
00474           //
00475           // Go ahead and mark the focus controller as being active.  We have
00476           // to do this even before the activate message comes in, since focus
00477           // memory kicks in prior to the activate being processed.
00478           focusController->SetActive(PR_TRUE);
00479 
00480           nsCOMPtr<nsIDOMWindowInternal> focusedWindow;
00481           focusController->GetFocusedWindow(getter_AddRefs(focusedWindow));
00482           if (focusedWindow) {
00483             // It's possible for focusing the window to cause it to close.
00484             // To avoid holding a pointer to deleted memory, keep a reference
00485             // on eventWindow. -bryner
00486             nsCOMPtr<nsIXULWindow> kungFuDeathGrip(eventWindow);
00487 
00488             nsCOMPtr<nsIDOMWindowInternal> domWindow = 
00489               do_QueryInterface(piWin);
00490 
00491             focusController->SetSuppressFocus(PR_TRUE, "Activation Suppression");
00492 
00493             NS_ASSERTION(domWindow,
00494                          "windows must support nsIDOMWindowInternal");
00495 
00496             domWindow->Focus(); // This sets focus, but we'll ignore it.  
00497                                 // A subsequent activate will cause us to stop suppressing.
00498 
00499             // since the window has been activated, replace persistent size data
00500             // with the newly activated window's
00501             if (eventWindow->mChromeLoaded) {
00502               eventWindow->PersistentAttributesDirty(
00503                              PAD_POSITION | PAD_SIZE | PAD_MISC);
00504               eventWindow->SavePersistentAttributes();
00505             }
00506 
00507             break;
00508           }
00509         }
00510         break;
00511       }
00512       default:
00513         break;
00514 
00515     }
00516   }
00517   return result;
00518 }
00519 
00520 //----------------------------------------
00521 void nsWebShellWindow::LoadNativeMenus(nsIDOMDocument *aDOMDoc,
00522                                        nsIWidget *aParentWindow) 
00523 {
00524   // Find the menubar tag (if there is more than one, we ignore all but
00525   // the first).
00526   nsCOMPtr<nsIDOMNodeList> menubarElements;
00527   aDOMDoc->GetElementsByTagNameNS(NS_LITERAL_STRING("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"),
00528                                   NS_LITERAL_STRING("menubar"),
00529                                   getter_AddRefs(menubarElements));
00530 
00531   
00532   nsCOMPtr<nsIDOMNode> menubarNode;
00533   if (menubarElements)
00534     menubarElements->Item(0, getter_AddRefs(menubarNode));
00535 
00536   if (!menubarNode)
00537     return;
00538 
00539   nsCOMPtr<nsIMenuBar> pnsMenuBar = do_CreateInstance(kMenuBarCID);
00540   if (!pnsMenuBar)
00541     return;
00542 
00543   // set pnsMenuBar as a nsMenuListener on aParentWindow
00544   nsCOMPtr<nsIMenuListener> menuListener = do_QueryInterface(pnsMenuBar);
00545 
00546   // fake event
00547   nsMenuEvent fake(PR_TRUE, 0, nsnull);
00548   menuListener->MenuConstruct(fake, aParentWindow, menubarNode, mDocShell);
00549 }
00550 
00551 void
00552 nsWebShellWindow::SetPersistenceTimer(PRUint32 aDirtyFlags)
00553 {
00554   if (!mSPTimerLock)
00555     return;
00556 
00557   PR_Lock(mSPTimerLock);
00558   if (mSPTimer) {
00559     mSPTimer->SetDelay(SIZE_PERSISTENCE_TIMEOUT);
00560     PersistentAttributesDirty(aDirtyFlags);
00561   } else {
00562     nsresult rv;
00563     mSPTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
00564     if (NS_SUCCEEDED(rv)) {
00565       NS_ADDREF_THIS(); // for the timer, which holds a reference to this window
00566       mSPTimer->InitWithFuncCallback(FirePersistenceTimer, this,
00567                                      SIZE_PERSISTENCE_TIMEOUT, nsITimer::TYPE_ONE_SHOT);
00568       PersistentAttributesDirty(aDirtyFlags);
00569     }
00570   }
00571   PR_Unlock(mSPTimerLock);
00572 }
00573 
00574 void
00575 nsWebShellWindow::FirePersistenceTimer(nsITimer *aTimer, void *aClosure)
00576 {
00577   nsWebShellWindow *win = NS_STATIC_CAST(nsWebShellWindow *, aClosure);
00578   if (!win->mSPTimerLock)
00579     return;
00580   PR_Lock(win->mSPTimerLock);
00581   win->SavePersistentAttributes();
00582   PR_Unlock(win->mSPTimerLock);
00583 }
00584 
00585 
00586 //----------------------------------------
00587 // nsIWebProgessListener implementation
00588 //----------------------------------------
00589 NS_IMETHODIMP
00590 nsWebShellWindow::OnProgressChange(nsIWebProgress *aProgress,
00591                                    nsIRequest *aRequest,
00592                                    PRInt32 aCurSelfProgress,
00593                                    PRInt32 aMaxSelfProgress,
00594                                    PRInt32 aCurTotalProgress,
00595                                    PRInt32 aMaxTotalProgress)
00596 {
00597   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
00598   return NS_OK;
00599 }
00600 
00601 NS_IMETHODIMP
00602 nsWebShellWindow::OnStateChange(nsIWebProgress *aProgress,
00603                                 nsIRequest *aRequest,
00604                                 PRUint32 aStateFlags,
00605                                 nsresult aStatus)
00606 {
00607   // If the notification is not about a document finishing, then just
00608   // ignore it...
00609   if (!(aStateFlags & nsIWebProgressListener::STATE_STOP) || 
00610       !(aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK)) {
00611     return NS_OK;
00612   }
00613 
00614   if (mChromeLoaded)
00615     return NS_OK;
00616 
00617   // If this document notification is for a frame then ignore it...
00618   nsCOMPtr<nsIDOMWindow> eventWin;
00619   aProgress->GetDOMWindow(getter_AddRefs(eventWin));
00620   nsCOMPtr<nsPIDOMWindow> eventPWin(do_QueryInterface(eventWin));
00621   if (eventPWin) {
00622     nsPIDOMWindow *rootPWin = eventPWin->GetPrivateRoot();
00623     if (eventPWin != rootPWin)
00624       return NS_OK;
00625   }
00626 
00627   mChromeLoaded = PR_TRUE;
00628   mLockedUntilChromeLoad = PR_FALSE;
00629 
00630 #ifdef USE_NATIVE_MENUS
00631 
00632   // Find the Menubar DOM  and Load the menus, hooking them up to the loaded commands
00634   nsCOMPtr<nsIDOMDocument> menubarDOMDoc(GetNamedDOMDoc(NS_LITERAL_STRING("this"))); // XXX "this" is a small kludge for code reused
00635   if (menubarDOMDoc)
00636   {
00637     LoadNativeMenus(menubarDOMDoc, mWindow);
00638   }
00639 #endif // USE_NATIVE_MENUS
00640 
00641   OnChromeLoaded();
00642   LoadContentAreas();
00643 
00644   return NS_OK;
00645 }
00646 
00647 NS_IMETHODIMP
00648 nsWebShellWindow::OnLocationChange(nsIWebProgress *aProgress,
00649                                    nsIRequest *aRequest,
00650                                    nsIURI *aURI)
00651 {
00652   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
00653   return NS_OK;
00654 }
00655 
00656 NS_IMETHODIMP 
00657 nsWebShellWindow::OnStatusChange(nsIWebProgress* aWebProgress,
00658                                  nsIRequest* aRequest,
00659                                  nsresult aStatus,
00660                                  const PRUnichar* aMessage)
00661 {
00662   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
00663   return NS_OK;
00664 }
00665 
00666 NS_IMETHODIMP
00667 nsWebShellWindow::OnSecurityChange(nsIWebProgress *aWebProgress,
00668                                    nsIRequest *aRequest,
00669                                    PRUint32 state)
00670 {
00671   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
00672   return NS_OK;
00673 }
00674 
00675 
00676 //----------------------------------------
00677 nsCOMPtr<nsIDOMDocument> nsWebShellWindow::GetNamedDOMDoc(const nsAString & aDocShellName)
00678 {
00679   nsCOMPtr<nsIDOMDocument> domDoc; // result == nsnull;
00680 
00681   // first get the toolbar child docShell
00682   nsCOMPtr<nsIDocShell> childDocShell;
00683   if (aDocShellName.EqualsLiteral("this")) { // XXX small kludge for code reused
00684     childDocShell = mDocShell;
00685   } else {
00686     nsCOMPtr<nsIDocShellTreeItem> docShellAsItem;
00687     nsCOMPtr<nsIDocShellTreeNode> docShellAsNode(do_QueryInterface(mDocShell));
00688     docShellAsNode->FindChildWithName(PromiseFlatString(aDocShellName).get(), 
00689       PR_TRUE, PR_FALSE, nsnull, nsnull, getter_AddRefs(docShellAsItem));
00690     childDocShell = do_QueryInterface(docShellAsItem);
00691     if (!childDocShell)
00692       return domDoc;
00693   }
00694   
00695   nsCOMPtr<nsIContentViewer> cv;
00696   childDocShell->GetContentViewer(getter_AddRefs(cv));
00697   if (!cv)
00698     return domDoc;
00699    
00700   nsCOMPtr<nsIDocumentViewer> docv(do_QueryInterface(cv));
00701   if (!docv)
00702     return domDoc;
00703 
00704   nsCOMPtr<nsIDocument> doc;
00705   docv->GetDocument(getter_AddRefs(doc));
00706   if (doc)
00707     return nsCOMPtr<nsIDOMDocument>(do_QueryInterface(doc));
00708 
00709   return domDoc;
00710 } // nsWebShellWindow::GetNamedDOMDoc
00711 
00712 //----------------------------------------
00713 
00714 // if the main document URL specified URLs for any content areas, start them loading
00715 void nsWebShellWindow::LoadContentAreas() {
00716 
00717   nsAutoString searchSpec;
00718 
00719   // fetch the chrome document URL
00720   nsCOMPtr<nsIContentViewer> contentViewer;
00721   // yes, it's possible for the docshell to be null even this early
00722   // see bug 57514.
00723   if (mDocShell)
00724     mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
00725   if (contentViewer) {
00726     nsCOMPtr<nsIDocumentViewer> docViewer = do_QueryInterface(contentViewer);
00727     if (docViewer) {
00728       nsCOMPtr<nsIDocument> doc;
00729       docViewer->GetDocument(getter_AddRefs(doc));
00730       nsIURI *mainURL = doc->GetDocumentURI();
00731 
00732       nsCOMPtr<nsIURL> url = do_QueryInterface(mainURL);
00733       if (url) {
00734         nsCAutoString search;
00735         url->GetQuery(search);
00736 
00737         AppendUTF8toUTF16(search, searchSpec);
00738       }
00739     }
00740   }
00741 
00742   // content URLs are specified in the search part of the URL
00743   // as <contentareaID>=<escapedURL>[;(repeat)]
00744   if (!searchSpec.IsEmpty()) {
00745     PRInt32     begPos,
00746                 eqPos,
00747                 endPos;
00748     nsString    contentAreaID,
00749                 contentURL;
00750     char        *urlChar;
00751     nsresult rv;
00752     for (endPos = 0; endPos < (PRInt32)searchSpec.Length(); ) {
00753       // extract contentAreaID and URL substrings
00754       begPos = endPos;
00755       eqPos = searchSpec.FindChar('=', begPos);
00756       if (eqPos < 0)
00757         break;
00758 
00759       endPos = searchSpec.FindChar(';', eqPos);
00760       if (endPos < 0)
00761         endPos = searchSpec.Length();
00762       searchSpec.Mid(contentAreaID, begPos, eqPos-begPos);
00763       searchSpec.Mid(contentURL, eqPos+1, endPos-eqPos-1);
00764       endPos++;
00765 
00766       // see if we have a docshell with a matching contentAreaID
00767       nsCOMPtr<nsIDocShellTreeItem> content;
00768       rv = GetContentShellById(contentAreaID.get(), getter_AddRefs(content));
00769       if (NS_SUCCEEDED(rv) && content) {
00770         nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(content));
00771         if (webNav) {
00772           urlChar = ToNewCString(contentURL);
00773           if (urlChar) {
00774             nsUnescape(urlChar);
00775             contentURL.AssignWithConversion(urlChar);
00776             webNav->LoadURI(contentURL.get(),
00777                           nsIWebNavigation::LOAD_FLAGS_NONE,
00778                           nsnull,
00779                           nsnull,
00780                           nsnull);
00781             nsMemory::Free(urlChar);
00782           }
00783         }
00784       }
00785     }
00786   }
00787 }
00788 
00793 PRBool nsWebShellWindow::ExecuteCloseHandler()
00794 {
00795   /* If the event handler closes this window -- a likely scenario --
00796      things get deleted out of order without this death grip.
00797      (The problem may be the death grip in nsWindow::windowProc,
00798      which forces this window's widget to remain alive longer
00799      than it otherwise would.) */
00800   nsCOMPtr<nsIXULWindow> kungFuDeathGrip(this);
00801 
00802   nsCOMPtr<nsIScriptGlobalObject> globalObject(do_GetInterface(mDocShell));
00803 
00804   if (globalObject) {
00805     nsCOMPtr<nsIContentViewer> contentViewer;
00806     mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
00807     nsCOMPtr<nsIDocumentViewer> docViewer(do_QueryInterface(contentViewer));
00808 
00809     if (docViewer) {
00810       nsCOMPtr<nsPresContext> presContext;
00811       docViewer->GetPresContext(getter_AddRefs(presContext));
00812 
00813       nsEventStatus status = nsEventStatus_eIgnore;
00814       nsMouseEvent event(PR_TRUE, NS_XUL_CLOSE, nsnull,
00815                          nsMouseEvent::eReal);
00816 
00817       nsresult rv = globalObject->HandleDOMEvent(presContext, &event, nsnull,
00818                                                  NS_EVENT_FLAG_INIT, &status);
00819       if (NS_SUCCEEDED(rv) && status == nsEventStatus_eConsumeNoDefault)
00820         return PR_TRUE;
00821       // else fall through and return PR_FALSE
00822     }
00823   }
00824 
00825   return PR_FALSE;
00826 } // ExecuteCloseHandler
00827 
00828 // nsIBaseWindow
00829 NS_IMETHODIMP nsWebShellWindow::Destroy()
00830 {
00831   nsresult rv;
00832   nsCOMPtr<nsIWebProgress> webProgress(do_GetInterface(mDocShell, &rv));
00833   if (webProgress) {
00834     webProgress->RemoveProgressListener(this);
00835   }
00836 
00837   nsCOMPtr<nsIXULWindow> kungFuDeathGrip(this);
00838   if (mSPTimerLock) {
00839   PR_Lock(mSPTimerLock);
00840   if (mSPTimer) {
00841     mSPTimer->Cancel();
00842     SavePersistentAttributes();
00843     mSPTimer = nsnull;
00844     NS_RELEASE_THIS(); // the timer held a reference to us
00845   }
00846   PR_Unlock(mSPTimerLock);
00847   PR_DestroyLock(mSPTimerLock);
00848   mSPTimerLock = nsnull;
00849   }
00850   return nsXULWindow::Destroy();
00851 }
00852