widget/windows/nsUXThemeData.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* vim: se cin sw=2 ts=2 et : */
     2 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     3  *
     4  * This Source Code Form is subject to the terms of the Mozilla Public
     5  * License, v. 2.0. If a copy of the MPL was not distributed with this
     6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     8 #include "mozilla/ArrayUtils.h"
     9 #include "mozilla/WindowsVersion.h"
    11 #include "nsUXThemeData.h"
    12 #include "nsDebug.h"
    13 #include "nsToolkit.h"
    14 #include "nsUXThemeConstants.h"
    16 using namespace mozilla;
    17 using namespace mozilla::widget;
    19 const wchar_t
    20 nsUXThemeData::kThemeLibraryName[] = L"uxtheme.dll";
    22 HANDLE
    23 nsUXThemeData::sThemes[eUXNumClasses];
    25 HMODULE
    26 nsUXThemeData::sThemeDLL = nullptr;
    28 bool
    29 nsUXThemeData::sFlatMenus = false;
    31 bool nsUXThemeData::sTitlebarInfoPopulatedAero = false;
    32 bool nsUXThemeData::sTitlebarInfoPopulatedThemed = false;
    33 SIZE nsUXThemeData::sCommandButtons[4];
    35 void
    36 nsUXThemeData::Teardown() {
    37   Invalidate();
    38   if(sThemeDLL)
    39     FreeLibrary(sThemeDLL);
    40 }
    42 void
    43 nsUXThemeData::Initialize()
    44 {
    45   ::ZeroMemory(sThemes, sizeof(sThemes));
    46   NS_ASSERTION(!sThemeDLL, "nsUXThemeData being initialized twice!");
    48   CheckForCompositor(true);
    49   Invalidate();
    50 }
    52 void
    53 nsUXThemeData::Invalidate() {
    54   for(int i = 0; i < eUXNumClasses; i++) {
    55     if(sThemes[i]) {
    56       CloseThemeData(sThemes[i]);
    57       sThemes[i] = nullptr;
    58     }
    59   }
    60   BOOL useFlat = FALSE;
    61   sFlatMenus = ::SystemParametersInfo(SPI_GETFLATMENU, 0, &useFlat, 0) ?
    62                    useFlat : false;
    63 }
    65 HANDLE
    66 nsUXThemeData::GetTheme(nsUXThemeClass cls) {
    67   NS_ASSERTION(cls < eUXNumClasses, "Invalid theme class!");
    68   if(!sThemes[cls])
    69   {
    70     sThemes[cls] = OpenThemeData(nullptr, GetClassName(cls));
    71   }
    72   return sThemes[cls];
    73 }
    75 HMODULE
    76 nsUXThemeData::GetThemeDLL() {
    77   if (!sThemeDLL)
    78     sThemeDLL = ::LoadLibraryW(kThemeLibraryName);
    79   return sThemeDLL;
    80 }
    82 const wchar_t *nsUXThemeData::GetClassName(nsUXThemeClass cls) {
    83   switch(cls) {
    84     case eUXButton:
    85       return L"Button";
    86     case eUXEdit:
    87       return L"Edit";
    88     case eUXTooltip:
    89       return L"Tooltip";
    90     case eUXRebar:
    91       return L"Rebar";
    92     case eUXMediaRebar:
    93       return L"Media::Rebar";
    94     case eUXCommunicationsRebar:
    95       return L"Communications::Rebar";
    96     case eUXBrowserTabBarRebar:
    97       return L"BrowserTabBar::Rebar";
    98     case eUXToolbar:
    99       return L"Toolbar";
   100     case eUXMediaToolbar:
   101       return L"Media::Toolbar";
   102     case eUXCommunicationsToolbar:
   103       return L"Communications::Toolbar";
   104     case eUXProgress:
   105       return L"Progress";
   106     case eUXTab:
   107       return L"Tab";
   108     case eUXScrollbar:
   109       return L"Scrollbar";
   110     case eUXTrackbar:
   111       return L"Trackbar";
   112     case eUXSpin:
   113       return L"Spin";
   114     case eUXStatus:
   115       return L"Status";
   116     case eUXCombobox:
   117       return L"Combobox";
   118     case eUXHeader:
   119       return L"Header";
   120     case eUXListview:
   121       return L"Listview";
   122     case eUXMenu:
   123       return L"Menu";
   124     case eUXWindowFrame:
   125       return L"Window";
   126     default:
   127       NS_NOTREACHED("unknown uxtheme class");
   128       return L"";
   129   }
   130 }
   132 // static
   133 void
   134 nsUXThemeData::InitTitlebarInfo()
   135 {
   136   // Pre-populate with generic metrics. These likley will not match
   137   // the current theme, but they insure the buttons at least show up.
   138   sCommandButtons[0].cx = GetSystemMetrics(SM_CXSIZE);
   139   sCommandButtons[0].cy = GetSystemMetrics(SM_CYSIZE);
   140   sCommandButtons[1].cx = sCommandButtons[2].cx = sCommandButtons[0].cx;
   141   sCommandButtons[1].cy = sCommandButtons[2].cy = sCommandButtons[0].cy;
   142   sCommandButtons[3].cx = sCommandButtons[0].cx * 3;
   143   sCommandButtons[3].cy = sCommandButtons[0].cy;
   145   // Use system metrics for pre-vista, otherwise trigger a
   146   // refresh on the next layout.
   147   sTitlebarInfoPopulatedAero = sTitlebarInfoPopulatedThemed =
   148     !IsVistaOrLater();
   149 }
   151 // static
   152 void
   153 nsUXThemeData::UpdateTitlebarInfo(HWND aWnd)
   154 {
   155   if (!aWnd)
   156     return;
   158   if (!sTitlebarInfoPopulatedAero && nsUXThemeData::CheckForCompositor()) {
   159     RECT captionButtons;
   160     if (SUCCEEDED(WinUtils::dwmGetWindowAttributePtr(aWnd,
   161                                                      DWMWA_CAPTION_BUTTON_BOUNDS,
   162                                                      &captionButtons,
   163                                                      sizeof(captionButtons)))) {
   164       sCommandButtons[CMDBUTTONIDX_BUTTONBOX].cx = captionButtons.right - captionButtons.left - 3;
   165       sCommandButtons[CMDBUTTONIDX_BUTTONBOX].cy = (captionButtons.bottom - captionButtons.top) - 1;
   166       sTitlebarInfoPopulatedAero = true;
   167     }
   168   }
   170   if (sTitlebarInfoPopulatedThemed)
   171     return;
   173   // Query a temporary, visible window with command buttons to get
   174   // the right metrics. 
   175   nsAutoString className;
   176   className.AssignLiteral(kClassNameTemp);
   177   WNDCLASSW wc;
   178   wc.style         = 0;
   179   wc.lpfnWndProc   = ::DefWindowProcW;
   180   wc.cbClsExtra    = 0;
   181   wc.cbWndExtra    = 0;
   182   wc.hInstance     = nsToolkit::mDllInstance;
   183   wc.hIcon         = nullptr;
   184   wc.hCursor       = nullptr;
   185   wc.hbrBackground = nullptr;
   186   wc.lpszMenuName  = nullptr;
   187   wc.lpszClassName = className.get();
   188   ::RegisterClassW(&wc);
   190   // Create a transparent descendant of the window passed in. This
   191   // keeps the window from showing up on the desktop or the taskbar.
   192   // Note the parent (browser) window is usually still hidden, we
   193   // don't want to display it, so we can't query it directly.
   194   HWND hWnd = CreateWindowExW(WS_EX_LAYERED,
   195                               className.get(), L"",
   196                               WS_OVERLAPPEDWINDOW,
   197                               0, 0, 0, 0, aWnd, nullptr,
   198                               nsToolkit::mDllInstance, nullptr);
   199   NS_ASSERTION(hWnd, "UpdateTitlebarInfo window creation failed.");
   201   ShowWindow(hWnd, SW_SHOW);
   202   TITLEBARINFOEX info = {0};
   203   info.cbSize = sizeof(TITLEBARINFOEX);
   204   SendMessage(hWnd, WM_GETTITLEBARINFOEX, 0, (LPARAM)&info); 
   205   DestroyWindow(hWnd);
   207   // Only set if we have valid data for all three buttons we use.
   208   if ((info.rgrect[2].right - info.rgrect[2].left) == 0 ||
   209       (info.rgrect[3].right - info.rgrect[3].left) == 0 ||
   210       (info.rgrect[5].right - info.rgrect[5].left) == 0) {
   211     NS_WARNING("WM_GETTITLEBARINFOEX query failed to find usable metrics.");
   212     return;
   213   }
   214   // minimize
   215   sCommandButtons[0].cx = info.rgrect[2].right - info.rgrect[2].left;
   216   sCommandButtons[0].cy = info.rgrect[2].bottom - info.rgrect[2].top;
   217   // maximize/restore
   218   sCommandButtons[1].cx = info.rgrect[3].right - info.rgrect[3].left;
   219   sCommandButtons[1].cy = info.rgrect[3].bottom - info.rgrect[3].top;
   220   // close
   221   sCommandButtons[2].cx = info.rgrect[5].right - info.rgrect[5].left;
   222   sCommandButtons[2].cy = info.rgrect[5].bottom - info.rgrect[5].top;
   224   sTitlebarInfoPopulatedThemed = true;
   225 }
   227 // visual style (aero glass, aero basic)
   228 //    theme (aero, luna, zune)
   229 //      theme color (silver, olive, blue)
   230 //        system colors
   232 struct THEMELIST {
   233   LPCWSTR name;
   234   int type;
   235 };
   237 const THEMELIST knownThemes[] = {
   238   { L"aero.msstyles", WINTHEME_AERO },
   239   { L"aerolite.msstyles", WINTHEME_AERO_LITE },
   240   { L"luna.msstyles", WINTHEME_LUNA },
   241   { L"zune.msstyles", WINTHEME_ZUNE },
   242   { L"royale.msstyles", WINTHEME_ROYALE }
   243 };
   245 const THEMELIST knownColors[] = {
   246   { L"normalcolor", WINTHEMECOLOR_NORMAL },
   247   { L"homestead",   WINTHEMECOLOR_HOMESTEAD },
   248   { L"metallic",    WINTHEMECOLOR_METALLIC }
   249 };
   251 LookAndFeel::WindowsTheme
   252 nsUXThemeData::sThemeId = LookAndFeel::eWindowsTheme_Generic;
   254 bool
   255 nsUXThemeData::sIsDefaultWindowsTheme = false;
   256 bool
   257 nsUXThemeData::sIsHighContrastOn = false;
   259 // static
   260 LookAndFeel::WindowsTheme
   261 nsUXThemeData::GetNativeThemeId()
   262 {
   263   return sThemeId;
   264 }
   266 // static
   267 bool nsUXThemeData::IsDefaultWindowTheme()
   268 {
   269   return sIsDefaultWindowsTheme;
   270 }
   272 bool nsUXThemeData::IsHighContrastOn()
   273 {
   274   return sIsHighContrastOn;
   275 }
   277 // static
   278 bool nsUXThemeData::CheckForCompositor(bool aUpdateCache)
   279 {
   280   static BOOL sCachedValue = FALSE;
   281   if (aUpdateCache && WinUtils::dwmIsCompositionEnabledPtr) {
   282     WinUtils::dwmIsCompositionEnabledPtr(&sCachedValue);
   283   }
   284   return sCachedValue;
   285 }
   287 // static
   288 void
   289 nsUXThemeData::UpdateNativeThemeInfo()
   290 {
   291   // Trigger a refresh of themed button metrics if needed
   292   sTitlebarInfoPopulatedThemed = !IsVistaOrLater();
   294   sIsDefaultWindowsTheme = false;
   295   sThemeId = LookAndFeel::eWindowsTheme_Generic;
   297   HIGHCONTRAST highContrastInfo;
   298   highContrastInfo.cbSize = sizeof(HIGHCONTRAST);
   299   if (SystemParametersInfo(SPI_GETHIGHCONTRAST, 0, &highContrastInfo, 0)) {
   300     sIsHighContrastOn = ((highContrastInfo.dwFlags & HCF_HIGHCONTRASTON) != 0);
   301   } else {
   302     sIsHighContrastOn = false;
   303   }
   305   if (!IsAppThemed()) {
   306     sThemeId = LookAndFeel::eWindowsTheme_Classic;
   307     return;
   308   }
   310   WCHAR themeFileName[MAX_PATH + 1];
   311   WCHAR themeColor[MAX_PATH + 1];
   312   if (FAILED(GetCurrentThemeName(themeFileName,
   313                                  MAX_PATH,
   314                                  themeColor,
   315                                  MAX_PATH,
   316                                  nullptr, 0))) {
   317     sThemeId = LookAndFeel::eWindowsTheme_Classic;
   318     return;
   319   }
   321   LPCWSTR themeName = wcsrchr(themeFileName, L'\\');
   322   themeName = themeName ? themeName + 1 : themeFileName;
   324   WindowsTheme theme = WINTHEME_UNRECOGNIZED;
   325   for (size_t i = 0; i < ArrayLength(knownThemes); ++i) {
   326     if (!lstrcmpiW(themeName, knownThemes[i].name)) {
   327       theme = (WindowsTheme)knownThemes[i].type;
   328       break;
   329     }
   330   }
   332   if (theme == WINTHEME_UNRECOGNIZED)
   333     return;
   335   // We're using the default theme if we're using any of Aero, Aero Lite, or
   336   // luna. However, on Win8, GetCurrentThemeName (see above) returns
   337   // AeroLite.msstyles for the 4 builtin highcontrast themes as well. Those
   338   // themes "don't count" as default themes, so we specifically check for high
   339   // contrast mode in that situation.
   340   if (!(IsWin8OrLater() && sIsHighContrastOn) &&
   341       (theme == WINTHEME_AERO || theme == WINTHEME_AERO_LITE || theme == WINTHEME_LUNA)) {
   342     sIsDefaultWindowsTheme = true;
   343   }
   345   if (theme != WINTHEME_LUNA) {
   346     switch(theme) {
   347       case WINTHEME_AERO:
   348         sThemeId = LookAndFeel::eWindowsTheme_Aero;
   349         return;
   350       case WINTHEME_AERO_LITE:
   351         sThemeId = LookAndFeel::eWindowsTheme_AeroLite;
   352         return;
   353       case WINTHEME_ZUNE:
   354         sThemeId = LookAndFeel::eWindowsTheme_Zune;
   355         return;
   356       case WINTHEME_ROYALE:
   357         sThemeId = LookAndFeel::eWindowsTheme_Royale;
   358         return;
   359       default:
   360         NS_WARNING("unhandled theme type.");
   361         return;
   362     }
   363   }
   365   // calculate the luna color scheme
   366   WindowsThemeColor color = WINTHEMECOLOR_UNRECOGNIZED;
   367   for (size_t i = 0; i < ArrayLength(knownColors); ++i) {
   368     if (!lstrcmpiW(themeColor, knownColors[i].name)) {
   369       color = (WindowsThemeColor)knownColors[i].type;
   370       break;
   371     }
   372   }
   374   switch(color) {
   375     case WINTHEMECOLOR_NORMAL:
   376       sThemeId = LookAndFeel::eWindowsTheme_LunaBlue;
   377       return;
   378     case WINTHEMECOLOR_HOMESTEAD:
   379       sThemeId = LookAndFeel::eWindowsTheme_LunaOlive;
   380       return;
   381     case WINTHEMECOLOR_METALLIC:
   382       sThemeId = LookAndFeel::eWindowsTheme_LunaSilver;
   383       return;
   384     default:
   385       NS_WARNING("unhandled theme color.");
   386       return;
   387   }
   388 }

mercurial