summaryrefslogtreecommitdiffstats
path: root/xpfe
diff options
context:
space:
mode:
Diffstat (limited to 'xpfe')
-rw-r--r--xpfe/appshell/moz.build42
-rw-r--r--xpfe/appshell/nsAppShellCID.h10
-rw-r--r--xpfe/appshell/nsAppShellFactory.cpp56
-rw-r--r--xpfe/appshell/nsAppShellService.cpp1001
-rw-r--r--xpfe/appshell/nsAppShellService.h58
-rw-r--r--xpfe/appshell/nsAppShellWindowEnumerator.cpp495
-rw-r--r--xpfe/appshell/nsAppShellWindowEnumerator.h166
-rw-r--r--xpfe/appshell/nsChromeTreeOwner.cpp561
-rw-r--r--xpfe/appshell/nsChromeTreeOwner.h53
-rw-r--r--xpfe/appshell/nsContentTreeOwner.cpp1131
-rw-r--r--xpfe/appshell/nsContentTreeOwner.h63
-rw-r--r--xpfe/appshell/nsIAppShellService.idl150
-rw-r--r--xpfe/appshell/nsIPopupWindowManager.idl35
-rw-r--r--xpfe/appshell/nsIWindowMediator.idl206
-rw-r--r--xpfe/appshell/nsIWindowMediatorListener.idl19
-rw-r--r--xpfe/appshell/nsIWindowlessBrowser.idl27
-rw-r--r--xpfe/appshell/nsIXULBrowserWindow.idl78
-rw-r--r--xpfe/appshell/nsIXULWindow.idl168
-rw-r--r--xpfe/appshell/nsWebShellWindow.cpp911
-rw-r--r--xpfe/appshell/nsWebShellWindow.h116
-rw-r--r--xpfe/appshell/nsWindowMediator.cpp846
-rw-r--r--xpfe/appshell/nsWindowMediator.h78
-rw-r--r--xpfe/appshell/nsXULWindow.cpp2334
-rw-r--r--xpfe/appshell/nsXULWindow.h199
-rw-r--r--xpfe/appshell/test/chrome.ini5
-rw-r--r--xpfe/appshell/test/test_hiddenPrivateWindow.xul45
-rw-r--r--xpfe/appshell/test/test_windowlessBrowser.xul75
-rw-r--r--xpfe/components/autocomplete/jar.mn9
-rw-r--r--xpfe/components/autocomplete/moz.build7
-rw-r--r--xpfe/components/autocomplete/resources/content/autocomplete.css46
-rw-r--r--xpfe/components/autocomplete/resources/content/autocomplete.xml1646
-rw-r--r--xpfe/components/build/moz.build15
-rw-r--r--xpfe/components/build/nsModule.cpp44
-rw-r--r--xpfe/components/directory/moz.build20
-rw-r--r--xpfe/components/directory/nsDirectoryViewer.cpp1393
-rw-r--r--xpfe/components/directory/nsDirectoryViewer.h126
-rw-r--r--xpfe/components/directory/nsIHTTPIndex.idl50
-rw-r--r--xpfe/components/moz.build12
-rw-r--r--xpfe/components/windowds/moz.build17
-rw-r--r--xpfe/components/windowds/nsIWindowDataSource.idl17
-rw-r--r--xpfe/components/windowds/nsWindowDataSource.cpp519
-rw-r--r--xpfe/components/windowds/nsWindowDataSource.h59
-rw-r--r--xpfe/test/child-window.html5
-rw-r--r--xpfe/test/winopen.js244
-rw-r--r--xpfe/test/winopen.xul75
45 files changed, 13232 insertions, 0 deletions
diff --git a/xpfe/appshell/moz.build b/xpfe/appshell/moz.build
new file mode 100644
index 000000000..5cfe41924
--- /dev/null
+++ b/xpfe/appshell/moz.build
@@ -0,0 +1,42 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
+
+XPIDL_SOURCES += [
+ 'nsIAppShellService.idl',
+ 'nsIPopupWindowManager.idl',
+ 'nsIWindowlessBrowser.idl',
+ 'nsIWindowMediator.idl',
+ 'nsIWindowMediatorListener.idl',
+ 'nsIXULBrowserWindow.idl',
+ 'nsIXULWindow.idl',
+]
+
+XPIDL_MODULE = 'appshell'
+
+EXPORTS += [
+ 'nsAppShellCID.h',
+]
+
+UNIFIED_SOURCES += [
+ 'nsAppShellFactory.cpp',
+ 'nsAppShellService.cpp',
+ 'nsAppShellWindowEnumerator.cpp',
+ 'nsChromeTreeOwner.cpp',
+ 'nsContentTreeOwner.cpp',
+ 'nsWebShellWindow.cpp',
+ 'nsWindowMediator.cpp',
+ 'nsXULWindow.cpp',
+]
+
+LOCAL_INCLUDES += [
+ '/dom/base',
+]
+
+FINAL_LIBRARY = 'xul'
+
+include('/ipc/chromium/chromium-config.mozbuild') \ No newline at end of file
diff --git a/xpfe/appshell/nsAppShellCID.h b/xpfe/appshell/nsAppShellCID.h
new file mode 100644
index 000000000..2b0edd4ac
--- /dev/null
+++ b/xpfe/appshell/nsAppShellCID.h
@@ -0,0 +1,10 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAppShellCID_h__
+#define nsAppShellCID_h__
+
+#define NS_APPSHELLSERVICE_CONTRACTID "@mozilla.org/appshell/appShellService;1"
+
+#endif
diff --git a/xpfe/appshell/nsAppShellFactory.cpp b/xpfe/appshell/nsAppShellFactory.cpp
new file mode 100644
index 000000000..0b6d3c47a
--- /dev/null
+++ b/xpfe/appshell/nsAppShellFactory.cpp
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ModuleUtils.h"
+#include "nscore.h"
+#include "nsIWindowMediator.h"
+
+#include "nsIAppShellService.h"
+#include "nsAppShellService.h"
+#include "nsWindowMediator.h"
+#include "nsChromeTreeOwner.h"
+#include "nsAppShellCID.h"
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsAppShellService)
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsWindowMediator, Init)
+
+NS_DEFINE_NAMED_CID(NS_APPSHELLSERVICE_CID);
+NS_DEFINE_NAMED_CID(NS_WINDOWMEDIATOR_CID);
+
+static const mozilla::Module::CIDEntry kAppShellCIDs[] = {
+ { &kNS_APPSHELLSERVICE_CID, false, nullptr, nsAppShellServiceConstructor },
+ { &kNS_WINDOWMEDIATOR_CID, false, nullptr, nsWindowMediatorConstructor },
+ { nullptr }
+};
+
+static const mozilla::Module::ContractIDEntry kAppShellContracts[] = {
+ { NS_APPSHELLSERVICE_CONTRACTID, &kNS_APPSHELLSERVICE_CID },
+ { NS_WINDOWMEDIATOR_CONTRACTID, &kNS_WINDOWMEDIATOR_CID },
+ { nullptr }
+};
+
+static nsresult
+nsAppShellModuleConstructor()
+{
+ return nsChromeTreeOwner::InitGlobals();
+}
+
+static void
+nsAppShellModuleDestructor()
+{
+ nsChromeTreeOwner::FreeGlobals();
+}
+
+static const mozilla::Module kAppShellModule = {
+ mozilla::Module::kVersion,
+ kAppShellCIDs,
+ kAppShellContracts,
+ nullptr,
+ nullptr,
+ nsAppShellModuleConstructor,
+ nsAppShellModuleDestructor
+};
+
+NSMODULE_DEFN(appshell) = &kAppShellModule;
diff --git a/xpfe/appshell/nsAppShellService.cpp b/xpfe/appshell/nsAppShellService.cpp
new file mode 100644
index 000000000..7e1ddf16c
--- /dev/null
+++ b/xpfe/appshell/nsAppShellService.cpp
@@ -0,0 +1,1001 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "nsIAppShellService.h"
+#include "nsIComponentManager.h"
+#include "nsIURL.h"
+#include "nsNetUtil.h"
+#include "nsIServiceManager.h"
+#include "nsIObserverService.h"
+#include "nsIObserver.h"
+#include "nsIXPConnect.h"
+#include "nsIXULRuntime.h"
+
+#include "nsIWindowMediator.h"
+#include "nsIWindowWatcher.h"
+#include "nsPIWindowWatcher.h"
+#include "nsIDOMWindow.h"
+#include "nsPIDOMWindow.h"
+#include "nsWebShellWindow.h"
+
+#include "prprf.h"
+
+#include "nsWidgetInitData.h"
+#include "nsWidgetsCID.h"
+#include "nsIWidget.h"
+#include "nsIRequestObserver.h"
+#include "nsIEmbeddingSiteWindow.h"
+
+#include "nsAppShellService.h"
+#include "nsContentUtils.h"
+#include "nsThreadUtils.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIChromeRegistry.h"
+#include "nsILoadContext.h"
+#include "nsIWebNavigation.h"
+#include "nsIWindowlessBrowser.h"
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/StartupTimeline.h"
+
+#include "nsEmbedCID.h"
+#include "nsIWebBrowser.h"
+#include "nsIDocShell.h"
+
+#ifdef MOZ_INSTRUMENT_EVENT_LOOP
+#include "EventTracer.h"
+#endif
+
+using namespace mozilla;
+
+// Default URL for the hidden window, can be overridden by a pref on Mac
+#define DEFAULT_HIDDENWINDOW_URL "resource://gre-resources/hiddenWindow.html"
+
+class nsIAppShell;
+
+nsAppShellService::nsAppShellService() :
+ mXPCOMWillShutDown(false),
+ mXPCOMShuttingDown(false),
+ mModalWindowCount(0),
+ mApplicationProvidedHiddenWindow(false),
+ mScreenId(0)
+{
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+
+ if (obs) {
+ obs->AddObserver(this, "xpcom-will-shutdown", false);
+ obs->AddObserver(this, "xpcom-shutdown", false);
+ }
+}
+
+nsAppShellService::~nsAppShellService()
+{
+}
+
+
+/*
+ * Implement the nsISupports methods...
+ */
+NS_IMPL_ISUPPORTS(nsAppShellService,
+ nsIAppShellService,
+ nsIObserver)
+
+NS_IMETHODIMP
+nsAppShellService::CreateHiddenWindow()
+{
+ return CreateHiddenWindowHelper(false);
+}
+
+NS_IMETHODIMP
+nsAppShellService::SetScreenId(uint32_t aScreenId)
+{
+ mScreenId = aScreenId;
+ return NS_OK;
+}
+
+void
+nsAppShellService::EnsurePrivateHiddenWindow()
+{
+ if (!mHiddenPrivateWindow) {
+ CreateHiddenWindowHelper(true);
+ }
+}
+
+nsresult
+nsAppShellService::CreateHiddenWindowHelper(bool aIsPrivate)
+{
+ nsresult rv;
+ int32_t initialHeight = 100, initialWidth = 100;
+
+#ifdef XP_MACOSX
+ uint32_t chromeMask = 0;
+ nsAdoptingCString prefVal =
+ Preferences::GetCString("browser.hiddenWindowChromeURL");
+ const char* hiddenWindowURL = prefVal.get() ? prefVal.get() : DEFAULT_HIDDENWINDOW_URL;
+ if (aIsPrivate) {
+ hiddenWindowURL = DEFAULT_HIDDENWINDOW_URL;
+ } else {
+ mApplicationProvidedHiddenWindow = prefVal.get() ? true : false;
+ }
+#else
+ static const char hiddenWindowURL[] = DEFAULT_HIDDENWINDOW_URL;
+ uint32_t chromeMask = nsIWebBrowserChrome::CHROME_ALL;
+#endif
+
+ nsCOMPtr<nsIURI> url;
+ rv = NS_NewURI(getter_AddRefs(url), hiddenWindowURL);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<nsWebShellWindow> newWindow;
+ if (!aIsPrivate) {
+ rv = JustCreateTopWindow(nullptr, url,
+ chromeMask, initialWidth, initialHeight,
+ true, nullptr, nullptr, getter_AddRefs(newWindow));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mHiddenWindow.swap(newWindow);
+ } else {
+ // Create the hidden private window
+ chromeMask |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
+
+ rv = JustCreateTopWindow(nullptr, url,
+ chromeMask, initialWidth, initialHeight,
+ true, nullptr, nullptr, getter_AddRefs(newWindow));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDocShell> docShell;
+ newWindow->GetDocShell(getter_AddRefs(docShell));
+ if (docShell) {
+ docShell->SetAffectPrivateSessionLifetime(false);
+ }
+
+ mHiddenPrivateWindow.swap(newWindow);
+ }
+
+ // RegisterTopLevelWindow(newWindow); -- Mac only
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAppShellService::DestroyHiddenWindow()
+{
+ if (mHiddenWindow) {
+ mHiddenWindow->Destroy();
+
+ mHiddenWindow = nullptr;
+ }
+
+ if (mHiddenPrivateWindow) {
+ mHiddenPrivateWindow->Destroy();
+
+ mHiddenPrivateWindow = nullptr;
+ }
+
+ return NS_OK;
+}
+
+/*
+ * Create a new top level window and display the given URL within it...
+ */
+NS_IMETHODIMP
+nsAppShellService::CreateTopLevelWindow(nsIXULWindow *aParent,
+ nsIURI *aUrl,
+ uint32_t aChromeMask,
+ int32_t aInitialWidth,
+ int32_t aInitialHeight,
+ nsITabParent *aOpeningTab,
+ mozIDOMWindowProxy *aOpenerWindow,
+ nsIXULWindow **aResult)
+
+{
+ nsresult rv;
+
+ StartupTimeline::RecordOnce(StartupTimeline::CREATE_TOP_LEVEL_WINDOW);
+
+ RefPtr<nsWebShellWindow> newWindow;
+ rv = JustCreateTopWindow(aParent, aUrl,
+ aChromeMask, aInitialWidth, aInitialHeight,
+ false, aOpeningTab, aOpenerWindow,
+ getter_AddRefs(newWindow));
+ newWindow.forget(aResult);
+
+ if (NS_SUCCEEDED(rv)) {
+ // the addref resulting from this is the owning addref for this window
+ RegisterTopLevelWindow(*aResult);
+ nsCOMPtr<nsIXULWindow> parent;
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_DEPENDENT)
+ parent = aParent;
+ (*aResult)->SetZLevel(CalculateWindowZLevel(parent, aChromeMask));
+ }
+
+ return rv;
+}
+
+/*
+ * This class provides a stub implementation of nsIWebBrowserChrome2, as needed
+ * by nsAppShellService::CreateWindowlessBrowser
+ */
+class WebBrowserChrome2Stub : public nsIWebBrowserChrome2,
+ public nsIEmbeddingSiteWindow,
+ public nsIInterfaceRequestor,
+ public nsSupportsWeakReference {
+protected:
+ virtual ~WebBrowserChrome2Stub() {}
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIWEBBROWSERCHROME
+ NS_DECL_NSIWEBBROWSERCHROME2
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSIEMBEDDINGSITEWINDOW
+};
+
+NS_INTERFACE_MAP_BEGIN(WebBrowserChrome2Stub)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebBrowserChrome)
+ NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome)
+ NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome2)
+ NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+ NS_INTERFACE_MAP_ENTRY(nsIEmbeddingSiteWindow)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(WebBrowserChrome2Stub)
+NS_IMPL_RELEASE(WebBrowserChrome2Stub)
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::SetStatus(uint32_t aStatusType, const char16_t* aStatus)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::GetWebBrowser(nsIWebBrowser** aWebBrowser)
+{
+ NS_NOTREACHED("WebBrowserChrome2Stub::GetWebBrowser is not supported");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::SetWebBrowser(nsIWebBrowser* aWebBrowser)
+{
+ NS_NOTREACHED("WebBrowserChrome2Stub::SetWebBrowser is not supported");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::GetChromeFlags(uint32_t* aChromeFlags)
+{
+ *aChromeFlags = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::SetChromeFlags(uint32_t aChromeFlags)
+{
+ NS_NOTREACHED("WebBrowserChrome2Stub::SetChromeFlags is not supported");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::DestroyBrowserWindow()
+{
+ NS_NOTREACHED("WebBrowserChrome2Stub::DestroyBrowserWindow is not supported");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::SizeBrowserTo(int32_t aCX, int32_t aCY)
+{
+ NS_NOTREACHED("WebBrowserChrome2Stub::SizeBrowserTo is not supported");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::ShowAsModal()
+{
+ NS_NOTREACHED("WebBrowserChrome2Stub::ShowAsModal is not supported");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::IsWindowModal(bool* aResult)
+{
+ *aResult = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::ExitModalEventLoop(nsresult aStatus)
+{
+ NS_NOTREACHED("WebBrowserChrome2Stub::ExitModalEventLoop is not supported");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::SetStatusWithContext(uint32_t aStatusType,
+ const nsAString& aStatusText,
+ nsISupports* aStatusContext)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::GetInterface(const nsIID& aIID, void** aSink)
+{
+ return QueryInterface(aIID, aSink);
+}
+
+// nsIEmbeddingSiteWindow impl
+NS_IMETHODIMP
+WebBrowserChrome2Stub::GetDimensions(uint32_t flags, int32_t* x, int32_t* y, int32_t* cx, int32_t* cy)
+{
+ if (x) {
+ *x = 0;
+ }
+
+ if (y) {
+ *y = 0;
+ }
+
+ if (cx) {
+ *cx = 0;
+ }
+
+ if (cy) {
+ *cy = 0;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::SetDimensions(uint32_t flags, int32_t x, int32_t y, int32_t cx, int32_t cy)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::SetFocus()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::GetVisibility(bool* aVisibility)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+NS_IMETHODIMP
+WebBrowserChrome2Stub::SetVisibility(bool aVisibility)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::GetTitle(char16_t** aTitle)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+NS_IMETHODIMP
+WebBrowserChrome2Stub::SetTitle(const char16_t* aTitle)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::GetSiteWindow(void** aSiteWindow)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::Blur()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+class BrowserDestroyer final : public Runnable
+{
+public:
+ BrowserDestroyer(nsIWebBrowser *aBrowser, nsISupports *aContainer) :
+ mBrowser(aBrowser),
+ mContainer(aContainer)
+ {
+ }
+
+ NS_IMETHOD
+ Run() override
+ {
+ // Explicitly destroy the browser, in case this isn't the last reference.
+ nsCOMPtr<nsIBaseWindow> window = do_QueryInterface(mBrowser);
+ return window->Destroy();
+ }
+
+protected:
+ virtual ~BrowserDestroyer() {}
+
+private:
+ nsCOMPtr<nsIWebBrowser> mBrowser;
+ nsCOMPtr<nsISupports> mContainer;
+};
+
+// This is the "stub" we return from CreateWindowlessBrowser - it exists
+// to manage the lifetimes of the nsIWebBrowser and container window.
+// In particular, it keeps a strong reference to both, to prevent them from
+// being collected while this object remains alive, and ensures that they
+// aren't destroyed when it's not safe to run scripts.
+class WindowlessBrowser final : public nsIWindowlessBrowser,
+ public nsIInterfaceRequestor
+{
+public:
+ WindowlessBrowser(nsIWebBrowser *aBrowser, nsISupports *aContainer) :
+ mBrowser(aBrowser),
+ mContainer(aContainer),
+ mClosed(false)
+ {
+ mWebNavigation = do_QueryInterface(aBrowser);
+ mInterfaceRequestor = do_QueryInterface(aBrowser);
+ }
+ NS_DECL_ISUPPORTS
+ NS_FORWARD_SAFE_NSIWEBNAVIGATION(mWebNavigation)
+ NS_FORWARD_SAFE_NSIINTERFACEREQUESTOR(mInterfaceRequestor)
+
+ NS_IMETHOD
+ Close() override
+ {
+ NS_ENSURE_TRUE(!mClosed, NS_ERROR_UNEXPECTED);
+ NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
+ "WindowlessBrowser::Close called when not safe to run scripts");
+
+ mClosed = true;
+
+ mWebNavigation = nullptr;
+ mInterfaceRequestor = nullptr;
+
+ nsCOMPtr<nsIBaseWindow> window = do_QueryInterface(mBrowser);
+ return window->Destroy();
+ }
+
+protected:
+ virtual ~WindowlessBrowser()
+ {
+ if (mClosed) {
+ return;
+ }
+
+ NS_WARNING("Windowless browser was not closed prior to destruction");
+
+ // The docshell destructor needs to dispatch events, and can only run
+ // when it's safe to run scripts. If this was triggered by GC, it may
+ // not always be safe to run scripts, in which cases we need to delay
+ // destruction until it is.
+ nsCOMPtr<nsIRunnable> runnable = new BrowserDestroyer(mBrowser, mContainer);
+ nsContentUtils::AddScriptRunner(runnable);
+ }
+
+private:
+ nsCOMPtr<nsIWebBrowser> mBrowser;
+ nsCOMPtr<nsIWebNavigation> mWebNavigation;
+ nsCOMPtr<nsIInterfaceRequestor> mInterfaceRequestor;
+ // we don't use the container but just hold a reference to it.
+ nsCOMPtr<nsISupports> mContainer;
+
+ bool mClosed;
+};
+
+NS_IMPL_ISUPPORTS(WindowlessBrowser, nsIWindowlessBrowser, nsIWebNavigation, nsIInterfaceRequestor)
+
+
+NS_IMETHODIMP
+nsAppShellService::CreateWindowlessBrowser(bool aIsChrome, nsIWindowlessBrowser **aResult)
+{
+ /* First, we create an instance of nsWebBrowser. Instances of this class have
+ * an associated doc shell, which is what we're interested in.
+ */
+ nsCOMPtr<nsIWebBrowser> browser = do_CreateInstance(NS_WEBBROWSER_CONTRACTID);
+ if (!browser) {
+ NS_ERROR("Couldn't create instance of nsWebBrowser!");
+ return NS_ERROR_FAILURE;
+ }
+
+ /* Next, we set the container window for our instance of nsWebBrowser. Since
+ * we don't actually have a window, we instead set the container window to be
+ * an instance of WebBrowserChrome2Stub, which provides a stub implementation
+ * of nsIWebBrowserChrome2.
+ */
+ RefPtr<WebBrowserChrome2Stub> stub = new WebBrowserChrome2Stub();
+ browser->SetContainerWindow(stub);
+
+ nsCOMPtr<nsIWebNavigation> navigation = do_QueryInterface(browser);
+
+ nsCOMPtr<nsIDocShellTreeItem> item = do_QueryInterface(navigation);
+ item->SetItemType(aIsChrome ? nsIDocShellTreeItem::typeChromeWrapper
+ : nsIDocShellTreeItem::typeContentWrapper);
+
+ /* A windowless web browser doesn't have an associated OS level window. To
+ * accomplish this, we initialize the window associated with our instance of
+ * nsWebBrowser with an instance of PuppetWidget, which provides a stub
+ * implementation of nsIWidget.
+ */
+ nsCOMPtr<nsIWidget> widget = nsIWidget::CreatePuppetWidget(nullptr);
+ if (!widget) {
+ NS_ERROR("Couldn't create instance of PuppetWidget");
+ return NS_ERROR_FAILURE;
+ }
+ nsresult rv =
+ widget->Create(nullptr, 0, LayoutDeviceIntRect(0, 0, 0, 0), nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIBaseWindow> window = do_QueryInterface(navigation);
+ window->InitWindow(0, widget, 0, 0, 0, 0);
+ window->Create();
+
+ nsISupports *isstub = NS_ISUPPORTS_CAST(nsIWebBrowserChrome2*, stub);
+ RefPtr<nsIWindowlessBrowser> result = new WindowlessBrowser(browser, isstub);
+ nsCOMPtr<nsIDocShell> docshell = do_GetInterface(result);
+ docshell->SetInvisible(true);
+
+ result.forget(aResult);
+ return NS_OK;
+}
+
+uint32_t
+nsAppShellService::CalculateWindowZLevel(nsIXULWindow *aParent,
+ uint32_t aChromeMask)
+{
+ uint32_t zLevel;
+
+ zLevel = nsIXULWindow::normalZ;
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_RAISED)
+ zLevel = nsIXULWindow::raisedZ;
+ else if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_LOWERED)
+ zLevel = nsIXULWindow::loweredZ;
+
+#ifdef XP_MACOSX
+ /* Platforms on which modal windows are always application-modal, not
+ window-modal (that's just the Mac, right?) want modal windows to
+ be stacked on top of everyone else.
+
+ On Mac OS X, bind modality to parent window instead of app (ala Mac OS 9)
+ */
+ uint32_t modalDepMask = nsIWebBrowserChrome::CHROME_MODAL |
+ nsIWebBrowserChrome::CHROME_DEPENDENT;
+ if (aParent && (aChromeMask & modalDepMask)) {
+ aParent->GetZLevel(&zLevel);
+ }
+#else
+ /* Platforms with native support for dependent windows (that's everyone
+ but pre-Mac OS X, right?) know how to stack dependent windows. On these
+ platforms, give the dependent window the same level as its parent,
+ so we won't try to override the normal platform behaviour. */
+ if ((aChromeMask & nsIWebBrowserChrome::CHROME_DEPENDENT) && aParent)
+ aParent->GetZLevel(&zLevel);
+#endif
+
+ return zLevel;
+}
+
+#ifdef XP_WIN
+/*
+ * Checks to see if any existing window is currently in fullscreen mode.
+ */
+static bool
+CheckForFullscreenWindow()
+{
+ nsCOMPtr<nsIWindowMediator> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
+ if (!wm)
+ return false;
+
+ nsCOMPtr<nsISimpleEnumerator> windowList;
+ wm->GetXULWindowEnumerator(nullptr, getter_AddRefs(windowList));
+ if (!windowList)
+ return false;
+
+ for (;;) {
+ bool more = false;
+ windowList->HasMoreElements(&more);
+ if (!more)
+ return false;
+
+ nsCOMPtr<nsISupports> supportsWindow;
+ windowList->GetNext(getter_AddRefs(supportsWindow));
+ nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(supportsWindow));
+ if (baseWin) {
+ nsCOMPtr<nsIWidget> widget;
+ baseWin->GetMainWidget(getter_AddRefs(widget));
+ if (widget && widget->SizeMode() == nsSizeMode_Fullscreen) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+#endif
+
+/*
+ * Just do the window-making part of CreateTopLevelWindow
+ */
+nsresult
+nsAppShellService::JustCreateTopWindow(nsIXULWindow *aParent,
+ nsIURI *aUrl,
+ uint32_t aChromeMask,
+ int32_t aInitialWidth,
+ int32_t aInitialHeight,
+ bool aIsHiddenWindow,
+ nsITabParent *aOpeningTab,
+ mozIDOMWindowProxy *aOpenerWindow,
+ nsWebShellWindow **aResult)
+{
+ *aResult = nullptr;
+ NS_ENSURE_STATE(!mXPCOMWillShutDown);
+
+ nsCOMPtr<nsIXULWindow> parent;
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_DEPENDENT)
+ parent = aParent;
+
+ RefPtr<nsWebShellWindow> window = new nsWebShellWindow(aChromeMask);
+
+#ifdef XP_WIN
+ // If the parent is currently fullscreen, tell the child to ignore persisted
+ // full screen states. This way new browser windows open on top of fullscreen
+ // windows normally.
+ if (window && CheckForFullscreenWindow())
+ window->IgnoreXULSizeMode(true);
+#endif
+
+ nsWidgetInitData widgetInitData;
+
+ if (aIsHiddenWindow)
+ widgetInitData.mWindowType = eWindowType_invisible;
+ else
+ widgetInitData.mWindowType = aChromeMask & nsIWebBrowserChrome::CHROME_OPENAS_DIALOG ?
+ eWindowType_dialog : eWindowType_toplevel;
+
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_POPUP)
+ widgetInitData.mWindowType = eWindowType_popup;
+
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_MAC_SUPPRESS_ANIMATION)
+ widgetInitData.mIsAnimationSuppressed = true;
+
+#ifdef XP_MACOSX
+ // Mac OS X sheet support
+ // Adding CHROME_OPENAS_CHROME to sheetMask makes modal windows opened from
+ // nsGlobalWindow::ShowModalDialog() be dialogs (not sheets), while modal
+ // windows opened from nsPromptService::DoDialog() still are sheets. This
+ // fixes bmo bug 395465 (see nsCocoaWindow::StandardCreate() and
+ // nsCocoaWindow::SetModal()).
+ uint32_t sheetMask = nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
+ nsIWebBrowserChrome::CHROME_MODAL |
+ nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
+ if (parent &&
+ (parent != mHiddenWindow && parent != mHiddenPrivateWindow) &&
+ ((aChromeMask & sheetMask) == sheetMask)) {
+ widgetInitData.mWindowType = eWindowType_sheet;
+ }
+#endif
+
+#if defined(XP_WIN)
+ if (widgetInitData.mWindowType == eWindowType_toplevel ||
+ widgetInitData.mWindowType == eWindowType_dialog)
+ widgetInitData.clipChildren = true;
+#endif
+
+ // note default chrome overrides other OS chrome settings, but
+ // not internal chrome
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_DEFAULT)
+ widgetInitData.mBorderStyle = eBorderStyle_default;
+ else if ((aChromeMask & nsIWebBrowserChrome::CHROME_ALL) == nsIWebBrowserChrome::CHROME_ALL)
+ widgetInitData.mBorderStyle = eBorderStyle_all;
+ else {
+ widgetInitData.mBorderStyle = eBorderStyle_none; // assumes none == 0x00
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_BORDERS)
+ widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(widgetInitData.mBorderStyle | eBorderStyle_border);
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_TITLEBAR)
+ widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(widgetInitData.mBorderStyle | eBorderStyle_title);
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_CLOSE)
+ widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(widgetInitData.mBorderStyle | eBorderStyle_close);
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE) {
+ widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(widgetInitData.mBorderStyle | eBorderStyle_resizeh);
+ // only resizable windows get the maximize button (but not dialogs)
+ if (!(aChromeMask & nsIWebBrowserChrome::CHROME_OPENAS_DIALOG))
+ widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(widgetInitData.mBorderStyle | eBorderStyle_maximize);
+ }
+ // all windows (except dialogs) get minimize buttons and the system menu
+ if (!(aChromeMask & nsIWebBrowserChrome::CHROME_OPENAS_DIALOG))
+ widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(widgetInitData.mBorderStyle | eBorderStyle_minimize | eBorderStyle_menu);
+ // but anyone can explicitly ask for a minimize button
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_MIN) {
+ widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(widgetInitData.mBorderStyle | eBorderStyle_minimize);
+ }
+ }
+
+ if (aInitialWidth == nsIAppShellService::SIZE_TO_CONTENT ||
+ aInitialHeight == nsIAppShellService::SIZE_TO_CONTENT) {
+ aInitialWidth = 1;
+ aInitialHeight = 1;
+ window->SetIntrinsicallySized(true);
+ }
+
+ bool center = aChromeMask & nsIWebBrowserChrome::CHROME_CENTER_SCREEN;
+
+ nsCOMPtr<nsIXULChromeRegistry> reg =
+ mozilla::services::GetXULChromeRegistryService();
+ if (reg) {
+ nsAutoCString package;
+ package.AssignLiteral("global");
+ bool isRTL = false;
+ reg->IsLocaleRTL(package, &isRTL);
+ widgetInitData.mRTL = isRTL;
+ }
+
+#ifdef MOZ_WIDGET_GONK
+ // B2G multi-screen support. Screen ID is for differentiating screens of
+ // windows, and due to the hardware limitation, it is platform-specific for
+ // now, which align with the value of display type defined in HWC.
+ widgetInitData.mScreenId = mScreenId;
+#endif
+
+ nsresult rv = window->Initialize(parent, center ? aParent : nullptr,
+ aUrl, aInitialWidth, aInitialHeight,
+ aIsHiddenWindow, aOpeningTab,
+ aOpenerWindow, widgetInitData);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Enforce the Private Browsing autoStart pref first.
+ bool isPrivateBrowsingWindow =
+ Preferences::GetBool("browser.privatebrowsing.autostart");
+ bool isUsingRemoteTabs = mozilla::BrowserTabsRemoteAutostart();
+
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW) {
+ // Caller requested a private window
+ isPrivateBrowsingWindow = true;
+ }
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW) {
+ isUsingRemoteTabs = true;
+ }
+
+ nsCOMPtr<mozIDOMWindowProxy> domWin = do_GetInterface(aParent);
+ nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(domWin);
+ nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(webNav);
+
+ if (!isPrivateBrowsingWindow && parentContext) {
+ // Ensure that we propagate any existing private browsing status
+ // from the parent, even if it will not actually be used
+ // as a parent value.
+ isPrivateBrowsingWindow = parentContext->UsePrivateBrowsing();
+ }
+
+ if (parentContext) {
+ isUsingRemoteTabs = parentContext->UseRemoteTabs();
+ }
+
+ nsCOMPtr<mozIDOMWindowProxy> newDomWin =
+ do_GetInterface(NS_ISUPPORTS_CAST(nsIBaseWindow*, window));
+ nsCOMPtr<nsIWebNavigation> newWebNav = do_GetInterface(newDomWin);
+ nsCOMPtr<nsILoadContext> thisContext = do_GetInterface(newWebNav);
+ if (thisContext) {
+ thisContext->SetPrivateBrowsing(isPrivateBrowsingWindow);
+ thisContext->SetRemoteTabs(isUsingRemoteTabs);
+ }
+
+ window.forget(aResult);
+ if (parent)
+ parent->AddChildWindow(*aResult);
+
+ if (center)
+ rv = (*aResult)->Center(parent, parent ? false : true, false);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsAppShellService::GetHiddenWindow(nsIXULWindow **aWindow)
+{
+ NS_ENSURE_ARG_POINTER(aWindow);
+
+ *aWindow = mHiddenWindow;
+ NS_IF_ADDREF(*aWindow);
+ return *aWindow ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsAppShellService::GetHiddenDOMWindow(mozIDOMWindowProxy **aWindow)
+{
+ nsresult rv;
+ nsCOMPtr<nsIDocShell> docShell;
+ NS_ENSURE_TRUE(mHiddenWindow, NS_ERROR_FAILURE);
+
+ rv = mHiddenWindow->GetDocShell(getter_AddRefs(docShell));
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsPIDOMWindowOuter> hiddenDOMWindow(docShell->GetWindow());
+ hiddenDOMWindow.forget(aWindow);
+ return *aWindow ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsAppShellService::GetHiddenPrivateWindow(nsIXULWindow **aWindow)
+{
+ NS_ENSURE_ARG_POINTER(aWindow);
+
+ EnsurePrivateHiddenWindow();
+
+ *aWindow = mHiddenPrivateWindow;
+ NS_IF_ADDREF(*aWindow);
+ return *aWindow ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsAppShellService::GetHiddenPrivateDOMWindow(mozIDOMWindowProxy **aWindow)
+{
+ EnsurePrivateHiddenWindow();
+
+ nsresult rv;
+ nsCOMPtr<nsIDocShell> docShell;
+ NS_ENSURE_TRUE(mHiddenPrivateWindow, NS_ERROR_FAILURE);
+
+ rv = mHiddenPrivateWindow->GetDocShell(getter_AddRefs(docShell));
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsPIDOMWindowOuter> hiddenPrivateDOMWindow(docShell->GetWindow());
+ hiddenPrivateDOMWindow.forget(aWindow);
+ return *aWindow ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsAppShellService::GetHasHiddenPrivateWindow(bool* aHasPrivateWindow)
+{
+ NS_ENSURE_ARG_POINTER(aHasPrivateWindow);
+
+ *aHasPrivateWindow = !!mHiddenPrivateWindow;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAppShellService::GetApplicationProvidedHiddenWindow(bool* aAPHW)
+{
+ *aAPHW = mApplicationProvidedHiddenWindow;
+ return NS_OK;
+}
+
+/*
+ * Register a new top level window (created elsewhere)
+ */
+NS_IMETHODIMP
+nsAppShellService::RegisterTopLevelWindow(nsIXULWindow* aWindow)
+{
+ NS_ENSURE_ARG_POINTER(aWindow);
+
+ nsCOMPtr<nsIDocShell> docShell;
+ aWindow->GetDocShell(getter_AddRefs(docShell));
+ NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsPIDOMWindowOuter> domWindow(docShell->GetWindow());
+ NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
+ domWindow->SetInitialPrincipalToSubject();
+
+ // tell the window mediator about the new window
+ nsCOMPtr<nsIWindowMediator> mediator
+ ( do_GetService(NS_WINDOWMEDIATOR_CONTRACTID) );
+ NS_ASSERTION(mediator, "Couldn't get window mediator.");
+
+ if (mediator)
+ mediator->RegisterWindow(aWindow);
+
+ // tell the window watcher about the new window
+ nsCOMPtr<nsPIWindowWatcher> wwatcher ( do_GetService(NS_WINDOWWATCHER_CONTRACTID) );
+ NS_ASSERTION(wwatcher, "No windowwatcher?");
+ if (wwatcher && domWindow) {
+ wwatcher->AddWindow(domWindow, 0);
+ }
+
+ // an ongoing attempt to quit is stopped by a newly opened window
+ nsCOMPtr<nsIObserverService> obssvc = services::GetObserverService();
+ NS_ASSERTION(obssvc, "Couldn't get observer service.");
+
+ if (obssvc) {
+ obssvc->NotifyObservers(aWindow, "xul-window-registered", nullptr);
+ nsXULWindow* xulWindow = static_cast<nsXULWindow*>(aWindow);
+ xulWindow->WasRegistered();
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsAppShellService::UnregisterTopLevelWindow(nsIXULWindow* aWindow)
+{
+ if (mXPCOMShuttingDown) {
+ /* return an error code in order to:
+ - avoid doing anything with other member variables while we are in
+ the destructor
+ - notify the caller not to release the AppShellService after
+ unregistering the window
+ (we don't want to be deleted twice consecutively to
+ mHiddenWindow->Destroy() in our destructor)
+ */
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_ENSURE_ARG_POINTER(aWindow);
+
+ if (aWindow == mHiddenWindow) {
+ // CreateHiddenWindow() does not register the window, so we're done.
+ return NS_OK;
+ }
+ if (aWindow == mHiddenPrivateWindow) {
+ // CreateHiddenWindow() does not register the window, so we're done.
+ return NS_OK;
+ }
+
+ // tell the window mediator
+ nsCOMPtr<nsIWindowMediator> mediator
+ ( do_GetService(NS_WINDOWMEDIATOR_CONTRACTID) );
+ NS_ASSERTION(mediator, "Couldn't get window mediator. Doing xpcom shutdown?");
+
+ if (mediator)
+ mediator->UnregisterWindow(aWindow);
+
+ // tell the window watcher
+ nsCOMPtr<nsPIWindowWatcher> wwatcher ( do_GetService(NS_WINDOWWATCHER_CONTRACTID) );
+ NS_ASSERTION(wwatcher, "Couldn't get windowwatcher, doing xpcom shutdown?");
+ if (wwatcher) {
+ nsCOMPtr<nsIDocShell> docShell;
+ aWindow->GetDocShell(getter_AddRefs(docShell));
+ if (docShell) {
+ nsCOMPtr<nsPIDOMWindowOuter> domWindow(docShell->GetWindow());
+ if (domWindow)
+ wwatcher->RemoveWindow(domWindow);
+ }
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsAppShellService::Observe(nsISupports* aSubject, const char *aTopic,
+ const char16_t *aData)
+{
+ if (!strcmp(aTopic, "xpcom-will-shutdown")) {
+ mXPCOMWillShutDown = true;
+ } else if (!strcmp(aTopic, "xpcom-shutdown")) {
+ mXPCOMShuttingDown = true;
+ if (mHiddenWindow) {
+ mHiddenWindow->Destroy();
+ }
+ if (mHiddenPrivateWindow) {
+ mHiddenPrivateWindow->Destroy();
+ }
+ } else {
+ NS_ERROR("Unexpected observer topic!");
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAppShellService::StartEventLoopLagTracking(bool* aResult)
+{
+#ifdef MOZ_INSTRUMENT_EVENT_LOOP
+ *aResult = mozilla::InitEventTracing(true);
+#endif
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAppShellService::StopEventLoopLagTracking()
+{
+#ifdef MOZ_INSTRUMENT_EVENT_LOOP
+ mozilla::ShutdownEventTracing();
+#endif
+ return NS_OK;
+}
diff --git a/xpfe/appshell/nsAppShellService.h b/xpfe/appshell/nsAppShellService.h
new file mode 100644
index 000000000..feb7b9947
--- /dev/null
+++ b/xpfe/appshell/nsAppShellService.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __nsAppShellService_h
+#define __nsAppShellService_h
+
+#include "nsIAppShellService.h"
+#include "nsIObserver.h"
+
+//Interfaces Needed
+#include "nsWebShellWindow.h"
+#include "nsStringFwd.h"
+#include "nsAutoPtr.h"
+#include "nsITabParent.h"
+#include "mozilla/Attributes.h"
+
+// {0099907D-123C-4853-A46A-43098B5FB68C}
+#define NS_APPSHELLSERVICE_CID \
+{ 0x99907d, 0x123c, 0x4853, { 0xa4, 0x6a, 0x43, 0x9, 0x8b, 0x5f, 0xb6, 0x8c } }
+
+class nsAppShellService final : public nsIAppShellService,
+ public nsIObserver
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIAPPSHELLSERVICE
+ NS_DECL_NSIOBSERVER
+
+ nsAppShellService();
+
+protected:
+ ~nsAppShellService();
+
+ nsresult CreateHiddenWindowHelper(bool aIsPrivate);
+ void EnsurePrivateHiddenWindow();
+
+ nsresult JustCreateTopWindow(nsIXULWindow *aParent,
+ nsIURI *aUrl,
+ uint32_t aChromeMask,
+ int32_t aInitialWidth, int32_t aInitialHeight,
+ bool aIsHiddenWindow,
+ nsITabParent *aOpeningTab,
+ mozIDOMWindowProxy *aOpenerWindow,
+ nsWebShellWindow **aResult);
+ uint32_t CalculateWindowZLevel(nsIXULWindow *aParent, uint32_t aChromeMask);
+
+ RefPtr<nsWebShellWindow> mHiddenWindow;
+ RefPtr<nsWebShellWindow> mHiddenPrivateWindow;
+ bool mXPCOMWillShutDown;
+ bool mXPCOMShuttingDown;
+ uint16_t mModalWindowCount;
+ bool mApplicationProvidedHiddenWindow;
+ uint32_t mScreenId;
+};
+
+#endif
diff --git a/xpfe/appshell/nsAppShellWindowEnumerator.cpp b/xpfe/appshell/nsAppShellWindowEnumerator.cpp
new file mode 100644
index 000000000..dc5fc120a
--- /dev/null
+++ b/xpfe/appshell/nsAppShellWindowEnumerator.cpp
@@ -0,0 +1,495 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAppShellWindowEnumerator.h"
+
+#include "nsIContentViewer.h"
+#include "nsIDocShell.h"
+#include "nsIDocument.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMElement.h"
+#include "nsIDOMWindow.h"
+#include "nsIFactory.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIXULWindow.h"
+
+#include "nsWindowMediator.h"
+
+//
+// static helper functions
+//
+
+static nsCOMPtr<nsIDOMNode> GetDOMNodeFromDocShell(nsIDocShell *aShell);
+static void GetAttribute(nsIXULWindow *inWindow, const nsAString &inAttribute,
+ nsAString &outValue);
+static void GetWindowType(nsIXULWindow* inWindow, nsString &outType);
+
+nsCOMPtr<nsIDOMNode> GetDOMNodeFromDocShell(nsIDocShell *aShell)
+{
+ nsCOMPtr<nsIDOMNode> node;
+
+ nsCOMPtr<nsIContentViewer> cv;
+ aShell->GetContentViewer(getter_AddRefs(cv));
+ if (cv) {
+ nsCOMPtr<nsIDOMDocument> domdoc(do_QueryInterface(cv->GetDocument()));
+ if (domdoc) {
+ nsCOMPtr<nsIDOMElement> element;
+ domdoc->GetDocumentElement(getter_AddRefs(element));
+ if (element)
+ node = element;
+ }
+ }
+
+ return node;
+}
+
+// generic "retrieve the value of a XUL attribute" function
+void GetAttribute(nsIXULWindow *inWindow, const nsAString &inAttribute,
+ nsAString &outValue)
+{
+ nsCOMPtr<nsIDocShell> shell;
+ if (inWindow && NS_SUCCEEDED(inWindow->GetDocShell(getter_AddRefs(shell)))) {
+ nsCOMPtr<nsIDOMNode> node(GetDOMNodeFromDocShell(shell));
+ if (node) {
+ nsCOMPtr<nsIDOMElement> webshellElement(do_QueryInterface(node));
+ if (webshellElement)
+ webshellElement->GetAttribute(inAttribute, outValue);
+ }
+ }
+}
+
+// retrieve the window type, stored as the value of a particular
+// attribute in its XUL window tag
+void GetWindowType(nsIXULWindow* aWindow, nsString &outType)
+{
+ GetAttribute(aWindow, NS_LITERAL_STRING("windowtype"), outType);
+}
+
+//
+// nsWindowInfo
+//
+
+nsWindowInfo::nsWindowInfo(nsIXULWindow* inWindow, int32_t inTimeStamp) :
+ mWindow(inWindow),mTimeStamp(inTimeStamp),mZLevel(nsIXULWindow::normalZ)
+{
+ ReferenceSelf(true, true);
+}
+
+nsWindowInfo::~nsWindowInfo()
+{
+}
+
+// return true if the window described by this WindowInfo has a type
+// equal to the given type
+bool nsWindowInfo::TypeEquals(const nsAString &aType)
+{
+ nsAutoString rtnString;
+ GetWindowType(mWindow, rtnString);
+ return rtnString == aType;
+}
+
+// insert the struct into their two linked lists, in position after the
+// given (independent) method arguments
+void nsWindowInfo::InsertAfter(nsWindowInfo *inOlder , nsWindowInfo *inHigher)
+{
+ if (inOlder) {
+ mOlder = inOlder;
+ mYounger = inOlder->mYounger;
+ mOlder->mYounger = this;
+ if (mOlder->mOlder == mOlder)
+ mOlder->mOlder = this;
+ mYounger->mOlder = this;
+ if (mYounger->mYounger == mYounger)
+ mYounger->mYounger = this;
+ }
+ if (inHigher) {
+ mHigher = inHigher;
+ mLower = inHigher->mLower;
+ mHigher->mLower = this;
+ if (mHigher->mHigher == mHigher)
+ mHigher->mHigher = this;
+ mLower->mHigher = this;
+ if (mLower->mLower == mLower)
+ mLower->mLower = this;
+ }
+}
+
+// remove the struct from its linked lists
+void nsWindowInfo::Unlink(bool inAge, bool inZ)
+{
+ if (inAge) {
+ mOlder->mYounger = mYounger;
+ mYounger->mOlder = mOlder;
+ }
+ if (inZ) {
+ mLower->mHigher = mHigher;
+ mHigher->mLower = mLower;
+ }
+ ReferenceSelf(inAge, inZ);
+}
+
+// initialize the struct to be a valid linked list of one element
+void nsWindowInfo::ReferenceSelf(bool inAge, bool inZ)
+{
+ if (inAge) {
+ mYounger = this;
+ mOlder = this;
+ }
+ if (inZ) {
+ mLower = this;
+ mHigher = this;
+ }
+}
+
+//
+// nsAppShellWindowEnumerator
+//
+
+NS_IMPL_ISUPPORTS(nsAppShellWindowEnumerator, nsISimpleEnumerator)
+
+nsAppShellWindowEnumerator::nsAppShellWindowEnumerator(
+ const char16_t* aTypeString,
+ nsWindowMediator& aMediator) :
+ mWindowMediator(&aMediator), mType(aTypeString), mCurrentPosition(nullptr)
+{
+ mWindowMediator->AddEnumerator(this);
+ NS_ADDREF(mWindowMediator);
+}
+
+nsAppShellWindowEnumerator::~nsAppShellWindowEnumerator()
+{
+ mWindowMediator->RemoveEnumerator(this);
+ NS_RELEASE(mWindowMediator);
+}
+
+// after mCurrentPosition has been initialized to point to the beginning
+// of the appropriate list, adjust it if necessary
+void nsAppShellWindowEnumerator::AdjustInitialPosition()
+{
+ if (!mType.IsEmpty() && mCurrentPosition && !mCurrentPosition->TypeEquals(mType))
+ mCurrentPosition = FindNext();
+}
+
+NS_IMETHODIMP nsAppShellWindowEnumerator::HasMoreElements(bool *retval)
+{
+ if (!retval)
+ return NS_ERROR_INVALID_ARG;
+
+ *retval = mCurrentPosition ? true : false;
+ return NS_OK;
+}
+
+// if a window is being removed adjust the iterator's current position
+void nsAppShellWindowEnumerator::WindowRemoved(nsWindowInfo *inInfo)
+{
+ if (mCurrentPosition == inInfo)
+ mCurrentPosition = FindNext();
+}
+
+//
+// nsASDOMWindowEnumerator
+//
+
+nsASDOMWindowEnumerator::nsASDOMWindowEnumerator(
+ const char16_t* aTypeString,
+ nsWindowMediator& aMediator) :
+ nsAppShellWindowEnumerator(aTypeString, aMediator)
+{
+}
+
+nsASDOMWindowEnumerator::~nsASDOMWindowEnumerator()
+{
+}
+
+NS_IMETHODIMP nsASDOMWindowEnumerator::GetNext(nsISupports **retval)
+{
+ if (!retval)
+ return NS_ERROR_INVALID_ARG;
+
+ *retval = nullptr;
+ while (mCurrentPosition) {
+ nsCOMPtr<nsPIDOMWindowOuter> domWindow;
+ nsWindowMediator::GetDOMWindow(mCurrentPosition->mWindow, domWindow);
+ mCurrentPosition = FindNext();
+ if (domWindow)
+ return CallQueryInterface(domWindow, retval);
+ }
+ return NS_OK;
+}
+
+//
+// nsASXULWindowEnumerator
+//
+
+nsASXULWindowEnumerator::nsASXULWindowEnumerator(
+ const char16_t* aTypeString,
+ nsWindowMediator& aMediator) :
+ nsAppShellWindowEnumerator(aTypeString, aMediator)
+{
+}
+
+nsASXULWindowEnumerator::~nsASXULWindowEnumerator()
+{
+}
+
+NS_IMETHODIMP nsASXULWindowEnumerator::GetNext(nsISupports **retval)
+{
+ if (!retval)
+ return NS_ERROR_INVALID_ARG;
+
+ *retval = nullptr;
+ if (mCurrentPosition) {
+ CallQueryInterface(mCurrentPosition->mWindow, retval);
+ mCurrentPosition = FindNext();
+ }
+ return NS_OK;
+}
+
+//
+// nsASDOMWindowEarlyToLateEnumerator
+//
+
+nsASDOMWindowEarlyToLateEnumerator::nsASDOMWindowEarlyToLateEnumerator(
+ const char16_t *aTypeString,
+ nsWindowMediator &aMediator) :
+ nsASDOMWindowEnumerator(aTypeString, aMediator)
+{
+ mCurrentPosition = aMediator.mOldestWindow;
+ AdjustInitialPosition();
+}
+
+nsASDOMWindowEarlyToLateEnumerator::~nsASDOMWindowEarlyToLateEnumerator()
+{
+}
+
+nsWindowInfo *nsASDOMWindowEarlyToLateEnumerator::FindNext()
+{
+ nsWindowInfo *info,
+ *listEnd;
+ bool allWindows = mType.IsEmpty();
+
+ // see nsXULWindowEarlyToLateEnumerator::FindNext
+ if (!mCurrentPosition)
+ return nullptr;
+
+ info = mCurrentPosition->mYounger;
+ listEnd = mWindowMediator->mOldestWindow;
+
+ while (info != listEnd) {
+ if (allWindows || info->TypeEquals(mType))
+ return info;
+ info = info->mYounger;
+ }
+
+ return nullptr;
+}
+
+//
+// nsASXULWindowEarlyToLateEnumerator
+//
+
+nsASXULWindowEarlyToLateEnumerator::nsASXULWindowEarlyToLateEnumerator(
+ const char16_t *aTypeString,
+ nsWindowMediator &aMediator) :
+ nsASXULWindowEnumerator(aTypeString, aMediator)
+{
+ mCurrentPosition = aMediator.mOldestWindow;
+ AdjustInitialPosition();
+}
+
+nsASXULWindowEarlyToLateEnumerator::~nsASXULWindowEarlyToLateEnumerator()
+{
+}
+
+nsWindowInfo *nsASXULWindowEarlyToLateEnumerator::FindNext()
+{
+ nsWindowInfo *info,
+ *listEnd;
+ bool allWindows = mType.IsEmpty();
+
+ /* mCurrentPosition null is assumed to mean that the enumerator has run
+ its course and is now basically useless. It could also be interpreted
+ to mean that it was created at a time when there were no windows. In
+ that case it would probably be more appropriate to check to see whether
+ windows have subsequently been added. But it's not guaranteed that we'll
+ pick up newly added windows anyway (if they occurred previous to our
+ current position) so we just don't worry about that. */
+ if (!mCurrentPosition)
+ return nullptr;
+
+ info = mCurrentPosition->mYounger;
+ listEnd = mWindowMediator->mOldestWindow;
+
+ while (info != listEnd) {
+ if (allWindows || info->TypeEquals(mType))
+ return info;
+ info = info->mYounger;
+ }
+
+ return nullptr;
+}
+
+//
+// nsASDOMWindowFrontToBackEnumerator
+//
+
+nsASDOMWindowFrontToBackEnumerator::nsASDOMWindowFrontToBackEnumerator(
+ const char16_t *aTypeString,
+ nsWindowMediator &aMediator) :
+ nsASDOMWindowEnumerator(aTypeString, aMediator)
+{
+ mCurrentPosition = aMediator.mTopmostWindow;
+ AdjustInitialPosition();
+}
+
+nsASDOMWindowFrontToBackEnumerator::~nsASDOMWindowFrontToBackEnumerator()
+{
+}
+
+nsWindowInfo *nsASDOMWindowFrontToBackEnumerator::FindNext()
+{
+ nsWindowInfo *info,
+ *listEnd;
+ bool allWindows = mType.IsEmpty();
+
+ // see nsXULWindowEarlyToLateEnumerator::FindNext
+ if (!mCurrentPosition)
+ return nullptr;
+
+ info = mCurrentPosition->mLower;
+ listEnd = mWindowMediator->mTopmostWindow;
+
+ while (info != listEnd) {
+ if (allWindows || info->TypeEquals(mType))
+ return info;
+ info = info->mLower;
+ }
+
+ return nullptr;
+}
+
+//
+// nsASXULWindowFrontToBackEnumerator
+//
+
+nsASXULWindowFrontToBackEnumerator::nsASXULWindowFrontToBackEnumerator(
+ const char16_t *aTypeString,
+ nsWindowMediator &aMediator) :
+ nsASXULWindowEnumerator(aTypeString, aMediator)
+{
+ mCurrentPosition = aMediator.mTopmostWindow;
+ AdjustInitialPosition();
+}
+
+nsASXULWindowFrontToBackEnumerator::~nsASXULWindowFrontToBackEnumerator()
+{
+}
+
+nsWindowInfo *nsASXULWindowFrontToBackEnumerator::FindNext()
+{
+ nsWindowInfo *info,
+ *listEnd;
+ bool allWindows = mType.IsEmpty();
+
+ // see nsXULWindowEarlyToLateEnumerator::FindNext
+ if (!mCurrentPosition)
+ return nullptr;
+
+ info = mCurrentPosition->mLower;
+ listEnd = mWindowMediator->mTopmostWindow;
+
+ while (info != listEnd) {
+ if (allWindows || info->TypeEquals(mType))
+ return info;
+ info = info->mLower;
+ }
+
+ return nullptr;
+}
+
+//
+// nsASDOMWindowBackToFrontEnumerator
+//
+
+nsASDOMWindowBackToFrontEnumerator::nsASDOMWindowBackToFrontEnumerator(
+ const char16_t *aTypeString,
+ nsWindowMediator &aMediator) :
+ nsASDOMWindowEnumerator(aTypeString, aMediator)
+{
+ mCurrentPosition = aMediator.mTopmostWindow ?
+ aMediator.mTopmostWindow->mHigher : nullptr;
+ AdjustInitialPosition();
+}
+
+nsASDOMWindowBackToFrontEnumerator::~nsASDOMWindowBackToFrontEnumerator()
+{
+}
+
+nsWindowInfo *nsASDOMWindowBackToFrontEnumerator::FindNext()
+{
+ nsWindowInfo *info,
+ *listEnd;
+ bool allWindows = mType.IsEmpty();
+
+ // see nsXULWindowEarlyToLateEnumerator::FindNext
+ if (!mCurrentPosition)
+ return nullptr;
+
+ info = mCurrentPosition->mHigher;
+ listEnd = mWindowMediator->mTopmostWindow;
+ if (listEnd)
+ listEnd = listEnd->mHigher;
+
+ while (info != listEnd) {
+ if (allWindows || info->TypeEquals(mType))
+ return info;
+ info = info->mHigher;
+ }
+
+ return nullptr;
+}
+
+//
+// nsASXULWindowBackToFrontEnumerator
+//
+
+nsASXULWindowBackToFrontEnumerator::nsASXULWindowBackToFrontEnumerator(
+ const char16_t *aTypeString,
+ nsWindowMediator &aMediator) :
+ nsASXULWindowEnumerator(aTypeString, aMediator)
+{
+ mCurrentPosition = aMediator.mTopmostWindow ?
+ aMediator.mTopmostWindow->mHigher : nullptr;
+ AdjustInitialPosition();
+}
+
+nsASXULWindowBackToFrontEnumerator::~nsASXULWindowBackToFrontEnumerator()
+{
+}
+
+nsWindowInfo *nsASXULWindowBackToFrontEnumerator::FindNext()
+{
+ nsWindowInfo *info,
+ *listEnd;
+ bool allWindows = mType.IsEmpty();
+
+ // see nsXULWindowEarlyToLateEnumerator::FindNext
+ if (!mCurrentPosition)
+ return nullptr;
+
+ info = mCurrentPosition->mHigher;
+ listEnd = mWindowMediator->mTopmostWindow;
+ if (listEnd)
+ listEnd = listEnd->mHigher;
+
+ while (info != listEnd) {
+ if (allWindows || info->TypeEquals(mType))
+ return info;
+ info = info->mHigher;
+ }
+
+ return nullptr;
+}
diff --git a/xpfe/appshell/nsAppShellWindowEnumerator.h b/xpfe/appshell/nsAppShellWindowEnumerator.h
new file mode 100644
index 000000000..f9a8a2d52
--- /dev/null
+++ b/xpfe/appshell/nsAppShellWindowEnumerator.h
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAppShellWindowEnumerator_h
+#define nsAppShellWindowEnumerator_h
+
+#include "nsCOMPtr.h"
+#include "nsString.h"
+
+#include "nsISimpleEnumerator.h"
+#include "nsIXULWindow.h"
+
+class nsWindowMediator;
+
+//
+// nsWindowInfo
+//
+
+struct nsWindowInfo
+{
+ nsWindowInfo(nsIXULWindow* inWindow, int32_t inTimeStamp);
+ ~nsWindowInfo();
+
+ nsCOMPtr<nsIXULWindow> mWindow;
+ int32_t mTimeStamp;
+ uint32_t mZLevel;
+
+ // each struct is in two, independent, circular, doubly-linked lists
+ nsWindowInfo *mYounger, // next younger in sequence
+ *mOlder;
+ nsWindowInfo *mLower, // next lower in z-order
+ *mHigher;
+
+ bool TypeEquals(const nsAString &aType);
+ void InsertAfter(nsWindowInfo *inOlder, nsWindowInfo *inHigher);
+ void Unlink(bool inAge, bool inZ);
+ void ReferenceSelf(bool inAge, bool inZ);
+};
+
+//
+// virtual enumerators
+//
+
+class nsAppShellWindowEnumerator : public nsISimpleEnumerator {
+
+friend class nsWindowMediator;
+
+public:
+ nsAppShellWindowEnumerator(const char16_t* aTypeString,
+ nsWindowMediator& inMediator);
+ NS_IMETHOD GetNext(nsISupports **retval) override = 0;
+ NS_IMETHOD HasMoreElements(bool *retval) override;
+
+ NS_DECL_ISUPPORTS
+
+protected:
+
+ virtual ~nsAppShellWindowEnumerator();
+
+ void AdjustInitialPosition();
+ virtual nsWindowInfo *FindNext() = 0;
+
+ void WindowRemoved(nsWindowInfo *inInfo);
+
+ nsWindowMediator *mWindowMediator;
+ nsString mType;
+ nsWindowInfo *mCurrentPosition;
+};
+
+class nsASDOMWindowEnumerator : public nsAppShellWindowEnumerator {
+
+public:
+ nsASDOMWindowEnumerator(const char16_t* aTypeString,
+ nsWindowMediator& inMediator);
+ virtual ~nsASDOMWindowEnumerator();
+ NS_IMETHOD GetNext(nsISupports **retval);
+};
+
+class nsASXULWindowEnumerator : public nsAppShellWindowEnumerator {
+
+public:
+ nsASXULWindowEnumerator(const char16_t* aTypeString,
+ nsWindowMediator& inMediator);
+ virtual ~nsASXULWindowEnumerator();
+ NS_IMETHOD GetNext(nsISupports **retval);
+};
+
+//
+// concrete enumerators
+//
+
+class nsASDOMWindowEarlyToLateEnumerator : public nsASDOMWindowEnumerator {
+
+public:
+ nsASDOMWindowEarlyToLateEnumerator(const char16_t* aTypeString,
+ nsWindowMediator& inMediator);
+
+ virtual ~nsASDOMWindowEarlyToLateEnumerator();
+
+protected:
+ virtual nsWindowInfo *FindNext();
+};
+
+class nsASXULWindowEarlyToLateEnumerator : public nsASXULWindowEnumerator {
+
+public:
+ nsASXULWindowEarlyToLateEnumerator(const char16_t* aTypeString,
+ nsWindowMediator& inMediator);
+
+ virtual ~nsASXULWindowEarlyToLateEnumerator();
+
+protected:
+ virtual nsWindowInfo *FindNext();
+};
+
+class nsASDOMWindowFrontToBackEnumerator : public nsASDOMWindowEnumerator {
+
+public:
+ nsASDOMWindowFrontToBackEnumerator(const char16_t* aTypeString,
+ nsWindowMediator& inMediator);
+
+ virtual ~nsASDOMWindowFrontToBackEnumerator();
+
+protected:
+ virtual nsWindowInfo *FindNext();
+};
+
+class nsASXULWindowFrontToBackEnumerator : public nsASXULWindowEnumerator {
+
+public:
+ nsASXULWindowFrontToBackEnumerator(const char16_t* aTypeString,
+ nsWindowMediator& inMediator);
+
+ virtual ~nsASXULWindowFrontToBackEnumerator();
+
+protected:
+ virtual nsWindowInfo *FindNext();
+};
+
+class nsASDOMWindowBackToFrontEnumerator : public nsASDOMWindowEnumerator {
+
+public:
+ nsASDOMWindowBackToFrontEnumerator(const char16_t* aTypeString,
+ nsWindowMediator& inMediator);
+
+ virtual ~nsASDOMWindowBackToFrontEnumerator();
+
+protected:
+ virtual nsWindowInfo *FindNext();
+};
+
+class nsASXULWindowBackToFrontEnumerator : public nsASXULWindowEnumerator {
+
+public:
+ nsASXULWindowBackToFrontEnumerator(const char16_t* aTypeString,
+ nsWindowMediator& inMediator);
+
+ virtual ~nsASXULWindowBackToFrontEnumerator();
+
+protected:
+ virtual nsWindowInfo *FindNext();
+};
+
+#endif
diff --git a/xpfe/appshell/nsChromeTreeOwner.cpp b/xpfe/appshell/nsChromeTreeOwner.cpp
new file mode 100644
index 000000000..63fa86373
--- /dev/null
+++ b/xpfe/appshell/nsChromeTreeOwner.cpp
@@ -0,0 +1,561 @@
+/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Local Includes
+#include "nsChromeTreeOwner.h"
+#include "nsXULWindow.h"
+
+// Helper Classes
+#include "nsString.h"
+#include "nsIEmbeddingSiteWindow.h"
+#include "nsIServiceManager.h"
+#include "nsIDocShellTreeItem.h"
+
+// Interfaces needed to include
+#include "nsIPrompt.h"
+#include "nsIAuthPrompt.h"
+#include "nsIBrowserDOMWindow.h"
+#include "nsIWebProgress.h"
+#include "nsIWidget.h"
+#include "nsIWindowMediator.h"
+#include "nsIDOMChromeWindow.h"
+#include "nsIDOMNode.h"
+#include "nsIDOMElement.h"
+#include "nsIDOMNodeList.h"
+#include "nsIDOMXULElement.h"
+#include "nsIXULBrowserWindow.h"
+#include "mozilla/dom/Element.h"
+
+using namespace mozilla;
+
+//*****************************************************************************
+// nsChromeTreeOwner string literals
+//*****************************************************************************
+
+struct nsChromeTreeOwnerLiterals
+{
+ const nsLiteralString kPersist;
+ const nsLiteralString kScreenX;
+ const nsLiteralString kScreenY;
+ const nsLiteralString kWidth;
+ const nsLiteralString kHeight;
+ const nsLiteralString kSizemode;
+ const nsLiteralString kSpace;
+
+ nsChromeTreeOwnerLiterals()
+ : NS_LITERAL_STRING_INIT(kPersist,"persist")
+ , NS_LITERAL_STRING_INIT(kScreenX,"screenX")
+ , NS_LITERAL_STRING_INIT(kScreenY,"screenY")
+ , NS_LITERAL_STRING_INIT(kWidth,"width")
+ , NS_LITERAL_STRING_INIT(kHeight,"height")
+ , NS_LITERAL_STRING_INIT(kSizemode,"sizemode")
+ , NS_LITERAL_STRING_INIT(kSpace," ")
+ {}
+};
+
+static nsChromeTreeOwnerLiterals *gLiterals;
+
+nsresult
+nsChromeTreeOwner::InitGlobals()
+{
+ NS_ASSERTION(gLiterals == nullptr, "already initialized");
+ gLiterals = new nsChromeTreeOwnerLiterals();
+ return NS_OK;
+}
+
+void
+nsChromeTreeOwner::FreeGlobals()
+{
+ delete gLiterals;
+ gLiterals = nullptr;
+}
+
+//*****************************************************************************
+//*** nsChromeTreeOwner: Object Management
+//*****************************************************************************
+
+nsChromeTreeOwner::nsChromeTreeOwner() : mXULWindow(nullptr)
+{
+}
+
+nsChromeTreeOwner::~nsChromeTreeOwner()
+{
+}
+
+//*****************************************************************************
+// nsChromeTreeOwner::nsISupports
+//*****************************************************************************
+
+NS_IMPL_ADDREF(nsChromeTreeOwner)
+NS_IMPL_RELEASE(nsChromeTreeOwner)
+
+NS_INTERFACE_MAP_BEGIN(nsChromeTreeOwner)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocShellTreeOwner)
+ NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeOwner)
+ NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
+ NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
+ NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+NS_INTERFACE_MAP_END
+
+//*****************************************************************************
+// nsChromeTreeOwner::nsIInterfaceRequestor
+//*****************************************************************************
+
+NS_IMETHODIMP nsChromeTreeOwner::GetInterface(const nsIID& aIID, void** aSink)
+{
+ NS_ENSURE_ARG_POINTER(aSink);
+
+ if(aIID.Equals(NS_GET_IID(nsIPrompt))) {
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetInterface(aIID, aSink);
+ }
+ if(aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetInterface(aIID, aSink);
+ }
+ if(aIID.Equals(NS_GET_IID(nsIWebBrowserChrome))) {
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetInterface(aIID, aSink);
+ }
+ if (aIID.Equals(NS_GET_IID(nsIEmbeddingSiteWindow))) {
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetInterface(aIID, aSink);
+ }
+ if (aIID.Equals(NS_GET_IID(nsIXULWindow))) {
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->QueryInterface(aIID, aSink);
+ }
+
+ return QueryInterface(aIID, aSink);
+}
+
+//*****************************************************************************
+// nsChromeTreeOwner::nsIDocShellTreeOwner
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsChromeTreeOwner::ContentShellAdded(nsIDocShellTreeItem* aContentShell,
+ bool aPrimary, bool aTargetable,
+ const nsAString& aID)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->ContentShellAdded(aContentShell, aPrimary, aTargetable,
+ aID);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::ContentShellRemoved(nsIDocShellTreeItem* aContentShell)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->ContentShellRemoved(aContentShell);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aShell)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetPrimaryContentShell(aShell);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::TabParentAdded(nsITabParent* aTab, bool aPrimary)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->TabParentAdded(aTab, aPrimary);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::TabParentRemoved(nsITabParent* aTab)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->TabParentRemoved(aTab);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::GetPrimaryTabParent(nsITabParent** aTab)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetPrimaryTabParent(aTab);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::GetPrimaryContentSize(int32_t* aWidth,
+ int32_t* aHeight)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetPrimaryContentSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::SetPrimaryContentSize(int32_t aWidth,
+ int32_t aHeight)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SetPrimaryContentSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::GetRootShellSize(int32_t* aWidth,
+ int32_t* aHeight)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetRootShellSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::SetRootShellSize(int32_t aWidth,
+ int32_t aHeight)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SetRootShellSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem,
+ int32_t aCX, int32_t aCY)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SizeShellTo(aShellItem, aCX, aCY);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::SetPersistence(bool aPersistPosition,
+ bool aPersistSize,
+ bool aPersistSizeMode)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ nsCOMPtr<dom::Element> docShellElement = mXULWindow->GetWindowDOMElement();
+ if (!docShellElement)
+ return NS_ERROR_FAILURE;
+
+ nsAutoString persistString;
+ docShellElement->GetAttribute(gLiterals->kPersist, persistString);
+
+ bool saveString = false;
+ int32_t index;
+
+#define FIND_PERSIST_STRING(aString, aCond) \
+ index = persistString.Find(aString); \
+ if (!aCond && index > kNotFound) { \
+ persistString.Cut(index, aString.Length()); \
+ saveString = true; \
+ } else if (aCond && index == kNotFound) { \
+ persistString.Append(gLiterals->kSpace + aString); \
+ saveString = true; \
+ }
+ FIND_PERSIST_STRING(gLiterals->kScreenX, aPersistPosition);
+ FIND_PERSIST_STRING(gLiterals->kScreenY, aPersistPosition);
+ FIND_PERSIST_STRING(gLiterals->kWidth, aPersistSize);
+ FIND_PERSIST_STRING(gLiterals->kHeight, aPersistSize);
+ FIND_PERSIST_STRING(gLiterals->kSizemode, aPersistSizeMode);
+
+ ErrorResult rv;
+ if (saveString) {
+ docShellElement->SetAttribute(gLiterals->kPersist, persistString, rv);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::GetPersistence(bool* aPersistPosition,
+ bool* aPersistSize,
+ bool* aPersistSizeMode)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ nsCOMPtr<dom::Element> docShellElement = mXULWindow->GetWindowDOMElement();
+ if (!docShellElement)
+ return NS_ERROR_FAILURE;
+
+ nsAutoString persistString;
+ docShellElement->GetAttribute(gLiterals->kPersist, persistString);
+
+ // data structure doesn't quite match the question, but it's close enough
+ // for what we want (since this method is never actually called...)
+ if (aPersistPosition)
+ *aPersistPosition = persistString.Find(gLiterals->kScreenX) > kNotFound ||
+ persistString.Find(gLiterals->kScreenY) > kNotFound;
+ if (aPersistSize)
+ *aPersistSize = persistString.Find(gLiterals->kWidth) > kNotFound ||
+ persistString.Find(gLiterals->kHeight) > kNotFound;
+ if (aPersistSizeMode)
+ *aPersistSizeMode = persistString.Find(gLiterals->kSizemode) > kNotFound;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::GetTargetableShellCount(uint32_t* aResult)
+{
+ *aResult = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::GetHasPrimaryContent(bool* aResult)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetHasPrimaryContent(aResult);
+}
+
+//*****************************************************************************
+// nsChromeTreeOwner::nsIBaseWindow
+//*****************************************************************************
+
+NS_IMETHODIMP nsChromeTreeOwner::InitWindow(nativeWindow aParentNativeWindow,
+ nsIWidget* parentWidget, int32_t x, int32_t y, int32_t cx, int32_t cy)
+{
+ // Ignore widget parents for now. Don't think those are a vaild thing to call.
+ NS_ENSURE_SUCCESS(SetPositionAndSize(x, y, cx, cy, 0), NS_ERROR_FAILURE);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::Create()
+{
+ NS_ASSERTION(false, "You can't call this");
+ return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::Destroy()
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->Destroy();
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetUnscaledDevicePixelsPerCSSPixel(double *aScale)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetUnscaledDevicePixelsPerCSSPixel(aScale);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetDevicePixelsPerDesktopPixel(double *aScale)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetDevicePixelsPerDesktopPixel(aScale);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::SetPositionDesktopPix(int32_t x, int32_t y)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SetPositionDesktopPix(x, y);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::SetPosition(int32_t x, int32_t y)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SetPosition(x, y);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetPosition(int32_t* x, int32_t* y)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetPosition(x, y);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::SetSize(int32_t cx, int32_t cy, bool fRepaint)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SetSize(cx, cy, fRepaint);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetSize(int32_t* cx, int32_t* cy)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetSize(cx, cy);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::SetPositionAndSize(int32_t x, int32_t y, int32_t cx,
+ int32_t cy, uint32_t aFlags)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SetPositionAndSize(x, y, cx, cy, aFlags);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetPositionAndSize(int32_t* x, int32_t* y, int32_t* cx,
+ int32_t* cy)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetPositionAndSize(x, y, cx, cy);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::Repaint(bool aForce)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->Repaint(aForce);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetParentWidget(nsIWidget** aParentWidget)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetParentWidget(aParentWidget);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::SetParentWidget(nsIWidget* aParentWidget)
+{
+ NS_ASSERTION(false, "You can't call this");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetParentNativeWindow(nativeWindow* aParentNativeWindow)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetParentNativeWindow(aParentNativeWindow);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::SetParentNativeWindow(nativeWindow aParentNativeWindow)
+{
+ NS_ASSERTION(false, "You can't call this");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetNativeHandle(nsAString& aNativeHandle)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetNativeHandle(aNativeHandle);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetVisibility(bool* aVisibility)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetVisibility(aVisibility);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::SetVisibility(bool aVisibility)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SetVisibility(aVisibility);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetEnabled(bool *aEnabled)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetEnabled(aEnabled);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::SetEnabled(bool aEnable)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SetEnabled(aEnable);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetMainWidget(nsIWidget** aMainWidget)
+{
+ NS_ENSURE_ARG_POINTER(aMainWidget);
+ NS_ENSURE_STATE(mXULWindow);
+
+ *aMainWidget = mXULWindow->mWindow;
+ NS_IF_ADDREF(*aMainWidget);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::SetFocus()
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SetFocus();
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetTitle(char16_t** aTitle)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetTitle(aTitle);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::SetTitle(const char16_t* aTitle)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SetTitle(aTitle);
+}
+
+//*****************************************************************************
+// nsChromeTreeOwner::nsIWebProgressListener
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsChromeTreeOwner::OnProgressChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ int32_t aCurSelfProgress,
+ int32_t aMaxSelfProgress,
+ int32_t aCurTotalProgress,
+ int32_t aMaxTotalProgress)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::OnStateChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ uint32_t aProgressStateFlags,
+ nsresult aStatus)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::OnLocationChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ nsIURI* aLocation,
+ uint32_t aFlags)
+{
+ bool itsForYou = true;
+
+ if (aWebProgress) {
+ NS_ENSURE_STATE(mXULWindow);
+ nsCOMPtr<mozIDOMWindowProxy> progressWin;
+ aWebProgress->GetDOMWindow(getter_AddRefs(progressWin));
+
+ nsCOMPtr<nsIDocShell> docshell;
+ mXULWindow->GetDocShell(getter_AddRefs(docshell));
+ // XXXkhuey this is totally wrong, bug 1223303.
+ nsCOMPtr<mozIDOMWindowProxy> ourWin(do_QueryInterface(docshell));
+
+ if (ourWin != progressWin)
+ itsForYou = false;
+ }
+
+ // If loading a new root .xul document, then redo chrome.
+ if (itsForYou) {
+ NS_ENSURE_STATE(mXULWindow);
+ mXULWindow->mChromeLoaded = false;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::OnStatusChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ nsresult aStatus,
+ const char16_t* aMessage)
+{
+ return NS_OK;
+}
+
+
+
+NS_IMETHODIMP
+nsChromeTreeOwner::OnSecurityChange(nsIWebProgress *aWebProgress,
+ nsIRequest *aRequest,
+ uint32_t state)
+{
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsChromeTreeOwner: Helpers
+//*****************************************************************************
+
+//*****************************************************************************
+// nsChromeTreeOwner: Accessors
+//*****************************************************************************
+
+void nsChromeTreeOwner::XULWindow(nsXULWindow* aXULWindow)
+{
+ mXULWindow = aXULWindow;
+}
+
+nsXULWindow* nsChromeTreeOwner::XULWindow()
+{
+ return mXULWindow;
+}
diff --git a/xpfe/appshell/nsChromeTreeOwner.h b/xpfe/appshell/nsChromeTreeOwner.h
new file mode 100644
index 000000000..77b0dea89
--- /dev/null
+++ b/xpfe/appshell/nsChromeTreeOwner.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsChromeTreeOwner_h__
+#define nsChromeTreeOwner_h__
+
+// Helper Classes
+#include "nsCOMPtr.h"
+
+// Interfaces Needed
+#include "nsIBaseWindow.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIWebProgressListener.h"
+#include "nsWeakReference.h"
+
+class nsXULWindow;
+
+class nsChromeTreeOwner : public nsIDocShellTreeOwner,
+ public nsIBaseWindow,
+ public nsIInterfaceRequestor,
+ public nsIWebProgressListener,
+ public nsSupportsWeakReference
+{
+friend class nsXULWindow;
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSIBASEWINDOW
+ NS_DECL_NSIDOCSHELLTREEOWNER
+ NS_DECL_NSIWEBPROGRESSLISTENER
+
+ static nsresult InitGlobals();
+ static void FreeGlobals();
+
+protected:
+ nsChromeTreeOwner();
+ virtual ~nsChromeTreeOwner();
+
+ void XULWindow(nsXULWindow* aXULWindow);
+ nsXULWindow* XULWindow();
+
+protected:
+ nsXULWindow* mXULWindow;
+};
+
+#endif /* nsChromeTreeOwner_h__ */
diff --git a/xpfe/appshell/nsContentTreeOwner.cpp b/xpfe/appshell/nsContentTreeOwner.cpp
new file mode 100644
index 000000000..b39b7610f
--- /dev/null
+++ b/xpfe/appshell/nsContentTreeOwner.cpp
@@ -0,0 +1,1131 @@
+/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=2 sw=2 et tw=79:
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Local Includes
+#include "nsContentTreeOwner.h"
+#include "nsXULWindow.h"
+
+// Helper Classes
+#include "nsIServiceManager.h"
+#include "nsAutoPtr.h"
+
+// Interfaces needed to be included
+#include "nsIDOMNode.h"
+#include "nsIDOMElement.h"
+#include "nsIDOMNodeList.h"
+#include "nsIDOMWindow.h"
+#include "nsIDOMChromeWindow.h"
+#include "nsIBrowserDOMWindow.h"
+#include "nsIDOMXULElement.h"
+#include "nsIEmbeddingSiteWindow.h"
+#include "nsIPrompt.h"
+#include "nsIAuthPrompt.h"
+#include "nsIWindowMediator.h"
+#include "nsIXULBrowserWindow.h"
+#include "nsIPrincipal.h"
+#include "nsIURIFixup.h"
+#include "nsCDefaultURIFixup.h"
+#include "nsIWebNavigation.h"
+#include "nsDocShellCID.h"
+#include "nsIExternalURLHandlerService.h"
+#include "nsIMIMEInfo.h"
+#include "nsIWidget.h"
+#include "nsWindowWatcher.h"
+#include "mozilla/BrowserElementParent.h"
+
+#include "nsIDOMDocument.h"
+#include "nsIScriptObjectPrincipal.h"
+#include "nsIURI.h"
+#include "nsIDocument.h"
+#if defined(XP_MACOSX)
+#include "nsThreadUtils.h"
+#endif
+
+#include "mozilla/Preferences.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/ScriptSettings.h"
+
+using namespace mozilla;
+
+//*****************************************************************************
+//*** nsSiteWindow declaration
+//*****************************************************************************
+
+class nsSiteWindow : public nsIEmbeddingSiteWindow
+{
+ // nsSiteWindow shares a lifetime with nsContentTreeOwner, and proxies it's
+ // AddRef and Release calls to said object.
+ // When nsContentTreeOwner is destroyed, nsSiteWindow will be destroyed as well.
+ // nsContentTreeOwner is a friend class of nsSiteWindow such that it can call
+ // nsSiteWindow's destructor, which is private, as public destructors
+ // on reference counted classes are generally unsafe.
+ friend class nsContentTreeOwner;
+
+public:
+ explicit nsSiteWindow(nsContentTreeOwner *aAggregator);
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIEMBEDDINGSITEWINDOW
+
+private:
+ virtual ~nsSiteWindow();
+ nsContentTreeOwner *mAggregator;
+};
+
+//*****************************************************************************
+//*** nsContentTreeOwner: Object Management
+//*****************************************************************************
+
+nsContentTreeOwner::nsContentTreeOwner(bool fPrimary) : mXULWindow(nullptr),
+ mPrimary(fPrimary), mContentTitleSetting(false)
+{
+ // note if this fails, QI on nsIEmbeddingSiteWindow(2) will simply fail
+ mSiteWindow = new nsSiteWindow(this);
+}
+
+nsContentTreeOwner::~nsContentTreeOwner()
+{
+ delete mSiteWindow;
+}
+
+//*****************************************************************************
+// nsContentTreeOwner::nsISupports
+//*****************************************************************************
+
+NS_IMPL_ADDREF(nsContentTreeOwner)
+NS_IMPL_RELEASE(nsContentTreeOwner)
+
+NS_INTERFACE_MAP_BEGIN(nsContentTreeOwner)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocShellTreeOwner)
+ NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeOwner)
+ NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
+ NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome)
+ NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome2)
+ NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome3)
+ NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+ NS_INTERFACE_MAP_ENTRY(nsIWindowProvider)
+ // NOTE: This is using aggregation because there are some properties and
+ // method on nsIBaseWindow (which we implement) and on
+ // nsIEmbeddingSiteWindow (which we also implement) that have the same name.
+ // And it just so happens that we want different behavior for these methods
+ // and properties depending on the interface through which they're called
+ // (SetFocus() is a good example here). If it were not for that, we could
+ // ditch the aggregation and just deal with not being able to use NS_DECL_*
+ // macros for this stuff....
+ NS_INTERFACE_MAP_ENTRY_AGGREGATED(nsIEmbeddingSiteWindow, mSiteWindow)
+NS_INTERFACE_MAP_END
+
+//*****************************************************************************
+// nsContentTreeOwner::nsIInterfaceRequestor
+//*****************************************************************************
+
+NS_IMETHODIMP nsContentTreeOwner::GetInterface(const nsIID& aIID, void** aSink)
+{
+ NS_ENSURE_ARG_POINTER(aSink);
+ *aSink = 0;
+
+ if(aIID.Equals(NS_GET_IID(nsIPrompt))) {
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetInterface(aIID, aSink);
+ }
+ if(aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetInterface(aIID, aSink);
+ }
+ if (aIID.Equals(NS_GET_IID(nsIDocShellTreeItem))) {
+ NS_ENSURE_STATE(mXULWindow);
+ nsCOMPtr<nsIDocShell> shell;
+ mXULWindow->GetDocShell(getter_AddRefs(shell));
+ if (shell)
+ return shell->QueryInterface(aIID, aSink);
+ return NS_ERROR_FAILURE;
+ }
+
+ if (aIID.Equals(NS_GET_IID(nsIDOMWindow)) ||
+ aIID.Equals(NS_GET_IID(nsPIDOMWindowOuter))) {
+ NS_ENSURE_STATE(mXULWindow);
+ nsCOMPtr<nsIDocShellTreeItem> shell;
+ mXULWindow->GetPrimaryContentShell(getter_AddRefs(shell));
+ if (shell) {
+ nsCOMPtr<nsIInterfaceRequestor> thing(do_QueryInterface(shell));
+ if (thing)
+ return thing->GetInterface(aIID, aSink);
+ }
+ return NS_ERROR_FAILURE;
+ }
+
+ if (aIID.Equals(NS_GET_IID(nsIXULWindow))) {
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->QueryInterface(aIID, aSink);
+ }
+
+ return QueryInterface(aIID, aSink);
+}
+
+//*****************************************************************************
+// nsContentTreeOwner::nsIDocShellTreeOwner
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsContentTreeOwner::ContentShellAdded(nsIDocShellTreeItem* aContentShell,
+ bool aPrimary, bool aTargetable,
+ const nsAString& aID)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->ContentShellAdded(aContentShell, aPrimary, aTargetable,
+ aID);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::ContentShellRemoved(nsIDocShellTreeItem* aContentShell)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->ContentShellRemoved(aContentShell);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aShell)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetPrimaryContentShell(aShell);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::TabParentAdded(nsITabParent* aTab, bool aPrimary)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->TabParentAdded(aTab, aPrimary);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::TabParentRemoved(nsITabParent* aTab)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->TabParentRemoved(aTab);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::GetPrimaryTabParent(nsITabParent** aTab)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetPrimaryTabParent(aTab);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::GetPrimaryContentSize(int32_t* aWidth,
+ int32_t* aHeight)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetPrimaryContentSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::SetPrimaryContentSize(int32_t aWidth,
+ int32_t aHeight)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SetPrimaryContentSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::GetRootShellSize(int32_t* aWidth,
+ int32_t* aHeight)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetRootShellSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::SetRootShellSize(int32_t aWidth,
+ int32_t aHeight)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SetRootShellSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem,
+ int32_t aCX, int32_t aCY)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SizeShellTo(aShellItem, aCX, aCY);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::SetPersistence(bool aPersistPosition,
+ bool aPersistSize,
+ bool aPersistSizeMode)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ nsCOMPtr<dom::Element> docShellElement = mXULWindow->GetWindowDOMElement();
+ if (!docShellElement)
+ return NS_ERROR_FAILURE;
+
+ nsAutoString persistString;
+ docShellElement->GetAttribute(NS_LITERAL_STRING("persist"), persistString);
+
+ bool saveString = false;
+ int32_t index;
+
+ // Set X
+ index = persistString.Find("screenX");
+ if (!aPersistPosition && index >= 0) {
+ persistString.Cut(index, 7);
+ saveString = true;
+ } else if (aPersistPosition && index < 0) {
+ persistString.AppendLiteral(" screenX");
+ saveString = true;
+ }
+ // Set Y
+ index = persistString.Find("screenY");
+ if (!aPersistPosition && index >= 0) {
+ persistString.Cut(index, 7);
+ saveString = true;
+ } else if (aPersistPosition && index < 0) {
+ persistString.AppendLiteral(" screenY");
+ saveString = true;
+ }
+ // Set CX
+ index = persistString.Find("width");
+ if (!aPersistSize && index >= 0) {
+ persistString.Cut(index, 5);
+ saveString = true;
+ } else if (aPersistSize && index < 0) {
+ persistString.AppendLiteral(" width");
+ saveString = true;
+ }
+ // Set CY
+ index = persistString.Find("height");
+ if (!aPersistSize && index >= 0) {
+ persistString.Cut(index, 6);
+ saveString = true;
+ } else if (aPersistSize && index < 0) {
+ persistString.AppendLiteral(" height");
+ saveString = true;
+ }
+ // Set SizeMode
+ index = persistString.Find("sizemode");
+ if (!aPersistSizeMode && (index >= 0)) {
+ persistString.Cut(index, 8);
+ saveString = true;
+ } else if (aPersistSizeMode && (index < 0)) {
+ persistString.AppendLiteral(" sizemode");
+ saveString = true;
+ }
+
+ ErrorResult rv;
+ if(saveString) {
+ docShellElement->SetAttribute(NS_LITERAL_STRING("persist"), persistString, rv);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::GetPersistence(bool* aPersistPosition,
+ bool* aPersistSize,
+ bool* aPersistSizeMode)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ nsCOMPtr<dom::Element> docShellElement = mXULWindow->GetWindowDOMElement();
+ if (!docShellElement)
+ return NS_ERROR_FAILURE;
+
+ nsAutoString persistString;
+ docShellElement->GetAttribute(NS_LITERAL_STRING("persist"), persistString);
+
+ // data structure doesn't quite match the question, but it's close enough
+ // for what we want (since this method is never actually called...)
+ if (aPersistPosition)
+ *aPersistPosition = persistString.Find("screenX") >= 0 || persistString.Find("screenY") >= 0 ? true : false;
+ if (aPersistSize)
+ *aPersistSize = persistString.Find("width") >= 0 || persistString.Find("height") >= 0 ? true : false;
+ if (aPersistSizeMode)
+ *aPersistSizeMode = persistString.Find("sizemode") >= 0 ? true : false;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::GetTargetableShellCount(uint32_t* aResult)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ *aResult = mXULWindow->mTargetableShells.Count();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::GetHasPrimaryContent(bool* aResult)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetHasPrimaryContent(aResult);
+}
+
+//*****************************************************************************
+// nsContentTreeOwner::nsIWebBrowserChrome3
+//*****************************************************************************
+
+NS_IMETHODIMP nsContentTreeOwner::OnBeforeLinkTraversal(const nsAString &originalTarget,
+ nsIURI *linkURI,
+ nsIDOMNode *linkNode,
+ bool isAppTab,
+ nsAString &_retval)
+{
+ NS_ENSURE_STATE(mXULWindow);
+
+ nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow;
+ mXULWindow->GetXULBrowserWindow(getter_AddRefs(xulBrowserWindow));
+
+ if (xulBrowserWindow)
+ return xulBrowserWindow->OnBeforeLinkTraversal(originalTarget, linkURI,
+ linkNode, isAppTab, _retval);
+
+ _retval = originalTarget;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsContentTreeOwner::ShouldLoadURI(nsIDocShell *aDocShell,
+ nsIURI *aURI,
+ nsIURI *aReferrer,
+ bool *_retval)
+{
+ NS_ENSURE_STATE(mXULWindow);
+
+ nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow;
+ mXULWindow->GetXULBrowserWindow(getter_AddRefs(xulBrowserWindow));
+
+ if (xulBrowserWindow)
+ return xulBrowserWindow->ShouldLoadURI(aDocShell, aURI, aReferrer, _retval);
+
+ *_retval = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsContentTreeOwner::ReloadInFreshProcess(nsIDocShell* aDocShell,
+ nsIURI* aURI,
+ nsIURI* aReferrer,
+ bool* aRetVal)
+{
+ NS_WARNING("Cannot reload in fresh process from a nsContentTreeOwner!");
+ *aRetVal = false;
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsContentTreeOwner::nsIWebBrowserChrome2
+//*****************************************************************************
+
+NS_IMETHODIMP nsContentTreeOwner::SetStatusWithContext(uint32_t aStatusType,
+ const nsAString &aStatusText,
+ nsISupports *aStatusContext)
+{
+ // We only allow the status to be set from the primary content shell
+ if (!mPrimary && aStatusType != STATUS_LINK)
+ return NS_OK;
+
+ NS_ENSURE_STATE(mXULWindow);
+
+ nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow;
+ mXULWindow->GetXULBrowserWindow(getter_AddRefs(xulBrowserWindow));
+
+ if (xulBrowserWindow)
+ {
+ switch(aStatusType)
+ {
+ case STATUS_SCRIPT:
+ xulBrowserWindow->SetJSStatus(aStatusText);
+ break;
+ case STATUS_LINK:
+ {
+ nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aStatusContext);
+ xulBrowserWindow->SetOverLink(aStatusText, element);
+ break;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsContentTreeOwner::nsIWebBrowserChrome
+//*****************************************************************************
+
+NS_IMETHODIMP nsContentTreeOwner::SetStatus(uint32_t aStatusType,
+ const char16_t* aStatus)
+{
+ return SetStatusWithContext(aStatusType,
+ aStatus ? static_cast<const nsString &>(nsDependentString(aStatus))
+ : EmptyString(),
+ nullptr);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SetWebBrowser(nsIWebBrowser* aWebBrowser)
+{
+ NS_ERROR("Haven't Implemented this yet");
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetWebBrowser(nsIWebBrowser** aWebBrowser)
+{
+ // Unimplemented, and probably will remain so; xpfe windows have docshells,
+ // not webbrowsers.
+ NS_ENSURE_ARG_POINTER(aWebBrowser);
+ *aWebBrowser = 0;
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SetChromeFlags(uint32_t aChromeFlags)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SetChromeFlags(aChromeFlags);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetChromeFlags(uint32_t* aChromeFlags)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetChromeFlags(aChromeFlags);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::DestroyBrowserWindow()
+{
+ NS_ERROR("Haven't Implemented this yet");
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SizeBrowserTo(int32_t aCX, int32_t aCY)
+{
+ NS_ERROR("Haven't Implemented this yet");
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsContentTreeOwner::ShowAsModal()
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->ShowModal();
+}
+
+NS_IMETHODIMP nsContentTreeOwner::IsWindowModal(bool *_retval)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ *_retval = mXULWindow->mContinueModalLoop;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsContentTreeOwner::ExitModalEventLoop(nsresult aStatus)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->ExitModalLoop(aStatus);
+}
+
+//*****************************************************************************
+// nsContentTreeOwner::nsIBaseWindow
+//*****************************************************************************
+
+NS_IMETHODIMP nsContentTreeOwner::InitWindow(nativeWindow aParentNativeWindow,
+ nsIWidget* parentWidget, int32_t x, int32_t y, int32_t cx, int32_t cy)
+{
+ // Ignore wigdet parents for now. Don't think those are a vaild thing to call.
+ NS_ENSURE_SUCCESS(SetPositionAndSize(x, y, cx, cy, 0), NS_ERROR_FAILURE);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsContentTreeOwner::Create()
+{
+ NS_ASSERTION(false, "You can't call this");
+ return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP nsContentTreeOwner::Destroy()
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->Destroy();
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetUnscaledDevicePixelsPerCSSPixel(double* aScale)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetUnscaledDevicePixelsPerCSSPixel(aScale);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetDevicePixelsPerDesktopPixel(double* aScale)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetDevicePixelsPerDesktopPixel(aScale);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SetPositionDesktopPix(int32_t aX, int32_t aY)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SetPositionDesktopPix(aX, aY);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SetPosition(int32_t aX, int32_t aY)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SetPosition(aX, aY);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetPosition(int32_t* aX, int32_t* aY)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetPosition(aX, aY);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SetSize(int32_t aCX, int32_t aCY, bool aRepaint)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SetSize(aCX, aCY, aRepaint);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetSize(int32_t* aCX, int32_t* aCY)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetSize(aCX, aCY);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SetPositionAndSize(int32_t aX, int32_t aY,
+ int32_t aCX, int32_t aCY, uint32_t aFlags)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SetPositionAndSize(aX, aY, aCX, aCY, aFlags);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetPositionAndSize(int32_t* aX, int32_t* aY,
+ int32_t* aCX, int32_t* aCY)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetPositionAndSize(aX, aY, aCX, aCY);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::Repaint(bool aForce)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->Repaint(aForce);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetParentWidget(nsIWidget** aParentWidget)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetParentWidget(aParentWidget);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SetParentWidget(nsIWidget* aParentWidget)
+{
+ NS_ASSERTION(false, "You can't call this");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetParentNativeWindow(nativeWindow* aParentNativeWindow)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetParentNativeWindow(aParentNativeWindow);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SetParentNativeWindow(nativeWindow aParentNativeWindow)
+{
+ NS_ASSERTION(false, "You can't call this");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetNativeHandle(nsAString& aNativeHandle)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetNativeHandle(aNativeHandle);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetVisibility(bool* aVisibility)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetVisibility(aVisibility);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SetVisibility(bool aVisibility)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SetVisibility(aVisibility);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetEnabled(bool *aEnabled)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->GetEnabled(aEnabled);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SetEnabled(bool aEnable)
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SetEnabled(aEnable);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetMainWidget(nsIWidget** aMainWidget)
+{
+ NS_ENSURE_ARG_POINTER(aMainWidget);
+ NS_ENSURE_STATE(mXULWindow);
+
+ *aMainWidget = mXULWindow->mWindow;
+ NS_IF_ADDREF(*aMainWidget);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SetFocus()
+{
+ NS_ENSURE_STATE(mXULWindow);
+ return mXULWindow->SetFocus();
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetTitle(char16_t** aTitle)
+{
+ NS_ENSURE_ARG_POINTER(aTitle);
+ NS_ENSURE_STATE(mXULWindow);
+
+ return mXULWindow->GetTitle(aTitle);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SetTitle(const char16_t* aTitle)
+{
+ // We only allow the title to be set from the primary content shell
+ if(!mPrimary || !mContentTitleSetting)
+ return NS_OK;
+
+ NS_ENSURE_STATE(mXULWindow);
+
+ nsAutoString title;
+ nsAutoString docTitle(aTitle);
+
+ if (docTitle.IsEmpty())
+ docTitle.Assign(mTitleDefault);
+
+ if (!docTitle.IsEmpty()) {
+ if (!mTitlePreface.IsEmpty()) {
+ // Title will be: "Preface: Doc Title - Mozilla"
+ title.Assign(mTitlePreface);
+ title.Append(docTitle);
+ }
+ else {
+ // Title will be: "Doc Title - Mozilla"
+ title = docTitle;
+ }
+
+ if (!mWindowTitleModifier.IsEmpty())
+ title += mTitleSeparator + mWindowTitleModifier;
+ }
+ else
+ title.Assign(mWindowTitleModifier); // Title will just be plain "Mozilla"
+
+ //
+ // if there is no location bar we modify the title to display at least
+ // the scheme and host (if any) as an anti-spoofing measure.
+ //
+ nsCOMPtr<dom::Element> docShellElement = mXULWindow->GetWindowDOMElement();
+
+ if (docShellElement) {
+ nsAutoString chromeString;
+ docShellElement->GetAttribute(NS_LITERAL_STRING("chromehidden"), chromeString);
+ if (chromeString.Find(NS_LITERAL_STRING("location")) != kNotFound) {
+ //
+ // location bar is turned off, find the browser location
+ //
+ // use the document's nsPrincipal to find the true owner
+ // in case of javascript: or data: documents
+ //
+ nsCOMPtr<nsIDocShellTreeItem> dsitem;
+ GetPrimaryContentShell(getter_AddRefs(dsitem));
+ nsCOMPtr<nsIScriptObjectPrincipal> doc =
+ do_QueryInterface(dsitem ? dsitem->GetDocument() : nullptr);
+ if (doc) {
+ nsCOMPtr<nsIURI> uri;
+ nsIPrincipal* principal = doc->GetPrincipal();
+ if (principal) {
+ principal->GetURI(getter_AddRefs(uri));
+ if (uri) {
+ //
+ // remove any user:pass information
+ //
+ nsCOMPtr<nsIURIFixup> fixup(do_GetService(NS_URIFIXUP_CONTRACTID));
+ if (fixup) {
+ nsCOMPtr<nsIURI> tmpuri;
+ nsresult rv = fixup->CreateExposableURI(uri,getter_AddRefs(tmpuri));
+ if (NS_SUCCEEDED(rv) && tmpuri) {
+ // (don't bother if there's no host)
+ nsAutoCString host;
+ nsAutoCString prepath;
+ tmpuri->GetHost(host);
+ tmpuri->GetPrePath(prepath);
+ if (!host.IsEmpty()) {
+ //
+ // We have a scheme/host, update the title
+ //
+ title.Insert(NS_ConvertUTF8toUTF16(prepath) +
+ mTitleSeparator, 0);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ nsIDocument* document = docShellElement->OwnerDoc();
+ ErrorResult rv;
+ document->SetTitle(title, rv);
+ return rv.StealNSResult();
+ }
+
+ return mXULWindow->SetTitle(title.get());
+}
+
+//*****************************************************************************
+// nsContentTreeOwner: nsIWindowProvider
+//*****************************************************************************
+NS_IMETHODIMP
+nsContentTreeOwner::ProvideWindow(mozIDOMWindowProxy* aParent,
+ uint32_t aChromeFlags,
+ bool aCalledFromJS,
+ bool aPositionSpecified,
+ bool aSizeSpecified,
+ nsIURI* aURI,
+ const nsAString& aName,
+ const nsACString& aFeatures,
+ bool aForceNoOpener,
+ bool* aWindowIsNew,
+ mozIDOMWindowProxy** aReturn)
+{
+ NS_ENSURE_ARG_POINTER(aParent);
+
+ auto* parent = nsPIDOMWindowOuter::From(aParent);
+
+ *aReturn = nullptr;
+
+ if (!mXULWindow) {
+ // Nothing to do here
+ return NS_OK;
+ }
+
+#ifdef DEBUG
+ nsCOMPtr<nsIWebNavigation> parentNav = do_GetInterface(aParent);
+ nsCOMPtr<nsIDocShellTreeOwner> parentOwner = do_GetInterface(parentNav);
+ NS_ASSERTION(SameCOMIdentity(parentOwner,
+ static_cast<nsIDocShellTreeOwner*>(this)),
+ "Parent from wrong docshell tree?");
+#endif
+
+ // If aParent is inside an <iframe mozbrowser> and this isn't a request to
+ // open a modal-type window, we're going to create a new <iframe mozbrowser>
+ // and return its window here.
+ nsCOMPtr<nsIDocShell> docshell = do_GetInterface(aParent);
+ if (docshell && docshell->GetIsInMozBrowserOrApp() &&
+ !(aChromeFlags & (nsIWebBrowserChrome::CHROME_MODAL |
+ nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
+ nsIWebBrowserChrome::CHROME_OPENAS_CHROME))) {
+
+ BrowserElementParent::OpenWindowResult opened =
+ BrowserElementParent::OpenWindowInProcess(parent, aURI, aName,
+ aFeatures, aForceNoOpener, aReturn);
+
+ // If OpenWindowInProcess handled the open (by opening it or blocking the
+ // popup), tell our caller not to proceed trying to create a new window
+ // through other means.
+ if (opened != BrowserElementParent::OPEN_WINDOW_IGNORED) {
+ *aWindowIsNew = opened == BrowserElementParent::OPEN_WINDOW_ADDED;
+ return *aWindowIsNew ? NS_OK : NS_ERROR_ABORT;
+ }
+
+ // If we're in an app and the target is _blank, send the url to the OS
+ if (aName.LowerCaseEqualsLiteral("_blank")) {
+ nsCOMPtr<nsIExternalURLHandlerService> exUrlServ(
+ do_GetService(NS_EXTERNALURLHANDLERSERVICE_CONTRACTID));
+ if (exUrlServ) {
+
+ nsCOMPtr<nsIHandlerInfo> info;
+ bool found;
+ exUrlServ->GetURLHandlerInfoFromOS(aURI, &found, getter_AddRefs(info));
+
+ if (info && found) {
+ info->LaunchWithURI(aURI, nullptr);
+ return NS_ERROR_ABORT;
+ }
+
+ }
+ }
+ }
+
+ int32_t openLocation =
+ nsWindowWatcher::GetWindowOpenLocation(parent, aChromeFlags, aCalledFromJS,
+ aPositionSpecified, aSizeSpecified);
+
+ if (openLocation != nsIBrowserDOMWindow::OPEN_NEWTAB &&
+ openLocation != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) {
+ // Just open a window normally
+ return NS_OK;
+ }
+
+ nsCOMPtr<mozIDOMWindowProxy> domWin;
+ mXULWindow->GetWindowDOMWindow(getter_AddRefs(domWin));
+ nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(domWin);
+ if (!chromeWin) {
+ // Really odd... but whatever
+ NS_WARNING("nsXULWindow's DOMWindow is not a chrome window");
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
+ chromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
+ if (!browserDOMWin) {
+ return NS_OK;
+ }
+
+ *aWindowIsNew = (openLocation != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW);
+
+ {
+ dom::AutoNoJSAPI nojsapi;
+
+ uint32_t flags = nsIBrowserDOMWindow::OPEN_NEW;
+ if (aForceNoOpener) {
+ flags |= nsIBrowserDOMWindow::OPEN_NO_OPENER;
+ }
+
+ // Get a new rendering area from the browserDOMWin. We don't want
+ // to be starting any loads here, so get it with a null URI.
+ //
+ // This method handles setting the opener for us, so we don't need to set it
+ // ourselves.
+ return browserDOMWin->OpenURI(nullptr, aParent,
+ openLocation,
+ flags, aReturn);
+ }
+}
+
+//*****************************************************************************
+// nsContentTreeOwner: Accessors
+//*****************************************************************************
+
+#if defined(XP_MACOSX)
+class nsContentTitleSettingEvent : public Runnable
+{
+public:
+ nsContentTitleSettingEvent(dom::Element* dse, const nsAString& wtm)
+ : mElement(dse),
+ mTitleDefault(wtm) {}
+
+ NS_IMETHOD Run() override
+ {
+ ErrorResult rv;
+ mElement->SetAttribute(NS_LITERAL_STRING("titledefault"), mTitleDefault, rv);
+ mElement->RemoveAttribute(NS_LITERAL_STRING("titlemodifier"), rv);
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<dom::Element> mElement;
+ nsString mTitleDefault;
+};
+#endif
+
+void nsContentTreeOwner::XULWindow(nsXULWindow* aXULWindow)
+{
+ mXULWindow = aXULWindow;
+ if (mXULWindow && mPrimary) {
+ // Get the window title modifiers
+ nsCOMPtr<dom::Element> docShellElement = mXULWindow->GetWindowDOMElement();
+
+ nsAutoString contentTitleSetting;
+
+ if(docShellElement)
+ {
+ docShellElement->GetAttribute(NS_LITERAL_STRING("contenttitlesetting"), contentTitleSetting);
+ if(contentTitleSetting.EqualsLiteral("true"))
+ {
+ mContentTitleSetting = true;
+ docShellElement->GetAttribute(NS_LITERAL_STRING("titledefault"), mTitleDefault);
+ docShellElement->GetAttribute(NS_LITERAL_STRING("titlemodifier"), mWindowTitleModifier);
+ docShellElement->GetAttribute(NS_LITERAL_STRING("titlepreface"), mTitlePreface);
+
+#if defined(XP_MACOSX)
+ // On OS X, treat the titlemodifier like it's the titledefault, and don't ever append
+ // the separator + appname.
+ if (mTitleDefault.IsEmpty()) {
+ NS_DispatchToCurrentThread(
+ new nsContentTitleSettingEvent(docShellElement,
+ mWindowTitleModifier));
+ mTitleDefault = mWindowTitleModifier;
+ mWindowTitleModifier.Truncate();
+ }
+#endif
+ docShellElement->GetAttribute(NS_LITERAL_STRING("titlemenuseparator"), mTitleSeparator);
+ }
+ }
+ else
+ {
+ NS_ERROR("This condition should never happen. If it does, "
+ "we just won't get a modifier, but it still shouldn't happen.");
+ }
+ }
+}
+
+nsXULWindow* nsContentTreeOwner::XULWindow()
+{
+ return mXULWindow;
+}
+
+//*****************************************************************************
+//*** nsSiteWindow implementation
+//*****************************************************************************
+
+nsSiteWindow::nsSiteWindow(nsContentTreeOwner *aAggregator)
+{
+ mAggregator = aAggregator;
+}
+
+nsSiteWindow::~nsSiteWindow()
+{
+}
+
+NS_IMPL_ADDREF_USING_AGGREGATOR(nsSiteWindow, mAggregator)
+NS_IMPL_RELEASE_USING_AGGREGATOR(nsSiteWindow, mAggregator)
+
+NS_INTERFACE_MAP_BEGIN(nsSiteWindow)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+ NS_INTERFACE_MAP_ENTRY(nsIEmbeddingSiteWindow)
+NS_INTERFACE_MAP_END_AGGREGATED(mAggregator)
+
+NS_IMETHODIMP
+nsSiteWindow::SetDimensions(uint32_t aFlags,
+ int32_t aX, int32_t aY, int32_t aCX, int32_t aCY)
+{
+ // XXX we're ignoring aFlags
+ return mAggregator->SetPositionAndSize(aX, aY, aCX, aCY,
+ nsIBaseWindow::eRepaint);
+}
+
+NS_IMETHODIMP
+nsSiteWindow::GetDimensions(uint32_t aFlags,
+ int32_t *aX, int32_t *aY, int32_t *aCX, int32_t *aCY)
+{
+ // XXX we're ignoring aFlags
+ return mAggregator->GetPositionAndSize(aX, aY, aCX, aCY);
+}
+
+NS_IMETHODIMP
+nsSiteWindow::SetFocus(void)
+{
+#if 0
+ /* This implementation focuses the main document and could make sense.
+ However this method is actually being used from within
+ nsGlobalWindow::Focus (providing a hook for MDI embedding apps)
+ and it's better for our purposes to not pick a document and
+ focus it, but allow nsGlobalWindow to carry on unhindered.
+ */
+ nsXULWindow *window = mAggregator->XULWindow();
+ if (window) {
+ nsCOMPtr<nsIDocShell> docshell;
+ window->GetDocShell(getter_AddRefs(docshell));
+ if (docShell) {
+ nsCOMPtr<nsPIDOMWindowOuter> domWindow(docShell->GetWindow());
+ if (domWindow)
+ domWindow->Focus();
+ }
+ }
+#endif
+ return NS_OK;
+}
+
+/* this implementation focuses another window. if there isn't another
+ window to focus, we do nothing. */
+NS_IMETHODIMP
+nsSiteWindow::Blur(void)
+{
+ NS_DEFINE_CID(kWindowMediatorCID, NS_WINDOWMEDIATOR_CID);
+
+ nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
+ nsCOMPtr<nsIXULWindow> xulWindow;
+ bool more, foundUs;
+ nsXULWindow *ourWindow = mAggregator->XULWindow();
+
+ {
+ nsCOMPtr<nsIWindowMediator> windowMediator(do_GetService(kWindowMediatorCID));
+ if (windowMediator)
+ windowMediator->GetZOrderXULWindowEnumerator(0, true,
+ getter_AddRefs(windowEnumerator));
+ }
+
+ if (!windowEnumerator)
+ return NS_ERROR_FAILURE;
+
+ // step through the top-level windows
+ foundUs = false;
+ windowEnumerator->HasMoreElements(&more);
+ while (more) {
+
+ nsCOMPtr<nsISupports> nextWindow;
+ nsCOMPtr<nsIXULWindow> nextXULWindow;
+
+ windowEnumerator->GetNext(getter_AddRefs(nextWindow));
+ nextXULWindow = do_QueryInterface(nextWindow);
+
+ // got it!(?)
+ if (foundUs) {
+ xulWindow = nextXULWindow;
+ break;
+ }
+
+ // remember the very first one, in case we have to wrap
+ if (!xulWindow)
+ xulWindow = nextXULWindow;
+
+ // look for us
+ if (nextXULWindow == ourWindow)
+ foundUs = true;
+
+ windowEnumerator->HasMoreElements(&more);
+ }
+
+ // change focus to the window we just found
+ if (xulWindow) {
+ nsCOMPtr<nsIDocShell> docshell;
+ xulWindow->GetDocShell(getter_AddRefs(docshell));
+ if (!docshell) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> domWindow = docshell->GetWindow();
+ if (domWindow)
+ domWindow->Focus();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSiteWindow::GetVisibility(bool *aVisibility)
+{
+ return mAggregator->GetVisibility(aVisibility);
+}
+
+NS_IMETHODIMP
+nsSiteWindow::SetVisibility(bool aVisibility)
+{
+ return mAggregator->SetVisibility(aVisibility);
+}
+
+NS_IMETHODIMP
+nsSiteWindow::GetTitle(char16_t * *aTitle)
+{
+ return mAggregator->GetTitle(aTitle);
+}
+
+NS_IMETHODIMP
+nsSiteWindow::SetTitle(const char16_t * aTitle)
+{
+ return mAggregator->SetTitle(aTitle);
+}
+
+NS_IMETHODIMP
+nsSiteWindow::GetSiteWindow(void **aSiteWindow)
+{
+ return mAggregator->GetParentNativeWindow(aSiteWindow);
+}
+
diff --git a/xpfe/appshell/nsContentTreeOwner.h b/xpfe/appshell/nsContentTreeOwner.h
new file mode 100644
index 000000000..d6a0d42b3
--- /dev/null
+++ b/xpfe/appshell/nsContentTreeOwner.h
@@ -0,0 +1,63 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsContentTreeOwner_h__
+#define nsContentTreeOwner_h__
+
+// Helper Classes
+#include "nsCOMPtr.h"
+#include "nsString.h"
+
+// Interfaces Needed
+#include "nsIBaseWindow.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIWebBrowserChrome3.h"
+#include "nsIWindowProvider.h"
+
+class nsXULWindow;
+class nsSiteWindow;
+
+class nsContentTreeOwner final : public nsIDocShellTreeOwner,
+ public nsIBaseWindow,
+ public nsIInterfaceRequestor,
+ public nsIWebBrowserChrome3,
+ public nsIWindowProvider
+{
+friend class nsXULWindow;
+friend class nsSiteWindow;
+
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_DECL_NSIBASEWINDOW
+ NS_DECL_NSIDOCSHELLTREEOWNER
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSIWEBBROWSERCHROME
+ NS_DECL_NSIWEBBROWSERCHROME2
+ NS_DECL_NSIWEBBROWSERCHROME3
+ NS_DECL_NSIWINDOWPROVIDER
+
+protected:
+ explicit nsContentTreeOwner(bool fPrimary);
+ virtual ~nsContentTreeOwner();
+
+ void XULWindow(nsXULWindow* aXULWindow);
+ nsXULWindow* XULWindow();
+
+protected:
+ nsXULWindow *mXULWindow;
+ nsSiteWindow *mSiteWindow;
+ bool mPrimary;
+ bool mContentTitleSetting;
+ nsString mWindowTitleModifier;
+ nsString mTitleSeparator;
+ nsString mTitlePreface;
+ nsString mTitleDefault;
+};
+
+#endif /* nsContentTreeOwner_h__ */
diff --git a/xpfe/appshell/nsIAppShellService.idl b/xpfe/appshell/nsIAppShellService.idl
new file mode 100644
index 000000000..e7d96a5d1
--- /dev/null
+++ b/xpfe/appshell/nsIAppShellService.idl
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIXULWindow;
+interface nsIWindowlessBrowser;
+interface nsIURI;
+interface mozIDOMWindowProxy;
+interface nsIAppShell;
+interface nsITabParent;
+
+[ptr] native JSContext(JSContext);
+
+%{C++
+#include "js/TypeDecls.h"
+%}
+
+[scriptable, uuid(19266025-354c-4bb9-986b-3483b2b1cdef)]
+interface nsIAppShellService : nsISupports
+{
+ /**
+ * Create a window, which will be initially invisible.
+ * @param aParent the parent window. Can be null.
+ * @param aUrl the contents of the new window.
+ * @param aChromeMask chrome flags affecting the kind of OS border
+ * given to the window. see nsIBrowserWindow for
+ * bit/flag definitions.
+ * @param aCallbacks interface providing C++ hooks for window initialization
+ * before the window is made visible. Can be null.
+ * Deprecated.
+ * @param aInitialWidth width, in pixels, of the window. Width of window
+ * at creation. Can be overridden by the "width"
+ * tag in the XUL. Set to NS_SIZETOCONTENT to force
+ * the window to wrap to its contents.
+ * @param aInitialHeight like aInitialWidth, but subtly different.
+ * @param aOpeningTab The TabParent that requested that this window be opened.
+ * Can be left null.
+ * @param aOpenerWindow The Window Proxy which requested that this window be opened.
+ * Can be left null.
+ */
+ const long SIZE_TO_CONTENT = -1;
+ nsIXULWindow createTopLevelWindow(in nsIXULWindow aParent,
+ in nsIURI aUrl,
+ in uint32_t aChromeMask,
+ in long aInitialWidth,
+ in long aInitialHeight,
+ in nsITabParent aOpeningTab,
+ in mozIDOMWindowProxy aOpenerWindow);
+
+ /**
+ * This is the constructor for creating an invisible DocShell.
+ * It is used to simulate DOM windows without an actual physical
+ * representation.
+ * @param aIsChrome Set true if you want to use it for chrome content.
+ */
+ nsIWindowlessBrowser createWindowlessBrowser([optional] in bool aIsChrome);
+
+ [noscript]
+ void createHiddenWindow();
+
+ void destroyHiddenWindow();
+
+ /**
+ * B2G multi-screen support. When open another top-level window on b2g,
+ * a screen ID is needed for identifying which screen this window is
+ * opened to.
+ * @param aScreenId Differentiate screens of windows. It is platform-
+ * specific due to the hardware limitation for now.
+ */
+ [noscript]
+ void setScreenId(in uint32_t aScreenId);
+
+ /**
+ * Return the (singleton) application hidden window, automatically created
+ * and maintained by this AppShellService.
+ * @param aResult the hidden window. Do not unhide hidden window.
+ * Do not taunt hidden window.
+ */
+ readonly attribute nsIXULWindow hiddenWindow;
+
+ /**
+ * Return the (singleton) application hidden window, automatically created
+ * and maintained by this AppShellService.
+ * @param aResult the hidden window. Do not unhide hidden window.
+ * Do not taunt hidden window.
+ */
+ readonly attribute mozIDOMWindowProxy hiddenDOMWindow;
+
+ /**
+ * Return the (singleton) application hidden private window, automatically
+ * created and maintained by this AppShellService. This window is created
+ * in private browsing mode.
+ * @param aResult the hidden private window. Do not unhide hidden window.
+ * Do not taunt hidden window.
+ */
+ readonly attribute nsIXULWindow hiddenPrivateWindow;
+
+ /**
+ * Return the (singleton) application hidden private window, automatically
+ * created and maintained by this AppShellService. This window is created
+ * in private browsing mode.
+ * @param aResult the hidden private window. Do not unhide hidden window.
+ * Do not taunt hidden window.
+ */
+ readonly attribute mozIDOMWindowProxy hiddenPrivateDOMWindow;
+
+ /**
+ * Return true if the application hidden window was provided by the
+ * application. If it wasn't, the default hidden window was used. This will
+ * usually be false on all non-mac platforms.
+ */
+ readonly attribute boolean applicationProvidedHiddenWindow;
+
+ /**
+ * Add a window to the application's registry of windows. These windows
+ * are generally shown in the Windows taskbar, and the application
+ * knows it can't quit until it's out of registered windows.
+ * @param aWindow the window to register
+ * @note When this method is successful, it fires the global notification
+ * "xul-window-registered"
+ */
+ void registerTopLevelWindow(in nsIXULWindow aWindow);
+
+ /**
+ * Remove a window from the application's window registry. Note that
+ * this method won't automatically attempt to quit the app when
+ * the last window is unregistered. For that, see Quit().
+ * @param aWindow you see the pattern
+ */
+ void unregisterTopLevelWindow(in nsIXULWindow aWindow);
+
+ /**
+ * Whether the hidden private window has been lazily created.
+ */
+ [noscript]
+ readonly attribute boolean hasHiddenPrivateWindow;
+
+ /**
+ * Start/stop tracking lags in the event loop.
+ * If the event loop gets unresponsive, a "event-loop-lag" notification
+ * is sent. Note that calling `startEventLoopLagTracking` when tracking
+ * is already enabled has no effect.
+ * @return true if tracking succeeded.
+ */
+ bool startEventLoopLagTracking();
+ void stopEventLoopLagTracking();
+};
diff --git a/xpfe/appshell/nsIPopupWindowManager.idl b/xpfe/appshell/nsIPopupWindowManager.idl
new file mode 100644
index 000000000..4e6cb99b3
--- /dev/null
+++ b/xpfe/appshell/nsIPopupWindowManager.idl
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This is the interface to the Popup Window Manager: an object which
+ * maintains popup window permissions by website.
+ */
+
+#include "nsISupports.idl"
+
+interface nsIPrincipal;
+
+[scriptable, uuid(66386aa9-2088-4bae-82c7-9f58bc02be64)]
+interface nsIPopupWindowManager : nsISupports {
+
+ /**
+ * These values are returned by the testPermission method
+ */
+ const uint32_t ALLOW_POPUP = 1;
+ const uint32_t DENY_POPUP = 2;
+ const uint32_t ALLOW_POPUP_WITH_PREJUDICE = 3;
+
+ /**
+ * Test whether a website has permission to show a popup window.
+ * @param principal is the principal to be tested
+ * @return one of the enumerated permission actions defined above
+ */
+ uint32_t testPermission(in nsIPrincipal principal);
+};
+
+%{ C++
+#define NS_POPUPWINDOWMANAGER_CONTRACTID "@mozilla.org/PopupWindowManager;1"
+%}
diff --git a/xpfe/appshell/nsIWindowMediator.idl b/xpfe/appshell/nsIWindowMediator.idl
new file mode 100644
index 000000000..b38297594
--- /dev/null
+++ b/xpfe/appshell/nsIWindowMediator.idl
@@ -0,0 +1,206 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsISimpleEnumerator.idl"
+
+%{C++
+#define NS_WINDOWMEDIATOR_CID \
+{ 0x79a2b7cc, 0xf05b, 0x4605, \
+ { 0xbf, 0xa0, 0xfa, 0xc5, 0x4f, 0x27, 0xee, 0xc8 } }
+
+#define NS_WINDOWMEDIATOR_CONTRACTID \
+ "@mozilla.org/appshell/window-mediator;1"
+%}
+
+interface mozIDOMWindow;
+interface mozIDOMWindowProxy;
+interface nsIXULWindow;
+interface nsIWidget;
+interface nsIWindowMediatorListener;
+
+[scriptable, uuid(df0da056-357d-427f-bafd-e6cbf19c9381)]
+interface nsIWindowMediator: nsISupports
+{
+ /** Return an enumerator which iterates over all windows of type aWindowType
+ * from the oldest window to the youngest.
+ * @param aWindowType the returned enumerator will enumerate only
+ * windows of this type. ("type" is the
+ * |windowtype| attribute of the XML <window> element.)
+ * If null, all windows will be enumerated.
+ * @return an enumerator of nsIDOMWindows. Note that windows close
+ * asynchronously in many cases, so windows returned from this
+ * enumerator can have .closed set to true. Caveat enumerator!
+ */
+ nsISimpleEnumerator getEnumerator(in wstring aWindowType);
+
+ /** Identical to getEnumerator except:
+ * @return an enumerator of nsIXULWindows
+ */
+ nsISimpleEnumerator getXULWindowEnumerator(in wstring aWindowType);
+
+ /** Return an enumerator which iterates over all windows of type aWindowType
+ * in their z (front-to-back) order. Note this interface makes
+ * no requirement that a window couldn't be revisited if windows
+ * are re-ordered while z-order enumerators are active.
+ * @param aWindowType the returned enumerator will enumerate only
+ * windows of this type. ("type" is the
+ * |windowtype| attribute of the XML <window> element.)
+ * If null, all windows will be enumerated.
+ * @param aFrontToBack if true, the enumerator enumerates windows in order
+ * from front to back. back to front if false.
+ * @return an enumerator of nsIDOMWindows
+ */
+ nsISimpleEnumerator getZOrderDOMWindowEnumerator(in wstring aWindowType,
+ in boolean aFrontToBack);
+
+ /** Identical to getZOrderDOMWindowEnumerator except:
+ * @return an enumerator of nsIXULWindows
+ */
+ nsISimpleEnumerator getZOrderXULWindowEnumerator(in wstring aWindowType,
+ in boolean aFrontToBack);
+
+ /** This is a shortcut for simply fetching the first window in
+ * front to back order.
+ * @param aWindowType return the topmost window of this type.
+ * ("type" is the |windowtype| attribute of
+ * the XML <window> element.)
+ * If null, return the topmost window of any type.
+ * @return the topmost window
+ */
+ mozIDOMWindowProxy getMostRecentWindow(in wstring aWindowType);
+
+ /**
+ * Return the outer window with the given ID, if any. Can return null.
+ */
+ mozIDOMWindowProxy getOuterWindowWithId(in unsigned long long aOuterWindowID);
+
+ /**
+ * Return the inner window with the given current window ID, if any.
+ * Can return null if no inner window with the ID exists or if it's not
+ * a current inner anymore.
+ */
+ mozIDOMWindow getCurrentInnerWindowWithId(in unsigned long long aInnerWindowID);
+
+ /** Add the window to the list of known windows. Listeners (see
+ * addListener) will be notified through their onOpenWindow method.
+ * @param aWindow the window to add
+ */
+ [noscript] void registerWindow(in nsIXULWindow aWindow);
+
+ /** Remove the window from the list of known windows. Listeners (see
+ * addListener) will be be notified through their onCloseWindow method.
+ * @param aWindow the window to remove
+ */
+ [noscript] void unregisterWindow(in nsIXULWindow aWindow);
+
+ /** Call this method when a window gains focus. It's a primitive means of
+ * determining the most recent window. It's no longer necessary and it
+ * really should be removed.
+ * @param aWindow the window which has gained focus
+ */
+ [noscript] void updateWindowTimeStamp(in nsIXULWindow aWindow);
+
+ /** Call this method when a window's title changes. Listeners (see
+ * addListener) will be notified through their onWindowTitleChange method.
+ * @param aWindow the window whose title has changed
+ * @param inTitle the window's new title
+ */
+ [noscript] void updateWindowTitle(in nsIXULWindow aWindow,
+ in wstring inTitle );
+
+ /* z-ordering: */
+
+ const unsigned long zLevelTop = 1;
+ const unsigned long zLevelBottom = 2;
+ const unsigned long zLevelBelow = 3; // below some window
+
+ /** A window wants to be moved in z-order. Calculate whether and how
+ * it should be constrained. Note this method is advisory only:
+ * it changes nothing either in WindowMediator's internal state
+ * or with the window.
+ * Note it compares the nsIXULWindow to nsIWidgets. A pure interface
+ * would use all nsIXULWindows. But we expect this to be called from
+ * callbacks originating in native window code. They are expected to
+ * hand us comparison values which are pulled from general storage
+ * in the native widget, and may not correspond to an nsIWidget at all.
+ * For that reason this interface requires only objects one step
+ * removed from the native window (nsIWidgets), and its implementation
+ * must be very understanding of what may be completely invalid
+ * pointers in those parameters.
+ *
+ * @param inWindow the window in question
+ * @param inPosition requested position
+ * values: zLevelTop: topmost window. zLevelBottom: bottom.
+ * zLevelBelow: below ioBelow. (the value of ioBelow will
+ * be ignored for zLevelTop and Bottom.)
+ * @param inBelow if inPosition==zLevelBelow, the window
+ * below which inWindow wants to be placed. Otherwise this
+ * variable is ignored.
+ * @param outPosition constrained position, values like inPosition.
+ * @param outBelow if outPosition==zLevelBelow, the window
+ * below which inWindow should be placed. Otherwise this
+ * this value will be null.
+ * @return PR_TRUE if the position returned is different from
+ * the position given.
+ */
+
+ [noscript] boolean calculateZPosition(in nsIXULWindow inWindow,
+ in unsigned long inPosition,
+ in nsIWidget inBelow,
+ out unsigned long outPosition,
+ out nsIWidget outBelow);
+
+ /** A window has been positioned behind another. Inform WindowMediator
+ * @param inWindow the window in question
+ * @param inPosition new position. values:
+ * zLevelTop: topmost window.
+ * zLevelBottom: bottom.
+ * zLevelBelow: below inBelow. (inBelow is ignored
+ * for other values of inPosition.)
+ * @param inBelow the window inWindow is behind, if zLevelBelow
+ */
+ [noscript] void setZPosition(in nsIXULWindow inWindow,
+ in unsigned long inPosition,
+ in nsIXULWindow inBelow);
+
+ /** Return the window's Z level (as defined in nsIXULWindow).
+ * @param aWindow the window in question
+ * @return aWindow's z level
+ */
+ [noscript] uint32_t getZLevel(in nsIXULWindow aWindow);
+
+ /** Set the window's Z level (as defined in nsIXULWindow). The implementation
+ * will reposition the window as necessary to match its new Z level.
+ * The implementation will assume a window's Z level to be
+ * nsIXULWindow::normalZ until it has been informed of a different level.
+ * @param aWindow the window in question
+ * @param aZLevel the window's new Z level
+ */
+ [noscript] void setZLevel(in nsIXULWindow aWindow, in uint32_t aZLevel);
+
+ /** Register a listener for window status changes.
+ * keeps strong ref? (to be decided)
+ * @param aListener the listener to register
+ */
+ void addListener(in nsIWindowMediatorListener aListener);
+
+ /** Unregister a listener of window status changes.
+ * @param aListener the listener to unregister
+ */
+ void removeListener(in nsIWindowMediatorListener aListener);
+};
+
+// XXXcatalinb: This should be merged to nsIWindowMediator. Using this
+// to avoid UUID change in aurora.
+[scriptable, uuid(b9ed4063-39a2-4302-8e5c-7287eef021fe)]
+interface nsIWindowMediator_44 : nsIWindowMediator
+{
+ /**
+ * Same as getMostRecentWindow, but ignores private browsing
+ * windows.
+ */
+ mozIDOMWindowProxy getMostRecentNonPBWindow(in wstring aWindowType);
+};
diff --git a/xpfe/appshell/nsIWindowMediatorListener.idl b/xpfe/appshell/nsIWindowMediatorListener.idl
new file mode 100644
index 000000000..459e3b6ab
--- /dev/null
+++ b/xpfe/appshell/nsIWindowMediatorListener.idl
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIXULWindow;
+
+[scriptable, uuid(2F276982-0D60-4377-A595-D350BA516395)]
+interface nsIWindowMediatorListener : nsISupports
+{
+ void onWindowTitleChange(in nsIXULWindow window,
+ in wstring newTitle);
+
+ void onOpenWindow(in nsIXULWindow window);
+ void onCloseWindow(in nsIXULWindow window);
+};
+
diff --git a/xpfe/appshell/nsIWindowlessBrowser.idl b/xpfe/appshell/nsIWindowlessBrowser.idl
new file mode 100644
index 000000000..c959e47a4
--- /dev/null
+++ b/xpfe/appshell/nsIWindowlessBrowser.idl
@@ -0,0 +1,27 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIWebNavigation.idl"
+
+/**
+ * This interface represents a nsIWebBrowser instance with no associated OS
+ * window. Its main function is to manage the lifetimes of those windows.
+ * A strong reference to this object must be held until the window is
+ * ready to be destroyed.
+ */
+[scriptable, uuid(abb46f48-abfc-41bf-aa9a-7feccefcf977)]
+interface nsIWindowlessBrowser : nsIWebNavigation
+{
+ /**
+ * "Closes" the windowless browser and destroys its associated nsIWebBrowser
+ * and docshell.
+ *
+ * This method *must* be called for every windowless browser before its last
+ * reference is released.
+ */
+ void close();
+};
+
diff --git a/xpfe/appshell/nsIXULBrowserWindow.idl b/xpfe/appshell/nsIXULBrowserWindow.idl
new file mode 100644
index 000000000..40f1898c8
--- /dev/null
+++ b/xpfe/appshell/nsIXULBrowserWindow.idl
@@ -0,0 +1,78 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIURI.idl"
+#include "nsIDOMNode.idl"
+
+interface nsIRequest;
+interface nsIDOMElement;
+interface nsIInputStream;
+interface nsIDocShell;
+interface nsITabParent;
+interface mozIDOMWindowProxy;
+
+/**
+ * The nsIXULBrowserWindow supplies the methods that may be called from the
+ * internals of the browser area to tell the containing xul window to update
+ * its ui.
+ */
+[scriptable, uuid(a8675fa9-c8b4-4350-9803-c38f344a9e38)]
+interface nsIXULBrowserWindow : nsISupports
+{
+ /**
+ * Sets the status according to JS' version of status.
+ */
+ void setJSStatus(in AString status);
+
+ /**
+ * Tells the object implementing this function what link we are currently
+ * over.
+ */
+ void setOverLink(in AString link, in nsIDOMElement element);
+
+ /**
+ * Determines the appropriate target for a link.
+ */
+ AString onBeforeLinkTraversal(in AString originalTarget,
+ in nsIURI linkURI,
+ in nsIDOMNode linkNode,
+ in boolean isAppTab);
+
+ /**
+ * Find the initial browser of the window and set its remote attribute.
+ * This can be used to ensure that there is a remote browser in a new
+ * window when it first spawns.
+ *
+ */
+ nsITabParent forceInitialBrowserRemote();
+ void forceInitialBrowserNonRemote(in mozIDOMWindowProxy openerWindow);
+
+ /**
+ * Determines whether a load should continue.
+ *
+ * @param aDocShell
+ * The docshell performing the load.
+ * @param aURI
+ * The URI being loaded.
+ * @param aReferrer
+ * The referrer of the load.
+ */
+ bool shouldLoadURI(in nsIDocShell aDocShell,
+ in nsIURI aURI,
+ in nsIURI aReferrer);
+ /**
+ * Show/hide a tooltip (when the user mouses over a link, say).
+ */
+ void showTooltip(in long x, in long y, in AString tooltip, in AString direction);
+ void hideTooltip();
+
+ /**
+ * Return the number of tabs in this window.
+ */
+ uint32_t getTabCount();
+};
+
diff --git a/xpfe/appshell/nsIXULWindow.idl b/xpfe/appshell/nsIXULWindow.idl
new file mode 100644
index 000000000..8db12adb6
--- /dev/null
+++ b/xpfe/appshell/nsIXULWindow.idl
@@ -0,0 +1,168 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * The nsIXULWindow
+ *
+ * When the window is destroyed, it will fire a "xul-window-destroyed"
+ * notification through the global observer service.
+ */
+
+interface nsIDocShell;
+interface nsIDocShellTreeItem;
+interface nsIXULBrowserWindow;
+interface nsITabParent;
+interface mozIDOMWindowProxy;
+
+[scriptable, uuid(d6d7a014-e28d-4c9d-8727-1cf6d870619b)]
+interface nsIXULWindow : nsISupports
+{
+ /**
+ * The docshell owning the XUL for this window.
+ */
+ readonly attribute nsIDocShell docShell;
+
+ /**
+ * Indicates if this window is instrinsically sized.
+ */
+ attribute boolean intrinsicallySized;
+
+ /**
+ * The primary content shell.
+ *
+ * Note that this is a docshell tree item and therefore can not be assured of
+ * what object it is. It could be an editor, a docshell, or a browser object.
+ * Or down the road any other object that supports being a DocShellTreeItem
+ * Query accordingly to determine the capabilities.
+ */
+ readonly attribute nsIDocShellTreeItem primaryContentShell;
+
+ /**
+ * In multiprocess case we may not have primaryContentShell but
+ * primaryTabParent.
+ */
+ readonly attribute nsITabParent primaryTabParent;
+
+ void tabParentAdded(in nsITabParent aTab, in boolean aPrimary);
+ void tabParentRemoved(in nsITabParent aTab);
+
+ /**
+ * The content shell specified by the supplied id.
+ *
+ * Note that this is a docshell tree item and therefore can not be assured of
+ * what object it is. It could be an editor, a docshell, or a browser object.
+ * Or down the road any other object that supports being a DocShellTreeItem
+ * Query accordingly to determine the capabilities.
+ */
+ nsIDocShellTreeItem getContentShellById(in wstring ID);
+
+ /**
+ * Tell this window that it has picked up a child XUL window
+ * @param aChild the child window being added
+ */
+ void addChildWindow(in nsIXULWindow aChild);
+
+ /**
+ * Tell this window that it has lost a child XUL window
+ * @param aChild the child window being removed
+ */
+ void removeChildWindow(in nsIXULWindow aChild);
+
+ /**
+ * Move the window to a centered position.
+ * @param aRelative If not null, the window relative to which the window is
+ * moved. See aScreen parameter for details.
+ * @param aScreen PR_TRUE to center the window relative to the screen
+ * containing aRelative if aRelative is not null. If
+ * aRelative is null then relative to the screen of the
+ * opener window if it was initialized by passing it to
+ * nsWebShellWindow::Initialize. Failing that relative to
+ * the main screen.
+ * PR_FALSE to center it relative to aRelative itself.
+ * @param aAlert PR_TRUE to move the window to an alert position,
+ * generally centered horizontally and 1/3 down from the top.
+ */
+ void center(in nsIXULWindow aRelative, in boolean aScreen, in boolean aAlert);
+
+ /**
+ * Shows the window as a modal window. That is, ensures that it is visible
+ * and runs a local event loop, exiting only once the window has been closed.
+ */
+ void showModal();
+
+ const unsigned long lowestZ = 0;
+ const unsigned long loweredZ = 4; /* "alwaysLowered" attribute */
+ const unsigned long normalZ = 5;
+ const unsigned long raisedZ = 6; /* "alwaysRaised" attribute */
+ const unsigned long highestZ = 9;
+
+ attribute unsigned long zLevel;
+
+ /**
+ * contextFlags are from nsIWindowCreator2
+ */
+ attribute uint32_t contextFlags;
+
+ attribute uint32_t chromeFlags;
+
+ /**
+ * Begin assuming |chromeFlags| don't change hereafter, and assert
+ * if they do change. The state change is one-way and idempotent.
+ */
+ void assumeChromeFlagsAreFrozen();
+
+ /**
+ * Create a new window.
+ * @param aChromeFlags see nsIWebBrowserChrome
+ * @param aOpeningTab the TabParent that requested this new window be opened.
+ * Can be left null.
+ * @param aOpener The window which is requesting that this new window be opened.
+ * @return the newly minted window
+ */
+ nsIXULWindow createNewWindow(in int32_t aChromeFlags,
+ in nsITabParent aOpeningTab,
+ in mozIDOMWindowProxy aOpener);
+
+ attribute nsIXULBrowserWindow XULBrowserWindow;
+
+ /**
+ * Back-door method to force application of chrome flags at a particular
+ * time. Do NOT call this unless you know what you're doing! In particular,
+ * calling this when this XUL window doesn't yet have a document in its
+ * docshell could cause problems.
+ */
+ [noscript] void applyChromeFlags();
+
+ /**
+ * Given the dimensions of some content area held within this
+ * XUL window, and assuming that that content area will change
+ * its dimensions in linear proportion to the dimensions of this
+ * XUL window, changes the size of the XUL window so that the
+ * content area reaches a particular size.
+ *
+ * We need to supply the content area dimensions because sometimes
+ * the child's nsDocShellTreeOwner needs to propagate a SizeShellTo
+ * call to the parent. But the shellItem argument of the call will
+ * not be available on the parent side.
+ *
+ * Note: this is an internal method, other consumers should never call this.
+ *
+ * @param aDesiredWidth
+ * The desired width of the content area in device pixels.
+ * @param aDesiredHeight
+ * The desired height of the content area in device pixels.
+ * @param shellItemWidth
+ * The current width of the content area.
+ * @param shellItemHeight
+ * The current height of the content area.
+ */
+ [noscript, notxpcom] void sizeShellToWithLimit(in int32_t aDesiredWidth,
+ in int32_t aDesiredHeight,
+ in int32_t shellItemWidth,
+ in int32_t shellItemHeight);
+};
diff --git a/xpfe/appshell/nsWebShellWindow.cpp b/xpfe/appshell/nsWebShellWindow.cpp
new file mode 100644
index 000000000..288d94759
--- /dev/null
+++ b/xpfe/appshell/nsWebShellWindow.cpp
@@ -0,0 +1,911 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "nsWebShellWindow.h"
+
+#include "nsLayoutCID.h"
+#include "nsContentCID.h"
+#include "nsIWeakReference.h"
+#include "nsIContentViewer.h"
+#include "nsIComponentManager.h"
+#include "nsIServiceManager.h"
+#include "nsIURL.h"
+#include "nsIIOService.h"
+#include "nsIURL.h"
+#include "nsNetCID.h"
+#include "nsIStringBundle.h"
+#include "nsReadableUtils.h"
+
+#include "nsContentUtils.h"
+#include "nsEscape.h"
+#include "nsPIDOMWindow.h"
+#include "nsIWebNavigation.h"
+#include "nsIWindowWatcher.h"
+
+#include "nsIDOMXULElement.h"
+
+#include "nsWidgetInitData.h"
+#include "nsWidgetsCID.h"
+#include "nsIWidget.h"
+#include "nsIWidgetListener.h"
+
+#include "nsIDOMCharacterData.h"
+#include "nsIDOMNodeList.h"
+
+#include "nsITimer.h"
+#include "nsXULPopupManager.h"
+
+
+#include "nsIDOMXULDocument.h"
+
+#include "nsFocusManager.h"
+
+#include "nsIWebProgress.h"
+#include "nsIWebProgressListener.h"
+
+#include "nsIDocument.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMNode.h"
+#include "nsIDOMElement.h"
+#include "nsIDocumentLoaderFactory.h"
+#include "nsIObserverService.h"
+#include "prprf.h"
+
+#include "nsIScreenManager.h"
+#include "nsIScreen.h"
+
+#include "nsIContent.h" // for menus
+#include "nsIScriptSecurityManager.h"
+
+// For calculating size
+#include "nsIPresShell.h"
+#include "nsPresContext.h"
+
+#include "nsIBaseWindow.h"
+#include "nsIDocShellTreeItem.h"
+
+#include "mozilla/Attributes.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/MouseEvents.h"
+
+#include "nsPIWindowRoot.h"
+
+#ifdef XP_MACOSX
+#include "nsINativeMenuService.h"
+#define USE_NATIVE_MENUS
+#endif
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+/* Define Class IDs */
+static NS_DEFINE_CID(kWindowCID, NS_WINDOW_CID);
+
+#define SIZE_PERSISTENCE_TIMEOUT 500 // msec
+
+nsWebShellWindow::nsWebShellWindow(uint32_t aChromeFlags)
+ : nsXULWindow(aChromeFlags)
+ , mSPTimerLock("nsWebShellWindow.mSPTimerLock")
+ , mWidgetListenerDelegate(this)
+{
+}
+
+nsWebShellWindow::~nsWebShellWindow()
+{
+ MutexAutoLock lock(mSPTimerLock);
+ if (mSPTimer)
+ mSPTimer->Cancel();
+}
+
+NS_IMPL_ADDREF_INHERITED(nsWebShellWindow, nsXULWindow)
+NS_IMPL_RELEASE_INHERITED(nsWebShellWindow, nsXULWindow)
+
+NS_INTERFACE_MAP_BEGIN(nsWebShellWindow)
+ NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
+NS_INTERFACE_MAP_END_INHERITING(nsXULWindow)
+
+nsresult nsWebShellWindow::Initialize(nsIXULWindow* aParent,
+ nsIXULWindow* aOpener,
+ nsIURI* aUrl,
+ int32_t aInitialWidth,
+ int32_t aInitialHeight,
+ bool aIsHiddenWindow,
+ nsITabParent *aOpeningTab,
+ mozIDOMWindowProxy *aOpenerWindow,
+ nsWidgetInitData& widgetInitData)
+{
+ nsresult rv;
+ nsCOMPtr<nsIWidget> parentWidget;
+
+ mIsHiddenWindow = aIsHiddenWindow;
+
+ int32_t initialX = 0, initialY = 0;
+ nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(aOpener));
+ if (base) {
+ rv = base->GetPositionAndSize(&mOpenerScreenRect.x,
+ &mOpenerScreenRect.y,
+ &mOpenerScreenRect.width,
+ &mOpenerScreenRect.height);
+ if (NS_FAILED(rv)) {
+ mOpenerScreenRect.SetEmpty();
+ } else {
+ double scale;
+ if (NS_SUCCEEDED(base->GetUnscaledDevicePixelsPerCSSPixel(&scale))) {
+ mOpenerScreenRect.x = NSToIntRound(mOpenerScreenRect.x / scale);
+ mOpenerScreenRect.y = NSToIntRound(mOpenerScreenRect.y / scale);
+ mOpenerScreenRect.width = NSToIntRound(mOpenerScreenRect.width / scale);
+ mOpenerScreenRect.height = NSToIntRound(mOpenerScreenRect.height / scale);
+ }
+ initialX = mOpenerScreenRect.x;
+ initialY = mOpenerScreenRect.y;
+ ConstrainToOpenerScreen(&initialX, &initialY);
+ }
+ }
+
+ // XXX: need to get the default window size from prefs...
+ // Doesn't come from prefs... will come from CSS/XUL/RDF
+ DesktopIntRect deskRect(initialX, initialY, aInitialWidth, aInitialHeight);
+
+ // Create top level window
+ mWindow = do_CreateInstance(kWindowCID, &rv);
+ if (NS_OK != rv) {
+ return rv;
+ }
+
+ /* This next bit is troublesome. We carry two different versions of a pointer
+ to our parent window. One is the parent window's widget, which is passed
+ to our own widget. The other is a weak reference we keep here to our
+ parent WebShellWindow. The former is useful to the widget, and we can't
+ trust its treatment of the parent reference because they're platform-
+ specific. The latter is useful to this class.
+ A better implementation would be one in which the parent keeps strong
+ references to its children and closes them before it allows itself
+ to be closed. This would mimic the behaviour of OSes that support
+ top-level child windows in OSes that do not. Later.
+ */
+ nsCOMPtr<nsIBaseWindow> parentAsWin(do_QueryInterface(aParent));
+ if (parentAsWin) {
+ parentAsWin->GetMainWidget(getter_AddRefs(parentWidget));
+ mParentWindow = do_GetWeakReference(aParent);
+ }
+
+ mWindow->SetWidgetListener(&mWidgetListenerDelegate);
+ rv = mWindow->Create((nsIWidget *)parentWidget, // Parent nsIWidget
+ nullptr, // Native parent widget
+ deskRect, // Widget dimensions
+ &widgetInitData); // Widget initialization data
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ LayoutDeviceIntRect r = mWindow->GetClientBounds();
+ // Match the default background color of content. Important on windows
+ // since we no longer use content child widgets.
+ mWindow->SetBackgroundColor(NS_RGB(255,255,255));
+
+ // Create web shell
+ mDocShell = do_CreateInstance("@mozilla.org/docshell;1");
+ NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
+
+ mDocShell->SetOpener(aOpeningTab);
+
+ // Make sure to set the item type on the docshell _before_ calling
+ // Create() so it knows what type it is.
+ nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(do_QueryInterface(mDocShell));
+ NS_ENSURE_TRUE(docShellAsItem, NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(EnsureChromeTreeOwner(), NS_ERROR_FAILURE);
+
+ docShellAsItem->SetTreeOwner(mChromeTreeOwner);
+ docShellAsItem->SetItemType(nsIDocShellTreeItem::typeChrome);
+
+ r.x = r.y = 0;
+ nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell));
+ NS_ENSURE_SUCCESS(docShellAsWin->InitWindow(nullptr, mWindow,
+ r.x, r.y, r.width, r.height), NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(docShellAsWin->Create(), NS_ERROR_FAILURE);
+
+ // Attach a WebProgress listener.during initialization...
+ nsCOMPtr<nsIWebProgress> webProgress(do_GetInterface(mDocShell, &rv));
+ if (webProgress) {
+ webProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_NETWORK);
+ }
+
+ if (aOpenerWindow) {
+ nsPIDOMWindowOuter* window = mDocShell->GetWindow();
+ MOZ_ASSERT(window);
+ window->SetOpenerWindow(nsPIDOMWindowOuter::From(aOpenerWindow), true);
+ }
+
+ // Eagerly create an about:blank content viewer with the right principal here,
+ // rather than letting it happening in the upcoming call to
+ // SetInitialPrincipalToSubject. This avoids creating the about:blank document
+ // and then blowing it away with a second one, which can cause problems for the
+ // top-level chrome window case. See bug 789773.
+ // Note that we don't accept expanded principals here, similar to
+ // SetInitialPrincipalToSubject.
+ if (nsContentUtils::IsInitialized()) { // Sometimes this happens really early See bug 793370.
+ MOZ_ASSERT(mDocShell->ItemType() == nsIDocShellTreeItem::typeChrome);
+ nsCOMPtr<nsIPrincipal> principal = nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller();
+ if (nsContentUtils::IsExpandedPrincipal(principal)) {
+ principal = nullptr;
+ }
+ rv = mDocShell->CreateAboutBlankContentViewer(principal);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIDocument> doc = mDocShell->GetDocument();
+ NS_ENSURE_TRUE(!!doc, NS_ERROR_FAILURE);
+ doc->SetIsInitialDocument(true);
+ }
+
+ if (nullptr != aUrl) {
+ nsCString tmpStr;
+
+ rv = aUrl->GetSpec(tmpStr);
+ if (NS_FAILED(rv)) return rv;
+
+ NS_ConvertUTF8toUTF16 urlString(tmpStr);
+ nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
+ NS_ENSURE_TRUE(webNav, NS_ERROR_FAILURE);
+ rv = webNav->LoadURI(urlString.get(),
+ nsIWebNavigation::LOAD_FLAGS_NONE,
+ nullptr,
+ nullptr,
+ nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return rv;
+}
+
+nsIPresShell*
+nsWebShellWindow::GetPresShell()
+{
+ if (!mDocShell)
+ return nullptr;
+
+ return mDocShell->GetPresShell();
+}
+
+bool
+nsWebShellWindow::WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y)
+{
+ nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+ if (pm) {
+ nsCOMPtr<nsPIDOMWindowOuter> window =
+ mDocShell ? mDocShell->GetWindow() : nullptr;
+ pm->AdjustPopupsOnWindowChange(window);
+ }
+
+ // Notify all tabs that the widget moved.
+ if (mDocShell && mDocShell->GetWindow()) {
+ nsCOMPtr<EventTarget> eventTarget = mDocShell->GetWindow()->GetTopWindowRoot();
+ nsContentUtils::DispatchChromeEvent(mDocShell->GetDocument(),
+ eventTarget,
+ NS_LITERAL_STRING("MozUpdateWindowPos"),
+ false, false, nullptr);
+ }
+
+ // Persist position, but not immediately, in case this OS is firing
+ // repeated move events as the user drags the window
+ SetPersistenceTimer(PAD_POSITION);
+ return false;
+}
+
+bool
+nsWebShellWindow::WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight)
+{
+ nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(mDocShell));
+ if (shellAsWin) {
+ shellAsWin->SetPositionAndSize(0, 0, aWidth, aHeight, 0);
+ }
+ // Persist size, but not immediately, in case this OS is firing
+ // repeated size events as the user drags the sizing handle
+ if (!IsLocked())
+ SetPersistenceTimer(PAD_POSITION | PAD_SIZE | PAD_MISC);
+ return true;
+}
+
+bool
+nsWebShellWindow::RequestWindowClose(nsIWidget* aWidget)
+{
+ // Maintain a reference to this as it is about to get destroyed.
+ nsCOMPtr<nsIXULWindow> xulWindow(this);
+
+ nsCOMPtr<nsPIDOMWindowOuter> window(mDocShell ? mDocShell->GetWindow() : nullptr);
+ nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(window);
+
+ nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
+ if (!presShell) {
+ mozilla::DebugOnly<bool> dying;
+ MOZ_ASSERT(NS_SUCCEEDED(mDocShell->IsBeingDestroyed(&dying)) && dying,
+ "No presShell, but window is not being destroyed");
+ } else if (eventTarget) {
+ RefPtr<nsPresContext> presContext = presShell->GetPresContext();
+
+ nsEventStatus status = nsEventStatus_eIgnore;
+ WidgetMouseEvent event(true, eWindowClose, nullptr,
+ WidgetMouseEvent::eReal);
+ if (NS_SUCCEEDED(eventTarget->DispatchDOMEvent(&event, nullptr, presContext, &status)) &&
+ status == nsEventStatus_eConsumeNoDefault)
+ return false;
+ }
+
+ Destroy();
+ return false;
+}
+
+void
+nsWebShellWindow::SizeModeChanged(nsSizeMode sizeMode)
+{
+ // An alwaysRaised (or higher) window will hide any newly opened normal
+ // browser windows, so here we just drop a raised window to the normal
+ // zlevel if it's maximized. We make no provision for automatically
+ // re-raising it when restored.
+ if (sizeMode == nsSizeMode_Maximized || sizeMode == nsSizeMode_Fullscreen) {
+ uint32_t zLevel;
+ GetZLevel(&zLevel);
+ if (zLevel > nsIXULWindow::normalZ)
+ SetZLevel(nsIXULWindow::normalZ);
+ }
+ mWindow->SetSizeMode(sizeMode);
+
+ // Persist mode, but not immediately, because in many (all?)
+ // cases this will merge with the similar call in NS_SIZE and
+ // write the attribute values only once.
+ SetPersistenceTimer(PAD_MISC);
+ nsCOMPtr<nsPIDOMWindowOuter> ourWindow =
+ mDocShell ? mDocShell->GetWindow() : nullptr;
+ if (ourWindow) {
+ MOZ_ASSERT(ourWindow->IsOuterWindow());
+
+ // Ensure that the fullscreen state is synchronized between
+ // the widget and the outer window object.
+ if (sizeMode == nsSizeMode_Fullscreen) {
+ ourWindow->SetFullScreen(true);
+ }
+ else if (sizeMode != nsSizeMode_Minimized) {
+ if (ourWindow->GetFullScreen()) {
+ // The first SetFullscreenInternal call below ensures that we do
+ // not trigger any fullscreen transition even if the window was
+ // put in fullscreen only for the Fullscreen API. The second
+ // SetFullScreen call ensures that the window really exit from
+ // fullscreen even if it entered fullscreen for both Fullscreen
+ // Mode and Fullscreen API.
+ ourWindow->SetFullscreenInternal(FullscreenReason::ForForceExitFullscreen, false);
+ ourWindow->SetFullScreen(false);
+ }
+ }
+
+ // And always fire a user-defined sizemodechange event on the window
+ ourWindow->DispatchCustomEvent(NS_LITERAL_STRING("sizemodechange"));
+ }
+
+ nsIPresShell* presShell;
+ if ((presShell = GetPresShell())) {
+ presShell->GetPresContext()->SizeModeChanged(sizeMode);
+ }
+
+ // Note the current implementation of SetSizeMode just stores
+ // the new state; it doesn't actually resize. So here we store
+ // the state and pass the event on to the OS. The day is coming
+ // when we'll handle the event here, and the return result will
+ // then need to be different.
+}
+
+void
+nsWebShellWindow::UIResolutionChanged()
+{
+ nsCOMPtr<nsPIDOMWindowOuter> ourWindow =
+ mDocShell ? mDocShell->GetWindow() : nullptr;
+ if (ourWindow) {
+ MOZ_ASSERT(ourWindow->IsOuterWindow());
+ ourWindow->DispatchCustomEvent(NS_LITERAL_STRING("resolutionchange"));
+ }
+}
+
+void
+nsWebShellWindow::FullscreenChanged(bool aInFullscreen)
+{
+ if (mDocShell) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
+ ourWindow->FinishFullscreenChange(aInFullscreen);
+ }
+ }
+}
+
+void
+nsWebShellWindow::OSToolbarButtonPressed()
+{
+ // Keep a reference as setting the chrome flags can fire events.
+ nsCOMPtr<nsIXULWindow> xulWindow(this);
+
+ // rjc: don't use "nsIWebBrowserChrome::CHROME_EXTRA"
+ // due to components with multiple sidebar components
+ // (such as Mail/News, Addressbook, etc)... and frankly,
+ // Mac IE, OmniWeb, and other Mac OS X apps all work this way
+ uint32_t chromeMask = (nsIWebBrowserChrome::CHROME_TOOLBAR |
+ nsIWebBrowserChrome::CHROME_LOCATIONBAR |
+ nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR);
+
+ nsCOMPtr<nsIWebBrowserChrome> wbc(do_GetInterface(xulWindow));
+ if (!wbc)
+ return;
+
+ uint32_t chromeFlags, newChromeFlags = 0;
+ wbc->GetChromeFlags(&chromeFlags);
+ newChromeFlags = chromeFlags & chromeMask;
+ if (!newChromeFlags) chromeFlags |= chromeMask;
+ else chromeFlags &= (~newChromeFlags);
+ wbc->SetChromeFlags(chromeFlags);
+}
+
+bool
+nsWebShellWindow::ZLevelChanged(bool aImmediate, nsWindowZ *aPlacement,
+ nsIWidget* aRequestBelow, nsIWidget** aActualBelow)
+{
+ if (aActualBelow)
+ *aActualBelow = nullptr;
+
+ return ConstrainToZLevel(aImmediate, aPlacement, aRequestBelow, aActualBelow);
+}
+
+void
+nsWebShellWindow::WindowActivated()
+{
+ nsCOMPtr<nsIXULWindow> xulWindow(this);
+
+ // focusing the window could cause it to close, so keep a reference to it
+ nsCOMPtr<nsPIDOMWindowOuter> window = mDocShell ? mDocShell->GetWindow() : nullptr;
+ nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
+ if (fm && window)
+ fm->WindowRaised(window);
+
+ if (mChromeLoaded) {
+ PersistentAttributesDirty(PAD_POSITION | PAD_SIZE | PAD_MISC);
+ SavePersistentAttributes();
+ }
+}
+
+void
+nsWebShellWindow::WindowDeactivated()
+{
+ nsCOMPtr<nsIXULWindow> xulWindow(this);
+
+ nsCOMPtr<nsPIDOMWindowOuter> window =
+ mDocShell ? mDocShell->GetWindow() : nullptr;
+ nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
+ if (fm && window)
+ fm->WindowLowered(window);
+}
+
+#ifdef USE_NATIVE_MENUS
+static void LoadNativeMenus(nsIDOMDocument *aDOMDoc, nsIWidget *aParentWindow)
+{
+ nsCOMPtr<nsINativeMenuService> nms = do_GetService("@mozilla.org/widget/nativemenuservice;1");
+ if (!nms) {
+ return;
+ }
+
+ // Find the menubar tag (if there is more than one, we ignore all but
+ // the first).
+ nsCOMPtr<nsIDOMNodeList> menubarElements;
+ aDOMDoc->GetElementsByTagNameNS(NS_LITERAL_STRING("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"),
+ NS_LITERAL_STRING("menubar"),
+ getter_AddRefs(menubarElements));
+
+ nsCOMPtr<nsIDOMNode> menubarNode;
+ if (menubarElements)
+ menubarElements->Item(0, getter_AddRefs(menubarNode));
+
+ if (menubarNode) {
+ nsCOMPtr<nsIContent> menubarContent(do_QueryInterface(menubarNode));
+ nms->CreateNativeMenuBar(aParentWindow, menubarContent);
+ } else {
+ nms->CreateNativeMenuBar(aParentWindow, nullptr);
+ }
+}
+#endif
+
+namespace mozilla {
+
+class WebShellWindowTimerCallback final : public nsITimerCallback
+{
+public:
+ explicit WebShellWindowTimerCallback(nsWebShellWindow* aWindow)
+ : mWindow(aWindow)
+ {}
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_IMETHOD Notify(nsITimer* aTimer) override
+ {
+ // Although this object participates in a refcount cycle (this -> mWindow
+ // -> mSPTimer -> this), mSPTimer is a one-shot timer and releases this
+ // after it fires. So we don't need to release mWindow here.
+
+ mWindow->FirePersistenceTimer();
+ return NS_OK;
+ }
+
+private:
+ ~WebShellWindowTimerCallback() {}
+
+ RefPtr<nsWebShellWindow> mWindow;
+};
+
+NS_IMPL_ISUPPORTS(WebShellWindowTimerCallback, nsITimerCallback)
+
+} // namespace mozilla
+
+void
+nsWebShellWindow::SetPersistenceTimer(uint32_t aDirtyFlags)
+{
+ MutexAutoLock lock(mSPTimerLock);
+ if (!mSPTimer) {
+ mSPTimer = do_CreateInstance("@mozilla.org/timer;1");
+ if (!mSPTimer) {
+ NS_WARNING("Couldn't create @mozilla.org/timer;1 instance?");
+ return;
+ }
+ }
+
+ RefPtr<WebShellWindowTimerCallback> callback =
+ new WebShellWindowTimerCallback(this);
+ mSPTimer->InitWithCallback(callback, SIZE_PERSISTENCE_TIMEOUT,
+ nsITimer::TYPE_ONE_SHOT);
+
+ PersistentAttributesDirty(aDirtyFlags);
+}
+
+void
+nsWebShellWindow::FirePersistenceTimer()
+{
+ MutexAutoLock lock(mSPTimerLock);
+ SavePersistentAttributes();
+}
+
+
+//----------------------------------------
+// nsIWebProgessListener implementation
+//----------------------------------------
+NS_IMETHODIMP
+nsWebShellWindow::OnProgressChange(nsIWebProgress *aProgress,
+ nsIRequest *aRequest,
+ int32_t aCurSelfProgress,
+ int32_t aMaxSelfProgress,
+ int32_t aCurTotalProgress,
+ int32_t aMaxTotalProgress)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWebShellWindow::OnStateChange(nsIWebProgress *aProgress,
+ nsIRequest *aRequest,
+ uint32_t aStateFlags,
+ nsresult aStatus)
+{
+ // If the notification is not about a document finishing, then just
+ // ignore it...
+ if (!(aStateFlags & nsIWebProgressListener::STATE_STOP) ||
+ !(aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK)) {
+ return NS_OK;
+ }
+
+ if (mChromeLoaded)
+ return NS_OK;
+
+ // If this document notification is for a frame then ignore it...
+ nsCOMPtr<mozIDOMWindowProxy> eventWin;
+ aProgress->GetDOMWindow(getter_AddRefs(eventWin));
+ auto* eventPWin = nsPIDOMWindowOuter::From(eventWin);
+ if (eventPWin) {
+ nsPIDOMWindowOuter *rootPWin = eventPWin->GetPrivateRoot();
+ if (eventPWin != rootPWin)
+ return NS_OK;
+ }
+
+ mChromeLoaded = true;
+ mLockedUntilChromeLoad = false;
+
+#ifdef USE_NATIVE_MENUS
+ ///////////////////////////////
+ // Find the Menubar DOM and Load the menus, hooking them up to the loaded commands
+ ///////////////////////////////
+ nsCOMPtr<nsIContentViewer> cv;
+ mDocShell->GetContentViewer(getter_AddRefs(cv));
+ if (cv) {
+ nsCOMPtr<nsIDOMDocument> menubarDOMDoc(do_QueryInterface(cv->GetDocument()));
+ if (menubarDOMDoc)
+ LoadNativeMenus(menubarDOMDoc, mWindow);
+ }
+#endif // USE_NATIVE_MENUS
+
+ OnChromeLoaded();
+ LoadContentAreas();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWebShellWindow::OnLocationChange(nsIWebProgress *aProgress,
+ nsIRequest *aRequest,
+ nsIURI *aURI,
+ uint32_t aFlags)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWebShellWindow::OnStatusChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ nsresult aStatus,
+ const char16_t* aMessage)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWebShellWindow::OnSecurityChange(nsIWebProgress *aWebProgress,
+ nsIRequest *aRequest,
+ uint32_t state)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+
+//----------------------------------------
+
+// if the main document URL specified URLs for any content areas, start them loading
+void nsWebShellWindow::LoadContentAreas() {
+
+ nsAutoString searchSpec;
+
+ // fetch the chrome document URL
+ nsCOMPtr<nsIContentViewer> contentViewer;
+ // yes, it's possible for the docshell to be null even this early
+ // see bug 57514.
+ if (mDocShell)
+ mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
+ if (contentViewer) {
+ nsIDocument* doc = contentViewer->GetDocument();
+ if (doc) {
+ nsIURI* mainURL = doc->GetDocumentURI();
+
+ nsCOMPtr<nsIURL> url = do_QueryInterface(mainURL);
+ if (url) {
+ nsAutoCString search;
+ url->GetQuery(search);
+
+ AppendUTF8toUTF16(search, searchSpec);
+ }
+ }
+ }
+
+ // content URLs are specified in the search part of the URL
+ // as <contentareaID>=<escapedURL>[;(repeat)]
+ if (!searchSpec.IsEmpty()) {
+ int32_t begPos,
+ eqPos,
+ endPos;
+ nsString contentAreaID,
+ contentURL;
+ char *urlChar;
+ nsresult rv;
+ for (endPos = 0; endPos < (int32_t)searchSpec.Length(); ) {
+ // extract contentAreaID and URL substrings
+ begPos = endPos;
+ eqPos = searchSpec.FindChar('=', begPos);
+ if (eqPos < 0)
+ break;
+
+ endPos = searchSpec.FindChar(';', eqPos);
+ if (endPos < 0)
+ endPos = searchSpec.Length();
+ searchSpec.Mid(contentAreaID, begPos, eqPos-begPos);
+ searchSpec.Mid(contentURL, eqPos+1, endPos-eqPos-1);
+ endPos++;
+
+ // see if we have a docshell with a matching contentAreaID
+ nsCOMPtr<nsIDocShellTreeItem> content;
+ rv = GetContentShellById(contentAreaID.get(), getter_AddRefs(content));
+ if (NS_SUCCEEDED(rv) && content) {
+ nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(content));
+ if (webNav) {
+ urlChar = ToNewCString(contentURL);
+ if (urlChar) {
+ nsUnescape(urlChar);
+ contentURL.AssignWithConversion(urlChar);
+ webNav->LoadURI(contentURL.get(),
+ nsIWebNavigation::LOAD_FLAGS_NONE,
+ nullptr,
+ nullptr,
+ nullptr);
+ free(urlChar);
+ }
+ }
+ }
+ }
+ }
+}
+
+/**
+ * ExecuteCloseHandler - Run the close handler, if any.
+ * @return true iff we found a close handler to run.
+ */
+bool nsWebShellWindow::ExecuteCloseHandler()
+{
+ /* If the event handler closes this window -- a likely scenario --
+ things get deleted out of order without this death grip.
+ (The problem may be the death grip in nsWindow::windowProc,
+ which forces this window's widget to remain alive longer
+ than it otherwise would.) */
+ nsCOMPtr<nsIXULWindow> kungFuDeathGrip(this);
+
+ nsCOMPtr<EventTarget> eventTarget;
+ if (mDocShell) {
+ eventTarget = do_QueryInterface(mDocShell->GetWindow());
+ }
+
+ if (eventTarget) {
+ nsCOMPtr<nsIContentViewer> contentViewer;
+ mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
+ if (contentViewer) {
+ RefPtr<nsPresContext> presContext;
+ contentViewer->GetPresContext(getter_AddRefs(presContext));
+
+ nsEventStatus status = nsEventStatus_eIgnore;
+ WidgetMouseEvent event(true, eWindowClose, nullptr,
+ WidgetMouseEvent::eReal);
+
+ nsresult rv =
+ eventTarget->DispatchDOMEvent(&event, nullptr, presContext, &status);
+ if (NS_SUCCEEDED(rv) && status == nsEventStatus_eConsumeNoDefault)
+ return true;
+ // else fall through and return false
+ }
+ }
+
+ return false;
+} // ExecuteCloseHandler
+
+void nsWebShellWindow::ConstrainToOpenerScreen(int32_t* aX, int32_t* aY)
+{
+ if (mOpenerScreenRect.IsEmpty()) {
+ *aX = *aY = 0;
+ return;
+ }
+
+ int32_t left, top, width, height;
+ // Constrain initial positions to the same screen as opener
+ nsCOMPtr<nsIScreenManager> screenmgr = do_GetService("@mozilla.org/gfx/screenmanager;1");
+ if (screenmgr) {
+ nsCOMPtr<nsIScreen> screen;
+ screenmgr->ScreenForRect(mOpenerScreenRect.x, mOpenerScreenRect.y,
+ mOpenerScreenRect.width, mOpenerScreenRect.height,
+ getter_AddRefs(screen));
+ if (screen) {
+ screen->GetAvailRectDisplayPix(&left, &top, &width, &height);
+ if (*aX < left || *aX > left + width) {
+ *aX = left;
+ }
+ if (*aY < top || *aY > top + height) {
+ *aY = top;
+ }
+ }
+ }
+}
+
+// nsIBaseWindow
+NS_IMETHODIMP nsWebShellWindow::Destroy()
+{
+ nsresult rv;
+ nsCOMPtr<nsIWebProgress> webProgress(do_GetInterface(mDocShell, &rv));
+ if (webProgress) {
+ webProgress->RemoveProgressListener(this);
+ }
+
+ nsCOMPtr<nsIXULWindow> kungFuDeathGrip(this);
+ {
+ MutexAutoLock lock(mSPTimerLock);
+ if (mSPTimer) {
+ mSPTimer->Cancel();
+ SavePersistentAttributes();
+ mSPTimer = nullptr;
+ }
+ }
+ return nsXULWindow::Destroy();
+}
+
+nsIXULWindow*
+nsWebShellWindow::WidgetListenerDelegate::GetXULWindow()
+{
+ return mWebShellWindow->GetXULWindow();
+}
+
+nsIPresShell*
+nsWebShellWindow::WidgetListenerDelegate::GetPresShell()
+{
+ return mWebShellWindow->GetPresShell();
+}
+
+bool
+nsWebShellWindow::WidgetListenerDelegate::WindowMoved(
+ nsIWidget* aWidget, int32_t aX, int32_t aY)
+{
+ RefPtr<nsWebShellWindow> holder = mWebShellWindow;
+ return holder->WindowMoved(aWidget, aX, aY);
+}
+
+bool
+nsWebShellWindow::WidgetListenerDelegate::WindowResized(
+ nsIWidget* aWidget, int32_t aWidth, int32_t aHeight)
+{
+ RefPtr<nsWebShellWindow> holder = mWebShellWindow;
+ return holder->WindowResized(aWidget, aWidth, aHeight);
+}
+
+bool
+nsWebShellWindow::WidgetListenerDelegate::RequestWindowClose(nsIWidget* aWidget)
+{
+ RefPtr<nsWebShellWindow> holder = mWebShellWindow;
+ return holder->RequestWindowClose(aWidget);
+}
+
+void
+nsWebShellWindow::WidgetListenerDelegate::SizeModeChanged(nsSizeMode aSizeMode)
+{
+ RefPtr<nsWebShellWindow> holder = mWebShellWindow;
+ holder->SizeModeChanged(aSizeMode);
+}
+
+void
+nsWebShellWindow::WidgetListenerDelegate::UIResolutionChanged()
+{
+ RefPtr<nsWebShellWindow> holder = mWebShellWindow;
+ holder->UIResolutionChanged();
+}
+
+void
+nsWebShellWindow::WidgetListenerDelegate::FullscreenChanged(bool aInFullscreen)
+{
+ RefPtr<nsWebShellWindow> holder = mWebShellWindow;
+ holder->FullscreenChanged(aInFullscreen);
+}
+
+void
+nsWebShellWindow::WidgetListenerDelegate::OSToolbarButtonPressed()
+{
+ RefPtr<nsWebShellWindow> holder = mWebShellWindow;
+ holder->OSToolbarButtonPressed();
+}
+
+bool
+nsWebShellWindow::WidgetListenerDelegate::ZLevelChanged(
+ bool aImmediate, nsWindowZ *aPlacement, nsIWidget* aRequestBelow,
+ nsIWidget** aActualBelow)
+{
+ RefPtr<nsWebShellWindow> holder = mWebShellWindow;
+ return holder->ZLevelChanged(aImmediate,
+ aPlacement,
+ aRequestBelow,
+ aActualBelow);
+}
+
+void
+nsWebShellWindow::WidgetListenerDelegate::WindowActivated()
+{
+ RefPtr<nsWebShellWindow> holder = mWebShellWindow;
+ holder->WindowActivated();
+}
+
+void
+nsWebShellWindow::WidgetListenerDelegate::WindowDeactivated()
+{
+ RefPtr<nsWebShellWindow> holder = mWebShellWindow;
+ holder->WindowDeactivated();
+}
diff --git a/xpfe/appshell/nsWebShellWindow.h b/xpfe/appshell/nsWebShellWindow.h
new file mode 100644
index 000000000..e2b796161
--- /dev/null
+++ b/xpfe/appshell/nsWebShellWindow.h
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsWebShellWindow_h__
+#define nsWebShellWindow_h__
+
+#include "mozilla/Mutex.h"
+#include "nsIWebProgressListener.h"
+#include "nsITimer.h"
+#include "nsCOMPtr.h"
+#include "nsXULWindow.h"
+#include "nsIWidgetListener.h"
+#include "nsITabParent.h"
+
+/* Forward declarations.... */
+class nsIURI;
+
+struct nsWidgetInitData;
+
+namespace mozilla {
+class WebShellWindowTimerCallback;
+} // namespace mozilla
+
+class nsWebShellWindow final : public nsXULWindow,
+ public nsIWebProgressListener
+{
+public:
+
+ // The implementation of non-refcounted nsIWidgetListener, which would hold a
+ // strong reference on stack before calling nsWebShellWindow
+ class WidgetListenerDelegate : public nsIWidgetListener
+ {
+ public:
+ explicit WidgetListenerDelegate(nsWebShellWindow* aWebShellWindow)
+ : mWebShellWindow(aWebShellWindow) {}
+
+ virtual nsIXULWindow* GetXULWindow() override;
+ virtual nsIPresShell* GetPresShell() override;
+ virtual bool WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y) override;
+ virtual bool WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight) override;
+ virtual bool RequestWindowClose(nsIWidget* aWidget) override;
+ virtual void SizeModeChanged(nsSizeMode sizeMode) override;
+ virtual void UIResolutionChanged() override;
+ virtual void FullscreenChanged(bool aInFullscreen) override;
+ virtual void OSToolbarButtonPressed() override;
+ virtual bool ZLevelChanged(bool aImmediate,
+ nsWindowZ *aPlacement,
+ nsIWidget* aRequestBelow,
+ nsIWidget** aActualBelow) override;
+ virtual void WindowActivated() override;
+ virtual void WindowDeactivated() override;
+
+ private:
+ // The lifetime of WidgetListenerDelegate is bound to nsWebShellWindow so
+ // we just use a raw pointer here.
+ nsWebShellWindow* mWebShellWindow;
+ };
+
+ explicit nsWebShellWindow(uint32_t aChromeFlags);
+
+ // nsISupports interface...
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // nsWebShellWindow methods...
+ nsresult Initialize(nsIXULWindow * aParent, nsIXULWindow * aOpener,
+ nsIURI* aUrl,
+ int32_t aInitialWidth, int32_t aInitialHeight,
+ bool aIsHiddenWindow,
+ nsITabParent *aOpeningTab,
+ mozIDOMWindowProxy *aOpenerWIndow,
+ nsWidgetInitData& widgetInitData);
+
+ nsresult Toolbar();
+
+ // nsIWebProgressListener
+ NS_DECL_NSIWEBPROGRESSLISTENER
+
+ // nsIBaseWindow
+ NS_IMETHOD Destroy() override;
+
+ // nsIWidgetListener
+ nsIXULWindow* GetXULWindow() { return this; }
+ nsIPresShell* GetPresShell();
+ bool WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y);
+ bool WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight);
+ bool RequestWindowClose(nsIWidget* aWidget);
+ void SizeModeChanged(nsSizeMode sizeMode);
+ void UIResolutionChanged();
+ void FullscreenChanged(bool aInFullscreen);
+ void OSToolbarButtonPressed();
+ bool ZLevelChanged(bool aImmediate, nsWindowZ *aPlacement,
+ nsIWidget* aRequestBelow, nsIWidget** aActualBelow);
+ void WindowActivated();
+ void WindowDeactivated();
+
+protected:
+ friend class mozilla::WebShellWindowTimerCallback;
+
+ virtual ~nsWebShellWindow();
+
+ void LoadContentAreas();
+ bool ExecuteCloseHandler();
+ void ConstrainToOpenerScreen(int32_t* aX, int32_t* aY);
+
+ nsCOMPtr<nsITimer> mSPTimer;
+ mozilla::Mutex mSPTimerLock;
+ WidgetListenerDelegate mWidgetListenerDelegate;
+
+ void SetPersistenceTimer(uint32_t aDirtyFlags);
+ void FirePersistenceTimer();
+};
+
+
+#endif /* nsWebShellWindow_h__ */
diff --git a/xpfe/appshell/nsWindowMediator.cpp b/xpfe/appshell/nsWindowMediator.cpp
new file mode 100644
index 000000000..6d69bc764
--- /dev/null
+++ b/xpfe/appshell/nsWindowMediator.cpp
@@ -0,0 +1,846 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsUnicharUtils.h"
+#include "nsTArray.h"
+#include "nsIBaseWindow.h"
+#include "nsIWidget.h"
+#include "nsIDOMWindow.h"
+#include "nsIObserverService.h"
+#include "nsIServiceManager.h"
+#include "nsISimpleEnumerator.h"
+#include "nsAppShellWindowEnumerator.h"
+#include "nsWindowMediator.h"
+#include "nsIWindowMediatorListener.h"
+#include "nsXPIDLString.h"
+#include "nsGlobalWindow.h"
+
+#include "nsIDocShell.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIXULWindow.h"
+
+using namespace mozilla;
+
+static bool notifyOpenWindow(nsIWindowMediatorListener *aElement, void* aData);
+static bool notifyCloseWindow(nsIWindowMediatorListener *aElement, void* aData);
+static bool notifyWindowTitleChange(nsIWindowMediatorListener *aElement, void* aData);
+
+// for notifyWindowTitleChange
+struct WindowTitleData {
+ nsIXULWindow* mWindow;
+ const char16_t *mTitle;
+};
+
+nsresult
+nsWindowMediator::GetDOMWindow(nsIXULWindow* inWindow,
+ nsCOMPtr<nsPIDOMWindowOuter>& outDOMWindow)
+{
+ nsCOMPtr<nsIDocShell> docShell;
+
+ outDOMWindow = nullptr;
+ inWindow->GetDocShell(getter_AddRefs(docShell));
+ NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
+
+ outDOMWindow = docShell->GetWindow();
+ return outDOMWindow ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsWindowMediator::nsWindowMediator() :
+ mEnumeratorList(), mOldestWindow(nullptr), mTopmostWindow(nullptr),
+ mTimeStamp(0), mSortingZOrder(false), mReady(false)
+{
+}
+
+nsWindowMediator::~nsWindowMediator()
+{
+ while (mOldestWindow)
+ UnregisterWindow(mOldestWindow);
+}
+
+nsresult nsWindowMediator::Init()
+{
+ nsresult rv;
+ nsCOMPtr<nsIObserverService> obsSvc =
+ do_GetService("@mozilla.org/observer-service;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = obsSvc->AddObserver(this, "xpcom-shutdown", true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mReady = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWindowMediator::RegisterWindow(nsIXULWindow* inWindow)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_ENSURE_STATE(mReady);
+
+ if (GetInfoFor(inWindow)) {
+ NS_ERROR("multiple window registration");
+ return NS_ERROR_FAILURE;
+ }
+
+ mTimeStamp++;
+
+ // Create window info struct and add to list of windows
+ nsWindowInfo* windowInfo = new nsWindowInfo(inWindow, mTimeStamp);
+
+ WindowTitleData winData = { inWindow, nullptr };
+ mListeners.EnumerateForwards(notifyOpenWindow, &winData);
+
+ if (mOldestWindow)
+ windowInfo->InsertAfter(mOldestWindow->mOlder, nullptr);
+ else
+ mOldestWindow = windowInfo;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::UnregisterWindow(nsIXULWindow* inWindow)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_ENSURE_STATE(mReady);
+ nsWindowInfo *info = GetInfoFor(inWindow);
+ if (info)
+ return UnregisterWindow(info);
+ return NS_ERROR_INVALID_ARG;
+}
+
+nsresult
+nsWindowMediator::UnregisterWindow(nsWindowInfo *inInfo)
+{
+ // Inform the iterators
+ uint32_t index = 0;
+ while (index < mEnumeratorList.Length()) {
+ mEnumeratorList[index]->WindowRemoved(inInfo);
+ index++;
+ }
+
+ WindowTitleData winData = { inInfo->mWindow.get(), nullptr };
+ mListeners.EnumerateForwards(notifyCloseWindow, &winData);
+
+ // Remove from the lists and free up
+ if (inInfo == mOldestWindow)
+ mOldestWindow = inInfo->mYounger;
+ if (inInfo == mTopmostWindow)
+ mTopmostWindow = inInfo->mLower;
+ inInfo->Unlink(true, true);
+ if (inInfo == mOldestWindow)
+ mOldestWindow = nullptr;
+ if (inInfo == mTopmostWindow)
+ mTopmostWindow = nullptr;
+ delete inInfo;
+
+ return NS_OK;
+}
+
+nsWindowInfo*
+nsWindowMediator::GetInfoFor(nsIXULWindow *aWindow)
+{
+ nsWindowInfo *info,
+ *listEnd;
+
+ if (!aWindow)
+ return nullptr;
+
+ info = mOldestWindow;
+ listEnd = nullptr;
+ while (info != listEnd) {
+ if (info->mWindow.get() == aWindow)
+ return info;
+ info = info->mYounger;
+ listEnd = mOldestWindow;
+ }
+ return nullptr;
+}
+
+nsWindowInfo*
+nsWindowMediator::GetInfoFor(nsIWidget *aWindow)
+{
+ nsWindowInfo *info,
+ *listEnd;
+
+ if (!aWindow)
+ return nullptr;
+
+ info = mOldestWindow;
+ listEnd = nullptr;
+
+ nsCOMPtr<nsIWidget> scanWidget;
+ while (info != listEnd) {
+ nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(info->mWindow));
+ if (base)
+ base->GetMainWidget(getter_AddRefs(scanWidget));
+ if (aWindow == scanWidget.get())
+ return info;
+ info = info->mYounger;
+ listEnd = mOldestWindow;
+ }
+ return nullptr;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::GetEnumerator(const char16_t* inType, nsISimpleEnumerator** outEnumerator)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_ENSURE_ARG_POINTER(outEnumerator);
+ NS_ENSURE_STATE(mReady);
+
+ RefPtr<nsAppShellWindowEnumerator> enumerator = new nsASDOMWindowEarlyToLateEnumerator(inType, *this);
+ enumerator.forget(outEnumerator);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::GetXULWindowEnumerator(const char16_t* inType, nsISimpleEnumerator** outEnumerator)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_ENSURE_ARG_POINTER(outEnumerator);
+ NS_ENSURE_STATE(mReady);
+
+ RefPtr<nsAppShellWindowEnumerator> enumerator = new nsASXULWindowEarlyToLateEnumerator(inType, *this);
+ enumerator.forget(outEnumerator);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::GetZOrderDOMWindowEnumerator(
+ const char16_t *aWindowType, bool aFrontToBack,
+ nsISimpleEnumerator **_retval)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_ENSURE_ARG_POINTER(_retval);
+ NS_ENSURE_STATE(mReady);
+
+ RefPtr<nsAppShellWindowEnumerator> enumerator;
+ if (aFrontToBack)
+ enumerator = new nsASDOMWindowFrontToBackEnumerator(aWindowType, *this);
+ else
+ enumerator = new nsASDOMWindowBackToFrontEnumerator(aWindowType, *this);
+
+ enumerator.forget(_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::GetZOrderXULWindowEnumerator(
+ const char16_t *aWindowType, bool aFrontToBack,
+ nsISimpleEnumerator **_retval)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_ENSURE_ARG_POINTER(_retval);
+ NS_ENSURE_STATE(mReady);
+
+ RefPtr<nsAppShellWindowEnumerator> enumerator;
+ if (aFrontToBack)
+ enumerator = new nsASXULWindowFrontToBackEnumerator(aWindowType, *this);
+ else
+ enumerator = new nsASXULWindowBackToFrontEnumerator(aWindowType, *this);
+
+ enumerator.forget(_retval);
+ return NS_OK;
+}
+
+int32_t
+nsWindowMediator::AddEnumerator(nsAppShellWindowEnumerator * inEnumerator)
+{
+ return mEnumeratorList.AppendElement(inEnumerator) != nullptr;
+}
+
+int32_t
+nsWindowMediator::RemoveEnumerator(nsAppShellWindowEnumerator * inEnumerator)
+{
+ return mEnumeratorList.RemoveElement(inEnumerator);
+}
+
+// Returns the window of type inType ( if null return any window type ) which has the most recent
+// time stamp
+NS_IMETHODIMP
+nsWindowMediator::GetMostRecentWindow(const char16_t* inType,
+ mozIDOMWindowProxy** outWindow)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_ENSURE_ARG_POINTER(outWindow);
+ *outWindow = nullptr;
+ if (!mReady)
+ return NS_OK;
+
+ // Find the most window with the highest time stamp that matches
+ // the requested type
+ nsWindowInfo* info = MostRecentWindowInfo(inType, false);
+ if (info && info->mWindow) {
+ nsCOMPtr<nsPIDOMWindowOuter> DOMWindow;
+ if (NS_SUCCEEDED(GetDOMWindow(info->mWindow, DOMWindow))) {
+ DOMWindow.forget(outWindow);
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::GetMostRecentNonPBWindow(const char16_t* aType, mozIDOMWindowProxy** aWindow)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_ENSURE_ARG_POINTER(aWindow);
+ *aWindow = nullptr;
+
+ nsWindowInfo *info = MostRecentWindowInfo(aType, true);
+ nsCOMPtr<nsPIDOMWindowOuter> domWindow;
+ if (info && info->mWindow) {
+ GetDOMWindow(info->mWindow, domWindow);
+ }
+
+ if (!domWindow) {
+ return NS_ERROR_FAILURE;
+ }
+
+ domWindow.forget(aWindow);
+ return NS_OK;
+}
+
+nsWindowInfo*
+nsWindowMediator::MostRecentWindowInfo(const char16_t* inType,
+ bool aSkipPrivateBrowsingOrClosed)
+{
+ int32_t lastTimeStamp = -1;
+ nsAutoString typeString(inType);
+ bool allWindows = !inType || typeString.IsEmpty();
+
+ // Find the most recent window with the highest time stamp that matches
+ // the requested type and has the correct browsing mode.
+ nsWindowInfo* searchInfo = mOldestWindow;
+ nsWindowInfo* listEnd = nullptr;
+ nsWindowInfo* foundInfo = nullptr;
+ for (; searchInfo != listEnd; searchInfo = searchInfo->mYounger) {
+ listEnd = mOldestWindow;
+
+ if (!allWindows && !searchInfo->TypeEquals(typeString)) {
+ continue;
+ }
+ if (searchInfo->mTimeStamp < lastTimeStamp) {
+ continue;
+ }
+ if (!searchInfo->mWindow) {
+ continue;
+ }
+ if (aSkipPrivateBrowsingOrClosed) {
+ nsCOMPtr<nsIDocShell> docShell;
+ searchInfo->mWindow->GetDocShell(getter_AddRefs(docShell));
+ nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
+ if (!loadContext || loadContext->UsePrivateBrowsing()) {
+ continue;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> piwindow = docShell->GetWindow();
+ if (!piwindow || piwindow->Closed()) {
+ continue;
+ }
+ }
+
+ foundInfo = searchInfo;
+ lastTimeStamp = searchInfo->mTimeStamp;
+ }
+
+ return foundInfo;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::GetOuterWindowWithId(uint64_t aWindowID,
+ mozIDOMWindowProxy** aWindow)
+{
+ RefPtr<nsGlobalWindow> window = nsGlobalWindow::GetOuterWindowWithId(aWindowID);
+ nsCOMPtr<nsPIDOMWindowOuter> outer = window ? window->AsOuter() : nullptr;
+ outer.forget(aWindow);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::GetCurrentInnerWindowWithId(uint64_t aWindowID,
+ mozIDOMWindow** aWindow)
+{
+ RefPtr<nsGlobalWindow> window = nsGlobalWindow::GetInnerWindowWithId(aWindowID);
+
+ // not found
+ if (!window)
+ return NS_OK;
+
+ nsCOMPtr<nsPIDOMWindowInner> inner = window->AsInner();
+ nsCOMPtr<nsPIDOMWindowOuter> outer = inner->GetOuterWindow();
+ NS_ENSURE_TRUE(outer, NS_ERROR_UNEXPECTED);
+
+ // outer is already using another inner, so it's same as not found
+ if (outer->GetCurrentInnerWindow() != inner)
+ return NS_OK;
+
+ inner.forget(aWindow);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::UpdateWindowTimeStamp(nsIXULWindow* inWindow)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_ENSURE_STATE(mReady);
+ nsWindowInfo *info = GetInfoFor(inWindow);
+ if (info) {
+ // increment the window's time stamp
+ info->mTimeStamp = ++mTimeStamp;
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::UpdateWindowTitle(nsIXULWindow* inWindow,
+ const char16_t* inTitle)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_ENSURE_STATE(mReady);
+ if (GetInfoFor(inWindow)) {
+ WindowTitleData winData = { inWindow, inTitle };
+ mListeners.EnumerateForwards(notifyWindowTitleChange, &winData);
+ }
+
+ return NS_OK;
+}
+
+/* This method's plan is to intervene only when absolutely necessary.
+ We will get requests to place our windows behind unknown windows.
+ For the most part, we need to leave those alone (turning them into
+ explicit requests to be on top breaks Windows.) So generally we
+ calculate a change as seldom as possible.
+*/
+NS_IMETHODIMP
+nsWindowMediator::CalculateZPosition(
+ nsIXULWindow *inWindow,
+ uint32_t inPosition,
+ nsIWidget *inBelow,
+ uint32_t *outPosition,
+ nsIWidget **outBelow,
+ bool *outAltered)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_ENSURE_ARG_POINTER(outBelow);
+ NS_ENSURE_STATE(mReady);
+
+ *outBelow = nullptr;
+
+ if (!inWindow || !outPosition || !outAltered)
+ return NS_ERROR_NULL_POINTER;
+
+ if (inPosition != nsIWindowMediator::zLevelTop &&
+ inPosition != nsIWindowMediator::zLevelBottom &&
+ inPosition != nsIWindowMediator::zLevelBelow)
+ return NS_ERROR_INVALID_ARG;
+
+ nsWindowInfo *info = mTopmostWindow;
+ nsIXULWindow *belowWindow = nullptr;
+ bool found = false;
+ nsresult result = NS_OK;
+
+ *outPosition = inPosition;
+ *outAltered = false;
+
+ if (mSortingZOrder) { // don't fight SortZOrder()
+ *outBelow = inBelow;
+ NS_IF_ADDREF(*outBelow);
+ return NS_OK;
+ }
+
+ uint32_t inZ;
+ GetZLevel(inWindow, &inZ);
+
+ if (inPosition == nsIWindowMediator::zLevelBelow) {
+ // locate inBelow. use topmost if it can't be found or isn't in the
+ // z-order list
+ info = GetInfoFor(inBelow);
+ if (!info || (info->mYounger != info && info->mLower == info))
+ info = mTopmostWindow;
+ else
+ found = true;
+
+ if (!found) {
+ /* Treat unknown windows as a request to be on top.
+ Not as it should be, but that's what Windows gives us.
+ Note we change inPosition, but not *outPosition. This forces
+ us to go through the "on top" calculation just below, without
+ necessarily changing the output parameters. */
+ inPosition = nsIWindowMediator::zLevelTop;
+ }
+ }
+
+ if (inPosition == nsIWindowMediator::zLevelTop) {
+ if (mTopmostWindow && mTopmostWindow->mZLevel > inZ) {
+ // asked for topmost, can't have it. locate highest allowed position.
+ do {
+ if (info->mZLevel <= inZ)
+ break;
+ info = info->mLower;
+ } while (info != mTopmostWindow);
+
+ *outPosition = nsIWindowMediator::zLevelBelow;
+ belowWindow = info->mHigher->mWindow;
+ *outAltered = true;
+ }
+ } else if (inPosition == nsIWindowMediator::zLevelBottom) {
+ if (mTopmostWindow && mTopmostWindow->mHigher->mZLevel < inZ) {
+ // asked for bottommost, can't have it. locate lowest allowed position.
+ do {
+ info = info->mHigher;
+ if (info->mZLevel >= inZ)
+ break;
+ } while (info != mTopmostWindow);
+
+ *outPosition = nsIWindowMediator::zLevelBelow;
+ belowWindow = info->mWindow;
+ *outAltered = true;
+ }
+ } else {
+ unsigned long relativeZ;
+
+ // check that we're in the right z-plane
+ if (found) {
+ belowWindow = info->mWindow;
+ relativeZ = info->mZLevel;
+ if (relativeZ > inZ) {
+ // might be OK. is lower window, if any, lower?
+ if (info->mLower != info && info->mLower->mZLevel > inZ) {
+ do {
+ if (info->mZLevel <= inZ)
+ break;
+ info = info->mLower;
+ } while (info != mTopmostWindow);
+
+ belowWindow = info->mHigher->mWindow;
+ *outAltered = true;
+ }
+ } else if (relativeZ < inZ) {
+ // nope. look for a higher window to be behind.
+ do {
+ info = info->mHigher;
+ if (info->mZLevel >= inZ)
+ break;
+ } while (info != mTopmostWindow);
+
+ if (info->mZLevel >= inZ)
+ belowWindow = info->mWindow;
+ else
+ *outPosition = nsIWindowMediator::zLevelTop;
+ *outAltered = true;
+ } // else they're equal, so it's OK
+ }
+ }
+
+ if (NS_SUCCEEDED(result) && belowWindow) {
+ nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(belowWindow));
+ if (base)
+ base->GetMainWidget(outBelow);
+ else
+ result = NS_ERROR_NO_INTERFACE;
+ }
+
+ return result;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::SetZPosition(
+ nsIXULWindow *inWindow,
+ uint32_t inPosition,
+ nsIXULWindow *inBelow)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ nsWindowInfo *inInfo,
+ *belowInfo;
+
+ if ((inPosition != nsIWindowMediator::zLevelTop &&
+ inPosition != nsIWindowMediator::zLevelBottom &&
+ inPosition != nsIWindowMediator::zLevelBelow) ||
+ !inWindow) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (mSortingZOrder) // don't fight SortZOrder()
+ return NS_OK;
+
+ NS_ENSURE_STATE(mReady);
+
+ /* Locate inWindow and unlink it from the z-order list.
+ It's important we look for it in the age list, not the z-order list.
+ This is because the former is guaranteed complete, while
+ now may be this window's first exposure to the latter. */
+ inInfo = GetInfoFor(inWindow);
+ if (!inInfo)
+ return NS_ERROR_INVALID_ARG;
+
+ // locate inBelow, place inWindow behind it
+ if (inPosition == nsIWindowMediator::zLevelBelow) {
+ belowInfo = GetInfoFor(inBelow);
+ // it had better also be in the z-order list
+ if (belowInfo &&
+ belowInfo->mYounger != belowInfo && belowInfo->mLower == belowInfo) {
+ belowInfo = nullptr;
+ }
+ if (!belowInfo) {
+ if (inBelow)
+ return NS_ERROR_INVALID_ARG;
+ else
+ inPosition = nsIWindowMediator::zLevelTop;
+ }
+ }
+ if (inPosition == nsIWindowMediator::zLevelTop ||
+ inPosition == nsIWindowMediator::zLevelBottom)
+ belowInfo = mTopmostWindow ? mTopmostWindow->mHigher : nullptr;
+
+ if (inInfo != belowInfo) {
+ inInfo->Unlink(false, true);
+ inInfo->InsertAfter(nullptr, belowInfo);
+ }
+ if (inPosition == nsIWindowMediator::zLevelTop)
+ mTopmostWindow = inInfo;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::GetZLevel(nsIXULWindow *aWindow, uint32_t *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = nsIXULWindow::normalZ;
+ nsWindowInfo *info = GetInfoFor(aWindow);
+ if (info) {
+ *_retval = info->mZLevel;
+ } else {
+ NS_WARNING("getting z level of unregistered window");
+ // this goes off during window destruction
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::SetZLevel(nsIXULWindow *aWindow, uint32_t aZLevel)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_ENSURE_STATE(mReady);
+
+ nsWindowInfo *info = GetInfoFor(aWindow);
+ NS_ASSERTION(info, "setting z level of unregistered window");
+ if (!info)
+ return NS_ERROR_FAILURE;
+
+ if (info->mZLevel != aZLevel) {
+ bool lowered = info->mZLevel > aZLevel;
+ info->mZLevel = aZLevel;
+ if (lowered)
+ SortZOrderFrontToBack();
+ else
+ SortZOrderBackToFront();
+ }
+ return NS_OK;
+}
+
+/* Fix potentially out-of-order windows by performing an insertion sort
+ on the z-order list. The method will work no matter how broken the
+ list, but its assumed usage is immediately after one window's z level
+ has been changed, so one window is potentially out of place. Such a sort
+ is most efficiently done in a particular direction. Use this one
+ if a window's z level has just been reduced, so the sort is most efficiently
+ done front to back.
+ Note it's hardly worth going to all the trouble to write two versions
+ of this method except that if we choose the inefficient sorting direction,
+ on slow systems windows could visibly bubble around the window that
+ was moved.
+*/
+void
+nsWindowMediator::SortZOrderFrontToBack()
+{
+ nsWindowInfo *scan, // scans list looking for problems
+ *search, // searches for correct placement for scan window
+ *prev, // previous search element
+ *lowest; // bottom-most window in list
+ bool finished;
+
+ if (!mTopmostWindow) // early during program execution there's no z list yet
+ return; // there's also only one window, so this is not dangerous
+
+ mSortingZOrder = true;
+
+ /* Step through the list from top to bottom. If we find a window which
+ should be moved down in the list, move it to its highest legal position. */
+ do {
+ finished = true;
+ lowest = mTopmostWindow->mHigher;
+ scan = mTopmostWindow;
+ while (scan != lowest) {
+ uint32_t scanZ = scan->mZLevel;
+ if (scanZ < scan->mLower->mZLevel) { // out of order
+ search = scan->mLower;
+ do {
+ prev = search;
+ search = search->mLower;
+ } while (prev != lowest && scanZ < search->mZLevel);
+
+ // reposition |scan| within the list
+ if (scan == mTopmostWindow)
+ mTopmostWindow = scan->mLower;
+ scan->Unlink(false, true);
+ scan->InsertAfter(nullptr, prev);
+
+ // fix actual window order
+ nsCOMPtr<nsIBaseWindow> base;
+ nsCOMPtr<nsIWidget> scanWidget;
+ nsCOMPtr<nsIWidget> prevWidget;
+ base = do_QueryInterface(scan->mWindow);
+ if (base)
+ base->GetMainWidget(getter_AddRefs(scanWidget));
+ base = do_QueryInterface(prev->mWindow);
+ if (base)
+ base->GetMainWidget(getter_AddRefs(prevWidget));
+ if (scanWidget)
+ scanWidget->PlaceBehind(eZPlacementBelow, prevWidget, false);
+
+ finished = false;
+ break;
+ }
+ scan = scan->mLower;
+ }
+ } while (!finished);
+
+ mSortingZOrder = false;
+}
+
+// see comment for SortZOrderFrontToBack
+void
+nsWindowMediator::SortZOrderBackToFront()
+{
+ nsWindowInfo *scan, // scans list looking for problems
+ *search, // searches for correct placement for scan window
+ *lowest; // bottom-most window in list
+ bool finished;
+
+ if (!mTopmostWindow) // early during program execution there's no z list yet
+ return; // there's also only one window, so this is not dangerous
+
+ mSortingZOrder = true;
+
+ /* Step through the list from bottom to top. If we find a window which
+ should be moved up in the list, move it to its lowest legal position. */
+ do {
+ finished = true;
+ lowest = mTopmostWindow->mHigher;
+ scan = lowest;
+ while (scan != mTopmostWindow) {
+ uint32_t scanZ = scan->mZLevel;
+ if (scanZ > scan->mHigher->mZLevel) { // out of order
+ search = scan;
+ do {
+ search = search->mHigher;
+ } while (search != lowest && scanZ > search->mZLevel);
+
+ // reposition |scan| within the list
+ if (scan != search && scan != search->mLower) {
+ scan->Unlink(false, true);
+ scan->InsertAfter(nullptr, search);
+ }
+ if (search == lowest)
+ mTopmostWindow = scan;
+
+ // fix actual window order
+ nsCOMPtr<nsIBaseWindow> base;
+ nsCOMPtr<nsIWidget> scanWidget;
+ nsCOMPtr<nsIWidget> searchWidget;
+ base = do_QueryInterface(scan->mWindow);
+ if (base)
+ base->GetMainWidget(getter_AddRefs(scanWidget));
+ if (mTopmostWindow != scan) {
+ base = do_QueryInterface(search->mWindow);
+ if (base)
+ base->GetMainWidget(getter_AddRefs(searchWidget));
+ }
+ if (scanWidget)
+ scanWidget->PlaceBehind(eZPlacementBelow, searchWidget, false);
+ finished = false;
+ break;
+ }
+ scan = scan->mHigher;
+ }
+ } while (!finished);
+
+ mSortingZOrder = false;
+}
+
+NS_IMPL_ISUPPORTS(nsWindowMediator,
+ nsIWindowMediator_44,
+ nsIWindowMediator,
+ nsIObserver,
+ nsISupportsWeakReference)
+
+NS_IMETHODIMP
+nsWindowMediator::AddListener(nsIWindowMediatorListener* aListener)
+{
+ NS_ENSURE_ARG_POINTER(aListener);
+
+ mListeners.AppendObject(aListener);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::RemoveListener(nsIWindowMediatorListener* aListener)
+{
+ NS_ENSURE_ARG_POINTER(aListener);
+
+ mListeners.RemoveObject(aListener);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData)
+{
+ if (!strcmp(aTopic, "xpcom-shutdown") && mReady) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ while (mOldestWindow)
+ UnregisterWindow(mOldestWindow);
+ mReady = false;
+ }
+ return NS_OK;
+}
+
+bool
+notifyOpenWindow(nsIWindowMediatorListener *aListener, void* aData)
+{
+ WindowTitleData* winData = static_cast<WindowTitleData*>(aData);
+ aListener->OnOpenWindow(winData->mWindow);
+
+ return true;
+}
+
+bool
+notifyCloseWindow(nsIWindowMediatorListener *aListener, void* aData)
+{
+ WindowTitleData* winData = static_cast<WindowTitleData*>(aData);
+ aListener->OnCloseWindow(winData->mWindow);
+
+ return true;
+}
+
+bool
+notifyWindowTitleChange(nsIWindowMediatorListener *aListener, void* aData)
+{
+ WindowTitleData* titleData = reinterpret_cast<WindowTitleData*>(aData);
+ aListener->OnWindowTitleChange(titleData->mWindow, titleData->mTitle);
+
+ return true;
+}
diff --git a/xpfe/appshell/nsWindowMediator.h b/xpfe/appshell/nsWindowMediator.h
new file mode 100644
index 000000000..a0eaab56d
--- /dev/null
+++ b/xpfe/appshell/nsWindowMediator.h
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsWindowMediator_h_
+#define nsWindowMediator_h_
+
+#include "nsCOMPtr.h"
+#include "nsIWindowMediator.h"
+#include "nsIObserver.h"
+#include "nsTArray.h"
+#include "nsXPIDLString.h"
+#include "nsWeakReference.h"
+#include "nsCOMArray.h"
+
+class nsAppShellWindowEnumerator;
+class nsASXULWindowEarlyToLateEnumerator;
+class nsASDOMWindowEarlyToLateEnumerator;
+class nsASDOMWindowFrontToBackEnumerator;
+class nsASXULWindowFrontToBackEnumerator;
+class nsASDOMWindowBackToFrontEnumerator;
+class nsASXULWindowBackToFrontEnumerator;
+class nsIWindowMediatorListener;
+struct nsWindowInfo;
+
+class nsWindowMediator :
+ public nsIWindowMediator_44,
+ public nsIObserver,
+ public nsSupportsWeakReference
+{
+friend class nsAppShellWindowEnumerator;
+friend class nsASXULWindowEarlyToLateEnumerator;
+friend class nsASDOMWindowEarlyToLateEnumerator;
+friend class nsASDOMWindowFrontToBackEnumerator;
+friend class nsASXULWindowFrontToBackEnumerator;
+friend class nsASDOMWindowBackToFrontEnumerator;
+friend class nsASXULWindowBackToFrontEnumerator;
+
+protected:
+ virtual ~nsWindowMediator();
+
+public:
+ nsWindowMediator();
+
+ nsresult Init();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIWINDOWMEDIATOR
+ NS_DECL_NSIWINDOWMEDIATOR_44
+ NS_DECL_NSIOBSERVER
+
+ static nsresult GetDOMWindow(nsIXULWindow* inWindow,
+ nsCOMPtr<nsPIDOMWindowOuter>& outDOMWindow);
+
+private:
+ int32_t AddEnumerator(nsAppShellWindowEnumerator* inEnumerator);
+ int32_t RemoveEnumerator(nsAppShellWindowEnumerator* inEnumerator);
+ nsWindowInfo* MostRecentWindowInfo(const char16_t* inType,
+ bool aSkipPrivateBrowsingOrClosed = false);
+
+ nsresult UnregisterWindow(nsWindowInfo *inInfo);
+ nsWindowInfo *GetInfoFor(nsIXULWindow *aWindow);
+ nsWindowInfo *GetInfoFor(nsIWidget *aWindow);
+ void SortZOrderFrontToBack();
+ void SortZOrderBackToFront();
+
+ nsTArray<nsAppShellWindowEnumerator*> mEnumeratorList;
+ nsWindowInfo *mOldestWindow;
+ nsWindowInfo *mTopmostWindow;
+ int32_t mTimeStamp;
+ bool mSortingZOrder;
+ bool mReady;
+
+ nsCOMArray<nsIWindowMediatorListener> mListeners;
+};
+
+#endif
diff --git a/xpfe/appshell/nsXULWindow.cpp b/xpfe/appshell/nsXULWindow.cpp
new file mode 100644
index 000000000..8f2d9fde5
--- /dev/null
+++ b/xpfe/appshell/nsXULWindow.cpp
@@ -0,0 +1,2334 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 ci et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/MathAlgorithms.h"
+
+// Local includes
+#include "nsXULWindow.h"
+#include <algorithm>
+
+// Helper classes
+#include "nsPrintfCString.h"
+#include "nsString.h"
+#include "nsWidgetsCID.h"
+#include "nsThreadUtils.h"
+#include "nsNetCID.h"
+#include "nsQueryObject.h"
+#include "mozilla/Sprintf.h"
+
+//Interfaces needed to be included
+#include "nsIAppShell.h"
+#include "nsIAppShellService.h"
+#include "nsIServiceManager.h"
+#include "nsIContentViewer.h"
+#include "nsIDocument.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMXULDocument.h"
+#include "nsIDOMElement.h"
+#include "nsIDOMXULElement.h"
+#include "nsPIDOMWindow.h"
+#include "nsIDOMScreen.h"
+#include "nsIEmbeddingSiteWindow.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIIOService.h"
+#include "nsILoadContext.h"
+#include "nsIObserverService.h"
+#include "nsIWindowMediator.h"
+#include "nsIScreenManager.h"
+#include "nsIScreen.h"
+#include "nsIScrollable.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIWindowWatcher.h"
+#include "nsIURI.h"
+#include "nsIDOMCSSStyleDeclaration.h"
+#include "nsAppShellCID.h"
+#include "nsReadableUtils.h"
+#include "nsStyleConsts.h"
+#include "nsPresContext.h"
+#include "nsContentUtils.h"
+#include "nsWebShellWindow.h" // get rid of this one, too...
+#include "nsGlobalWindow.h"
+
+#include "prenv.h"
+#include "mozilla/AutoRestore.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/dom/BarProps.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/Event.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/TabParent.h"
+
+using namespace mozilla;
+using dom::AutoNoJSAPI;
+
+#define SIZEMODE_NORMAL NS_LITERAL_STRING("normal")
+#define SIZEMODE_MAXIMIZED NS_LITERAL_STRING("maximized")
+#define SIZEMODE_MINIMIZED NS_LITERAL_STRING("minimized")
+#define SIZEMODE_FULLSCREEN NS_LITERAL_STRING("fullscreen")
+
+#define WINDOWTYPE_ATTRIBUTE NS_LITERAL_STRING("windowtype")
+
+#define PERSIST_ATTRIBUTE NS_LITERAL_STRING("persist")
+#define SCREENX_ATTRIBUTE NS_LITERAL_STRING("screenX")
+#define SCREENY_ATTRIBUTE NS_LITERAL_STRING("screenY")
+#define WIDTH_ATTRIBUTE NS_LITERAL_STRING("width")
+#define HEIGHT_ATTRIBUTE NS_LITERAL_STRING("height")
+#define MODE_ATTRIBUTE NS_LITERAL_STRING("sizemode")
+#define ZLEVEL_ATTRIBUTE NS_LITERAL_STRING("zlevel")
+
+
+//*****************************************************************************
+//*** nsXULWindow: Object Management
+//*****************************************************************************
+
+nsXULWindow::nsXULWindow(uint32_t aChromeFlags)
+ : mChromeTreeOwner(nullptr),
+ mContentTreeOwner(nullptr),
+ mPrimaryContentTreeOwner(nullptr),
+ mModalStatus(NS_OK),
+ mContinueModalLoop(false),
+ mDebuting(false),
+ mChromeLoaded(false),
+ mShowAfterLoad(false),
+ mIntrinsicallySized(false),
+ mCenterAfterLoad(false),
+ mIsHiddenWindow(false),
+ mLockedUntilChromeLoad(false),
+ mIgnoreXULSize(false),
+ mIgnoreXULPosition(false),
+ mChromeFlagsFrozen(false),
+ mIgnoreXULSizeMode(false),
+ mDestroying(false),
+ mRegistered(false),
+ mContextFlags(0),
+ mPersistentAttributesDirty(0),
+ mPersistentAttributesMask(0),
+ mChromeFlags(aChromeFlags)
+{
+}
+
+nsXULWindow::~nsXULWindow()
+{
+ Destroy();
+}
+
+//*****************************************************************************
+// nsXULWindow::nsISupports
+//*****************************************************************************
+
+NS_IMPL_ADDREF(nsXULWindow)
+NS_IMPL_RELEASE(nsXULWindow)
+
+NS_INTERFACE_MAP_BEGIN(nsXULWindow)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULWindow)
+ NS_INTERFACE_MAP_ENTRY(nsIXULWindow)
+ NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
+ NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+ if (aIID.Equals(NS_GET_IID(nsXULWindow)))
+ foundInterface = reinterpret_cast<nsISupports*>(this);
+ else
+NS_INTERFACE_MAP_END
+
+//*****************************************************************************
+// nsXULWindow::nsIIntefaceRequestor
+//*****************************************************************************
+
+NS_IMETHODIMP nsXULWindow::GetInterface(const nsIID& aIID, void** aSink)
+{
+ nsresult rv;
+
+ NS_ENSURE_ARG_POINTER(aSink);
+
+ if (aIID.Equals(NS_GET_IID(nsIPrompt))) {
+ rv = EnsurePrompter();
+ if (NS_FAILED(rv)) return rv;
+ return mPrompter->QueryInterface(aIID, aSink);
+ }
+ if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
+ rv = EnsureAuthPrompter();
+ if (NS_FAILED(rv)) return rv;
+ return mAuthPrompter->QueryInterface(aIID, aSink);
+ }
+ if (aIID.Equals(NS_GET_IID(mozIDOMWindowProxy))) {
+ return GetWindowDOMWindow(reinterpret_cast<mozIDOMWindowProxy**>(aSink));
+ }
+ if (aIID.Equals(NS_GET_IID(nsIDOMWindow))) {
+ nsCOMPtr<mozIDOMWindowProxy> window = nullptr;
+ rv = GetWindowDOMWindow(getter_AddRefs(window));
+ nsCOMPtr<nsIDOMWindow> domWindow = do_QueryInterface(window);
+ domWindow.forget(aSink);
+ return rv;
+ }
+ if (aIID.Equals(NS_GET_IID(nsIDOMWindowInternal))) {
+ nsCOMPtr<mozIDOMWindowProxy> window = nullptr;
+ rv = GetWindowDOMWindow(getter_AddRefs(window));
+ nsCOMPtr<nsIDOMWindowInternal> domWindowInternal = do_QueryInterface(window);
+ domWindowInternal.forget(aSink);
+ return rv;
+ }
+ if (aIID.Equals(NS_GET_IID(nsIWebBrowserChrome)) &&
+ NS_SUCCEEDED(EnsureContentTreeOwner()) &&
+ NS_SUCCEEDED(mContentTreeOwner->QueryInterface(aIID, aSink)))
+ return NS_OK;
+
+ if (aIID.Equals(NS_GET_IID(nsIEmbeddingSiteWindow)) &&
+ NS_SUCCEEDED(EnsureContentTreeOwner()) &&
+ NS_SUCCEEDED(mContentTreeOwner->QueryInterface(aIID, aSink)))
+ return NS_OK;
+
+ return QueryInterface(aIID, aSink);
+}
+
+//*****************************************************************************
+// nsXULWindow::nsIXULWindow
+//*****************************************************************************
+
+NS_IMETHODIMP nsXULWindow::GetDocShell(nsIDocShell** aDocShell)
+{
+ NS_ENSURE_ARG_POINTER(aDocShell);
+
+ *aDocShell = mDocShell;
+ NS_IF_ADDREF(*aDocShell);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::GetZLevel(uint32_t *outLevel)
+{
+ nsCOMPtr<nsIWindowMediator> mediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
+ if (mediator)
+ mediator->GetZLevel(this, outLevel);
+ else
+ *outLevel = normalZ;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::SetZLevel(uint32_t aLevel)
+{
+ nsCOMPtr<nsIWindowMediator> mediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
+ if (!mediator)
+ return NS_ERROR_FAILURE;
+
+ uint32_t zLevel;
+ mediator->GetZLevel(this, &zLevel);
+ if (zLevel == aLevel)
+ return NS_OK;
+
+ /* refuse to raise a maximized window above the normal browser level,
+ for fear it could hide newly opened browser windows */
+ if (aLevel > nsIXULWindow::normalZ && mWindow) {
+ nsSizeMode sizeMode = mWindow->SizeMode();
+ if (sizeMode == nsSizeMode_Maximized || sizeMode == nsSizeMode_Fullscreen) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ // do it
+ mediator->SetZLevel(this, aLevel);
+ PersistentAttributesDirty(PAD_MISC);
+ SavePersistentAttributes();
+
+ nsCOMPtr<nsIContentViewer> cv;
+ mDocShell->GetContentViewer(getter_AddRefs(cv));
+ if (cv) {
+ nsCOMPtr<nsIDocument> doc = cv->GetDocument();
+ if (doc) {
+ ErrorResult rv;
+ RefPtr<dom::Event> event =
+ doc->CreateEvent(NS_LITERAL_STRING("Events"),rv);
+ if (event) {
+ event->InitEvent(NS_LITERAL_STRING("windowZLevel"), true, false);
+
+ event->SetTrusted(true);
+
+ bool defaultActionEnabled;
+ doc->DispatchEvent(event, &defaultActionEnabled);
+ }
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::GetContextFlags(uint32_t *aContextFlags)
+{
+ NS_ENSURE_ARG_POINTER(aContextFlags);
+ *aContextFlags = mContextFlags;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::SetContextFlags(uint32_t aContextFlags)
+{
+ mContextFlags = aContextFlags;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::GetChromeFlags(uint32_t *aChromeFlags)
+{
+ NS_ENSURE_ARG_POINTER(aChromeFlags);
+ *aChromeFlags = mChromeFlags;
+ /* mChromeFlags is kept up to date, except for scrollbar visibility.
+ That can be changed directly by the content DOM window, which
+ doesn't know to update the chrome window. So that we must check
+ separately. */
+
+ // however, it's pointless to ask if the window isn't set up yet
+ if (!mChromeLoaded)
+ return NS_OK;
+
+ if (GetContentScrollbarVisibility())
+ *aChromeFlags |= nsIWebBrowserChrome::CHROME_SCROLLBARS;
+ else
+ *aChromeFlags &= ~nsIWebBrowserChrome::CHROME_SCROLLBARS;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::SetChromeFlags(uint32_t aChromeFlags)
+{
+ NS_ASSERTION(!mChromeFlagsFrozen,
+ "SetChromeFlags() after AssumeChromeFlagsAreFrozen()!");
+
+ mChromeFlags = aChromeFlags;
+ if (mChromeLoaded)
+ NS_ENSURE_SUCCESS(ApplyChromeFlags(), NS_ERROR_FAILURE);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::AssumeChromeFlagsAreFrozen()
+{
+ mChromeFlagsFrozen = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::SetIntrinsicallySized(bool aIntrinsicallySized)
+{
+ mIntrinsicallySized = aIntrinsicallySized;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::GetIntrinsicallySized(bool* aIntrinsicallySized)
+{
+ NS_ENSURE_ARG_POINTER(aIntrinsicallySized);
+
+ *aIntrinsicallySized = mIntrinsicallySized;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::GetPrimaryContentShell(nsIDocShellTreeItem**
+ aDocShellTreeItem)
+{
+ NS_ENSURE_ARG_POINTER(aDocShellTreeItem);
+ NS_IF_ADDREF(*aDocShellTreeItem = mPrimaryContentShell);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULWindow::TabParentAdded(nsITabParent* aTab, bool aPrimary)
+{
+ if (aPrimary) {
+ mPrimaryTabParent = aTab;
+ mPrimaryContentShell = nullptr;
+ } else if (mPrimaryTabParent == aTab) {
+ mPrimaryTabParent = nullptr;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULWindow::TabParentRemoved(nsITabParent* aTab)
+{
+ if (aTab == mPrimaryTabParent) {
+ mPrimaryTabParent = nullptr;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULWindow::GetPrimaryTabParent(nsITabParent** aTab)
+{
+ nsCOMPtr<nsITabParent> tab = mPrimaryTabParent;
+ tab.forget(aTab);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::GetContentShellById(const char16_t* aID,
+ nsIDocShellTreeItem** aDocShellTreeItem)
+{
+ NS_ENSURE_ARG_POINTER(aDocShellTreeItem);
+ *aDocShellTreeItem = nullptr;
+
+ uint32_t count = mContentShells.Length();
+ for (uint32_t i = 0; i < count; i++) {
+ nsContentShellInfo* shellInfo = mContentShells.ElementAt(i);
+ if (shellInfo->id.Equals(aID)) {
+ *aDocShellTreeItem = nullptr;
+ if (shellInfo->child)
+ CallQueryReferent(shellInfo->child.get(), aDocShellTreeItem);
+ return NS_OK;
+ }
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsXULWindow::AddChildWindow(nsIXULWindow *aChild)
+{
+ // we're not really keeping track of this right now
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::RemoveChildWindow(nsIXULWindow *aChild)
+{
+ // we're not really keeping track of this right now
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::ShowModal()
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
+
+ // Store locally so it doesn't die on us
+ nsCOMPtr<nsIWidget> window = mWindow;
+ nsCOMPtr<nsIXULWindow> tempRef = this;
+
+ window->SetModal(true);
+ mContinueModalLoop = true;
+ EnableParent(false);
+
+ {
+ AutoNoJSAPI nojsapi;
+ nsIThread *thread = NS_GetCurrentThread();
+ while (mContinueModalLoop) {
+ if (!NS_ProcessNextEvent(thread))
+ break;
+ }
+ }
+
+ mContinueModalLoop = false;
+ window->SetModal(false);
+ /* Note there's no EnableParent(true) here to match the false one
+ above. That's done in ExitModalLoop. It's important that the parent
+ be re-enabled before this window is made invisible; to do otherwise
+ causes bizarre z-ordering problems. At this point, the window is
+ already invisible.
+ No known current implementation of Enable would have a problem with
+ re-enabling the parent twice, so we could do it again here without
+ breaking any current implementation. But that's unnecessary if the
+ modal loop is always exited using ExitModalLoop (the other way would be
+ to change the protected member variable directly.)
+ */
+
+ return mModalStatus;
+}
+
+//*****************************************************************************
+// nsXULWindow::nsIBaseWindow
+//*****************************************************************************
+
+NS_IMETHODIMP nsXULWindow::InitWindow(nativeWindow aParentNativeWindow,
+ nsIWidget* parentWidget, int32_t x, int32_t y, int32_t cx, int32_t cy)
+{
+ //XXX First Check In
+ NS_ASSERTION(false, "Not Yet Implemented");
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::Create()
+{
+ //XXX First Check In
+ NS_ASSERTION(false, "Not Yet Implemented");
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::Destroy()
+{
+ if (!mWindow)
+ return NS_OK;
+
+ // Ensure we don't reenter this code
+ if (mDestroying)
+ return NS_OK;
+
+ mozilla::AutoRestore<bool> guard(mDestroying);
+ mDestroying = true;
+
+ nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
+ NS_ASSERTION(appShell, "Couldn't get appShell... xpcom shutdown?");
+ if (appShell)
+ appShell->UnregisterTopLevelWindow(static_cast<nsIXULWindow*>(this));
+
+ nsCOMPtr<nsIXULWindow> parentWindow(do_QueryReferent(mParentWindow));
+ if (parentWindow)
+ parentWindow->RemoveChildWindow(this);
+
+ // let's make sure the window doesn't get deleted out from under us
+ // while we are trying to close....this can happen if the docshell
+ // we close ends up being the last owning reference to this xulwindow
+
+ // XXXTAB This shouldn't be an issue anymore because the ownership model
+ // only goes in one direction. When webshell container is fully removed
+ // try removing this...
+
+ nsCOMPtr<nsIXULWindow> placeHolder = this;
+
+ // Remove modality (if any) and hide while destroying. More than
+ // a convenience, the hide prevents user interaction with the partially
+ // destroyed window. This is especially necessary when the eldest window
+ // in a stack of modal windows is destroyed first. It happens.
+ ExitModalLoop(NS_OK);
+ // XXX: Skip unmapping the window on Linux due to GLX hangs on the compositor
+ // thread with NVIDIA driver 310.32. We don't need to worry about user
+ // interactions with destroyed windows on X11 either.
+#ifndef MOZ_WIDGET_GTK
+ if (mWindow)
+ mWindow->Show(false);
+#endif
+
+#if defined(XP_WIN)
+ // We need to explicitly set the focus on Windows, but
+ // only if the parent is visible.
+ nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow));
+ if (parent) {
+ nsCOMPtr<nsIWidget> parentWidget;
+ parent->GetMainWidget(getter_AddRefs(parentWidget));
+ if (!parentWidget || parentWidget->IsVisible()) {
+ nsCOMPtr<nsIBaseWindow> baseHiddenWindow;
+ if (appShell) {
+ nsCOMPtr<nsIXULWindow> hiddenWindow;
+ appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow));
+ if (hiddenWindow)
+ baseHiddenWindow = do_GetInterface(hiddenWindow);
+ }
+ // somebody screwed up somewhere. hiddenwindow shouldn't be anybody's
+ // parent. still, when it happens, skip activating it.
+ if (baseHiddenWindow != parent) {
+ nsCOMPtr<nsIWidget> parentWidget;
+ parent->GetMainWidget(getter_AddRefs(parentWidget));
+ if (parentWidget)
+ parentWidget->PlaceBehind(eZPlacementTop, 0, true);
+ }
+ }
+ }
+#endif
+
+ mDOMWindow = nullptr;
+ if (mDocShell) {
+ nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(mDocShell));
+ shellAsWin->Destroy();
+ mDocShell = nullptr; // this can cause reentrancy of this function
+ }
+
+ // Remove our ref on the content shells
+ uint32_t count = mContentShells.Length();
+ for (uint32_t i = 0; i < count; i++) {
+ nsContentShellInfo* shellInfo = mContentShells.ElementAt(i);
+ delete shellInfo;
+ }
+ mContentShells.Clear();
+ mPrimaryContentShell = nullptr;
+
+ if (mContentTreeOwner) {
+ mContentTreeOwner->XULWindow(nullptr);
+ NS_RELEASE(mContentTreeOwner);
+ }
+ if (mPrimaryContentTreeOwner) {
+ mPrimaryContentTreeOwner->XULWindow(nullptr);
+ NS_RELEASE(mPrimaryContentTreeOwner);
+ }
+ if (mChromeTreeOwner) {
+ mChromeTreeOwner->XULWindow(nullptr);
+ NS_RELEASE(mChromeTreeOwner);
+ }
+ if (mWindow) {
+ mWindow->SetWidgetListener(nullptr); // nsWebShellWindow hackery
+ mWindow->Destroy();
+ mWindow = nullptr;
+ }
+
+ if (!mIsHiddenWindow && mRegistered) {
+ /* Inform appstartup we've destroyed this window and it could
+ quit now if it wanted. This must happen at least after mDocShell
+ is destroyed, because onunload handlers fire then, and those being
+ script, anything could happen. A new window could open, even.
+ See bug 130719. */
+ nsCOMPtr<nsIObserverService> obssvc = services::GetObserverService();
+ NS_ASSERTION(obssvc, "Couldn't get observer service?");
+
+ if (obssvc)
+ obssvc->NotifyObservers(nullptr, "xul-window-destroyed", nullptr);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::GetDevicePixelsPerDesktopPixel(double *aScale)
+{
+ *aScale = mWindow ? mWindow->GetDesktopToDeviceScale().scale : 1.0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::GetUnscaledDevicePixelsPerCSSPixel(double *aScale)
+{
+ *aScale = mWindow ? mWindow->GetDefaultScale().scale : 1.0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::SetPositionDesktopPix(int32_t aX, int32_t aY)
+{
+ nsresult rv = mWindow->Move(aX, aY);
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+ if (!mChromeLoaded) {
+ // If we're called before the chrome is loaded someone obviously wants this
+ // window at this position. We don't persist this one-time position.
+ mIgnoreXULPosition = true;
+ return NS_OK;
+ }
+ PersistentAttributesDirty(PAD_POSITION);
+ SavePersistentAttributes();
+ return NS_OK;
+}
+
+// The parameters here are device pixels; do the best we can to convert to
+// desktop px, using the window's current scale factor (if available).
+NS_IMETHODIMP nsXULWindow::SetPosition(int32_t aX, int32_t aY)
+{
+ // Don't reset the window's size mode here - platforms that don't want to move
+ // maximized windows should reset it in their respective Move implementation.
+ DesktopToLayoutDeviceScale currScale = mWindow->GetDesktopToDeviceScale();
+ DesktopPoint pos = LayoutDeviceIntPoint(aX, aY) / currScale;
+ return SetPositionDesktopPix(pos.x, pos.y);
+}
+
+NS_IMETHODIMP nsXULWindow::GetPosition(int32_t* aX, int32_t* aY)
+{
+ return GetPositionAndSize(aX, aY, nullptr, nullptr);
+}
+
+NS_IMETHODIMP nsXULWindow::SetSize(int32_t aCX, int32_t aCY, bool aRepaint)
+{
+ /* any attempt to set the window's size or position overrides the window's
+ zoom state. this is important when these two states are competing while
+ the window is being opened. but it should probably just always be so. */
+ mWindow->SetSizeMode(nsSizeMode_Normal);
+
+ mIntrinsicallySized = false;
+
+ DesktopToLayoutDeviceScale scale = mWindow->GetDesktopToDeviceScale();
+ DesktopSize size = LayoutDeviceIntSize(aCX, aCY) / scale;
+ nsresult rv = mWindow->Resize(size.width, size.height, aRepaint);
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+ if (!mChromeLoaded) {
+ // If we're called before the chrome is loaded someone obviously wants this
+ // window at this size & in the normal size mode (since it is the only mode
+ // in which setting dimensions makes sense). We don't persist this one-time
+ // size.
+ mIgnoreXULSize = true;
+ mIgnoreXULSizeMode = true;
+ return NS_OK;
+ }
+ PersistentAttributesDirty(PAD_SIZE);
+ SavePersistentAttributes();
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::GetSize(int32_t* aCX, int32_t* aCY)
+{
+ return GetPositionAndSize(nullptr, nullptr, aCX, aCY);
+}
+
+NS_IMETHODIMP nsXULWindow::SetPositionAndSize(int32_t aX, int32_t aY,
+ int32_t aCX, int32_t aCY, uint32_t aFlags)
+{
+ /* any attempt to set the window's size or position overrides the window's
+ zoom state. this is important when these two states are competing while
+ the window is being opened. but it should probably just always be so. */
+ mWindow->SetSizeMode(nsSizeMode_Normal);
+
+ mIntrinsicallySized = false;
+
+ DesktopToLayoutDeviceScale scale = mWindow->GetDesktopToDeviceScale();
+ DesktopRect rect = LayoutDeviceIntRect(aX, aY, aCX, aCY) / scale;
+ nsresult rv = mWindow->Resize(rect.x, rect.y, rect.width, rect.height,
+ !!(aFlags & nsIBaseWindow::eRepaint));
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+ if (!mChromeLoaded) {
+ // If we're called before the chrome is loaded someone obviously wants this
+ // window at this size and position. We don't persist this one-time setting.
+ mIgnoreXULPosition = true;
+ mIgnoreXULSize = true;
+ mIgnoreXULSizeMode = true;
+ return NS_OK;
+ }
+ PersistentAttributesDirty(PAD_POSITION | PAD_SIZE);
+ SavePersistentAttributes();
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::GetPositionAndSize(int32_t* x, int32_t* y, int32_t* cx,
+ int32_t* cy)
+{
+
+ if (!mWindow)
+ return NS_ERROR_FAILURE;
+
+ LayoutDeviceIntRect rect = mWindow->GetScreenBounds();
+
+ if (x)
+ *x = rect.x;
+ if (y)
+ *y = rect.y;
+ if (cx)
+ *cx = rect.width;
+ if (cy)
+ *cy = rect.height;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::Center(nsIXULWindow *aRelative, bool aScreen, bool aAlert)
+{
+ int32_t left, top, width, height,
+ ourWidth, ourHeight;
+ bool screenCoordinates = false,
+ windowCoordinates = false;
+ nsresult result;
+
+ if (!mChromeLoaded) {
+ // note we lose the parameters. at time of writing, this isn't a problem.
+ mCenterAfterLoad = true;
+ return NS_OK;
+ }
+
+ if (!aScreen && !aRelative)
+ return NS_ERROR_INVALID_ARG;
+
+ nsCOMPtr<nsIScreenManager> screenmgr = do_GetService("@mozilla.org/gfx/screenmanager;1", &result);
+ if (NS_FAILED(result))
+ return result;
+
+ nsCOMPtr<nsIScreen> screen;
+
+ if (aRelative) {
+ nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(aRelative, &result));
+ if (base) {
+ // get window rect
+ result = base->GetPositionAndSize(&left, &top, &width, &height);
+ if (NS_SUCCEEDED(result)) {
+ double scale;
+ if (NS_SUCCEEDED(base->GetDevicePixelsPerDesktopPixel(&scale))) {
+ left = NSToIntRound(left / scale);
+ top = NSToIntRound(top / scale);
+ width = NSToIntRound(width / scale);
+ height = NSToIntRound(height / scale);
+ }
+ // if centering on screen, convert that to the corresponding screen
+ if (aScreen)
+ screenmgr->ScreenForRect(left, top, width, height, getter_AddRefs(screen));
+ else
+ windowCoordinates = true;
+ } else {
+ // something's wrong with the reference window.
+ // fall back to the primary screen
+ aRelative = 0;
+ aScreen = true;
+ }
+ }
+ }
+ if (!aRelative) {
+ if (!mOpenerScreenRect.IsEmpty()) {
+ // FIXME - check if these are device or display pixels
+ screenmgr->ScreenForRect(mOpenerScreenRect.x, mOpenerScreenRect.y,
+ mOpenerScreenRect.width, mOpenerScreenRect.height,
+ getter_AddRefs(screen));
+ } else {
+ screenmgr->GetPrimaryScreen(getter_AddRefs(screen));
+ }
+ }
+
+ if (aScreen && screen) {
+ screen->GetAvailRectDisplayPix(&left, &top, &width, &height);
+ screenCoordinates = true;
+ }
+
+ if (screenCoordinates || windowCoordinates) {
+ NS_ASSERTION(mWindow, "what, no window?");
+ double scale = mWindow->GetDesktopToDeviceScale().scale;
+ GetSize(&ourWidth, &ourHeight);
+ int32_t scaledWidth, scaledHeight;
+ scaledWidth = NSToIntRound(ourWidth / scale);
+ scaledHeight = NSToIntRound(ourHeight / scale);
+ left += (width - scaledWidth) / 2;
+ top += (height - scaledHeight) / (aAlert ? 3 : 2);
+ if (windowCoordinates) {
+ mWindow->ConstrainPosition(false, &left, &top);
+ }
+ SetPosition(left * scale, top * scale);
+
+ // If moving the window caused it to change size,
+ // re-do the centering.
+ int32_t newWidth, newHeight;
+ GetSize(&newWidth, &newHeight);
+ if (newWidth != ourWidth || newHeight != ourHeight) {
+ return Center(aRelative, aScreen, aAlert);
+ }
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsXULWindow::Repaint(bool aForce)
+{
+ //XXX First Check In
+ NS_ASSERTION(false, "Not Yet Implemented");
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::GetParentWidget(nsIWidget** aParentWidget)
+{
+ NS_ENSURE_ARG_POINTER(aParentWidget);
+ NS_ENSURE_STATE(mWindow);
+
+ NS_IF_ADDREF(*aParentWidget = mWindow->GetParent());
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::SetParentWidget(nsIWidget* aParentWidget)
+{
+ //XXX First Check In
+ NS_ASSERTION(false, "Not Yet Implemented");
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::GetParentNativeWindow(nativeWindow* aParentNativeWindow)
+{
+ NS_ENSURE_ARG_POINTER(aParentNativeWindow);
+
+ nsCOMPtr<nsIWidget> parentWidget;
+ NS_ENSURE_SUCCESS(GetParentWidget(getter_AddRefs(parentWidget)), NS_ERROR_FAILURE);
+
+ if (parentWidget) {
+ *aParentNativeWindow = parentWidget->GetNativeData(NS_NATIVE_WIDGET);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::SetParentNativeWindow(nativeWindow aParentNativeWindow)
+{
+ //XXX First Check In
+ NS_ASSERTION(false, "Not Yet Implemented");
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::GetNativeHandle(nsAString& aNativeHandle)
+{
+ nsCOMPtr<nsIWidget> mainWidget;
+ NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(mainWidget)), NS_ERROR_FAILURE);
+
+ if (mainWidget) {
+ nativeWindow nativeWindowPtr = mainWidget->GetNativeData(NS_NATIVE_WINDOW);
+ /* the nativeWindow pointer is converted to and exposed as a string. This
+ is a more reliable way not to lose information (as opposed to JS
+ |Number| for instance) */
+ aNativeHandle = NS_ConvertASCIItoUTF16(nsPrintfCString("0x%p", nativeWindowPtr));
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::GetVisibility(bool* aVisibility)
+{
+ NS_ENSURE_ARG_POINTER(aVisibility);
+
+ // Always claim to be visible for now. See bug
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=306245.
+
+ *aVisibility = true;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::SetVisibility(bool aVisibility)
+{
+ if (!mChromeLoaded) {
+ mShowAfterLoad = aVisibility;
+ return NS_OK;
+ }
+
+ if (mDebuting) {
+ return NS_OK;
+ }
+ mDebuting = true; // (Show / Focus is recursive)
+
+ //XXXTAB Do we really need to show docshell and the window? Isn't
+ // the window good enough?
+ nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(mDocShell));
+ shellAsWin->SetVisibility(aVisibility);
+ // Store locally so it doesn't die on us. 'Show' can result in the window
+ // being closed with nsXULWindow::Destroy being called. That would set
+ // mWindow to null and posibly destroy the nsIWidget while its Show method
+ // is on the stack. We need to keep it alive until Show finishes.
+ nsCOMPtr<nsIWidget> window = mWindow;
+ window->Show(aVisibility);
+
+ nsCOMPtr<nsIWindowMediator> windowMediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
+ if (windowMediator)
+ windowMediator->UpdateWindowTimeStamp(static_cast<nsIXULWindow*>(this));
+
+ // notify observers so that we can hide the splash screen if possible
+ nsCOMPtr<nsIObserverService> obssvc = services::GetObserverService();
+ NS_ASSERTION(obssvc, "Couldn't get observer service.");
+ if (obssvc) {
+ obssvc->NotifyObservers(nullptr, "xul-window-visible", nullptr);
+ }
+
+ mDebuting = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::GetEnabled(bool *aEnabled)
+{
+ NS_ENSURE_ARG_POINTER(aEnabled);
+
+ if (mWindow) {
+ *aEnabled = mWindow->IsEnabled();
+ return NS_OK;
+ }
+
+ *aEnabled = true; // better guess than most
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsXULWindow::SetEnabled(bool aEnable)
+{
+ if (mWindow) {
+ mWindow->Enable(aEnable);
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsXULWindow::GetMainWidget(nsIWidget** aMainWidget)
+{
+ NS_ENSURE_ARG_POINTER(aMainWidget);
+
+ *aMainWidget = mWindow;
+ NS_IF_ADDREF(*aMainWidget);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::SetFocus()
+{
+ //XXX First Check In
+ NS_ASSERTION(false, "Not Yet Implemented");
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::GetTitle(char16_t** aTitle)
+{
+ NS_ENSURE_ARG_POINTER(aTitle);
+
+ *aTitle = ToNewUnicode(mTitle);
+ if (!*aTitle)
+ return NS_ERROR_OUT_OF_MEMORY;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::SetTitle(const char16_t* aTitle)
+{
+ NS_ENSURE_STATE(mWindow);
+ mTitle.Assign(aTitle);
+ mTitle.StripChars("\n\r");
+ NS_ENSURE_SUCCESS(mWindow->SetTitle(mTitle), NS_ERROR_FAILURE);
+
+ // Tell the window mediator that a title has changed
+ nsCOMPtr<nsIWindowMediator> windowMediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
+ if (!windowMediator)
+ return NS_OK;
+
+ windowMediator->UpdateWindowTitle(static_cast<nsIXULWindow*>(this), aTitle);
+
+ return NS_OK;
+}
+
+
+//*****************************************************************************
+// nsXULWindow: Helpers
+//*****************************************************************************
+
+NS_IMETHODIMP nsXULWindow::EnsureChromeTreeOwner()
+{
+ if (mChromeTreeOwner)
+ return NS_OK;
+
+ mChromeTreeOwner = new nsChromeTreeOwner();
+ NS_ADDREF(mChromeTreeOwner);
+ mChromeTreeOwner->XULWindow(this);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::EnsureContentTreeOwner()
+{
+ if (mContentTreeOwner)
+ return NS_OK;
+
+ mContentTreeOwner = new nsContentTreeOwner(false);
+ NS_ADDREF(mContentTreeOwner);
+ mContentTreeOwner->XULWindow(this);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::EnsurePrimaryContentTreeOwner()
+{
+ if (mPrimaryContentTreeOwner)
+ return NS_OK;
+
+ mPrimaryContentTreeOwner = new nsContentTreeOwner(true);
+ NS_ADDREF(mPrimaryContentTreeOwner);
+ mPrimaryContentTreeOwner->XULWindow(this);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::EnsurePrompter()
+{
+ if (mPrompter)
+ return NS_OK;
+
+ nsCOMPtr<mozIDOMWindowProxy> ourWindow;
+ nsresult rv = GetWindowDOMWindow(getter_AddRefs(ourWindow));
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIWindowWatcher> wwatch =
+ do_GetService(NS_WINDOWWATCHER_CONTRACTID);
+ if (wwatch)
+ wwatch->GetNewPrompter(ourWindow, getter_AddRefs(mPrompter));
+ }
+ return mPrompter ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsXULWindow::EnsureAuthPrompter()
+{
+ if (mAuthPrompter)
+ return NS_OK;
+
+ nsCOMPtr<mozIDOMWindowProxy> ourWindow;
+ nsresult rv = GetWindowDOMWindow(getter_AddRefs(ourWindow));
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
+ if (wwatch)
+ wwatch->GetNewAuthPrompter(ourWindow, getter_AddRefs(mAuthPrompter));
+ }
+ return mAuthPrompter ? NS_OK : NS_ERROR_FAILURE;
+}
+
+void nsXULWindow::OnChromeLoaded()
+{
+ nsresult rv = EnsureContentTreeOwner();
+
+ if (NS_SUCCEEDED(rv)) {
+ mChromeLoaded = true;
+ ApplyChromeFlags();
+ SyncAttributesToWidget();
+
+ int32_t specWidth = -1, specHeight = -1;
+ bool gotSize = false;
+
+ if (!mIgnoreXULSize) {
+ gotSize = LoadSizeFromXUL(specWidth, specHeight);
+ }
+
+ bool positionSet = !mIgnoreXULPosition;
+ nsCOMPtr<nsIXULWindow> parentWindow(do_QueryReferent(mParentWindow));
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+ // don't override WM placement on unix for independent, top-level windows
+ // (however, we think the benefits of intelligent dependent window placement
+ // trump that override.)
+ if (!parentWindow)
+ positionSet = false;
+#endif
+ if (positionSet) {
+ // We have to do this before sizing the window, because sizing depends
+ // on the resolution of the screen we're on. But positioning needs to
+ // know the size so that it can constrain to screen bounds.... as an
+ // initial guess here, we'll use the specified size (if any).
+ positionSet = LoadPositionFromXUL(specWidth, specHeight);
+ }
+
+ if (gotSize) {
+ SetSpecifiedSize(specWidth, specHeight);
+ }
+
+ if (mIntrinsicallySized) {
+ // (if LoadSizeFromXUL set the size, mIntrinsicallySized will be false)
+ nsCOMPtr<nsIContentViewer> cv;
+ mDocShell->GetContentViewer(getter_AddRefs(cv));
+ if (cv) {
+ nsCOMPtr<nsIDocShellTreeItem> docShellAsItem = do_QueryInterface(mDocShell);
+ nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
+ docShellAsItem->GetTreeOwner(getter_AddRefs(treeOwner));
+ if (treeOwner) {
+ // GetContentSize can fail, so initialise |width| and |height| to be
+ // on the safe side.
+ int32_t width = 0, height = 0;
+ if (NS_SUCCEEDED(cv->GetContentSize(&width, &height))) {
+ treeOwner->SizeShellTo(docShellAsItem, width, height);
+ // Update specified size for the final LoadPositionFromXUL call.
+ specWidth = width;
+ specHeight = height;
+ }
+ }
+ }
+ }
+
+ // Now that we have set the window's final size, we can re-do its
+ // positioning so that it is properly constrained to the screen.
+ if (positionSet) {
+ LoadPositionFromXUL(specWidth, specHeight);
+ }
+
+ LoadMiscPersistentAttributesFromXUL();
+
+ if (mCenterAfterLoad && !positionSet) {
+ Center(parentWindow, parentWindow ? false : true, false);
+ }
+
+ if (mShowAfterLoad) {
+ SetVisibility(true);
+ // At this point the window may have been closed during Show(), so
+ // nsXULWindow::Destroy may already have been called. Take care!
+ }
+ }
+ mPersistentAttributesMask |= PAD_POSITION | PAD_SIZE | PAD_MISC;
+}
+
+// If aSpecWidth and/or aSpecHeight are > 0, we will use these CSS px sizes
+// to fit to the screen when staggering windows; if they're negative,
+// we use the window's current size instead.
+bool nsXULWindow::LoadPositionFromXUL(int32_t aSpecWidth, int32_t aSpecHeight)
+{
+ bool gotPosition = false;
+
+ // if we're the hidden window, don't try to validate our size/position. We're
+ // special.
+ if (mIsHiddenWindow)
+ return false;
+
+ nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
+ NS_ENSURE_TRUE(windowElement, false);
+
+ int32_t currX = 0;
+ int32_t currY = 0;
+ int32_t currWidth = 0;
+ int32_t currHeight = 0;
+ nsresult errorCode;
+ int32_t temp;
+
+ GetPositionAndSize(&currX, &currY, &currWidth, &currHeight);
+
+ // Convert to global display pixels for consistent window management across
+ // screens with diverse resolutions
+ double devToDesktopScale = 1.0 / mWindow->GetDesktopToDeviceScale().scale;
+ currX = NSToIntRound(currX * devToDesktopScale);
+ currY = NSToIntRound(currY * devToDesktopScale);
+
+ // For size, use specified value if > 0, else current value
+ double devToCSSScale = 1.0 / mWindow->GetDefaultScale().scale;
+ int32_t cssWidth =
+ aSpecWidth > 0 ? aSpecWidth : NSToIntRound(currWidth * devToCSSScale);
+ int32_t cssHeight =
+ aSpecHeight > 0 ? aSpecHeight : NSToIntRound(currHeight * devToCSSScale);
+
+ // Obtain the position information from the <xul:window> element.
+ int32_t specX = currX;
+ int32_t specY = currY;
+ nsAutoString posString;
+
+ windowElement->GetAttribute(SCREENX_ATTRIBUTE, posString);
+ temp = posString.ToInteger(&errorCode);
+ if (NS_SUCCEEDED(errorCode)) {
+ specX = temp;
+ gotPosition = true;
+ }
+ windowElement->GetAttribute(SCREENY_ATTRIBUTE, posString);
+ temp = posString.ToInteger(&errorCode);
+ if (NS_SUCCEEDED(errorCode)) {
+ specY = temp;
+ gotPosition = true;
+ }
+
+ if (gotPosition) {
+ // our position will be relative to our parent, if any
+ nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow));
+ if (parent) {
+ int32_t parentX, parentY;
+ if (NS_SUCCEEDED(parent->GetPosition(&parentX, &parentY))) {
+ double scale;
+ if (NS_SUCCEEDED(parent->GetDevicePixelsPerDesktopPixel(&scale))) {
+ parentX = NSToIntRound(parentX / scale);
+ parentY = NSToIntRound(parentY / scale);
+ }
+ specX += parentX;
+ specY += parentY;
+ }
+ }
+ else {
+ StaggerPosition(specX, specY, cssWidth, cssHeight);
+ }
+ }
+ mWindow->ConstrainPosition(false, &specX, &specY);
+ if (specX != currX || specY != currY) {
+ SetPositionDesktopPix(specX, specY);
+ }
+
+ return gotPosition;
+}
+
+bool
+nsXULWindow::LoadSizeFromXUL(int32_t& aSpecWidth, int32_t& aSpecHeight)
+{
+ bool gotSize = false;
+
+ // if we're the hidden window, don't try to validate our size/position. We're
+ // special.
+ if (mIsHiddenWindow) {
+ return false;
+ }
+
+ nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
+ NS_ENSURE_TRUE(windowElement, false);
+
+ nsresult errorCode;
+ int32_t temp;
+
+ // Obtain the sizing information from the <xul:window> element.
+ aSpecWidth = 100;
+ aSpecHeight = 100;
+ nsAutoString sizeString;
+
+ windowElement->GetAttribute(WIDTH_ATTRIBUTE, sizeString);
+ temp = sizeString.ToInteger(&errorCode);
+ if (NS_SUCCEEDED(errorCode) && temp > 0) {
+ aSpecWidth = std::max(temp, 100);
+ gotSize = true;
+ }
+ windowElement->GetAttribute(HEIGHT_ATTRIBUTE, sizeString);
+ temp = sizeString.ToInteger(&errorCode);
+ if (NS_SUCCEEDED(errorCode) && temp > 0) {
+ aSpecHeight = std::max(temp, 100);
+ gotSize = true;
+ }
+
+ return gotSize;
+}
+
+void
+nsXULWindow::SetSpecifiedSize(int32_t aSpecWidth, int32_t aSpecHeight)
+{
+ // constrain to screen size
+ nsCOMPtr<mozIDOMWindowProxy> domWindow;
+ GetWindowDOMWindow(getter_AddRefs(domWindow));
+ if (domWindow) {
+ auto* window = nsPIDOMWindowOuter::From(domWindow);
+ nsCOMPtr<nsIDOMScreen> screen = window->GetScreen();
+ if (screen) {
+ int32_t screenWidth;
+ int32_t screenHeight;
+ screen->GetAvailWidth(&screenWidth); // CSS pixels
+ screen->GetAvailHeight(&screenHeight);
+ if (aSpecWidth > screenWidth) {
+ aSpecWidth = screenWidth;
+ }
+ if (aSpecHeight > screenHeight) {
+ aSpecHeight = screenHeight;
+ }
+ }
+ }
+
+ NS_ASSERTION(mWindow, "we expected to have a window already");
+
+ int32_t currWidth = 0;
+ int32_t currHeight = 0;
+ GetSize(&currWidth, &currHeight); // returns device pixels
+
+ // convert specified values to device pixels, and resize if needed
+ double cssToDevPx = mWindow ? mWindow->GetDefaultScale().scale : 1.0;
+ aSpecWidth = NSToIntRound(aSpecWidth * cssToDevPx);
+ aSpecHeight = NSToIntRound(aSpecHeight * cssToDevPx);
+ mIntrinsicallySized = false;
+ if (aSpecWidth != currWidth || aSpecHeight != currHeight) {
+ SetSize(aSpecWidth, aSpecHeight, false);
+ }
+}
+
+/* Miscellaneous persistent attributes are attributes named in the
+ |persist| attribute, other than size and position. Those are special
+ because it's important to load those before one of the misc
+ attributes (sizemode) and they require extra processing. */
+bool nsXULWindow::LoadMiscPersistentAttributesFromXUL()
+{
+ bool gotState = false;
+
+ /* There are no misc attributes of interest to the hidden window.
+ It's especially important not to try to validate that window's
+ size or position, because some platforms (Mac OS X) need to
+ make it visible and offscreen. */
+ if (mIsHiddenWindow)
+ return false;
+
+ nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
+ NS_ENSURE_TRUE(windowElement, false);
+
+ nsAutoString stateString;
+
+ // sizemode
+ windowElement->GetAttribute(MODE_ATTRIBUTE, stateString);
+ nsSizeMode sizeMode = nsSizeMode_Normal;
+ /* ignore request to minimize, to not confuse novices
+ if (stateString.Equals(SIZEMODE_MINIMIZED))
+ sizeMode = nsSizeMode_Minimized;
+ */
+ if (!mIgnoreXULSizeMode &&
+ (stateString.Equals(SIZEMODE_MAXIMIZED) || stateString.Equals(SIZEMODE_FULLSCREEN))) {
+ /* Honor request to maximize only if the window is sizable.
+ An unsizable, unmaximizable, yet maximized window confuses
+ Windows OS and is something of a travesty, anyway. */
+ if (mChromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE) {
+ mIntrinsicallySized = false;
+
+ if (stateString.Equals(SIZEMODE_MAXIMIZED))
+ sizeMode = nsSizeMode_Maximized;
+ else
+ sizeMode = nsSizeMode_Fullscreen;
+ }
+ }
+
+ // If we are told to ignore the size mode attribute update the
+ // document so the attribute and window are in sync.
+ if (mIgnoreXULSizeMode) {
+ nsAutoString sizeString;
+ if (sizeMode == nsSizeMode_Maximized)
+ sizeString.Assign(SIZEMODE_MAXIMIZED);
+ else if (sizeMode == nsSizeMode_Fullscreen)
+ sizeString.Assign(SIZEMODE_FULLSCREEN);
+ else if (sizeMode == nsSizeMode_Normal)
+ sizeString.Assign(SIZEMODE_NORMAL);
+ if (!sizeString.IsEmpty()) {
+ ErrorResult rv;
+ windowElement->SetAttribute(MODE_ATTRIBUTE, sizeString, rv);
+ }
+ }
+
+ if (sizeMode == nsSizeMode_Fullscreen) {
+ nsCOMPtr<mozIDOMWindowProxy> ourWindow;
+ GetWindowDOMWindow(getter_AddRefs(ourWindow));
+ auto* piWindow = nsPIDOMWindowOuter::From(ourWindow);
+ piWindow->SetFullScreen(true);
+ } else {
+ mWindow->SetSizeMode(sizeMode);
+ }
+ gotState = true;
+
+ // zlevel
+ windowElement->GetAttribute(ZLEVEL_ATTRIBUTE, stateString);
+ if (!stateString.IsEmpty()) {
+ nsresult errorCode;
+ int32_t zLevel = stateString.ToInteger(&errorCode);
+ if (NS_SUCCEEDED(errorCode) && zLevel >= lowestZ && zLevel <= highestZ)
+ SetZLevel(zLevel);
+ }
+
+ return gotState;
+}
+
+/* Stagger windows of the same type so they don't appear on top of each other.
+ This code does have a scary double loop -- it'll keep passing through
+ the entire list of open windows until it finds a non-collision. Doesn't
+ seem to be a problem, but it deserves watching.
+ The aRequested{X,Y} parameters here are in desktop pixels;
+ the aSpec{Width,Height} parameters are CSS pixel dimensions.
+*/
+void nsXULWindow::StaggerPosition(int32_t &aRequestedX, int32_t &aRequestedY,
+ int32_t aSpecWidth, int32_t aSpecHeight)
+{
+ // These "constants" will be converted from CSS to desktop pixels
+ // for the appropriate screen, assuming we find a screen to use...
+ // hence they're not actually declared const here.
+ int32_t kOffset = 22;
+ uint32_t kSlop = 4;
+
+ bool keepTrying;
+ int bouncedX = 0, // bounced off vertical edge of screen
+ bouncedY = 0; // bounced off horizontal edge
+
+ // look for any other windows of this type
+ nsCOMPtr<nsIWindowMediator> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
+ if (!wm)
+ return;
+
+ nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
+ if (!windowElement)
+ return;
+
+ nsCOMPtr<nsIXULWindow> ourXULWindow(this);
+
+ nsAutoString windowType;
+ windowElement->GetAttribute(WINDOWTYPE_ATTRIBUTE, windowType);
+
+ int32_t screenTop = 0, // it's pointless to initialize these ...
+ screenRight = 0, // ... but to prevent oversalubrious and ...
+ screenBottom = 0, // ... underbright compilers from ...
+ screenLeft = 0; // ... issuing warnings.
+ bool gotScreen = false;
+
+ { // fetch screen coordinates
+ nsCOMPtr<nsIScreenManager> screenMgr(do_GetService(
+ "@mozilla.org/gfx/screenmanager;1"));
+ if (screenMgr) {
+ nsCOMPtr<nsIScreen> ourScreen;
+ // the coordinates here are already display pixels
+ screenMgr->ScreenForRect(aRequestedX, aRequestedY,
+ aSpecWidth, aSpecHeight,
+ getter_AddRefs(ourScreen));
+ if (ourScreen) {
+ int32_t screenWidth, screenHeight;
+ ourScreen->GetAvailRectDisplayPix(&screenLeft, &screenTop,
+ &screenWidth, &screenHeight);
+ screenBottom = screenTop + screenHeight;
+ screenRight = screenLeft + screenWidth;
+ // Get the screen's scaling factors and convert staggering constants
+ // from CSS px to desktop pixel units
+ double desktopToDeviceScale = 1.0, cssToDeviceScale = 1.0;
+ ourScreen->GetContentsScaleFactor(&desktopToDeviceScale);
+ ourScreen->GetDefaultCSSScaleFactor(&cssToDeviceScale);
+ double cssToDesktopFactor = cssToDeviceScale / desktopToDeviceScale;
+ kOffset = NSToIntRound(kOffset * cssToDesktopFactor);
+ kSlop = NSToIntRound(kSlop * cssToDesktopFactor);
+ // Convert dimensions from CSS to desktop pixels
+ aSpecWidth = NSToIntRound(aSpecWidth * cssToDesktopFactor);
+ aSpecHeight = NSToIntRound(aSpecHeight * cssToDesktopFactor);
+ gotScreen = true;
+ }
+ }
+ }
+
+ // One full pass through all windows of this type, repeat until no collisions.
+ do {
+ keepTrying = false;
+ nsCOMPtr<nsISimpleEnumerator> windowList;
+ wm->GetXULWindowEnumerator(windowType.get(), getter_AddRefs(windowList));
+
+ if (!windowList)
+ break;
+
+ // One full pass through all windows of this type, offset and stop on collision.
+ do {
+ bool more;
+ windowList->HasMoreElements(&more);
+ if (!more)
+ break;
+
+ nsCOMPtr<nsISupports> supportsWindow;
+ windowList->GetNext(getter_AddRefs(supportsWindow));
+
+ nsCOMPtr<nsIXULWindow> listXULWindow(do_QueryInterface(supportsWindow));
+ if (listXULWindow != ourXULWindow) {
+ int32_t listX, listY;
+ nsCOMPtr<nsIBaseWindow> listBaseWindow(do_QueryInterface(supportsWindow));
+ listBaseWindow->GetPosition(&listX, &listY);
+ double scale;
+ if (NS_SUCCEEDED(listBaseWindow->GetDevicePixelsPerDesktopPixel(&scale))) {
+ listX = NSToIntRound(listX / scale);
+ listY = NSToIntRound(listY / scale);
+ }
+
+ if (Abs(listX - aRequestedX) <= kSlop && Abs(listY - aRequestedY) <= kSlop) {
+ // collision! offset and start over
+ if (bouncedX & 0x1)
+ aRequestedX -= kOffset;
+ else
+ aRequestedX += kOffset;
+ aRequestedY += kOffset;
+
+ if (gotScreen) {
+ // if we're moving to the right and we need to bounce...
+ if (!(bouncedX & 0x1) && ((aRequestedX + aSpecWidth) > screenRight)) {
+ aRequestedX = screenRight - aSpecWidth;
+ ++bouncedX;
+ }
+
+ // if we're moving to the left and we need to bounce...
+ if ((bouncedX & 0x1) && aRequestedX < screenLeft) {
+ aRequestedX = screenLeft;
+ ++bouncedX;
+ }
+
+ // if we hit the bottom then bounce to the top
+ if (aRequestedY + aSpecHeight > screenBottom) {
+ aRequestedY = screenTop;
+ ++bouncedY;
+ }
+ }
+
+ /* loop around again,
+ but it's time to give up once we've covered the screen.
+ there's a potential infinite loop with lots of windows. */
+ keepTrying = bouncedX < 2 || bouncedY == 0;
+ break;
+ }
+ }
+ } while(1);
+ } while (keepTrying);
+}
+
+void nsXULWindow::SyncAttributesToWidget()
+{
+ nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
+ if (!windowElement)
+ return;
+
+ nsAutoString attr;
+
+ // "hidechrome" attribute
+ if (windowElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidechrome,
+ nsGkAtoms::_true, eCaseMatters)) {
+ mWindow->HideWindowChrome(true);
+ }
+
+ // "chromemargin" attribute
+ nsIntMargin margins;
+ windowElement->GetAttribute(NS_LITERAL_STRING("chromemargin"), attr);
+ if (nsContentUtils::ParseIntMarginValue(attr, margins)) {
+ LayoutDeviceIntMargin tmp = LayoutDeviceIntMargin::FromUnknownMargin(margins);
+ mWindow->SetNonClientMargins(tmp);
+ }
+
+ // "windowtype" attribute
+ windowElement->GetAttribute(WINDOWTYPE_ATTRIBUTE, attr);
+ if (!attr.IsEmpty()) {
+ mWindow->SetWindowClass(attr);
+ }
+
+ // "id" attribute for icon
+ windowElement->GetAttribute(NS_LITERAL_STRING("id"), attr);
+ if (attr.IsEmpty()) {
+ attr.AssignLiteral("default");
+ }
+ mWindow->SetIcon(attr);
+
+ // "drawtitle" attribute
+ windowElement->GetAttribute(NS_LITERAL_STRING("drawtitle"), attr);
+ mWindow->SetDrawsTitle(attr.LowerCaseEqualsLiteral("true"));
+
+ // "toggletoolbar" attribute
+ windowElement->GetAttribute(NS_LITERAL_STRING("toggletoolbar"), attr);
+ mWindow->SetShowsToolbarButton(attr.LowerCaseEqualsLiteral("true"));
+
+ // "fullscreenbutton" attribute
+ windowElement->GetAttribute(NS_LITERAL_STRING("fullscreenbutton"), attr);
+ mWindow->SetShowsFullScreenButton(attr.LowerCaseEqualsLiteral("true"));
+
+ // "macanimationtype" attribute
+ windowElement->GetAttribute(NS_LITERAL_STRING("macanimationtype"), attr);
+ if (attr.EqualsLiteral("document")) {
+ mWindow->SetWindowAnimationType(nsIWidget::eDocumentWindowAnimation);
+ }
+}
+
+NS_IMETHODIMP nsXULWindow::SavePersistentAttributes()
+{
+ // can happen when the persistence timer fires at an inopportune time
+ // during window shutdown
+ if (!mDocShell)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
+ if (!docShellElement)
+ return NS_ERROR_FAILURE;
+
+ nsAutoString persistString;
+ docShellElement->GetAttribute(PERSIST_ATTRIBUTE, persistString);
+ if (persistString.IsEmpty()) { // quick check which sometimes helps
+ mPersistentAttributesDirty = 0;
+ return NS_OK;
+ }
+
+ bool isFullscreen = false;
+ if (nsPIDOMWindowOuter* domWindow = mDocShell->GetWindow()) {
+ isFullscreen = domWindow->GetFullScreen();
+ }
+
+ // get our size, position and mode to persist
+ LayoutDeviceIntRect rect;
+ bool gotRestoredBounds = NS_SUCCEEDED(mWindow->GetRestoredBounds(rect));
+
+ // we use CSS pixels for size, but desktop pixels for position
+ CSSToLayoutDeviceScale sizeScale = mWindow->GetDefaultScale();
+ DesktopToLayoutDeviceScale posScale = mWindow->GetDesktopToDeviceScale();
+
+ // make our position relative to our parent, if any
+ nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow));
+ if (parent && gotRestoredBounds) {
+ int32_t parentX, parentY;
+ if (NS_SUCCEEDED(parent->GetPosition(&parentX, &parentY))) {
+ rect.x -= parentX;
+ rect.y -= parentY;
+ }
+ }
+
+ char sizeBuf[10];
+ nsAutoString sizeString;
+ nsAutoString windowElementId;
+ nsCOMPtr<nsIDOMXULDocument> ownerXULDoc;
+
+ // fetch docShellElement's ID and XUL owner document
+ ownerXULDoc = do_QueryInterface(docShellElement->OwnerDoc());
+ if (docShellElement->IsXULElement()) {
+ docShellElement->GetId(windowElementId);
+ }
+
+ bool shouldPersist = !isFullscreen && ownerXULDoc;
+ ErrorResult rv;
+ // (only for size elements which are persisted)
+ if ((mPersistentAttributesDirty & PAD_POSITION) && gotRestoredBounds) {
+ if (persistString.Find("screenX") >= 0) {
+ SprintfLiteral(sizeBuf, "%d", NSToIntRound(rect.x / posScale.scale));
+ sizeString.AssignWithConversion(sizeBuf);
+ docShellElement->SetAttribute(SCREENX_ATTRIBUTE, sizeString, rv);
+ if (shouldPersist) {
+ ownerXULDoc->Persist(windowElementId, SCREENX_ATTRIBUTE);
+ }
+ }
+ if (persistString.Find("screenY") >= 0) {
+ SprintfLiteral(sizeBuf, "%d", NSToIntRound(rect.y / posScale.scale));
+ sizeString.AssignWithConversion(sizeBuf);
+ docShellElement->SetAttribute(SCREENY_ATTRIBUTE, sizeString, rv);
+ if (shouldPersist) {
+ ownerXULDoc->Persist(windowElementId, SCREENY_ATTRIBUTE);
+ }
+ }
+ }
+
+ if ((mPersistentAttributesDirty & PAD_SIZE) && gotRestoredBounds) {
+ if (persistString.Find("width") >= 0) {
+ SprintfLiteral(sizeBuf, "%d", NSToIntRound(rect.width / sizeScale.scale));
+ sizeString.AssignWithConversion(sizeBuf);
+ docShellElement->SetAttribute(WIDTH_ATTRIBUTE, sizeString, rv);
+ if (shouldPersist) {
+ ownerXULDoc->Persist(windowElementId, WIDTH_ATTRIBUTE);
+ }
+ }
+ if (persistString.Find("height") >= 0) {
+ SprintfLiteral(sizeBuf, "%d", NSToIntRound(rect.height / sizeScale.scale));
+ sizeString.AssignWithConversion(sizeBuf);
+ docShellElement->SetAttribute(HEIGHT_ATTRIBUTE, sizeString, rv);
+ if (shouldPersist) {
+ ownerXULDoc->Persist(windowElementId, HEIGHT_ATTRIBUTE);
+ }
+ }
+ }
+
+ if (mPersistentAttributesDirty & PAD_MISC) {
+ nsSizeMode sizeMode = mWindow->SizeMode();
+
+ if (sizeMode != nsSizeMode_Minimized) {
+ if (sizeMode == nsSizeMode_Maximized)
+ sizeString.Assign(SIZEMODE_MAXIMIZED);
+ else if (sizeMode == nsSizeMode_Fullscreen)
+ sizeString.Assign(SIZEMODE_FULLSCREEN);
+ else
+ sizeString.Assign(SIZEMODE_NORMAL);
+ docShellElement->SetAttribute(MODE_ATTRIBUTE, sizeString, rv);
+ if (shouldPersist && persistString.Find("sizemode") >= 0) {
+ ownerXULDoc->Persist(windowElementId, MODE_ATTRIBUTE);
+ }
+ }
+ if (persistString.Find("zlevel") >= 0) {
+ uint32_t zLevel;
+ nsCOMPtr<nsIWindowMediator> mediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
+ if (mediator) {
+ mediator->GetZLevel(this, &zLevel);
+ SprintfLiteral(sizeBuf, "%" PRIu32, zLevel);
+ sizeString.AssignWithConversion(sizeBuf);
+ docShellElement->SetAttribute(ZLEVEL_ATTRIBUTE, sizeString, rv);
+ if (shouldPersist) {
+ ownerXULDoc->Persist(windowElementId, ZLEVEL_ATTRIBUTE);
+ }
+ }
+ }
+ }
+
+ mPersistentAttributesDirty = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::GetWindowDOMWindow(mozIDOMWindowProxy** aDOMWindow)
+{
+ NS_ENSURE_STATE(mDocShell);
+
+ if (!mDOMWindow)
+ mDOMWindow = mDocShell->GetWindow();
+ NS_ENSURE_TRUE(mDOMWindow, NS_ERROR_FAILURE);
+
+ *aDOMWindow = mDOMWindow;
+ NS_ADDREF(*aDOMWindow);
+ return NS_OK;
+}
+
+dom::Element*
+nsXULWindow::GetWindowDOMElement() const
+{
+ NS_ENSURE_TRUE(mDocShell, nullptr);
+
+ nsCOMPtr<nsIContentViewer> cv;
+ mDocShell->GetContentViewer(getter_AddRefs(cv));
+ NS_ENSURE_TRUE(cv, nullptr);
+
+ const nsIDocument* document = cv->GetDocument();
+ NS_ENSURE_TRUE(document, nullptr);
+
+ return document->GetRootElement();
+}
+
+nsresult nsXULWindow::ContentShellAdded(nsIDocShellTreeItem* aContentShell,
+ bool aPrimary, bool aTargetable, const nsAString& aID)
+{
+ nsContentShellInfo* shellInfo = nullptr;
+
+ uint32_t i, count = mContentShells.Length();
+ nsWeakPtr contentShellWeak = do_GetWeakReference(aContentShell);
+ for (i = 0; i < count; i++) {
+ nsContentShellInfo* info = mContentShells.ElementAt(i);
+ if (info->id.Equals(aID)) {
+ // We already exist. Do a replace.
+ info->child = contentShellWeak;
+ shellInfo = info;
+ }
+ else if (info->child == contentShellWeak)
+ info->child = nullptr;
+ }
+
+ if (!shellInfo) {
+ shellInfo = new nsContentShellInfo(aID, contentShellWeak);
+ mContentShells.AppendElement(shellInfo);
+ }
+
+ // Set the default content tree owner
+ if (aPrimary) {
+ NS_ENSURE_SUCCESS(EnsurePrimaryContentTreeOwner(), NS_ERROR_FAILURE);
+ aContentShell->SetTreeOwner(mPrimaryContentTreeOwner);
+ mPrimaryContentShell = aContentShell;
+ mPrimaryTabParent = nullptr;
+ }
+ else {
+ NS_ENSURE_SUCCESS(EnsureContentTreeOwner(), NS_ERROR_FAILURE);
+ aContentShell->SetTreeOwner(mContentTreeOwner);
+ if (mPrimaryContentShell == aContentShell)
+ mPrimaryContentShell = nullptr;
+ }
+
+ if (aTargetable) {
+#ifdef DEBUG
+ int32_t debugCount = mTargetableShells.Count();
+ int32_t debugCounter;
+ for (debugCounter = debugCount - 1; debugCounter >= 0; --debugCounter) {
+ nsCOMPtr<nsIDocShellTreeItem> curItem =
+ do_QueryReferent(mTargetableShells[debugCounter]);
+ NS_ASSERTION(!SameCOMIdentity(curItem, aContentShell),
+ "Adding already existing item to mTargetableShells");
+ }
+#endif
+
+ // put the new shell at the start of the targetable shells list if either
+ // it's the new primary shell or there is no existing primary shell (which
+ // means that chances are this one just stopped being primary). If we
+ // really cared, we could keep track of the "last no longer primary shell"
+ // explicitly, but it probably doesn't matter enough: the difference would
+ // only be felt in a situation where all shells were non-primary, which
+ // doesn't happen much. In a situation where there is one and only one
+ // primary shell, and in which shells get unmarked as primary before some
+ // other shell gets marked as primary, this effectively stores the list of
+ // targetable shells in "most recently primary first" order.
+ bool inserted;
+ if (aPrimary || !mPrimaryContentShell) {
+ inserted = mTargetableShells.InsertObjectAt(contentShellWeak, 0);
+ } else {
+ inserted = mTargetableShells.AppendObject(contentShellWeak);
+ }
+ NS_ENSURE_TRUE(inserted, NS_ERROR_OUT_OF_MEMORY);
+ }
+
+ return NS_OK;
+}
+
+nsresult nsXULWindow::ContentShellRemoved(nsIDocShellTreeItem* aContentShell)
+{
+ if (mPrimaryContentShell == aContentShell) {
+ mPrimaryContentShell = nullptr;
+ }
+
+ int32_t i, count = mContentShells.Length();
+ for (i = count - 1; i >= 0; --i) {
+ nsContentShellInfo* info = mContentShells.ElementAt(i);
+ nsCOMPtr<nsIDocShellTreeItem> curItem = do_QueryReferent(info->child);
+ if (!curItem || SameCOMIdentity(curItem, aContentShell)) {
+ mContentShells.RemoveElementAt(i);
+ delete info;
+ }
+ }
+
+ count = mTargetableShells.Count();
+ for (i = count - 1; i >= 0; --i) {
+ nsCOMPtr<nsIDocShellTreeItem> curItem =
+ do_QueryReferent(mTargetableShells[i]);
+ if (!curItem || SameCOMIdentity(curItem, aContentShell)) {
+ mTargetableShells.RemoveObjectAt(i);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULWindow::GetPrimaryContentSize(int32_t* aWidth,
+ int32_t* aHeight)
+{
+ if (mPrimaryTabParent) {
+ return GetPrimaryTabParentSize(aWidth, aHeight);
+ } else if (mPrimaryContentShell) {
+ return GetPrimaryContentShellSize(aWidth, aHeight);
+ }
+ return NS_ERROR_UNEXPECTED;
+}
+
+nsresult
+nsXULWindow::GetPrimaryTabParentSize(int32_t* aWidth,
+ int32_t* aHeight)
+{
+ TabParent* tabParent = TabParent::GetFrom(mPrimaryTabParent);
+ // Need strong ref, since Client* can run script.
+ nsCOMPtr<Element> element = tabParent->GetOwnerElement();
+ NS_ENSURE_STATE(element);
+
+ *aWidth = element->ClientWidth();
+ *aHeight = element->ClientHeight();
+ return NS_OK;
+}
+
+nsresult
+nsXULWindow::GetPrimaryContentShellSize(int32_t* aWidth,
+ int32_t* aHeight)
+{
+ NS_ENSURE_STATE(mPrimaryContentShell);
+
+ nsCOMPtr<nsIBaseWindow> shellWindow(do_QueryInterface(mPrimaryContentShell));
+ NS_ENSURE_STATE(shellWindow);
+
+ int32_t devicePixelWidth, devicePixelHeight;
+ double shellScale = 1.0;
+ // We want to return CSS pixels. First, we get device pixels
+ // from the content area...
+ shellWindow->GetSize(&devicePixelWidth, &devicePixelHeight);
+ // And then get the device pixel scaling factor. Dividing device
+ // pixels by this scaling factor gives us CSS pixels.
+ shellWindow->GetUnscaledDevicePixelsPerCSSPixel(&shellScale);
+ *aWidth = NSToIntRound(devicePixelWidth / shellScale);
+ *aHeight = NSToIntRound(devicePixelHeight / shellScale);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULWindow::SetPrimaryContentSize(int32_t aWidth,
+ int32_t aHeight)
+{
+ if (mPrimaryTabParent) {
+ return SetPrimaryTabParentSize(aWidth, aHeight);
+ } else if (mPrimaryContentShell) {
+ return SizeShellTo(mPrimaryContentShell, aWidth, aHeight);
+ }
+ return NS_ERROR_UNEXPECTED;
+}
+
+nsresult
+nsXULWindow::SetPrimaryTabParentSize(int32_t aWidth,
+ int32_t aHeight)
+{
+ int32_t shellWidth, shellHeight;
+ GetPrimaryTabParentSize(&shellWidth, &shellHeight);
+
+ double scale = 1.0;
+ GetUnscaledDevicePixelsPerCSSPixel(&scale);
+
+ SizeShellToWithLimit(aWidth, aHeight,
+ shellWidth * scale, shellHeight * scale);
+ return NS_OK;
+}
+
+nsresult
+nsXULWindow::GetRootShellSize(int32_t* aWidth,
+ int32_t* aHeight)
+{
+ nsCOMPtr<nsIBaseWindow> shellAsWin = do_QueryInterface(mDocShell);
+ NS_ENSURE_TRUE(shellAsWin, NS_ERROR_FAILURE);
+ return shellAsWin->GetSize(aWidth, aHeight);
+}
+
+nsresult
+nsXULWindow::SetRootShellSize(int32_t aWidth,
+ int32_t aHeight)
+{
+ nsCOMPtr<nsIDocShellTreeItem> docShellAsItem = do_QueryInterface(mDocShell);
+ return SizeShellTo(docShellAsItem, aWidth, aHeight);
+}
+
+NS_IMETHODIMP nsXULWindow::SizeShellTo(nsIDocShellTreeItem* aShellItem,
+ int32_t aCX, int32_t aCY)
+{
+ // XXXTAB This is wrong, we should actually reflow based on the passed in
+ // shell. For now we are hacking and doing delta sizing. This is bad
+ // because it assumes all size we add will go to the shell which probably
+ // won't happen.
+
+ nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(aShellItem));
+ NS_ENSURE_TRUE(shellAsWin, NS_ERROR_FAILURE);
+
+ int32_t width = 0;
+ int32_t height = 0;
+ shellAsWin->GetSize(&width, &height);
+
+ SizeShellToWithLimit(aCX, aCY, width, height);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::ExitModalLoop(nsresult aStatus)
+{
+ if (mContinueModalLoop)
+ EnableParent(true);
+ mContinueModalLoop = false;
+ mModalStatus = aStatus;
+ return NS_OK;
+}
+
+// top-level function to create a new window
+NS_IMETHODIMP nsXULWindow::CreateNewWindow(int32_t aChromeFlags,
+ nsITabParent *aOpeningTab,
+ mozIDOMWindowProxy *aOpener,
+ nsIXULWindow **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ if (aChromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME)
+ return CreateNewChromeWindow(aChromeFlags, aOpeningTab, aOpener, _retval);
+ return CreateNewContentWindow(aChromeFlags, aOpeningTab, aOpener, _retval);
+}
+
+NS_IMETHODIMP nsXULWindow::CreateNewChromeWindow(int32_t aChromeFlags,
+ nsITabParent *aOpeningTab,
+ mozIDOMWindowProxy *aOpener,
+ nsIXULWindow **_retval)
+{
+ nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
+ NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE);
+
+ // Just do a normal create of a window and return.
+ nsCOMPtr<nsIXULWindow> newWindow;
+ appShell->CreateTopLevelWindow(this, nullptr, aChromeFlags,
+ nsIAppShellService::SIZE_TO_CONTENT,
+ nsIAppShellService::SIZE_TO_CONTENT,
+ aOpeningTab, aOpener,
+ getter_AddRefs(newWindow));
+
+ NS_ENSURE_TRUE(newWindow, NS_ERROR_FAILURE);
+
+ *_retval = newWindow;
+ NS_ADDREF(*_retval);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::CreateNewContentWindow(int32_t aChromeFlags,
+ nsITabParent *aOpeningTab,
+ mozIDOMWindowProxy *aOpener,
+ nsIXULWindow **_retval)
+{
+ nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
+ NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE);
+
+ // We need to create a new top level window and then enter a nested
+ // loop. Eventually the new window will be told that it has loaded,
+ // at which time we know it is safe to spin out of the nested loop
+ // and allow the opening code to proceed.
+
+ nsCOMPtr<nsIURI> uri;
+
+ nsAdoptingCString urlStr = Preferences::GetCString("browser.chromeURL");
+ if (urlStr.IsEmpty()) {
+ urlStr.AssignLiteral("chrome://navigator/content/navigator.xul");
+ }
+
+ nsCOMPtr<nsIIOService> service(do_GetService(NS_IOSERVICE_CONTRACTID));
+ if (service) {
+ service->NewURI(urlStr, nullptr, nullptr, getter_AddRefs(uri));
+ }
+ NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
+
+ // We need to create a chrome window to contain the content window we're about
+ // to pass back. The subject principal needs to be system while we're creating
+ // it to make things work right, so force a system caller. See bug 799348
+ // comment 13 for a description of what happens when we don't.
+ nsCOMPtr<nsIXULWindow> newWindow;
+ {
+ AutoNoJSAPI nojsapi;
+ // We actually want this toplevel window which we are creating to have a
+ // null opener, as we will be creating the content xul:browser window inside
+ // of it, so we pass nullptr as our aOpener.
+ appShell->CreateTopLevelWindow(this, uri,
+ aChromeFlags, 615, 480,
+ aOpeningTab, nullptr,
+ getter_AddRefs(newWindow));
+ NS_ENSURE_TRUE(newWindow, NS_ERROR_FAILURE);
+ }
+
+ // Specify that we want the window to remain locked until the chrome has loaded.
+ nsXULWindow *xulWin = static_cast<nsXULWindow*>
+ (static_cast<nsIXULWindow*>
+ (newWindow));
+
+ if (aOpener) {
+ nsCOMPtr<nsIDocShell> docShell;
+ xulWin->GetDocShell(getter_AddRefs(docShell));
+ MOZ_ASSERT(docShell);
+ nsCOMPtr<nsIDOMChromeWindow> chromeWindow =
+ do_QueryInterface(docShell->GetWindow());
+ MOZ_ASSERT(chromeWindow);
+
+ chromeWindow->SetOpenerForInitialContentBrowser(aOpener);
+ }
+
+ xulWin->LockUntilChromeLoad();
+
+ {
+ AutoNoJSAPI nojsapi;
+ nsIThread *thread = NS_GetCurrentThread();
+ while (xulWin->IsLocked()) {
+ if (!NS_ProcessNextEvent(thread))
+ break;
+ }
+ }
+
+ NS_ENSURE_STATE(xulWin->mPrimaryContentShell || xulWin->mPrimaryTabParent);
+
+ *_retval = newWindow;
+ NS_ADDREF(*_retval);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::GetHasPrimaryContent(bool* aResult)
+{
+ *aResult = mPrimaryTabParent || mPrimaryContentShell;
+ return NS_OK;
+}
+
+void nsXULWindow::EnableParent(bool aEnable)
+{
+ nsCOMPtr<nsIBaseWindow> parentWindow;
+ nsCOMPtr<nsIWidget> parentWidget;
+
+ parentWindow = do_QueryReferent(mParentWindow);
+ if (parentWindow)
+ parentWindow->GetMainWidget(getter_AddRefs(parentWidget));
+ if (parentWidget)
+ parentWidget->Enable(aEnable);
+}
+
+// Constrain the window to its proper z-level
+bool nsXULWindow::ConstrainToZLevel(bool aImmediate,
+ nsWindowZ *aPlacement,
+ nsIWidget *aReqBelow,
+ nsIWidget **aActualBelow)
+{
+#if 0
+ /* Do we have a parent window? This means our z-order is already constrained,
+ since we're a dependent window. Our window list isn't hierarchical,
+ so we can't properly calculate placement for such a window.
+ Should we just abort? */
+ nsCOMPtr<nsIBaseWindow> parentWindow = do_QueryReferent(mParentWindow);
+ if (parentWindow)
+ return false;
+#endif
+
+ nsCOMPtr<nsIWindowMediator> mediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
+ if (!mediator)
+ return false;
+
+ bool altered;
+ uint32_t position,
+ newPosition,
+ zLevel;
+ nsIXULWindow *us = this;
+
+ altered = false;
+ mediator->GetZLevel(this, &zLevel);
+
+ // translate from WidgetGUIEvent to nsIWindowMediator constants
+ position = nsIWindowMediator::zLevelTop;
+ if (*aPlacement == nsWindowZBottom || zLevel == nsIXULWindow::lowestZ)
+ position = nsIWindowMediator::zLevelBottom;
+ else if (*aPlacement == nsWindowZRelative)
+ position = nsIWindowMediator::zLevelBelow;
+
+ if (NS_SUCCEEDED(mediator->CalculateZPosition(us, position, aReqBelow,
+ &newPosition, aActualBelow, &altered))) {
+ /* If we were asked to move to the top but constrained to remain
+ below one of our other windows, first move all windows in that
+ window's layer and above to the top. This allows the user to
+ click a window which can't be topmost and still bring mozilla
+ to the foreground. */
+ if (altered &&
+ (position == nsIWindowMediator::zLevelTop ||
+ (position == nsIWindowMediator::zLevelBelow && aReqBelow == 0)))
+ PlaceWindowLayersBehind(zLevel + 1, nsIXULWindow::highestZ, 0);
+
+ if (*aPlacement != nsWindowZBottom &&
+ position == nsIWindowMediator::zLevelBottom)
+ altered = true;
+ if (altered || aImmediate) {
+ if (newPosition == nsIWindowMediator::zLevelTop)
+ *aPlacement = nsWindowZTop;
+ else if (newPosition == nsIWindowMediator::zLevelBottom)
+ *aPlacement = nsWindowZBottom;
+ else
+ *aPlacement = nsWindowZRelative;
+
+ if (aImmediate) {
+ nsCOMPtr<nsIBaseWindow> ourBase = do_QueryObject(this);
+ if (ourBase) {
+ nsCOMPtr<nsIWidget> ourWidget;
+ ourBase->GetMainWidget(getter_AddRefs(ourWidget));
+ ourWidget->PlaceBehind(*aPlacement == nsWindowZBottom ?
+ eZPlacementBottom : eZPlacementBelow,
+ *aActualBelow, false);
+ }
+ }
+ }
+
+ /* CalculateZPosition can tell us to be below nothing, because it tries
+ not to change something it doesn't recognize. A request to verify
+ being below an unrecognized window, then, is treated as a request
+ to come to the top (below null) */
+ nsCOMPtr<nsIXULWindow> windowAbove;
+ if (newPosition == nsIWindowMediator::zLevelBelow && *aActualBelow) {
+ windowAbove = (*aActualBelow)->GetWidgetListener()->GetXULWindow();
+ }
+
+ mediator->SetZPosition(us, newPosition, windowAbove);
+ }
+
+ return altered;
+}
+
+/* Re-z-position all windows in the layers from aLowLevel to aHighLevel,
+ inclusive, to be behind aBehind. aBehind of null means on top.
+ Note this method actually does nothing to our relative window positions.
+ (And therefore there's no need to inform WindowMediator we're moving
+ things, because we aren't.) This method is useful for, say, moving
+ a range of layers of our own windows relative to windows belonging to
+ external applications.
+*/
+void nsXULWindow::PlaceWindowLayersBehind(uint32_t aLowLevel,
+ uint32_t aHighLevel,
+ nsIXULWindow *aBehind)
+{
+ // step through windows in z-order from top to bottommost window
+
+ nsCOMPtr<nsIWindowMediator> mediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
+ if (!mediator)
+ return;
+
+ nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
+ mediator->GetZOrderXULWindowEnumerator(0, true,
+ getter_AddRefs(windowEnumerator));
+ if (!windowEnumerator)
+ return;
+
+ // each window will be moved behind previousHighWidget, itself
+ // a moving target. initialize it.
+ nsCOMPtr<nsIWidget> previousHighWidget;
+ if (aBehind) {
+ nsCOMPtr<nsIBaseWindow> highBase(do_QueryInterface(aBehind));
+ if (highBase)
+ highBase->GetMainWidget(getter_AddRefs(previousHighWidget));
+ }
+
+ // get next lower window
+ bool more;
+ while (windowEnumerator->HasMoreElements(&more), more) {
+ uint32_t nextZ; // z-level of nextWindow
+ nsCOMPtr<nsISupports> nextWindow;
+ windowEnumerator->GetNext(getter_AddRefs(nextWindow));
+ nsCOMPtr<nsIXULWindow> nextXULWindow(do_QueryInterface(nextWindow));
+ nextXULWindow->GetZLevel(&nextZ);
+ if (nextZ < aLowLevel)
+ break; // we've processed all windows through aLowLevel
+
+ // move it just below its next higher window
+ nsCOMPtr<nsIBaseWindow> nextBase(do_QueryInterface(nextXULWindow));
+ if (nextBase) {
+ nsCOMPtr<nsIWidget> nextWidget;
+ nextBase->GetMainWidget(getter_AddRefs(nextWidget));
+ if (nextZ <= aHighLevel)
+ nextWidget->PlaceBehind(eZPlacementBelow, previousHighWidget, false);
+ previousHighWidget = nextWidget;
+ }
+ }
+}
+
+void nsXULWindow::SetContentScrollbarVisibility(bool aVisible)
+{
+ nsCOMPtr<nsPIDOMWindowOuter> contentWin(do_GetInterface(mPrimaryContentShell));
+ if (!contentWin) {
+ return;
+ }
+
+ nsContentUtils::SetScrollbarsVisibility(contentWin->GetDocShell(), aVisible);
+}
+
+bool nsXULWindow::GetContentScrollbarVisibility()
+{
+ // This code already exists in dom/src/base/nsBarProp.cpp, but we
+ // can't safely get to that from here as this function is called
+ // while the DOM window is being set up, and we need the DOM window
+ // to get to that code.
+ nsCOMPtr<nsIScrollable> scroller(do_QueryInterface(mPrimaryContentShell));
+
+ if (scroller) {
+ int32_t prefValue;
+ scroller->GetDefaultScrollbarPreferences(
+ nsIScrollable::ScrollOrientation_Y, &prefValue);
+ if (prefValue == nsIScrollable::Scrollbar_Never) // try the other way
+ scroller->GetDefaultScrollbarPreferences(
+ nsIScrollable::ScrollOrientation_X, &prefValue);
+
+ if (prefValue == nsIScrollable::Scrollbar_Never)
+ return false;
+ }
+
+ return true;
+}
+
+// during spinup, attributes that haven't been loaded yet can't be dirty
+void nsXULWindow::PersistentAttributesDirty(uint32_t aDirtyFlags)
+{
+ mPersistentAttributesDirty |= aDirtyFlags & mPersistentAttributesMask;
+}
+
+NS_IMETHODIMP nsXULWindow::ApplyChromeFlags()
+{
+ nsCOMPtr<dom::Element> window = GetWindowDOMElement();
+ NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
+
+ if (mChromeLoaded) {
+ // The two calls in this block don't need to happen early because they
+ // don't cause a global restyle on the document. Not only that, but the
+ // scrollbar stuff needs a content area to toggle the scrollbars on anyway.
+ // So just don't do these until mChromeLoaded is true.
+
+ // Scrollbars have their own special treatment.
+ SetContentScrollbarVisibility(mChromeFlags &
+ nsIWebBrowserChrome::CHROME_SCROLLBARS ?
+ true : false);
+ }
+
+ /* the other flags are handled together. we have style rules
+ in navigator.css that trigger visibility based on
+ the 'chromehidden' attribute of the <window> tag. */
+ nsAutoString newvalue;
+
+ if (! (mChromeFlags & nsIWebBrowserChrome::CHROME_MENUBAR))
+ newvalue.AppendLiteral("menubar ");
+
+ if (! (mChromeFlags & nsIWebBrowserChrome::CHROME_TOOLBAR))
+ newvalue.AppendLiteral("toolbar ");
+
+ if (! (mChromeFlags & nsIWebBrowserChrome::CHROME_LOCATIONBAR))
+ newvalue.AppendLiteral("location ");
+
+ if (! (mChromeFlags & nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR))
+ newvalue.AppendLiteral("directories ");
+
+ if (! (mChromeFlags & nsIWebBrowserChrome::CHROME_STATUSBAR))
+ newvalue.AppendLiteral("status ");
+
+ if (! (mChromeFlags & nsIWebBrowserChrome::CHROME_EXTRA))
+ newvalue.AppendLiteral("extrachrome ");
+
+ // Note that if we're not actually changing the value this will be a no-op,
+ // so no need to compare to the old value.
+ ErrorResult rv;
+ window->SetAttribute(NS_LITERAL_STRING("chromehidden"), newvalue, rv);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::GetXULBrowserWindow(nsIXULBrowserWindow * *aXULBrowserWindow)
+{
+ NS_IF_ADDREF(*aXULBrowserWindow = mXULBrowserWindow);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXULWindow::SetXULBrowserWindow(nsIXULBrowserWindow * aXULBrowserWindow)
+{
+ mXULBrowserWindow = aXULBrowserWindow;
+ return NS_OK;
+}
+
+void nsXULWindow::SizeShellToWithLimit(int32_t aDesiredWidth,
+ int32_t aDesiredHeight,
+ int32_t shellItemWidth,
+ int32_t shellItemHeight)
+{
+ int32_t widthDelta = aDesiredWidth - shellItemWidth;
+ int32_t heightDelta = aDesiredHeight - shellItemHeight;
+
+ if (widthDelta || heightDelta) {
+ int32_t winWidth = 0;
+ int32_t winHeight = 0;
+
+ GetSize(&winWidth, &winHeight);
+ // There's no point in trying to make the window smaller than the
+ // desired content area size --- that's not likely to work. This whole
+ // function assumes that the outer docshell is adding some constant
+ // "border" chrome to the content area.
+ winWidth = std::max(winWidth + widthDelta, aDesiredWidth);
+ winHeight = std::max(winHeight + heightDelta, aDesiredHeight);
+ SetSize(winWidth, winHeight, true);
+ }
+}
+
+//*****************************************************************************
+//*** nsContentShellInfo: Object Management
+//*****************************************************************************
+
+nsContentShellInfo::nsContentShellInfo(const nsAString& aID,
+ nsIWeakReference* aContentShell)
+ : id(aID),
+ child(aContentShell)
+{
+ MOZ_COUNT_CTOR(nsContentShellInfo);
+}
+
+nsContentShellInfo::~nsContentShellInfo()
+{
+ MOZ_COUNT_DTOR(nsContentShellInfo);
+ //XXX Set Tree Owner to null if the tree owner is nsXULWindow->mContentTreeOwner
+}
diff --git a/xpfe/appshell/nsXULWindow.h b/xpfe/appshell/nsXULWindow.h
new file mode 100644
index 000000000..eb059c939
--- /dev/null
+++ b/xpfe/appshell/nsXULWindow.h
@@ -0,0 +1,199 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsXULWindow_h__
+#define nsXULWindow_h__
+
+// Local Includes
+#include "nsChromeTreeOwner.h"
+#include "nsContentTreeOwner.h"
+
+// Helper classes
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+#include "nsString.h"
+#include "nsWeakReference.h"
+#include "nsCOMArray.h"
+#include "nsRect.h"
+#include "Units.h"
+
+// Interfaces needed
+#include "nsIBaseWindow.h"
+#include "nsIDocShell.h"
+#include "nsIDocShellTreeItem.h"
+#include "nsIDOMWindow.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIXULWindow.h"
+#include "nsIPrompt.h"
+#include "nsIAuthPrompt.h"
+#include "nsIXULBrowserWindow.h"
+#include "nsIWeakReference.h"
+#include "nsIWidgetListener.h"
+#include "nsITabParent.h"
+
+namespace mozilla {
+namespace dom {
+class Element;
+} // namespace dom
+} // namespace mozilla
+
+// nsXULWindow
+
+#define NS_XULWINDOW_IMPL_CID \
+{ /* 8eaec2f3-ed02-4be2-8e0f-342798477298 */ \
+ 0x8eaec2f3, \
+ 0xed02, \
+ 0x4be2, \
+ { 0x8e, 0x0f, 0x34, 0x27, 0x98, 0x47, 0x72, 0x98 } \
+}
+
+class nsContentShellInfo;
+
+class nsXULWindow : public nsIBaseWindow,
+ public nsIInterfaceRequestor,
+ public nsIXULWindow,
+ public nsSupportsWeakReference
+{
+friend class nsChromeTreeOwner;
+friend class nsContentTreeOwner;
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSIXULWINDOW
+ NS_DECL_NSIBASEWINDOW
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_XULWINDOW_IMPL_CID)
+
+ void LockUntilChromeLoad() { mLockedUntilChromeLoad = true; }
+ bool IsLocked() const { return mLockedUntilChromeLoad; }
+ void IgnoreXULSizeMode(bool aEnable) { mIgnoreXULSizeMode = aEnable; }
+ void WasRegistered() { mRegistered = true; }
+
+protected:
+ enum persistentAttributes {
+ PAD_MISC = 0x1,
+ PAD_POSITION = 0x2,
+ PAD_SIZE = 0x4
+ };
+
+ explicit nsXULWindow(uint32_t aChromeFlags);
+ virtual ~nsXULWindow();
+
+ NS_IMETHOD EnsureChromeTreeOwner();
+ NS_IMETHOD EnsureContentTreeOwner();
+ NS_IMETHOD EnsurePrimaryContentTreeOwner();
+ NS_IMETHOD EnsurePrompter();
+ NS_IMETHOD EnsureAuthPrompter();
+
+ void OnChromeLoaded();
+ void StaggerPosition(int32_t &aRequestedX, int32_t &aRequestedY,
+ int32_t aSpecWidth, int32_t aSpecHeight);
+ bool LoadPositionFromXUL(int32_t aSpecWidth, int32_t aSpecHeight);
+ bool LoadSizeFromXUL(int32_t& aSpecWidth, int32_t& aSpecHeight);
+ void SetSpecifiedSize(int32_t aSpecWidth, int32_t aSpecHeight);
+ bool LoadMiscPersistentAttributesFromXUL();
+ void SyncAttributesToWidget();
+ NS_IMETHOD SavePersistentAttributes();
+
+ NS_IMETHOD GetWindowDOMWindow(mozIDOMWindowProxy** aDOMWindow);
+ mozilla::dom::Element* GetWindowDOMElement() const;
+
+ // See nsIDocShellTreeOwner for docs on next two methods
+ nsresult ContentShellAdded(nsIDocShellTreeItem* aContentShell,
+ bool aPrimary, bool aTargetable,
+ const nsAString& aID);
+ nsresult ContentShellRemoved(nsIDocShellTreeItem* aContentShell);
+ NS_IMETHOD GetPrimaryContentSize(int32_t* aWidth,
+ int32_t* aHeight);
+ NS_IMETHOD SetPrimaryContentSize(int32_t aWidth,
+ int32_t aHeight);
+ nsresult GetRootShellSize(int32_t* aWidth,
+ int32_t* aHeight);
+ nsresult SetRootShellSize(int32_t aWidth,
+ int32_t aHeight);
+
+ NS_IMETHOD SizeShellTo(nsIDocShellTreeItem* aShellItem, int32_t aCX,
+ int32_t aCY);
+ NS_IMETHOD ExitModalLoop(nsresult aStatus);
+ NS_IMETHOD CreateNewChromeWindow(int32_t aChromeFlags, nsITabParent* aOpeningTab, mozIDOMWindowProxy* aOpenerWindow, nsIXULWindow **_retval);
+ NS_IMETHOD CreateNewContentWindow(int32_t aChromeFlags, nsITabParent* aOpeningTab, mozIDOMWindowProxy* aOpenerWindow, nsIXULWindow **_retval);
+ NS_IMETHOD GetHasPrimaryContent(bool* aResult);
+
+ void EnableParent(bool aEnable);
+ bool ConstrainToZLevel(bool aImmediate, nsWindowZ *aPlacement,
+ nsIWidget *aReqBelow, nsIWidget **aActualBelow);
+ void PlaceWindowLayersBehind(uint32_t aLowLevel, uint32_t aHighLevel,
+ nsIXULWindow *aBehind);
+ void SetContentScrollbarVisibility(bool aVisible);
+ bool GetContentScrollbarVisibility();
+ void PersistentAttributesDirty(uint32_t aDirtyFlags);
+
+ nsChromeTreeOwner* mChromeTreeOwner;
+ nsContentTreeOwner* mContentTreeOwner;
+ nsContentTreeOwner* mPrimaryContentTreeOwner;
+ nsCOMPtr<nsIWidget> mWindow;
+ nsCOMPtr<nsIDocShell> mDocShell;
+ nsCOMPtr<nsPIDOMWindowOuter> mDOMWindow;
+ nsCOMPtr<nsIWeakReference> mParentWindow;
+ nsCOMPtr<nsIPrompt> mPrompter;
+ nsCOMPtr<nsIAuthPrompt> mAuthPrompter;
+ nsCOMPtr<nsIXULBrowserWindow> mXULBrowserWindow;
+ nsCOMPtr<nsIDocShellTreeItem> mPrimaryContentShell;
+ nsTArray<nsContentShellInfo*> mContentShells; // array of doc shells by id
+ nsresult mModalStatus;
+ bool mContinueModalLoop;
+ bool mDebuting; // being made visible right now
+ bool mChromeLoaded; // True when chrome has loaded
+ bool mShowAfterLoad;
+ bool mIntrinsicallySized;
+ bool mCenterAfterLoad;
+ bool mIsHiddenWindow;
+ bool mLockedUntilChromeLoad;
+ bool mIgnoreXULSize;
+ bool mIgnoreXULPosition;
+ bool mChromeFlagsFrozen;
+ bool mIgnoreXULSizeMode;
+ // mDestroying is used to prevent reentry into into Destroy(), which can
+ // otherwise happen due to script running as we tear down various things.
+ bool mDestroying;
+ bool mRegistered;
+ uint32_t mContextFlags;
+ uint32_t mPersistentAttributesDirty; // persistentAttributes
+ uint32_t mPersistentAttributesMask;
+ uint32_t mChromeFlags;
+ nsString mTitle;
+ nsIntRect mOpenerScreenRect; // the screen rect of the opener
+
+ nsCOMArray<nsIWeakReference> mTargetableShells; // targetable shells only
+
+ nsCOMPtr<nsITabParent> mPrimaryTabParent;
+private:
+ nsresult GetPrimaryTabParentSize(int32_t* aWidth, int32_t* aHeight);
+ nsresult GetPrimaryContentShellSize(int32_t* aWidth, int32_t* aHeight);
+ nsresult SetPrimaryTabParentSize(int32_t aWidth, int32_t aHeight);
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsXULWindow, NS_XULWINDOW_IMPL_CID)
+
+// nsContentShellInfo
+// Used to map shell IDs to nsIDocShellTreeItems.
+
+class nsContentShellInfo
+{
+public:
+ nsContentShellInfo(const nsAString& aID,
+ nsIWeakReference* aContentShell);
+ ~nsContentShellInfo();
+
+public:
+ nsString id; // The identifier of the content shell
+ nsWeakPtr child; // content shell (weak reference to nsIDocShellTreeItem)
+};
+
+#endif /* nsXULWindow_h__ */
diff --git a/xpfe/appshell/test/chrome.ini b/xpfe/appshell/test/chrome.ini
new file mode 100644
index 000000000..6f4b72829
--- /dev/null
+++ b/xpfe/appshell/test/chrome.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+skip-if = os == 'android'
+
+[test_hiddenPrivateWindow.xul]
+[test_windowlessBrowser.xul]
diff --git a/xpfe/appshell/test/test_hiddenPrivateWindow.xul b/xpfe/appshell/test/test_hiddenPrivateWindow.xul
new file mode 100644
index 000000000..a1dfb486d
--- /dev/null
+++ b/xpfe/appshell/test/test_hiddenPrivateWindow.xul
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=815847
+-->
+<window title="Mozilla Bug 815847"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=815847">Mozilla Bug 815847</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+var Cu = Components.utils;
+
+Cu.import('resource://gre/modules/Services.jsm');
+Cu.import('resource://gre/modules/PrivateBrowsingUtils.jsm');
+
+ok(Services.appShell.hiddenWindow, "hiddenWindow should exist");
+ok(Services.appShell.hiddenDOMWindow, "hiddenDOMWindow should exist");
+ok(Services.appShell.hiddenPrivateWindow, "hiddenPrivateWindow should exist");
+ok(Services.appShell.hiddenPrivateDOMWindow, "hiddenPrivateDOMWindow should exist");
+
+ok(!PrivateBrowsingUtils.isWindowPrivate(Services.appShell.hiddenWindow.docShell), "hiddenWindow should not be private");
+ok(!PrivateBrowsingUtils.isWindowPrivate(Services.appShell.hiddenDOMWindow), "hiddenDOMWindow should not be private");
+ok(PrivateBrowsingUtils.isWindowPrivate(Services.appShell.hiddenPrivateWindow.docShell), "hiddenPrivateWindow should be private");
+ok(PrivateBrowsingUtils.isWindowPrivate(Services.appShell.hiddenPrivateDOMWindow), "hiddenPrivateDOMWindow should be private");
+
+]]>
+</script>
+
+</window>
diff --git a/xpfe/appshell/test/test_windowlessBrowser.xul b/xpfe/appshell/test/test_windowlessBrowser.xul
new file mode 100644
index 000000000..ef91f7655
--- /dev/null
+++ b/xpfe/appshell/test/test_windowlessBrowser.xul
@@ -0,0 +1,75 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=815847
+-->
+<window title="Mozilla Bug 815847"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1214174">Mozilla Bug 1214174</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+var Cu = Components.utils;
+var Ci = Components.interfaces;
+
+Cu.import('resource://gre/modules/Services.jsm');
+
+function testWindowlessBrowser(chromePrivileged) {
+ var webNav = Services.appShell.createWindowlessBrowser(chromePrivileged);
+
+ ok(webNav, "createWindowlessBrowser should return a wevNav");
+
+ let interfaceRequestor = webNav.QueryInterface(Ci.nsIInterfaceRequestor);
+ let docShell = interfaceRequestor.getInterface(Ci.nsIDocShell);
+
+ ok(docShell, "docShell should be defined");
+ ok(docShell.contentViewer.DOMDocument.defaultView, "docShell defaultView should be defined");
+
+ var win = docShell.contentViewer.DOMDocument.defaultView;
+
+ ok(win.screenX == 0, "window.screenX should be 0 in a windowless browser");
+ ok(win.screenY == 0, "window.screenY should be 0 in a windowless browser");
+ ok(win.outerWidth == 0, "window.outerWidth should be 0 in a windowless browser");
+ ok(win.outerHeight == 0, "window.outerHeight should be 0 in a windowless browser");
+
+ ok(win.sidebar, "window.sidebar should be defined");
+ ok(win.external, "window.external should be defined");
+
+ var exception;
+
+ try {
+ win.external.AddSearchProvider("http://test-fake.url");
+ } catch(e) {
+ exception = e;
+ }
+
+ ok(!exception, "window.external.AddSearchProvider should be ignore withour raising an exception");
+
+ webNav.close();
+}
+
+info("Test Bug 1214174 on a content privileged windowless browser");
+testWindowlessBrowser(false);
+
+info("Test Bug 1214174 on a chrome privileged windowless browser");
+testWindowlessBrowser(true);
+
+]]>
+</script>
+
+</window>
diff --git a/xpfe/components/autocomplete/jar.mn b/xpfe/components/autocomplete/jar.mn
new file mode 100644
index 000000000..d3ddf8a61
--- /dev/null
+++ b/xpfe/components/autocomplete/jar.mn
@@ -0,0 +1,9 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+toolkit.jar:
+ content/global/autocomplete.xml (resources/content/autocomplete.xml)
+
+comm.jar:
+ content/communicator/autocomplete.css (resources/content/autocomplete.css)
diff --git a/xpfe/components/autocomplete/moz.build b/xpfe/components/autocomplete/moz.build
new file mode 100644
index 000000000..eb4454d28
--- /dev/null
+++ b/xpfe/components/autocomplete/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ['jar.mn'] \ No newline at end of file
diff --git a/xpfe/components/autocomplete/resources/content/autocomplete.css b/xpfe/components/autocomplete/resources/content/autocomplete.css
new file mode 100644
index 000000000..6c67bad2e
--- /dev/null
+++ b/xpfe/components/autocomplete/resources/content/autocomplete.css
@@ -0,0 +1,46 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+.autocomplete-result-popupset {
+ width: 0 !important;
+}
+
+.autocomplete-result-popup {
+ display: -moz-popup !important;
+}
+
+/* the C++ implementation of widgets is too eager to make popups visible.
+ this causes problems (bug 120155 and others), thus this workaround: */
+.autocomplete-result-popup[hidden="true"] {
+ visibility: hidden;
+}
+
+.autocomplete-tree {
+ -moz-user-focus: ignore;
+}
+
+.autocomplete-history-dropmarker {
+ display: none;
+}
+
+.autocomplete-history-dropmarker[enablehistory="true"] {
+ display: -moz-box;
+}
+
+/* The following rule is here to fix bug 96899 (and now 117952).
+ Somehow trees create a situation
+ in which a popupset flows itself as if its popup child is directly within it
+ instead of the placeholder child that should actually be inside the popupset.
+ This is a stopgap measure, and it does not address the real bug. */
+popupset {
+ max-width: 0px;
+ width: 0px;
+ min-width: 0%;
+ min-height: 0%;
+}
+
+treecolpicker {
+ display: none;
+}
diff --git a/xpfe/components/autocomplete/resources/content/autocomplete.xml b/xpfe/components/autocomplete/resources/content/autocomplete.xml
new file mode 100644
index 000000000..93b6dfdb0
--- /dev/null
+++ b/xpfe/components/autocomplete/resources/content/autocomplete.xml
@@ -0,0 +1,1646 @@
+<?xml version="1.0"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<bindings id="autocompleteBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:xbl="http://www.mozilla.org/xbl">
+
+ <binding id="autocomplete" role="xul:combobox"
+ extends="chrome://global/content/bindings/textbox.xml#textbox">
+ <resources>
+ <stylesheet src="chrome://communicator/content/autocomplete.css"/>
+ <stylesheet src="chrome://global/skin/autocomplete.css"/>
+ </resources>
+
+ <content>
+ <children includes="menupopup"/>
+
+ <xul:hbox class="autocomplete-textbox-container" flex="1" align="center">
+ <children includes="image|deck|stack|box">
+ <xul:image class="autocomplete-icon" allowevents="true"/>
+ </children>
+
+ <xul:hbox class="textbox-input-box" flex="1" xbl:inherits="context,tooltiptext=inputtooltiptext">
+ <children/>
+ <html:input anonid="input" class="autocomplete-textbox textbox-input"
+ allowevents="true"
+ xbl:inherits="tooltiptext=inputtooltiptext,value,type,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey,mozactionhint,userAction"/>
+ </xul:hbox>
+ <children includes="hbox"/>
+ </xul:hbox>
+
+ <xul:dropmarker class="autocomplete-history-dropmarker" allowevents="true"
+ xbl:inherits="open,enablehistory" anonid="historydropmarker"/>
+
+ <xul:popupset>
+ <xul:panel type="autocomplete" anonid="popup"
+ ignorekeys="true" noautofocus="true" level="top"
+ xbl:inherits="for=id,nomatch"/>
+ </xul:popupset>
+ </content>
+
+ <implementation implements="nsIDOMXULMenuListElement">
+
+ <constructor><![CDATA[
+ // XXX bug 90337 band-aid until we figure out what's going on here
+ if (this.value != this.mInputElt.value)
+ this.mInputElt.value = this.value;
+ delete this.value;
+
+ // listen for pastes
+ this.mInputElt.controllers.insertControllerAt(0, this.mPasteController);
+
+ // listen for menubar activation
+ window.top.addEventListener("DOMMenuBarActive", this.mMenuBarListener, true);
+
+ // set default property values
+ this.ifSetAttribute("timeout", 50);
+ this.ifSetAttribute("pastetimeout", 1000);
+ this.ifSetAttribute("maxrows", 5);
+ this.ifSetAttribute("showpopup", true);
+ this.ifSetAttribute("disableKeyNavigation", true);
+
+ // initialize the search sessions
+ if (this.hasAttribute("autocompletesearch"))
+ this.initAutoCompleteSearch();
+
+ // hack to work around lack of bottom-up constructor calling
+ if ("initialize" in this.popup)
+ this.popup.initialize();
+ ]]></constructor>
+
+ <destructor><![CDATA[
+ this.clearResults(false);
+ window.top.removeEventListener("DOMMenuBarActive", this.mMenuBarListener, true);
+ this.mInputElt.controllers.removeController(this.mPasteController);
+ ]]></destructor>
+
+ <!-- =================== nsIAutoCompleteInput =================== -->
+ <!-- XXX: This implementation is currently incomplete. -->
+
+ <!-- reference to the results popup element -->
+ <field name="popup"><![CDATA[
+ document.getAnonymousElementByAttribute(this, "anonid", "popup");
+ ]]></field>
+
+ <property name="popupOpen"
+ onget="return this.mMenuOpen;"
+ onset="if (val) this.openPopup(); else this.closePopup(); return val;"/>
+
+ <!-- option to turn off autocomplete -->
+ <property name="disableAutoComplete"
+ onset="this.setAttribute('disableautocomplete', val); return val;"
+ onget="return this.getAttribute('disableautocomplete') == 'true';"/>
+
+ <!-- if the resulting match string is not at the beginning of the typed string,
+ this will optionally autofill like this "bar |>> foobar|" -->
+ <property name="completeDefaultIndex"
+ onset="this.setAttribute('completedefaultindex', val); return val;"
+ onget="return this.getAttribute('completedefaultindex') == 'true';"/>
+
+ <!-- option for completing to the default result whenever the user hits
+ enter or the textbox loses focus -->
+ <property name="forceComplete"
+ onset="this.setAttribute('forcecomplete', val); return val;"
+ onget="return this.getAttribute('forcecomplete') == 'true';"/>
+
+ <property name="minResultsForPopup"
+ onset="this.setAttribute('minresultsforpopup', val); return val;"
+ onget="var t = this.getAttribute('minresultsforpopup'); return t ? parseInt(t) : 1;"/>
+
+ <!-- maximum number of rows to display -->
+ <property name="maxRows"
+ onset="this.setAttribute('maxrows', val); return val;"
+ onget="return parseInt(this.getAttribute('maxrows')) || 0;"/>
+
+ <!-- toggles a second column in the results list which contains
+ the string in the comment field of each autocomplete result -->
+ <property name="showCommentColumn"
+ onget="return this.getAttribute('showcommentcolumn') == 'true';">
+ <setter><![CDATA[
+ this.popup.showCommentColumn = val;
+ this.setAttribute('showcommentcolumn', val);
+ return val;
+ ]]></setter>
+ </property>
+
+ <!-- number of milliseconds after a keystroke before a search begins -->
+ <property name="timeout"
+ onset="this.setAttribute('timeout', val); return val;"
+ onget="return parseInt(this.getAttribute('timeout')) || 0;"/>
+
+ <property name="searchParam"
+ onget="return this.getAttribute('autocompletesearchparam') || '';"
+ onset="this.setAttribute('autocompletesearchparam', val); return val;"/>
+
+ <property name="searchCount" readonly="true"
+ onget="return this.sessionCount;"/>
+
+ <method name="getSearchAt">
+ <parameter name="aIndex"/>
+ <body><![CDATA[
+ var idx = -1;
+ for (var name in this.mSessions)
+ if (++idx == aIndex)
+ return name;
+
+ return null;
+ ]]></body>
+ </method>
+
+ <property name="textValue"
+ onget="return this.value;"
+ onset="this.setTextValue(val); return val;"/>
+
+ <method name="onSearchBegin">
+ <body><![CDATA[
+ this._fireEvent("searchbegin");
+ ]]></body>
+ </method>
+
+ <method name="onSearchComplete">
+ <body><![CDATA[
+ if (this.noMatch)
+ this.setAttribute("nomatch", "true");
+ else
+ this.removeAttribute("nomatch");
+
+ this._fireEvent("searchcomplete");
+ ]]></body>
+ </method>
+
+ <method name="onTextReverted">
+ <body><![CDATA[
+ return this._fireEvent("textreverted");
+ ]]></body>
+ </method>
+
+ <!-- =================== nsIDOMXULMenuListElement =================== -->
+
+ <property name="editable" readonly="true"
+ onget="return true;" />
+
+ <property name="crop"
+ onset="this.setAttribute('crop', val); return val;"
+ onget="return this.getAttribute('crop');"/>
+
+ <property name="label" readonly="true"
+ onget="return this.mInputElt.value;"/>
+
+ <property name="open"
+ onget="return this.getAttribute('open') == 'true';">
+ <setter>
+ <![CDATA[
+ var historyPopup = document.getAnonymousElementByAttribute(this, "anonid", "historydropmarker");
+ if (val) {
+ this.setAttribute('open', true);
+ historyPopup.showPopup();
+ } else {
+ this.removeAttribute('open');
+ historyPopup.hidePopup();
+ }
+ ]]>
+ </setter>
+ </property>
+
+ <!-- =================== PUBLIC PROPERTIES =================== -->
+
+ <property name="value"
+ onget="return this.mInputElt.value;">
+ <setter><![CDATA[
+ this.ignoreInputEvent = true;
+ this.mInputElt.value = val;
+ this.ignoreInputEvent = false;
+ var event = document.createEvent('Events');
+ event.initEvent('ValueChange', true, true);
+ this.mInputElt.dispatchEvent(event);
+ return val;
+ ]]></setter>
+ </property>
+
+ <property name="focused"
+ onget="return this.getAttribute('focused') == 'true';"/>
+
+ <method name="initAutoCompleteSearch">
+ <body><![CDATA[
+ var list = this.getAttribute("autocompletesearch").split(" ");
+ for (var i = 0; i < list.length; i++) {
+ var name = list[i];
+ var contractid = "@mozilla.org/autocomplete/search;1?name=" + name;
+ if (contractid in Components.classes) {
+ try {
+ this.mSessions[name] =
+ Components.classes[contractid].getService(Components.interfaces.nsIAutoCompleteSearch);
+ this.mLastResults[name] = null;
+ this.mLastRows[name] = 0;
+ ++this.sessionCount;
+ } catch (e) {
+ dump("### ERROR - unable to create search \"" + name + "\".\n");
+ }
+ } else {
+ dump("search \"" + name + "\" not found - skipping.\n");
+ }
+ }
+ ]]></body>
+ </method>
+
+ <!-- the number of sessions currently in use -->
+ <field name="sessionCount">0</field>
+
+ <!-- number of milliseconds after a paste before a search begins -->
+ <property name="pasteTimeout"
+ onset="this.setAttribute('pastetimeout', val); return val;"
+ onget="var t = parseInt(this.getAttribute('pastetimeout')); return t ? t : 0;"/>
+
+ <!-- option for filling the textbox with the best match while typing
+ and selecting the difference -->
+ <property name="autoFill"
+ onset="this.setAttribute('autofill', val); return val;"
+ onget="return this.getAttribute('autofill') == 'true';"/>
+
+ <!-- if this attribute is set, allow different style for
+ non auto-completed lines -->
+ <property name="highlightNonMatches"
+ onset="this.setAttribute('highlightnonmatches', val); return val;"
+ onget="return this.getAttribute('highlightnonmatches') == 'true';"/>
+
+ <!-- option to show the popup containing the results -->
+ <property name="showPopup"
+ onset="this.setAttribute('showpopup', val); return val;"
+ onget="return this.getAttribute('showpopup') == 'true';"/>
+
+ <!-- option to allow scrolling through the list via the tab key, rather than
+ tab moving focus out of the textbox -->
+ <property name="tabScrolling"
+ onset="this.setAttribute('tabscrolling', val); return val;"
+ onget="return this.getAttribute('tabscrolling') == 'true';"/>
+
+ <!-- option to completely ignore any blur events while
+ searches are still going on. This is useful so that nothing
+ gets autopicked if the window is required to lose focus for
+ some reason (eg in LDAP autocomplete, another window may be
+ brought up so that the user can enter a password to authenticate
+ to an LDAP server). -->
+ <property name="ignoreBlurWhileSearching"
+ onset="this.setAttribute('ignoreblurwhilesearching', val); return val;"
+ onget="return this.getAttribute('ignoreblurwhilesearching') == 'true';"/>
+
+ <!-- state which indicates the current action being performed by the user.
+ Possible values are : none, typing, scrolling -->
+ <property name="userAction"
+ onset="this.setAttribute('userAction', val); return val;"
+ onget="return this.getAttribute('userAction');"/>
+
+ <!-- state which indicates if the last search had no matches -->
+ <field name="noMatch">true</field>
+
+ <!-- state which indicates a search is currently happening -->
+ <field name="isSearching">false</field>
+
+ <!-- state which indicates a search timeout is current waiting -->
+ <property name="isWaiting"
+ onget="return this.mAutoCompleteTimer != 0;"/>
+
+ <!-- =================== PRIVATE PROPERTIES =================== -->
+
+ <field name="mSessions">({})</field>
+ <field name="mLastResults">({})</field>
+ <field name="mLastRows">({})</field>
+ <field name="mLastKeyCode">null</field>
+ <field name="mAutoCompleteTimer">0</field>
+ <field name="mMenuOpen">false</field>
+ <field name="mFireAfterSearch">false</field>
+ <field name="mFinishAfterSearch">false</field>
+ <field name="mNeedToFinish">false</field>
+ <field name="mNeedToComplete">false</field>
+ <field name="mTransientValue">false</field>
+ <field name="mView">null</field>
+ <field name="currentSearchString">""</field>
+ <field name="ignoreInputEvent">false</field>
+ <field name="oninit">null</field>
+ <field name="mDefaultMatchFilled">false</field>
+ <field name="mFirstReturn">true</field>
+ <field name="mIsPasting">false</field>
+
+ <field name="mPasteController"><![CDATA[
+ ({
+ self: this,
+ kGlobalClipboard: Components.interfaces.nsIClipboard.kGlobalClipboard,
+ supportsCommand: function(aCommand) {
+ return aCommand == "cmd_paste";
+ },
+ isCommandEnabled: function(aCommand) {
+ return aCommand == "cmd_paste" &&
+ this.self.editor.isSelectionEditable &&
+ this.self.editor.canPaste(this.kGlobalClipboard);
+ },
+ doCommand: function(aCommand) {
+ if (aCommand == "cmd_paste") {
+ this.self.mIsPasting = true;
+ this.self.editor.paste(this.kGlobalClipboard);
+ this.self.mIsPasting = false;
+ }
+ },
+ onEvent: function() {}
+ })
+ ]]></field>
+
+ <field name="mMenuBarListener"><![CDATA[
+ ({
+ self: this,
+ handleEvent: function(aEvent) {
+ try {
+ this.self.finishAutoComplete(false, false, aEvent);
+ this.self.clearTimer();
+ this.self.closePopup();
+ } catch (e) {
+ window.top.removeEventListener("DOMMenuBarActive", this, true);
+ }
+ }
+ })
+ ]]></field>
+
+ <field name="mAutoCompleteObserver"><![CDATA[
+ ({
+ self: this,
+ onSearchResult: function(aSearch, aResult) {
+ for (var name in this.self.mSessions)
+ if (this.self.mSessions[name] == aSearch)
+ this.self.processResults(name, aResult);
+ }
+ })
+ ]]></field>
+
+ <field name="mInputElt"><![CDATA[
+ document.getAnonymousElementByAttribute(this, "anonid", "input");
+ ]]></field>
+
+ <field name="mMenuAccessKey"><![CDATA[
+ Components.classes["@mozilla.org/preferences-service;1"]
+ .getService(Components.interfaces.nsIPrefBranch)
+ .getIntPref("ui.key.menuAccessKey");
+ ]]></field>
+
+ <!-- =================== PUBLIC METHODS =================== -->
+
+ <method name="getErrorAt">
+ <parameter name="aIndex"/>
+ <body><![CDATA[
+ var obj = aIndex < 0 ? null : this.convertIndexToSession(aIndex);
+ return obj && this.mLastResults[obj.session] &&
+ this.mLastResults[obj.session].errorDescription;
+ ]]></body>
+ </method>
+
+ <!-- get a value from the autocomplete results as a string via an absolute index-->
+ <method name="getResultValueAt">
+ <parameter name="aIndex"/>
+ <body><![CDATA[
+ var obj = this.convertIndexToSession(aIndex);
+ return obj ? this.getSessionValueAt(obj.session, obj.index) : null;
+ ]]></body>
+ </method>
+
+ <!-- get a value from the autocomplete results as a string from a specific session -->
+ <method name="getSessionValueAt">
+ <parameter name="aSession"/>
+ <parameter name="aIndex"/>
+ <body><![CDATA[
+ var result = this.mLastResults[aSession];
+ return result.errorDescription || result.getValueAt(aIndex);
+ ]]></body>
+ </method>
+
+ <!-- get the total number of results overall -->
+ <method name="getResultCount">
+ <body><![CDATA[
+ return this.view.rowCount;
+ ]]></body>
+ </method>
+
+ <!-- get the first session that has results -->
+ <method name="getDefaultSession">
+ <body><![CDATA[
+ for (var name in this.mLastResults) {
+ var results = this.mLastResults[name];
+ if (results && results.matchCount > 0 && !results.errorDescription)
+ return name;
+ }
+ return null;
+ ]]></body>
+ </method>
+
+ <!-- empty the cached result data and empty the results popup -->
+ <method name="clearResults">
+ <parameter name="aInvalidate"/>
+ <body><![CDATA[
+ this.clearResultData();
+ this.clearResultElements(aInvalidate);
+ ]]></body>
+ </method>
+
+ <!-- =================== PRIVATE METHODS =================== -->
+
+ <!-- ::::::::::::: session searching ::::::::::::: -->
+
+ <!-- -->
+ <method name="callListener">
+ <parameter name="me"/>
+ <parameter name="aAction"/>
+ <body><![CDATA[
+ // bail if the binding was detached or the element removed from
+ // document during the timeout
+ if (!("startLookup" in me) || !me.ownerDocument || !me.parentNode)
+ return;
+
+ me.clearTimer();
+
+ if (me.disableAutoComplete)
+ return;
+
+ switch (aAction) {
+ case "startLookup":
+ me.startLookup();
+ break;
+
+ case "stopLookup":
+ me.stopLookup();
+ break;
+ }
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="startLookup">
+ <body><![CDATA[
+ var str = this.currentSearchString;
+ if (!str) {
+ this.clearResults(false);
+ this.closePopup();
+ return;
+ }
+
+ this.isSearching = true;
+ this.mFirstReturn = true;
+ this.mSessionReturns = this.sessionCount;
+ this.mFailureItems = 0;
+ this.mDefaultMatchFilled = false; // clear out our prefill state.
+
+ // Notify the input that the search is beginning.
+ this.onSearchBegin();
+
+ // tell each session to start searching...
+ for (var name in this.mSessions)
+ try {
+ this.mSessions[name].startSearch(str, this.searchParam, this.mLastResults[name], this.mAutoCompleteObserver);
+ } catch (e) {
+ --this.mSessionReturns;
+ this.searchFailed();
+ }
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="stopLookup">
+ <body><![CDATA[
+ for (var name in this.mSessions)
+ this.mSessions[name].stopSearch();
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="processResults">
+ <parameter name="aSessionName"/>
+ <parameter name="aResults"/>
+ <body><![CDATA[
+ if (this.disableAutoComplete)
+ return;
+
+ const ACR = Components.interfaces.nsIAutoCompleteResult;
+ var status = aResults.searchResult;
+ if (status != ACR.RESULT_NOMATCH_ONGOING &&
+ status != ACR.RESULT_SUCCESS_ONGOING)
+ --this.mSessionReturns;
+
+ // check the many criteria for failure
+ if (aResults.errorDescription)
+ ++this.mFailureItems;
+ else if (status == ACR.RESULT_IGNORED ||
+ status == ACR.RESULT_FAILURE ||
+ status == ACR.RESULT_NOMATCH ||
+ status == ACR.RESULT_NOMATCH_ONGOING ||
+ aResults.matchCount == 0 ||
+ aResults.searchString != this.currentSearchString)
+ {
+ this.mLastResults[aSessionName] = null;
+ if (this.mFirstReturn)
+ this.clearResultElements(false);
+ this.mFirstReturn = false;
+ this.searchFailed();
+ return;
+ }
+
+ if (this.mFirstReturn) {
+ if (this.view.mTree)
+ this.view.mTree.beginUpdateBatch();
+ this.clearResultElements(false); // clear results, but don't repaint yet
+ }
+
+ // always call openPopup...we may not have opened it
+ // if a previous search session didn't return enough search results.
+ // it's smart and doesn't try to open itself multiple times...
+ // be sure to add our result elements before calling openPopup as we need
+ // to know the total # of results found so far.
+ this.addResultElements(aSessionName, aResults);
+
+ this.autoFillInput(aSessionName, aResults, false);
+ if (this.mFirstReturn && this.view.mTree)
+ this.view.mTree.endUpdateBatch();
+ this.openPopup();
+ this.mFirstReturn = false;
+
+ // if this is the last session to return...
+ if (this.mSessionReturns == 0)
+ this.postSearchCleanup();
+
+ if (this.mFinishAfterSearch)
+ this.finishAutoComplete(false, this.mFireAfterSearch, null);
+ ]]></body>
+ </method>
+
+ <!-- called each time a search fails, except when failure items need
+ to be displayed. If all searches have failed, clear the list
+ and close the popup -->
+ <method name="searchFailed">
+ <body><![CDATA[
+ // if all searches are done and they all failed...
+ if (this.mSessionReturns == 0 && this.getResultCount() == 0) {
+ if (this.minResultsForPopup == 0) {
+ this.clearResults(true); // clear data and repaint empty
+ this.openPopup();
+ } else {
+ this.closePopup();
+ }
+ }
+
+ // if it's the last session to return, time to clean up...
+ if (this.mSessionReturns == 0)
+ this.postSearchCleanup();
+ ]]></body>
+ </method>
+
+ <!-- does some stuff after a search is done (success or failure) -->
+ <method name="postSearchCleanup">
+ <body><![CDATA[
+ this.isSearching = false;
+
+ // figure out if there are no matches in all search sessions
+ var failed = true;
+ for (var name in this.mSessions) {
+ if (this.mLastResults[name])
+ failed = this.mLastResults[name].errorDescription ||
+ this.mLastResults[name].matchCount == 0;
+ if (!failed)
+ break;
+ }
+ this.noMatch = failed;
+
+ // if we have processed all of our searches, and none of them gave us a default index,
+ // then we should try to auto fill the input field with the first match.
+ // note: autoFillInput is smart enough to kick out if we've already prefilled something...
+ if (!this.noMatch) {
+ var defaultSession = this.getDefaultSession();
+ if (defaultSession)
+ this.autoFillInput(defaultSession, this.mLastResults[defaultSession], true);
+ }
+
+ // Notify the input that the search is complete.
+ this.onSearchComplete();
+ ]]></body>
+ </method>
+
+ <!-- when the focus exits the widget or user hits return,
+ determine what value to leave in the textbox -->
+ <method name="finishAutoComplete">
+ <parameter name="aForceComplete"/>
+ <parameter name="aFireTextCommand"/>
+ <parameter name="aTriggeringEvent"/>
+ <body><![CDATA[
+ this.mFinishAfterSearch = false;
+ this.mFireAfterSearch = false;
+ if (this.mNeedToFinish && !this.disableAutoComplete) {
+ // set textbox value to either override value, or default search result
+ var val = this.popup.overrideValue;
+ if (val) {
+ this.setTextValue(val);
+ this.mNeedToFinish = false;
+ } else if (this.mTransientValue ||
+ !(this.forceComplete ||
+ (aForceComplete &&
+ this.mDefaultMatchFilled &&
+ this.mNeedToComplete))) {
+ this.mNeedToFinish = false;
+ } else if (this.isWaiting) {
+ // if the user typed, the search results are out of date, so let
+ // the search finish, and tell it to come back here when it's done
+ this.mFinishAfterSearch = true;
+ this.mFireAfterSearch = aFireTextCommand;
+ return;
+ } else {
+ // we want to use the default item index for the first session which gave us a valid
+ // default item index...
+ for (var name in this.mLastResults) {
+ var results = this.mLastResults[name];
+ if (results && results.matchCount > 0 &&
+ !results.errorDescription && results.defaultIndex != -1)
+ {
+ val = results.getValueAt(results.defaultIndex);
+ this.setTextValue(val);
+ this.mDefaultMatchFilled = true;
+ this.mNeedToFinish = false;
+ break;
+ }
+ }
+
+ if (this.mNeedToFinish) {
+ // if a search is happening at this juncture, bail out of this function
+ // and let the search finish, and tell it to come back here when it's done
+ if (this.isSearching) {
+ this.mFinishAfterSearch = true;
+ this.mFireAfterSearch = aFireTextCommand;
+ return;
+ }
+
+ this.mNeedToFinish = false;
+ var defaultSession = this.getDefaultSession();
+ if (defaultSession)
+ {
+ // preselect the first one
+ var first = this.getSessionValueAt(defaultSession, 0);
+ this.setTextValue(first);
+ this.mDefaultMatchFilled = true;
+ }
+ }
+ }
+
+ this.stopLookup();
+
+ this.closePopup();
+ }
+
+ this.mNeedToComplete = false;
+ this.clearTimer();
+
+ if (aFireTextCommand)
+ this._fireEvent("textentered", this.userAction, aTriggeringEvent);
+ ]]></body>
+ </method>
+
+ <!-- when the user clicks an entry in the autocomplete popup -->
+ <method name="onResultClick">
+ <body><![CDATA[
+ // set textbox value to either override value, or the clicked result
+ var errItem = this.getErrorAt(this.popup.selectedIndex);
+ var val = this.popup.overrideValue;
+ if (val)
+ this.setTextValue(val);
+ else if (this.popup.selectedIndex != -1) {
+ if (errItem) {
+ this.setTextValue(this.currentSearchString);
+ this.mTransientValue = true;
+ } else {
+ this.setTextValue(this.getResultValueAt(
+ this.popup.selectedIndex));
+ }
+ }
+
+ this.mNeedToFinish = false;
+ this.mNeedToComplete = false;
+
+ this.closePopup();
+
+ this.currentSearchString = "";
+
+ if (errItem)
+ this._fireEvent("errorcommand", errItem);
+ this._fireEvent("textentered", "clicking");
+ ]]></body>
+ </method>
+
+ <!-- when the user hits escape, revert the previously typed value in the textbox -->
+ <method name="undoAutoComplete">
+ <body><![CDATA[
+ var val = this.currentSearchString;
+
+ var ok = this.onTextReverted();
+ if ((ok || ok == undefined) && val)
+ this.setTextValue(val);
+
+ this.userAction = "typing";
+
+ this.currentSearchString = this.value;
+ this.mNeedToComplete = false;
+ ]]></body>
+ </method>
+
+ <!-- convert an absolute result index into a session name/index pair -->
+ <method name="convertIndexToSession">
+ <parameter name="aIndex"/>
+ <body><![CDATA[
+ for (var name in this.mLastRows) {
+ if (aIndex < this.mLastRows[name])
+ return { session: name, index: aIndex };
+ aIndex -= this.mLastRows[name];
+ }
+ return null;
+ ]]></body>
+ </method>
+
+ <!-- ::::::::::::: user input handling ::::::::::::: -->
+
+ <!-- -->
+ <method name="processInput">
+ <body><![CDATA[
+ // stop current lookup in case it's async.
+ this.stopLookup();
+ // stop the queued up lookup on a timer
+ this.clearTimer();
+
+ if (this.disableAutoComplete)
+ return;
+
+ this.userAction = "typing";
+ this.mFinishAfterSearch = false;
+ this.mNeedToFinish = true;
+ this.mTransientValue = false;
+ this.mNeedToComplete = true;
+ var str = this.value;
+ this.currentSearchString = str;
+ this.popup.clearSelection();
+
+ var timeout = this.mIsPasting ? this.pasteTimeout : this.timeout;
+ this.mAutoCompleteTimer = setTimeout(this.callListener, timeout, this, "startLookup");
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="processKeyPress">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ this.mLastKeyCode = aEvent.keyCode;
+
+ var killEvent = false;
+
+ switch (aEvent.keyCode) {
+ case KeyEvent.DOM_VK_TAB:
+ if (this.tabScrolling) {
+ // don't kill this event if alt-tab or ctrl-tab is hit
+ if (!aEvent.altKey && !aEvent.ctrlKey) {
+ killEvent = this.mMenuOpen;
+ if (killEvent)
+ this.keyNavigation(aEvent);
+ }
+ }
+ break;
+
+ case KeyEvent.DOM_VK_RETURN:
+
+ // if this is a failure item, save it for fireErrorCommand
+ var errItem = this.getErrorAt(this.popup.selectedIndex);
+
+ killEvent = this.mMenuOpen;
+ this.finishAutoComplete(true, true, aEvent);
+ this.closePopup();
+ if (errItem) {
+ this._fireEvent("errorcommand", errItem);
+ }
+ break;
+
+ case KeyEvent.DOM_VK_ESCAPE:
+ this.clearTimer();
+ killEvent = this.mMenuOpen;
+ this.undoAutoComplete();
+ this.closePopup();
+ break;
+
+ case KeyEvent.DOM_VK_LEFT:
+ case KeyEvent.DOM_VK_RIGHT:
+ case KeyEvent.DOM_VK_HOME:
+ case KeyEvent.DOM_VK_END:
+ this.finishAutoComplete(true, false, aEvent);
+ this.clearTimer();
+ this.closePopup();
+ break;
+
+ case KeyEvent.DOM_VK_DOWN:
+ if (!aEvent.altKey) {
+ this.clearTimer();
+ killEvent = this.keyNavigation(aEvent);
+ break;
+ }
+ // Alt+Down falls through to history popup toggling code
+
+ case KeyEvent.DOM_VK_F4:
+ if (!aEvent.ctrlKey && !aEvent.shiftKey && this.getAttribute("enablehistory") == "true") {
+ var historyPopup = document.getAnonymousElementByAttribute(this, "anonid", "historydropmarker");
+ if (historyPopup)
+ historyPopup.showPopup();
+ else
+ historyPopup.hidePopup();
+ }
+ break;
+ case KeyEvent.DOM_VK_PAGE_UP:
+ case KeyEvent.DOM_VK_PAGE_DOWN:
+ case KeyEvent.DOM_VK_UP:
+ if (!aEvent.ctrlKey && !aEvent.metaKey) {
+ this.clearTimer();
+ killEvent = this.keyNavigation(aEvent);
+ }
+ break;
+
+ case KeyEvent.DOM_VK_BACK_SPACE:
+ if (!aEvent.ctrlKey && !aEvent.altKey && !aEvent.shiftKey &&
+ this.selectionStart == this.currentSearchString.length &&
+ this.selectionEnd == this.value.length &&
+ this.mDefaultMatchFilled) {
+ this.mDefaultMatchFilled = false;
+ this.value = this.currentSearchString;
+ }
+
+ if (!/Mac/.test(navigator.platform))
+ break;
+ case KeyEvent.DOM_VK_DELETE:
+ if (/Mac/.test(navigator.platform) && !aEvent.shiftKey)
+ break;
+
+ if (this.mMenuOpen && this.popup.selectedIndex != -1) {
+ var obj = this.convertIndexToSession(this.popup.selectedIndex);
+ if (obj) {
+ var result = this.mLastResults[obj.session];
+ if (!result.errorDescription) {
+ var count = result.matchCount;
+ result.removeValueAt(obj.index, true);
+ this.view.updateResults(this.popup.selectedIndex, result.matchCount - count);
+ killEvent = true;
+ }
+ }
+ }
+ break;
+ }
+
+ if (killEvent) {
+ aEvent.preventDefault();
+ aEvent.stopPropagation();
+ }
+
+ return true;
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="processStartComposition">
+ <body><![CDATA[
+ this.finishAutoComplete(false, false, null);
+ this.clearTimer();
+ this.closePopup();
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="keyNavigation">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ var k = aEvent.keyCode;
+ if (k == KeyEvent.DOM_VK_TAB ||
+ k == KeyEvent.DOM_VK_UP || k == KeyEvent.DOM_VK_DOWN ||
+ k == KeyEvent.DOM_VK_PAGE_UP || k == KeyEvent.DOM_VK_PAGE_DOWN)
+ {
+ if (!this.mMenuOpen) {
+ // Original xpfe style was to allow the up and down keys to have
+ // their default Mac action if the popup could not be opened.
+ // For compatibility for toolkit we now have to predict which
+ // keys have a default action that we can always allow to fire.
+ if (/Mac/.test(navigator.platform) &&
+ ((k == KeyEvent.DOM_VK_UP &&
+ (this.selectionStart != 0 ||
+ this.selectionEnd != 0)) ||
+ (k == KeyEvent.DOM_VK_DOWN &&
+ (this.selectionStart != this.value.length ||
+ this.selectionEnd != this.value.length))))
+ return false;
+ if (this.currentSearchString != this.value) {
+ this.processInput();
+ return true;
+ }
+ if (this.view.rowCount < this.minResultsForPopup)
+ return true; // used to be false, see above
+
+ this.mNeedToFinish = true;
+ this.openPopup();
+ return true;
+ }
+
+ this.userAction = "scrolling";
+ this.mNeedToComplete = false;
+
+ var reverse = k == KeyEvent.DOM_VK_TAB && aEvent.shiftKey ||
+ k == KeyEvent.DOM_VK_UP ||
+ k == KeyEvent.DOM_VK_PAGE_UP;
+ var page = k == KeyEvent.DOM_VK_PAGE_UP ||
+ k == KeyEvent.DOM_VK_PAGE_DOWN;
+ var selected = this.popup.selectBy(reverse, page);
+
+ // determine which value to place in the textbox
+ this.ignoreInputEvent = true;
+ if (selected != -1) {
+ if (this.getErrorAt(selected)) {
+ if (this.currentSearchString)
+ this.setTextValue(this.currentSearchString);
+ } else {
+ this.setTextValue(this.getResultValueAt(selected));
+ }
+ this.mTransientValue = true;
+ } else {
+ if (this.currentSearchString)
+ this.setTextValue(this.currentSearchString);
+ this.mTransientValue = false;
+ }
+
+ // move cursor to the end
+ this.mInputElt.setSelectionRange(this.value.length, this.value.length);
+ this.ignoreInputEvent = false;
+ }
+ return true;
+ ]]></body>
+ </method>
+
+ <!-- while the user is typing, fill the textbox with the "default" value
+ if one can be assumed, and select the end of the text -->
+ <method name="autoFillInput">
+ <parameter name="aSessionName"/>
+ <parameter name="aResults"/>
+ <parameter name="aUseFirstMatchIfNoDefault"/>
+ <body><![CDATA[
+ if (this.mInputElt.selectionEnd < this.currentSearchString.length ||
+ this.mDefaultMatchFilled)
+ return;
+
+ if (!this.mFinishAfterSearch &&
+ (this.autoFill || this.completeDefaultIndex) &&
+ this.mLastKeyCode != KeyEvent.DOM_VK_BACK_SPACE &&
+ this.mLastKeyCode != KeyEvent.DOM_VK_DELETE) {
+ var indexToUse = aResults.defaultIndex;
+ if (aUseFirstMatchIfNoDefault && indexToUse == -1)
+ indexToUse = 0;
+
+ if (indexToUse != -1) {
+ var resultValue = this.getSessionValueAt(aSessionName, indexToUse);
+ var match = resultValue.toLowerCase();
+ var entry = this.currentSearchString.toLowerCase();
+ this.ignoreInputEvent = true;
+ if (match.indexOf(entry) == 0) {
+ var endPoint = this.value.length;
+ this.setTextValue(this.value + resultValue.substr(endPoint));
+ this.mInputElt.setSelectionRange(endPoint, this.value.length);
+ } else {
+ if (this.completeDefaultIndex) {
+ this.setTextValue(this.value + " >> " + resultValue);
+ this.mInputElt.setSelectionRange(entry.length, this.value.length);
+ } else {
+ var postIndex = resultValue.indexOf(this.value);
+ if (postIndex >= 0) {
+ var startPt = this.value.length;
+ this.setTextValue(this.value +
+ resultValue.substr(startPt+postIndex));
+ this.mInputElt.setSelectionRange(startPt, this.value.length);
+ }
+ }
+ }
+ this.mNeedToComplete = true;
+ this.ignoreInputEvent = false;
+ this.mDefaultMatchFilled = true;
+ }
+ }
+ ]]></body>
+ </method>
+
+ <!-- ::::::::::::: popup and tree ::::::::::::: -->
+
+ <!-- -->
+ <method name="openPopup">
+ <body><![CDATA[
+ if (!this.mMenuOpen && this.focused &&
+ (this.getResultCount() >= this.minResultsForPopup ||
+ this.mFailureItems)) {
+ var w = this.boxObject.width;
+ if (w != this.popup.boxObject.width)
+ this.popup.setAttribute("width", w);
+ this.popup.showPopup(this, -1, -1, "popup", "bottomleft", "topleft");
+ this.mMenuOpen = true;
+ }
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="closePopup">
+ <body><![CDATA[
+ if (this.popup && this.mMenuOpen) {
+ this.popup.hidePopup();
+ this.mMenuOpen = false;
+ }
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="addResultElements">
+ <parameter name="aSession"/>
+ <parameter name="aResults"/>
+ <body><![CDATA[
+ var count = aResults.errorDescription ? 1 : aResults.matchCount;
+ if (this.focused && this.showPopup) {
+ var row = 0;
+ for (var name in this.mSessions) {
+ row += this.mLastRows[name];
+ if (name == aSession)
+ break;
+ }
+ this.view.updateResults(row, count - this.mLastRows[name]);
+ this.popup.adjustHeight();
+ }
+ this.mLastResults[aSession] = aResults;
+ this.mLastRows[aSession] = count;
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="clearResultElements">
+ <parameter name="aInvalidate"/>
+ <body><![CDATA[
+ for (var name in this.mSessions)
+ this.mLastRows[name] = 0;
+ this.view.clearResults();
+ if (aInvalidate)
+ this.popup.adjustHeight();
+
+ this.noMatch = true;
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="setTextValue">
+ <parameter name="aValue"/>
+ <body><![CDATA[
+ this.value = aValue;
+
+ // Completing a result should simulate the user typing the result,
+ // so fire an input event.
+ var evt = document.createEvent("UIEvents");
+ evt.initUIEvent("input", true, false, window, 0);
+ var oldIgnoreInput = this.ignoreInputEvent;
+ this.ignoreInputEvent = true;
+ this.dispatchEvent(evt);
+ this.ignoreInputEvent = oldIgnoreInput;
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="clearResultData">
+ <body><![CDATA[
+ for (var name in this.mSessions)
+ this.mLastResults[name] = null;
+ ]]></body>
+ </method>
+
+ <!-- ::::::::::::: miscellaneous ::::::::::::: -->
+
+ <!-- -->
+ <method name="ifSetAttribute">
+ <parameter name="aAttr"/>
+ <parameter name="aVal"/>
+ <body><![CDATA[
+ if (!this.hasAttribute(aAttr))
+ this.setAttribute(aAttr, aVal);
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="clearTimer">
+ <body><![CDATA[
+ if (this.mAutoCompleteTimer) {
+ clearTimeout(this.mAutoCompleteTimer);
+ this.mAutoCompleteTimer = 0;
+ }
+ ]]></body>
+ </method>
+
+ <!-- ::::::::::::: event dispatching ::::::::::::: -->
+
+ <method name="_fireEvent">
+ <parameter name="aEventType"/>
+ <parameter name="aEventParam"/>
+ <parameter name="aTriggeringEvent"/>
+ <body>
+ <![CDATA[
+ var noCancel = true;
+ // handle any xml attribute event handlers
+ var handler = this.getAttribute("on"+aEventType);
+ if (handler) {
+ var fn = new Function("eventParam", "domEvent", handler);
+ var returned = fn.apply(this, [aEventParam, aTriggeringEvent]);
+ if (returned == false)
+ noCancel = false;
+ }
+
+ return noCancel;
+ ]]>
+ </body>
+ </method>
+
+ <!-- =================== TREE VIEW =================== -->
+
+ <field name="view"><![CDATA[
+ ({
+ mTextbox: this,
+ mTree: null,
+ mSelection: null,
+ mRowCount: 0,
+
+ clearResults: function()
+ {
+ var oldCount = this.mRowCount;
+ this.mRowCount = 0;
+
+ if (this.mTree) {
+ this.mTree.rowCountChanged(0, -oldCount);
+ this.mTree.scrollToRow(0);
+ }
+ },
+
+ updateResults: function(aRow, aCount)
+ {
+ this.mRowCount += aCount;
+
+ if (this.mTree)
+ this.mTree.rowCountChanged(aRow, aCount);
+ },
+
+ //////////////////////////////////////////////////////////
+ // nsIAutoCompleteController interface
+
+ // this is the only method required by the treebody mouseup handler
+ handleEnter: function(aIsPopupSelection) {
+ this.mTextbox.onResultClick();
+ },
+
+ //////////////////////////////////////////////////////////
+ // nsITreeView interface
+
+ get rowCount() {
+ return this.mRowCount;
+ },
+
+ get selection() {
+ return this.mSelection;
+ },
+
+ set selection(aVal) {
+ return this.mSelection = aVal;
+ },
+
+ setTree: function(aTree)
+ {
+ this.mTree = aTree;
+ },
+
+ getCellText: function(aRow, aCol)
+ {
+ for (var name in this.mTextbox.mSessions) {
+ if (aRow < this.mTextbox.mLastRows[name]) {
+ var result = this.mTextbox.mLastResults[name];
+ switch (aCol.id) {
+ case "treecolAutoCompleteValue":
+ return result.errorDescription || result.getLabelAt(aRow);
+ case "treecolAutoCompleteComment":
+ if (!result.errorDescription)
+ return result.getCommentAt(aRow);
+ default:
+ return "";
+ }
+ }
+ aRow -= this.mTextbox.mLastRows[name];
+ }
+ return "";
+ },
+
+ getRowProperties: function(aIndex)
+ {
+ return "";
+ },
+
+ getCellProperties: function(aIndex, aCol)
+ {
+ // for the value column, append nsIAutoCompleteItem::className
+ // to the property list so that we can style this column
+ // using that property
+ if (aCol.id == "treecolAutoCompleteValue") {
+ for (var name in this.mTextbox.mSessions) {
+ if (aIndex < this.mTextbox.mLastRows[name]) {
+ var result = this.mTextbox.mLastResults[name];
+ if (result.errorDescription)
+ return "";
+ return result.getStyleAt(aIndex);
+ }
+ aIndex -= this.mTextbox.mLastRows[name];
+ }
+ }
+ return "";
+ },
+
+ getColumnProperties: function(aCol)
+ {
+ return "";
+ },
+
+ getImageSrc: function(aRow, aCol)
+ {
+ if (aCol.id == "treecolAutoCompleteValue") {
+ for (var name in this.mTextbox.mSessions) {
+ if (aRow < this.mTextbox.mLastRows[name]) {
+ var result = this.mTextbox.mLastResults[name];
+ if (result.errorDescription)
+ return "";
+ return result.getImageAt(aRow);
+ }
+ aRow -= this.mTextbox.mLastRows[name];
+ }
+ }
+ return "";
+ },
+
+ getParentIndex: function(aRowIndex) { },
+ hasNextSibling: function(aRowIndex, aAfterIndex) { },
+ getLevel: function(aIndex) {},
+ getProgressMode: function(aRow, aCol) {},
+ getCellValue: function(aRow, aCol) {},
+ isContainer: function(aIndex) {},
+ isContainerOpen: function(aIndex) {},
+ isContainerEmpty: function(aIndex) {},
+ isSeparator: function(aIndex) {},
+ isSorted: function() {},
+ toggleOpenState: function(aIndex) {},
+ selectionChanged: function() {},
+ cycleHeader: function(aCol) {},
+ cycleCell: function(aRow, aCol) {},
+ isEditable: function(aRow, aCol) {},
+ isSelectable: function(aRow, aCol) {},
+ setCellValue: function(aRow, aCol, aValue) {},
+ setCellText: function(aRow, aCol, aValue) {},
+ performAction: function(aAction) {},
+ performActionOnRow: function(aAction, aRow) {},
+ performActionOnCell: function(aAction, aRow, aCol) {}
+ });
+ ]]></field>
+
+ </implementation>
+
+ <handlers>
+ <handler event="input"
+ action="if (!this.ignoreInputEvent) this.processInput();"/>
+
+ <handler event="keypress" phase="capturing"
+ action="return this.processKeyPress(event);"/>
+
+ <handler event="compositionstart" phase="capturing"
+ action="this.processStartComposition();"/>
+
+ <handler event="focus" phase="capturing"
+ action="this.userAction = 'typing';"/>
+
+ <handler event="blur" phase="capturing"
+ action="if ( !(this.ignoreBlurWhileSearching &amp;&amp; this.isSearching) ) {this.userAction = 'none'; this.finishAutoComplete(false, false, event);}"/>
+
+ <handler event="mousedown" phase="capturing"
+ action="if ( !this.mMenuOpen ) this.finishAutoComplete(false, false, event);"/>
+ </handlers>
+ </binding>
+
+ <binding id="autocomplete-result-popup" extends="chrome://global/content/bindings/popup.xml#popup">
+ <resources>
+ <stylesheet src="chrome://communicator/content/autocomplete.css"/>
+ <stylesheet src="chrome://global/skin/autocomplete.css"/>
+ </resources>
+
+ <content ignorekeys="true" level="top">
+ <xul:tree anonid="tree" class="autocomplete-tree plain" flex="1">
+ <xul:treecols anonid="treecols">
+ <xul:treecol class="autocomplete-treecol" id="treecolAutoCompleteValue" flex="2"/>
+ <xul:treecol class="autocomplete-treecol" id="treecolAutoCompleteComment" flex="1" hidden="true"/>
+ </xul:treecols>
+ <xul:treechildren anonid="treebody" class="autocomplete-treebody"/>
+ </xul:tree>
+ </content>
+
+ <implementation implements="nsIAutoCompletePopup">
+ <constructor><![CDATA[
+ if (this.textbox && this.textbox.view)
+ this.initialize();
+ ]]></constructor>
+
+ <destructor><![CDATA[
+ if (this.view)
+ this.tree.view = null;
+ ]]></destructor>
+
+ <field name="textbox">
+ document.getBindingParent(this);
+ </field>
+
+ <field name="tree">
+ document.getAnonymousElementByAttribute(this, "anonid", "tree");
+ </field>
+
+ <field name="treecols">
+ document.getAnonymousElementByAttribute(this, "anonid", "treecols");
+ </field>
+
+ <field name="treebody">
+ document.getAnonymousElementByAttribute(this, "anonid", "treebody");
+ </field>
+
+ <field name="view">
+ null
+ </field>
+
+ <!-- Setting tree.view doesn't always immediately create a selection,
+ so we ensure the selection by asking the tree for the view. Note:
+ this.view.selection is quicker if we know the selection exists. -->
+ <property name="selection" onget="return this.tree.view.selection;"/>
+
+ <property name="pageCount"
+ onget="return this.tree.treeBoxObject.getPageLength();"/>
+
+ <field name="maxRows">0</field>
+ <field name="mLastRows">0</field>
+
+ <method name="initialize">
+ <body><![CDATA[
+ this.showCommentColumn = this.textbox.showCommentColumn;
+ this.tree.view = this.textbox.view;
+ this.view = this.textbox.view;
+ this.maxRows = this.textbox.maxRows;
+ ]]></body>
+ </method>
+
+ <property name="showCommentColumn"
+ onget="return !this.treecols.lastChild.hidden;"
+ onset="this.treecols.lastChild.hidden = !val; return val;"/>
+
+ <method name="adjustHeight">
+ <body><![CDATA[
+ // detect the desired height of the tree
+ var bx = this.tree.treeBoxObject;
+ var view = this.view;
+ var rows = this.maxRows || 6;
+ if (!view.rowCount || (rows && view.rowCount < rows))
+ rows = view.rowCount;
+
+ var height = rows * bx.rowHeight;
+
+ if (height == 0)
+ this.tree.setAttribute("collapsed", "true");
+ else {
+ if (this.tree.hasAttribute("collapsed"))
+ this.tree.removeAttribute("collapsed");
+ this.tree.setAttribute("height", height);
+ }
+ ]]></body>
+ </method>
+
+ <method name="clearSelection">
+ <body>
+ this.selection.clearSelection();
+ </body>
+ </method>
+
+ <method name="getNextIndex">
+ <parameter name="aReverse"/>
+ <parameter name="aPage"/>
+ <parameter name="aIndex"/>
+ <parameter name="aMaxRow"/>
+ <body><![CDATA[
+ if (aMaxRow < 0)
+ return -1;
+
+ if (aIndex == -1)
+ return aReverse ? aMaxRow : 0;
+ if (aIndex == (aReverse ? 0 : aMaxRow))
+ return -1;
+
+ var amount = aPage ? this.pageCount - 1 : 1;
+ aIndex = aReverse ? aIndex - amount : aIndex + amount;
+ if (aIndex > aMaxRow)
+ return aMaxRow;
+ if (aIndex < 0)
+ return 0;
+ return aIndex;
+ ]]></body>
+ </method>
+
+ <!-- =================== nsIAutoCompletePopup =================== -->
+
+ <field name="input">
+ null
+ </field>
+
+ <!-- This property is meant to be overriden by bindings extending
+ this one. When the user selects an item from the list by
+ hitting enter or clicking, this method can set the value
+ of the textbox to a different value if it wants to. -->
+ <property name="overrideValue" readonly="true" onget="return null;"/>
+
+ <property name="selectedIndex">
+ <getter>
+ if (!this.view || !this.selection.count)
+ return -1;
+ var start = {}, end = {};
+ this.view.selection.getRangeAt(0, start, end);
+ return start.value;
+ </getter>
+ <setter>
+ if (this.view) {
+ this.selection.select(val);
+ if (val >= 0) {
+ this.view.selection.currentIndex = -1;
+ this.tree.treeBoxObject.ensureRowIsVisible(val);
+ }
+ }
+ return val;
+ </setter>
+ </property>
+
+ <property name="popupOpen" onget="return !!this.input;" readonly="true"/>
+
+ <method name="openAutocompletePopup">
+ <parameter name="aInput"/>
+ <parameter name="aElement"/>
+ <body><![CDATA[
+ if (!this.input) {
+ this.tree.view = aInput.controller;
+ this.view = this.tree.view;
+ this.showCommentColumn = aInput.showCommentColumn;
+ this.maxRows = aInput.maxRows;
+ this.invalidate();
+
+ var viewer = aElement
+ .ownerDocument
+ .defaultView
+ .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIWebNavigation)
+ .QueryInterface(Components.interfaces.nsIDocShell)
+ .contentViewer;
+ var rect = aElement.getBoundingClientRect();
+ var width = Math.round((rect.right - rect.left) * viewer.fullZoom);
+ this.setAttribute("width", width > 100 ? width : 100);
+ // Adjust the direction (which is not inherited) of the autocomplete
+ // popup list, based on the textbox direction. (Bug 707039)
+ this.style.direction = aElement.ownerDocument.defaultView
+ .getComputedStyle(aElement)
+ .direction;
+ this.popupBoxObject.setConsumeRollupEvent(aInput.consumeRollupEvent
+ ? PopupBoxObject.ROLLUP_CONSUME
+ : PopupBoxObject.ROLLUP_NO_CONSUME);
+ this.openPopup(aElement, "after_start", 0, 0, false, false);
+ if (this.state != "closed")
+ this.input = aInput;
+ }
+ ]]></body>
+ </method>
+
+ <method name="closePopup">
+ <body>
+ this.hidePopup();
+ </body>
+ </method>
+
+ <method name="invalidate">
+ <body>
+ if (this.view)
+ this.adjustHeight();
+ this.tree.treeBoxObject.invalidate();
+ </body>
+ </method>
+
+ <method name="selectBy">
+ <parameter name="aReverse"/>
+ <parameter name="aPage"/>
+ <body><![CDATA[
+ try {
+ return this.selectedIndex = this.getNextIndex(aReverse, aPage, this.selectedIndex, this.view.rowCount - 1);
+ } catch (ex) {
+ // do nothing - occasionally timer-related js errors happen here
+ // e.g. "this.selectedIndex has no properties", when you type fast and hit a
+ // navigation key before this popup has opened
+ return -1;
+ }
+ ]]></body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="popupshowing">
+ if (this.textbox)
+ this.textbox.mMenuOpen = true;
+ </handler>
+
+ <handler event="popuphiding">
+ if (this.textbox)
+ this.textbox.mMenuOpen = false;
+ this.clearSelection();
+ this.input = null;
+ </handler>
+ </handlers>
+ </binding>
+
+ <binding id="autocomplete-treebody">
+ <implementation>
+ <field name="popup">document.getBindingParent(this);</field>
+
+ <field name="mLastMoveTime">Date.now()</field>
+ </implementation>
+
+ <handlers>
+ <handler event="mouseout" action="this.popup.selectedIndex = -1;"/>
+
+ <handler event="mouseup"><![CDATA[
+ var rc = this.parentNode.treeBoxObject.getRowAt(event.clientX, event.clientY);
+ if (rc != -1) {
+ this.popup.selectedIndex = rc;
+ this.popup.view.handleEnter(true);
+ }
+ ]]></handler>
+
+ <handler event="mousemove"><![CDATA[
+ if (Date.now() - this.mLastMoveTime > 30) {
+ var rc = this.parentNode.treeBoxObject.getRowAt(event.clientX, event.clientY);
+ if (rc != -1 && rc != this.popup.selectedIndex)
+ this.popup.selectedIndex = rc;
+ this.mLastMoveTime = Date.now();
+ }
+ ]]></handler>
+ </handlers>
+ </binding>
+
+ <binding id="autocomplete-history-popup"
+ extends="chrome://global/content/bindings/popup.xml#popup-scrollbars">
+ <resources>
+ <stylesheet src="chrome://communicator/content/autocomplete.css"/>
+ <stylesheet src="chrome://global/skin/autocomplete.css"/>
+ </resources>
+
+ <implementation>
+ <method name="removeOpenAttribute">
+ <parameter name="parentNode"/>
+ <body><![CDATA[
+ parentNode.removeAttribute("open");
+ ]]></body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="popuphiding"><![CDATA[
+ setTimeout(this.removeOpenAttribute, 0, this.parentNode);
+ ]]></handler>
+ </handlers>
+ </binding>
+
+ <binding id="history-dropmarker" extends="chrome://global/content/bindings/general.xml#dropmarker">
+
+ <implementation>
+ <method name="showPopup">
+ <body><![CDATA[
+ var textbox = document.getBindingParent(this);
+ var kids = textbox.getElementsByClassName("autocomplete-history-popup");
+ if (kids.item(0) && textbox.getAttribute("open") != "true") { // Open history popup
+ var w = textbox.boxObject.width;
+ if (w != kids[0].boxObject.width)
+ kids[0].width = w;
+ kids[0].showPopup(textbox, -1, -1, "popup", "bottomleft", "topleft");
+ textbox.setAttribute("open", "true");
+ }
+ ]]></body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="mousedown"><![CDATA[
+ this.showPopup();
+ ]]></handler>
+ </handlers>
+ </binding>
+
+</bindings>
diff --git a/xpfe/components/build/moz.build b/xpfe/components/build/moz.build
new file mode 100644
index 000000000..3d02e0409
--- /dev/null
+++ b/xpfe/components/build/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+SOURCES += [
+ 'nsModule.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+ '../directory',
+]
diff --git a/xpfe/components/build/nsModule.cpp b/xpfe/components/build/nsModule.cpp
new file mode 100644
index 000000000..43966f94d
--- /dev/null
+++ b/xpfe/components/build/nsModule.cpp
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ModuleUtils.h"
+#include "nsDirectoryViewer.h"
+#include "rdf.h"
+#include "nsRDFCID.h"
+#include "nsCURILoader.h"
+
+// Factory constructors
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsHTTPIndex, Init)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsDirectoryViewerFactory)
+
+NS_DEFINE_NAMED_CID(NS_DIRECTORYVIEWERFACTORY_CID);
+NS_DEFINE_NAMED_CID(NS_HTTPINDEX_SERVICE_CID);
+
+static const mozilla::Module::CIDEntry kXPFECIDs[] = {
+ { &kNS_DIRECTORYVIEWERFACTORY_CID, false, nullptr, nsDirectoryViewerFactoryConstructor },
+ { &kNS_HTTPINDEX_SERVICE_CID, false, nullptr, nsHTTPIndexConstructor },
+ { nullptr }
+};
+
+static const mozilla::Module::ContractIDEntry kXPFEContracts[] = {
+ { "@mozilla.org/xpfe/http-index-format-factory-constructor", &kNS_DIRECTORYVIEWERFACTORY_CID },
+ { NS_HTTPINDEX_SERVICE_CONTRACTID, &kNS_HTTPINDEX_SERVICE_CID },
+ { NS_HTTPINDEX_DATASOURCE_CONTRACTID, &kNS_HTTPINDEX_SERVICE_CID },
+ { nullptr }
+};
+
+static const mozilla::Module::CategoryEntry kXPFECategories[] = {
+ { "Gecko-Content-Viewers", "application/http-index-format", "@mozilla.org/xpfe/http-index-format-factory-constructor" },
+ { nullptr }
+};
+
+static const mozilla::Module kXPFEModule = {
+ mozilla::Module::kVersion,
+ kXPFECIDs,
+ kXPFEContracts,
+ kXPFECategories
+};
+
+NSMODULE_DEFN(application) = &kXPFEModule;
diff --git a/xpfe/components/directory/moz.build b/xpfe/components/directory/moz.build
new file mode 100644
index 000000000..248da07ec
--- /dev/null
+++ b/xpfe/components/directory/moz.build
@@ -0,0 +1,20 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPIDL_SOURCES += [
+ 'nsIHTTPIndex.idl',
+]
+
+XPIDL_MODULE = 'directory'
+
+SOURCES += [
+ 'nsDirectoryViewer.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/xpfe/components/directory/nsDirectoryViewer.cpp b/xpfe/components/directory/nsDirectoryViewer.cpp
new file mode 100644
index 000000000..9d23c5e74
--- /dev/null
+++ b/xpfe/components/directory/nsDirectoryViewer.cpp
@@ -0,0 +1,1393 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+
+ A directory viewer object. Parses "application/http-index-format"
+ per Lou Montulli's original spec:
+
+ http://www.mozilla.org/projects/netlib/dirindexformat.html
+
+ One added change is for a description entry, for when the
+ target does not match the filename
+
+*/
+
+#include "nsDirectoryViewer.h"
+#include "nsArray.h"
+#include "nsArrayUtils.h"
+#include "nsIDirIndex.h"
+#include "nsIDocShell.h"
+#include "jsapi.h"
+#include "nsCOMPtr.h"
+#include "nsEnumeratorUtils.h"
+#include "nsEscape.h"
+#include "nsIRDFService.h"
+#include "nsRDFCID.h"
+#include "rdf.h"
+#include "nsIServiceManager.h"
+#include "nsIXPConnect.h"
+#include "nsEnumeratorUtils.h"
+#include "nsString.h"
+#include "nsXPIDLString.h"
+#include "nsReadableUtils.h"
+#include "nsITextToSubURI.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIFTPChannel.h"
+#include "nsIWindowWatcher.h"
+#include "nsIPrompt.h"
+#include "nsIAuthPrompt.h"
+#include "nsIProgressEventSink.h"
+#include "nsIDOMWindow.h"
+#include "nsIDOMElement.h"
+#include "nsIStreamConverterService.h"
+#include "nsICategoryManager.h"
+#include "nsXPCOMCID.h"
+#include "nsIDocument.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "nsContentUtils.h"
+#include "nsIURI.h"
+#include "nsNetUtil.h"
+
+using namespace mozilla;
+
+static const int FORMAT_XUL = 3;
+
+//----------------------------------------------------------------------
+//
+// Common CIDs
+//
+
+static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
+
+// Various protocols we have to special case
+static const char kFTPProtocol[] = "ftp://";
+
+//----------------------------------------------------------------------
+//
+// nsHTTPIndex
+//
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsHTTPIndex)
+ NS_INTERFACE_MAP_ENTRY(nsIHTTPIndex)
+ NS_INTERFACE_MAP_ENTRY(nsIRDFDataSource)
+ NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
+ NS_INTERFACE_MAP_ENTRY(nsIDirIndexListener)
+ NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+ NS_INTERFACE_MAP_ENTRY(nsIFTPEventSink)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIHTTPIndex)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION(nsHTTPIndex, mInner)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHTTPIndex)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHTTPIndex)
+
+NS_IMETHODIMP
+nsHTTPIndex::GetInterface(const nsIID &anIID, void **aResult )
+{
+ if (anIID.Equals(NS_GET_IID(nsIFTPEventSink))) {
+ // If we don't have a container to store the logged data
+ // then don't report ourselves back to the caller
+
+ if (!mRequestor)
+ return NS_ERROR_NO_INTERFACE;
+ *aResult = static_cast<nsIFTPEventSink*>(this);
+ NS_ADDREF(this);
+ return NS_OK;
+ }
+
+ if (anIID.Equals(NS_GET_IID(nsIPrompt))) {
+
+ if (!mRequestor)
+ return NS_ERROR_NO_INTERFACE;
+
+ nsCOMPtr<nsPIDOMWindowOuter> aDOMWindow = do_GetInterface(mRequestor);
+ if (!aDOMWindow)
+ return NS_ERROR_NO_INTERFACE;
+
+ nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
+
+ return wwatch->GetNewPrompter(aDOMWindow, (nsIPrompt**)aResult);
+ }
+
+ if (anIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
+
+ if (!mRequestor)
+ return NS_ERROR_NO_INTERFACE;
+
+ nsCOMPtr<nsPIDOMWindowOuter> aDOMWindow = do_GetInterface(mRequestor);
+ if (!aDOMWindow)
+ return NS_ERROR_NO_INTERFACE;
+
+ nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
+
+ return wwatch->GetNewAuthPrompter(aDOMWindow, (nsIAuthPrompt**)aResult);
+ }
+
+ if (anIID.Equals(NS_GET_IID(nsIProgressEventSink))) {
+
+ if (!mRequestor)
+ return NS_ERROR_NO_INTERFACE;
+
+ nsCOMPtr<nsIProgressEventSink> sink = do_GetInterface(mRequestor);
+ if (!sink)
+ return NS_ERROR_NO_INTERFACE;
+
+ *aResult = sink;
+ NS_ADDREF((nsISupports*)*aResult);
+ return NS_OK;
+ }
+
+ return NS_ERROR_NO_INTERFACE;
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::OnFTPControlLog(bool server, const char *msg)
+{
+ NS_ENSURE_TRUE(mRequestor, NS_OK);
+
+ nsCOMPtr<nsIGlobalObject> globalObject = do_GetInterface(mRequestor);
+ NS_ENSURE_TRUE(globalObject, NS_OK);
+
+ // We're going to run script via JS_CallFunctionName, so we need an
+ // AutoEntryScript. This is Gecko specific and not in any spec.
+ dom::AutoEntryScript aes(globalObject,
+ "nsHTTPIndex OnFTPControlLog");
+ JSContext* cx = aes.cx();
+
+ JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
+ NS_ENSURE_TRUE(global, NS_OK);
+
+ nsString unicodeMsg;
+ unicodeMsg.AssignWithConversion(msg);
+ JSString* jsMsgStr = JS_NewUCStringCopyZ(cx, unicodeMsg.get());
+ NS_ENSURE_TRUE(jsMsgStr, NS_ERROR_OUT_OF_MEMORY);
+
+ JS::AutoValueArray<2> params(cx);
+ params[0].setBoolean(server);
+ params[1].setString(jsMsgStr);
+
+ JS::Rooted<JS::Value> val(cx);
+ JS_CallFunctionName(cx,
+ global,
+ "OnFTPControlLog",
+ params,
+ &val);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::SetEncoding(const char *encoding)
+{
+ mEncoding = encoding;
+ return(NS_OK);
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::GetEncoding(char **encoding)
+{
+ NS_PRECONDITION(encoding, "null ptr");
+ if (! encoding)
+ return(NS_ERROR_NULL_POINTER);
+
+ *encoding = ToNewCString(mEncoding);
+ if (!*encoding)
+ return(NS_ERROR_OUT_OF_MEMORY);
+
+ return(NS_OK);
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::OnStartRequest(nsIRequest *request, nsISupports* aContext)
+{
+ nsresult rv;
+
+ mParser = do_CreateInstance(NS_DIRINDEXPARSER_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = mParser->SetEncoding(mEncoding.get());
+ if (NS_FAILED(rv)) return rv;
+
+ rv = mParser->SetListener(this);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = mParser->OnStartRequest(request,aContext);
+ if (NS_FAILED(rv)) return rv;
+
+ // This should only run once...
+ // Unless we don't have a container to start with
+ // (ie called from bookmarks as an rdf datasource)
+ if (mBindToGlobalObject && mRequestor) {
+ mBindToGlobalObject = false;
+
+ nsCOMPtr<nsIGlobalObject> globalObject = do_GetInterface(mRequestor);
+ NS_ENSURE_TRUE(globalObject, NS_ERROR_FAILURE);
+
+ // We might run script via JS_SetProperty, so we need an AutoEntryScript.
+ // This is Gecko specific and not in any spec.
+ dom::AutoEntryScript aes(globalObject,
+ "nsHTTPIndex set HTTPIndex property");
+ JSContext* cx = aes.cx();
+
+ JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
+
+ // Using XPConnect, wrap the HTTP index object...
+ static NS_DEFINE_CID(kXPConnectCID, NS_XPCONNECT_CID);
+ nsCOMPtr<nsIXPConnect> xpc(do_GetService(kXPConnectCID, &rv));
+ if (NS_FAILED(rv)) return rv;
+
+ JS::Rooted<JSObject*> jsobj(cx);
+ rv = xpc->WrapNative(cx,
+ global,
+ static_cast<nsIHTTPIndex*>(this),
+ NS_GET_IID(nsIHTTPIndex),
+ jsobj.address());
+
+ NS_ASSERTION(NS_SUCCEEDED(rv), "unable to xpconnect-wrap http-index");
+ if (NS_FAILED(rv)) return rv;
+
+ NS_ASSERTION(jsobj,
+ "unable to get jsobj from xpconnect wrapper");
+ if (!jsobj) return NS_ERROR_UNEXPECTED;
+
+ JS::Rooted<JS::Value> jslistener(cx, JS::ObjectValue(*jsobj));
+
+ // ...and stuff it into the global context
+ bool ok = JS_SetProperty(cx, global, "HTTPIndex", jslistener);
+ NS_ASSERTION(ok, "unable to set Listener property");
+ if (!ok)
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!aContext) {
+ nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
+ NS_ASSERTION(channel, "request should be a channel");
+
+ // lets hijack the notifications:
+ channel->SetNotificationCallbacks(this);
+
+ // now create the top most resource
+ nsCOMPtr<nsIURI> uri;
+ channel->GetURI(getter_AddRefs(uri));
+
+ nsAutoCString entryuriC;
+ rv = uri->GetSpec(entryuriC);
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIRDFResource> entry;
+ rv = mDirRDF->GetResource(entryuriC, getter_AddRefs(entry));
+
+ NS_ConvertUTF8toUTF16 uriUnicode(entryuriC);
+
+ nsCOMPtr<nsIRDFLiteral> URLVal;
+ rv = mDirRDF->GetLiteral(uriUnicode.get(), getter_AddRefs(URLVal));
+
+ Assert(entry, kNC_URL, URLVal, true);
+ mDirectory = do_QueryInterface(entry);
+ }
+ else
+ {
+ // Get the directory from the context
+ mDirectory = do_QueryInterface(aContext);
+ }
+
+ if (!mDirectory) {
+ request->Cancel(NS_BINDING_ABORTED);
+ return NS_BINDING_ABORTED;
+ }
+
+ // Mark the directory as "loading"
+ rv = Assert(mDirectory, kNC_Loading,
+ kTrueLiteral, true);
+ if (NS_FAILED(rv)) return rv;
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsHTTPIndex::OnStopRequest(nsIRequest *request,
+ nsISupports* aContext,
+ nsresult aStatus)
+{
+ // If mDirectory isn't set, then we should just bail. Either an
+ // error occurred and OnStartRequest() never got called, or
+ // something exploded in OnStartRequest().
+ if (! mDirectory)
+ return NS_BINDING_ABORTED;
+
+ mParser->OnStopRequest(request,aContext,aStatus);
+
+ nsresult rv;
+
+ nsXPIDLCString commentStr;
+ mParser->GetComment(getter_Copies(commentStr));
+
+ nsCOMPtr<nsIRDFLiteral> comment;
+ rv = mDirRDF->GetLiteral(NS_ConvertASCIItoUTF16(commentStr).get(), getter_AddRefs(comment));
+ if (NS_FAILED(rv)) return rv;
+
+ rv = Assert(mDirectory, kNC_Comment, comment, true);
+ if (NS_FAILED(rv)) return rv;
+
+ // hack: Remove the 'loading' annotation (ignore errors)
+ AddElement(mDirectory, kNC_Loading, kTrueLiteral);
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsHTTPIndex::OnDataAvailable(nsIRequest *request,
+ nsISupports* aContext,
+ nsIInputStream* aStream,
+ uint64_t aSourceOffset,
+ uint32_t aCount)
+{
+ // If mDirectory isn't set, then we should just bail. Either an
+ // error occurred and OnStartRequest() never got called, or
+ // something exploded in OnStartRequest().
+ if (! mDirectory)
+ return NS_BINDING_ABORTED;
+
+ return mParser->OnDataAvailable(request, mDirectory, aStream, aSourceOffset, aCount);
+}
+
+
+nsresult
+nsHTTPIndex::OnIndexAvailable(nsIRequest* aRequest, nsISupports *aContext,
+ nsIDirIndex* aIndex)
+{
+ nsCOMPtr<nsIRDFResource> parentRes = do_QueryInterface(aContext);
+ if (!parentRes) {
+ NS_ERROR("Could not obtain parent resource");
+ return(NS_ERROR_UNEXPECTED);
+ }
+
+ const char* baseStr;
+ parentRes->GetValueConst(&baseStr);
+ if (! baseStr) {
+ NS_ERROR("Could not reconstruct base uri");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // we found the filename; construct a resource for its entry
+ nsAutoCString entryuriC(baseStr);
+
+ nsXPIDLCString filename;
+ nsresult rv = aIndex->GetLocation(getter_Copies(filename));
+ if (NS_FAILED(rv)) return rv;
+ entryuriC.Append(filename);
+
+ // if its a directory, make sure it ends with a trailing slash.
+ uint32_t type;
+ rv = aIndex->GetType(&type);
+ if (NS_FAILED(rv))
+ return rv;
+
+ bool isDirType = (type == nsIDirIndex::TYPE_DIRECTORY);
+ if (isDirType && entryuriC.Last() != '/') {
+ entryuriC.Append('/');
+ }
+
+ nsCOMPtr<nsIRDFResource> entry;
+ rv = mDirRDF->GetResource(entryuriC, getter_AddRefs(entry));
+
+ // At this point, we'll (hopefully) have found the filename and
+ // constructed a resource for it, stored in entry. So now take a
+ // second pass through the values and add as statements to the RDF
+ // datasource.
+
+ if (entry && NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIRDFLiteral> lit;
+ nsString str;
+
+ str.AssignWithConversion(entryuriC.get());
+
+ rv = mDirRDF->GetLiteral(str.get(), getter_AddRefs(lit));
+
+ if (NS_SUCCEEDED(rv)) {
+ rv = Assert(entry, kNC_URL, lit, true);
+ if (NS_FAILED(rv)) return rv;
+
+ nsXPIDLString xpstr;
+
+ // description
+ rv = aIndex->GetDescription(getter_Copies(xpstr));
+ if (NS_FAILED(rv)) return rv;
+ if (xpstr.Last() == '/')
+ xpstr.Truncate(xpstr.Length() - 1);
+
+ rv = mDirRDF->GetLiteral(xpstr.get(), getter_AddRefs(lit));
+ if (NS_FAILED(rv)) return rv;
+ rv = Assert(entry, kNC_Description, lit, true);
+ if (NS_FAILED(rv)) return rv;
+
+ // contentlength
+ int64_t size;
+ rv = aIndex->GetSize(&size);
+ if (NS_FAILED(rv)) return rv;
+ int64_t minus1 = UINT64_MAX;
+ if (size != minus1) {
+ int32_t intSize = int32_t(size);
+ // XXX RDF should support 64 bit integers (bug 240160)
+ nsCOMPtr<nsIRDFInt> val;
+ rv = mDirRDF->GetIntLiteral(intSize, getter_AddRefs(val));
+ if (NS_FAILED(rv)) return rv;
+ rv = Assert(entry, kNC_ContentLength, val, true);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ // lastmodified
+ PRTime tm;
+ rv = aIndex->GetLastModified(&tm);
+ if (NS_FAILED(rv)) return rv;
+ if (tm != -1) {
+ nsCOMPtr<nsIRDFDate> val;
+ rv = mDirRDF->GetDateLiteral(tm, getter_AddRefs(val));
+ if (NS_FAILED(rv)) return rv;
+ rv = Assert(entry, kNC_LastModified, val, true);
+ }
+
+ // filetype
+ uint32_t type;
+ rv = aIndex->GetType(&type);
+ switch (type) {
+ case nsIDirIndex::TYPE_UNKNOWN:
+ rv = mDirRDF->GetLiteral(u"UNKNOWN", getter_AddRefs(lit));
+ break;
+ case nsIDirIndex::TYPE_DIRECTORY:
+ rv = mDirRDF->GetLiteral(u"DIRECTORY", getter_AddRefs(lit));
+ break;
+ case nsIDirIndex::TYPE_FILE:
+ rv = mDirRDF->GetLiteral(u"FILE", getter_AddRefs(lit));
+ break;
+ case nsIDirIndex::TYPE_SYMLINK:
+ rv = mDirRDF->GetLiteral(u"SYMLINK", getter_AddRefs(lit));
+ break;
+ }
+
+ if (NS_FAILED(rv)) return rv;
+ rv = Assert(entry, kNC_FileType, lit, true);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ // Since the definition of a directory depends on the protocol, we would have
+ // to do string comparisons all the time.
+ // But we're told if we're a container right here - so save that fact
+ if (isDirType)
+ Assert(entry, kNC_IsContainer, kTrueLiteral, true);
+ else
+ Assert(entry, kNC_IsContainer, kFalseLiteral, true);
+
+// instead of
+// rv = Assert(parentRes, kNC_Child, entry, true);
+// if (NS_FAILED(rv)) return rv;
+// defer insertion onto a timer so that the UI isn't starved
+ AddElement(parentRes, kNC_Child, entry);
+ }
+
+ return rv;
+}
+
+nsresult
+nsHTTPIndex::OnInformationAvailable(nsIRequest *aRequest,
+ nsISupports *aCtxt,
+ const nsAString& aInfo) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+//----------------------------------------------------------------------
+//
+// nsHTTPIndex implementation
+//
+
+nsHTTPIndex::nsHTTPIndex()
+ : mBindToGlobalObject(true),
+ mRequestor(nullptr)
+{
+}
+
+
+nsHTTPIndex::nsHTTPIndex(nsIInterfaceRequestor* aRequestor)
+ : mBindToGlobalObject(true),
+ mRequestor(aRequestor)
+{
+}
+
+
+nsHTTPIndex::~nsHTTPIndex()
+{
+ // note: these are NOT statics due to the native of nsHTTPIndex
+ // where it may or may not be treated as a singleton
+
+ if (mTimer)
+ {
+ // be sure to cancel the timer, as it holds a
+ // weak reference back to nsHTTPIndex
+ mTimer->Cancel();
+ mTimer = nullptr;
+ }
+
+ mConnectionList = nullptr;
+ mNodeList = nullptr;
+
+ if (mDirRDF)
+ {
+ // UnregisterDataSource() may fail; just ignore errors
+ mDirRDF->UnregisterDataSource(this);
+ }
+}
+
+
+
+nsresult
+nsHTTPIndex::CommonInit()
+{
+ nsresult rv = NS_OK;
+
+ // set initial/default encoding to ISO-8859-1 (not UTF-8)
+ mEncoding = "ISO-8859-1";
+
+ mDirRDF = do_GetService(kRDFServiceCID, &rv);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF service");
+ if (NS_FAILED(rv)) {
+ return(rv);
+ }
+
+ mInner = do_CreateInstance("@mozilla.org/rdf/datasource;1?name=in-memory-datasource", &rv);
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "child"),
+ getter_AddRefs(kNC_Child));
+ mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "loading"),
+ getter_AddRefs(kNC_Loading));
+ mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Comment"),
+ getter_AddRefs(kNC_Comment));
+ mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "URL"),
+ getter_AddRefs(kNC_URL));
+ mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Name"),
+ getter_AddRefs(kNC_Description));
+ mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Content-Length"),
+ getter_AddRefs(kNC_ContentLength));
+ mDirRDF->GetResource(NS_LITERAL_CSTRING(WEB_NAMESPACE_URI "LastModifiedDate"),
+ getter_AddRefs(kNC_LastModified));
+ mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Content-Type"),
+ getter_AddRefs(kNC_ContentType));
+ mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "File-Type"),
+ getter_AddRefs(kNC_FileType));
+ mDirRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "IsContainer"),
+ getter_AddRefs(kNC_IsContainer));
+
+ rv = mDirRDF->GetLiteral(u"true", getter_AddRefs(kTrueLiteral));
+ if (NS_FAILED(rv)) return(rv);
+ rv = mDirRDF->GetLiteral(u"false", getter_AddRefs(kFalseLiteral));
+ if (NS_FAILED(rv)) return(rv);
+
+ mConnectionList = nsArray::Create();
+
+ // note: don't register DS here
+ return rv;
+}
+
+
+nsresult
+nsHTTPIndex::Init()
+{
+ nsresult rv;
+
+ // set initial/default encoding to ISO-8859-1 (not UTF-8)
+ mEncoding = "ISO-8859-1";
+
+ rv = CommonInit();
+ if (NS_FAILED(rv)) return(rv);
+
+ // (do this last) register this as a named data source with the RDF service
+ rv = mDirRDF->RegisterDataSource(this, false);
+ if (NS_FAILED(rv)) return(rv);
+
+ return(NS_OK);
+}
+
+
+
+nsresult
+nsHTTPIndex::Init(nsIURI* aBaseURL)
+{
+ NS_PRECONDITION(aBaseURL != nullptr, "null ptr");
+ if (! aBaseURL)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+
+ rv = CommonInit();
+ if (NS_FAILED(rv)) return(rv);
+
+ // note: don't register DS here (singleton case)
+
+ rv = aBaseURL->GetSpec(mBaseURL);
+ if (NS_FAILED(rv)) return rv;
+
+ // Mark the base url as a container
+ nsCOMPtr<nsIRDFResource> baseRes;
+ mDirRDF->GetResource(mBaseURL, getter_AddRefs(baseRes));
+ Assert(baseRes, kNC_IsContainer, kTrueLiteral, true);
+
+ return NS_OK;
+}
+
+
+
+nsresult
+nsHTTPIndex::Create(nsIURI* aBaseURL, nsIInterfaceRequestor* aRequestor,
+ nsIHTTPIndex** aResult)
+{
+ *aResult = nullptr;
+
+ nsHTTPIndex* result = new nsHTTPIndex(aRequestor);
+ nsresult rv = result->Init(aBaseURL);
+ if (NS_SUCCEEDED(rv))
+ {
+ NS_ADDREF(result);
+ *aResult = result;
+ }
+ else
+ {
+ delete result;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::GetBaseURL(char** _result)
+{
+ *_result = ToNewCString(mBaseURL);
+ if (! *_result)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::GetDataSource(nsIRDFDataSource** _result)
+{
+ NS_ADDREF(*_result = this);
+ return NS_OK;
+}
+
+// This function finds the destination when following a given nsIRDFResource
+// If the resource has a URL attribute, we use that. If not, just use
+// the uri.
+//
+// Do NOT try to get the destination of a uri in any other way
+void nsHTTPIndex::GetDestination(nsIRDFResource* r, nsXPIDLCString& dest) {
+ // First try the URL attribute
+ nsCOMPtr<nsIRDFNode> node;
+
+ GetTarget(r, kNC_URL, true, getter_AddRefs(node));
+ nsCOMPtr<nsIRDFLiteral> url;
+
+ if (node)
+ url = do_QueryInterface(node);
+
+ if (!url) {
+ const char* temp;
+ r->GetValueConst(&temp);
+ dest.Adopt(temp ? strdup(temp) : 0);
+ } else {
+ const char16_t* uri;
+ url->GetValueConst(&uri);
+ dest.Adopt(ToNewUTF8String(nsDependentString(uri)));
+ }
+}
+
+// rjc: isWellknownContainerURI() decides whether a URI is a container for which,
+// when asked (say, by the template builder), we'll make a network connection
+// to get its contents. For the moment, all we speak is ftp:// URLs, even though
+// a) we can get "http-index" mimetypes for really anything
+// b) we could easily handle file:// URLs here
+// Q: Why don't we?
+// A: The file system datasource ("rdf:file"); at some point, the two
+// should be perhaps united. Until then, we can't aggregate both
+// "rdf:file" and "http-index" (such as with bookmarks) because we'd
+// get double the # of answers we really want... also, "rdf:file" is
+// less expensive in terms of both memory usage as well as speed
+
+
+
+// We use an rdf attribute to mark if this is a container or not.
+// Note that we still have to do string comparisons as a fallback
+// because stuff like the personal toolbar and bookmarks check whether
+// a URL is a container, and we have no attribute in that case.
+bool
+nsHTTPIndex::isWellknownContainerURI(nsIRDFResource *r)
+{
+ nsCOMPtr<nsIRDFNode> node;
+ GetTarget(r, kNC_IsContainer, true, getter_AddRefs(node));
+ if (node) {
+ bool isContainerFlag;
+ if (NS_SUCCEEDED(node->EqualsNode(kTrueLiteral, &isContainerFlag)))
+ return isContainerFlag;
+ }
+
+ nsXPIDLCString uri;
+ GetDestination(r, uri);
+ return uri.get() && !strncmp(uri, kFTPProtocol, sizeof(kFTPProtocol) - 1) &&
+ (uri.Last() == '/');
+}
+
+
+NS_IMETHODIMP
+nsHTTPIndex::GetURI(char * *uri)
+{
+ NS_PRECONDITION(uri != nullptr, "null ptr");
+ if (! uri)
+ return(NS_ERROR_NULL_POINTER);
+
+ if ((*uri = strdup("rdf:httpindex")) == nullptr)
+ return(NS_ERROR_OUT_OF_MEMORY);
+
+ return(NS_OK);
+}
+
+
+
+NS_IMETHODIMP
+nsHTTPIndex::GetSource(nsIRDFResource *aProperty, nsIRDFNode *aTarget, bool aTruthValue,
+ nsIRDFResource **_retval)
+{
+ nsresult rv = NS_ERROR_UNEXPECTED;
+
+ *_retval = nullptr;
+
+ if (mInner)
+ {
+ rv = mInner->GetSource(aProperty, aTarget, aTruthValue, _retval);
+ }
+ return(rv);
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::GetSources(nsIRDFResource *aProperty, nsIRDFNode *aTarget, bool aTruthValue,
+ nsISimpleEnumerator **_retval)
+{
+ nsresult rv = NS_ERROR_UNEXPECTED;
+
+ if (mInner)
+ {
+ rv = mInner->GetSources(aProperty, aTarget, aTruthValue, _retval);
+ }
+ else
+ {
+ rv = NS_NewEmptyEnumerator(_retval);
+ }
+ return(rv);
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::GetTarget(nsIRDFResource *aSource, nsIRDFResource *aProperty, bool aTruthValue,
+ nsIRDFNode **_retval)
+{
+ nsresult rv = NS_ERROR_UNEXPECTED;
+
+ *_retval = nullptr;
+
+ if ((aTruthValue) && (aProperty == kNC_Child) && isWellknownContainerURI(aSource))
+ {
+ // fake out the generic builder (i.e. return anything in this case)
+ // so that search containers never appear to be empty
+ NS_IF_ADDREF(aSource);
+ *_retval = aSource;
+ return(NS_OK);
+ }
+
+ if (mInner)
+ {
+ rv = mInner->GetTarget(aSource, aProperty, aTruthValue, _retval);
+ }
+ return(rv);
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::GetTargets(nsIRDFResource *aSource, nsIRDFResource *aProperty, bool aTruthValue,
+ nsISimpleEnumerator **_retval)
+{
+ nsresult rv = NS_ERROR_UNEXPECTED;
+
+ if (mInner)
+ {
+ rv = mInner->GetTargets(aSource, aProperty, aTruthValue, _retval);
+ }
+ else
+ {
+ rv = NS_NewEmptyEnumerator(_retval);
+ }
+
+ if ((aProperty == kNC_Child) && isWellknownContainerURI(aSource))
+ {
+ bool doNetworkRequest = true;
+ if (NS_SUCCEEDED(rv) && (_retval))
+ {
+ // check and see if we already have data for the search in question;
+ // if we do, don't bother doing the search again
+ bool hasResults;
+ if (NS_SUCCEEDED((*_retval)->HasMoreElements(&hasResults)) &&
+ hasResults)
+ doNetworkRequest = false;
+ }
+
+ // Note: if we need to do a network request, do it out-of-band
+ // (because the XUL template builder isn't re-entrant)
+ // by using a global connection list and an immediately-firing timer
+ if (doNetworkRequest && mConnectionList)
+ {
+ uint32_t connectionIndex;
+ nsresult idx_rv = mConnectionList->IndexOf(0, aSource, &connectionIndex);
+ if (NS_FAILED(idx_rv))
+ {
+ // add aSource into list of connections to make
+ mConnectionList->AppendElement(aSource, /*weak =*/ false);
+
+ // if we don't have a timer about to fire, create one
+ // which should fire as soon as possible (out-of-band)
+ if (!mTimer)
+ {
+ mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create a timer");
+ if (NS_SUCCEEDED(rv))
+ {
+ mTimer->InitWithFuncCallback(nsHTTPIndex::FireTimer, this, 1,
+ nsITimer::TYPE_ONE_SHOT);
+ // Note: don't addref "this" as we'll cancel the
+ // timer in the httpIndex destructor
+ }
+ }
+ }
+ }
+ }
+
+ return(rv);
+}
+
+
+nsresult
+nsHTTPIndex::AddElement(nsIRDFResource *parent, nsIRDFResource *prop, nsIRDFNode *child)
+{
+ nsresult rv;
+
+ if (!mNodeList)
+ {
+ mNodeList = nsArray::Create();
+ }
+
+ // order required: parent, prop, then child
+ mNodeList->AppendElement(parent, /*weak =*/ false);
+ mNodeList->AppendElement(prop, /*weak =*/ false);
+ mNodeList->AppendElement(child, /*weak = */ false);
+
+ if (!mTimer)
+ {
+ mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create a timer");
+ if (NS_FAILED(rv)) return(rv);
+
+ mTimer->InitWithFuncCallback(nsHTTPIndex::FireTimer, this, 1,
+ nsITimer::TYPE_ONE_SHOT);
+ // Note: don't addref "this" as we'll cancel the
+ // timer in the httpIndex destructor
+ }
+
+ return(NS_OK);
+}
+
+void
+nsHTTPIndex::FireTimer(nsITimer* aTimer, void* aClosure)
+{
+ nsHTTPIndex *httpIndex = static_cast<nsHTTPIndex *>(aClosure);
+ if (!httpIndex)
+ return;
+
+ // don't return out of this loop as mTimer may need to be cancelled afterwards
+ uint32_t numItems = 0;
+ if (httpIndex->mConnectionList)
+ {
+ httpIndex->mConnectionList->GetLength(&numItems);
+ if (numItems > 0)
+ {
+ nsCOMPtr<nsIRDFResource> source =
+ do_QueryElementAt(httpIndex->mConnectionList, 0);
+ httpIndex->mConnectionList->RemoveElementAt(0);
+
+ nsXPIDLCString uri;
+ if (source) {
+ httpIndex->GetDestination(source, uri);
+ }
+
+ if (!uri) {
+ NS_ERROR("Could not reconstruct uri");
+ return;
+ }
+
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIURI> url;
+
+ rv = NS_NewURI(getter_AddRefs(url), uri.get());
+ nsCOMPtr<nsIChannel> channel;
+ if (NS_SUCCEEDED(rv) && (url)) {
+ rv = NS_NewChannel(getter_AddRefs(channel),
+ url,
+ nsContentUtils::GetSystemPrincipal(),
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER);
+ }
+ if (NS_SUCCEEDED(rv) && (channel)) {
+ channel->SetNotificationCallbacks(httpIndex);
+ rv = channel->AsyncOpen2(httpIndex);
+ }
+ }
+ }
+
+ if (httpIndex->mNodeList)
+ {
+ httpIndex->mNodeList->GetLength(&numItems);
+ if (numItems > 0)
+ {
+ // account for order required: src, prop, then target
+ numItems /=3;
+ if (numItems > 10)
+ numItems = 10;
+
+ int32_t loop;
+ for (loop=0; loop<(int32_t)numItems; loop++)
+ {
+ nsCOMPtr<nsIRDFResource> src = do_QueryElementAt(httpIndex->mNodeList, 0);
+ httpIndex->mNodeList->RemoveElementAt(0);
+
+ nsCOMPtr<nsIRDFResource> prop = do_QueryElementAt(httpIndex->mNodeList, 0);
+ httpIndex->mNodeList->RemoveElementAt(0);
+
+ nsCOMPtr<nsIRDFNode> target = do_QueryElementAt(httpIndex->mNodeList, 0);
+ httpIndex->mNodeList->RemoveElementAt(0);
+
+ if (src && prop && target)
+ {
+ if (prop.get() == httpIndex->kNC_Loading)
+ {
+ httpIndex->Unassert(src, prop, target);
+ }
+ else
+ {
+ httpIndex->Assert(src, prop, target, true);
+ }
+ }
+ }
+ }
+ }
+
+ bool refireTimer = false;
+ // check both lists to see if the timer needs to continue firing
+ if (httpIndex->mConnectionList)
+ {
+ httpIndex->mConnectionList->GetLength(&numItems);
+ if (numItems > 0)
+ {
+ refireTimer = true;
+ }
+ else
+ {
+ httpIndex->mConnectionList->Clear();
+ }
+ }
+
+ if (httpIndex->mNodeList)
+ {
+ httpIndex->mNodeList->GetLength(&numItems);
+ if (numItems > 0)
+ {
+ refireTimer = true;
+ }
+ else
+ {
+ httpIndex->mNodeList->Clear();
+ }
+ }
+
+ // be sure to cancel the timer, as it holds a
+ // weak reference back to nsHTTPIndex
+ httpIndex->mTimer->Cancel();
+ httpIndex->mTimer = nullptr;
+
+ // after firing off any/all of the connections be sure
+ // to cancel the timer if we don't need to refire it
+ if (refireTimer)
+ {
+ httpIndex->mTimer = do_CreateInstance("@mozilla.org/timer;1");
+ if (httpIndex->mTimer)
+ {
+ httpIndex->mTimer->InitWithFuncCallback(nsHTTPIndex::FireTimer, aClosure, 10,
+ nsITimer::TYPE_ONE_SHOT);
+ // Note: don't addref "this" as we'll cancel the
+ // timer in the httpIndex destructor
+ }
+ }
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::Assert(nsIRDFResource *aSource, nsIRDFResource *aProperty, nsIRDFNode *aTarget,
+ bool aTruthValue)
+{
+ nsresult rv = NS_ERROR_UNEXPECTED;
+ if (mInner)
+ {
+ rv = mInner->Assert(aSource, aProperty, aTarget, aTruthValue);
+ }
+ return(rv);
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::Unassert(nsIRDFResource *aSource, nsIRDFResource *aProperty, nsIRDFNode *aTarget)
+{
+ nsresult rv = NS_ERROR_UNEXPECTED;
+ if (mInner)
+ {
+ rv = mInner->Unassert(aSource, aProperty, aTarget);
+ }
+ return(rv);
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::Change(nsIRDFResource *aSource, nsIRDFResource *aProperty,
+ nsIRDFNode *aOldTarget, nsIRDFNode *aNewTarget)
+{
+ nsresult rv = NS_ERROR_UNEXPECTED;
+ if (mInner)
+ {
+ rv = mInner->Change(aSource, aProperty, aOldTarget, aNewTarget);
+ }
+ return(rv);
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::Move(nsIRDFResource *aOldSource, nsIRDFResource *aNewSource,
+ nsIRDFResource *aProperty, nsIRDFNode *aTarget)
+{
+ nsresult rv = NS_ERROR_UNEXPECTED;
+ if (mInner)
+ {
+ rv = mInner->Move(aOldSource, aNewSource, aProperty, aTarget);
+ }
+ return(rv);
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::HasAssertion(nsIRDFResource *aSource, nsIRDFResource *aProperty,
+ nsIRDFNode *aTarget, bool aTruthValue, bool *_retval)
+{
+ nsresult rv = NS_ERROR_UNEXPECTED;
+ if (mInner)
+ {
+ rv = mInner->HasAssertion(aSource, aProperty, aTarget, aTruthValue, _retval);
+ }
+ return(rv);
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::AddObserver(nsIRDFObserver *aObserver)
+{
+ nsresult rv = NS_ERROR_UNEXPECTED;
+ if (mInner)
+ {
+ rv = mInner->AddObserver(aObserver);
+ }
+ return(rv);
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::RemoveObserver(nsIRDFObserver *aObserver)
+{
+ nsresult rv = NS_ERROR_UNEXPECTED;
+ if (mInner)
+ {
+ rv = mInner->RemoveObserver(aObserver);
+ }
+ return(rv);
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, bool *result)
+{
+ if (!mInner) {
+ *result = false;
+ return NS_OK;
+ }
+ return mInner->HasArcIn(aNode, aArc, result);
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, bool *result)
+{
+ if (aArc == kNC_Child && isWellknownContainerURI(aSource)) {
+ *result = true;
+ return NS_OK;
+ }
+
+ if (mInner) {
+ return mInner->HasArcOut(aSource, aArc, result);
+ }
+
+ *result = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::ArcLabelsIn(nsIRDFNode *aNode, nsISimpleEnumerator **_retval)
+{
+ nsresult rv = NS_ERROR_UNEXPECTED;
+ if (mInner)
+ {
+ rv = mInner->ArcLabelsIn(aNode, _retval);
+ }
+ return(rv);
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::ArcLabelsOut(nsIRDFResource *aSource, nsISimpleEnumerator **_retval)
+{
+ *_retval = nullptr;
+
+ nsCOMPtr<nsISimpleEnumerator> child, anonArcs;
+ if (isWellknownContainerURI(aSource))
+ {
+ NS_NewSingletonEnumerator(getter_AddRefs(child), kNC_Child);
+ }
+
+ if (mInner)
+ {
+ mInner->ArcLabelsOut(aSource, getter_AddRefs(anonArcs));
+ }
+
+ return NS_NewUnionEnumerator(_retval, child, anonArcs);
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::GetAllResources(nsISimpleEnumerator **_retval)
+{
+ nsresult rv = NS_ERROR_UNEXPECTED;
+ if (mInner)
+ {
+ rv = mInner->GetAllResources(_retval);
+ }
+ return(rv);
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::IsCommandEnabled(nsISupports *aSources, nsIRDFResource *aCommand,
+ nsISupports *aArguments, bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::DoCommand(nsISupports *aSources, nsIRDFResource *aCommand,
+ nsISupports *aArguments)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::BeginUpdateBatch()
+{
+ return mInner->BeginUpdateBatch();
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::EndUpdateBatch()
+{
+ return mInner->EndUpdateBatch();
+}
+
+NS_IMETHODIMP
+nsHTTPIndex::GetAllCmds(nsIRDFResource *aSource, nsISimpleEnumerator **_retval)
+{
+ nsresult rv = NS_ERROR_UNEXPECTED;
+ if (mInner)
+ {
+ rv = mInner->GetAllCmds(aSource, _retval);
+ }
+ return(rv);
+}
+
+
+//----------------------------------------------------------------------
+//
+// nsDirectoryViewerFactory
+//
+nsDirectoryViewerFactory::nsDirectoryViewerFactory()
+{
+}
+
+
+
+nsDirectoryViewerFactory::~nsDirectoryViewerFactory()
+{
+}
+
+
+NS_IMPL_ISUPPORTS(nsDirectoryViewerFactory, nsIDocumentLoaderFactory)
+
+
+
+NS_IMETHODIMP
+nsDirectoryViewerFactory::CreateInstance(const char *aCommand,
+ nsIChannel* aChannel,
+ nsILoadGroup* aLoadGroup,
+ const nsACString& aContentType,
+ nsIDocShell* aContainer,
+ nsISupports* aExtraInfo,
+ nsIStreamListener** aDocListenerResult,
+ nsIContentViewer** aDocViewerResult)
+{
+ nsresult rv;
+
+ bool viewSource = FindInReadable(NS_LITERAL_CSTRING("view-source"),
+ aContentType);
+
+ if (!viewSource &&
+ Preferences::GetInt("network.dir.format", FORMAT_XUL) == FORMAT_XUL) {
+ // ... and setup the original channel's content type
+ (void)aChannel->SetContentType(NS_LITERAL_CSTRING("application/vnd.mozilla.xul+xml"));
+
+ // This is where we shunt the HTTP/Index stream into our datasource,
+ // and open the directory viewer XUL file as the content stream to
+ // load in its place.
+
+ // Create a dummy loader that will load a stub XUL document.
+ nsCOMPtr<nsICategoryManager> catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return rv;
+ nsXPIDLCString contractID;
+ rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "application/vnd.mozilla.xul+xml",
+ getter_Copies(contractID));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIDocumentLoaderFactory> factory(do_GetService(contractID, &rv));
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIURI> uri;
+ rv = NS_NewURI(getter_AddRefs(uri), "chrome://communicator/content/directory/directory.xul");
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIChannel> channel;
+ rv = NS_NewChannel(getter_AddRefs(channel),
+ uri,
+ nsContentUtils::GetSystemPrincipal(),
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER,
+ aLoadGroup);
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIStreamListener> listener;
+ rv = factory->CreateInstance(aCommand, channel, aLoadGroup,
+ NS_LITERAL_CSTRING("application/vnd.mozilla.xul+xml"),
+ aContainer, aExtraInfo, getter_AddRefs(listener),
+ aDocViewerResult);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = channel->AsyncOpen2(listener);
+ if (NS_FAILED(rv)) return rv;
+
+ // Create an HTTPIndex object so that we can stuff it into the script context
+ nsCOMPtr<nsIURI> baseuri;
+ rv = aChannel->GetURI(getter_AddRefs(baseuri));
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIInterfaceRequestor> requestor = do_QueryInterface(aContainer,&rv);
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIHTTPIndex> httpindex;
+ rv = nsHTTPIndex::Create(baseuri, requestor, getter_AddRefs(httpindex));
+ if (NS_FAILED(rv)) return rv;
+
+ // Now shanghai the stream into our http-index parsing datasource
+ // wrapper beastie.
+ listener = do_QueryInterface(httpindex,&rv);
+ *aDocListenerResult = listener.get();
+ NS_ADDREF(*aDocListenerResult);
+
+ return NS_OK;
+ }
+
+ // setup the original channel's content type
+ (void)aChannel->SetContentType(NS_LITERAL_CSTRING("text/html"));
+
+ // Otherwise, lets use the html listing
+ nsCOMPtr<nsICategoryManager> catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return rv;
+ nsXPIDLCString contractID;
+ rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "text/html",
+ getter_Copies(contractID));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIDocumentLoaderFactory> factory(do_GetService(contractID, &rv));
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIStreamListener> listener;
+
+ if (viewSource) {
+ rv = factory->CreateInstance("view-source", aChannel, aLoadGroup,
+ NS_LITERAL_CSTRING("text/html; x-view-type=view-source"),
+ aContainer, aExtraInfo, getter_AddRefs(listener),
+ aDocViewerResult);
+ } else {
+ rv = factory->CreateInstance("view", aChannel, aLoadGroup,
+ NS_LITERAL_CSTRING("text/html"),
+ aContainer, aExtraInfo, getter_AddRefs(listener),
+ aDocViewerResult);
+ }
+
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIStreamConverterService> scs = do_GetService("@mozilla.org/streamConverters;1", &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = scs->AsyncConvertData("application/http-index-format",
+ "text/html",
+ listener,
+ nullptr,
+ aDocListenerResult);
+
+ if (NS_FAILED(rv)) return rv;
+
+ return NS_OK;
+}
+
+
+
+NS_IMETHODIMP
+nsDirectoryViewerFactory::CreateInstanceForDocument(nsISupports* aContainer,
+ nsIDocument* aDocument,
+ const char *aCommand,
+ nsIContentViewer** aDocViewerResult)
+{
+ NS_NOTYETIMPLEMENTED("didn't expect to get here");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDirectoryViewerFactory::CreateBlankDocument(nsILoadGroup *aLoadGroup,
+ nsIPrincipal *aPrincipal,
+ nsIDocument **_retval) {
+
+ NS_NOTYETIMPLEMENTED("didn't expect to get here");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
diff --git a/xpfe/components/directory/nsDirectoryViewer.h b/xpfe/components/directory/nsDirectoryViewer.h
new file mode 100644
index 000000000..05b68f1b6
--- /dev/null
+++ b/xpfe/components/directory/nsDirectoryViewer.h
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsdirectoryviewer__h____
+#define nsdirectoryviewer__h____
+
+#include "nsCOMPtr.h"
+#include "nsIStreamListener.h"
+#include "nsIContentViewer.h"
+#include "nsIHTTPIndex.h"
+#include "nsIRDFService.h"
+#include "nsIRDFDataSource.h"
+#include "nsIRDFLiteral.h"
+#include "nsIDocumentLoaderFactory.h"
+#include "nsITimer.h"
+#include "nsXPIDLString.h"
+#include "nsIDirIndexListener.h"
+#include "nsIFTPChannel.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIURI.h"
+
+class nsIMutableArray;
+
+class nsDirectoryViewerFactory : public nsIDocumentLoaderFactory
+{
+public:
+ nsDirectoryViewerFactory();
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOCUMENTLOADERFACTORY
+
+protected:
+ virtual ~nsDirectoryViewerFactory();
+};
+
+class nsHTTPIndex final : public nsIHTTPIndex,
+ public nsIRDFDataSource,
+ public nsIStreamListener,
+ public nsIDirIndexListener,
+ public nsIInterfaceRequestor,
+ public nsIFTPEventSink
+{
+private:
+
+ // note: these are NOT statics due to the native of nsHTTPIndex
+ // where it may or may not be treated as a singleton
+
+ nsCOMPtr<nsIRDFResource> kNC_Child;
+ nsCOMPtr<nsIRDFResource> kNC_Comment;
+ nsCOMPtr<nsIRDFResource> kNC_Loading;
+ nsCOMPtr<nsIRDFResource> kNC_URL;
+ nsCOMPtr<nsIRDFResource> kNC_Description;
+ nsCOMPtr<nsIRDFResource> kNC_ContentLength;
+ nsCOMPtr<nsIRDFResource> kNC_LastModified;
+ nsCOMPtr<nsIRDFResource> kNC_ContentType;
+ nsCOMPtr<nsIRDFResource> kNC_FileType;
+ nsCOMPtr<nsIRDFResource> kNC_IsContainer;
+ nsCOMPtr<nsIRDFLiteral> kTrueLiteral;
+ nsCOMPtr<nsIRDFLiteral> kFalseLiteral;
+
+ nsCOMPtr<nsIRDFService> mDirRDF;
+
+protected:
+ // We grab a reference to the content viewer container (which
+ // indirectly owns us) so that we can insert ourselves as a global
+ // in the script context _after_ the XUL doc has been embedded into
+ // content viewer. We'll know that this has happened once we receive
+ // an OnStartRequest() notification
+
+ nsCOMPtr<nsIRDFDataSource> mInner;
+ nsCOMPtr<nsIMutableArray> mConnectionList;
+ nsCOMPtr<nsIMutableArray> mNodeList;
+ nsCOMPtr<nsITimer> mTimer;
+ nsCOMPtr<nsIDirIndexParser> mParser;
+ nsCString mBaseURL;
+ nsCString mEncoding;
+ bool mBindToGlobalObject;
+ nsIInterfaceRequestor* mRequestor; // WEAK
+ nsCOMPtr<nsIRDFResource> mDirectory;
+
+ explicit nsHTTPIndex(nsIInterfaceRequestor* aRequestor);
+ nsresult CommonInit(void);
+ nsresult Init(nsIURI* aBaseURL);
+ void GetDestination(nsIRDFResource* r, nsXPIDLCString& dest);
+ bool isWellknownContainerURI(nsIRDFResource *r);
+ nsresult AddElement(nsIRDFResource *parent, nsIRDFResource *prop,
+ nsIRDFNode *child);
+
+ static void FireTimer(nsITimer* aTimer, void* aClosure);
+
+ virtual ~nsHTTPIndex();
+
+public:
+ nsHTTPIndex();
+ nsresult Init(void);
+
+ static nsresult Create(nsIURI* aBaseURI, nsIInterfaceRequestor* aContainer,
+ nsIHTTPIndex** aResult);
+
+ // nsIHTTPIndex interface
+ NS_DECL_NSIHTTPINDEX
+
+ // NSIRDFDataSource interface
+ NS_DECL_NSIRDFDATASOURCE
+
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+
+ NS_DECL_NSIDIRINDEXLISTENER
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSIFTPEVENTSINK
+
+ // nsISupports interface
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsHTTPIndex, nsIHTTPIndex)
+};
+
+// {82776710-5690-11d3-BE36-00104BDE6048}
+#define NS_DIRECTORYVIEWERFACTORY_CID \
+{ 0x82776710, 0x5690, 0x11d3, { 0xbe, 0x36, 0x0, 0x10, 0x4b, 0xde, 0x60, 0x48 } }
+
+#endif // nsdirectoryviewer__h____
diff --git a/xpfe/components/directory/nsIHTTPIndex.idl b/xpfe/components/directory/nsIHTTPIndex.idl
new file mode 100644
index 000000000..47697172b
--- /dev/null
+++ b/xpfe/components/directory/nsIHTTPIndex.idl
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+
+ The interface to an HTTP index
+
+*/
+
+#include "nsISupports.idl"
+
+interface nsIStreamListener;
+interface nsIRDFDataSource;
+interface nsIRDFNode;
+interface nsIRDFResource;
+
+[scriptable, uuid(6F2BDBD0-58C3-11d3-BE36-00104BDE6048)]
+interface nsIHTTPIndex : nsISupports
+{
+ /**
+ * The base URL of the HTTP index
+ */
+ readonly attribute string BaseURL;
+
+ /**
+ * The RDF datasource that contains the HTTP index information.
+ */
+ readonly attribute nsIRDFDataSource DataSource;
+
+ /**
+ * The charset to use for decoding FTP filenames
+ */
+ attribute string encoding;
+};
+
+%{C++
+
+// {{2587e382-1324-11d4-a652-eadbb2be3484}
+#define NS_HTTPINDEX_SERVICE_CID \
+{ 0x2587e382, 0x1324, 0x11d4, { 0xa6, 0x52, 0xea, 0xdb, 0xb2, 0xbe, 0x34, 0x84 } }
+
+#define NS_HTTPINDEX_SERVICE_CONTRACTID \
+ "@mozilla.org/browser/httpindex-service;1"
+
+#define NS_HTTPINDEX_DATASOURCE_CONTRACTID \
+ "@mozilla.org/rdf/datasource;1?name=httpindex"
+
+%}
diff --git a/xpfe/components/moz.build b/xpfe/components/moz.build
new file mode 100644
index 000000000..cb23f336e
--- /dev/null
+++ b/xpfe/components/moz.build
@@ -0,0 +1,12 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += [
+ 'windowds',
+ 'directory',
+ 'build',
+]
+
diff --git a/xpfe/components/windowds/moz.build b/xpfe/components/windowds/moz.build
new file mode 100644
index 000000000..0e7536b16
--- /dev/null
+++ b/xpfe/components/windowds/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPIDL_SOURCES += [
+ 'nsIWindowDataSource.idl',
+]
+
+XPIDL_MODULE = 'windowds'
+
+SOURCES += [
+ 'nsWindowDataSource.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
diff --git a/xpfe/components/windowds/nsIWindowDataSource.idl b/xpfe/components/windowds/nsIWindowDataSource.idl
new file mode 100644
index 000000000..6143a4317
--- /dev/null
+++ b/xpfe/components/windowds/nsIWindowDataSource.idl
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIDOMWindow.idl"
+
+// interface for accessing RDF-specific data from the window datasource
+[scriptable, uuid(3722A5B9-5323-4ed0-BB1A-8299F27A4E89)]
+interface nsIWindowDataSource : nsISupports
+{
+ /**
+ * for the given resource name, return the window
+ */
+ nsIDOMWindow getWindowForResource(in string inResource);
+};
diff --git a/xpfe/components/windowds/nsWindowDataSource.cpp b/xpfe/components/windowds/nsWindowDataSource.cpp
new file mode 100644
index 000000000..3e7a42060
--- /dev/null
+++ b/xpfe/components/windowds/nsWindowDataSource.cpp
@@ -0,0 +1,519 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsWindowDataSource.h"
+#include "nsIXULWindow.h"
+#include "rdf.h"
+#include "nsIRDFContainerUtils.h"
+#include "nsIServiceManager.h"
+#include "nsReadableUtils.h"
+#include "nsIObserverService.h"
+#include "nsIWindowMediator.h"
+#include "nsXPCOMCID.h"
+#include "mozilla/ModuleUtils.h"
+#include "nsString.h"
+
+// just to do the reverse-lookup! sheesh.
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIDocShell.h"
+
+uint32_t nsWindowDataSource::windowCount = 0;
+
+nsIRDFResource* nsWindowDataSource::kNC_Name = nullptr;
+nsIRDFResource* nsWindowDataSource::kNC_WindowRoot = nullptr;
+nsIRDFResource* nsWindowDataSource::kNC_KeyIndex = nullptr;
+
+nsIRDFService* nsWindowDataSource::gRDFService = nullptr;
+
+uint32_t nsWindowDataSource::gRefCnt = 0;
+
+#define URINC_WINDOWROOT "NC:WindowMediatorRoot"
+#define URINC_NAME NC_NAMESPACE_URI "Name"
+#define URINC_KEYINDEX NC_NAMESPACE_URI "KeyIndex"
+
+nsresult
+nsWindowDataSource::Init()
+{
+ nsresult rv;
+
+ if (gRefCnt++ == 0) {
+ rv = CallGetService("@mozilla.org/rdf/rdf-service;1", &gRDFService);
+ if (NS_FAILED(rv)) return rv;
+
+ gRDFService->GetResource(NS_LITERAL_CSTRING(URINC_WINDOWROOT), &kNC_WindowRoot);
+ gRDFService->GetResource(NS_LITERAL_CSTRING(URINC_NAME), &kNC_Name);
+ gRDFService->GetResource(NS_LITERAL_CSTRING(URINC_KEYINDEX), &kNC_KeyIndex);
+ }
+
+ mInner = do_CreateInstance("@mozilla.org/rdf/datasource;1?name=in-memory-datasource", &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIRDFContainerUtils> rdfc =
+ do_GetService("@mozilla.org/rdf/container-utils;1", &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = rdfc->MakeSeq(this, kNC_WindowRoot, getter_AddRefs(mContainer));
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIWindowMediator> windowMediator =
+ do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = windowMediator->AddListener(this);
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIObserverService> observerService =
+ do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv)) {
+ rv = observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
+ false);
+ }
+ return NS_OK;
+}
+
+nsWindowDataSource::~nsWindowDataSource()
+{
+ if (--gRefCnt == 0) {
+ NS_IF_RELEASE(kNC_Name);
+ NS_IF_RELEASE(kNC_KeyIndex);
+ NS_IF_RELEASE(kNC_WindowRoot);
+ NS_IF_RELEASE(gRDFService);
+ }
+}
+
+NS_IMETHODIMP
+nsWindowDataSource::Observe(nsISupports *aSubject, const char* aTopic, const char16_t *aData)
+{
+ if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
+ // release these objects so that they release their reference
+ // to us
+ mContainer = nullptr;
+ mInner = nullptr;
+ }
+
+ return NS_OK;
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsWindowDataSource)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsWindowDataSource)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsWindowDataSource)
+ // XXX mContainer?
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInner)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsWindowDataSource)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsWindowDataSource)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsWindowDataSource)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIWindowMediatorListener)
+ NS_INTERFACE_MAP_ENTRY(nsIWindowDataSource)
+ NS_INTERFACE_MAP_ENTRY(nsIRDFDataSource)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
+NS_INTERFACE_MAP_END
+
+// nsIWindowMediatorListener implementation
+// handle notifications from the window mediator and reflect them into
+// RDF
+
+NS_IMETHODIMP
+nsWindowDataSource::OnWindowTitleChange(nsIXULWindow *window,
+ const char16_t *newTitle)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIRDFResource> windowResource;
+ mWindowResources.Get(window, getter_AddRefs(windowResource));
+
+ // oops, make sure this window is in the hashtable!
+ if (!windowResource) {
+ OnOpenWindow(window);
+ mWindowResources.Get(window, getter_AddRefs(windowResource));
+ }
+
+ NS_ENSURE_TRUE(windowResource, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsIRDFLiteral> newTitleLiteral;
+ rv = gRDFService->GetLiteral(newTitle, getter_AddRefs(newTitleLiteral));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // get the old title
+ nsCOMPtr<nsIRDFNode> oldTitleNode;
+ rv = GetTarget(windowResource, kNC_Name, true,
+ getter_AddRefs(oldTitleNode));
+
+ // assert the change
+ if (NS_SUCCEEDED(rv) && oldTitleNode)
+ // has an existing window title, update it
+ rv = Change(windowResource, kNC_Name, oldTitleNode, newTitleLiteral);
+ else
+ // removed from the tasklist
+ rv = Assert(windowResource, kNC_Name, newTitleLiteral, true);
+
+ if (rv != NS_RDF_ASSERTION_ACCEPTED)
+ {
+ NS_ERROR("unable to set window name");
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowDataSource::OnOpenWindow(nsIXULWindow *window)
+{
+ nsAutoCString windowId(NS_LITERAL_CSTRING("window-"));
+ windowId.AppendInt(windowCount++, 10);
+
+ nsCOMPtr<nsIRDFResource> windowResource;
+ gRDFService->GetResource(windowId, getter_AddRefs(windowResource));
+
+ mWindowResources.Put(window, windowResource);
+
+ // assert the new window
+ if (mContainer)
+ mContainer->AppendElement(windowResource);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowDataSource::OnCloseWindow(nsIXULWindow *window)
+{
+ nsresult rv;
+ nsCOMPtr<nsIRDFResource> resource;
+ mWindowResources.Get(window, getter_AddRefs(resource));
+ if (!resource) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ mWindowResources.Remove(window);
+
+ // make sure we're not shutting down
+ if (!mContainer) return NS_OK;
+
+ nsCOMPtr<nsIRDFNode> oldKeyNode;
+ nsCOMPtr<nsIRDFInt> oldKeyInt;
+
+ // get the old keyIndex, if any
+ rv = GetTarget(resource, kNC_KeyIndex, true,
+ getter_AddRefs(oldKeyNode));
+ if (NS_SUCCEEDED(rv) && (rv != NS_RDF_NO_VALUE))
+ oldKeyInt = do_QueryInterface(oldKeyNode);
+
+
+ // update RDF and keyindex - from this point forward we'll ignore
+ // errors, because they just indicate some kind of RDF inconsistency
+ int32_t winIndex = -1;
+ rv = mContainer->IndexOf(resource, &winIndex);
+
+ if (NS_FAILED(rv))
+ return NS_OK;
+
+ // unassert the old window, ignore any error
+ mContainer->RemoveElement(resource, true);
+
+ nsCOMPtr<nsISimpleEnumerator> children;
+ rv = mContainer->GetElements(getter_AddRefs(children));
+ if (NS_FAILED(rv))
+ return NS_OK;
+
+ bool more = false;
+
+ while (NS_SUCCEEDED(rv = children->HasMoreElements(&more)) && more) {
+ nsCOMPtr<nsISupports> sup;
+ rv = children->GetNext(getter_AddRefs(sup));
+ if (NS_FAILED(rv))
+ break;
+
+ nsCOMPtr<nsIRDFResource> windowResource = do_QueryInterface(sup, &rv);
+ if (NS_FAILED(rv))
+ continue;
+
+ int32_t currentIndex = -1;
+ mContainer->IndexOf(windowResource, &currentIndex);
+
+ // can skip updating windows with lower indexes
+ // than the window that was removed
+ if (currentIndex < winIndex)
+ continue;
+
+ nsCOMPtr<nsIRDFNode> newKeyNode;
+ nsCOMPtr<nsIRDFInt> newKeyInt;
+
+ rv = GetTarget(windowResource, kNC_KeyIndex, true,
+ getter_AddRefs(newKeyNode));
+ if (NS_SUCCEEDED(rv) && (rv != NS_RDF_NO_VALUE))
+ newKeyInt = do_QueryInterface(newKeyNode);
+
+ // changing from one key index to another
+ if (oldKeyInt && newKeyInt)
+ Change(windowResource, kNC_KeyIndex, oldKeyInt, newKeyInt);
+ // creating a new keyindex - probably window going
+ // from (none) to "9"
+ else if (newKeyInt)
+ Assert(windowResource, kNC_KeyIndex, newKeyInt, true);
+
+ // somehow inserting a window above this one,
+ // "9" to (none)
+ else if (oldKeyInt)
+ Unassert(windowResource, kNC_KeyIndex, oldKeyInt);
+
+ }
+ return NS_OK;
+}
+
+// nsIWindowDataSource implementation
+
+NS_IMETHODIMP
+nsWindowDataSource::GetWindowForResource(const char *aResourceString,
+ nsIDOMWindow** aResult)
+{
+ if (NS_WARN_IF(!aResourceString)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsCOMPtr<nsIRDFResource> windowResource;
+ gRDFService->GetResource(nsDependentCString(aResourceString),
+ getter_AddRefs(windowResource));
+
+ // now reverse-lookup in the hashtable
+ for (auto iter = mWindowResources.Iter(); !iter.Done(); iter.Next()) {
+ nsIXULWindow* window = iter.Key();
+ nsIRDFResource* resource = iter.UserData();
+
+ if (resource == windowResource) {
+ // This sucks, we have to jump through docshell to go from
+ // nsIXULWindow -> nsIDOMWindow.
+ nsCOMPtr<nsIDocShell> docShell;
+ window->GetDocShell(getter_AddRefs(docShell));
+
+ if (docShell) {
+ nsCOMPtr<nsIDOMWindow> result = do_GetInterface(docShell);
+
+ *aResult = result;
+ NS_IF_ADDREF(*aResult);
+ }
+ break;
+ }
+ }
+
+ return NS_OK;
+}
+
+
+// nsIRDFDataSource implementation
+// mostly, we just forward to mInner, except:
+// GetURI() - need to return "rdf:window-mediator"
+// GetTarget() - need to handle kNC_KeyIndex
+
+
+NS_IMETHODIMP nsWindowDataSource::GetURI(char * *aURI)
+{
+ NS_ENSURE_ARG_POINTER(aURI);
+
+ *aURI = ToNewCString(NS_LITERAL_CSTRING("rdf:window-mediator"));
+
+ if (!*aURI)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWindowDataSource::GetTarget(nsIRDFResource *aSource, nsIRDFResource *aProperty, bool aTruthValue, nsIRDFNode **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ // add extra nullptr checking for top-crash bug # 146466
+ if (!gRDFService) return NS_RDF_NO_VALUE;
+ if (!mInner) return NS_RDF_NO_VALUE;
+ if (!mContainer) return NS_RDF_NO_VALUE;
+ // special case kNC_KeyIndex before we forward to mInner
+ if (aProperty == kNC_KeyIndex) {
+
+ int32_t theIndex = 0;
+ nsresult rv = mContainer->IndexOf(aSource, &theIndex);
+ if (NS_FAILED(rv)) return rv;
+
+ // only allow the range of 1 to 9 for single key access
+ if (theIndex < 1 || theIndex > 9) return(NS_RDF_NO_VALUE);
+
+ nsCOMPtr<nsIRDFInt> indexInt;
+ rv = gRDFService->GetIntLiteral(theIndex, getter_AddRefs(indexInt));
+ if (NS_FAILED(rv)) return(rv);
+ if (!indexInt) return(NS_ERROR_FAILURE);
+
+ indexInt.forget(_retval);
+ return NS_OK;
+ }
+
+ return mInner->GetTarget(aSource, aProperty, aTruthValue, _retval);
+}
+
+NS_IMETHODIMP nsWindowDataSource::GetSource(nsIRDFResource *aProperty, nsIRDFNode *aTarget, bool aTruthValue, nsIRDFResource **_retval)
+{
+ if (mInner)
+ return mInner->GetSource(aProperty, aTarget, aTruthValue, _retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWindowDataSource::GetSources(nsIRDFResource *aProperty, nsIRDFNode *aTarget, bool aTruthValue, nsISimpleEnumerator **_retval)
+{
+ if (mInner)
+ return mInner->GetSources(aProperty, aTarget, aTruthValue, _retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWindowDataSource::GetTargets(nsIRDFResource *aSource, nsIRDFResource *aProperty, bool aTruthValue, nsISimpleEnumerator **_retval)
+{
+ if (mInner)
+ return mInner->GetTargets(aSource, aProperty, aTruthValue, _retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWindowDataSource::Assert(nsIRDFResource *aSource, nsIRDFResource *aProperty, nsIRDFNode *aTarget, bool aTruthValue)
+{
+ if (mInner)
+ return mInner->Assert(aSource, aProperty, aTarget, aTruthValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWindowDataSource::Unassert(nsIRDFResource *aSource, nsIRDFResource *aProperty, nsIRDFNode *aTarget)
+{
+ if (mInner)
+ return mInner->Unassert(aSource, aProperty, aTarget);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWindowDataSource::Change(nsIRDFResource *aSource, nsIRDFResource *aProperty, nsIRDFNode *aOldTarget, nsIRDFNode *aNewTarget)
+{
+ if (mInner)
+ return mInner->Change(aSource, aProperty, aOldTarget, aNewTarget);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWindowDataSource::Move(nsIRDFResource *aOldSource, nsIRDFResource *aNewSource, nsIRDFResource *aProperty, nsIRDFNode *aTarget)
+{
+ if (mInner)
+ return mInner->Move(aOldSource, aNewSource, aProperty, aTarget);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWindowDataSource::HasAssertion(nsIRDFResource *aSource, nsIRDFResource *aProperty, nsIRDFNode *aTarget, bool aTruthValue, bool *_retval)
+{
+ if (mInner)
+ return mInner->HasAssertion(aSource, aProperty, aTarget, aTruthValue, _retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWindowDataSource::AddObserver(nsIRDFObserver *aObserver)
+{
+ if (mInner)
+ return mInner->AddObserver(aObserver);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWindowDataSource::RemoveObserver(nsIRDFObserver *aObserver)
+{
+ if (mInner)
+ return mInner->RemoveObserver(aObserver);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWindowDataSource::ArcLabelsIn(nsIRDFNode *aNode, nsISimpleEnumerator **_retval)
+{
+ if (mInner)
+ return mInner->ArcLabelsIn(aNode, _retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWindowDataSource::ArcLabelsOut(nsIRDFResource *aSource, nsISimpleEnumerator **_retval)
+{
+ if (mInner)
+ return mInner->ArcLabelsOut(aSource, _retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWindowDataSource::GetAllResources(nsISimpleEnumerator **_retval)
+{
+ if (mInner)
+ return mInner->GetAllResources(_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWindowDataSource::IsCommandEnabled(nsISupports *aSources, nsIRDFResource *aCommand, nsISupports *aArguments, bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsWindowDataSource::DoCommand(nsISupports *aSources, nsIRDFResource *aCommand, nsISupports *aArguments)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsWindowDataSource::GetAllCmds(nsIRDFResource *aSource, nsISimpleEnumerator **_retval)
+{
+ if (mInner)
+ return mInner->GetAllCmds(aSource, _retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWindowDataSource::HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, bool *_retval)
+{
+ if (mInner)
+ return mInner->HasArcIn(aNode, aArc, _retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWindowDataSource::HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, bool *_retval)
+{
+ if (mInner)
+ return mInner->HasArcOut(aSource, aArc, _retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWindowDataSource::BeginUpdateBatch()
+{
+ if (mInner)
+ return mInner->BeginUpdateBatch();
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWindowDataSource::EndUpdateBatch()
+{
+ if (mInner)
+ return mInner->EndUpdateBatch();
+ return NS_OK;
+}
+
+// The module goop
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsWindowDataSource, Init)
+
+NS_DEFINE_NAMED_CID(NS_WINDOWDATASOURCE_CID);
+
+static const mozilla::Module::CIDEntry kWindowDSCIDs[] = {
+ { &kNS_WINDOWDATASOURCE_CID, false, nullptr, nsWindowDataSourceConstructor },
+ { nullptr }
+};
+
+static const mozilla::Module::ContractIDEntry kWindowDSContracts[] = {
+ { NS_RDF_DATASOURCE_CONTRACTID_PREFIX "window-mediator", &kNS_WINDOWDATASOURCE_CID },
+ { nullptr }
+};
+
+static const mozilla::Module::CategoryEntry kWindowDSCategories[] = {
+ { "app-startup", "Window Data Source", "service," NS_RDF_DATASOURCE_CONTRACTID_PREFIX "window-mediator" },
+ { nullptr }
+};
+
+static const mozilla::Module kWindowDSModule = {
+ mozilla::Module::kVersion,
+ kWindowDSCIDs,
+ kWindowDSContracts,
+ kWindowDSCategories
+};
+
+NSMODULE_DEFN(nsWindowDataSourceModule) = &kWindowDSModule;
diff --git a/xpfe/components/windowds/nsWindowDataSource.h b/xpfe/components/windowds/nsWindowDataSource.h
new file mode 100644
index 000000000..351ff951b
--- /dev/null
+++ b/xpfe/components/windowds/nsWindowDataSource.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIRDFDataSource.h"
+#include "nsIWindowMediatorListener.h"
+#include "nsIWindowDataSource.h"
+#include "nsIObserver.h"
+
+#include "nsHashKeys.h"
+#include "nsIRDFService.h"
+#include "nsIRDFContainer.h"
+#include "nsInterfaceHashtable.h"
+#include "nsCycleCollectionParticipant.h"
+
+// {C744CA3D-840B-460a-8D70-7CE63C51C958}
+#define NS_WINDOWDATASOURCE_CID \
+{ 0xc744ca3d, 0x840b, 0x460a, \
+ { 0x8d, 0x70, 0x7c, 0xe6, 0x3c, 0x51, 0xc9, 0x58 } }
+
+
+class nsWindowDataSource final : public nsIRDFDataSource,
+ public nsIObserver,
+ public nsIWindowMediatorListener,
+ public nsIWindowDataSource
+{
+ public:
+ nsWindowDataSource() { }
+
+ nsresult Init();
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsWindowDataSource,
+ nsIRDFDataSource)
+ NS_DECL_NSIOBSERVER
+ NS_DECL_NSIWINDOWMEDIATORLISTENER
+ NS_DECL_NSIWINDOWDATASOURCE
+ NS_DECL_NSIRDFDATASOURCE
+
+ protected:
+ virtual ~nsWindowDataSource();
+
+ private:
+
+ // mapping of window -> RDF resource
+ nsInterfaceHashtable<nsPtrHashKey<nsIXULWindow>, nsIRDFResource> mWindowResources;
+
+ static uint32_t windowCount;
+ static uint32_t gRefCnt;
+
+ nsCOMPtr<nsIRDFDataSource> mInner;
+ nsCOMPtr<nsIRDFContainer> mContainer;
+
+ static nsIRDFResource* kNC_Name;
+ static nsIRDFResource* kNC_KeyIndex;
+ static nsIRDFResource* kNC_WindowRoot;
+ static nsIRDFService* gRDFService;
+};
diff --git a/xpfe/test/child-window.html b/xpfe/test/child-window.html
new file mode 100644
index 000000000..228ea3462
--- /dev/null
+++ b/xpfe/test/child-window.html
@@ -0,0 +1,5 @@
+<html><body onload="
+ var gLoadEventTime = (new Date()).getTime();
+ if (window.opener)
+ window.setTimeout('window.opener.childIsOpen('+gLoadEventTime+');',1000);
+"></body></html>
diff --git a/xpfe/test/winopen.js b/xpfe/test/winopen.js
new file mode 100644
index 000000000..11548c441
--- /dev/null
+++ b/xpfe/test/winopen.js
@@ -0,0 +1,244 @@
+// target for window.open()
+const KID_URL = "child-window.html";
+
+// formats final results
+const SERVER_URL = "http://jrgm.mcom.com/cgi-bin/window-open-2.0/openreport.pl";
+
+// let system settle between each window.open
+const OPENER_DELAY = 1000;
+
+// three phases: single open/close; overlapped open/close; open-all/close-all
+var PHASE_ONE = 10;
+var PHASE_TWO = 0;
+var PHASE_THREE = 0;
+
+// keep this many windows concurrently open during overlapped phase
+var OVERLAP_COUNT = 3;
+
+// repeat three phases CYCLES times
+var CYCLES = 1;
+
+// autoclose flag
+var AUTOCLOSE = 1;
+
+// Chrome url for child windows.
+var KID_CHROME = null;
+var SAVED_CHROME = null;
+
+// URL options and correspnding vars.
+const options = [ [ "phase1", "PHASE_ONE", false ],
+ [ "phase2", "PHASE_TWO", false ],
+ [ "phase3", "PHASE_THREE", false ],
+ [ "overlap", "OVERLAP_COUNT", false ],
+ [ "cycles", "CYCLES", false ],
+ [ "chrome", "KID_CHROME", true ],
+ [ "close", "AUTOCLOSE", false ] ];
+
+// Note: You can attach search options to the url for this file to control
+// any of the options in the array above. E.g., specifying
+// mozilla --chrome "file:///D|/mozilla/xpfe/test/winopen.xul?phase1=16&close=0"
+// will run this script with PHASE_ONE=16 and AUTOCLOSE=0.
+//
+// On Win32, you must enclose the --chrome option in quotes in order pass funny Win32 shell
+// characters such as '&' or '|'!
+
+var opts = window.location.search.substring(1).split( '&' );
+for ( opt in opts ) {
+ for ( var i in options ) {
+ if ( opts[opt].indexOf( options[i][0]+"=" ) == 0 ) {
+ var newVal = opts[opt].split( '=' )[ 1 ];
+ // wrap with quotes, if required.
+ if ( options[i][2] ) {
+ newVal = '"' + newVal + '"';
+ }
+ eval( options[i][1] + "=" + newVal + ";" );
+ }
+ }
+}
+
+var prefs = null;
+
+if ( KID_CHROME ) {
+ // Reset browser.chromeURL so it points to KID_CHROME.
+ // This will cause window.open in openWindow to open that chrome.
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ prefs = Components.classes["@mozilla.org/preferences-service;1"]
+ .getService( Components.interfaces.nsIPrefBranch );
+ SAVED_CHROME = prefs.getCharPref( "browser.chromeURL" );
+ prefs.setCharPref( "browser.chromeURL", KID_CHROME );
+}
+
+const CYCLE_SIZE = PHASE_ONE + PHASE_TWO + PHASE_THREE;
+const MAX_INDEX = CYCLE_SIZE * CYCLES; // total number of windows to open
+
+var windowList = []; // handles to opened windows
+var startingTimes = []; // time that window.open is called
+var openingTimes = []; // time that child window took to fire onload
+var closingTimes = []; // collect stats for case of closing >1 windows
+var currentIndex = 0;
+
+
+function childIsOpen(aTime) {
+ openingTimes[currentIndex] = aTime - startingTimes[currentIndex];
+ updateDisplay(currentIndex, openingTimes[currentIndex]);
+ reapWindows(currentIndex);
+ currentIndex++;
+ if (currentIndex < MAX_INDEX)
+ scheduleNextWindow();
+ else
+ window.setTimeout(reportResults, OPENER_DELAY);
+}
+
+
+function updateDisplay(index, time) {
+ var formIndex = document.getElementById("formIndex");
+ if (formIndex)
+ formIndex.setAttribute("value", index+1);
+ var formTime = document.getElementById("formTime");
+ if (formTime)
+ formTime.setAttribute("value", time);
+}
+
+
+function scheduleNextWindow() {
+ window.setTimeout(openWindow, OPENER_DELAY);
+}
+
+
+function closeOneWindow(aIndex) {
+ var win = windowList[aIndex];
+ // no-op if window is already closed
+ if (win && !win.closed) {
+ win.close();
+ windowList[aIndex] = null;
+ }
+}
+
+
+function closeAllWindows(aRecordTimeToClose) {
+ var timeToClose = (new Date()).getTime();
+ var count = 0;
+ for (var i = 0; i < windowList.length; i++) {
+ if (windowList[i])
+ count++;
+ closeOneWindow(i);
+ }
+ if (aRecordTimeToClose && count > 0) {
+ timeToClose = (new Date()).getTime() - timeToClose;
+ closingTimes.push(parseInt(timeToClose/count));
+ }
+}
+
+
+// close some, none, or all open windows in the list
+function reapWindows() {
+ var modIndex = currentIndex % CYCLE_SIZE;
+ if (modIndex < PHASE_ONE-1) {
+ // first phase in each "cycle", are single open/close sequences
+ closeOneWindow(currentIndex);
+ }
+ else if (PHASE_ONE-1 <= modIndex && modIndex < PHASE_ONE+PHASE_TWO-1) {
+ // next phase in each "cycle", keep N windows concurrently open
+ closeOneWindow(currentIndex - OVERLAP_COUNT);
+ }
+ else if (modIndex == PHASE_ONE+PHASE_TWO-1) {
+ // end overlapping windows cycle; close all windows
+ closeAllWindows(false);
+ }
+ else if (PHASE_ONE+PHASE_TWO <= modIndex && modIndex < CYCLE_SIZE-1) {
+ // do nothing; keep adding windows
+ }
+ else if (modIndex == CYCLE_SIZE-1) {
+ // end open-all/close-all phase; close windows, recording time to close
+ closeAllWindows(true);
+ }
+}
+
+function calcMedian( numbers ) {
+ if ( numbers.length == 0 ) {
+ return 0;
+ } else if ( numbers.length == 1 ) {
+ return numbers[0];
+ } else if ( numbers.length == 2 ) {
+ return ( numbers[0] + numbers[1] ) / 2;
+ } else {
+ numbers.sort( function (a,b){ return a-b; } );
+ var n = Math.floor( numbers.length / 2 );
+ return numbers.length % 2 ? numbers[n] : ( numbers[n-1] + numbers[n] ) / 2;
+ }
+}
+
+function reportResults() {
+ //XXX need to create a client-side method to do this?
+ var opening = openingTimes.join(':'); // times for each window open
+ var closing = closingTimes.join(':'); // these are for >1 url, as a group
+ //var ua = escape(navigator.userAgent).replace(/\+/g, "%2B"); // + == ' ', on servers
+ //var reportURL = SERVER_URL +
+ // "?opening=" + opening +
+ // "&closing=" + closing +
+ // "&maxIndex=" + MAX_INDEX +
+ // "&cycleSize=" + CYCLE_SIZE +
+ //"&ua=" + ua;
+ //window.open(reportURL, "test-results");
+ var avgOpenTime = 0;
+ var minOpenTime = 99999;
+ var maxOpenTime = 0;
+ var medOpenTime = calcMedian( openingTimes.slice(1) );
+ // ignore first open
+ for (i = 1; i < MAX_INDEX; i++) {
+ avgOpenTime += openingTimes[i];
+ if ( minOpenTime > openingTimes[i] ) {
+ minOpenTime = openingTimes[i];
+ }
+ if ( maxOpenTime < openingTimes[i] ) {
+ maxOpenTime = openingTimes[i];
+ }
+ }
+ avgOpenTime = Math.round(avgOpenTime / (MAX_INDEX - 1));
+ dump("openingTimes="+openingTimes.slice(1)+"\n");
+ dump("avgOpenTime:" + avgOpenTime + "\n" );
+ dump("minOpenTime:" + minOpenTime + "\n" );
+ dump("maxOpenTime:" + maxOpenTime + "\n" );
+ dump("medOpenTime:" + medOpenTime + "\n" );
+ dump("__xulWinOpenTime:" + medOpenTime + "\n");
+ // Close the root window, if required.
+ if ( AUTOCLOSE ) {
+ window.close();
+ } else {
+ document.getElementById("formTimes").value = openingTimes.slice(1);
+ document.getElementById("formAvg").value = avgOpenTime;
+ document.getElementById("formMin").value = minOpenTime;
+ document.getElementById("formMax").value = maxOpenTime;
+ document.getElementById("formMed").value = medOpenTime;
+ document.getElementById("formAgain").setAttribute( "disabled", "false" );
+ }
+}
+
+function tryAgain() {
+ document.getElementById("formAgain").setAttribute( "disabled", "true" );
+ windowList = [];
+ startingTimes = [];
+ openingTimes = [];
+ closingTimes = [];
+ currentIndex = 0;
+ openWindow();
+}
+
+function restoreChromeURL() {
+ // Restore browser.chromeURL pref.
+ if ( KID_CHROME && SAVED_CHROME.length ) {
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ prefs.setCharPref( "browser.chromeURL", SAVED_CHROME );
+ }
+}
+
+function openWindow() {
+ startingTimes[currentIndex] = (new Date()).getTime();
+ var path = window.location.pathname.substring( 0, window.location.pathname.lastIndexOf('/') );
+ var url = window.location.protocol + "//" +
+ window.location.hostname + path + "/" +
+ KID_URL;
+ windowList[currentIndex] = window.open(url, currentIndex);
+}
+
+
diff --git a/xpfe/test/winopen.xul b/xpfe/test/winopen.xul
new file mode 100644
index 000000000..929370b8a
--- /dev/null
+++ b/xpfe/test/winopen.xul
@@ -0,0 +1,75 @@
+<?xml version="1.0"?> <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<window
+xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+orient="vertical"
+ height="300"
+ width="400"
+ windowtype="opener:test"
+ onunload="restoreChromeURL();"
+ onload="scheduleNextWindow();">
+
+<script src="winopen.js" type="application/x-javascript"></script>
+
+<groupbox orient="vertical">
+<caption label="Window Opening Test"/>
+<html>
+This will open a series of browser windows, either "one at a
+ time" or in a sequence of some form. When this test is complete
+ a final window will be opened which will report the overall results.
+ </html>
+<separator class="thick"/>
+<grid>
+<columns><column/><column/></columns>
+<rows>
+<row align="center">
+<text value="Index:"/>
+<textbox id="formIndex" size="6" value=""/>
+</row>
+<row align="center">
+<text value="Time:"/>
+<textbox id="formTime" size="6" value=""/>
+<text value="msec"/>
+</row>
+</rows>
+</grid>
+<separator class="thick"/>
+</groupbox>
+
+<groupbox orient="vertical">
+ <caption label="Results"/>
+ <grid>
+ <columns>
+ <column/>
+ <column/>
+ </columns>
+ <rows>
+ <row align="center">
+ <text value="Times (ignoring the first):"/>
+ <textbox id="formTimes" size="45" value=""/>
+ </row>
+ <row align="center">
+ <text value="Txul (median):"/>
+ <hbox>
+ <textbox id="formMed" size="6" value=""/>
+ <spring/>
+ <button id="formAgain" onclick="tryAgain();" label="Try again" disabled="true"/>
+ </hbox>
+ </row>
+ <row align="center">
+ <text value="Avg:"/>
+ <hbox><textbox id="formAvg" size="6" value=""/></hbox>
+ </row>
+ <row align="center">
+ <text value="Min:"/>
+ <hbox><textbox id="formMin" size="6" value=""/></hbox>
+ </row>
+ <row align="center">
+ <text value="Max:"/>
+ <hbox><textbox id="formMax" size="6" value=""/></hbox>
+ </row>
+ </rows>
+ </grid>
+</groupbox>
+
+</window>
+