зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1830725: Synchronous `nsWindowsPackageManager::GetCampaignId` blocks main thread for many seconds r=nrishel
Made GetCampaignId asynchronous to not block anything. Also fix Bug 1872933 (broken build) Differential Revision: https://phabricator.services.mozilla.com/D188520
This commit is contained in:
Родитель
05f593b0cc
Коммит
3b64a17a14
|
@ -64,10 +64,12 @@ export var AttributionCode = {
|
|||
* Wrapper to pull campaign IDs from MSIX builds.
|
||||
* This function solely exists to make it easy to mock out for tests.
|
||||
*/
|
||||
get msixCampaignId() {
|
||||
return Cc["@mozilla.org/windows-package-manager;1"]
|
||||
.createInstance(Ci.nsIWindowsPackageManager)
|
||||
.getCampaignId();
|
||||
async msixCampaignId() {
|
||||
const windowsPackageManager = Cc[
|
||||
"@mozilla.org/windows-package-manager;1"
|
||||
].createInstance(Ci.nsIWindowsPackageManager);
|
||||
|
||||
return windowsPackageManager.campaignId();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -346,7 +348,7 @@ export var AttributionCode = {
|
|||
)}`
|
||||
);
|
||||
let encoder = new TextEncoder();
|
||||
bytes = encoder.encode(encodeURIComponent(this.msixCampaignId));
|
||||
bytes = encoder.encode(encodeURIComponent(await this.msixCampaignId()));
|
||||
} else {
|
||||
bytes = await AttributionIOUtils.read(attributionFile.path);
|
||||
}
|
||||
|
|
|
@ -80,9 +80,17 @@ add_task(async function test_read_error() {
|
|||
throw new Error("read_error");
|
||||
};
|
||||
|
||||
// On MSIX builds, AttributionIOUtils.read is not used; AttributionCode.msixCampaignId is.
|
||||
// Ensure we override that as well.
|
||||
let oldMsixCampaignId = AttributionCode.msixCampaignId;
|
||||
AttributionCode.msixCampaignId = async () => {
|
||||
throw new Error("read_error");
|
||||
};
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
AttributionIOUtils.exists = oldExists;
|
||||
AttributionIOUtils.read = oldRead;
|
||||
AttributionCode.msixCampaignId = oldMsixCampaignId;
|
||||
});
|
||||
|
||||
// Try to read the file
|
||||
|
|
|
@ -20,6 +20,8 @@ add_task(async () => {
|
|||
* to make sure we reject bad ones and accept good ones.
|
||||
*/
|
||||
add_task(async function testValidAttrCodes() {
|
||||
let msixCampaignIdStub = sinon.stub(AttributionCode, "msixCampaignId");
|
||||
|
||||
let currentCode = null;
|
||||
for (let entry of validAttrCodes) {
|
||||
currentCode = entry.code;
|
||||
|
@ -36,14 +38,13 @@ add_task(async function testValidAttrCodes() {
|
|||
// In real life, the attribution codes returned from Microsoft APIs
|
||||
// are not URI encoded, and the AttributionCode code that deals with
|
||||
// them expects that - so we have to simulate that as well.
|
||||
sinon
|
||||
.stub(AttributionCode, "msixCampaignId")
|
||||
.get(() => decodeURIComponent(currentCode));
|
||||
msixCampaignIdStub.callsFake(async () => decodeURIComponent(currentCode));
|
||||
} else {
|
||||
await AttributionCode.writeAttributionFile(currentCode);
|
||||
}
|
||||
AttributionCode._clearCache();
|
||||
let result = await AttributionCode.getAttrDataAsync();
|
||||
|
||||
Assert.deepEqual(
|
||||
result,
|
||||
entry.parsed,
|
||||
|
@ -51,13 +52,18 @@ add_task(async function testValidAttrCodes() {
|
|||
);
|
||||
}
|
||||
AttributionCode._clearCache();
|
||||
|
||||
// Restore the msixCampaignId stub so that other tests don't fail stubbing it
|
||||
msixCampaignIdStub.restore();
|
||||
});
|
||||
|
||||
/**
|
||||
* Make sure codes with various formatting errors are not seen as valid.
|
||||
*/
|
||||
add_task(async function testInvalidAttrCodes() {
|
||||
let msixCampaignIdStub = sinon.stub(AttributionCode, "msixCampaignId");
|
||||
let currentCode = null;
|
||||
|
||||
for (let code of invalidAttrCodes) {
|
||||
currentCode = code;
|
||||
|
||||
|
@ -73,9 +79,7 @@ add_task(async function testInvalidAttrCodes() {
|
|||
continue;
|
||||
}
|
||||
|
||||
sinon
|
||||
.stub(AttributionCode, "msixCampaignId")
|
||||
.get(() => decodeURIComponent(currentCode));
|
||||
msixCampaignIdStub.callsFake(async () => decodeURIComponent(currentCode));
|
||||
} else {
|
||||
await AttributionCode.writeAttributionFile(currentCode);
|
||||
}
|
||||
|
@ -88,6 +92,9 @@ add_task(async function testInvalidAttrCodes() {
|
|||
);
|
||||
}
|
||||
AttributionCode._clearCache();
|
||||
|
||||
// Restore the msixCampaignId stub so that other tests don't fail stubbing it
|
||||
msixCampaignIdStub.restore();
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,7 +23,7 @@ interface nsIWindowsPackageManager : nsISupports
|
|||
*/
|
||||
unsigned long long getInstalledDate();
|
||||
|
||||
/* Retrieves the campaignId, if any, a user's Microsoft Store install is
|
||||
/* Asynchronously retrieves the campaignId, if any, a user's Microsoft Store install is
|
||||
* associated with. These are present if the user clicked a "ms-window-store://"
|
||||
* or "https://" link that included a "cid" query argument the very first time
|
||||
* they installed the app. (This value appears to be cached forever, so
|
||||
|
@ -37,5 +37,6 @@ interface nsIWindowsPackageManager : nsISupports
|
|||
* a non-packaged build.
|
||||
* @throw NS_ERROR_FAILURE for any other errors
|
||||
*/
|
||||
AString getCampaignId();
|
||||
[implicit_jscontext]
|
||||
Promise campaignId();
|
||||
};
|
||||
|
|
|
@ -4,10 +4,11 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsWindowsPackageManager.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/WinHeaderOnlyUtils.h"
|
||||
#include "mozilla/mscom/EnsureMTA.h"
|
||||
#ifndef __MINGW32__
|
||||
# include "nsProxyRelease.h"
|
||||
# include <comutil.h>
|
||||
# include <wrl.h>
|
||||
# include <windows.applicationmodel.store.h>
|
||||
|
@ -141,16 +142,72 @@ nsWindowsPackageManager::GetInstalledDate(uint64_t* ts) {
|
|||
#endif // __MINGW32__
|
||||
}
|
||||
|
||||
static HRESULT RejectOnMainThread(
|
||||
const char* aName, nsMainThreadPtrHandle<dom::Promise> promiseHolder,
|
||||
nsresult result) {
|
||||
DebugOnly<nsresult> rv = NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||
aName, [promiseHolder = std::move(promiseHolder), result]() {
|
||||
promiseHolder.get()->MaybeReject(result);
|
||||
}));
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed");
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
#ifndef __MINGW32__
|
||||
// forward declarations
|
||||
static void GetCampaignIdFromStoreProductOnBackgroundThread(
|
||||
ComPtr<IAsyncOperation<StoreProductResult*> > asyncSpr,
|
||||
ComPtr<IStoreContext> storeContext,
|
||||
nsMainThreadPtrHandle<dom::Promise> promiseHolder);
|
||||
static void GetCampaignIdFromLicenseOnBackgroundThread(
|
||||
ComPtr<IAsyncOperation<StoreAppLicense*> > asyncSal,
|
||||
nsMainThreadPtrHandle<dom::Promise> promiseHolder,
|
||||
nsAutoString aCampaignId);
|
||||
#endif // __MINGW32__
|
||||
|
||||
static std::tuple<nsMainThreadPtrHolder<dom::Promise>*, nsresult>
|
||||
InitializePromise(JSContext* aCx, dom::Promise** aPromise) {
|
||||
*aPromise = nullptr;
|
||||
|
||||
nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
|
||||
if (NS_WARN_IF(!global)) {
|
||||
return std::make_tuple(nullptr, NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
ErrorResult erv;
|
||||
RefPtr<dom::Promise> outer = dom::Promise::Create(global, erv);
|
||||
if (NS_WARN_IF(erv.Failed())) {
|
||||
return std::make_tuple(nullptr, erv.StealNSResult());
|
||||
}
|
||||
|
||||
auto promiseHolder = new nsMainThreadPtrHolder<dom::Promise>(
|
||||
"nsWindowsPackageManager::CampaignId Promise", outer);
|
||||
|
||||
outer.forget(aPromise);
|
||||
|
||||
return std::make_tuple(promiseHolder, NS_OK);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowsPackageManager::GetCampaignId(nsAString& aCampaignId) {
|
||||
nsWindowsPackageManager::CampaignId(JSContext* aCx, dom::Promise** aPromise) {
|
||||
NS_ENSURE_ARG_POINTER(aPromise);
|
||||
|
||||
#ifdef __MINGW32__
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
#else
|
||||
|
||||
// This is only relevant for MSIX packaged builds.
|
||||
if (!mozilla::HasPackageIdentity()) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
auto [unwrappedPromiseHolder, result] = InitializePromise(aCx, aPromise);
|
||||
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
|
||||
nsMainThreadPtrHandle<dom::Promise> promiseHolder(unwrappedPromiseHolder);
|
||||
|
||||
ComPtr<IStoreContextStatics> scStatics = nullptr;
|
||||
HRESULT hr = RoGetActivationFactory(
|
||||
HStringReference(RuntimeClass_Windows_Services_Store_StoreContext).Get(),
|
||||
|
@ -169,160 +226,236 @@ nsWindowsPackageManager::GetCampaignId(nsAString& aCampaignId) {
|
|||
* supporting that scenario.
|
||||
*
|
||||
*/
|
||||
if (!SUCCEEDED(hr) || scStatics == nullptr) return NS_ERROR_FAILURE;
|
||||
ComPtr<IStoreContext> storeContext = nullptr;
|
||||
hr = scStatics->GetDefault(&storeContext);
|
||||
if (!SUCCEEDED(hr) || storeContext == nullptr) return NS_ERROR_FAILURE;
|
||||
|
||||
ComPtr<IAsyncOperation<StoreProductResult*> > asyncSpr = nullptr;
|
||||
|
||||
{
|
||||
nsAutoHandle event(CreateEventW(nullptr, true, false, nullptr));
|
||||
bool asyncOpSucceeded = false;
|
||||
|
||||
// Despite the documentation indicating otherwise, the async operations
|
||||
// and callbacks used here don't seem to work outside of a COM MTA.
|
||||
mozilla::mscom::EnsureMTA(
|
||||
[&event, &asyncOpSucceeded, &hr, &storeContext, &asyncSpr]() -> void {
|
||||
auto callback =
|
||||
Callback<IAsyncOperationCompletedHandler<StoreProductResult*> >(
|
||||
[&asyncOpSucceeded, &event](
|
||||
IAsyncOperation<StoreProductResult*>* asyncInfo,
|
||||
AsyncStatus status) -> HRESULT {
|
||||
asyncOpSucceeded = status == AsyncStatus::Completed;
|
||||
return SetEvent(event.get());
|
||||
});
|
||||
|
||||
hr = storeContext->GetStoreProductForCurrentAppAsync(&asyncSpr);
|
||||
if (!SUCCEEDED(hr) || asyncSpr == nullptr) {
|
||||
asyncOpSucceeded = false;
|
||||
return;
|
||||
}
|
||||
hr = asyncSpr->put_Completed(callback.Get());
|
||||
if (!SUCCEEDED(hr)) {
|
||||
asyncOpSucceeded = false;
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD ret = WaitForSingleObject(event.get(), 30000);
|
||||
if (ret != WAIT_OBJECT_0) {
|
||||
asyncOpSucceeded = false;
|
||||
}
|
||||
});
|
||||
if (!asyncOpSucceeded) return NS_ERROR_FAILURE;
|
||||
if (!SUCCEEDED(hr) || scStatics == nullptr) {
|
||||
promiseHolder.get()->MaybeReject(NS_ERROR_FAILURE);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ComPtr<IStoreContext> storeContext = nullptr;
|
||||
hr = scStatics->GetDefault(&storeContext);
|
||||
if (!SUCCEEDED(hr) || storeContext == nullptr) {
|
||||
promiseHolder.get()->MaybeReject(NS_ERROR_FAILURE);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* Despite the documentation not saying otherwise, these don't work
|
||||
* consistently when called from the main thread. I tried the two scenarios
|
||||
* described above multiple times, and couldn't consistently get the campaign
|
||||
* id when running this code async on the main thread. So instead, this
|
||||
* dispatches to a background task to do the work, and then dispatches to the
|
||||
* main thread to call back into the Javascript asynchronously
|
||||
*
|
||||
*/
|
||||
result = NS_DispatchBackgroundTask(
|
||||
NS_NewRunnableFunction(
|
||||
__func__,
|
||||
[storeContext = std::move(storeContext),
|
||||
promiseHolder = std::move(promiseHolder)]() -> void {
|
||||
ComPtr<IAsyncOperation<StoreProductResult*> > asyncSpr = nullptr;
|
||||
|
||||
auto hr =
|
||||
storeContext->GetStoreProductForCurrentAppAsync(&asyncSpr);
|
||||
if (!SUCCEEDED(hr) || asyncSpr == nullptr) {
|
||||
RejectOnMainThread(__func__, std::move(promiseHolder),
|
||||
NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
auto callback =
|
||||
Callback<IAsyncOperationCompletedHandler<StoreProductResult*> >(
|
||||
[promiseHolder, asyncSpr,
|
||||
storeContext = std::move(storeContext)](
|
||||
IAsyncOperation<StoreProductResult*>* asyncInfo,
|
||||
AsyncStatus status) -> HRESULT {
|
||||
bool asyncOpSucceeded = status == AsyncStatus::Completed;
|
||||
if (!asyncOpSucceeded) {
|
||||
return RejectOnMainThread(__func__,
|
||||
std::move(promiseHolder),
|
||||
NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
GetCampaignIdFromStoreProductOnBackgroundThread(
|
||||
std::move(asyncSpr), std::move(storeContext),
|
||||
std::move(promiseHolder));
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
|
||||
hr = asyncSpr->put_Completed(callback.Get());
|
||||
if (!SUCCEEDED(hr)) {
|
||||
RejectOnMainThread(__func__, std::move(promiseHolder),
|
||||
NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
}),
|
||||
NS_DISPATCH_EVENT_MAY_BLOCK);
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
promiseHolder.get()->MaybeReject(NS_ERROR_FAILURE);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
#endif // __MINGW32__
|
||||
}
|
||||
|
||||
#ifndef __MINGW32__
|
||||
static void GetCampaignIdFromStoreProductOnBackgroundThread(
|
||||
ComPtr<IAsyncOperation<StoreProductResult*> > asyncSpr,
|
||||
ComPtr<IStoreContext> storeContext,
|
||||
nsMainThreadPtrHandle<dom::Promise> promiseHolder) {
|
||||
ComPtr<IStoreProductResult> productResult = nullptr;
|
||||
hr = asyncSpr->GetResults(&productResult);
|
||||
if (!SUCCEEDED(hr) || productResult == nullptr) return NS_ERROR_FAILURE;
|
||||
|
||||
auto hr = asyncSpr->GetResults(&productResult);
|
||||
if (!SUCCEEDED(hr) || productResult == nullptr) {
|
||||
RejectOnMainThread(__func__, std::move(promiseHolder), NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString campaignId;
|
||||
|
||||
ComPtr<IStoreProduct> product = nullptr;
|
||||
hr = productResult->get_Product(&product);
|
||||
if (!SUCCEEDED(hr) || product == nullptr) return NS_ERROR_FAILURE;
|
||||
ComPtr<Collections::IVectorView<StoreSku*> > skus = nullptr;
|
||||
hr = product->get_Skus(&skus);
|
||||
if (!SUCCEEDED(hr) || skus == nullptr) return NS_ERROR_FAILURE;
|
||||
|
||||
unsigned int size;
|
||||
hr = skus->get_Size(&size);
|
||||
if (!SUCCEEDED(hr)) return NS_ERROR_FAILURE;
|
||||
|
||||
for (unsigned int i = 0; i < size; i++) {
|
||||
ComPtr<IStoreSku> sku = nullptr;
|
||||
hr = skus->GetAt(i, &sku);
|
||||
if (!SUCCEEDED(hr) || sku == nullptr) return NS_ERROR_FAILURE;
|
||||
|
||||
boolean isInUserCollection = false;
|
||||
hr = sku->get_IsInUserCollection(&isInUserCollection);
|
||||
if (!SUCCEEDED(hr) || !isInUserCollection) continue;
|
||||
|
||||
ComPtr<IStoreCollectionData> scd = nullptr;
|
||||
hr = sku->get_CollectionData(&scd);
|
||||
if (!SUCCEEDED(hr) || scd == nullptr) continue;
|
||||
|
||||
HString campaignId;
|
||||
hr = scd->get_CampaignId(campaignId.GetAddressOf());
|
||||
if (!SUCCEEDED(hr)) continue;
|
||||
|
||||
unsigned int tmp;
|
||||
aCampaignId.Assign(campaignId.GetRawBuffer(&tmp));
|
||||
if (aCampaignId.Length() > 0) {
|
||||
aCampaignId.AppendLiteral("&msstoresignedin=true");
|
||||
if (SUCCEEDED(hr) && (product != nullptr)) {
|
||||
ComPtr<Collections::IVectorView<StoreSku*> > skus = nullptr;
|
||||
hr = product->get_Skus(&skus);
|
||||
if (!SUCCEEDED(hr) || skus == nullptr) {
|
||||
RejectOnMainThread(__func__, std::move(promiseHolder), NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int size;
|
||||
hr = skus->get_Size(&size);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
RejectOnMainThread(__func__, std::move(promiseHolder), NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < size; i++) {
|
||||
ComPtr<IStoreSku> sku = nullptr;
|
||||
hr = skus->GetAt(i, &sku);
|
||||
if (!SUCCEEDED(hr) || sku == nullptr) {
|
||||
RejectOnMainThread(__func__, std::move(promiseHolder),
|
||||
NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isInUserCollection = false;
|
||||
hr = sku->get_IsInUserCollection(&isInUserCollection);
|
||||
if (!SUCCEEDED(hr) || !isInUserCollection) continue;
|
||||
|
||||
ComPtr<IStoreCollectionData> scd = nullptr;
|
||||
hr = sku->get_CollectionData(&scd);
|
||||
if (!SUCCEEDED(hr) || scd == nullptr) continue;
|
||||
|
||||
HString msCampaignId;
|
||||
hr = scd->get_CampaignId(msCampaignId.GetAddressOf());
|
||||
if (!SUCCEEDED(hr)) continue;
|
||||
|
||||
unsigned int tmp;
|
||||
campaignId.Assign(msCampaignId.GetRawBuffer(&tmp));
|
||||
if (campaignId.Length() > 0) {
|
||||
campaignId.AppendLiteral("&msstoresignedin=true");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!campaignId.IsEmpty()) {
|
||||
// If we got here, it means that campaignId has been processed and can be
|
||||
// sent back via the promise
|
||||
DebugOnly<nsresult> rv = NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||
__func__, [promiseHolder = std::move(promiseHolder),
|
||||
campaignId = std::move(campaignId)]() {
|
||||
promiseHolder.get()->MaybeResolve(campaignId);
|
||||
}));
|
||||
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// There's various points above that could exit without a failure.
|
||||
// If we get here without a campaignId we may as well just check
|
||||
// the AppStoreLicense.
|
||||
if (aCampaignId.IsEmpty()) {
|
||||
ComPtr<IAsyncOperation<StoreAppLicense*> > asyncSal = nullptr;
|
||||
bool asyncOpSucceeded = false;
|
||||
nsAutoHandle event(CreateEventW(nullptr, true, false, nullptr));
|
||||
mozilla::mscom::EnsureMTA(
|
||||
[&event, &asyncOpSucceeded, &hr, &storeContext, &asyncSal]() -> void {
|
||||
auto callback =
|
||||
Callback<IAsyncOperationCompletedHandler<StoreAppLicense*> >(
|
||||
[&asyncOpSucceeded, &event](
|
||||
IAsyncOperation<StoreAppLicense*>* asyncInfo,
|
||||
AsyncStatus status) -> HRESULT {
|
||||
asyncOpSucceeded = status == AsyncStatus::Completed;
|
||||
return SetEvent(event.get());
|
||||
});
|
||||
ComPtr<IAsyncOperation<StoreAppLicense*> > asyncSal = nullptr;
|
||||
|
||||
hr = storeContext->GetAppLicenseAsync(&asyncSal);
|
||||
if (!SUCCEEDED(hr) || asyncSal == nullptr) {
|
||||
asyncOpSucceeded = false;
|
||||
return;
|
||||
}
|
||||
hr = asyncSal->put_Completed(callback.Get());
|
||||
if (!SUCCEEDED(hr)) {
|
||||
asyncOpSucceeded = false;
|
||||
return;
|
||||
}
|
||||
hr = storeContext->GetAppLicenseAsync(&asyncSal);
|
||||
if (!SUCCEEDED(hr) || asyncSal == nullptr) {
|
||||
RejectOnMainThread(__func__, std::move(promiseHolder), NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD ret = WaitForSingleObject(event.get(), 30000);
|
||||
if (ret != WAIT_OBJECT_0) {
|
||||
asyncOpSucceeded = false;
|
||||
}
|
||||
});
|
||||
if (!asyncOpSucceeded) return NS_ERROR_FAILURE;
|
||||
auto callback = Callback<IAsyncOperationCompletedHandler<StoreAppLicense*> >(
|
||||
[asyncSal, promiseHolder, campaignId = std::move(campaignId)](
|
||||
IAsyncOperation<StoreAppLicense*>* asyncInfo,
|
||||
AsyncStatus status) -> HRESULT {
|
||||
bool asyncOpSucceeded = status == AsyncStatus::Completed;
|
||||
if (!asyncOpSucceeded) {
|
||||
return RejectOnMainThread(__func__, std::move(promiseHolder),
|
||||
NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
ComPtr<IStoreAppLicense> license = nullptr;
|
||||
hr = asyncSal->GetResults(&license);
|
||||
if (!SUCCEEDED(hr) || license == nullptr) return NS_ERROR_FAILURE;
|
||||
GetCampaignIdFromLicenseOnBackgroundThread(std::move(asyncSal),
|
||||
std::move(promiseHolder),
|
||||
std::move(campaignId));
|
||||
|
||||
HString extendedData;
|
||||
hr = license->get_ExtendedJsonData(extendedData.GetAddressOf());
|
||||
if (!SUCCEEDED(hr)) return NS_ERROR_FAILURE;
|
||||
return S_OK;
|
||||
});
|
||||
|
||||
Json::Value jsonData;
|
||||
Json::Reader jsonReader;
|
||||
hr = asyncSal->put_Completed(callback.Get());
|
||||
if (!SUCCEEDED(hr)) {
|
||||
RejectOnMainThread(__func__, std::move(promiseHolder), NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int tmp;
|
||||
nsAutoString key(extendedData.GetRawBuffer(&tmp));
|
||||
if (!jsonReader.parse(NS_ConvertUTF16toUTF8(key).get(), jsonData, false)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
static void GetCampaignIdFromLicenseOnBackgroundThread(
|
||||
ComPtr<IAsyncOperation<StoreAppLicense*> > asyncSal,
|
||||
nsMainThreadPtrHandle<dom::Promise> promiseHolder,
|
||||
nsAutoString aCampaignId) {
|
||||
ComPtr<IStoreAppLicense> license = nullptr;
|
||||
auto hr = asyncSal->GetResults(&license);
|
||||
if (!SUCCEEDED(hr) || license == nullptr) {
|
||||
RejectOnMainThread(__func__, std::move(promiseHolder), NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (jsonData.isMember(CAMPAIGN_ID_JSON_FIELD_NAME) &&
|
||||
jsonData[CAMPAIGN_ID_JSON_FIELD_NAME].isString()) {
|
||||
aCampaignId.Assign(
|
||||
NS_ConvertUTF8toUTF16(
|
||||
jsonData[CAMPAIGN_ID_JSON_FIELD_NAME].asString().c_str())
|
||||
.get());
|
||||
if (aCampaignId.Length() > 0) {
|
||||
aCampaignId.AppendLiteral("&msstoresignedin=false");
|
||||
}
|
||||
HString extendedData;
|
||||
hr = license->get_ExtendedJsonData(extendedData.GetAddressOf());
|
||||
if (!SUCCEEDED(hr)) {
|
||||
RejectOnMainThread(__func__, std::move(promiseHolder), NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
Json::Value jsonData;
|
||||
Json::Reader jsonReader;
|
||||
|
||||
unsigned int tmp;
|
||||
nsAutoString key(extendedData.GetRawBuffer(&tmp));
|
||||
if (!jsonReader.parse(NS_ConvertUTF16toUTF8(key).get(), jsonData, false)) {
|
||||
RejectOnMainThread(__func__, std::move(promiseHolder), NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (jsonData.isMember(CAMPAIGN_ID_JSON_FIELD_NAME) &&
|
||||
jsonData[CAMPAIGN_ID_JSON_FIELD_NAME].isString()) {
|
||||
aCampaignId.Assign(
|
||||
NS_ConvertUTF8toUTF16(
|
||||
jsonData[CAMPAIGN_ID_JSON_FIELD_NAME].asString().c_str())
|
||||
.get());
|
||||
if (aCampaignId.Length() > 0) {
|
||||
aCampaignId.AppendLiteral("&msstoresignedin=false");
|
||||
}
|
||||
}
|
||||
|
||||
// No matter what happens in either block above, if they don't exit with a
|
||||
// failure we managed to successfully pull the campaignId from somewhere
|
||||
// (even if its empty).
|
||||
return NS_OK;
|
||||
|
||||
#endif // __MINGW32__
|
||||
DebugOnly<nsresult> rv = NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||
__func__, [promiseHolder = std::move(promiseHolder),
|
||||
aCampaignId = std::move(aCampaignId)]() {
|
||||
promiseHolder.get()->MaybeResolve(aCampaignId);
|
||||
}));
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed");
|
||||
}
|
||||
#endif // __MINGW32__
|
||||
|
||||
} // namespace system
|
||||
} // namespace toolkit
|
||||
|
|
Загрузка…
Ссылка в новой задаче