widget/windows/TaskbarTabPreview.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 "TaskbarTabPreview.h"
     9 #include "nsWindowGfx.h"
    10 #include "nsUXThemeData.h"
    11 #include "WinUtils.h"
    12 #include <nsITaskbarPreviewController.h>
    14 #define TASKBARPREVIEW_HWNDID L"TaskbarTabPreviewHwnd"
    16 namespace mozilla {
    17 namespace widget {
    19 NS_IMPL_ISUPPORTS(TaskbarTabPreview, nsITaskbarTabPreview)
    21 const wchar_t *const kWindowClass = L"MozillaTaskbarPreviewClass";
    23 TaskbarTabPreview::TaskbarTabPreview(ITaskbarList4 *aTaskbar, nsITaskbarPreviewController *aController, HWND aHWND, nsIDocShell *aShell)
    24   : TaskbarPreview(aTaskbar, aController, aHWND, aShell),
    25     mProxyWindow(nullptr),
    26     mIcon(nullptr),
    27     mRegistered(false)
    28 {
    29   WindowHook &hook = GetWindowHook();
    30   hook.AddMonitor(WM_WINDOWPOSCHANGED, MainWindowHook, this);
    31 }
    33 TaskbarTabPreview::~TaskbarTabPreview() {
    34   if (mIcon) {
    35     ::DestroyIcon(mIcon);
    36     mIcon = nullptr;
    37   }
    39   // We need to ensure that proxy window disappears or else Bad Things happen.
    40   if (mProxyWindow)
    41     Disable();
    43   NS_ASSERTION(!mProxyWindow, "Taskbar proxy window was not destroyed!");
    45   if (IsWindowAvailable()) {
    46     DetachFromNSWindow();
    47   } else {
    48     mWnd = nullptr;
    49   }
    50 }
    52 nsresult
    53 TaskbarTabPreview::ShowActive(bool active) {
    54   NS_ASSERTION(mVisible && CanMakeTaskbarCalls(), "ShowActive called on invisible window or before taskbar calls can be made for this window");
    55   return FAILED(mTaskbar->SetTabActive(active ? mProxyWindow : nullptr,
    56                                        mWnd, 0)) ? NS_ERROR_FAILURE : NS_OK;
    57 }
    59 HWND &
    60 TaskbarTabPreview::PreviewWindow() {
    61   return mProxyWindow;
    62 }
    64 nativeWindow
    65 TaskbarTabPreview::GetHWND() {
    66   return mProxyWindow;
    67 }
    69 void
    70 TaskbarTabPreview::EnsureRegistration() {
    71   NS_ASSERTION(mVisible && CanMakeTaskbarCalls(), "EnsureRegistration called when it is not safe to do so");
    73   (void) UpdateTaskbarProperties();
    74 }
    76 NS_IMETHODIMP
    77 TaskbarTabPreview::GetTitle(nsAString &aTitle) {
    78   aTitle = mTitle;
    79   return NS_OK;
    80 }
    82 NS_IMETHODIMP
    83 TaskbarTabPreview::SetTitle(const nsAString &aTitle) {
    84   mTitle = aTitle;
    85   return mVisible ? UpdateTitle() : NS_OK;
    86 }
    88 NS_IMETHODIMP
    89 TaskbarTabPreview::SetIcon(imgIContainer *icon) {
    90   HICON hIcon = nullptr;
    91   if (icon) {
    92     nsresult rv;
    93     rv = nsWindowGfx::CreateIcon(icon, false, 0, 0,
    94                                  nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon),
    95                                  &hIcon);
    96     NS_ENSURE_SUCCESS(rv, rv);
    97   }
    99   if (mIcon)
   100     ::DestroyIcon(mIcon);
   101   mIcon = hIcon;
   102   mIconImage = icon;
   103   return mVisible ? UpdateIcon() : NS_OK;
   104 }
   106 NS_IMETHODIMP
   107 TaskbarTabPreview::GetIcon(imgIContainer **icon) {
   108   NS_IF_ADDREF(*icon = mIconImage);
   109   return NS_OK;
   110 }
   112 NS_IMETHODIMP
   113 TaskbarTabPreview::Move(nsITaskbarTabPreview *aNext) {
   114   if (aNext == this)
   115     return NS_ERROR_INVALID_ARG;
   116   mNext = aNext;
   117   return CanMakeTaskbarCalls() ? UpdateNext() : NS_OK;
   118 }
   120 nsresult
   121 TaskbarTabPreview::UpdateTaskbarProperties() {
   122   if (mRegistered)
   123     return NS_OK;
   125   if (FAILED(mTaskbar->RegisterTab(mProxyWindow, mWnd)))
   126     return NS_ERROR_FAILURE;
   128   nsresult rv = UpdateNext();
   129   NS_ENSURE_SUCCESS(rv, rv);
   130   rv = TaskbarPreview::UpdateTaskbarProperties();
   131   mRegistered = true;
   132   return rv;
   133 }
   135 LRESULT
   136 TaskbarTabPreview::WndProc(UINT nMsg, WPARAM wParam, LPARAM lParam) {
   137   nsRefPtr<TaskbarTabPreview> kungFuDeathGrip(this);
   138   switch (nMsg) {
   139     case WM_CREATE:
   140       TaskbarPreview::EnableCustomDrawing(mProxyWindow, true);
   141       return 0;
   142     case WM_CLOSE:
   143       mController->OnClose();
   144       return 0;
   145     case WM_ACTIVATE:
   146       if (LOWORD(wParam) == WA_ACTIVE) {
   147         // Activate the tab the user selected then restore the main window,
   148         // keeping normal/max window state intact.
   149         bool activateWindow;
   150         nsresult rv = mController->OnActivate(&activateWindow);
   151         if (NS_SUCCEEDED(rv) && activateWindow) {
   152           nsWindow* win = WinUtils::GetNSWindowPtr(mWnd);
   153           if (win) {
   154             nsWindow * parent = win->GetTopLevelWindow(true);
   155             if (parent) {
   156               parent->Show(true);
   157             }
   158           }
   159         }
   160       }
   161       return 0;
   162     case WM_GETICON:
   163       return (LRESULT)mIcon;
   164     case WM_SYSCOMMAND:
   165       // Send activation events to the top level window and select the proper
   166       // tab through the controller.
   167       if (wParam == SC_RESTORE || wParam == SC_MAXIMIZE) {
   168         bool activateWindow;
   169         nsresult rv = mController->OnActivate(&activateWindow);
   170         if (NS_SUCCEEDED(rv) && activateWindow) {
   171           // Note, restoring an iconic, maximized window here will only
   172           // activate the maximized window. This is not a bug, it's default
   173           // windows behavior.
   174           ::SendMessageW(mWnd, WM_SYSCOMMAND, wParam, lParam);
   175         }
   176         return 0;
   177       }
   178       // Forward everything else to the top level window. Do not forward
   179       // close since that's intended for the tab. When the preview proxy
   180       // closes, we'll close the tab above.
   181       return wParam == SC_CLOSE
   182            ? ::DefWindowProcW(mProxyWindow, WM_SYSCOMMAND, wParam, lParam)
   183            : ::SendMessageW(mWnd, WM_SYSCOMMAND, wParam, lParam);
   184       return 0;
   185   }
   186   return TaskbarPreview::WndProc(nMsg, wParam, lParam);
   187 }
   189 /* static */
   190 LRESULT CALLBACK
   191 TaskbarTabPreview::GlobalWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam) {
   192   TaskbarTabPreview *preview(nullptr);
   193   if (nMsg == WM_CREATE) {
   194     CREATESTRUCT *cs = reinterpret_cast<CREATESTRUCT*>(lParam);
   195     preview = reinterpret_cast<TaskbarTabPreview*>(cs->lpCreateParams);
   196     if (!::SetPropW(hWnd, TASKBARPREVIEW_HWNDID, preview))
   197       NS_ERROR("Could not associate native window with tab preview");
   198     preview->mProxyWindow = hWnd;
   199   } else {
   200     preview = reinterpret_cast<TaskbarTabPreview*>(::GetPropW(hWnd, TASKBARPREVIEW_HWNDID));
   201     if (nMsg == WM_DESTROY)
   202       ::RemovePropW(hWnd, TASKBARPREVIEW_HWNDID);
   203   }
   205   if (preview)
   206     return preview->WndProc(nMsg, wParam, lParam);
   207   return ::DefWindowProcW(hWnd, nMsg, wParam, lParam);
   208 }
   210 nsresult
   211 TaskbarTabPreview::Enable() {
   212   WNDCLASSW wc;
   213   HINSTANCE module = GetModuleHandle(nullptr);
   215   if (!GetClassInfoW(module, kWindowClass, &wc)) {
   216     wc.style         = 0;
   217     wc.lpfnWndProc   = GlobalWndProc;
   218     wc.cbClsExtra    = 0;
   219     wc.cbWndExtra    = 0;
   220     wc.hInstance     = module;
   221     wc.hIcon         = nullptr;
   222     wc.hCursor       = nullptr;
   223     wc.hbrBackground = (HBRUSH) nullptr;
   224     wc.lpszMenuName  = (LPCWSTR) nullptr;
   225     wc.lpszClassName = kWindowClass;
   226     RegisterClassW(&wc);
   227   }
   228   ::CreateWindowW(kWindowClass, L"TaskbarPreviewWindow",
   229                   WS_CAPTION | WS_SYSMENU, 0, 0, 200, 60,
   230                   nullptr, nullptr, module, this);
   231   // GlobalWndProc will set mProxyWindow so that WM_CREATE can have a valid HWND
   232   if (!mProxyWindow)
   233     return NS_ERROR_INVALID_ARG;
   235   UpdateProxyWindowStyle();
   237   nsresult rv = TaskbarPreview::Enable();
   238   nsresult rvUpdate;
   239   rvUpdate = UpdateTitle();
   240   if (NS_FAILED(rvUpdate))
   241     rv = rvUpdate;
   243   rvUpdate = UpdateIcon();
   244   if (NS_FAILED(rvUpdate))
   245     rv = rvUpdate;
   247   return rv;
   248 }
   250 nsresult
   251 TaskbarTabPreview::Disable() {
   252   // TaskbarPreview::Disable assumes that mWnd is valid but this method can be
   253   // called when it is null iff the nsWindow has already been destroyed and we
   254   // are still visible for some reason during object destruction.
   255   if (mWnd)
   256     TaskbarPreview::Disable();
   258   if (FAILED(mTaskbar->UnregisterTab(mProxyWindow)))
   259     return NS_ERROR_FAILURE;
   260   mRegistered = false;
   262   // TaskbarPreview::WndProc will set mProxyWindow to null
   263   if (!DestroyWindow(mProxyWindow))
   264     return NS_ERROR_FAILURE;
   265   mProxyWindow = nullptr;
   266   return NS_OK;
   267 }
   269 void
   270 TaskbarTabPreview::DetachFromNSWindow() {
   271   (void) SetVisible(false);
   272   WindowHook &hook = GetWindowHook();
   273   hook.RemoveMonitor(WM_WINDOWPOSCHANGED, MainWindowHook, this);
   275   TaskbarPreview::DetachFromNSWindow();
   276 }
   278 /* static */
   279 bool
   280 TaskbarTabPreview::MainWindowHook(void *aContext,
   281                                   HWND hWnd, UINT nMsg,
   282                                   WPARAM wParam, LPARAM lParam,
   283                                   LRESULT *aResult) {
   284   if (nMsg == WM_WINDOWPOSCHANGED) {
   285     TaskbarTabPreview *preview = reinterpret_cast<TaskbarTabPreview*>(aContext);
   286     WINDOWPOS *pos = reinterpret_cast<WINDOWPOS*>(lParam);
   287     if (SWP_FRAMECHANGED == (pos->flags & SWP_FRAMECHANGED))
   288       preview->UpdateProxyWindowStyle();
   289   } else {
   290     NS_NOTREACHED("Style changed hook fired on non-style changed message");
   291   }
   292   return false;
   293 }
   295 void
   296 TaskbarTabPreview::UpdateProxyWindowStyle() {
   297   if (!mProxyWindow)
   298     return;
   300   DWORD minMaxMask = WS_MINIMIZE | WS_MAXIMIZE;
   301   DWORD windowStyle = GetWindowLongW(mWnd, GWL_STYLE);
   303   DWORD proxyStyle = GetWindowLongW(mProxyWindow, GWL_STYLE);
   304   proxyStyle &= ~minMaxMask;
   305   proxyStyle |= windowStyle & minMaxMask;
   306   SetWindowLongW(mProxyWindow, GWL_STYLE, proxyStyle);
   308   DWORD exStyle = (WS_MAXIMIZE == (windowStyle & WS_MAXIMIZE)) ? WS_EX_TOOLWINDOW : 0;
   309   SetWindowLongW(mProxyWindow, GWL_EXSTYLE, exStyle);
   310 }
   312 nsresult
   313 TaskbarTabPreview::UpdateTitle() {
   314   NS_ASSERTION(mVisible, "UpdateTitle called on invisible preview");
   316   if (!::SetWindowTextW(mProxyWindow, mTitle.get()))
   317     return NS_ERROR_FAILURE;
   318   return NS_OK;
   319 }
   321 nsresult
   322 TaskbarTabPreview::UpdateIcon() {
   323   NS_ASSERTION(mVisible, "UpdateIcon called on invisible preview");
   325   ::SendMessageW(mProxyWindow, WM_SETICON, ICON_SMALL, (LPARAM)mIcon);
   327   return NS_OK;
   328 }
   330 nsresult
   331 TaskbarTabPreview::UpdateNext() {
   332   NS_ASSERTION(CanMakeTaskbarCalls() && mVisible, "UpdateNext called on invisible tab preview");
   333   HWND hNext = nullptr;
   334   if (mNext) {
   335     bool visible;
   336     nsresult rv = mNext->GetVisible(&visible);
   338     NS_ENSURE_SUCCESS(rv, rv);
   340     // Can only move next to enabled previews
   341     if (!visible)
   342       return NS_ERROR_FAILURE;
   344     hNext = (HWND)mNext->GetHWND();
   346     // hNext must be registered with the taskbar if the call is to succeed
   347     mNext->EnsureRegistration();
   348   }
   349   if (FAILED(mTaskbar->SetTabOrder(mProxyWindow, hNext)))
   350     return NS_ERROR_FAILURE;
   351   return NS_OK;
   352 }
   355 } // namespace widget
   356 } // namespace mozilla

mercurial