Bug 1936020: Part 8 - Update the content analysis service's callers r=dlp-reviewers,win-reviewers,reusable-components-reviewers,gstoll,tgiles

AnalyzeContentRequest* calls now require fewer fields.  For example, the
URI is, by default, now obtained from the request's WindowGlobalParent's
BrowsingContext.

browser-custom-element.js needed to re-get service for tests, since it was
sometimes getting an old mock version from a previous test.

ContentAnalysisCallback does what SafeContentAnalysisResultCallback used to do.

Differential Revision: https://phabricator.services.mozilla.com/D236631
This commit is contained in:
David P
2025-02-13 19:19:18 +00:00
parent 469f02dde1
commit 1a51dc7863
5 changed files with 77 additions and 194 deletions

View File

@@ -179,13 +179,14 @@ function commonDialogOnLoad() {
const selectionDirection =
endIndex < startIndex ? "backward" : "forward";
try {
const response = await lazy.gContentAnalysis.analyzeContentRequest(
const response = await lazy.gContentAnalysis.analyzeContentRequests(
[
{
requestToken: Services.uuid.generateUUID().toString(),
resources: [],
analysisType: Ci.nsIContentAnalysisRequest.eBulkDataEntry,
reason: Ci.nsIContentAnalysisRequest.eClipboardPaste,
operationTypeForDisplay: Ci.nsIContentAnalysisRequest.eClipboard,
resources: [],
operationTypeForDisplay:
Ci.nsIContentAnalysisRequest.eClipboard,
url: lazy.gContentAnalysis.getURIForBrowsingContext(
args.owningBrowsingContext
),
@@ -193,6 +194,7 @@ function commonDialogOnLoad() {
windowGlobalParent:
args.owningBrowsingContext.currentWindowContext,
},
],
true
);
if (response.shouldAllowContent) {

View File

@@ -30,25 +30,12 @@
Services.io.newURI("about:blank")
);
XPCOMUtils.defineLazyServiceGetter(
lazy,
"contentAnalysis",
"@mozilla.org/contentanalysis;1",
Ci.nsIContentAnalysis
);
let lazyPrefs = {};
XPCOMUtils.defineLazyPreferenceGetter(
lazyPrefs,
"unloadTimeoutMs",
"dom.beforeunload_timeout_ms"
);
XPCOMUtils.defineLazyPreferenceGetter(
lazyPrefs,
"_contentAnalysisDragDropEnabled",
"browser.contentanalysis.interception_point.drag_and_drop.enabled",
true
);
Object.defineProperty(lazy, "ProcessHangMonitor", {
configurable: true,
@@ -173,10 +160,10 @@
this.addEventListener(
"drop",
event => {
if (
lazy.contentAnalysis.isActive &&
lazyPrefs._contentAnalysisDragDropEnabled
) {
const contentAnalysis = Cc[
"@mozilla.org/contentanalysis;1"
].getService(Ci.nsIContentAnalysis);
if (contentAnalysis.isActive) {
let dragService = Cc[
"@mozilla.org/widget/dragservice;1"
].getService(Ci.nsIDragService);
@@ -185,102 +172,30 @@
return;
}
// Don't check items from drags that take place inside of a single
// frame, or in a same-origin iframe hierarchy. Drag sessions with an
// external source have no sourceWindowContext and must be checked.
let sourceWC = dragSession.sourceWindowContext;
let targetWC = this.browsingContext?.currentWindowContext;
if (!targetWC) {
return;
}
if (
sourceWC &&
sourceWC.browsingContext.top == targetWC.browsingContext.top &&
targetWC.documentPrincipal?.subsumes(sourceWC.documentPrincipal)
) {
return;
}
// Create a request for each text and file item in the DataTransfer
try {
// Submit a content analysis request for the DataTransfer and
// stop dispatching this drop event. Reissue the drop if all
// requests are permitted, otherwise issue a dragexit.
let request = {
analysisType: Ci.nsIContentAnalysisRequest.eBulkDataEntry,
dataTransfer: event.dataTransfer,
operationTypeForDisplay:
Ci.nsIContentAnalysisRequest.eDroppedText,
reason: Ci.nsIContentAnalysisRequest.eDragAndDrop,
resources: [],
sourceWindowGlobal: dragSession.sourceWindowContext,
uri: contentAnalysis.getURIForDropEvent(event),
windowGlobalParent: this.browsingContext.currentWindowContext,
};
// Tell browser to record the event target and to delay EndDragSession
// until the content analysis results are given.
dragSession.sendStoreDropTargetAndDelayEndDragSession(event);
// Submit a content analysis request for each checkable entry in the
// DataTransfer and stop dispatching this drop event. Reissue the
// drop if all requests are permitted.
let caPromises = [];
let items = event.dataTransfer.items;
for (let elt of items) {
const kTextMimeTypes = [
"text/plain",
"text/html",
"application/x-moz-nativehtml",
];
let requestFields;
if (
elt.kind === "string" &&
kTextMimeTypes.includes(elt.type)
) {
let str = event.dataTransfer.getData(elt.type);
if (!str) {
continue;
}
requestFields = {
analysisType: Ci.nsIContentAnalysisRequest.eBulkDataEntry,
operationTypeForDisplay:
Ci.nsIContentAnalysisRequest.eDroppedText,
textContent: str,
};
} else if (elt.kind === "file") {
let file = elt.getAsFile();
requestFields = {
analysisType: Ci.nsIContentAnalysisRequest.eFileAttached,
operationTypeForDisplay:
Ci.nsIContentAnalysisRequest.eCustomDisplayString,
operationDisplayString: file.name,
filePath: file.mozFullPath,
};
} else {
// Unrecognized data type -- don't send to content analysis
continue;
}
caPromises.push(
lazy.contentAnalysis.analyzeContentRequest(
{
reason: Ci.nsIContentAnalysisRequest.eDragAndDrop,
requestToken: Services.uuid.generateUUID().toString(),
resources: [],
url: lazy.contentAnalysis.getURIForDropEvent(event),
windowGlobalParent:
this.browsingContext.currentWindowContext,
...requestFields,
},
true /* autoAcknowledge */
)
);
}
if (!caPromises.length) {
// Nothing was analyzable.
contentAnalysis.analyzeContentRequest(request, true).then(
caResult => {
dragSession.sendDispatchToDropTargetAndResumeEndDragSession(
true
);
return;
}
// Only permit the drop if all requests were approved. Issue dragexit
// instead of drop if CA rejected the content or there was an error.
Promise.all(caPromises).then(
caResults => {
let allApproved = caResults.reduce((prev, current) => {
return prev && current.shouldAllowContent;
}, true);
dragSession.sendDispatchToDropTargetAndResumeEndDragSession(
allApproved
caResult.shouldAllowContent
);
},
() => {
@@ -293,7 +208,9 @@
// Do not allow this drop to continue dispatch.
event.preventDefault();
event.stopPropagation();
} catch {
} catch (e) {
console.error(`content analysis dnd error: ${e}`);
// On internal error, deny any drop. CA has its own behavior to
// handle internal errors, like a lost connection to the agent, but
// we are more strict when facing errors here.

View File

@@ -119,16 +119,14 @@ static RefPtr<ClipboardResultPromise> GetClipboardImpl(
auto resultPromise = MakeRefPtr<ClipboardResultPromise::Private>(__func__);
auto contentAnalysisCallback =
mozilla::MakeRefPtr<mozilla::contentanalysis::ContentAnalysis::
SafeContentAnalysisResultCallback>(
mozilla::MakeRefPtr<mozilla::contentanalysis::ContentAnalysisCallback>(
[transferable, resultPromise,
cpHandle = RefPtr{aRequestingContentParent}](
RefPtr<nsIContentAnalysisResult>&& aResult) {
nsIContentAnalysisResult* aResult) {
// Needed to call cpHandle->GetContentParent()
AssertIsOnMainThread();
bool shouldAllow = aResult->GetShouldAllowContent();
if (!shouldAllow) {
if (!aResult->GetShouldAllowContent()) {
resultPromise->Reject(NS_ERROR_CONTENT_BLOCKED, __func__);
return;
}

View File

@@ -1017,11 +1017,9 @@ NS_IMETHODIMP nsBaseClipboard::ClipboardDataSnapshot::GetData(
MOZ_ASSERT(mClipboard);
auto contentAnalysisCallback =
mozilla::MakeRefPtr<mozilla::contentanalysis::ContentAnalysis::
SafeContentAnalysisResultCallback>(
mozilla::MakeRefPtr<mozilla::contentanalysis::ContentAnalysisCallback>(
[transferable = nsCOMPtr{aTransferable},
callback = nsCOMPtr{aCallback}](
RefPtr<nsIContentAnalysisResult>&& aResult) {
callback = nsCOMPtr{aCallback}](nsIContentAnalysisResult* aResult) {
if (aResult->GetShouldAllowContent()) {
callback->OnComplete(NS_OK);
} else {

View File

@@ -455,26 +455,8 @@ static auto AsyncExecute(Fn1 local, Fn2 remote, Args const&... args) ->
});
}
// Asynchronously invokes `aPredicate` on each member of `aItems`.
// Yields `false` (and stops immediately) if any invocation of
// `predicate` yielded `false`; otherwise yields `true`.
template <typename T>
static RefPtr<mozilla::MozPromise<bool, nsresult, true>> AsyncAll(
nsTArray<T> aItems,
std::function<
RefPtr<mozilla::MozPromise<bool, nsresult, true>>(const T& item)>
aPredicate) {
auto promise =
mozilla::MakeRefPtr<mozilla::MozPromise<bool, nsresult, true>::Private>(
__func__);
auto iterator = mozilla::MakeRefPtr<details::AsyncAllIterator<T>>(
std::move(aItems), aPredicate, promise);
iterator->StartIterating();
return promise;
}
} // namespace fd_async
using fd_async::AsyncAll;
using fd_async::AsyncExecute;
} // namespace mozilla::detail
@@ -713,14 +695,6 @@ nsFilePicker::CheckContentAnalysisService() {
__func__);
}
nsCOMPtr<nsIURI> uri =
mozilla::contentanalysis::ContentAnalysis::GetURIForBrowsingContext(
mBrowsingContext->Canonical());
if (!uri) {
return nsFilePicker::ContentAnalysisResponse::CreateAndReject(
NS_ERROR_FAILURE, __func__);
}
// Entries may be files or folders. Folder contents will be recursively
// checked.
nsTArray<mozilla::PathString> filePaths;
@@ -737,47 +711,41 @@ nsFilePicker::CheckContentAnalysisService() {
std::transform(mFiles.begin(), mFiles.end(), MakeBackInserter(filePaths),
[](auto* entry) { return entry->NativePath(); });
}
auto processOneItem = [self = RefPtr{this},
contentAnalysis = std::move(contentAnalysis),
uri =
std::move(uri)](const mozilla::PathString& aItem) {
nsCString emptyDigestString;
auto* windowGlobal =
self->mBrowsingContext->Canonical()->GetCurrentWindowGlobal();
nsCOMPtr<nsIContentAnalysisRequest> contentAnalysisRequest(
new mozilla::contentanalysis::ContentAnalysisRequest(
nsIContentAnalysisRequest::AnalysisType::eFileAttached,
nsIContentAnalysisRequest::Reason::eFilePickerDialog, aItem, true,
std::move(emptyDigestString), uri,
nsIContentAnalysisRequest::OperationType::eCustomDisplayString,
windowGlobal));
MOZ_ASSERT(!filePaths.IsEmpty());
auto promise =
mozilla::MakeRefPtr<nsFilePicker::ContentAnalysisResponse::Private>(
__func__);
auto contentAnalysisCallback =
mozilla::MakeRefPtr<mozilla::contentanalysis::ContentAnalysisCallback>(
[promise](nsIContentAnalysisResponse* aResponse) {
bool shouldAllow = false;
mozilla::DebugOnly<nsresult> rv =
aResponse->GetShouldAllowContent(&shouldAllow);
MOZ_ASSERT(NS_SUCCEEDED(rv));
promise->Resolve(shouldAllow, __func__);
},
[promise](nsresult aError) { promise->Reject(aError, __func__); });
[promise](nsIContentAnalysisResult* aResult) {
promise->Resolve(aResult->GetShouldAllowContent(), __func__);
});
nsresult rv = contentAnalysis->AnalyzeContentRequestCallback(
contentAnalysisRequest, /* aAutoAcknowledge */ true,
contentAnalysisCallback);
if (NS_WARN_IF(NS_FAILED(rv))) {
promise->Reject(rv, __func__);
auto* windowGlobal = mBrowsingContext->Canonical()->GetCurrentWindowGlobal();
NS_ENSURE_TRUE(
windowGlobal,
nsFilePicker::ContentAnalysisResponse::CreateAndReject(rv, __func__));
nsTArray<RefPtr<nsIContentAnalysisRequest>> requests(filePaths.Length());
for (auto& path : filePaths) {
#ifdef XP_WIN
nsString pathString(std::move(path));
#else
nsString pathString = NS_ConvertUTF8toUTF16(path);
#endif
requests.AppendElement(new mozilla::contentanalysis::ContentAnalysisRequest(
nsIContentAnalysisRequest::AnalysisType::eFileAttached,
nsIContentAnalysisRequest::Reason::eFilePickerDialog, pathString,
true /* aStringIsFilePath */, EmptyCString(), nullptr,
nsIContentAnalysisRequest::OperationType::eCustomDisplayString,
windowGlobal));
}
return promise;
};
return mozilla::detail::AsyncAll<mozilla::PathString>(std::move(filePaths),
processOneItem);
contentAnalysis->AnalyzeContentRequestsCallback(
requests, true /* aAutoAcknowledge */, contentAnalysisCallback);
return promise;
};
///////////////////////////////////////////////////////////////////////////////