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:
@@ -179,20 +179,22 @@ function commonDialogOnLoad() {
|
|||||||
const selectionDirection =
|
const selectionDirection =
|
||||||
endIndex < startIndex ? "backward" : "forward";
|
endIndex < startIndex ? "backward" : "forward";
|
||||||
try {
|
try {
|
||||||
const response = await lazy.gContentAnalysis.analyzeContentRequest(
|
const response = await lazy.gContentAnalysis.analyzeContentRequests(
|
||||||
{
|
[
|
||||||
requestToken: Services.uuid.generateUUID().toString(),
|
{
|
||||||
resources: [],
|
analysisType: Ci.nsIContentAnalysisRequest.eBulkDataEntry,
|
||||||
analysisType: Ci.nsIContentAnalysisRequest.eBulkDataEntry,
|
reason: Ci.nsIContentAnalysisRequest.eClipboardPaste,
|
||||||
reason: Ci.nsIContentAnalysisRequest.eClipboardPaste,
|
resources: [],
|
||||||
operationTypeForDisplay: Ci.nsIContentAnalysisRequest.eClipboard,
|
operationTypeForDisplay:
|
||||||
url: lazy.gContentAnalysis.getURIForBrowsingContext(
|
Ci.nsIContentAnalysisRequest.eClipboard,
|
||||||
args.owningBrowsingContext
|
url: lazy.gContentAnalysis.getURIForBrowsingContext(
|
||||||
),
|
args.owningBrowsingContext
|
||||||
textContent: data,
|
),
|
||||||
windowGlobalParent:
|
textContent: data,
|
||||||
args.owningBrowsingContext.currentWindowContext,
|
windowGlobalParent:
|
||||||
},
|
args.owningBrowsingContext.currentWindowContext,
|
||||||
|
},
|
||||||
|
],
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
if (response.shouldAllowContent) {
|
if (response.shouldAllowContent) {
|
||||||
|
|||||||
@@ -30,25 +30,12 @@
|
|||||||
Services.io.newURI("about:blank")
|
Services.io.newURI("about:blank")
|
||||||
);
|
);
|
||||||
|
|
||||||
XPCOMUtils.defineLazyServiceGetter(
|
|
||||||
lazy,
|
|
||||||
"contentAnalysis",
|
|
||||||
"@mozilla.org/contentanalysis;1",
|
|
||||||
Ci.nsIContentAnalysis
|
|
||||||
);
|
|
||||||
|
|
||||||
let lazyPrefs = {};
|
let lazyPrefs = {};
|
||||||
XPCOMUtils.defineLazyPreferenceGetter(
|
XPCOMUtils.defineLazyPreferenceGetter(
|
||||||
lazyPrefs,
|
lazyPrefs,
|
||||||
"unloadTimeoutMs",
|
"unloadTimeoutMs",
|
||||||
"dom.beforeunload_timeout_ms"
|
"dom.beforeunload_timeout_ms"
|
||||||
);
|
);
|
||||||
XPCOMUtils.defineLazyPreferenceGetter(
|
|
||||||
lazyPrefs,
|
|
||||||
"_contentAnalysisDragDropEnabled",
|
|
||||||
"browser.contentanalysis.interception_point.drag_and_drop.enabled",
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
Object.defineProperty(lazy, "ProcessHangMonitor", {
|
Object.defineProperty(lazy, "ProcessHangMonitor", {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
@@ -173,10 +160,10 @@
|
|||||||
this.addEventListener(
|
this.addEventListener(
|
||||||
"drop",
|
"drop",
|
||||||
event => {
|
event => {
|
||||||
if (
|
const contentAnalysis = Cc[
|
||||||
lazy.contentAnalysis.isActive &&
|
"@mozilla.org/contentanalysis;1"
|
||||||
lazyPrefs._contentAnalysisDragDropEnabled
|
].getService(Ci.nsIContentAnalysis);
|
||||||
) {
|
if (contentAnalysis.isActive) {
|
||||||
let dragService = Cc[
|
let dragService = Cc[
|
||||||
"@mozilla.org/widget/dragservice;1"
|
"@mozilla.org/widget/dragservice;1"
|
||||||
].getService(Ci.nsIDragService);
|
].getService(Ci.nsIDragService);
|
||||||
@@ -185,102 +172,30 @@
|
|||||||
return;
|
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 {
|
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
|
// Tell browser to record the event target and to delay EndDragSession
|
||||||
// until the content analysis results are given.
|
// until the content analysis results are given.
|
||||||
dragSession.sendStoreDropTargetAndDelayEndDragSession(event);
|
dragSession.sendStoreDropTargetAndDelayEndDragSession(event);
|
||||||
|
|
||||||
// Submit a content analysis request for each checkable entry in the
|
contentAnalysis.analyzeContentRequest(request, true).then(
|
||||||
// DataTransfer and stop dispatching this drop event. Reissue the
|
caResult => {
|
||||||
// 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.
|
|
||||||
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(
|
dragSession.sendDispatchToDropTargetAndResumeEndDragSession(
|
||||||
allApproved
|
caResult.shouldAllowContent
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
@@ -293,7 +208,9 @@
|
|||||||
// Do not allow this drop to continue dispatch.
|
// Do not allow this drop to continue dispatch.
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
} catch {
|
} catch (e) {
|
||||||
|
console.error(`content analysis dnd error: ${e}`);
|
||||||
|
|
||||||
// On internal error, deny any drop. CA has its own behavior to
|
// On internal error, deny any drop. CA has its own behavior to
|
||||||
// handle internal errors, like a lost connection to the agent, but
|
// handle internal errors, like a lost connection to the agent, but
|
||||||
// we are more strict when facing errors here.
|
// we are more strict when facing errors here.
|
||||||
|
|||||||
@@ -119,16 +119,14 @@ static RefPtr<ClipboardResultPromise> GetClipboardImpl(
|
|||||||
auto resultPromise = MakeRefPtr<ClipboardResultPromise::Private>(__func__);
|
auto resultPromise = MakeRefPtr<ClipboardResultPromise::Private>(__func__);
|
||||||
|
|
||||||
auto contentAnalysisCallback =
|
auto contentAnalysisCallback =
|
||||||
mozilla::MakeRefPtr<mozilla::contentanalysis::ContentAnalysis::
|
mozilla::MakeRefPtr<mozilla::contentanalysis::ContentAnalysisCallback>(
|
||||||
SafeContentAnalysisResultCallback>(
|
|
||||||
[transferable, resultPromise,
|
[transferable, resultPromise,
|
||||||
cpHandle = RefPtr{aRequestingContentParent}](
|
cpHandle = RefPtr{aRequestingContentParent}](
|
||||||
RefPtr<nsIContentAnalysisResult>&& aResult) {
|
nsIContentAnalysisResult* aResult) {
|
||||||
// Needed to call cpHandle->GetContentParent()
|
// Needed to call cpHandle->GetContentParent()
|
||||||
AssertIsOnMainThread();
|
AssertIsOnMainThread();
|
||||||
|
|
||||||
bool shouldAllow = aResult->GetShouldAllowContent();
|
if (!aResult->GetShouldAllowContent()) {
|
||||||
if (!shouldAllow) {
|
|
||||||
resultPromise->Reject(NS_ERROR_CONTENT_BLOCKED, __func__);
|
resultPromise->Reject(NS_ERROR_CONTENT_BLOCKED, __func__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1017,11 +1017,9 @@ NS_IMETHODIMP nsBaseClipboard::ClipboardDataSnapshot::GetData(
|
|||||||
MOZ_ASSERT(mClipboard);
|
MOZ_ASSERT(mClipboard);
|
||||||
|
|
||||||
auto contentAnalysisCallback =
|
auto contentAnalysisCallback =
|
||||||
mozilla::MakeRefPtr<mozilla::contentanalysis::ContentAnalysis::
|
mozilla::MakeRefPtr<mozilla::contentanalysis::ContentAnalysisCallback>(
|
||||||
SafeContentAnalysisResultCallback>(
|
|
||||||
[transferable = nsCOMPtr{aTransferable},
|
[transferable = nsCOMPtr{aTransferable},
|
||||||
callback = nsCOMPtr{aCallback}](
|
callback = nsCOMPtr{aCallback}](nsIContentAnalysisResult* aResult) {
|
||||||
RefPtr<nsIContentAnalysisResult>&& aResult) {
|
|
||||||
if (aResult->GetShouldAllowContent()) {
|
if (aResult->GetShouldAllowContent()) {
|
||||||
callback->OnComplete(NS_OK);
|
callback->OnComplete(NS_OK);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -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
|
} // namespace fd_async
|
||||||
|
|
||||||
using fd_async::AsyncAll;
|
|
||||||
using fd_async::AsyncExecute;
|
using fd_async::AsyncExecute;
|
||||||
|
|
||||||
} // namespace mozilla::detail
|
} // namespace mozilla::detail
|
||||||
@@ -713,14 +695,6 @@ nsFilePicker::CheckContentAnalysisService() {
|
|||||||
__func__);
|
__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
|
// Entries may be files or folders. Folder contents will be recursively
|
||||||
// checked.
|
// checked.
|
||||||
nsTArray<mozilla::PathString> filePaths;
|
nsTArray<mozilla::PathString> filePaths;
|
||||||
@@ -737,47 +711,41 @@ nsFilePicker::CheckContentAnalysisService() {
|
|||||||
std::transform(mFiles.begin(), mFiles.end(), MakeBackInserter(filePaths),
|
std::transform(mFiles.begin(), mFiles.end(), MakeBackInserter(filePaths),
|
||||||
[](auto* entry) { return entry->NativePath(); });
|
[](auto* entry) { return entry->NativePath(); });
|
||||||
}
|
}
|
||||||
|
MOZ_ASSERT(!filePaths.IsEmpty());
|
||||||
|
|
||||||
auto processOneItem = [self = RefPtr{this},
|
auto promise =
|
||||||
contentAnalysis = std::move(contentAnalysis),
|
mozilla::MakeRefPtr<nsFilePicker::ContentAnalysisResponse::Private>(
|
||||||
uri =
|
__func__);
|
||||||
std::move(uri)](const mozilla::PathString& aItem) {
|
auto contentAnalysisCallback =
|
||||||
nsCString emptyDigestString;
|
mozilla::MakeRefPtr<mozilla::contentanalysis::ContentAnalysisCallback>(
|
||||||
auto* windowGlobal =
|
[promise](nsIContentAnalysisResult* aResult) {
|
||||||
self->mBrowsingContext->Canonical()->GetCurrentWindowGlobal();
|
promise->Resolve(aResult->GetShouldAllowContent(), __func__);
|
||||||
nsCOMPtr<nsIContentAnalysisRequest> contentAnalysisRequest(
|
});
|
||||||
new mozilla::contentanalysis::ContentAnalysisRequest(
|
|
||||||
nsIContentAnalysisRequest::AnalysisType::eFileAttached,
|
|
||||||
nsIContentAnalysisRequest::Reason::eFilePickerDialog, aItem, true,
|
|
||||||
std::move(emptyDigestString), uri,
|
|
||||||
nsIContentAnalysisRequest::OperationType::eCustomDisplayString,
|
|
||||||
windowGlobal));
|
|
||||||
|
|
||||||
auto promise =
|
auto* windowGlobal = mBrowsingContext->Canonical()->GetCurrentWindowGlobal();
|
||||||
mozilla::MakeRefPtr<nsFilePicker::ContentAnalysisResponse::Private>(
|
NS_ENSURE_TRUE(
|
||||||
__func__);
|
windowGlobal,
|
||||||
auto contentAnalysisCallback =
|
nsFilePicker::ContentAnalysisResponse::CreateAndReject(rv, __func__));
|
||||||
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__); });
|
|
||||||
|
|
||||||
nsresult rv = contentAnalysis->AnalyzeContentRequestCallback(
|
nsTArray<RefPtr<nsIContentAnalysisRequest>> requests(filePaths.Length());
|
||||||
contentAnalysisRequest, /* aAutoAcknowledge */ true,
|
for (auto& path : filePaths) {
|
||||||
contentAnalysisCallback);
|
#ifdef XP_WIN
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
nsString pathString(std::move(path));
|
||||||
promise->Reject(rv, __func__);
|
#else
|
||||||
}
|
nsString pathString = NS_ConvertUTF8toUTF16(path);
|
||||||
return promise;
|
#endif
|
||||||
};
|
|
||||||
|
|
||||||
return mozilla::detail::AsyncAll<mozilla::PathString>(std::move(filePaths),
|
requests.AppendElement(new mozilla::contentanalysis::ContentAnalysisRequest(
|
||||||
processOneItem);
|
nsIContentAnalysisRequest::AnalysisType::eFileAttached,
|
||||||
|
nsIContentAnalysisRequest::Reason::eFilePickerDialog, pathString,
|
||||||
|
true /* aStringIsFilePath */, EmptyCString(), nullptr,
|
||||||
|
nsIContentAnalysisRequest::OperationType::eCustomDisplayString,
|
||||||
|
windowGlobal));
|
||||||
|
}
|
||||||
|
|
||||||
|
contentAnalysis->AnalyzeContentRequestsCallback(
|
||||||
|
requests, true /* aAutoAcknowledge */, contentAnalysisCallback);
|
||||||
|
return promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
Reference in New Issue
Block a user