From d7567404262ce1c468e2bdf5ba9ba07b98a48fda Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Tue, 12 Dec 2023 16:30:04 +0000 Subject: [PATCH] Bug 1811076: Part 6 - Show a cancelable "slow agent" dialog on slow content analysis r=nika,Gijs,rkraesig,fluent-reviewers,bolsson This adds a modal dialog to some content analysis use cases that blocks input while CA is running. The dialog can be canceled, which cancels the CA (if possible) and the operation that required it. Differential Revision: https://phabricator.services.mozilla.com/D189578 UltraBlame original commit: 7ca7d7ab66453b81c86545a277ab0fdde9f19171 --- .../content/ContentAnalysis.sys.mjs | 16 +- netwerk/base/nsIPrompt.idl | 2 + .../contentanalysis/ContentAnalysis.cpp | 396 +++++++++++++----- .../contentanalysis/ContentAnalysis.h | 46 +- .../contentanalysis/nsIContentAnalysis.idl | 9 + .../prompts/content/commonDialog.css | 9 + .../prompts/content/commonDialog.js | 1 + .../prompts/content/commonDialog.xhtml | 10 + .../prompts/src/CommonDialog.sys.mjs | 4 + .../components/prompts/src/Prompter.sys.mjs | 4 + .../windowwatcher/nsIPromptService.idl | 5 + .../en-US/toolkit/global/commonDialog.ftl | 4 + 12 files changed, 396 insertions(+), 110 deletions(-) diff --git a/browser/components/contentanalysis/content/ContentAnalysis.sys.mjs b/browser/components/contentanalysis/content/ContentAnalysis.sys.mjs index 85cfb7b86156..b56766ac77fa 100644 --- a/browser/components/contentanalysis/content/ContentAnalysis.sys.mjs +++ b/browser/components/contentanalysis/content/ContentAnalysis.sys.mjs @@ -16,6 +16,13 @@ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; const lazy = {}; +XPCOMUtils.defineLazyServiceGetter( + lazy, + "gContentAnalysis", + "@mozilla.org/contentanalysis;1", + Ci.nsIContentAnalysis +); + ChromeUtils.defineESModuleGetters(lazy, { clearTimeout: "resource://gre/modules/Timer.sys.mjs", setTimeout: "resource://gre/modules/Timer.sys.mjs", @@ -387,7 +394,10 @@ export const ContentAnalysis = { }, _shouldShowBlockingNotification(aOperation) { - return false; + return !( + aOperation == Ci.nsIContentAnalysisRequest.FILE_DOWNLOADED || + aOperation == Ci.nsIContentAnalysisRequest.PRINT + ); }, // This function also transforms the nameOrL10NId so we won't have to @@ -486,7 +496,8 @@ export const ContentAnalysis = { content: this._getResourceNameFromNameOrL10NId(aResourceNameOrL10NId), }), Ci.nsIPromptService.BUTTON_POS_0 * - Ci.nsIPromptService.BUTTON_TITLE_CANCEL, + Ci.nsIPromptService.BUTTON_TITLE_CANCEL + + Ci.nsIPromptService.SHOW_SPINNER, null, null, null, @@ -504,6 +515,7 @@ export const ContentAnalysis = { // This is also be called if the tab/window is closed while a request is in progress, // in which case we need to cancel the request. if (this.requestTokenToRequestInfo.delete(aRequestToken)) { + lazy.gContentAnalysis.cancelContentAnalysisRequest(aRequestToken); let dlpBusyView = this.dlpBusyViewsByTopBrowsingContext.getEntry(aBrowsingContext); if (dlpBusyView) { diff --git a/netwerk/base/nsIPrompt.idl b/netwerk/base/nsIPrompt.idl index dde9d70a7446..75afd6108e02 100644 --- a/netwerk/base/nsIPrompt.idl +++ b/netwerk/base/nsIPrompt.idl @@ -56,6 +56,8 @@ interface nsIPrompt : nsISupports const unsigned long BUTTON_DELAY_ENABLE = 1 << 26; + const unsigned long SHOW_SPINNER = 1 << 27; + const unsigned long STD_OK_CANCEL_BUTTONS = (BUTTON_TITLE_OK * BUTTON_POS_0) + (BUTTON_TITLE_CANCEL * BUTTON_POS_1); const unsigned long STD_YES_NO_BUTTONS = (BUTTON_TITLE_YES * BUTTON_POS_0) + diff --git a/toolkit/components/contentanalysis/ContentAnalysis.cpp b/toolkit/components/contentanalysis/ContentAnalysis.cpp index 9f931ffb6e42..0fc5e9da6e60 100644 --- a/toolkit/components/contentanalysis/ContentAnalysis.cpp +++ b/toolkit/components/contentanalysis/ContentAnalysis.cpp @@ -9,6 +9,7 @@ #include "content_analysis/sdk/analysis_client.h" #include "base/process_util.h" +#include "mozilla/Components.h" #include "mozilla/dom/Promise.h" #include "mozilla/Logging.h" #include "mozilla/ScopeExit.h" @@ -32,6 +33,19 @@ # include #endif +namespace mozilla::contentanalysis { + +LazyLogModule gContentAnalysisLog("contentanalysis"); +#define LOGD(...) \ + MOZ_LOG(mozilla::contentanalysis::gContentAnalysisLog, \ + mozilla::LogLevel::Debug, (__VA_ARGS__)) + +#define LOGE(...) \ + MOZ_LOG(mozilla::contentanalysis::gContentAnalysisLog, \ + mozilla::LogLevel::Error, (__VA_ARGS__)) + +} + namespace { const char* kIsDLPEnabledPref = "browser.contentanalysis.enabled"; @@ -72,9 +86,6 @@ static nsresult GetFileDisplayName(const nsString& aFilePath, namespace mozilla::contentanalysis { -LazyLogModule gContentAnalysisLog("contentanalysis"); -#define LOGD(...) MOZ_LOG(gContentAnalysisLog, LogLevel::Debug, (__VA_ARGS__)) - NS_IMETHODIMP ContentAnalysisRequest::GetAnalysisType(uint32_t* aAnalysisType) { *aAnalysisType = mAnalysisType; @@ -144,25 +155,19 @@ ContentAnalysisRequest::GetWindowGlobalParent( return NS_OK; } +nsresult ContentAnalysis::CreateContentAnalysisClient(nsCString&& aPipePathName, + bool aIsPerUser) { + MOZ_ASSERT(!NS_IsMainThread()); + + MOZ_ASSERT(!mCaClientPromise->IsResolved()); -StaticDataMutex> - ContentAnalysis::sCaClient("ContentAnalysisClient"); - -nsresult ContentAnalysis::EnsureContentAnalysisClient() { - auto caClientRef = sCaClient.Lock(); - auto& caClient = caClientRef.ref(); - if (caClient) { - return NS_OK; - } - - nsAutoCString pipePathName; - Preferences::GetCString(kPipePathNamePref, pipePathName); - caClient.reset( - content_analysis::sdk::Client::Create( - {pipePathName.Data(), Preferences::GetBool(kIsPerUserPref)}) + std::shared_ptr client( + content_analysis::sdk::Client::Create({aPipePathName.Data(), aIsPerUser}) .release()); - LOGD("Content analysis is %s", caClient ? "connected" : "not available"); - return caClient ? NS_OK : NS_ERROR_NOT_AVAILABLE; + LOGD("Content analysis is %s", client ? "connected" : "not available"); + mCaClientPromise->Resolve(client, __func__); + + return NS_OK; } ContentAnalysisRequest::ContentAnalysisRequest( @@ -562,26 +567,61 @@ NS_IMPL_CLASSINFO(ContentAnalysisResponse, nullptr, 0, {0}); NS_IMPL_ISUPPORTS_CI(ContentAnalysisResponse, nsIContentAnalysisResponse); NS_IMPL_ISUPPORTS(ContentAnalysisCallback, nsIContentAnalysisCallback); NS_IMPL_ISUPPORTS(ContentAnalysisResult, nsIContentAnalysisResult); -NS_IMPL_ISUPPORTS(ContentAnalysis, nsIContentAnalysis); +NS_IMPL_ISUPPORTS(ContentAnalysis, nsIContentAnalysis, ContentAnalysis); + +ContentAnalysis::ContentAnalysis() + : mCaClientPromise( + new ClientPromise::Private("ContentAnalysis::ContentAnalysis")), + mClientCreationAttempted(false), + mCallbackMap("ContentAnalysis::mCallbackMap") {} ContentAnalysis::~ContentAnalysis() { - auto caClientRef = sCaClient.Lock(); - auto& caClient = caClientRef.ref(); - caClient = nullptr; + + MOZ_ASSERT(NS_IsMainThread()); + if (!mClientCreationAttempted) { + + mCaClientPromise->Reject(NS_ERROR_ILLEGAL_DURING_SHUTDOWN, __func__); + } } NS_IMETHODIMP ContentAnalysis::GetIsActive(bool* aIsActive) { *aIsActive = false; + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess()); if (!gAllowContentAnalysis || !Preferences::GetBool(kIsDLPEnabledPref)) { + LOGD("Local DLP Content Analysis is not active"); return NS_OK; } - - nsresult rv = EnsureContentAnalysisClient(); - *aIsActive = NS_SUCCEEDED(rv); - LOGD("Local DLP Content Analysis is %sactive", *aIsActive ? "" : "not "); + *aIsActive = true; + LOGD("Local DLP Content Analysis is active"); + + + if (!mClientCreationAttempted) { + mClientCreationAttempted = true; + LOGD("Dispatching background task to create Content Analysis client"); + + nsCString pipePathName; + nsresult rv = Preferences::GetCString(kPipePathNamePref, pipePathName); + if (NS_WARN_IF(NS_FAILED(rv))) { + mCaClientPromise->Reject(rv, __func__); + return rv; + } + bool isPerUser = Preferences::GetBool(kIsPerUserPref); + rv = NS_DispatchBackgroundTask(NS_NewCancelableRunnableFunction( + "ContentAnalysis::CreateContentAnalysisClient", + [owner = RefPtr{this}, pipePathName = std::move(pipePathName), + isPerUser]() mutable { + owner->CreateContentAnalysisClient(std::move(pipePathName), + isPerUser); + })); + if (NS_WARN_IF(NS_FAILED(rv))) { + mCaClientPromise->Reject(rv, __func__); + return rv; + } + } return NS_OK; } @@ -596,6 +636,41 @@ ContentAnalysis::GetMightBeActive(bool* aMightBeActive) { return NS_OK; } +nsresult ContentAnalysis::CancelWithError(nsCString aRequestToken, + nsresult aResult) { + return NS_DispatchToMainThread(NS_NewCancelableRunnableFunction( + "ContentAnalysis::RunAnalyzeRequestTask::HandleResponse", + [aResult, aRequestToken = std::move(aRequestToken)] { + RefPtr owner = GetContentAnalysisFromService(); + if (!owner) { + + return; + } + nsMainThreadPtrHandle callbackHolder; + { + auto lock = owner->mCallbackMap.Lock(); + auto callbackData = lock->Extract(aRequestToken); + if (callbackData.isSome()) { + callbackHolder = callbackData->TakeCallbackHolder(); + } + } + if (callbackHolder) { + callbackHolder->Error(aResult); + } + })); +} + +RefPtr ContentAnalysis::GetContentAnalysisFromService() { + RefPtr contentAnalysisService = + mozilla::components::nsIContentAnalysis::Service(); + if (!contentAnalysisService) { + + return nullptr; + } + + return contentAnalysisService; +} + nsresult ContentAnalysis::RunAnalyzeRequestTask( RefPtr aRequest, RefPtr aCallback) { @@ -603,15 +678,11 @@ nsresult ContentAnalysis::RunAnalyzeRequestTask( auto callbackCopy = aCallback; auto se = MakeScopeExit([&] { if (!NS_SUCCEEDED(rv)) { - LOGD("RunAnalyzeRequestTask failed"); + LOGE("RunAnalyzeRequestTask failed"); callbackCopy->Error(rv); } }); - - - RefPtr owner = this; - content_analysis::sdk::ContentAnalysisRequest pbRequest; rv = ConvertToProtobuf(aRequest, &pbRequest); NS_ENSURE_SUCCESS(rv, rv); @@ -620,69 +691,142 @@ nsresult ContentAnalysis::RunAnalyzeRequestTask( LogRequest(&pbRequest); nsCString requestToken; + nsMainThreadPtrHandle callbackHolderCopy( + new nsMainThreadPtrHolder( + "content analysis callback", aCallback)); + CallbackData callbackData(std::move(callbackHolderCopy)); rv = aRequest->GetRequestToken(requestToken); NS_ENSURE_SUCCESS(rv, rv); + { + auto lock = mCallbackMap.Lock(); + lock->InsertOrUpdate(requestToken, std::move(callbackData)); + } - - rv = NS_DispatchBackgroundTask( - NS_NewRunnableFunction( - "RunAnalyzeRequestTask", - [pbRequest = std::move(pbRequest), aCallback = std::move(aCallback), - requestToken = std::move(requestToken), owner] { - nsresult rv = NS_ERROR_FAILURE; - content_analysis::sdk::ContentAnalysisResponse pbResponse; - - auto resolveOnMainThread = MakeScopeExit([&] { - NS_DispatchToMainThread(NS_NewRunnableFunction( - "ResolveOnMainThread", - [rv, owner, aCallback = std::move(aCallback), - pbResponse = std::move(pbResponse), requestToken]() mutable { - if (NS_SUCCEEDED(rv)) { + LOGD("Issuing ContentAnalysisRequest for token %s", requestToken.get()); + LogRequest(&pbRequest); + + mCaClientPromise->Then( + GetCurrentSerialEventTarget(), __func__, + [requestToken, pbRequest = std::move(pbRequest)]( + std::shared_ptr client) { + + NS_DispatchBackgroundTask( + NS_NewCancelableRunnableFunction( + __func__, + [requestToken, pbRequest = std::move(pbRequest), client] { + RefPtr owner = + GetContentAnalysisFromService(); + if (!owner) { + + return; + } + + if (!client) { + owner->CancelWithError(std::move(requestToken), + NS_ERROR_NOT_AVAILABLE); + return; + } + { + auto callbackMap = owner->mCallbackMap.Lock(); + if (!callbackMap->Contains(requestToken)) { LOGD( - "Content analysis resolving response promise for " - "token %s", + "RunAnalyzeRequestTask token %s has already been " + "cancelled - not issuing request", requestToken.get()); - RefPtr response = - ContentAnalysisResponse::FromProtobuf( - std::move(pbResponse)); - if (response) { + return; + } + } + + + + content_analysis::sdk::ContentAnalysisResponse pbResponse; + int err = client->Send(pbRequest, &pbResponse); + if (err != 0) { + LOGE("RunAnalyzeRequestTask client transaction failed"); + owner->CancelWithError(std::move(requestToken), + NS_ERROR_FAILURE); + return; + } + LOGD("Content analysis client transaction succeeded"); + LogResponse(&pbResponse); + NS_DispatchToMainThread(NS_NewCancelableRunnableFunction( + "ContentAnalysis::RunAnalyzeRequestTask::HandleResponse", + [pbResponse = std::move(pbResponse)]() mutable { + RefPtr owner = + GetContentAnalysisFromService(); + if (!owner) { + + return; + } + + RefPtr response = + ContentAnalysisResponse::FromProtobuf( + std::move(pbResponse)); + if (!response) { + LOGE("Content analysis got invalid response!"); + return; + } + nsCString responseRequestToken; + nsresult requestRv = + response->GetRequestToken(responseRequestToken); + if (NS_FAILED(requestRv)) { + LOGE( + "Content analysis couldn't get request token " + "from response!"); + return; + } + + Maybe maybeCallbackData; + { + auto callbackMap = owner->mCallbackMap.Lock(); + maybeCallbackData = + callbackMap->Extract(responseRequestToken); + } + if (maybeCallbackData.isNothing()) { + LOGD( + "Content analysis did not find callback for " + "token %s", + responseRequestToken.get()); + return; + } response->SetOwner(owner); + if (maybeCallbackData->Canceled()) { + + + LOGD( + "Content analysis got response but ignoring " + "because it was already cancelled for token %s", + responseRequestToken.get()); + return; + } + + LOGD( + "Content analysis resolving response promise for " + "token %s", + responseRequestToken.get()); nsCOMPtr obsServ = mozilla::services::GetObserverService(); + obsServ->NotifyObservers(response, "dlp-response", nullptr); - aCallback->ContentResult(response); - } else { - aCallback->Error(NS_ERROR_FAILURE); - } - } else { - aCallback->Error(rv); - } - })); - }); - - auto caClientRef = sCaClient.Lock(); - auto& caClient = caClientRef.ref(); - if (!caClient) { - LOGD("RunAnalyzeRequestTask failed to get client"); - rv = NS_ERROR_NOT_AVAILABLE; - return; - } - - - - int err = caClient->Send(pbRequest, &pbResponse); - if (err != 0) { - LOGD("RunAnalyzeRequestTask client transaction failed"); - rv = NS_ERROR_FAILURE; - return; - } - - LOGD("Content analysis client transaction succeeded"); - LogResponse(&pbResponse); - rv = NS_OK; - }), - NS_DISPATCH_EVENT_MAY_BLOCK); + + nsMainThreadPtrHandle + callbackHolder = + maybeCallbackData->TakeCallbackHolder(); + callbackHolder->ContentResult(response); + })); + }), + NS_DISPATCH_EVENT_MAY_BLOCK); + }, + [requestToken](nsresult rv) { + LOGD("RunAnalyzeRequestTask failed to get client"); + RefPtr owner = GetContentAnalysisFromService(); + if (!owner) { + + return; + } + owner->CancelWithError(std::move(requestToken), rv); + }); return rv; } @@ -722,6 +866,37 @@ ContentAnalysis::AnalyzeContentRequestCallback( return rv; } +NS_IMETHODIMP +ContentAnalysis::CancelContentAnalysisRequest(const nsACString& aRequestToken) { + NS_DispatchToMainThread(NS_NewCancelableRunnableFunction( + "CancelContentAnalysisRequest", + [requestToken = nsCString{aRequestToken}]() { + RefPtr self = GetContentAnalysisFromService(); + if (!self) { + + return; + } + + auto callbackMap = self->mCallbackMap.Lock(); + auto entry = callbackMap->Lookup(requestToken); + LOGD("Content analysis cancelling request %s", requestToken.get()); + if (entry) { + entry->SetCanceled(); + RefPtr cancelResponse = + ContentAnalysisResponse::FromAction( + nsIContentAnalysisResponse::CANCELED, requestToken); + cancelResponse->SetOwner(self); + nsMainThreadPtrHandle callbackHolder = + entry->TakeCallbackHolder(); + callbackHolder->ContentResult(cancelResponse.get()); + } else { + LOGD("Content analysis request not found when trying to cancel %s", + requestToken.get()); + } + })); + return NS_OK; +} + NS_IMETHODIMP ContentAnalysisResponse::Acknowledge( nsIContentAnalysisAcknowledgement* aAcknowledgement) { @@ -747,24 +922,36 @@ nsresult ContentAnalysis::RunAcknowledgeTask( LogAcknowledgement(&pbAck); - - RefPtr owner = this; - - LOGD("RunAcknowledgeTask dispatching acknowledge task"); - return NS_DispatchBackgroundTask(NS_NewRunnableFunction( - "RunAcknowledgeTask", [owner, pbAck = std::move(pbAck)] { - auto caClientRef = sCaClient.Lock(); - auto& caClient = caClientRef.ref(); - if (!caClient) { - LOGD("RunAcknowledgeTask failed to get the client"); - return; - } - - DebugOnly err = caClient->Acknowledge(pbAck); - MOZ_ASSERT(err == 0); - LOGD("RunAcknowledgeTask sent transaction acknowledgement"); - })); + mCaClientPromise->Then( + GetCurrentSerialEventTarget(), __func__, + [pbAck = std::move(pbAck)]( + std::shared_ptr client) { + NS_DispatchBackgroundTask( + NS_NewCancelableRunnableFunction( + __func__, + [pbAck = std::move(pbAck), client] { + RefPtr owner = + GetContentAnalysisFromService(); + if (!owner) { + + return; + } + if (!client) { + return; + } + + int err = client->Acknowledge(pbAck); + MOZ_ASSERT(err == 0); + LOGD( + "RunAcknowledgeTask sent transaction acknowledgement, " + "err=%d", + err); + }), + NS_DISPATCH_EVENT_MAY_BLOCK); + }, + [](nsresult rv) { LOGD("RunAcknowledgeTask failed to get the client"); }); + return rv; } NS_IMETHODIMP ContentAnalysisCallback::ContentResult( @@ -789,4 +976,7 @@ NS_IMETHODIMP ContentAnalysisCallback::Error(nsresult aError) { ContentAnalysisCallback::ContentAnalysisCallback(RefPtr aPromise) : mPromise(Some(new nsMainThreadPtrHolder( "content analysis promise", aPromise))) {} + +#undef LOGD +#undef LOGE } diff --git a/toolkit/components/contentanalysis/ContentAnalysis.h b/toolkit/components/contentanalysis/ContentAnalysis.h index 0dbd8bf7b035..7daca3723481 100644 --- a/toolkit/components/contentanalysis/ContentAnalysis.h +++ b/toolkit/components/contentanalysis/ContentAnalysis.h @@ -8,11 +8,12 @@ #include "mozilla/DataMutex.h" #include "mozilla/dom/WindowGlobalParent.h" -#include "mozilla/Mutex.h" #include "nsIContentAnalysis.h" #include "nsProxyRelease.h" #include "nsString.h" +#include "nsTHashMap.h" +#include #include namespace content_analysis::sdk { @@ -73,30 +74,65 @@ class ContentAnalysisRequest final : public nsIContentAnalysisRequest { RefPtr mWindowGlobalParent; }; +#define CONTENTANALYSIS_IID \ + { \ + 0xa37bed74, 0x4b50, 0x443a, { \ + 0xbf, 0x58, 0xf4, 0xeb, 0xbd, 0x30, 0x67, 0xb4 \ + } \ + } + class ContentAnalysisResponse; class ContentAnalysis final : public nsIContentAnalysis { public: + NS_DECLARE_STATIC_IID_ACCESSOR(CONTENTANALYSIS_IID) NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSICONTENTANALYSIS - ContentAnalysis() = default; + ContentAnalysis(); private: ~ContentAnalysis(); ContentAnalysis(const ContentAnalysis&) = delete; ContentAnalysis& operator=(ContentAnalysis&) = delete; - nsresult EnsureContentAnalysisClient(); + nsresult CreateContentAnalysisClient(nsCString&& aPipePathName, + bool aIsPerUser); nsresult RunAnalyzeRequestTask(RefPtr aRequest, RefPtr aCallback); nsresult RunAcknowledgeTask( nsIContentAnalysisAcknowledgement* aAcknowledgement, const nsACString& aRequestToken); - - static StaticDataMutex> sCaClient; + nsresult CancelWithError(nsCString aRequestToken, nsresult aResult); + static RefPtr GetContentAnalysisFromService(); + + using ClientPromise = + MozPromise, nsresult, + false>; + RefPtr mCaClientPromise; + + bool mClientCreationAttempted; + + class CallbackData { + public: + explicit CallbackData( + nsMainThreadPtrHandle&& aCallbackHolder) + : mCallbackHolder(aCallbackHolder) {} + + nsMainThreadPtrHandle TakeCallbackHolder() { + return std::move(mCallbackHolder); + } + void SetCanceled() { mCallbackHolder = nullptr; } + bool Canceled() const { return !mCallbackHolder; } + + private: + nsMainThreadPtrHandle mCallbackHolder; + }; + DataMutex> mCallbackMap; friend class ContentAnalysisResponse; }; +NS_DEFINE_STATIC_IID_ACCESSOR(ContentAnalysis, CONTENTANALYSIS_IID) + class ContentAnalysisResponse final : public nsIContentAnalysisResponse { public: NS_DECL_ISUPPORTS diff --git a/toolkit/components/contentanalysis/nsIContentAnalysis.idl b/toolkit/components/contentanalysis/nsIContentAnalysis.idl index 2d145eceba06..7f70d6a3d0f3 100644 --- a/toolkit/components/contentanalysis/nsIContentAnalysis.idl +++ b/toolkit/components/contentanalysis/nsIContentAnalysis.idl @@ -46,6 +46,7 @@ interface nsIContentAnalysisResponse : nsISupports const unsigned long ALLOW = 1000; + const unsigned long CANCELED = 1001; [infallible] readonly attribute unsigned long action; [infallible] readonly attribute boolean shouldAllowContent; @@ -162,6 +163,7 @@ interface nsIContentAnalysis : nsISupports + readonly attribute bool isActive; @@ -194,4 +196,11 @@ interface nsIContentAnalysis : nsISupports void analyzeContentRequestCallback(in nsIContentAnalysisRequest aCar, in nsIContentAnalysisCallback callback); + + + + + + + void cancelContentAnalysisRequest(in ACString aRequestToken); }; diff --git a/toolkit/components/prompts/content/commonDialog.css b/toolkit/components/prompts/content/commonDialog.css index 3521af13c6ed..ac01353aae42 100644 --- a/toolkit/components/prompts/content/commonDialog.css +++ b/toolkit/components/prompts/content/commonDialog.css @@ -58,6 +58,15 @@ dialog[insecureauth] { flex: 1; } +#spinnerContainer { + align-items: center; +} + +#spinnerContainer > img { + width: 16px; + height: 16px; +} + #loginLabel, #password1Label { text-align: start; } diff --git a/toolkit/components/prompts/content/commonDialog.js b/toolkit/components/prompts/content/commonDialog.js index 6afc3b87662d..83ea0ca89800 100644 --- a/toolkit/components/prompts/content/commonDialog.js +++ b/toolkit/components/prompts/content/commonDialog.js @@ -83,6 +83,7 @@ function commonDialogOnLoad() { infoBody: document.getElementById("infoBody"), infoTitle: document.getElementById("infoTitle"), infoIcon: document.getElementById("infoIcon"), + spinnerContainer: document.getElementById("spinnerContainer"), checkbox: document.getElementById("checkbox"), checkboxContainer: document.getElementById("checkboxContainer"), button3: dialog.getButton("extra2"), diff --git a/toolkit/components/prompts/content/commonDialog.xhtml b/toolkit/components/prompts/content/commonDialog.xhtml index 83cc37d9edb7..def3b93956ea 100644 --- a/toolkit/components/prompts/content/commonDialog.xhtml +++ b/toolkit/components/prompts/content/commonDialog.xhtml @@ -83,6 +83,16 @@ /> +