зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1529276 - Create a new JumpListBuilder implementation that is simpler and doesn't block the main thread. r=mhowell,win-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D181675
This commit is contained in:
Родитель
7226d25048
Коммит
481dde8e1f
|
@ -0,0 +1,49 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A WindowsJumpListShortcutDescription is a structure that describes an entry
|
||||
* to be created in the Windows Jump List. Both tasks, as well as custom
|
||||
* items can be described using this structure.
|
||||
*
|
||||
* nsIJumpListBuilder.populateJumpList accepts arrays of these structures.
|
||||
*/
|
||||
[GenerateInit, GenerateConversionToJS]
|
||||
dictionary WindowsJumpListShortcutDescription {
|
||||
/**
|
||||
* The title of the Jump List item to be displayed to the user.
|
||||
*/
|
||||
required DOMString title;
|
||||
|
||||
/**
|
||||
* The path to the executable that Windows should run when the item is
|
||||
* selected in the Jump List.
|
||||
*/
|
||||
required DOMString path;
|
||||
|
||||
/**
|
||||
* Arguments to be supplied to the executable when the item is selected in
|
||||
* the Jump List.
|
||||
*/
|
||||
DOMString arguments;
|
||||
|
||||
/**
|
||||
* A description of the item that is displayed as a tooltip.
|
||||
*/
|
||||
required DOMString description;
|
||||
|
||||
/**
|
||||
* The path to an icon to assign to the Jump List item. If this is not
|
||||
* supplied then the fallbackIconIndex is used instead.
|
||||
*/
|
||||
DOMString iconPath;
|
||||
|
||||
/**
|
||||
* The icon index associated with the executable at the path to use in the
|
||||
* event that no iconPath is supplied.
|
||||
*/
|
||||
required long fallbackIconIndex;
|
||||
};
|
|
@ -111,3 +111,8 @@ if CONFIG["MOZ_PLACES"]:
|
|||
"PlacesEvent.webidl",
|
||||
"PlacesObservers.webidl",
|
||||
]
|
||||
|
||||
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":
|
||||
WEBIDL_FILES += [
|
||||
"WindowsJumpListShortcutDescription.webidl",
|
||||
]
|
||||
|
|
|
@ -79,6 +79,7 @@ elif toolkit == "gtk":
|
|||
]
|
||||
elif toolkit == "windows":
|
||||
XPIDL_SOURCES += [
|
||||
"nsIJumpListBuilder.idl",
|
||||
"nsILegacyJumpListBuilder.idl",
|
||||
"nsILegacyJumpListItem.idl",
|
||||
"nsIPrintSettingsWin.idl",
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
interface nsIURI;
|
||||
|
||||
[scriptable, uuid(5769F08D-0303-4E38-8FE6-86B5473022F6)]
|
||||
interface nsIJumpListBuilder : nsISupports
|
||||
{
|
||||
/**
|
||||
* Returns the local filesystem path for a favicon for a page hosted at
|
||||
* faviconURL if we happen to have written one to disk before. If we have not,
|
||||
* then a background thread retrieves the favicon and will write it to disk
|
||||
* and NS_ERROR_NOT_AVAILABLE will be thrown.
|
||||
*
|
||||
* @param {nsIURI} faviconURL
|
||||
* The URL for the web page for which we would like a filesystem path for
|
||||
* the favicon.
|
||||
* @returns {AString}
|
||||
* The local filesystem path for the favicon if it has been cached before.
|
||||
* If it has not been cached before, this method will throw
|
||||
* NS_ERROR_NOT_AVAILABLE.
|
||||
* @throws NS_ERROR_NOT_AVAILABLE
|
||||
* In the event that the favicon has never been cached to disk before.
|
||||
*/
|
||||
AString obtainAndCacheFavicon(in nsIURI faviconURL);
|
||||
|
||||
/**
|
||||
* Returns a Promise that resolves with whether or not the Jump List backend
|
||||
* on the background thread is up and running.
|
||||
*
|
||||
* @returns {Promise<boolean>}
|
||||
* Resolves to true if the backend is ready to accept
|
||||
* WindowsJumpListShortcutDescriptions. False, otherwise.
|
||||
* @throws NS_ERROR_FAILURE
|
||||
* If an attempt to communicate with the background thread fails.
|
||||
*/
|
||||
[implicit_jscontext]
|
||||
Promise isAvailable();
|
||||
|
||||
/**
|
||||
* Asks the Windows Jump List API for any items that might have been removed
|
||||
* by the user from the Jump List UI.
|
||||
*
|
||||
* Important: This should be called prior to any attempt to call
|
||||
* `populateJumpList` to ensure that any passed in
|
||||
* WindowsJumpListShortcutDescriptions do not describe items that the user has
|
||||
* just removed. Failing to do so will cause the Promise returned from
|
||||
* `populateJumpList` to reject. This is a constraint of the underlying win32
|
||||
* API. Please see
|
||||
* https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-icustomdestinationlist-beginlist
|
||||
* for more details.
|
||||
*
|
||||
* @returns {Promise<string[], nsresult>}
|
||||
* On success, will return an array of strings for URLs of history that
|
||||
* have been removed by the user via the Windows Jump List. These items will
|
||||
* also have had their cached favicons removed from the disk off of the
|
||||
* main thread. On failure, this will reject with the nsresult failure code.
|
||||
* @throws NS_ERROR_FAILURE
|
||||
* If an attempt to communicate with the background thread fails.
|
||||
*/
|
||||
[implicit_jscontext]
|
||||
Promise checkForRemovals();
|
||||
|
||||
/**
|
||||
* Writes a new set of items to the Windows Jump List. This occurs
|
||||
* asynchronously, off of the main thread.
|
||||
*
|
||||
* Important: Callers should first call `checkForRemovals` to remove any
|
||||
* browsing history items that the user chose to remove in the Jump List
|
||||
* Only then should any WindowsJumpListShortcutDescriptions be created
|
||||
* and passed to this method. Any attempt to add
|
||||
* WindowsJumpListShortcutDescriptions matching items that have been removed
|
||||
* by the user will result in the returned Promise rejecting. This is a
|
||||
* constraint of the underlying win32 API. Please see
|
||||
* https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-icustomdestinationlist-beginlist
|
||||
* for more details.
|
||||
*
|
||||
* @param {WindowsJumpListShortcutDescription[]} aTaskDescriptions
|
||||
* 0 or more WindowsJumpListShortcutDescriptions to place items within the
|
||||
* "tasks" section of the Jump List.
|
||||
* @param {AString} aCustomTitle
|
||||
* An optional title for a custom sub-list within the Jump List that will be
|
||||
* populated via aCustomDescriptions. This must be supplied if
|
||||
* aCustomDescriptions is not empty.
|
||||
* @param {WindowsJumpListShortcutDescription[]} aCustomDescriptions
|
||||
* 0 or more WindowsJumpListShortcutDescriptions to place items within the
|
||||
* custom section of the Jump List. aCustomTitle must be supplied if this
|
||||
* array is non-empty.
|
||||
* @returns {Promise<undefined, nsresult>}
|
||||
* Returns a Promise that resolves if the Jump List was properly written
|
||||
* to, and rejects otherwise with the nsresult of the failure.
|
||||
* @throws NS_ERROR_INVALID_ARG
|
||||
* If any of the passed arguments do not meet the requirements set out
|
||||
* above.
|
||||
* @throws NS_ERROR_FAILURE
|
||||
* If an attempt to communicate with the background thread fails.
|
||||
*/
|
||||
[implicit_jscontext]
|
||||
Promise populateJumpList(
|
||||
in Array<jsval> aTaskDescriptions,
|
||||
in AString aCustomTitle,
|
||||
in Array<jsval> aCustomDescriptions
|
||||
);
|
||||
|
||||
/**
|
||||
* Removes all items from the Jump List.
|
||||
*
|
||||
* @returns {Promise<undefined, nsresult>}
|
||||
* Resolves with undefined on successfully clearing the Jump List. If it
|
||||
* fails to do so, it will reject with the failure code.
|
||||
* @throws NS_ERROR_FAILURE
|
||||
* If an attempt to communicate with the background thread fails.
|
||||
*/
|
||||
[implicit_jscontext]
|
||||
Promise clearJumpList();
|
||||
};
|
|
@ -9,6 +9,7 @@
|
|||
#include "nsIBaseWindow.idl"
|
||||
|
||||
interface nsIDocShell;
|
||||
interface nsIJumpListBuilder;
|
||||
interface nsITaskbarTabPreview;
|
||||
interface nsITaskbarWindowPreview;
|
||||
interface nsITaskbarPreviewController;
|
||||
|
@ -132,6 +133,14 @@ interface nsIWinTaskbar : nsISupports
|
|||
*/
|
||||
nsILegacyJumpListBuilder createLegacyJumpListBuilder(in boolean aPrivateBrowsing);
|
||||
|
||||
/**
|
||||
* Retrieves a Windows Jump List builder. This jump list builder can be used
|
||||
* to asynchronously add, remove, and update items in the Windows Jump List.
|
||||
*
|
||||
* @throws NS_ERROR_UNEXPECTED if the builder failed to be created.
|
||||
*/
|
||||
nsIJumpListBuilder createJumpListBuilder(in boolean aPrivateBrowsing);
|
||||
|
||||
/**
|
||||
* Application window taskbar group settings
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,793 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include <propkey.h>
|
||||
#include <propvarutil.h>
|
||||
#include <shellapi.h>
|
||||
#include "JumpListBuilder.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/WindowsJumpListShortcutDescriptionBinding.h"
|
||||
#include "mozilla/mscom/EnsureMTA.h"
|
||||
#include "nsIObserverService.h"
|
||||
|
||||
using mozilla::dom::Promise;
|
||||
using mozilla::dom::WindowsJumpListShortcutDescription;
|
||||
|
||||
namespace mozilla {
|
||||
namespace widget {
|
||||
|
||||
NS_IMPL_ISUPPORTS(JumpListBuilder, nsIJumpListBuilder, nsIObserver)
|
||||
|
||||
#define TOPIC_PROFILE_BEFORE_CHANGE "profile-before-change"
|
||||
#define TOPIC_CLEAR_PRIVATE_DATA "clear-private-data"
|
||||
|
||||
// The amount of time, in milliseconds, that our IO thread will stay alive after
|
||||
// the last event it processes.
|
||||
#define DEFAULT_THREAD_TIMEOUT_MS 30000
|
||||
|
||||
const char kPrefTaskbarEnabled[] = "browser.taskbar.lists.enabled";
|
||||
|
||||
/**
|
||||
* A wrapper around a ICustomDestinationList that implements the JumpListBackend
|
||||
* interface. This is an implementation of JumpListBackend that actually causes
|
||||
* items to appear in a Windows jump list.
|
||||
*/
|
||||
class NativeJumpListBackend : public JumpListBackend {
|
||||
// We use NS_INLINE_DECL_REFCOUNTING_ONEVENTTARGET because this
|
||||
// class might be destroyed on a different thread than the one it
|
||||
// was created on, since it's maintained by a LazyIdleThread.
|
||||
//
|
||||
// This is a workaround for bug 1648031.
|
||||
NS_INLINE_DECL_REFCOUNTING_ONEVENTTARGET(JumpListBackend, override)
|
||||
|
||||
NativeJumpListBackend() {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
mscom::EnsureMTA([&]() {
|
||||
RefPtr<ICustomDestinationList> destList;
|
||||
HRESULT hr = ::CoCreateInstance(
|
||||
CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER,
|
||||
IID_ICustomDestinationList, getter_AddRefs(destList));
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mWindowsDestList = destList;
|
||||
});
|
||||
}
|
||||
|
||||
virtual bool IsAvailable() override {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
return mWindowsDestList != nullptr;
|
||||
}
|
||||
|
||||
virtual HRESULT SetAppID(LPCWSTR pszAppID) override {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(mWindowsDestList);
|
||||
|
||||
return mWindowsDestList->SetAppID(pszAppID);
|
||||
}
|
||||
|
||||
virtual HRESULT BeginList(UINT* pcMinSlots, REFIID riid,
|
||||
void** ppv) override {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(mWindowsDestList);
|
||||
|
||||
return mWindowsDestList->BeginList(pcMinSlots, riid, ppv);
|
||||
}
|
||||
|
||||
virtual HRESULT AddUserTasks(IObjectArray* poa) override {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(mWindowsDestList);
|
||||
|
||||
return mWindowsDestList->AddUserTasks(poa);
|
||||
}
|
||||
|
||||
virtual HRESULT AppendCategory(LPCWSTR pszCategory,
|
||||
IObjectArray* poa) override {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(mWindowsDestList);
|
||||
|
||||
return mWindowsDestList->AppendCategory(pszCategory, poa);
|
||||
}
|
||||
|
||||
virtual HRESULT CommitList() override {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(mWindowsDestList);
|
||||
|
||||
return mWindowsDestList->CommitList();
|
||||
}
|
||||
|
||||
virtual HRESULT AbortList() override {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(mWindowsDestList);
|
||||
|
||||
return mWindowsDestList->AbortList();
|
||||
}
|
||||
|
||||
virtual HRESULT DeleteList(LPCWSTR pszAppID) override {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(mWindowsDestList);
|
||||
|
||||
return mWindowsDestList->DeleteList(pszAppID);
|
||||
}
|
||||
|
||||
virtual HRESULT AppendKnownCategory(KNOWNDESTCATEGORY category) override {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(mWindowsDestList);
|
||||
|
||||
return mWindowsDestList->AppendKnownCategory(category);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~NativeJumpListBackend() override{};
|
||||
|
||||
private:
|
||||
RefPtr<ICustomDestinationList> mWindowsDestList;
|
||||
};
|
||||
|
||||
JumpListBuilder::JumpListBuilder(const nsAString& aAppUserModelId,
|
||||
RefPtr<JumpListBackend> aTestingBackend) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mAppUserModelId.Assign(aAppUserModelId);
|
||||
|
||||
Preferences::AddStrongObserver(this, kPrefTaskbarEnabled);
|
||||
|
||||
// Make a lazy thread for any IO.
|
||||
mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS, "Jump List",
|
||||
LazyIdleThread::ManualShutdown);
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
do_GetService("@mozilla.org/observer-service;1");
|
||||
if (observerService) {
|
||||
observerService->AddObserver(this, TOPIC_PROFILE_BEFORE_CHANGE, false);
|
||||
observerService->AddObserver(this, TOPIC_CLEAR_PRIVATE_DATA, false);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable;
|
||||
|
||||
if (aTestingBackend) {
|
||||
// Dispatch a task that hands a reference to the testing backend
|
||||
// to the background thread. The testing backend was probably
|
||||
// constructed on the main thread, and is responsible for doing
|
||||
// any locking as well as cleanup.
|
||||
runnable = NewRunnableMethod<RefPtr<JumpListBackend>>(
|
||||
"SetupTestingBackend", this, &JumpListBuilder::DoSetupTestingBackend,
|
||||
aTestingBackend);
|
||||
|
||||
} else {
|
||||
// Dispatch a task that constructs the native jump list backend.
|
||||
runnable = NewRunnableMethod("SetupBackend", this,
|
||||
&JumpListBuilder::DoSetupBackend);
|
||||
}
|
||||
|
||||
mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||
|
||||
// MSIX packages explicitly do not support setting the appid from within
|
||||
// the app, as it is set in the package manifest instead.
|
||||
if (!mozilla::widget::WinUtils::HasPackageIdentity()) {
|
||||
mIOThread->Dispatch(
|
||||
NewRunnableMethod<nsString>(
|
||||
"SetAppID", this, &JumpListBuilder::DoSetAppID, aAppUserModelId),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
JumpListBuilder::~JumpListBuilder() {
|
||||
Preferences::RemoveObserver(this, kPrefTaskbarEnabled);
|
||||
}
|
||||
|
||||
void JumpListBuilder::DoSetupTestingBackend(
|
||||
RefPtr<JumpListBackend> aTestingBackend) {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
mJumpListBackend = aTestingBackend;
|
||||
}
|
||||
|
||||
void JumpListBuilder::DoSetupBackend() {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(!mJumpListBackend);
|
||||
mJumpListBackend = new NativeJumpListBackend();
|
||||
}
|
||||
|
||||
void JumpListBuilder::DoShutdownBackend() {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
mJumpListBackend = nullptr;
|
||||
}
|
||||
|
||||
void JumpListBuilder::DoSetAppID(nsString aAppUserModelID) {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(mJumpListBackend);
|
||||
mJumpListBackend->SetAppID(aAppUserModelID.get());
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
JumpListBuilder::ObtainAndCacheFavicon(nsIURI* aFaviconURI,
|
||||
nsAString& aCachedIconPath) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsString iconFilePath;
|
||||
nsresult rv = mozilla::widget::FaviconHelper::ObtainCachedIconFile(
|
||||
aFaviconURI, iconFilePath, mIOThread, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
aCachedIconPath = iconFilePath;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
JumpListBuilder::IsAvailable(JSContext* aCx, Promise** aPromise) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aPromise);
|
||||
MOZ_ASSERT(mIOThread);
|
||||
|
||||
ErrorResult result;
|
||||
RefPtr<Promise> promise =
|
||||
Promise::Create(xpc::CurrentNativeGlobal(aCx), result);
|
||||
|
||||
if (MOZ_UNLIKELY(result.Failed())) {
|
||||
return result.StealNSResult();
|
||||
}
|
||||
|
||||
nsMainThreadPtrHandle<Promise> promiseHolder(
|
||||
new nsMainThreadPtrHolder<Promise>("JumpListBuilder::IsAvailable promise",
|
||||
promise));
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NewRunnableMethod<nsMainThreadPtrHandle<Promise>>(
|
||||
"IsAvailable", this, &JumpListBuilder::DoIsAvailable,
|
||||
std::move(promiseHolder));
|
||||
nsresult rv = mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
promise.forget(aPromise);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
JumpListBuilder::CheckForRemovals(JSContext* aCx, Promise** aPromise) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aPromise);
|
||||
MOZ_ASSERT(mIOThread);
|
||||
|
||||
ErrorResult result;
|
||||
RefPtr<Promise> promise =
|
||||
Promise::Create(xpc::CurrentNativeGlobal(aCx), result);
|
||||
|
||||
if (MOZ_UNLIKELY(result.Failed())) {
|
||||
return result.StealNSResult();
|
||||
}
|
||||
|
||||
nsMainThreadPtrHandle<Promise> promiseHolder(
|
||||
new nsMainThreadPtrHolder<Promise>(
|
||||
"JumpListBuilder::CheckForRemovals promise", promise));
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NewRunnableMethod<nsMainThreadPtrHandle<Promise>>(
|
||||
"CheckForRemovals", this, &JumpListBuilder::DoCheckForRemovals,
|
||||
std::move(promiseHolder));
|
||||
|
||||
nsresult rv = mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
promise.forget(aPromise);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
JumpListBuilder::PopulateJumpList(
|
||||
const nsTArray<JS::Value>& aTaskDescriptions, const nsAString& aCustomTitle,
|
||||
const nsTArray<JS::Value>& aCustomDescriptions, JSContext* aCx,
|
||||
Promise** aPromise) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aPromise);
|
||||
MOZ_ASSERT(mIOThread);
|
||||
|
||||
if (aCustomDescriptions.Length() && aCustomTitle.IsEmpty()) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// Get rid of the old icons
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
new mozilla::widget::AsyncDeleteAllFaviconsFromDisk(true);
|
||||
mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
|
||||
nsTArray<WindowsJumpListShortcutDescription> taskDescs;
|
||||
for (auto& jsval : aTaskDescriptions) {
|
||||
JS::Rooted<JS::Value> rootedVal(aCx);
|
||||
if (NS_WARN_IF(!dom::ToJSValue(aCx, jsval, &rootedVal))) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
WindowsJumpListShortcutDescription desc;
|
||||
if (!desc.Init(aCx, rootedVal)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
taskDescs.AppendElement(std::move(desc));
|
||||
}
|
||||
|
||||
nsTArray<WindowsJumpListShortcutDescription> customDescs;
|
||||
for (auto& jsval : aCustomDescriptions) {
|
||||
JS::Rooted<JS::Value> rootedVal(aCx);
|
||||
if (NS_WARN_IF(!dom::ToJSValue(aCx, jsval, &rootedVal))) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
WindowsJumpListShortcutDescription desc;
|
||||
if (!desc.Init(aCx, rootedVal)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
customDescs.AppendElement(std::move(desc));
|
||||
}
|
||||
|
||||
ErrorResult result;
|
||||
RefPtr<Promise> promise =
|
||||
Promise::Create(xpc::CurrentNativeGlobal(aCx), result);
|
||||
|
||||
if (MOZ_UNLIKELY(result.Failed())) {
|
||||
return result.StealNSResult();
|
||||
}
|
||||
|
||||
nsMainThreadPtrHandle<Promise> promiseHolder(
|
||||
new nsMainThreadPtrHolder<Promise>(
|
||||
"JumpListBuilder::PopulateJumpList promise", promise));
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod<
|
||||
StoreCopyPassByRRef<nsTArray<WindowsJumpListShortcutDescription>>,
|
||||
nsString,
|
||||
StoreCopyPassByRRef<nsTArray<WindowsJumpListShortcutDescription>>,
|
||||
nsMainThreadPtrHandle<Promise>>(
|
||||
"PopulateJumpList", this, &JumpListBuilder::DoPopulateJumpList,
|
||||
std::move(taskDescs), aCustomTitle, std::move(customDescs),
|
||||
std::move(promiseHolder));
|
||||
nsresult rv = mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
promise.forget(aPromise);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
JumpListBuilder::ClearJumpList(JSContext* aCx, Promise** aPromise) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aPromise);
|
||||
MOZ_ASSERT(mIOThread);
|
||||
|
||||
ErrorResult result;
|
||||
RefPtr<Promise> promise =
|
||||
Promise::Create(xpc::CurrentNativeGlobal(aCx), result);
|
||||
|
||||
if (MOZ_UNLIKELY(result.Failed())) {
|
||||
return result.StealNSResult();
|
||||
}
|
||||
|
||||
nsMainThreadPtrHandle<Promise> promiseHolder(
|
||||
new nsMainThreadPtrHolder<Promise>(
|
||||
"JumpListBuilder::ClearJumpList promise", promise));
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NewRunnableMethod<nsMainThreadPtrHandle<Promise>>(
|
||||
"ClearJumpList", this, &JumpListBuilder::DoClearJumpList,
|
||||
std::move(promiseHolder));
|
||||
nsresult rv = mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
promise.forget(aPromise);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void JumpListBuilder::DoIsAvailable(
|
||||
const nsMainThreadPtrHandle<Promise>& aPromiseHolder) {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(aPromiseHolder);
|
||||
|
||||
if (!mJumpListBackend) {
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||
"DoIsAvailable", [promiseHolder = std::move(aPromiseHolder)]() {
|
||||
promiseHolder.get()->MaybeResolve(false);
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
bool isAvailable = mJumpListBackend->IsAvailable();
|
||||
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||
"DoIsAvailable",
|
||||
[promiseHolder = std::move(aPromiseHolder), isAvailable]() {
|
||||
promiseHolder.get()->MaybeResolve(isAvailable);
|
||||
}));
|
||||
}
|
||||
|
||||
void JumpListBuilder::DoCheckForRemovals(
|
||||
const nsMainThreadPtrHandle<Promise>& aPromiseHolder) {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(aPromiseHolder);
|
||||
|
||||
nsresult rv = NS_ERROR_UNEXPECTED;
|
||||
|
||||
auto errorHandler = MakeScopeExit([&aPromiseHolder, &rv]() {
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||
"DoCheckForRemovals",
|
||||
[promiseHolder = std::move(aPromiseHolder), rv]() {
|
||||
promiseHolder.get()->MaybeReject(rv);
|
||||
}));
|
||||
});
|
||||
|
||||
MOZ_ASSERT(mJumpListBackend);
|
||||
if (!mJumpListBackend) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Abort any ongoing list building that might not have been committed,
|
||||
// otherwise BeginList will give us problems.
|
||||
Unused << mJumpListBackend->AbortList();
|
||||
|
||||
nsTArray<nsString> urisToRemove;
|
||||
RefPtr<IObjectArray> objArray;
|
||||
uint32_t maxItems = 0;
|
||||
|
||||
HRESULT hr = mJumpListBackend->BeginList(
|
||||
&maxItems,
|
||||
IID_PPV_ARGS(static_cast<IObjectArray**>(getter_AddRefs(objArray))));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
rv = NS_ERROR_UNEXPECTED;
|
||||
return;
|
||||
}
|
||||
|
||||
RemoveIconCacheAndGetJumplistShortcutURIs(objArray, urisToRemove);
|
||||
|
||||
// We began a list in order to get the removals, which we can now abort.
|
||||
Unused << mJumpListBackend->AbortList();
|
||||
|
||||
errorHandler.release();
|
||||
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||
"DoCheckForRemovals", [urisToRemove = std::move(urisToRemove),
|
||||
promiseHolder = std::move(aPromiseHolder)]() {
|
||||
promiseHolder.get()->MaybeResolve(urisToRemove);
|
||||
}));
|
||||
}
|
||||
|
||||
void JumpListBuilder::DoPopulateJumpList(
|
||||
const nsTArray<WindowsJumpListShortcutDescription>&& aTaskDescriptions,
|
||||
const nsAString& aCustomTitle,
|
||||
const nsTArray<WindowsJumpListShortcutDescription>&& aCustomDescriptions,
|
||||
const nsMainThreadPtrHandle<Promise>& aPromiseHolder) {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(aPromiseHolder);
|
||||
|
||||
nsresult rv = NS_ERROR_UNEXPECTED;
|
||||
|
||||
auto errorHandler = MakeScopeExit([&aPromiseHolder, &rv]() {
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||
"DoPopulateJumpList",
|
||||
[promiseHolder = std::move(aPromiseHolder), rv]() {
|
||||
promiseHolder.get()->MaybeReject(rv);
|
||||
}));
|
||||
});
|
||||
|
||||
MOZ_ASSERT(mJumpListBackend);
|
||||
if (!mJumpListBackend) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Abort any ongoing list building that might not have been committed,
|
||||
// otherwise BeginList will give us problems.
|
||||
Unused << mJumpListBackend->AbortList();
|
||||
|
||||
nsTArray<nsString> urisToRemove;
|
||||
RefPtr<IObjectArray> objArray;
|
||||
uint32_t maxItems = 0;
|
||||
|
||||
HRESULT hr = mJumpListBackend->BeginList(
|
||||
&maxItems,
|
||||
IID_PPV_ARGS(static_cast<IObjectArray**>(getter_AddRefs(objArray))));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
rv = NS_ERROR_UNEXPECTED;
|
||||
return;
|
||||
}
|
||||
|
||||
if (urisToRemove.Length()) {
|
||||
// It'd be nice if we could return a more descriptive error here so that
|
||||
// the caller knows that they should have called checkForRemovals first.
|
||||
rv = NS_ERROR_UNEXPECTED;
|
||||
return;
|
||||
}
|
||||
|
||||
if (aTaskDescriptions.Length()) {
|
||||
RefPtr<IObjectCollection> taskCollection;
|
||||
hr = CoCreateInstance(CLSID_EnumerableObjectCollection, nullptr,
|
||||
CLSCTX_INPROC_SERVER, IID_IObjectCollection,
|
||||
getter_AddRefs(taskCollection));
|
||||
if (FAILED(hr)) {
|
||||
rv = NS_ERROR_UNEXPECTED;
|
||||
return;
|
||||
}
|
||||
|
||||
// Start by building the task list
|
||||
for (auto& desc : aTaskDescriptions) {
|
||||
// These should all be ShellLinks
|
||||
RefPtr<IShellLinkW> link;
|
||||
rv = GetShellLinkFromDescription(desc, link);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Let the errorHandler deal with this.
|
||||
return;
|
||||
}
|
||||
taskCollection->AddObject(link);
|
||||
}
|
||||
|
||||
RefPtr<IObjectArray> pTaskArray;
|
||||
hr = taskCollection->QueryInterface(IID_IObjectArray,
|
||||
getter_AddRefs(pTaskArray));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
rv = NS_ERROR_UNEXPECTED;
|
||||
return;
|
||||
}
|
||||
|
||||
hr = mJumpListBackend->AddUserTasks(pTaskArray);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
rv = NS_ERROR_FAILURE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (aCustomDescriptions.Length()) {
|
||||
// Then build the custom list
|
||||
RefPtr<IObjectCollection> customCollection;
|
||||
hr = CoCreateInstance(CLSID_EnumerableObjectCollection, nullptr,
|
||||
CLSCTX_INPROC_SERVER, IID_IObjectCollection,
|
||||
getter_AddRefs(customCollection));
|
||||
if (FAILED(hr)) {
|
||||
rv = NS_ERROR_UNEXPECTED;
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& desc : aCustomDescriptions) {
|
||||
// These should all be ShellLinks
|
||||
RefPtr<IShellLinkW> link;
|
||||
rv = GetShellLinkFromDescription(desc, link);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Let the errorHandler deal with this.
|
||||
return;
|
||||
}
|
||||
customCollection->AddObject(link);
|
||||
}
|
||||
|
||||
RefPtr<IObjectArray> pCustomArray;
|
||||
hr = customCollection->QueryInterface(IID_IObjectArray,
|
||||
getter_AddRefs(pCustomArray));
|
||||
if (FAILED(hr)) {
|
||||
rv = NS_ERROR_UNEXPECTED;
|
||||
return;
|
||||
}
|
||||
|
||||
hr = mJumpListBackend->AppendCategory(
|
||||
reinterpret_cast<const wchar_t*>(aCustomTitle.BeginReading()),
|
||||
pCustomArray);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
rv = NS_ERROR_UNEXPECTED;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
hr = mJumpListBackend->CommitList();
|
||||
if (FAILED(hr)) {
|
||||
rv = NS_ERROR_FAILURE;
|
||||
return;
|
||||
}
|
||||
|
||||
errorHandler.release();
|
||||
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||
"DoPopulateJumpList", [promiseHolder = std::move(aPromiseHolder)]() {
|
||||
promiseHolder.get()->MaybeResolveWithUndefined();
|
||||
}));
|
||||
}
|
||||
|
||||
void JumpListBuilder::DoClearJumpList(
|
||||
const nsMainThreadPtrHandle<Promise>& aPromiseHolder) {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(aPromiseHolder);
|
||||
|
||||
if (!mJumpListBackend) {
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||
"DoClearJumpList", [promiseHolder = std::move(aPromiseHolder)]() {
|
||||
promiseHolder.get()->MaybeReject(NS_ERROR_UNEXPECTED);
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(mJumpListBackend->DeleteList(mAppUserModelId.get()))) {
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||
"DoClearJumpList", [promiseHolder = std::move(aPromiseHolder)]() {
|
||||
promiseHolder.get()->MaybeResolveWithUndefined();
|
||||
}));
|
||||
} else {
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||
"DoClearJumpList", [promiseHolder = std::move(aPromiseHolder)]() {
|
||||
promiseHolder.get()->MaybeReject(NS_ERROR_FAILURE);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveIconCacheAndGetJumplistShortcutURIs - does multiple things to
|
||||
// avoid unnecessary extra XPCOM incantations. For each object in the input
|
||||
// array, if it's an IShellLinkW, this deletes the cached icon and adds the
|
||||
// url param to a list of URLs to be removed from the places database.
|
||||
void JumpListBuilder::RemoveIconCacheAndGetJumplistShortcutURIs(
|
||||
IObjectArray* aObjArray, nsTArray<nsString>& aURISpecs) {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
// Early return here just in case some versions of Windows don't populate this
|
||||
if (!aObjArray) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t count = 0;
|
||||
aObjArray->GetCount(&count);
|
||||
|
||||
for (uint32_t idx = 0; idx < count; idx++) {
|
||||
RefPtr<IShellLinkW> pLink;
|
||||
|
||||
if (FAILED(aObjArray->GetAt(idx, IID_IShellLinkW,
|
||||
static_cast<void**>(getter_AddRefs(pLink))))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
wchar_t buf[MAX_PATH];
|
||||
HRESULT hres = pLink->GetArguments(buf, MAX_PATH);
|
||||
if (SUCCEEDED(hres)) {
|
||||
LPWSTR* arglist;
|
||||
int32_t numArgs;
|
||||
|
||||
arglist = ::CommandLineToArgvW(buf, &numArgs);
|
||||
if (arglist && numArgs > 0) {
|
||||
nsString spec(arglist[0]);
|
||||
aURISpecs.AppendElement(std::move(spec));
|
||||
::LocalFree(arglist);
|
||||
}
|
||||
}
|
||||
|
||||
int iconIdx = 0;
|
||||
hres = pLink->GetIconLocation(buf, MAX_PATH, &iconIdx);
|
||||
if (SUCCEEDED(hres)) {
|
||||
nsDependentString spec(buf);
|
||||
DeleteIconFromDisk(spec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JumpListBuilder::DeleteIconFromDisk(const nsAString& aPath) {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
// Check that we aren't deleting some arbitrary file that is not an icon
|
||||
if (StringTail(aPath, 4).LowerCaseEqualsASCII(".ico")) {
|
||||
// Construct the parent path of the passed in path
|
||||
nsCOMPtr<nsIFile> icoFile;
|
||||
nsresult rv = NS_NewLocalFile(aPath, true, getter_AddRefs(icoFile));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
icoFile->Remove(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Converts a WindowsJumpListShortcutDescription into a IShellLinkW
|
||||
nsresult JumpListBuilder::GetShellLinkFromDescription(
|
||||
const WindowsJumpListShortcutDescription& aDesc,
|
||||
RefPtr<IShellLinkW>& aShellLink) {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
HRESULT hr;
|
||||
IShellLinkW* psl;
|
||||
|
||||
// Shell links:
|
||||
// http://msdn.microsoft.com/en-us/library/bb776891(VS.85).aspx
|
||||
// http://msdn.microsoft.com/en-us/library/bb774950(VS.85).aspx
|
||||
|
||||
// Create a IShellLink
|
||||
hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
|
||||
IID_IShellLinkW, (LPVOID*)&psl);
|
||||
if (FAILED(hr)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// Store the title of the app
|
||||
if (!aDesc.mTitle.IsEmpty()) {
|
||||
IPropertyStore* pPropStore = nullptr;
|
||||
hr = psl->QueryInterface(IID_IPropertyStore, (LPVOID*)&pPropStore);
|
||||
if (FAILED(hr)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
PROPVARIANT pv;
|
||||
::InitPropVariantFromString(aDesc.mTitle.get(), &pv);
|
||||
|
||||
pPropStore->SetValue(PKEY_Title, pv);
|
||||
pPropStore->Commit();
|
||||
pPropStore->Release();
|
||||
|
||||
PropVariantClear(&pv);
|
||||
}
|
||||
|
||||
// Store the rest of the params
|
||||
hr = psl->SetPath(aDesc.mPath.get());
|
||||
hr = psl->SetDescription(aDesc.mDescription.get());
|
||||
if (aDesc.mArguments.WasPassed() && !aDesc.mArguments.Value().IsEmpty()) {
|
||||
hr = psl->SetArguments(aDesc.mArguments.Value().get());
|
||||
} else {
|
||||
hr = psl->SetArguments(L"");
|
||||
}
|
||||
|
||||
// Set up the fallback icon in the event that a valid icon URI has
|
||||
// not been supplied.
|
||||
hr = psl->SetIconLocation(aDesc.mPath.get(), aDesc.mFallbackIconIndex);
|
||||
|
||||
if (aDesc.mIconPath.WasPassed() && !aDesc.mIconPath.Value().IsEmpty()) {
|
||||
// Always use the first icon in the ICO file, as our encoded icon only has 1
|
||||
// resource
|
||||
hr = psl->SetIconLocation(aDesc.mIconPath.Value().get(), 0);
|
||||
}
|
||||
|
||||
aShellLink = dont_AddRef(psl);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
JumpListBuilder::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_ENSURE_ARG_POINTER(aTopic);
|
||||
if (strcmp(aTopic, TOPIC_PROFILE_BEFORE_CHANGE) == 0) {
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
do_GetService("@mozilla.org/observer-service;1");
|
||||
if (observerService) {
|
||||
observerService->RemoveObserver(this, TOPIC_PROFILE_BEFORE_CHANGE);
|
||||
observerService->RemoveObserver(this, TOPIC_CLEAR_PRIVATE_DATA);
|
||||
}
|
||||
|
||||
mIOThread->Dispatch(NewRunnableMethod("ShutdownBackend", this,
|
||||
&JumpListBuilder::DoShutdownBackend),
|
||||
NS_DISPATCH_NORMAL);
|
||||
|
||||
mIOThread->Shutdown();
|
||||
} else if (strcmp(aTopic, "nsPref:changed") == 0 &&
|
||||
nsDependentString(aData).EqualsASCII(kPrefTaskbarEnabled)) {
|
||||
bool enabled = Preferences::GetBool(kPrefTaskbarEnabled, true);
|
||||
if (!enabled) {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
new mozilla::widget::AsyncDeleteAllFaviconsFromDisk();
|
||||
mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
} else if (strcmp(aTopic, TOPIC_CLEAR_PRIVATE_DATA) == 0) {
|
||||
// Delete JumpListCache icons from Disk, if any.
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
new mozilla::widget::AsyncDeleteAllFaviconsFromDisk(false);
|
||||
mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace widget
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,101 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef __JumpListBuilder_h__
|
||||
#define __JumpListBuilder_h__
|
||||
|
||||
#include "nsIJumpListBuilder.h"
|
||||
|
||||
#include "nsIObserver.h"
|
||||
#include "mozilla/LazyIdleThread.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
struct WindowsJumpListShortcutDescription;
|
||||
} // namespace dom
|
||||
|
||||
namespace widget {
|
||||
|
||||
/**
|
||||
* This is an abstract class for a backend to write to the Windows Jump List.
|
||||
*
|
||||
* It has a 1-to-1 method mapping with ICustomDestinationList. The abtract
|
||||
* class allows us to implement a "fake" backend for automated testing.
|
||||
*/
|
||||
class JumpListBackend {
|
||||
NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
|
||||
|
||||
virtual bool IsAvailable() = 0;
|
||||
|
||||
virtual HRESULT SetAppID(LPCWSTR pszAppID) = 0;
|
||||
virtual HRESULT BeginList(UINT* pcMinSlots, REFIID riid, void** ppv) = 0;
|
||||
virtual HRESULT AddUserTasks(IObjectArray* poa) = 0;
|
||||
virtual HRESULT AppendCategory(LPCWSTR pszCategory, IObjectArray* poa) = 0;
|
||||
virtual HRESULT CommitList() = 0;
|
||||
virtual HRESULT AbortList() = 0;
|
||||
virtual HRESULT DeleteList(LPCWSTR pszAppID) = 0;
|
||||
virtual HRESULT AppendKnownCategory(KNOWNDESTCATEGORY category) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~JumpListBackend() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* JumpListBuilder is a component that can be used to manage the Windows
|
||||
* Jump List off of the main thread.
|
||||
*/
|
||||
class JumpListBuilder : public nsIJumpListBuilder, public nsIObserver {
|
||||
virtual ~JumpListBuilder();
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIJUMPLISTBUILDER
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
explicit JumpListBuilder(const nsAString& aAppUserModelId,
|
||||
RefPtr<JumpListBackend> aTestingBackend = nullptr);
|
||||
|
||||
private:
|
||||
// These all run on the lazy helper thread.
|
||||
void DoSetupBackend();
|
||||
void DoSetupTestingBackend(RefPtr<JumpListBackend> aTestingBackend);
|
||||
void DoShutdownBackend();
|
||||
void DoSetAppID(nsString aAppUserModelID);
|
||||
void DoIsAvailable(const nsMainThreadPtrHandle<dom::Promise>& aPromiseHolder);
|
||||
void DoCheckForRemovals(
|
||||
const nsMainThreadPtrHandle<dom::Promise>& aPromiseHolder);
|
||||
void DoPopulateJumpList(
|
||||
const nsTArray<dom::WindowsJumpListShortcutDescription>&&
|
||||
aTaskDescriptions,
|
||||
const nsAString& aCustomTitle,
|
||||
const nsTArray<dom::WindowsJumpListShortcutDescription>&&
|
||||
aCustomDescriptions,
|
||||
const nsMainThreadPtrHandle<dom::Promise>& aPromiseHolder);
|
||||
void DoClearJumpList(
|
||||
const nsMainThreadPtrHandle<dom::Promise>& aPromiseHolder);
|
||||
void RemoveIconCacheAndGetJumplistShortcutURIs(IObjectArray* aObjArray,
|
||||
nsTArray<nsString>& aURISpecs);
|
||||
void DeleteIconFromDisk(const nsAString& aPath);
|
||||
nsresult GetShellLinkFromDescription(
|
||||
const dom::WindowsJumpListShortcutDescription& aDesc,
|
||||
RefPtr<IShellLinkW>& aShellLink);
|
||||
|
||||
// This is written to once during construction on the main thread before the
|
||||
// lazy helper thread is created. After that, the lazy helper thread might
|
||||
// read from it.
|
||||
nsString mAppUserModelId;
|
||||
|
||||
// This is only accessed by the lazy helper thread.
|
||||
RefPtr<JumpListBackend> mJumpListBackend;
|
||||
|
||||
// This is only accessed by the main thread.
|
||||
RefPtr<LazyIdleThread> mIOThread;
|
||||
};
|
||||
|
||||
} // namespace widget
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* __JumpListBuilder_h__ */
|
|
@ -11,6 +11,7 @@
|
|||
#include "nsITaskbarPreviewController.h"
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/widget/JumpListBuilder.h"
|
||||
#include <nsError.h>
|
||||
#include <nsCOMPtr.h>
|
||||
#include <nsIWidget.h>
|
||||
|
@ -36,7 +37,8 @@
|
|||
#include <propkey.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
static NS_DEFINE_CID(kLegacyJumpListBuilderCID, NS_WIN_LEGACYJUMPLISTBUILDER_CID);
|
||||
static NS_DEFINE_CID(kLegacyJumpListBuilderCID,
|
||||
NS_WIN_LEGACYJUMPLISTBUILDER_CID);
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -404,8 +406,8 @@ WinTaskbar::GetOverlayIconController(
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WinTaskbar::CreateLegacyJumpListBuilder(bool aPrivateBrowsing,
|
||||
nsILegacyJumpListBuilder** aJumpListBuilder) {
|
||||
WinTaskbar::CreateLegacyJumpListBuilder(
|
||||
bool aPrivateBrowsing, nsILegacyJumpListBuilder** aJumpListBuilder) {
|
||||
nsresult rv;
|
||||
|
||||
if (LegacyJumpListBuilder::sBuildingList) return NS_ERROR_ALREADY_INITIALIZED;
|
||||
|
@ -423,6 +425,21 @@ WinTaskbar::CreateLegacyJumpListBuilder(bool aPrivateBrowsing,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WinTaskbar::CreateJumpListBuilder(bool aPrivateBrowsing,
|
||||
nsIJumpListBuilder** aJumpListBuilder) {
|
||||
nsAutoString aumid;
|
||||
GenerateAppUserModelID(aumid, aPrivateBrowsing);
|
||||
|
||||
nsCOMPtr<nsIJumpListBuilder> builder = new JumpListBuilder(aumid);
|
||||
if (!builder) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
NS_IF_ADDREF(*aJumpListBuilder = builder);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WinTaskbar::SetGroupIdForWindow(mozIDOMWindow* aParent,
|
||||
const nsAString& aIdentifier) {
|
||||
|
|
|
@ -52,6 +52,7 @@ EXPORTS.mozilla.widget += [
|
|||
"CompositorWidgetChild.h",
|
||||
"CompositorWidgetParent.h",
|
||||
"InProcessWinCompositorWidget.h",
|
||||
"JumpListBuilder.h",
|
||||
"nsWindowLoggedMessages.h",
|
||||
"WinCompositorWidget.h",
|
||||
"WinCompositorWindowThread.h",
|
||||
|
@ -72,6 +73,7 @@ UNIFIED_SOURCES += [
|
|||
"GfxInfo.cpp",
|
||||
"IEnumFE.cpp",
|
||||
"IMMHandler.cpp",
|
||||
"JumpListBuilder.cpp",
|
||||
"KeyboardLayout.cpp",
|
||||
"LegacyJumpListItem.cpp",
|
||||
"LSPAnnotator.cpp",
|
||||
|
|
Загрузка…
Ссылка в новой задаче