summaryrefslogtreecommitdiffstats
path: root/widget/nsScreenManagerProxy.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/nsScreenManagerProxy.cpp')
-rw-r--r--widget/nsScreenManagerProxy.cpp218
1 files changed, 218 insertions, 0 deletions
diff --git a/widget/nsScreenManagerProxy.cpp b/widget/nsScreenManagerProxy.cpp
new file mode 100644
index 000000000..18f400ff5
--- /dev/null
+++ b/widget/nsScreenManagerProxy.cpp
@@ -0,0 +1,218 @@
+/* -*- 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/. */
+
+#include "mozilla/Unused.h"
+#include "mozilla/dom/ContentChild.h"
+#include "nsScreenManagerProxy.h"
+#include "nsServiceManagerUtils.h"
+#include "nsContentUtils.h"
+#include "nsIAppShell.h"
+#include "nsIScreen.h"
+#include "nsIScreenManager.h"
+#include "nsWidgetsCID.h"
+
+static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::widget;
+
+NS_IMPL_ISUPPORTS(nsScreenManagerProxy, nsIScreenManager)
+
+nsScreenManagerProxy::nsScreenManagerProxy()
+ : mNumberOfScreens(-1)
+ , mSystemDefaultScale(1.0)
+ , mCacheValid(true)
+ , mCacheWillInvalidate(false)
+{
+ bool success = false;
+ Unused << ContentChild::GetSingleton()->SendPScreenManagerConstructor(
+ this,
+ &mNumberOfScreens,
+ &mSystemDefaultScale,
+ &success);
+
+ if (!success) {
+ // We're in bad shape. We'll return the default values, but we'll basically
+ // be lying.
+ NS_WARNING("Setting up communications with the parent nsIScreenManager failed.");
+ }
+
+ InvalidateCacheOnNextTick();
+
+ // nsScreenManagerProxy is a service, which will always have a reference
+ // held to it by the Component Manager once the service is requested.
+ // However, nsScreenManagerProxy also implements PScreenManagerChild, and
+ // that means that the manager of the PScreenManager protocol (PContent
+ // in this case) needs to know how to deallocate this actor. We AddRef here
+ // so that in the event that PContent tries to deallocate us either before
+ // or after process shutdown, we don't try to do a double-free.
+ AddRef();
+}
+
+/**
+ * nsIScreenManager
+ **/
+
+NS_IMETHODIMP
+nsScreenManagerProxy::GetPrimaryScreen(nsIScreen** outScreen)
+{
+ InvalidateCacheOnNextTick();
+
+ if (!mPrimaryScreen) {
+ ScreenDetails details;
+ bool success = false;
+ Unused << SendGetPrimaryScreen(&details, &success);
+ if (!success) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mPrimaryScreen = new ScreenProxy(this, details);
+ }
+ NS_ADDREF(*outScreen = mPrimaryScreen);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScreenManagerProxy::ScreenForId(uint32_t aId, nsIScreen** outScreen)
+{
+ // At this time, there's no need for child processes to query for
+ // screens by ID.
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsScreenManagerProxy::ScreenForRect(int32_t inLeft,
+ int32_t inTop,
+ int32_t inWidth,
+ int32_t inHeight,
+ nsIScreen** outScreen)
+{
+ bool success = false;
+ ScreenDetails details;
+ Unused << SendScreenForRect(inLeft, inTop, inWidth, inHeight, &details, &success);
+ if (!success) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<ScreenProxy> screen = new ScreenProxy(this, details);
+ NS_ADDREF(*outScreen = screen);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScreenManagerProxy::ScreenForNativeWidget(void* aWidget,
+ nsIScreen** outScreen)
+{
+ // Because ScreenForNativeWidget can be called numerous times
+ // indirectly from content via the DOM Screen API, we cache the
+ // results for this tick of the event loop.
+ TabChild* tabChild = static_cast<TabChild*>(aWidget);
+
+ // Enumerate the cached screen array, looking for one that has
+ // the TabChild that we're looking for...
+ for (uint32_t i = 0; i < mScreenCache.Length(); ++i) {
+ ScreenCacheEntry& curr = mScreenCache[i];
+ if (curr.mTabChild == aWidget) {
+ NS_ADDREF(*outScreen = static_cast<nsIScreen*>(curr.mScreenProxy));
+ return NS_OK;
+ }
+ }
+
+ // Never cached this screen, so we have to ask the parent process
+ // for it.
+ bool success = false;
+ ScreenDetails details;
+ Unused << SendScreenForBrowser(tabChild->GetTabId(), &details, &success);
+ if (!success) {
+ return NS_ERROR_FAILURE;
+ }
+
+ ScreenCacheEntry newEntry;
+ RefPtr<ScreenProxy> screen = new ScreenProxy(this, details);
+
+ newEntry.mScreenProxy = screen;
+ newEntry.mTabChild = tabChild;
+
+ mScreenCache.AppendElement(newEntry);
+
+ NS_ADDREF(*outScreen = screen);
+
+ InvalidateCacheOnNextTick();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScreenManagerProxy::GetNumberOfScreens(uint32_t* aNumberOfScreens)
+{
+ if (!EnsureCacheIsValid()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aNumberOfScreens = mNumberOfScreens;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScreenManagerProxy::GetSystemDefaultScale(float *aSystemDefaultScale)
+{
+ if (!EnsureCacheIsValid()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aSystemDefaultScale = mSystemDefaultScale;
+ return NS_OK;
+}
+
+bool
+nsScreenManagerProxy::EnsureCacheIsValid()
+{
+ if (mCacheValid) {
+ return true;
+ }
+
+ bool success = false;
+ // Kick off a synchronous IPC call to the parent to get the
+ // most up-to-date information.
+ Unused << SendRefresh(&mNumberOfScreens, &mSystemDefaultScale, &success);
+ if (!success) {
+ NS_WARNING("Refreshing nsScreenManagerProxy failed in the parent process.");
+ return false;
+ }
+
+ mCacheValid = true;
+
+ InvalidateCacheOnNextTick();
+ return true;
+}
+
+void
+nsScreenManagerProxy::InvalidateCacheOnNextTick()
+{
+ if (mCacheWillInvalidate) {
+ return;
+ }
+
+ mCacheWillInvalidate = true;
+
+ nsContentUtils::RunInStableState(NewRunnableMethod(this, &nsScreenManagerProxy::InvalidateCache));
+}
+
+void
+nsScreenManagerProxy::InvalidateCache()
+{
+ mCacheValid = false;
+ mCacheWillInvalidate = false;
+
+ if (mPrimaryScreen) {
+ mPrimaryScreen = nullptr;
+ }
+ for (int32_t i = mScreenCache.Length() - 1; i >= 0; --i) {
+ mScreenCache.RemoveElementAt(i);
+ }
+}
+