widget/windows/WinTaskbar.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 "WinTaskbar.h"
     9 #include "TaskbarPreview.h"
    10 #include <nsITaskbarPreviewController.h>
    12 #include <nsError.h>
    13 #include <nsCOMPtr.h>
    14 #include <nsIWidget.h>
    15 #include <nsIBaseWindow.h>
    16 #include <nsIObserverService.h>
    17 #include <nsServiceManagerUtils.h>
    18 #include <nsAutoPtr.h>
    19 #include "nsIXULAppInfo.h"
    20 #include "nsIJumpListBuilder.h"
    21 #include "nsUXThemeData.h"
    22 #include "nsWindow.h"
    23 #include "WinUtils.h"
    24 #include "TaskbarTabPreview.h"
    25 #include "TaskbarWindowPreview.h"
    26 #include "JumpListBuilder.h"
    27 #include "nsWidgetsCID.h"
    28 #include "nsPIDOMWindow.h"
    29 #include "nsAppDirectoryServiceDefs.h"
    30 #include "mozilla/Preferences.h"
    31 #include "mozilla/WindowsVersion.h"
    32 #include <io.h>
    33 #include <propvarutil.h>
    34 #include <propkey.h>
    35 #include <shellapi.h>
    37 const wchar_t kShellLibraryName[] =  L"shell32.dll";
    39 static NS_DEFINE_CID(kJumpListBuilderCID, NS_WIN_JUMPLISTBUILDER_CID);
    41 namespace {
    43 HWND
    44 GetHWNDFromDocShell(nsIDocShell *aShell) {
    45   nsCOMPtr<nsIBaseWindow> baseWindow(do_QueryInterface(reinterpret_cast<nsISupports*>(aShell)));
    47   if (!baseWindow)
    48     return nullptr;
    50   nsCOMPtr<nsIWidget> widget;
    51   baseWindow->GetMainWidget(getter_AddRefs(widget));
    53   return widget ? (HWND)widget->GetNativeData(NS_NATIVE_WINDOW) : nullptr;
    54 }
    56 HWND
    57 GetHWNDFromDOMWindow(nsIDOMWindow *dw) {
    58   nsCOMPtr<nsIWidget> widget;
    60   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(dw);
    61   if (!window) 
    62     return nullptr;
    64   return GetHWNDFromDocShell(window->GetDocShell());
    65 }
    67 nsresult
    68 SetWindowAppUserModelProp(nsIDOMWindow *aParent,
    69                           const nsString & aIdentifier) {
    70   NS_ENSURE_ARG_POINTER(aParent);
    72   if (aIdentifier.IsEmpty())
    73     return NS_ERROR_INVALID_ARG;
    75   HWND toplevelHWND = ::GetAncestor(GetHWNDFromDOMWindow(aParent), GA_ROOT);
    77   if (!toplevelHWND)
    78     return NS_ERROR_INVALID_ARG;
    80   typedef HRESULT (WINAPI * SHGetPropertyStoreForWindowPtr)
    81                     (HWND hwnd, REFIID riid, void** ppv);
    82   SHGetPropertyStoreForWindowPtr funcGetProStore = nullptr;
    84   HMODULE hDLL = ::LoadLibraryW(kShellLibraryName);
    85   funcGetProStore = (SHGetPropertyStoreForWindowPtr)
    86     GetProcAddress(hDLL, "SHGetPropertyStoreForWindow");
    88   if (!funcGetProStore) {
    89     FreeLibrary(hDLL);
    90     return NS_ERROR_NO_INTERFACE;
    91   }
    93   IPropertyStore* pPropStore;
    94   if (FAILED(funcGetProStore(toplevelHWND,
    95                              IID_PPV_ARGS(&pPropStore)))) {
    96     FreeLibrary(hDLL);
    97     return NS_ERROR_INVALID_ARG;
    98   }
   100   PROPVARIANT pv;
   101   if (FAILED(InitPropVariantFromString(aIdentifier.get(), &pv))) {
   102     pPropStore->Release();
   103     FreeLibrary(hDLL);
   104     return NS_ERROR_UNEXPECTED;
   105   }
   107   nsresult rv = NS_OK;
   108   if (FAILED(pPropStore->SetValue(PKEY_AppUserModel_ID, pv)) ||
   109       FAILED(pPropStore->Commit())) {
   110     rv = NS_ERROR_FAILURE;
   111   }
   113   PropVariantClear(&pv);
   114   pPropStore->Release();
   115   FreeLibrary(hDLL);
   117   return rv;
   118 }
   120 ///////////////////////////////////////////////////////////////////////////////
   121 // default nsITaskbarPreviewController
   123 class DefaultController MOZ_FINAL : public nsITaskbarPreviewController
   124 {
   125   HWND mWnd;
   126 public:
   127   DefaultController(HWND hWnd) 
   128     : mWnd(hWnd)
   129   {
   130   }
   132   NS_DECL_ISUPPORTS
   133   NS_DECL_NSITASKBARPREVIEWCONTROLLER
   134 };
   136 NS_IMETHODIMP
   137 DefaultController::GetWidth(uint32_t *aWidth)
   138 {
   139   RECT r;
   140   ::GetClientRect(mWnd, &r);
   141   *aWidth = r.right;
   142   return NS_OK;
   143 }
   145 NS_IMETHODIMP
   146 DefaultController::GetHeight(uint32_t *aHeight)
   147 {
   148   RECT r;
   149   ::GetClientRect(mWnd, &r);
   150   *aHeight = r.bottom;
   151   return NS_OK;
   152 }
   154 NS_IMETHODIMP
   155 DefaultController::GetThumbnailAspectRatio(float *aThumbnailAspectRatio) {
   156   uint32_t width, height;
   157   GetWidth(&width);
   158   GetHeight(&height);
   159   if (!height)
   160     height = 1;
   162   *aThumbnailAspectRatio = width/float(height);
   163   return NS_OK;
   164 }
   166 NS_IMETHODIMP
   167 DefaultController::DrawPreview(nsISupports *ctx, bool *rDrawFrame) {
   168   *rDrawFrame = true;
   169   return NS_OK;
   170 }
   172 NS_IMETHODIMP
   173 DefaultController::DrawThumbnail(nsISupports *ctx, uint32_t width, uint32_t height, bool *rDrawFrame) {
   174   *rDrawFrame = false;
   175   return NS_OK;
   176 }
   178 NS_IMETHODIMP
   179 DefaultController::OnClose(void) {
   180   NS_NOTREACHED("OnClose should not be called for TaskbarWindowPreviews");
   181   return NS_OK;
   182 }
   184 NS_IMETHODIMP
   185 DefaultController::OnActivate(bool *rAcceptActivation) {
   186   *rAcceptActivation = true;
   187   NS_NOTREACHED("OnActivate should not be called for TaskbarWindowPreviews");
   188   return NS_OK;
   189 }
   191 NS_IMETHODIMP
   192 DefaultController::OnClick(nsITaskbarPreviewButton *button) {
   193   return NS_OK;
   194 }
   196 NS_IMPL_ISUPPORTS(DefaultController, nsITaskbarPreviewController)
   197 }
   199 namespace mozilla {
   200 namespace widget {
   202 ///////////////////////////////////////////////////////////////////////////////
   203 // nsIWinTaskbar
   205 NS_IMPL_ISUPPORTS(WinTaskbar, nsIWinTaskbar)
   207 bool
   208 WinTaskbar::Initialize() {
   209   if (mTaskbar)
   210     return true;
   212   ::CoInitialize(nullptr);
   213   HRESULT hr = ::CoCreateInstance(CLSID_TaskbarList,
   214                                   nullptr,
   215                                   CLSCTX_INPROC_SERVER,
   216                                   IID_ITaskbarList4,
   217                                   (void**)&mTaskbar);
   218   if (FAILED(hr))
   219     return false;
   221   hr = mTaskbar->HrInit();
   222   if (FAILED(hr)) {
   223     // This may fail with shell extensions like blackbox installed.
   224     NS_WARNING("Unable to initialize taskbar");
   225     NS_RELEASE(mTaskbar);
   226     return false;
   227   }
   228   return true;
   229 }
   231 WinTaskbar::WinTaskbar() 
   232   : mTaskbar(nullptr) {
   233 }
   235 WinTaskbar::~WinTaskbar() {
   236   if (mTaskbar) { // match successful Initialize() call
   237     NS_RELEASE(mTaskbar);
   238     ::CoUninitialize();
   239   }
   240 }
   242 // static
   243 bool
   244 WinTaskbar::GetAppUserModelID(nsAString & aDefaultGroupId) {
   245   // For win8 metro builds, we can't set this. The value is static
   246   // for the app.
   247   if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) {
   248     return false;
   249   }
   250   // If marked as such in prefs, use a hash of the profile path for the id
   251   // instead of the install path hash setup by the installer.
   252   bool useProfile =
   253     Preferences::GetBool("taskbar.grouping.useprofile", false);
   254   if (useProfile) {
   255     nsCOMPtr<nsIFile> profileDir;
   256     NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
   257                            getter_AddRefs(profileDir));
   258     bool exists = false;
   259     if (profileDir && NS_SUCCEEDED(profileDir->Exists(&exists)) && exists) {
   260       nsAutoCString path;
   261       if (NS_SUCCEEDED(profileDir->GetNativePath(path))) {
   262         nsAutoString id;
   263         id.AppendInt(HashString(path));
   264         if (!id.IsEmpty()) {
   265           aDefaultGroupId.Assign(id);
   266           return true;
   267         }
   268       }
   269     }
   270   }
   272   // The default value is set by the installer and is stored in the registry
   273   // under (HKLM||HKCU)/Software/Mozilla/Firefox/TaskBarIDs. If for any reason
   274   // hash generation operation fails, the installer will not store a value in
   275   // the registry or set ids on shortcuts. A lack of an id can also occur for
   276   // zipped builds. We skip setting the global id in this case as well.
   277   nsCOMPtr<nsIXULAppInfo> appInfo =
   278     do_GetService("@mozilla.org/xre/app-info;1");
   279   if (!appInfo)
   280     return false;
   282   nsCString appName;
   283   if (NS_FAILED(appInfo->GetName(appName))) {
   284     // We just won't register then, let Windows handle it.
   285     return false;
   286   }
   288   nsAutoString regKey;
   289   regKey.AssignLiteral("Software\\Mozilla\\");
   290   AppendASCIItoUTF16(appName, regKey);
   291   regKey.AppendLiteral("\\TaskBarIDs");
   293   WCHAR path[MAX_PATH];
   294   if (GetModuleFileNameW(nullptr, path, MAX_PATH)) {
   295     wchar_t* slash = wcsrchr(path, '\\');
   296     if (!slash)
   297       return false;
   298     *slash = '\0'; // no trailing slash
   300     // The hash is short, but users may customize this, so use a respectable
   301     // string buffer.
   302     wchar_t buf[256];
   303     if (WinUtils::GetRegistryKey(HKEY_LOCAL_MACHINE,
   304                                  regKey.get(),
   305                                  path,
   306                                  buf,
   307                                  sizeof buf)) {
   308       aDefaultGroupId.Assign(buf);
   309     } else if (WinUtils::GetRegistryKey(HKEY_CURRENT_USER,
   310                                         regKey.get(),
   311                                         path,
   312                                         buf,
   313                                         sizeof buf)) {
   314       aDefaultGroupId.Assign(buf);
   315     }
   316   }
   318   return !aDefaultGroupId.IsEmpty();
   320   return true;
   321 }
   323 /* readonly attribute AString defaultGroupId; */
   324 NS_IMETHODIMP
   325 WinTaskbar::GetDefaultGroupId(nsAString & aDefaultGroupId) {
   326   if (!GetAppUserModelID(aDefaultGroupId))
   327     return NS_ERROR_UNEXPECTED;
   329   return NS_OK;
   330 }
   332 // (static) Called from AppShell
   333 bool
   334 WinTaskbar::RegisterAppUserModelID() {
   335   if (!IsWin7OrLater())
   336     return false;
   338   if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) {
   339     return false;
   340   }
   342   SetCurrentProcessExplicitAppUserModelIDPtr funcAppUserModelID = nullptr;
   343   bool retVal = false;
   345   nsAutoString uid;
   346   if (!GetAppUserModelID(uid))
   347     return false;
   349   HMODULE hDLL = ::LoadLibraryW(kShellLibraryName);
   351   funcAppUserModelID = (SetCurrentProcessExplicitAppUserModelIDPtr)
   352                         GetProcAddress(hDLL, "SetCurrentProcessExplicitAppUserModelID");
   354   if (!funcAppUserModelID) {
   355     ::FreeLibrary(hDLL);
   356     return false;
   357   }
   359   if (SUCCEEDED(funcAppUserModelID(uid.get())))
   360     retVal = true;
   362   if (hDLL)
   363     ::FreeLibrary(hDLL);
   365   return retVal;
   366 }
   368 NS_IMETHODIMP
   369 WinTaskbar::GetAvailable(bool *aAvailable) {
   370   // ITaskbarList4::HrInit() may fail with shell extensions like blackbox
   371   // installed. Initialize early to return available=false in those cases.
   372   *aAvailable = IsWin7OrLater() && Initialize();
   374   return NS_OK;
   375 }
   377 NS_IMETHODIMP
   378 WinTaskbar::CreateTaskbarTabPreview(nsIDocShell *shell, nsITaskbarPreviewController *controller, nsITaskbarTabPreview **_retval) {
   379   if (!Initialize())
   380     return NS_ERROR_NOT_AVAILABLE;
   382   NS_ENSURE_ARG_POINTER(shell);
   383   NS_ENSURE_ARG_POINTER(controller);
   385   HWND toplevelHWND = ::GetAncestor(GetHWNDFromDocShell(shell), GA_ROOT);
   387   if (!toplevelHWND)
   388     return NS_ERROR_INVALID_ARG;
   390   nsRefPtr<TaskbarTabPreview> preview(new TaskbarTabPreview(mTaskbar, controller, toplevelHWND, shell));
   391   if (!preview)
   392     return NS_ERROR_OUT_OF_MEMORY;
   394   preview.forget(_retval);
   396   return NS_OK;
   397 }
   399 NS_IMETHODIMP
   400 WinTaskbar::GetTaskbarWindowPreview(nsIDocShell *shell, nsITaskbarWindowPreview **_retval) {
   401   if (!Initialize())
   402     return NS_ERROR_NOT_AVAILABLE;
   404   NS_ENSURE_ARG_POINTER(shell);
   406   HWND toplevelHWND = ::GetAncestor(GetHWNDFromDocShell(shell), GA_ROOT);
   408   if (!toplevelHWND)
   409     return NS_ERROR_INVALID_ARG;
   411   nsWindow *window = WinUtils::GetNSWindowPtr(toplevelHWND);
   413   if (!window)
   414     return NS_ERROR_FAILURE;
   416   nsCOMPtr<nsITaskbarWindowPreview> preview = window->GetTaskbarPreview();
   417   if (!preview) {
   418     nsRefPtr<DefaultController> defaultController = new DefaultController(toplevelHWND);
   419     preview = new TaskbarWindowPreview(mTaskbar, defaultController, toplevelHWND, shell);
   420     if (!preview)
   421       return NS_ERROR_OUT_OF_MEMORY;
   422     window->SetTaskbarPreview(preview);
   423   }
   425   preview.forget(_retval);
   427   return NS_OK;
   428 }
   430 NS_IMETHODIMP
   431 WinTaskbar::GetTaskbarProgress(nsIDocShell *shell, nsITaskbarProgress **_retval) {
   432   nsCOMPtr<nsITaskbarWindowPreview> preview;
   433   nsresult rv = GetTaskbarWindowPreview(shell, getter_AddRefs(preview));
   434   NS_ENSURE_SUCCESS(rv, rv);
   436   return CallQueryInterface(preview, _retval);
   437 }
   439 NS_IMETHODIMP
   440 WinTaskbar::GetOverlayIconController(nsIDocShell *shell,
   441                                      nsITaskbarOverlayIconController **_retval) {
   442   nsCOMPtr<nsITaskbarWindowPreview> preview;
   443   nsresult rv = GetTaskbarWindowPreview(shell, getter_AddRefs(preview));
   444   NS_ENSURE_SUCCESS(rv, rv);
   446   return CallQueryInterface(preview, _retval);
   447 }
   449 /* nsIJumpListBuilder createJumpListBuilder(); */
   450 NS_IMETHODIMP
   451 WinTaskbar::CreateJumpListBuilder(nsIJumpListBuilder * *aJumpListBuilder) {
   452   nsresult rv;
   454   if (JumpListBuilder::sBuildingList)
   455     return NS_ERROR_ALREADY_INITIALIZED;
   457   nsCOMPtr<nsIJumpListBuilder> builder = 
   458     do_CreateInstance(kJumpListBuilderCID, &rv);
   459   if (NS_FAILED(rv))
   460     return NS_ERROR_UNEXPECTED;
   462   NS_IF_ADDREF(*aJumpListBuilder = builder);
   464   return NS_OK;
   465 }
   467 /* void setGroupIdForWindow (in nsIDOMWindow aParent, in AString aIdentifier); */
   468 NS_IMETHODIMP
   469 WinTaskbar::SetGroupIdForWindow(nsIDOMWindow *aParent,
   470                                 const nsAString & aIdentifier) {
   471   return SetWindowAppUserModelProp(aParent, nsString(aIdentifier));
   472 }
   474 /* void prepareFullScreen(in nsIDOMWindow aWindow, in boolean aFullScreen); */
   475 NS_IMETHODIMP
   476 WinTaskbar::PrepareFullScreen(nsIDOMWindow *aWindow, bool aFullScreen) {
   477   NS_ENSURE_ARG_POINTER(aWindow);
   479   HWND toplevelHWND = ::GetAncestor(GetHWNDFromDOMWindow(aWindow), GA_ROOT);
   480   if (!toplevelHWND)
   481     return NS_ERROR_INVALID_ARG;
   483   return PrepareFullScreenHWND(toplevelHWND, aFullScreen);
   484 }
   486 /* void prepareFullScreen(in voidPtr aWindow, in boolean aFullScreen); */
   487 NS_IMETHODIMP
   488 WinTaskbar::PrepareFullScreenHWND(void *aHWND, bool aFullScreen) {
   489   if (!Initialize())
   490     return NS_ERROR_NOT_AVAILABLE;
   492   NS_ENSURE_ARG_POINTER(aHWND);
   494   if (!::IsWindow((HWND)aHWND)) 
   495     return NS_ERROR_INVALID_ARG;
   497   HRESULT hr = mTaskbar->MarkFullscreenWindow((HWND)aHWND, aFullScreen);
   498   if (FAILED(hr)) {
   499     return NS_ERROR_UNEXPECTED;
   500   }
   502   return NS_OK;
   503 }
   505 } // namespace widget
   506 } // namespace mozilla

mercurial