xpfe/appshell/src/nsWebShellWindow.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/xpfe/appshell/src/nsWebShellWindow.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,751 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +
    1.10 +#include "nsWebShellWindow.h"
    1.11 +
    1.12 +#include "nsLayoutCID.h"
    1.13 +#include "nsContentCID.h"
    1.14 +#include "nsIWeakReference.h"
    1.15 +#include "nsIContentViewer.h"
    1.16 +#include "nsIComponentManager.h"
    1.17 +#include "nsIServiceManager.h"
    1.18 +#include "nsIURL.h"
    1.19 +#include "nsIIOService.h"
    1.20 +#include "nsIURL.h"
    1.21 +#include "nsNetCID.h"
    1.22 +#include "nsIStringBundle.h"
    1.23 +#include "nsReadableUtils.h"
    1.24 +
    1.25 +#include "nsEscape.h"
    1.26 +#include "nsPIDOMWindow.h"
    1.27 +#include "nsIWebNavigation.h"
    1.28 +#include "nsIWindowWatcher.h"
    1.29 +
    1.30 +#include "nsIDOMXULElement.h"
    1.31 +
    1.32 +#include "nsWidgetInitData.h"
    1.33 +#include "nsWidgetsCID.h"
    1.34 +#include "nsIWidget.h"
    1.35 +#include "nsIWidgetListener.h"
    1.36 +
    1.37 +#include "nsIDOMCharacterData.h"
    1.38 +#include "nsIDOMNodeList.h"
    1.39 +
    1.40 +#include "nsITimer.h"
    1.41 +#include "nsXULPopupManager.h"
    1.42 +
    1.43 +
    1.44 +#include "nsIDOMXULDocument.h"
    1.45 +
    1.46 +#include "nsFocusManager.h"
    1.47 +
    1.48 +#include "nsIWebProgress.h"
    1.49 +#include "nsIWebProgressListener.h"
    1.50 +
    1.51 +#include "nsIDocument.h"
    1.52 +#include "nsIDOMDocument.h"
    1.53 +#include "nsIDOMNode.h"
    1.54 +#include "nsIDOMElement.h"
    1.55 +#include "nsIDocumentLoaderFactory.h"
    1.56 +#include "nsIObserverService.h"
    1.57 +#include "prprf.h"
    1.58 +
    1.59 +#include "nsIScreenManager.h"
    1.60 +#include "nsIScreen.h"
    1.61 +
    1.62 +#include "nsIContent.h" // for menus
    1.63 +#include "nsIScriptSecurityManager.h"
    1.64 +
    1.65 +// For calculating size
    1.66 +#include "nsIPresShell.h"
    1.67 +#include "nsPresContext.h"
    1.68 +
    1.69 +#include "nsIBaseWindow.h"
    1.70 +#include "nsIDocShellTreeItem.h"
    1.71 +
    1.72 +#include "nsIMarkupDocumentViewer.h"
    1.73 +#include "mozilla/Attributes.h"
    1.74 +#include "mozilla/DebugOnly.h"
    1.75 +#include "mozilla/MouseEvents.h"
    1.76 +
    1.77 +#ifdef XP_MACOSX
    1.78 +#include "nsINativeMenuService.h"
    1.79 +#define USE_NATIVE_MENUS
    1.80 +#endif
    1.81 +
    1.82 +using namespace mozilla;
    1.83 +using namespace mozilla::dom;
    1.84 +
    1.85 +/* Define Class IDs */
    1.86 +static NS_DEFINE_CID(kWindowCID,           NS_WINDOW_CID);
    1.87 +
    1.88 +#define SIZE_PERSISTENCE_TIMEOUT 500 // msec
    1.89 +
    1.90 +nsWebShellWindow::nsWebShellWindow(uint32_t aChromeFlags)
    1.91 +  : nsXULWindow(aChromeFlags)
    1.92 +  , mSPTimerLock("nsWebShellWindow.mSPTimerLock")
    1.93 +{
    1.94 +}
    1.95 +
    1.96 +nsWebShellWindow::~nsWebShellWindow()
    1.97 +{
    1.98 +  MutexAutoLock lock(mSPTimerLock);
    1.99 +  if (mSPTimer)
   1.100 +    mSPTimer->Cancel();
   1.101 +}
   1.102 +
   1.103 +NS_IMPL_ADDREF_INHERITED(nsWebShellWindow, nsXULWindow)
   1.104 +NS_IMPL_RELEASE_INHERITED(nsWebShellWindow, nsXULWindow)
   1.105 +
   1.106 +NS_INTERFACE_MAP_BEGIN(nsWebShellWindow)
   1.107 +  NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
   1.108 +NS_INTERFACE_MAP_END_INHERITING(nsXULWindow)
   1.109 +
   1.110 +nsresult nsWebShellWindow::Initialize(nsIXULWindow* aParent,
   1.111 +                                      nsIXULWindow* aOpener,
   1.112 +                                      nsIURI* aUrl,
   1.113 +                                      int32_t aInitialWidth,
   1.114 +                                      int32_t aInitialHeight,
   1.115 +                                      bool aIsHiddenWindow,
   1.116 +                                      nsWidgetInitData& widgetInitData)
   1.117 +{
   1.118 +  nsresult rv;
   1.119 +  nsCOMPtr<nsIWidget> parentWidget;
   1.120 +
   1.121 +  mIsHiddenWindow = aIsHiddenWindow;
   1.122 +
   1.123 +  int32_t initialX = 0, initialY = 0;
   1.124 +  nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(aOpener));
   1.125 +  if (base) {
   1.126 +    rv = base->GetPositionAndSize(&mOpenerScreenRect.x,
   1.127 +                                  &mOpenerScreenRect.y,
   1.128 +                                  &mOpenerScreenRect.width,
   1.129 +                                  &mOpenerScreenRect.height);
   1.130 +    if (NS_FAILED(rv)) {
   1.131 +      mOpenerScreenRect.SetEmpty();
   1.132 +    } else {
   1.133 +      double scale;
   1.134 +      if (NS_SUCCEEDED(base->GetUnscaledDevicePixelsPerCSSPixel(&scale))) {
   1.135 +        mOpenerScreenRect.x = NSToIntRound(mOpenerScreenRect.x / scale);
   1.136 +        mOpenerScreenRect.y = NSToIntRound(mOpenerScreenRect.y / scale);
   1.137 +        mOpenerScreenRect.width = NSToIntRound(mOpenerScreenRect.width / scale);
   1.138 +        mOpenerScreenRect.height = NSToIntRound(mOpenerScreenRect.height / scale);
   1.139 +      }
   1.140 +      initialX = mOpenerScreenRect.x;
   1.141 +      initialY = mOpenerScreenRect.y;
   1.142 +      ConstrainToOpenerScreen(&initialX, &initialY);
   1.143 +    }
   1.144 +  }
   1.145 +
   1.146 +  // XXX: need to get the default window size from prefs...
   1.147 +  // Doesn't come from prefs... will come from CSS/XUL/RDF
   1.148 +  nsIntRect r(initialX, initialY, aInitialWidth, aInitialHeight);
   1.149 +  
   1.150 +  // Create top level window
   1.151 +  mWindow = do_CreateInstance(kWindowCID, &rv);
   1.152 +  if (NS_OK != rv) {
   1.153 +    return rv;
   1.154 +  }
   1.155 +
   1.156 +  /* This next bit is troublesome. We carry two different versions of a pointer
   1.157 +     to our parent window. One is the parent window's widget, which is passed
   1.158 +     to our own widget. The other is a weak reference we keep here to our
   1.159 +     parent WebShellWindow. The former is useful to the widget, and we can't
   1.160 +     trust its treatment of the parent reference because they're platform-
   1.161 +     specific. The latter is useful to this class.
   1.162 +       A better implementation would be one in which the parent keeps strong
   1.163 +     references to its children and closes them before it allows itself
   1.164 +     to be closed. This would mimic the behaviour of OSes that support
   1.165 +     top-level child windows in OSes that do not. Later.
   1.166 +  */
   1.167 +  nsCOMPtr<nsIBaseWindow> parentAsWin(do_QueryInterface(aParent));
   1.168 +  if (parentAsWin) {
   1.169 +    parentAsWin->GetMainWidget(getter_AddRefs(parentWidget));
   1.170 +    mParentWindow = do_GetWeakReference(aParent);
   1.171 +  }
   1.172 +
   1.173 +  mWindow->SetWidgetListener(this);
   1.174 +  mWindow->Create((nsIWidget *)parentWidget,          // Parent nsIWidget
   1.175 +                  nullptr,                            // Native parent widget
   1.176 +                  r,                                  // Widget dimensions
   1.177 +                  nullptr,                            // Device context
   1.178 +                  &widgetInitData);                   // Widget initialization data
   1.179 +  mWindow->GetClientBounds(r);
   1.180 +  // Match the default background color of content. Important on windows
   1.181 +  // since we no longer use content child widgets.
   1.182 +  mWindow->SetBackgroundColor(NS_RGB(255,255,255));
   1.183 +
   1.184 +  // Create web shell
   1.185 +  mDocShell = do_CreateInstance("@mozilla.org/docshell;1");
   1.186 +  NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
   1.187 +
   1.188 +  // Make sure to set the item type on the docshell _before_ calling
   1.189 +  // Create() so it knows what type it is.
   1.190 +  nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(do_QueryInterface(mDocShell));
   1.191 +  NS_ENSURE_TRUE(docShellAsItem, NS_ERROR_FAILURE);
   1.192 +  NS_ENSURE_SUCCESS(EnsureChromeTreeOwner(), NS_ERROR_FAILURE);
   1.193 +
   1.194 +  docShellAsItem->SetTreeOwner(mChromeTreeOwner);
   1.195 +  docShellAsItem->SetItemType(nsIDocShellTreeItem::typeChrome);
   1.196 +
   1.197 +  r.x = r.y = 0;
   1.198 +  nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell));
   1.199 +  NS_ENSURE_SUCCESS(docShellAsWin->InitWindow(nullptr, mWindow, 
   1.200 +   r.x, r.y, r.width, r.height), NS_ERROR_FAILURE);
   1.201 +  NS_ENSURE_SUCCESS(docShellAsWin->Create(), NS_ERROR_FAILURE);
   1.202 +
   1.203 +  // Attach a WebProgress listener.during initialization...
   1.204 +  nsCOMPtr<nsIWebProgress> webProgress(do_GetInterface(mDocShell, &rv));
   1.205 +  if (webProgress) {
   1.206 +    webProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_NETWORK);
   1.207 +  }
   1.208 +
   1.209 +  // Eagerly create an about:blank content viewer with the right principal here,
   1.210 +  // rather than letting it happening in the upcoming call to
   1.211 +  // SetInitialPrincipalToSubject. This avoids creating the about:blank document
   1.212 +  // and then blowing it away with a second one, which can cause problems for the
   1.213 +  // top-level chrome window case. See bug 789773.
   1.214 +  nsCOMPtr<nsIScriptSecurityManager> ssm =
   1.215 +    do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
   1.216 +  if (ssm) { // Sometimes this happens really early  See bug 793370.
   1.217 +    nsCOMPtr<nsIPrincipal> principal;
   1.218 +    ssm->GetSubjectPrincipal(getter_AddRefs(principal));
   1.219 +    if (!principal) {
   1.220 +      ssm->GetSystemPrincipal(getter_AddRefs(principal));
   1.221 +    }
   1.222 +    rv = mDocShell->CreateAboutBlankContentViewer(principal);
   1.223 +    NS_ENSURE_SUCCESS(rv, rv);
   1.224 +    nsCOMPtr<nsIDocument> doc = do_GetInterface(mDocShell);
   1.225 +    NS_ENSURE_TRUE(!!doc, NS_ERROR_FAILURE);
   1.226 +    doc->SetIsInitialDocument(true);
   1.227 +  }
   1.228 +
   1.229 +  if (nullptr != aUrl)  {
   1.230 +    nsCString tmpStr;
   1.231 +
   1.232 +    rv = aUrl->GetSpec(tmpStr);
   1.233 +    if (NS_FAILED(rv)) return rv;
   1.234 +
   1.235 +    NS_ConvertUTF8toUTF16 urlString(tmpStr);
   1.236 +    nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
   1.237 +    NS_ENSURE_TRUE(webNav, NS_ERROR_FAILURE);
   1.238 +    rv = webNav->LoadURI(urlString.get(),
   1.239 +                         nsIWebNavigation::LOAD_FLAGS_NONE,
   1.240 +                         nullptr,
   1.241 +                         nullptr,
   1.242 +                         nullptr);
   1.243 +    NS_ENSURE_SUCCESS(rv, rv);
   1.244 +  }
   1.245 +                     
   1.246 +  return rv;
   1.247 +}
   1.248 +
   1.249 +nsIPresShell*
   1.250 +nsWebShellWindow::GetPresShell()
   1.251 +{
   1.252 +  if (!mDocShell)
   1.253 +    return nullptr;
   1.254 +
   1.255 +  return mDocShell->GetPresShell();
   1.256 +}
   1.257 +
   1.258 +bool
   1.259 +nsWebShellWindow::WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y)
   1.260 +{
   1.261 +  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   1.262 +  if (pm) {
   1.263 +    nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(mDocShell);
   1.264 +    pm->AdjustPopupsOnWindowChange(window);
   1.265 +  }
   1.266 +
   1.267 +  // Persist position, but not immediately, in case this OS is firing
   1.268 +  // repeated move events as the user drags the window
   1.269 +  SetPersistenceTimer(PAD_POSITION);
   1.270 +  return false;
   1.271 +}
   1.272 +
   1.273 +bool
   1.274 +nsWebShellWindow::WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight)
   1.275 +{
   1.276 +  nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(mDocShell));
   1.277 +  if (shellAsWin) {
   1.278 +    shellAsWin->SetPositionAndSize(0, 0, aWidth, aHeight, false);
   1.279 +  }
   1.280 +  // Persist size, but not immediately, in case this OS is firing
   1.281 +  // repeated size events as the user drags the sizing handle
   1.282 +  if (!IsLocked())
   1.283 +    SetPersistenceTimer(PAD_POSITION | PAD_SIZE | PAD_MISC);
   1.284 +  return true;
   1.285 +}
   1.286 +
   1.287 +bool
   1.288 +nsWebShellWindow::RequestWindowClose(nsIWidget* aWidget)
   1.289 +{
   1.290 +  // Maintain a reference to this as it is about to get destroyed.
   1.291 +  nsCOMPtr<nsIXULWindow> xulWindow(this);
   1.292 +
   1.293 +  nsCOMPtr<nsPIDOMWindow> window(do_GetInterface(mDocShell));
   1.294 +  nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(window);
   1.295 +
   1.296 +  nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
   1.297 +
   1.298 +  if (!presShell) {
   1.299 +    mozilla::DebugOnly<bool> dying;
   1.300 +    MOZ_ASSERT(NS_SUCCEEDED(mDocShell->IsBeingDestroyed(&dying)) && dying,
   1.301 +               "No presShell, but window is not being destroyed");
   1.302 +  } else if (eventTarget) {
   1.303 +    nsRefPtr<nsPresContext> presContext = presShell->GetPresContext();
   1.304 +
   1.305 +    nsEventStatus status = nsEventStatus_eIgnore;
   1.306 +    WidgetMouseEvent event(true, NS_XUL_CLOSE, nullptr,
   1.307 +                           WidgetMouseEvent::eReal);
   1.308 +    if (NS_SUCCEEDED(eventTarget->DispatchDOMEvent(&event, nullptr, presContext, &status)) &&
   1.309 +        status == nsEventStatus_eConsumeNoDefault)
   1.310 +      return false;
   1.311 +  }
   1.312 +
   1.313 +  Destroy();
   1.314 +  return false;
   1.315 +}
   1.316 +
   1.317 +void
   1.318 +nsWebShellWindow::SizeModeChanged(nsSizeMode sizeMode)
   1.319 +{
   1.320 +  // An alwaysRaised (or higher) window will hide any newly opened normal
   1.321 +  // browser windows, so here we just drop a raised window to the normal
   1.322 +  // zlevel if it's maximized. We make no provision for automatically
   1.323 +  // re-raising it when restored.
   1.324 +  if (sizeMode == nsSizeMode_Maximized || sizeMode == nsSizeMode_Fullscreen) {
   1.325 +    uint32_t zLevel;
   1.326 +    GetZLevel(&zLevel);
   1.327 +    if (zLevel > nsIXULWindow::normalZ)
   1.328 +      SetZLevel(nsIXULWindow::normalZ);
   1.329 +  }
   1.330 +  mWindow->SetSizeMode(sizeMode);
   1.331 +
   1.332 +  // Persist mode, but not immediately, because in many (all?)
   1.333 +  // cases this will merge with the similar call in NS_SIZE and
   1.334 +  // write the attribute values only once.
   1.335 +  SetPersistenceTimer(PAD_MISC);
   1.336 +  nsCOMPtr<nsPIDOMWindow> ourWindow = do_GetInterface(mDocShell);
   1.337 +  if (ourWindow) {
   1.338 +    // Let the application know if it's in fullscreen mode so it
   1.339 +    // can update its UI.
   1.340 +    if (sizeMode == nsSizeMode_Fullscreen) {
   1.341 +      ourWindow->SetFullScreen(true);
   1.342 +    }
   1.343 +    else if (sizeMode != nsSizeMode_Minimized) {
   1.344 +      ourWindow->SetFullScreen(false);
   1.345 +    }
   1.346 +
   1.347 +    // And always fire a user-defined sizemodechange event on the window
   1.348 +    ourWindow->DispatchCustomEvent("sizemodechange");
   1.349 +  }
   1.350 +
   1.351 +  // Note the current implementation of SetSizeMode just stores
   1.352 +  // the new state; it doesn't actually resize. So here we store
   1.353 +  // the state and pass the event on to the OS. The day is coming
   1.354 +  // when we'll handle the event here, and the return result will
   1.355 +  // then need to be different.
   1.356 +}
   1.357 +
   1.358 +void
   1.359 +nsWebShellWindow::OSToolbarButtonPressed()
   1.360 +{
   1.361 +  // Keep a reference as setting the chrome flags can fire events.
   1.362 +  nsCOMPtr<nsIXULWindow> xulWindow(this);
   1.363 +
   1.364 +  // rjc: don't use "nsIWebBrowserChrome::CHROME_EXTRA"
   1.365 +  //      due to components with multiple sidebar components
   1.366 +  //      (such as Mail/News, Addressbook, etc)... and frankly,
   1.367 +  //      Mac IE, OmniWeb, and other Mac OS X apps all work this way
   1.368 +  uint32_t    chromeMask = (nsIWebBrowserChrome::CHROME_TOOLBAR |
   1.369 +                            nsIWebBrowserChrome::CHROME_LOCATIONBAR |
   1.370 +                            nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR);
   1.371 +
   1.372 +  nsCOMPtr<nsIWebBrowserChrome> wbc(do_GetInterface(xulWindow));
   1.373 +  if (!wbc)
   1.374 +    return;
   1.375 +
   1.376 +  uint32_t    chromeFlags, newChromeFlags = 0;
   1.377 +  wbc->GetChromeFlags(&chromeFlags);
   1.378 +  newChromeFlags = chromeFlags & chromeMask;
   1.379 +  if (!newChromeFlags)    chromeFlags |= chromeMask;
   1.380 +  else                    chromeFlags &= (~newChromeFlags);
   1.381 +  wbc->SetChromeFlags(chromeFlags);
   1.382 +}
   1.383 +
   1.384 +bool
   1.385 +nsWebShellWindow::ZLevelChanged(bool aImmediate, nsWindowZ *aPlacement,
   1.386 +                                nsIWidget* aRequestBelow, nsIWidget** aActualBelow)
   1.387 +{
   1.388 +  if (aActualBelow)
   1.389 +    *aActualBelow = nullptr;
   1.390 +
   1.391 +  return ConstrainToZLevel(aImmediate, aPlacement, aRequestBelow, aActualBelow);
   1.392 +}
   1.393 +
   1.394 +void
   1.395 +nsWebShellWindow::WindowActivated()
   1.396 +{
   1.397 +  nsCOMPtr<nsIXULWindow> xulWindow(this);
   1.398 +
   1.399 +  // focusing the window could cause it to close, so keep a reference to it
   1.400 +  nsCOMPtr<nsIDOMWindow> window = do_GetInterface(mDocShell);
   1.401 +  nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
   1.402 +  if (fm && window)
   1.403 +    fm->WindowRaised(window);
   1.404 +
   1.405 +  if (mChromeLoaded) {
   1.406 +    PersistentAttributesDirty(PAD_POSITION | PAD_SIZE | PAD_MISC);
   1.407 +    SavePersistentAttributes();
   1.408 +   }
   1.409 +}
   1.410 +
   1.411 +void
   1.412 +nsWebShellWindow::WindowDeactivated()
   1.413 +{
   1.414 +  nsCOMPtr<nsIXULWindow> xulWindow(this);
   1.415 +
   1.416 +  nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(mDocShell);
   1.417 +  nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
   1.418 +  if (fm && window)
   1.419 +    fm->WindowLowered(window);
   1.420 +}
   1.421 +
   1.422 +#ifdef USE_NATIVE_MENUS
   1.423 +static void LoadNativeMenus(nsIDOMDocument *aDOMDoc, nsIWidget *aParentWindow)
   1.424 +{
   1.425 +  // Find the menubar tag (if there is more than one, we ignore all but
   1.426 +  // the first).
   1.427 +  nsCOMPtr<nsIDOMNodeList> menubarElements;
   1.428 +  aDOMDoc->GetElementsByTagNameNS(NS_LITERAL_STRING("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"),
   1.429 +                                  NS_LITERAL_STRING("menubar"),
   1.430 +                                  getter_AddRefs(menubarElements));
   1.431 +
   1.432 +  nsCOMPtr<nsIDOMNode> menubarNode;
   1.433 +  if (menubarElements)
   1.434 +    menubarElements->Item(0, getter_AddRefs(menubarNode));
   1.435 +  if (!menubarNode)
   1.436 +    return;
   1.437 +
   1.438 +  nsCOMPtr<nsINativeMenuService> nms = do_GetService("@mozilla.org/widget/nativemenuservice;1");
   1.439 +  nsCOMPtr<nsIContent> menubarContent(do_QueryInterface(menubarNode));
   1.440 +  if (nms && menubarContent)
   1.441 +    nms->CreateNativeMenuBar(aParentWindow, menubarContent);
   1.442 +}
   1.443 +#endif
   1.444 +
   1.445 +namespace mozilla {
   1.446 +
   1.447 +class WebShellWindowTimerCallback MOZ_FINAL : public nsITimerCallback
   1.448 +{
   1.449 +public:
   1.450 +  WebShellWindowTimerCallback(nsWebShellWindow* aWindow)
   1.451 +    : mWindow(aWindow)
   1.452 +  {}
   1.453 +
   1.454 +  NS_DECL_THREADSAFE_ISUPPORTS
   1.455 +
   1.456 +  NS_IMETHOD Notify(nsITimer* aTimer)
   1.457 +  {
   1.458 +    // Although this object participates in a refcount cycle (this -> mWindow
   1.459 +    // -> mSPTimer -> this), mSPTimer is a one-shot timer and releases this
   1.460 +    // after it fires.  So we don't need to release mWindow here.
   1.461 +
   1.462 +    mWindow->FirePersistenceTimer();
   1.463 +    return NS_OK;
   1.464 +  }
   1.465 +
   1.466 +private:
   1.467 +  nsRefPtr<nsWebShellWindow> mWindow;
   1.468 +};
   1.469 +
   1.470 +NS_IMPL_ISUPPORTS(WebShellWindowTimerCallback, nsITimerCallback)
   1.471 +
   1.472 +} // namespace mozilla
   1.473 +
   1.474 +void
   1.475 +nsWebShellWindow::SetPersistenceTimer(uint32_t aDirtyFlags)
   1.476 +{
   1.477 +  MutexAutoLock lock(mSPTimerLock);
   1.478 +  if (!mSPTimer) {
   1.479 +    mSPTimer = do_CreateInstance("@mozilla.org/timer;1");
   1.480 +    if (!mSPTimer) {
   1.481 +      NS_WARNING("Couldn't create @mozilla.org/timer;1 instance?");
   1.482 +      return;
   1.483 +    }
   1.484 +  }
   1.485 +
   1.486 +  nsRefPtr<WebShellWindowTimerCallback> callback =
   1.487 +    new WebShellWindowTimerCallback(this);
   1.488 +  mSPTimer->InitWithCallback(callback, SIZE_PERSISTENCE_TIMEOUT,
   1.489 +                             nsITimer::TYPE_ONE_SHOT);
   1.490 +
   1.491 +  PersistentAttributesDirty(aDirtyFlags);
   1.492 +}
   1.493 +
   1.494 +void
   1.495 +nsWebShellWindow::FirePersistenceTimer()
   1.496 +{
   1.497 +  MutexAutoLock lock(mSPTimerLock);
   1.498 +  SavePersistentAttributes();
   1.499 +}
   1.500 +
   1.501 +
   1.502 +//----------------------------------------
   1.503 +// nsIWebProgessListener implementation
   1.504 +//----------------------------------------
   1.505 +NS_IMETHODIMP
   1.506 +nsWebShellWindow::OnProgressChange(nsIWebProgress *aProgress,
   1.507 +                                   nsIRequest *aRequest,
   1.508 +                                   int32_t aCurSelfProgress,
   1.509 +                                   int32_t aMaxSelfProgress,
   1.510 +                                   int32_t aCurTotalProgress,
   1.511 +                                   int32_t aMaxTotalProgress)
   1.512 +{
   1.513 +  NS_NOTREACHED("notification excluded in AddProgressListener(...)");
   1.514 +  return NS_OK;
   1.515 +}
   1.516 +
   1.517 +NS_IMETHODIMP
   1.518 +nsWebShellWindow::OnStateChange(nsIWebProgress *aProgress,
   1.519 +                                nsIRequest *aRequest,
   1.520 +                                uint32_t aStateFlags,
   1.521 +                                nsresult aStatus)
   1.522 +{
   1.523 +  // If the notification is not about a document finishing, then just
   1.524 +  // ignore it...
   1.525 +  if (!(aStateFlags & nsIWebProgressListener::STATE_STOP) || 
   1.526 +      !(aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK)) {
   1.527 +    return NS_OK;
   1.528 +  }
   1.529 +
   1.530 +  if (mChromeLoaded)
   1.531 +    return NS_OK;
   1.532 +
   1.533 +  // If this document notification is for a frame then ignore it...
   1.534 +  nsCOMPtr<nsIDOMWindow> eventWin;
   1.535 +  aProgress->GetDOMWindow(getter_AddRefs(eventWin));
   1.536 +  nsCOMPtr<nsPIDOMWindow> eventPWin(do_QueryInterface(eventWin));
   1.537 +  if (eventPWin) {
   1.538 +    nsPIDOMWindow *rootPWin = eventPWin->GetPrivateRoot();
   1.539 +    if (eventPWin != rootPWin)
   1.540 +      return NS_OK;
   1.541 +  }
   1.542 +
   1.543 +  mChromeLoaded = true;
   1.544 +  mLockedUntilChromeLoad = false;
   1.545 +
   1.546 +#ifdef USE_NATIVE_MENUS
   1.547 +  ///////////////////////////////
   1.548 +  // Find the Menubar DOM  and Load the menus, hooking them up to the loaded commands
   1.549 +  ///////////////////////////////
   1.550 +  nsCOMPtr<nsIContentViewer> cv;
   1.551 +  mDocShell->GetContentViewer(getter_AddRefs(cv));
   1.552 +  if (cv) {
   1.553 +    nsCOMPtr<nsIDOMDocument> menubarDOMDoc(do_QueryInterface(cv->GetDocument()));
   1.554 +    if (menubarDOMDoc)
   1.555 +      LoadNativeMenus(menubarDOMDoc, mWindow);
   1.556 +  }
   1.557 +#endif // USE_NATIVE_MENUS
   1.558 +
   1.559 +  OnChromeLoaded();
   1.560 +  LoadContentAreas();
   1.561 +
   1.562 +  return NS_OK;
   1.563 +}
   1.564 +
   1.565 +NS_IMETHODIMP
   1.566 +nsWebShellWindow::OnLocationChange(nsIWebProgress *aProgress,
   1.567 +                                   nsIRequest *aRequest,
   1.568 +                                   nsIURI *aURI,
   1.569 +                                   uint32_t aFlags)
   1.570 +{
   1.571 +  NS_NOTREACHED("notification excluded in AddProgressListener(...)");
   1.572 +  return NS_OK;
   1.573 +}
   1.574 +
   1.575 +NS_IMETHODIMP 
   1.576 +nsWebShellWindow::OnStatusChange(nsIWebProgress* aWebProgress,
   1.577 +                                 nsIRequest* aRequest,
   1.578 +                                 nsresult aStatus,
   1.579 +                                 const char16_t* aMessage)
   1.580 +{
   1.581 +  NS_NOTREACHED("notification excluded in AddProgressListener(...)");
   1.582 +  return NS_OK;
   1.583 +}
   1.584 +
   1.585 +NS_IMETHODIMP
   1.586 +nsWebShellWindow::OnSecurityChange(nsIWebProgress *aWebProgress,
   1.587 +                                   nsIRequest *aRequest,
   1.588 +                                   uint32_t state)
   1.589 +{
   1.590 +  NS_NOTREACHED("notification excluded in AddProgressListener(...)");
   1.591 +  return NS_OK;
   1.592 +}
   1.593 +
   1.594 +
   1.595 +//----------------------------------------
   1.596 +
   1.597 +// if the main document URL specified URLs for any content areas, start them loading
   1.598 +void nsWebShellWindow::LoadContentAreas() {
   1.599 +
   1.600 +  nsAutoString searchSpec;
   1.601 +
   1.602 +  // fetch the chrome document URL
   1.603 +  nsCOMPtr<nsIContentViewer> contentViewer;
   1.604 +  // yes, it's possible for the docshell to be null even this early
   1.605 +  // see bug 57514.
   1.606 +  if (mDocShell)
   1.607 +    mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
   1.608 +  if (contentViewer) {
   1.609 +    nsIDocument* doc = contentViewer->GetDocument();
   1.610 +    if (doc) {
   1.611 +      nsIURI* mainURL = doc->GetDocumentURI();
   1.612 +
   1.613 +      nsCOMPtr<nsIURL> url = do_QueryInterface(mainURL);
   1.614 +      if (url) {
   1.615 +        nsAutoCString search;
   1.616 +        url->GetQuery(search);
   1.617 +
   1.618 +        AppendUTF8toUTF16(search, searchSpec);
   1.619 +      }
   1.620 +    }
   1.621 +  }
   1.622 +
   1.623 +  // content URLs are specified in the search part of the URL
   1.624 +  // as <contentareaID>=<escapedURL>[;(repeat)]
   1.625 +  if (!searchSpec.IsEmpty()) {
   1.626 +    int32_t     begPos,
   1.627 +                eqPos,
   1.628 +                endPos;
   1.629 +    nsString    contentAreaID,
   1.630 +                contentURL;
   1.631 +    char        *urlChar;
   1.632 +    nsresult rv;
   1.633 +    for (endPos = 0; endPos < (int32_t)searchSpec.Length(); ) {
   1.634 +      // extract contentAreaID and URL substrings
   1.635 +      begPos = endPos;
   1.636 +      eqPos = searchSpec.FindChar('=', begPos);
   1.637 +      if (eqPos < 0)
   1.638 +        break;
   1.639 +
   1.640 +      endPos = searchSpec.FindChar(';', eqPos);
   1.641 +      if (endPos < 0)
   1.642 +        endPos = searchSpec.Length();
   1.643 +      searchSpec.Mid(contentAreaID, begPos, eqPos-begPos);
   1.644 +      searchSpec.Mid(contentURL, eqPos+1, endPos-eqPos-1);
   1.645 +      endPos++;
   1.646 +
   1.647 +      // see if we have a docshell with a matching contentAreaID
   1.648 +      nsCOMPtr<nsIDocShellTreeItem> content;
   1.649 +      rv = GetContentShellById(contentAreaID.get(), getter_AddRefs(content));
   1.650 +      if (NS_SUCCEEDED(rv) && content) {
   1.651 +        nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(content));
   1.652 +        if (webNav) {
   1.653 +          urlChar = ToNewCString(contentURL);
   1.654 +          if (urlChar) {
   1.655 +            nsUnescape(urlChar);
   1.656 +            contentURL.AssignWithConversion(urlChar);
   1.657 +            webNav->LoadURI(contentURL.get(),
   1.658 +                          nsIWebNavigation::LOAD_FLAGS_NONE,
   1.659 +                          nullptr,
   1.660 +                          nullptr,
   1.661 +                          nullptr);
   1.662 +            nsMemory::Free(urlChar);
   1.663 +          }
   1.664 +        }
   1.665 +      }
   1.666 +    }
   1.667 +  }
   1.668 +}
   1.669 +
   1.670 +/**
   1.671 + * ExecuteCloseHandler - Run the close handler, if any.
   1.672 + * @return true iff we found a close handler to run.
   1.673 + */
   1.674 +bool nsWebShellWindow::ExecuteCloseHandler()
   1.675 +{
   1.676 +  /* If the event handler closes this window -- a likely scenario --
   1.677 +     things get deleted out of order without this death grip.
   1.678 +     (The problem may be the death grip in nsWindow::windowProc,
   1.679 +     which forces this window's widget to remain alive longer
   1.680 +     than it otherwise would.) */
   1.681 +  nsCOMPtr<nsIXULWindow> kungFuDeathGrip(this);
   1.682 +
   1.683 +  nsCOMPtr<nsPIDOMWindow> window(do_GetInterface(mDocShell));
   1.684 +  nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(window);
   1.685 +
   1.686 +  if (eventTarget) {
   1.687 +    nsCOMPtr<nsIContentViewer> contentViewer;
   1.688 +    mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
   1.689 +    if (contentViewer) {
   1.690 +      nsRefPtr<nsPresContext> presContext;
   1.691 +      contentViewer->GetPresContext(getter_AddRefs(presContext));
   1.692 +
   1.693 +      nsEventStatus status = nsEventStatus_eIgnore;
   1.694 +      WidgetMouseEvent event(true, NS_XUL_CLOSE, nullptr,
   1.695 +                             WidgetMouseEvent::eReal);
   1.696 +
   1.697 +      nsresult rv =
   1.698 +        eventTarget->DispatchDOMEvent(&event, nullptr, presContext, &status);
   1.699 +      if (NS_SUCCEEDED(rv) && status == nsEventStatus_eConsumeNoDefault)
   1.700 +        return true;
   1.701 +      // else fall through and return false
   1.702 +    }
   1.703 +  }
   1.704 +
   1.705 +  return false;
   1.706 +} // ExecuteCloseHandler
   1.707 +
   1.708 +void nsWebShellWindow::ConstrainToOpenerScreen(int32_t* aX, int32_t* aY)
   1.709 +{
   1.710 +  if (mOpenerScreenRect.IsEmpty()) {
   1.711 +    *aX = *aY = 0;
   1.712 +    return;
   1.713 +  }
   1.714 +
   1.715 +  int32_t left, top, width, height;
   1.716 +  // Constrain initial positions to the same screen as opener
   1.717 +  nsCOMPtr<nsIScreenManager> screenmgr = do_GetService("@mozilla.org/gfx/screenmanager;1");
   1.718 +  if (screenmgr) {
   1.719 +    nsCOMPtr<nsIScreen> screen;
   1.720 +    screenmgr->ScreenForRect(mOpenerScreenRect.x, mOpenerScreenRect.y,
   1.721 +                             mOpenerScreenRect.width, mOpenerScreenRect.height,
   1.722 +                             getter_AddRefs(screen));
   1.723 +    if (screen) {
   1.724 +      screen->GetAvailRectDisplayPix(&left, &top, &width, &height);
   1.725 +      if (*aX < left || *aX > left + width) {
   1.726 +        *aX = left;
   1.727 +      }
   1.728 +      if (*aY < top || *aY > top + height) {
   1.729 +        *aY = top;
   1.730 +      }
   1.731 +    }
   1.732 +  }
   1.733 +}
   1.734 +
   1.735 +// nsIBaseWindow
   1.736 +NS_IMETHODIMP nsWebShellWindow::Destroy()
   1.737 +{
   1.738 +  nsresult rv;
   1.739 +  nsCOMPtr<nsIWebProgress> webProgress(do_GetInterface(mDocShell, &rv));
   1.740 +  if (webProgress) {
   1.741 +    webProgress->RemoveProgressListener(this);
   1.742 +  }
   1.743 +
   1.744 +  nsCOMPtr<nsIXULWindow> kungFuDeathGrip(this);
   1.745 +  {
   1.746 +    MutexAutoLock lock(mSPTimerLock);
   1.747 +    if (mSPTimer) {
   1.748 +      mSPTimer->Cancel();
   1.749 +      SavePersistentAttributes();
   1.750 +      mSPTimer = nullptr;
   1.751 +    }
   1.752 +  }
   1.753 +  return nsXULWindow::Destroy();
   1.754 +}

mercurial