Bug 1294568 - Support changing the Windows system theme (e.g. to high contrast mode) in mozscreenshots for automation r?mconley. draft
authorRand Mustafa <rndmustafa@gmail.com>
Thu, 09 Nov 2017 23:36:13 -0500
changeset 714614 b38488a69f2cf8adfdd124136f202f7e4edc429b
parent 694565 40df5dd35fdb7ce3652fe4448ac8961c075c928e
child 744631 08c154063dd7d353e0d504a22b9d205702aaeb88
push id93970
push userbmo:rndmustafa@gmail.com
push dateWed, 27 Dec 2017 04:41:27 +0000
reviewersmconley
bugs1294568
milestone58.0a1
Bug 1294568 - Support changing the Windows system theme (e.g. to high contrast mode) in mozscreenshots for automation r?mconley. MozReview-Commit-ID: BwqUT4MYzYo
browser/tools/mozscreenshots/mozscreenshots/extension/configurations/SystemTheme.jsm
browser/tools/mozscreenshots/mozscreenshots/extension/lib/ChangeWindowsTheme.bat
browser/tools/mozscreenshots/mozscreenshots/extension/lib/CloseDisplayProperties.vbs
new file mode 100644
--- /dev/null
+++ b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/SystemTheme.jsm
@@ -0,0 +1,238 @@
+/* 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/. */
+
+ "use strict";
+
+ this.EXPORTED_SYMBOLS = [ "SystemTheme" ];
+
+ const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+ Cu.import("resource://gre/modules/Services.jsm");
+ Cu.import("resource://gre/modules/ctypes.jsm");
+ Cu.import("resource://gre/modules/Timer.jsm");
+ Cu.import("resource://gre/modules/Console.jsm");
+ Cu.import("resource://gre/modules/AppConstants.jsm")
+ Cu.import("resource://testing-common/TestUtils.jsm");
+
+ let SystemTheme = {
+   _libDir: null,
+   configurations: {},
+
+   init(libDir) {
+     this._libDir = libDir;
+
+     if (Services.appinfo.OS === "WINNT") {
+      Cu.import("resource://gre/modules/WindowsRegistry.jsm")
+       let env = Cc["@mozilla.org/process/environment;1"]
+                   .getService(Ci.nsIEnvironment);
+       let resourcesDir = env.get("SystemRoot") + "\\Resources";
+       let winVersion = getWindowsVersion();
+       let currentThemePath = getCurrentThemePath();
+
+       if (winVersion.majorVersion == 10) { // Windows 10
+         this.configurations = {
+           highContrast1: {
+            selectors: [":root"],
+             async applyConfig() {
+              await SystemTheme._changeWindowsTheme(resourcesDir + "\\Ease of Access Themes\\hc1.theme");
+             }
+           },
+           highContrast2: {
+            selectors: [":root"],
+             async applyConfig() {
+              await SystemTheme._changeWindowsTheme(resourcesDir + "\\Ease of Access Themes\\hc2.theme");
+             }
+           },
+           highContrastBlack: {
+            selectors: [":root"],
+            async applyConfig() {
+             await SystemTheme._changeWindowsTheme(resourcesDir + "\\Ease of Access Themes\\hcblack.theme");
+            }
+          },
+          highContrastWhite: {
+            selectors: [":root"],
+            async applyConfig() {
+             await SystemTheme._changeWindowsTheme(resourcesDir + "\\Ease of Access Themes\\hcwhite.theme");
+            }
+          },
+           aero: {
+            selectors: [":root"],
+             async applyConfig() {
+              await SystemTheme._changeWindowsTheme(resourcesDir + "\\Themes\\aero.theme");
+             }
+           },
+           userTheme: {
+            selectors: [":root"],
+             async applyConfig() {
+               await SystemTheme._changeWindowsTheme(currentThemePath);
+             }
+           }
+         }
+       } else if (AppConstants.isPlatformAndVersionAtLeast("win", "6")) { // Windows 7 and 8
+         this.configurations = {
+           aeroGlass: {
+            selectors: [":root"],
+             async applyConfig() {
+               await SystemTheme._changeWindowsTheme(resourcesDir + "\\Themes\\aero.theme");
+             },
+           },
+           aeroBasic: {
+            selectors: [":root"],
+             async applyConfig() {
+               await SystemTheme._changeWindowsTheme(resourcesDir + "\\Ease of Access Themes\\basic.theme");
+             },
+           },
+           classic: {
+            selectors: [":root"],
+             async applyConfig() {
+               await SystemTheme._changeWindowsTheme(resourcesDir + "\\Ease of Access Themes\\classic.theme");
+             },
+           },
+           highContrast1: {
+            selectors: [":root"],
+             async applyConfig() {
+               await SystemTheme._changeWindowsTheme(resourcesDir + "\\Ease of Access Themes\\hc1.theme");
+             },
+           },
+           userTheme: {
+            selectors: [":root"],
+            async applyConfig() {
+              await SystemTheme._changeWindowsTheme(currentThemePath);
+            }
+          }
+         };
+       }
+     } else {
+       this.configurations = {
+         defaultTheme: {
+           async applyConfig() {
+            return Promise.reject("SystemTheme is only supported on Windows");
+           },
+         },
+       };
+     }
+   },
+
+
+   // helpers //
+
+   _changeWindowsTheme(themeFilePath) {
+     let changeWindowsThemeBat = this._libDir.clone();
+     changeWindowsThemeBat.append("ChangeWindowsTheme.bat");
+     let process = Cc["@mozilla.org/process/util;1"]
+                     .createInstance(Ci.nsIProcess);
+     process.init(changeWindowsThemeBat);
+
+     // Run the process.
+     let args = [themeFilePath];
+     process.runAsync(args, args.length, function themeChangeComplete() {
+       // Focus the browser again (instead of the theme selector)
+       setTimeout(this._closeWindowsPersonalizationWindow, 1000);
+
+       setTimeout(function focusTab() {
+         let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
+         browserWindow.gBrowser.selectedBrowser.focus();
+       }, 5000);
+     }.bind(this));
+
+     // The line below fails to take screenshots, unsure why.
+     // return TestUtils.waitForCondition(() => getCurrentThemePath() == themeFilePath, "Waiting for theme to load");
+
+     return new Promise((c) => setTimeout(c, 5000)); // Need to wait for everything to settle
+   },
+
+   _closeWindowsPersonalizationWindow() {
+     try {
+       let lib = ctypes.open("user32.dll");
+
+       let findWindowFunc = lib.declare("FindWindowW", ctypes.winapi_abi, ctypes.int32_t, ctypes.jschar.ptr, ctypes.jschar.ptr);
+       // Get the Personalization window
+       let hWnd;
+       if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
+         hWnd = findWindowFunc("ApplicationFrameWindow", "Settings");
+       } else if (AppConstants.isPlatformAndVersionAtLeast("win", "6")) {
+        hWnd = findWindowFunc("CabinetWClass", "Personalization");
+       }
+
+       if (!hWnd) {
+         console.error("Cannot find the Personalization window");
+         return;
+       }
+
+       // Post the close event to the window
+       let postMessageFunc = lib.declare("PostMessageW", ctypes.winapi_abi, ctypes.bool, ctypes.int32_t, ctypes.uint32_t, ctypes.size_t, ctypes.size_t);
+       const WM_CLOSE = 16;
+       postMessageFunc(hWnd, WM_CLOSE, 0, 0);
+
+       lib.close();
+     } catch (ex) {
+       console.error(ex);
+     }
+   },
+ };
+
+
+ // helpers
+
+ /**
+  * Based on getServicePack from toolkit/mozapps/update/test/unit/test_0040_general.js
+  */
+ function getWindowsVersion() {
+   if (Services.appinfo.OS !== "WINNT")
+     throw ("getWindowsVersion called on non-Windows operating system");
+
+   const BYTE = ctypes.uint8_t;
+   const WORD = ctypes.uint16_t;
+   const DWORD = ctypes.uint32_t;
+   const WCHAR = ctypes.jschar;
+   const BOOL = ctypes.int;
+
+   // This structure is described at:
+   // http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
+   const SZCSDVERSIONLENGTH = 128;
+   const OSVERSIONINFOEXW = new ctypes.StructType("OSVERSIONINFOEXW",
+                                                  [
+                                                    {dwOSVersionInfoSize: DWORD},
+                                                    {dwMajorVersion: DWORD},
+                                                    {dwMinorVersion: DWORD},
+                                                    {dwBuildNumber: DWORD},
+                                                    {dwPlatformId: DWORD},
+                                                    {szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
+                                                    {wServicePackMajor: WORD},
+                                                    {wServicePackMinor: WORD},
+                                                    {wSuiteMask: WORD},
+                                                    {wProductType: BYTE},
+                                                    {wReserved: BYTE}
+                                                  ]);
+
+   let kernel32 = ctypes.open("kernel32");
+   try {
+     let GetVersionEx = kernel32.declare("GetVersionExW",
+                                         ctypes.default_abi,
+                                         BOOL,
+                                         OSVERSIONINFOEXW.ptr);
+     let winVer = OSVERSIONINFOEXW();
+     winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
+
+     if (0 === GetVersionEx(winVer.address())) {
+       throw ("Failure in GetVersionEx (returned 0)");
+     }
+
+     let versionInfo = {
+       majorVersion: winVer.dwMajorVersion,
+       minorVersion: winVer.dwMinorVersion,
+       servicePackMajor: winVer.wServicePackMajor,
+       servicePackMinor: winVer.wServicePackMinor,
+     };
+
+     return versionInfo;
+   } finally {
+     kernel32.close();
+   }
+ }
+
+ function getCurrentThemePath() {
+   return WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+                                     "Software\\Microsoft\\Windows\\CurrentVersion\\Themes", "CurrentTheme");
+ }
new file mode 100644
--- /dev/null
+++ b/browser/tools/mozscreenshots/mozscreenshots/extension/lib/ChangeWindowsTheme.bat
@@ -0,0 +1,39 @@
+@echo OFF
+
+REM   This Source Code Form is subject to the terms of the Mozilla Public
+REM   License, v. 2.0. If a copy of the MPL was not distributed with this
+REM   file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+REM The first argument is the full path to the Windows theme.
+Set _ThemePath=%1
+
+REM   CALL :dequote _ThemePath
+
+@echo %_ThemePath%
+CALL %_ThemePath%
+
+setlocal ENABLEEXTENSIONS
+set KEY_NAME="HKCU\Software\Microsoft\Windows\CurrentVersion\Themes"
+set VALUE_NAME=CurrentTheme
+
+FOR /F "usebackq skip=2 tokens=3*" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul`) DO (
+    set ValueValue=%%A %%B
+)
+
+if defined ValueValue (
+    @echo Value Value = %ValueValue%
+) else (
+    @echo %KEY_NAME%\%VALUE_NAME% not found.
+)
+
+if /i "%ValueValue%" == "%_ThemePath%" (
+  @echo "Trying to change to existing theme. Quitting"
+  Goto :eof
+)
+
+
+REM ===== helpers =====
+
+:DeQuote
+for /f "delims=" %%A in ('echo %%%1%%') do set %1=%%~A
+Goto :eof
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/tools/mozscreenshots/mozscreenshots/extension/lib/CloseDisplayProperties.vbs
@@ -0,0 +1,17 @@
+' 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/.
+
+Set oShell = CreateObject("Wscript.Shell")
+
+' Busy-wait until the Display Properties dialog is loaded.
+While oShell.AppActivate("Display Properties") = FALSE
+    WScript.Sleep 800
+Wend
+
+' Loop while Display Properties is open and send the ENTER key until it closes (to apply the theme change).
+While oShell.AppActivate("Display Properties") = TRUE
+    oShell.AppActivate "Display Properties"
+    WScript.Sleep 250
+    oShell.SendKeys "{ENTER}"
+Wend