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.

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

mercurial