summaryrefslogtreecommitdiffstats
path: root/widget/windows/tests/gtest/TestJumpListBuilder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/windows/tests/gtest/TestJumpListBuilder.cpp')
-rw-r--r--widget/windows/tests/gtest/TestJumpListBuilder.cpp261
1 files changed, 228 insertions, 33 deletions
diff --git a/widget/windows/tests/gtest/TestJumpListBuilder.cpp b/widget/windows/tests/gtest/TestJumpListBuilder.cpp
index 5494c42d37..531e326465 100644
--- a/widget/windows/tests/gtest/TestJumpListBuilder.cpp
+++ b/widget/windows/tests/gtest/TestJumpListBuilder.cpp
@@ -275,14 +275,14 @@ class TestingJumpListBackend : public JumpListBackend {
* @param {nsTArray<WindowsJumpListShortcutDescription>&} aArray
* The outparam for the array of generated
* WindowsJumpListShortcutDescriptions.
- * @param {nsTArray<JS::Value>&} aJSValArray
+ * @param {JS::Handle<JSObject*>} aJSArrayObj
* The outparam for the array of JS::Value's representing the generated
* WindowsJumpListShortcutDescriptions.
*/
void GenerateWindowsJumpListShortcutDescriptions(
JSContext* aCx, uint32_t howMany, bool longDescription,
nsTArray<WindowsJumpListShortcutDescription>& aArray,
- nsTArray<JS::Value>& aJSValArray) {
+ JS::Handle<JSObject*> aJSArrayObj) {
for (uint32_t i = 0; i < howMany; ++i) {
WindowsJumpListShortcutDescription desc;
nsAutoString title(u"Test Task #");
@@ -321,7 +321,8 @@ void GenerateWindowsJumpListShortcutDescriptions(
aArray.AppendElement(desc);
JS::Rooted<JS::Value> descJSValue(aCx);
ASSERT_TRUE(ToJSValue(aCx, desc, &descJSValue));
- aJSValArray.AppendElement(std::move(descJSValue));
+
+ MOZ_ALWAYS_TRUE(JS_SetElement(aCx, aJSArrayObj, i, descJSValue));
}
}
@@ -393,15 +394,15 @@ TEST(JumpListBuilder, CheckForRemovals)
EXPECT_CALL(*testBackend, BeginList)
.WillOnce([](UINT* pcMinSlots, REFIID riid, void** ppv) {
RefPtr<IObjectCollection> collection;
- DebugOnly<HRESULT> hr = CoCreateInstance(
+ HRESULT hr = CoCreateInstance(
CLSID_EnumerableObjectCollection, nullptr, CLSCTX_INPROC_SERVER,
IID_IObjectCollection, getter_AddRefs(collection));
- MOZ_ASSERT(SUCCEEDED(hr));
+ MOZ_RELEASE_ASSERT(SUCCEEDED(hr));
RefPtr<IShellLinkW> link;
hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
IID_IShellLinkW, getter_AddRefs(link));
- MOZ_ASSERT(SUCCEEDED(hr));
+ MOZ_RELEASE_ASSERT(SUCCEEDED(hr));
nsAutoString firstLinkHref(u"https://example.com"_ns);
link->SetArguments(firstLinkHref.get());
@@ -421,7 +422,7 @@ TEST(JumpListBuilder, CheckForRemovals)
RefPtr<IObjectArray> pArray;
hr = collection->QueryInterface(IID_IObjectArray,
getter_AddRefs(pArray));
- MOZ_ASSERT(SUCCEEDED(hr));
+ MOZ_RELEASE_ASSERT(SUCCEEDED(hr));
*ppv = static_cast<IObjectArray*>(pArray);
(static_cast<IUnknown*>(*ppv))->AddRef();
@@ -478,6 +479,115 @@ TEST(JumpListBuilder, CheckForRemovals)
}
/**
+ * Tests calling CheckForRemovals and receiving a jump list entry with a very
+ * long URL doesn't result in JumpListBuilder truncating the URL before handing
+ * it back to the caller. Expects the following calls in order:
+ *
+ * - SetAppID
+ * - AbortList
+ * - BeginList
+ * - AbortList
+ */
+TEST(JumpListBuilder, CheckForRemovalsLongURL)
+{
+ RefPtr<StrictMock<TestingJumpListBackend>> testBackend =
+ new StrictMock<TestingJumpListBackend>();
+ nsAutoString aumid(u"TestApplicationID");
+ // We set up this expectation here because SetAppID will be called soon
+ // after construction of the JumpListBuilder via the background thread.
+ EXPECT_CALL(*testBackend, SetAppID(_)).Times(1);
+
+ nsCOMPtr<nsIJumpListBuilder> builder =
+ new JumpListBuilder(aumid, testBackend);
+ ASSERT_TRUE(builder);
+
+ EXPECT_CALL(*testBackend, AbortList()).Times(2);
+
+ constexpr static const nsLiteralString veryLongHref(
+ u"https://example.verylongurl.test/first/second/third/fourth/fifth/"
+ "sixth/seventh/eighth/ninth/tenth/eleventh/twelfth/thirteenth/"
+ "fourteenth/fifteenth-path-item/some/more/junk/after/that/more/more/"
+ "more/more/more/more/more/more/more/more/more/more/more/more/more/more/"
+ "more/more/more/more/more/more/more/more/more/more/more"_ns);
+ // This test ensures that URLs longer than MAX_PATH do not get truncated by
+ // JumpListBuilder or one of its utilities, so we must ensure that the static
+ // URL we just defined is actually longer than MAX_PATH.
+ static_assert(veryLongHref.Length() > MAX_PATH);
+
+ // Let's prepare BeginList to return a one entry collection of IShellLinks.
+ // The IShellLink will have the URL be very long - over MAX_PATH characters.
+ EXPECT_CALL(*testBackend, BeginList)
+ .WillOnce([](UINT* pcMinSlots, REFIID riid, void** ppv) {
+ RefPtr<IObjectCollection> collection;
+ HRESULT hr = CoCreateInstance(
+ CLSID_EnumerableObjectCollection, nullptr, CLSCTX_INPROC_SERVER,
+ IID_IObjectCollection, getter_AddRefs(collection));
+ MOZ_RELEASE_ASSERT(SUCCEEDED(hr));
+
+ RefPtr<IShellLinkW> link;
+ hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
+ IID_IShellLinkW, getter_AddRefs(link));
+ MOZ_RELEASE_ASSERT(SUCCEEDED(hr));
+
+ link->SetArguments(veryLongHref.get());
+
+ nsAutoString appPath(u"C:\\Tmp\\firefox.exe"_ns);
+ link->SetIconLocation(appPath.get(), 0);
+
+ collection->AddObject(link);
+
+ RefPtr<IObjectArray> pArray;
+ hr = collection->QueryInterface(IID_IObjectArray,
+ getter_AddRefs(pArray));
+ MOZ_RELEASE_ASSERT(SUCCEEDED(hr));
+
+ *ppv = static_cast<IObjectArray*>(pArray);
+ (static_cast<IUnknown*>(*ppv))->AddRef();
+
+ // This is the default value to return, according to
+ // https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-icustomdestinationlist-beginlist
+ *pcMinSlots = 10;
+
+ return S_OK;
+ });
+
+ AutoJSAPI jsapi;
+ MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope()));
+ JSContext* cx = jsapi.cx();
+ RefPtr<Promise> promise;
+ nsresult rv = builder->CheckForRemovals(cx, getter_AddRefs(promise));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(promise);
+
+ RefPtr<WaitForResolver> resolver = new WaitForResolver();
+ promise->AppendNativeHandler(resolver);
+ JS::Rooted<JS::Value> result(cx);
+ resolver->SpinUntilResolvedWithResult(&result);
+
+ ASSERT_TRUE(result.isObject());
+ JS::Rooted<JSObject*> obj(cx, result.toObjectOrNull());
+
+ bool isArray;
+ ASSERT_TRUE(JS::IsArrayObject(cx, obj, &isArray));
+ ASSERT_TRUE(isArray);
+
+ // We should expect to see 1 URL string returned in the array.
+ uint32_t length = 0;
+ ASSERT_TRUE(JS::GetArrayLength(cx, obj, &length));
+ ASSERT_EQ(length, 1U);
+
+ // The URL should match veryLongHref
+ JS::Rooted<JS::Value> returnedURLValue(cx);
+ ASSERT_TRUE(JS_GetElement(cx, obj, 0, &returnedURLValue));
+ JS::Rooted<JSString*> returnedURLValueJSString(cx,
+ returnedURLValue.toString());
+ nsAutoJSString returnedURLValueAutoString;
+ ASSERT_TRUE(returnedURLValueAutoString.init(cx, returnedURLValueJSString));
+
+ ASSERT_TRUE(returnedURLValueAutoString.Equals(veryLongHref));
+}
+
+/**
* Tests calling PopulateJumpList with empty arguments, which should call the
* following methods on the backend, in order:
*
@@ -506,9 +616,13 @@ TEST(JumpListBuilder, PopulateJumpListEmpty)
JSContext* cx = jsapi.cx();
RefPtr<Promise> promise;
- nsTArray<JS::Value> taskDescJSVals;
+ JS::Rooted<JSObject*> taskDescsObj(cx, JS::NewArrayObject(cx, 0));
+ JS::Rooted<JS::Value> taskDescsJSVal(cx, JS::ObjectValue(*taskDescsObj));
+
nsAutoString customTitle(u"");
- nsTArray<JS::Value> customDescJSVals;
+
+ JS::Rooted<JSObject*> customDescsObj(cx, JS::NewArrayObject(cx, 0));
+ JS::Rooted<JS::Value> customDescsJSVal(cx, JS::ObjectValue(*customDescsObj));
EXPECT_CALL(*testBackend, AbortList()).Times(1);
EXPECT_CALL(*testBackend, BeginList(_, _, _)).Times(1);
@@ -516,7 +630,7 @@ TEST(JumpListBuilder, PopulateJumpListEmpty)
EXPECT_CALL(*testBackend, DeleteList(_)).Times(0);
nsresult rv =
- builder->PopulateJumpList(taskDescJSVals, customTitle, customDescJSVals,
+ builder->PopulateJumpList(taskDescsJSVal, customTitle, customDescsJSVal,
cx, getter_AddRefs(promise));
ASSERT_TRUE(NS_SUCCEEDED(rv));
ASSERT_TRUE(promise);
@@ -558,13 +672,15 @@ TEST(JumpListBuilder, PopulateJumpListOnlyTasks)
JSContext* cx = jsapi.cx();
RefPtr<Promise> promise;
- nsTArray<JS::Value> taskDescJSVals;
+ JS::Rooted<JSObject*> taskDescsObj(cx, JS::NewArrayObject(cx, 0));
+ JS::Rooted<JS::Value> taskDescsJSVal(cx, JS::ObjectValue(*taskDescsObj));
nsTArray<WindowsJumpListShortcutDescription> taskDescs;
GenerateWindowsJumpListShortcutDescriptions(cx, 2, false, taskDescs,
- taskDescJSVals);
+ taskDescsObj);
nsAutoString customTitle(u"");
- nsTArray<JS::Value> customDescJSVals;
+ JS::Rooted<JSObject*> customDescsObj(cx, JS::NewArrayObject(cx, 0));
+ JS::Rooted<JS::Value> customDescsJSVal(cx, JS::ObjectValue(*customDescsObj));
EXPECT_CALL(*testBackend, AbortList()).Times(1);
EXPECT_CALL(*testBackend, BeginList(_, _, _)).Times(1);
@@ -575,7 +691,7 @@ TEST(JumpListBuilder, PopulateJumpListOnlyTasks)
EXPECT_CALL(*testBackend, DeleteList(_)).Times(0);
nsresult rv =
- builder->PopulateJumpList(taskDescJSVals, customTitle, customDescJSVals,
+ builder->PopulateJumpList(taskDescsJSVal, customTitle, customDescsJSVal,
cx, getter_AddRefs(promise));
ASSERT_TRUE(NS_SUCCEEDED(rv));
ASSERT_TRUE(promise);
@@ -617,13 +733,17 @@ TEST(JumpListBuilder, PopulateJumpListOnlyCustomItems)
JSContext* cx = jsapi.cx();
RefPtr<Promise> promise;
- nsTArray<WindowsJumpListShortcutDescription> descs;
- nsTArray<JS::Value> customDescJSVals;
- GenerateWindowsJumpListShortcutDescriptions(cx, 2, false, descs,
- customDescJSVals);
+ JS::Rooted<JSObject*> taskDescsObj(cx, JS::NewArrayObject(cx, 0));
+ JS::Rooted<JS::Value> taskDescsJSVal(cx);
+ taskDescsJSVal.setObject(*taskDescsObj);
nsAutoString customTitle(u"My custom title");
- nsTArray<JS::Value> taskDescJSVals;
+
+ JS::Rooted<JSObject*> customDescsObj(cx, JS::NewArrayObject(cx, 0));
+ JS::Rooted<JS::Value> customDescsJSVal(cx, JS::ObjectValue(*customDescsObj));
+ nsTArray<WindowsJumpListShortcutDescription> descs;
+ GenerateWindowsJumpListShortcutDescriptions(cx, 2, false, descs,
+ customDescsObj);
EXPECT_CALL(*testBackend, AbortList()).Times(1);
EXPECT_CALL(*testBackend, BeginList(_, _, _)).Times(1);
@@ -636,7 +756,7 @@ TEST(JumpListBuilder, PopulateJumpListOnlyCustomItems)
EXPECT_CALL(*testBackend, DeleteList(_)).Times(0);
nsresult rv =
- builder->PopulateJumpList(taskDescJSVals, customTitle, customDescJSVals,
+ builder->PopulateJumpList(taskDescsJSVal, customTitle, customDescsJSVal,
cx, getter_AddRefs(promise));
ASSERT_TRUE(NS_SUCCEEDED(rv));
ASSERT_TRUE(promise);
@@ -679,30 +799,102 @@ TEST(JumpListBuilder, PopulateJumpList)
JSContext* cx = jsapi.cx();
RefPtr<Promise> promise;
+ JS::Rooted<JSObject*> taskDescsObj(cx, JS::NewArrayObject(cx, 0));
+ JS::Rooted<JS::Value> taskDescsJSVal(cx, JS::ObjectValue(*taskDescsObj));
nsTArray<WindowsJumpListShortcutDescription> taskDescs;
- nsTArray<JS::Value> taskDescJSVals;
GenerateWindowsJumpListShortcutDescriptions(cx, 2, false, taskDescs,
- taskDescJSVals);
+ taskDescsObj);
+
+ nsAutoString customTitle(u"My custom title");
+ JS::Rooted<JSObject*> customDescsObj(cx, JS::NewArrayObject(cx, 0));
+ JS::Rooted<JS::Value> customDescsJSVal(cx, JS::ObjectValue(*customDescsObj));
nsTArray<WindowsJumpListShortcutDescription> customDescs;
- nsTArray<JS::Value> customDescJSVals;
GenerateWindowsJumpListShortcutDescriptions(cx, 2, false, customDescs,
- customDescJSVals);
+ customDescsObj);
+
+ EXPECT_CALL(*testBackend, AbortList()).Times(1);
+ EXPECT_CALL(*testBackend, BeginList(_, _, _)).Times(1);
+ EXPECT_CALL(*testBackend, AddUserTasks(ShellLinksEq(&taskDescs))).Times(1);
+
+ EXPECT_CALL(*testBackend, AppendCategory(LPCWSTREq(customTitle.get()),
+ ShellLinksEq(&customDescs)))
+ .Times(1);
+ EXPECT_CALL(*testBackend, CommitList()).Times(1);
+ EXPECT_CALL(*testBackend, DeleteList(_)).Times(0);
+
+ nsresult rv =
+ builder->PopulateJumpList(taskDescsJSVal, customTitle, customDescsJSVal,
+ cx, getter_AddRefs(promise));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE(promise);
+
+ RefPtr<WaitForResolver> resolver = new WaitForResolver();
+ promise->AppendNativeHandler(resolver);
+ JS::Rooted<JS::Value> result(cx);
+ resolver->SpinUntilResolved();
+}
+
+/**
+ * Tests calling PopulateJumpList with tasks and custom items, but makes it so
+ * that AppendCategory returns E_ACCESSDENIED, which can occur if Windows is
+ * configured to not show recently opened items. The PopulateJumpList Promise
+ * should still resolve.
+ *
+ * - SetAppID
+ * - AbortList
+ * - BeginList
+ * - AddUserTasks
+ * - AppendCategory
+ * - CommitList
+ *
+ * This should result in a jump list with just built-in tasks.
+ */
+TEST(JumpListBuilder, PopulateJumpListNoOpenedItems)
+{
+ RefPtr<StrictMock<TestingJumpListBackend>> testBackend =
+ new StrictMock<TestingJumpListBackend>();
+ nsAutoString aumid(u"TestApplicationID");
+ // We set up this expectation here because SetAppID will be called soon
+ // after construction of the JumpListBuilder via the background thread.
+ EXPECT_CALL(*testBackend, SetAppID(_)).Times(1);
+
+ nsCOMPtr<nsIJumpListBuilder> builder =
+ new JumpListBuilder(aumid, testBackend);
+ ASSERT_TRUE(builder);
+
+ AutoJSAPI jsapi;
+ MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope()));
+ JSContext* cx = jsapi.cx();
+ RefPtr<Promise> promise;
+
+ JS::Rooted<JSObject*> taskDescsObj(cx, JS::NewArrayObject(cx, 0));
+ JS::Rooted<JS::Value> taskDescsJSVal(cx, JS::ObjectValue(*taskDescsObj));
+ nsTArray<WindowsJumpListShortcutDescription> taskDescs;
+ GenerateWindowsJumpListShortcutDescriptions(cx, 2, false, taskDescs,
+ taskDescsObj);
nsAutoString customTitle(u"My custom title");
+ JS::Rooted<JSObject*> customDescsObj(cx, JS::NewArrayObject(cx, 0));
+ JS::Rooted<JS::Value> customDescsJSVal(cx, JS::ObjectValue(*customDescsObj));
+ nsTArray<WindowsJumpListShortcutDescription> customDescs;
+ GenerateWindowsJumpListShortcutDescriptions(cx, 2, false, customDescs,
+ customDescsObj);
+
EXPECT_CALL(*testBackend, AbortList()).Times(1);
EXPECT_CALL(*testBackend, BeginList(_, _, _)).Times(1);
EXPECT_CALL(*testBackend, AddUserTasks(ShellLinksEq(&taskDescs))).Times(1);
EXPECT_CALL(*testBackend, AppendCategory(LPCWSTREq(customTitle.get()),
ShellLinksEq(&customDescs)))
- .Times(1);
+ .WillOnce([] { return E_ACCESSDENIED; });
+
EXPECT_CALL(*testBackend, CommitList()).Times(1);
EXPECT_CALL(*testBackend, DeleteList(_)).Times(0);
nsresult rv =
- builder->PopulateJumpList(taskDescJSVals, customTitle, customDescJSVals,
+ builder->PopulateJumpList(taskDescsJSVal, customTitle, customDescsJSVal,
cx, getter_AddRefs(promise));
ASSERT_TRUE(NS_SUCCEEDED(rv));
ASSERT_TRUE(promise);
@@ -780,15 +972,20 @@ TEST(JumpListBuilder, TruncateDescription)
JSContext* cx = jsapi.cx();
RefPtr<Promise> promise;
+ JS::Rooted<JSObject*> taskDescsObj(cx, JS::NewArrayObject(cx, 0));
+ JS::Rooted<JS::Value> taskDescsJSVal(cx, JS::ObjectValue(*taskDescsObj));
nsTArray<WindowsJumpListShortcutDescription> taskDescs;
- nsTArray<JS::Value> taskDescJSVals;
GenerateWindowsJumpListShortcutDescriptions(cx, 2, true, taskDescs,
- taskDescJSVals);
+ taskDescsObj);
+ nsAutoString customTitle(u"My custom title");
+
+ JS::Rooted<JSObject*> customDescsObj(cx, JS::NewArrayObject(cx, 0));
+ JS::Rooted<JS::Value> customDescsJSVal(cx, JS::ObjectValue(*customDescsObj));
nsTArray<WindowsJumpListShortcutDescription> customDescs;
- nsTArray<JS::Value> customDescJSVals;
GenerateWindowsJumpListShortcutDescriptions(cx, 2, true, customDescs,
- customDescJSVals);
+ customDescsObj);
+
// We expect the long descriptions to be truncated to 260 characters, so
// we'll truncate the descriptions here ourselves.
for (auto& taskDesc : taskDescs) {
@@ -798,8 +995,6 @@ TEST(JumpListBuilder, TruncateDescription)
customDesc.mDescription.SetLength(MAX_PATH - 1);
}
- nsAutoString customTitle(u"My custom title");
-
EXPECT_CALL(*testBackend, AbortList()).Times(1);
EXPECT_CALL(*testBackend, BeginList(_, _, _)).Times(1);
EXPECT_CALL(*testBackend, AddUserTasks(ShellLinksEq(&taskDescs))).Times(1);
@@ -811,7 +1006,7 @@ TEST(JumpListBuilder, TruncateDescription)
EXPECT_CALL(*testBackend, DeleteList(_)).Times(0);
nsresult rv =
- builder->PopulateJumpList(taskDescJSVals, customTitle, customDescJSVals,
+ builder->PopulateJumpList(taskDescsJSVal, customTitle, customDescsJSVal,
cx, getter_AddRefs(promise));
ASSERT_TRUE(NS_SUCCEEDED(rv));
ASSERT_TRUE(promise);