summaryrefslogtreecommitdiffstats
path: root/toolkit/components/contentanalysis/ContentAnalysis.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:34:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:34:50 +0000
commitdef92d1b8e9d373e2f6f27c366d578d97d8960c6 (patch)
tree2ef34b9ad8bb9a9220e05d60352558b15f513894 /toolkit/components/contentanalysis/ContentAnalysis.cpp
parentAdding debian version 125.0.3-1. (diff)
downloadfirefox-def92d1b8e9d373e2f6f27c366d578d97d8960c6.tar.xz
firefox-def92d1b8e9d373e2f6f27c366d578d97d8960c6.zip
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/contentanalysis/ContentAnalysis.cpp')
-rw-r--r--toolkit/components/contentanalysis/ContentAnalysis.cpp466
1 files changed, 433 insertions, 33 deletions
diff --git a/toolkit/components/contentanalysis/ContentAnalysis.cpp b/toolkit/components/contentanalysis/ContentAnalysis.cpp
index 1c71f5d986..2977072984 100644
--- a/toolkit/components/contentanalysis/ContentAnalysis.cpp
+++ b/toolkit/components/contentanalysis/ContentAnalysis.cpp
@@ -11,6 +11,7 @@
#include "base/process_util.h"
#include "GMPUtils.h" // ToHexString
#include "mozilla/Components.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/Logging.h"
@@ -24,6 +25,9 @@
#include "nsIFile.h"
#include "nsIGlobalObject.h"
#include "nsIObserverService.h"
+#include "nsIOutputStream.h"
+#include "nsIPrintSettings.h"
+#include "nsIStorageStream.h"
#include "ScopedNSSTypes.h"
#include "xpcpublic.h"
@@ -35,6 +39,7 @@
# include <windows.h>
# define SECURITY_WIN32 1
# include <security.h>
+# include "mozilla/NativeNt.h"
# include "mozilla/WinDllServices.h"
#endif // XP_WIN
@@ -114,6 +119,11 @@ nsIContentAnalysisAcknowledgement::FinalAction ConvertResult(
} // anonymous namespace
namespace mozilla::contentanalysis {
+ContentAnalysisRequest::~ContentAnalysisRequest() {
+#ifdef XP_WIN
+ CloseHandle(mPrintDataHandle);
+#endif
+}
NS_IMETHODIMP
ContentAnalysisRequest::GetAnalysisType(AnalysisType* aAnalysisType) {
@@ -134,6 +144,34 @@ ContentAnalysisRequest::GetFilePath(nsAString& aFilePath) {
}
NS_IMETHODIMP
+ContentAnalysisRequest::GetPrintDataHandle(uint64_t* aPrintDataHandle) {
+#ifdef XP_WIN
+ uintptr_t printDataHandle = reinterpret_cast<uintptr_t>(mPrintDataHandle);
+ uint64_t printDataValue = static_cast<uint64_t>(printDataHandle);
+ *aPrintDataHandle = printDataValue;
+ return NS_OK;
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+NS_IMETHODIMP
+ContentAnalysisRequest::GetPrinterName(nsAString& aPrinterName) {
+ aPrinterName = mPrinterName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ContentAnalysisRequest::GetPrintDataSize(uint64_t* aPrintDataSize) {
+#ifdef XP_WIN
+ *aPrintDataSize = mPrintDataSize;
+ return NS_OK;
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+NS_IMETHODIMP
ContentAnalysisRequest::GetUrl(nsIURI** aUrl) {
NS_ENSURE_ARG_POINTER(aUrl);
NS_IF_ADDREF(*aUrl = mUrl);
@@ -234,6 +272,8 @@ ContentAnalysisRequest::ContentAnalysisRequest(
mUrl(std::move(aUrl)),
mSha256Digest(std::move(aSha256Digest)),
mWindowGlobalParent(aWindowGlobalParent) {
+ MOZ_ASSERT(aAnalysisType != AnalysisType::ePrint,
+ "Print should use other ContentAnalysisRequest constructor!");
if (aStringIsFilePath) {
mFilePath = std::move(aString);
} else {
@@ -251,6 +291,32 @@ ContentAnalysisRequest::ContentAnalysisRequest(
mRequestToken = GenerateRequestToken();
}
+ContentAnalysisRequest::ContentAnalysisRequest(
+ const nsTArray<uint8_t> aPrintData, nsCOMPtr<nsIURI> aUrl,
+ nsString aPrinterName, dom::WindowGlobalParent* aWindowGlobalParent)
+ : mAnalysisType(AnalysisType::ePrint),
+ mUrl(std::move(aUrl)),
+ mPrinterName(std::move(aPrinterName)),
+ mWindowGlobalParent(aWindowGlobalParent) {
+#ifdef XP_WIN
+ LARGE_INTEGER dataContentLength;
+ dataContentLength.QuadPart = static_cast<LONGLONG>(aPrintData.Length());
+ mPrintDataHandle = ::CreateFileMappingW(
+ INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, dataContentLength.HighPart,
+ dataContentLength.LowPart, nullptr);
+ if (mPrintDataHandle) {
+ mozilla::nt::AutoMappedView view(mPrintDataHandle, FILE_MAP_ALL_ACCESS);
+ memcpy(view.as<uint8_t>(), aPrintData.Elements(), aPrintData.Length());
+ mPrintDataSize = aPrintData.Length();
+ }
+#else
+ MOZ_ASSERT_UNREACHABLE(
+ "Content Analysis is not supported on non-Windows platforms");
+#endif
+ mOperationTypeForDisplay = OperationType::eOperationPrint;
+ mRequestToken = GenerateRequestToken();
+}
+
nsresult ContentAnalysisRequest::GetFileDigest(const nsAString& aFilePath,
nsCString& aDigestString) {
MOZ_DIAGNOSTIC_ASSERT(
@@ -366,22 +432,44 @@ static nsresult ConvertToProtobuf(
requestData->set_digest(sha256Digest.get());
}
- nsString filePath;
- rv = aIn->GetFilePath(filePath);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!filePath.IsEmpty()) {
- std::string filePathStr = NS_ConvertUTF16toUTF8(filePath).get();
- aOut->set_file_path(filePathStr);
- auto filename = filePathStr.substr(filePathStr.find_last_of("/\\") + 1);
- if (!filename.empty()) {
- requestData->set_filename(filename);
+ if (analysisType == nsIContentAnalysisRequest::AnalysisType::ePrint) {
+#if XP_WIN
+ uint64_t printDataHandle;
+ MOZ_TRY(aIn->GetPrintDataHandle(&printDataHandle));
+ if (!printDataHandle) {
+ return NS_ERROR_OUT_OF_MEMORY;
}
+ aOut->mutable_print_data()->set_handle(printDataHandle);
+
+ uint64_t printDataSize;
+ MOZ_TRY(aIn->GetPrintDataSize(&printDataSize));
+ aOut->mutable_print_data()->set_size(printDataSize);
+
+ nsString printerName;
+ MOZ_TRY(aIn->GetPrinterName(printerName));
+ requestData->mutable_print_metadata()->set_printer_name(
+ NS_ConvertUTF16toUTF8(printerName).get());
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
} else {
- nsString textContent;
- rv = aIn->GetTextContent(textContent);
+ nsString filePath;
+ rv = aIn->GetFilePath(filePath);
NS_ENSURE_SUCCESS(rv, rv);
- MOZ_ASSERT(!textContent.IsEmpty());
- aOut->set_text_content(NS_ConvertUTF16toUTF8(textContent).get());
+ if (!filePath.IsEmpty()) {
+ std::string filePathStr = NS_ConvertUTF16toUTF8(filePath).get();
+ aOut->set_file_path(filePathStr);
+ auto filename = filePathStr.substr(filePathStr.find_last_of("/\\") + 1);
+ if (!filename.empty()) {
+ requestData->set_filename(filename);
+ }
+ } else {
+ nsString textContent;
+ rv = aIn->GetTextContent(textContent);
+ NS_ENSURE_SUCCESS(rv, rv);
+ MOZ_ASSERT(!textContent.IsEmpty());
+ aOut->set_text_content(NS_ConvertUTF16toUTF8(textContent).get());
+ }
}
#ifdef XP_WIN
@@ -412,8 +500,31 @@ static nsresult ConvertToProtobuf(
return NS_OK;
}
+namespace {
+// We don't want this overload to be called for string parameters, so
+// use std::enable_if
+template <typename T>
+typename std::enable_if_t<!std::is_same<std::string, std::decay_t<T>>::value,
+ void>
+LogWithMaxLength(std::stringstream& ss, T value, size_t maxLength) {
+ ss << value;
+}
+
+// 0 indicates no max length
+template <typename T>
+typename std::enable_if_t<std::is_same<std::string, std::decay_t<T>>::value,
+ void>
+LogWithMaxLength(std::stringstream& ss, T value, size_t maxLength) {
+ if (!maxLength || value.length() < maxLength) {
+ ss << value;
+ } else {
+ ss << value.substr(0, maxLength) << " (truncated)";
+ }
+}
+} // namespace
+
static void LogRequest(
- content_analysis::sdk::ContentAnalysisRequest* aPbRequest) {
+ const content_analysis::sdk::ContentAnalysisRequest* aPbRequest) {
// We cannot use Protocol Buffer's DebugString() because we optimize for
// lite runtime.
if (!static_cast<LogModule*>(gContentAnalysisLog)
@@ -425,12 +536,13 @@ static void LogRequest(
ss << "ContentAnalysisRequest:"
<< "\n";
-#define ADD_FIELD(PBUF, NAME, FUNC) \
- ss << " " << (NAME) << ": "; \
- if ((PBUF)->has_##FUNC()) \
- ss << (PBUF)->FUNC() << "\n"; \
- else \
- ss << "<none>" \
+#define ADD_FIELD(PBUF, NAME, FUNC) \
+ ss << " " << (NAME) << ": "; \
+ if ((PBUF)->has_##FUNC()) { \
+ LogWithMaxLength(ss, (PBUF)->FUNC(), 500); \
+ ss << "\n"; \
+ } else \
+ ss << "<none>" \
<< "\n";
#define ADD_EXISTS(PBUF, NAME, FUNC) \
@@ -612,6 +724,12 @@ ContentAnalysisResponse::GetAction(Action* aAction) {
return NS_OK;
}
+NS_IMETHODIMP
+ContentAnalysisResponse::GetCancelError(CancelError* aCancelError) {
+ *aCancelError = mCancelError;
+ return NS_OK;
+}
+
static void LogAcknowledgement(
content_analysis::sdk::ContentAnalysisAcknowledgement* aPbAck) {
if (!static_cast<LogModule*>(gContentAnalysisLog)
@@ -643,6 +761,10 @@ void ContentAnalysisResponse::SetOwner(RefPtr<ContentAnalysis> aOwner) {
mOwner = std::move(aOwner);
}
+void ContentAnalysisResponse::SetCancelError(CancelError aCancelError) {
+ mCancelError = aCancelError;
+}
+
void ContentAnalysisResponse::ResolveWarnAction(bool aAllowContent) {
MOZ_ASSERT(mAction == Action::eWarn);
mAction = aAllowContent ? Action::eAllow : Action::eBlock;
@@ -684,15 +806,17 @@ NS_IMETHODIMP ContentAnalysisResult::GetShouldAllowContent(
if (mValue.is<NoContentAnalysisResult>()) {
NoContentAnalysisResult result = mValue.as<NoContentAnalysisResult>();
if (Preferences::GetBool(kDefaultAllowPref)) {
- *aShouldAllowContent = result != NoContentAnalysisResult::CANCELED;
+ *aShouldAllowContent =
+ result != NoContentAnalysisResult::DENY_DUE_TO_CANCELED;
} else {
// Note that we allow content if we're unable to get it (for example, if
// there's clipboard content that is not text or file)
*aShouldAllowContent =
- result == NoContentAnalysisResult::CONTENT_ANALYSIS_NOT_ACTIVE ||
- result ==
- NoContentAnalysisResult::CONTEXT_EXEMPT_FROM_CONTENT_ANALYSIS ||
- result == NoContentAnalysisResult::ERROR_COULD_NOT_GET_DATA;
+ result == NoContentAnalysisResult::
+ ALLOW_DUE_TO_CONTENT_ANALYSIS_NOT_ACTIVE ||
+ result == NoContentAnalysisResult::
+ ALLOW_DUE_TO_CONTEXT_EXEMPT_FROM_CONTENT_ANALYSIS ||
+ result == NoContentAnalysisResult::ALLOW_DUE_TO_COULD_NOT_GET_DATA;
}
} else {
*aShouldAllowContent =
@@ -735,8 +859,8 @@ ContentAnalysis::UrlFilterResult ContentAnalysis::FilterByUrlLists(
nsIContentAnalysisRequest* aRequest) {
EnsureParsedUrlFilters();
- nsIURI* nsiUrl = nullptr;
- MOZ_ALWAYS_SUCCEEDS(aRequest->GetUrl(&nsiUrl));
+ nsCOMPtr<nsIURI> nsiUrl;
+ MOZ_ALWAYS_SUCCEEDS(aRequest->GetUrl(getter_AddRefs(nsiUrl)));
nsCString urlString;
nsresult rv = nsiUrl->GetSpec(urlString);
NS_ENSURE_SUCCESS(rv, UrlFilterResult::eDeny);
@@ -825,6 +949,8 @@ NS_IMPL_ISUPPORTS(ContentAnalysisAcknowledgement,
nsIContentAnalysisAcknowledgement);
NS_IMPL_ISUPPORTS(ContentAnalysisCallback, nsIContentAnalysisCallback);
NS_IMPL_ISUPPORTS(ContentAnalysisResult, nsIContentAnalysisResult);
+NS_IMPL_ISUPPORTS(ContentAnalysisDiagnosticInfo,
+ nsIContentAnalysisDiagnosticInfo);
NS_IMPL_ISUPPORTS(ContentAnalysis, nsIContentAnalysis, ContentAnalysis);
ContentAnalysis::ContentAnalysis()
@@ -942,6 +1068,7 @@ nsresult ContentAnalysis::CancelWithError(nsCString aRequestToken,
// May be shutting down
return;
}
+ owner->SetLastResult(aResult);
nsCOMPtr<nsIObserverService> obsServ =
mozilla::services::GetObserverService();
bool allow = Preferences::GetBool(kDefaultAllowPref);
@@ -951,6 +1078,20 @@ nsresult ContentAnalysis::CancelWithError(nsCString aRequestToken,
: nsIContentAnalysisResponse::Action::eCanceled,
aRequestToken);
response->SetOwner(owner);
+ nsIContentAnalysisResponse::CancelError cancelError;
+ switch (aResult) {
+ case NS_ERROR_NOT_AVAILABLE:
+ cancelError = nsIContentAnalysisResponse::CancelError::eNoAgent;
+ break;
+ case NS_ERROR_INVALID_SIGNATURE:
+ cancelError =
+ nsIContentAnalysisResponse::CancelError::eInvalidAgentSignature;
+ break;
+ default:
+ cancelError = nsIContentAnalysisResponse::CancelError::eErrorOther;
+ break;
+ }
+ response->SetCancelError(cancelError);
obsServ->NotifyObservers(response, "dlp-response", nullptr);
nsMainThreadPtrHandle<nsIContentAnalysisCallback> callbackHolder;
{
@@ -1156,6 +1297,8 @@ void ContentAnalysis::IssueResponse(RefPtr<ContentAnalysisResponse>& response) {
LOGE("Content analysis couldn't get request token from response!");
return;
}
+ // Successfully made a request to the agent, so mark that we succeeded
+ mLastResult = NS_OK;
Maybe<CallbackData> maybeCallbackData;
{
@@ -1187,7 +1330,9 @@ void ContentAnalysis::IssueResponse(RefPtr<ContentAnalysisResponse>& response) {
LOGD("Content analysis resolving response promise for token %s",
responseRequestToken.get());
- nsIContentAnalysisResponse::Action action = response->GetAction();
+ nsIContentAnalysisResponse::Action action;
+ DebugOnly<nsresult> rv = response->GetAction(&action);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
nsCOMPtr<nsIObserverService> obsServ =
mozilla::services::GetObserverService();
if (action == nsIContentAnalysisResponse::Action::eWarn) {
@@ -1232,8 +1377,28 @@ NS_IMETHODIMP
ContentAnalysis::AnalyzeContentRequestCallback(
nsIContentAnalysisRequest* aRequest, bool aAutoAcknowledge,
nsIContentAnalysisCallback* aCallback) {
+ MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_ARG(aRequest);
NS_ENSURE_ARG(aCallback);
+ nsresult rv = AnalyzeContentRequestCallbackPrivate(aRequest, aAutoAcknowledge,
+ aCallback);
+ if (NS_FAILED(rv)) {
+ nsCString requestToken;
+ nsresult requestTokenRv = aRequest->GetRequestToken(requestToken);
+ NS_ENSURE_SUCCESS(requestTokenRv, requestTokenRv);
+ CancelWithError(requestToken, rv);
+ }
+ return rv;
+}
+
+nsresult ContentAnalysis::AnalyzeContentRequestCallbackPrivate(
+ nsIContentAnalysisRequest* aRequest, bool aAutoAcknowledge,
+ nsIContentAnalysisCallback* aCallback) {
+ // Make sure we send the notification first, so if we later return
+ // an error the JS will handle it correctly.
+ nsCOMPtr<nsIObserverService> obsServ =
+ mozilla::services::GetObserverService();
+ obsServ->NotifyObservers(aRequest, "dlp-request-made", nullptr);
bool isActive;
nsresult rv = GetIsActive(&isActive);
@@ -1242,10 +1407,6 @@ ContentAnalysis::AnalyzeContentRequestCallback(
return NS_ERROR_NOT_AVAILABLE;
}
- nsCOMPtr<nsIObserverService> obsServ =
- mozilla::services::GetObserverService();
- obsServ->NotifyObservers(aRequest, "dlp-request-made", nullptr);
-
MOZ_ASSERT(NS_IsMainThread());
// since we're on the main thread, don't need to synchronize this
int64_t requestCount = ++mRequestCount;
@@ -1333,7 +1494,9 @@ ContentAnalysis::RespondToWarnDialog(const nsACString& aRequestToken,
return;
}
entry->mResponse->ResolveWarnAction(aAllowContent);
- auto action = entry->mResponse->GetAction();
+ nsIContentAnalysisResponse::Action action;
+ DebugOnly<nsresult> rv = entry->mResponse->GetAction(&action);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
if (entry->mCallbackData.AutoAcknowledge()) {
RefPtr<ContentAnalysisAcknowledgement> acknowledgement =
new ContentAnalysisAcknowledgement(
@@ -1358,6 +1521,181 @@ ContentAnalysis::RespondToWarnDialog(const nsACString& aRequestToken,
return NS_OK;
}
+#if defined(XP_WIN)
+RefPtr<ContentAnalysis::PrintAllowedPromise>
+ContentAnalysis::PrintToPDFToDetermineIfPrintAllowed(
+ dom::CanonicalBrowsingContext* aBrowsingContext,
+ nsIPrintSettings* aPrintSettings) {
+ // Note that the IsChrome() check here excludes a few
+ // common about pages like about:config, about:preferences,
+ // and about:support, but other about: pages may still
+ // go through content analysis.
+ if (aBrowsingContext->IsChrome()) {
+ return PrintAllowedPromise::CreateAndResolve(PrintAllowedResult(true),
+ __func__);
+ }
+ nsCOMPtr<nsIPrintSettings> contentAnalysisPrintSettings;
+ if (NS_WARN_IF(NS_FAILED(aPrintSettings->Clone(
+ getter_AddRefs(contentAnalysisPrintSettings)))) ||
+ NS_WARN_IF(!aBrowsingContext->GetCurrentWindowGlobal())) {
+ return PrintAllowedPromise::CreateAndReject(
+ PrintAllowedError(NS_ERROR_FAILURE), __func__);
+ }
+ contentAnalysisPrintSettings->SetOutputDestination(
+ nsIPrintSettings::OutputDestinationType::kOutputDestinationStream);
+ contentAnalysisPrintSettings->SetOutputFormat(
+ nsIPrintSettings::kOutputFormatPDF);
+ nsCOMPtr<nsIStorageStream> storageStream =
+ do_CreateInstance("@mozilla.org/storagestream;1");
+ if (!storageStream) {
+ return PrintAllowedPromise::CreateAndReject(
+ PrintAllowedError(NS_ERROR_FAILURE), __func__);
+ }
+ // Use segment size of 512K
+ nsresult rv = storageStream->Init(0x80000, UINT32_MAX);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return PrintAllowedPromise::CreateAndReject(PrintAllowedError(rv),
+ __func__);
+ }
+
+ nsCOMPtr<nsIOutputStream> outputStream;
+ storageStream->QueryInterface(NS_GET_IID(nsIOutputStream),
+ getter_AddRefs(outputStream));
+ MOZ_ASSERT(outputStream);
+
+ contentAnalysisPrintSettings->SetOutputStream(outputStream.get());
+ RefPtr<dom::CanonicalBrowsingContext> browsingContext = aBrowsingContext;
+ auto promise = MakeRefPtr<PrintAllowedPromise::Private>(__func__);
+ nsCOMPtr<nsIPrintSettings> finalPrintSettings(aPrintSettings);
+ aBrowsingContext
+ ->PrintWithNoContentAnalysis(contentAnalysisPrintSettings, true, nullptr)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [browsingContext, contentAnalysisPrintSettings, finalPrintSettings,
+ promise](
+ dom::MaybeDiscardedBrowsingContext cachedStaticBrowsingContext)
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA mutable {
+ nsCOMPtr<nsIOutputStream> outputStream;
+ contentAnalysisPrintSettings->GetOutputStream(
+ getter_AddRefs(outputStream));
+ nsCOMPtr<nsIStorageStream> storageStream =
+ do_QueryInterface(outputStream);
+ MOZ_ASSERT(storageStream);
+ nsTArray<uint8_t> printData;
+ uint32_t length = 0;
+ storageStream->GetLength(&length);
+ if (!printData.SetLength(length, fallible)) {
+ promise->Reject(
+ PrintAllowedError(NS_ERROR_OUT_OF_MEMORY,
+ cachedStaticBrowsingContext),
+ __func__);
+ return;
+ }
+ nsCOMPtr<nsIInputStream> inputStream;
+ nsresult rv = storageStream->NewInputStream(
+ 0, getter_AddRefs(inputStream));
+ if (NS_FAILED(rv)) {
+ promise->Reject(
+ PrintAllowedError(rv, cachedStaticBrowsingContext),
+ __func__);
+ return;
+ }
+ uint32_t currentPosition = 0;
+ while (currentPosition < length) {
+ uint32_t elementsRead = 0;
+ // Make sure the reinterpret_cast<> below is safe
+ static_assert(std::is_trivially_assignable_v<
+ decltype(*printData.Elements()), char>);
+ rv = inputStream->Read(
+ reinterpret_cast<char*>(printData.Elements()) +
+ currentPosition,
+ length - currentPosition, &elementsRead);
+ if (NS_WARN_IF(NS_FAILED(rv) || !elementsRead)) {
+ promise->Reject(
+ PrintAllowedError(NS_FAILED(rv) ? rv : NS_ERROR_FAILURE,
+ cachedStaticBrowsingContext),
+ __func__);
+ return;
+ }
+ currentPosition += elementsRead;
+ }
+
+ nsString printerName;
+ rv = contentAnalysisPrintSettings->GetPrinterName(printerName);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ promise->Reject(
+ PrintAllowedError(rv, cachedStaticBrowsingContext),
+ __func__);
+ return;
+ }
+
+ auto* windowParent = browsingContext->GetCurrentWindowGlobal();
+ if (!windowParent) {
+ // The print window may have been closed by the user by now.
+ // Cancel the print.
+ promise->Reject(
+ PrintAllowedError(NS_ERROR_ABORT,
+ cachedStaticBrowsingContext),
+ __func__);
+ return;
+ }
+ nsCOMPtr<nsIURI> uri = windowParent->GetDocumentURI();
+ nsCOMPtr<nsIContentAnalysisRequest> contentAnalysisRequest =
+ new contentanalysis::ContentAnalysisRequest(
+ std::move(printData), std::move(uri),
+ std::move(printerName), windowParent);
+ auto callback =
+ MakeRefPtr<contentanalysis::ContentAnalysisCallback>(
+ [browsingContext, cachedStaticBrowsingContext, promise,
+ finalPrintSettings = std::move(finalPrintSettings)](
+ nsIContentAnalysisResponse* aResponse)
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA mutable {
+ bool shouldAllow = false;
+ DebugOnly<nsresult> rv =
+ aResponse->GetShouldAllowContent(
+ &shouldAllow);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ promise->Resolve(
+ PrintAllowedResult(
+ shouldAllow, cachedStaticBrowsingContext),
+ __func__);
+ },
+ [promise,
+ cachedStaticBrowsingContext](nsresult aError) {
+ promise->Reject(
+ PrintAllowedError(aError,
+ cachedStaticBrowsingContext),
+ __func__);
+ });
+ nsCOMPtr<nsIContentAnalysis> contentAnalysis =
+ mozilla::components::nsIContentAnalysis::Service();
+ if (NS_WARN_IF(!contentAnalysis)) {
+ promise->Reject(
+ PrintAllowedError(rv, cachedStaticBrowsingContext),
+ __func__);
+ } else {
+ bool isActive = false;
+ nsresult rv = contentAnalysis->GetIsActive(&isActive);
+ // Should not be called if content analysis is not active
+ MOZ_ASSERT(isActive);
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+ rv = contentAnalysis->AnalyzeContentRequestCallback(
+ contentAnalysisRequest, /* aAutoAcknowledge */ true,
+ callback);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ promise->Reject(
+ PrintAllowedError(rv, cachedStaticBrowsingContext),
+ __func__);
+ }
+ }
+ },
+ [promise](nsresult aError) {
+ promise->Reject(PrintAllowedError(aError), __func__);
+ });
+ return promise;
+}
+#endif
+
NS_IMETHODIMP
ContentAnalysisResponse::Acknowledge(
nsIContentAnalysisAcknowledgement* aAcknowledgement) {
@@ -1425,6 +1763,46 @@ nsresult ContentAnalysis::RunAcknowledgeTask(
return rv;
}
+bool ContentAnalysis::LastRequestSucceeded() {
+ return mLastResult != NS_ERROR_NOT_AVAILABLE &&
+ mLastResult != NS_ERROR_INVALID_SIGNATURE &&
+ mLastResult != NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+ContentAnalysis::GetDiagnosticInfo(JSContext* aCx,
+ mozilla::dom::Promise** aPromise) {
+ RefPtr<mozilla::dom::Promise> promise;
+ nsresult rv = MakePromise(aCx, &promise);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mCaClientPromise->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [promise](std::shared_ptr<content_analysis::sdk::Client> client) mutable {
+ if (!client) {
+ auto info = MakeRefPtr<ContentAnalysisDiagnosticInfo>(
+ false, EmptyString(), false, 0);
+ promise->MaybeResolve(info);
+ return;
+ }
+ RefPtr<ContentAnalysis> self = GetContentAnalysisFromService();
+ std::string agentPath = client->GetAgentInfo().binary_path;
+ nsString agentWidePath = NS_ConvertUTF8toUTF16(agentPath);
+ auto info = MakeRefPtr<ContentAnalysisDiagnosticInfo>(
+ self->LastRequestSucceeded(), std::move(agentWidePath), false,
+ self ? self->mRequestCount : 0);
+ promise->MaybeResolve(info);
+ },
+ [promise](nsresult rv) {
+ RefPtr<ContentAnalysis> self = GetContentAnalysisFromService();
+ auto info = MakeRefPtr<ContentAnalysisDiagnosticInfo>(
+ false, EmptyString(), rv == NS_ERROR_INVALID_SIGNATURE,
+ self ? self->mRequestCount : 0);
+ promise->MaybeResolve(info);
+ });
+ promise.forget(aPromise);
+ return NS_OK;
+}
+
NS_IMETHODIMP ContentAnalysisCallback::ContentResult(
nsIContentAnalysisResponse* aResponse) {
if (mPromise.isSome()) {
@@ -1448,6 +1826,28 @@ ContentAnalysisCallback::ContentAnalysisCallback(RefPtr<dom::Promise> aPromise)
: mPromise(Some(new nsMainThreadPtrHolder<dom::Promise>(
"content analysis promise", aPromise))) {}
+NS_IMETHODIMP ContentAnalysisDiagnosticInfo::GetConnectedToAgent(
+ bool* aConnectedToAgent) {
+ *aConnectedToAgent = mConnectedToAgent;
+ return NS_OK;
+}
+NS_IMETHODIMP ContentAnalysisDiagnosticInfo::GetAgentPath(
+ nsAString& aAgentPath) {
+ aAgentPath = mAgentPath;
+ return NS_OK;
+}
+NS_IMETHODIMP ContentAnalysisDiagnosticInfo::GetFailedSignatureVerification(
+ bool* aFailedSignatureVerification) {
+ *aFailedSignatureVerification = mFailedSignatureVerification;
+ return NS_OK;
+}
+
+NS_IMETHODIMP ContentAnalysisDiagnosticInfo::GetRequestCount(
+ int64_t* aRequestCount) {
+ *aRequestCount = mRequestCount;
+ return NS_OK;
+}
+
#undef LOGD
#undef LOGE
} // namespace mozilla::contentanalysis