Skip to content

Commit 19759c6

Browse files
committed
Bug 1811076: Part 8 - Show content analysis WARN dialog and let user to choose allow/deny r=Gijs,fluent-reviewers,flod,rkraesig
If a WARN response is returned from the content analysis agent, the browser displays a dialog box and lets the user choose whether to allow or deny the content. Differential Revision: https://phabricator.services.mozilla.com/D189581
1 parent 2f5afc2 commit 19759c6

File tree

5 files changed

+144
-25
lines changed

5 files changed

+144
-25
lines changed

browser/components/contentanalysis/content/ContentAnalysis.sys.mjs

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -301,8 +301,8 @@ export const ContentAnalysis = {
301301
);
302302
}
303303
const responseResult =
304-
request?.action ?? Ci.nsIContentAnalysisResponse.ACTION_UNSPECIFIED;
305-
this._showCAResult(
304+
request?.action ?? Ci.nsIContentAnalysisResponse.eUnspecified;
305+
await this._showCAResult(
306306
windowAndResourceNameOrL10NId.resourceNameOrL10NId,
307307
windowAndResourceNameOrL10NId.browsingContext,
308308
request.requestToken,
@@ -395,8 +395,8 @@ export const ContentAnalysis = {
395395

396396
_shouldShowBlockingNotification(aOperation) {
397397
return !(
398-
aOperation == Ci.nsIContentAnalysisRequest.FILE_DOWNLOADED ||
399-
aOperation == Ci.nsIContentAnalysisRequest.PRINT
398+
aOperation == Ci.nsIContentAnalysisRequest.eFileDownloaded ||
399+
aOperation == Ci.nsIContentAnalysisRequest.ePrint
400400
);
401401
},
402402

@@ -413,16 +413,16 @@ export const ContentAnalysis = {
413413
_getResourceNameOrL10NIdFromRequest(aRequest) {
414414
if (
415415
aRequest.operationTypeForDisplay ==
416-
Ci.nsIContentAnalysisRequest.OPERATION_CUSTOMDISPLAYSTRING
416+
Ci.nsIContentAnalysisRequest.eCustomDisplayString
417417
) {
418418
return { name: aRequest.operationDisplayString };
419419
}
420420
let l10nId;
421421
switch (aRequest.operationTypeForDisplay) {
422-
case Ci.nsIContentAnalysisRequest.OPERATION_CLIPBOARD:
422+
case Ci.nsIContentAnalysisRequest.eClipboard:
423423
l10nId = "contentanalysis-operationtype-clipboard";
424424
break;
425-
case Ci.nsIContentAnalysisRequest.OPERATION_DROPPEDTEXT:
425+
case Ci.nsIContentAnalysisRequest.eDroppedText:
426426
l10nId = "contentanalysis-operationtype-dropped-text";
427427
break;
428428
}
@@ -535,7 +535,7 @@ export const ContentAnalysis = {
535535
*
536536
* @returns {object} a notification object (if shown)
537537
*/
538-
_showCAResult(
538+
async _showCAResult(
539539
aResourceNameOrL10NId,
540540
aBrowsingContext,
541541
aRequestToken,
@@ -545,11 +545,11 @@ export const ContentAnalysis = {
545545
let timeoutMs = 0;
546546

547547
switch (aCAResult) {
548-
case Ci.nsIContentAnalysisResponse.ALLOW:
548+
case Ci.nsIContentAnalysisResponse.eAllow:
549549
// We don't need to show anything
550550
return null;
551-
case Ci.nsIContentAnalysisResponse.REPORT_ONLY:
552-
message = this.l10n.formatValueSync(
551+
case Ci.nsIContentAnalysisResponse.eReportOnly:
552+
message = await this.l10n.formatValue(
553553
"contentanalysis-genericresponse-message",
554554
{
555555
content: this._getResourceNameFromNameOrL10NId(
@@ -560,26 +560,42 @@ export const ContentAnalysis = {
560560
);
561561
timeoutMs = this._RESULT_NOTIFICATION_FAST_TIMEOUT_MS;
562562
break;
563-
case Ci.nsIContentAnalysisResponse.WARN:
564-
message = this.l10n.formatValueSync(
565-
"contentanalysis-genericresponse-message",
566-
{
563+
case Ci.nsIContentAnalysisResponse.eWarn:
564+
const result = await Services.prompt.asyncConfirmEx(
565+
aBrowsingContext,
566+
Ci.nsIPromptService.MODAL_TYPE_TAB,
567+
await this.l10n.formatValue("contentanalysis-warndialogtitle"),
568+
await this.l10n.formatValue("contentanalysis-warndialogtext", {
567569
content: this._getResourceNameFromNameOrL10NId(
568570
aResourceNameOrL10NId
569571
),
570-
response: "WARN",
571-
}
572+
}),
573+
Ci.nsIPromptService.BUTTON_POS_0 *
574+
Ci.nsIPromptService.BUTTON_TITLE_IS_STRING +
575+
Ci.nsIPromptService.BUTTON_POS_1 *
576+
Ci.nsIPromptService.BUTTON_TITLE_IS_STRING +
577+
Ci.nsIPromptService.BUTTON_POS_1_DEFAULT,
578+
await this.l10n.formatValue(
579+
"contentanalysis-warndialog-response-allow"
580+
),
581+
await this.l10n.formatValue(
582+
"contentanalysis-warndialog-response-deny"
583+
),
584+
null,
585+
null,
586+
{}
572587
);
573-
timeoutMs = this._RESULT_NOTIFICATION_TIMEOUT_MS;
574-
break;
575-
case Ci.nsIContentAnalysisResponse.BLOCK:
576-
message = this.l10n.formatValueSync("contentanalysis-block-message", {
588+
const allow = result.get("buttonNumClicked") === 0;
589+
lazy.gContentAnalysis.respondToWarnDialog(aRequestToken, allow);
590+
return null;
591+
case Ci.nsIContentAnalysisResponse.eBlock:
592+
message = await this.l10n.formatValue("contentanalysis-block-message", {
577593
content: this._getResourceNameFromNameOrL10NId(aResourceNameOrL10NId),
578594
});
579595
timeoutMs = this._RESULT_NOTIFICATION_TIMEOUT_MS;
580596
break;
581-
case Ci.nsIContentAnalysisResponse.ACTION_UNSPECIFIED:
582-
message = this.l10n.formatValueSync("contentanalysis-error-message", {
597+
case Ci.nsIContentAnalysisResponse.eUnspecified:
598+
message = await this.l10n.formatValue("contentanalysis-error-message", {
583599
content: this._getResourceNameFromNameOrL10NId(aResourceNameOrL10NId),
584600
});
585601
timeoutMs = this._RESULT_NOTIFICATION_TIMEOUT_MS;
@@ -588,6 +604,14 @@ export const ContentAnalysis = {
588604
throw new Error("Unexpected CA result value: " + aCAResult);
589605
}
590606

607+
if (!message) {
608+
console.error(
609+
"_showCAResult did not get a message populated for result value " +
610+
aCAResult
611+
);
612+
return null;
613+
}
614+
591615
return this._showMessage(message, aBrowsingContext, timeoutMs);
592616
},
593617
};

toolkit/components/contentanalysis/ContentAnalysis.cpp

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,11 @@ void ContentAnalysisResponse::SetOwner(RefPtr<ContentAnalysis> aOwner) {
557557
mOwner = std::move(aOwner);
558558
}
559559

560+
void ContentAnalysisResponse::ResolveWarnAction(bool aAllowContent) {
561+
MOZ_ASSERT(mAction == Action::eWarn);
562+
mAction = aAllowContent ? Action::eAllow : Action::eBlock;
563+
}
564+
560565
ContentAnalysisAcknowledgement::ContentAnalysisAcknowledgement(
561566
Result aResult, FinalAction aFinalAction)
562567
: mResult(aResult), mFinalAction(aFinalAction) {}
@@ -616,7 +621,8 @@ ContentAnalysis::ContentAnalysis()
616621
: mCaClientPromise(
617622
new ClientPromise::Private("ContentAnalysis::ContentAnalysis")),
618623
mClientCreationAttempted(false),
619-
mCallbackMap("ContentAnalysis::mCallbackMap") {}
624+
mCallbackMap("ContentAnalysis::mCallbackMap"),
625+
mWarnResponseDataMap("ContentAnalysis::mWarnResponseDataMap") {}
620626

621627
ContentAnalysis::~ContentAnalysis() {
622628
// Accessing mClientCreationAttempted so need to be on the main thread
@@ -872,12 +878,22 @@ void ContentAnalysis::DoAnalyzeRequest(
872878
"Content analysis resolving response promise for "
873879
"token %s",
874880
responseRequestToken.get());
881+
nsIContentAnalysisResponse::Action action = response->GetAction();
875882
nsCOMPtr<nsIObserverService> obsServ =
876883
mozilla::services::GetObserverService();
884+
if (action == nsIContentAnalysisResponse::Action::eWarn) {
885+
{
886+
auto warnResponseDataMap = owner->mWarnResponseDataMap.Lock();
887+
warnResponseDataMap->InsertOrUpdate(
888+
responseRequestToken,
889+
WarnResponseData(std::move(*maybeCallbackData), response));
890+
}
891+
obsServ->NotifyObservers(response, "dlp-response", nullptr);
892+
return;
893+
}
877894

878895
obsServ->NotifyObservers(response, "dlp-response", nullptr);
879896
if (maybeCallbackData->AutoAcknowledge()) {
880-
nsIContentAnalysisResponse::Action action = response->GetAction();
881897
auto acknowledgement = MakeRefPtr<ContentAnalysisAcknowledgement>(
882898
nsIContentAnalysisAcknowledgement::Result::eSuccess,
883899
ConvertResult(action));
@@ -962,6 +978,59 @@ ContentAnalysis::CancelContentAnalysisRequest(const nsACString& aRequestToken) {
962978
return NS_OK;
963979
}
964980

981+
NS_IMETHODIMP
982+
ContentAnalysis::RespondToWarnDialog(const nsACString& aRequestToken,
983+
bool aAllowContent) {
984+
nsCString requestToken(aRequestToken);
985+
NS_DispatchToMainThread(NS_NewCancelableRunnableFunction(
986+
"RespondToWarnDialog",
987+
[aAllowContent, requestToken = std::move(requestToken)]() {
988+
RefPtr<ContentAnalysis> self = GetContentAnalysisFromService();
989+
if (!self) {
990+
// May be shutting down
991+
return;
992+
}
993+
994+
LOGD("Content analysis getting warn response %d for request %s",
995+
aAllowContent ? 1 : 0, requestToken.get());
996+
Maybe<WarnResponseData> entry;
997+
{
998+
auto warnResponseDataMap = self->mWarnResponseDataMap.Lock();
999+
entry = warnResponseDataMap->Extract(requestToken);
1000+
}
1001+
if (!entry) {
1002+
LOGD(
1003+
"Content analysis request not found when trying to send warn "
1004+
"response for request %s",
1005+
requestToken.get());
1006+
return;
1007+
}
1008+
entry->mResponse->ResolveWarnAction(aAllowContent);
1009+
auto action = entry->mResponse->GetAction();
1010+
if (entry->mCallbackData.AutoAcknowledge()) {
1011+
RefPtr<ContentAnalysisAcknowledgement> acknowledgement =
1012+
new ContentAnalysisAcknowledgement(
1013+
nsIContentAnalysisAcknowledgement::Result::eSuccess,
1014+
ConvertResult(action));
1015+
entry->mResponse->Acknowledge(acknowledgement);
1016+
}
1017+
nsMainThreadPtrHandle<nsIContentAnalysisCallback> callbackHolder =
1018+
entry->mCallbackData.TakeCallbackHolder();
1019+
if (callbackHolder) {
1020+
RefPtr<ContentAnalysisResponse> response =
1021+
ContentAnalysisResponse::FromAction(action, requestToken);
1022+
response->SetOwner(self);
1023+
callbackHolder.get()->ContentResult(response.get());
1024+
} else {
1025+
LOGD(
1026+
"Content analysis had no callback to send warn final response "
1027+
"to for request %s",
1028+
requestToken.get());
1029+
}
1030+
}));
1031+
return NS_OK;
1032+
}
1033+
9651034
NS_IMETHODIMP
9661035
ContentAnalysisResponse::Acknowledge(
9671036
nsIContentAnalysisAcknowledgement* aAcknowledgement) {

toolkit/components/contentanalysis/ContentAnalysis.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,16 @@ class ContentAnalysis final : public nsIContentAnalysis {
138138
bool mAutoAcknowledge;
139139
};
140140
DataMutex<nsTHashMap<nsCString, CallbackData>> mCallbackMap;
141+
142+
struct WarnResponseData {
143+
WarnResponseData(CallbackData&& aCallbackData,
144+
RefPtr<ContentAnalysisResponse> aResponse)
145+
: mCallbackData(std::move(aCallbackData)), mResponse(aResponse) {}
146+
ContentAnalysis::CallbackData mCallbackData;
147+
RefPtr<ContentAnalysisResponse> mResponse;
148+
};
149+
DataMutex<nsTHashMap<nsCString, WarnResponseData>> mWarnResponseDataMap;
150+
141151
friend class ContentAnalysisResponse;
142152
};
143153

@@ -164,6 +174,8 @@ class ContentAnalysisResponse final : public nsIContentAnalysisResponse {
164174
static already_AddRefed<ContentAnalysisResponse> FromProtobuf(
165175
content_analysis::sdk::ContentAnalysisResponse&& aResponse);
166176

177+
void ResolveWarnAction(bool aAllowContent);
178+
167179
// Action requested by the agent
168180
Action mAction;
169181

toolkit/components/contentanalysis/nsIContentAnalysis.idl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,4 +227,10 @@ interface nsIContentAnalysis : nsISupports
227227
* The token for the request to cancel.
228228
*/
229229
void cancelContentAnalysisRequest(in ACString aRequestToken);
230+
231+
/**
232+
* Indicates that the user has responded to a WARN dialog. aAllowContent represents
233+
* whether the user wants to allow the request to go through.
234+
*/
235+
void respondToWarnDialog(in ACString aRequestToken, in bool aAllowContent);
230236
};

toolkit/locales/en-US/toolkit/contentanalysis/contentanalysis.ftl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ contentanalysis-slow-agent-dialog-body = Content Analysis is analyzing resource
1515
contentanalysis-operationtype-clipboard = clipboard
1616
contentanalysis-operationtype-dropped-text = dropped text
1717
18+
contentanalysis-warndialogtitle = This content may be unsafe
19+
20+
# Variables:
21+
# $content - Description of the content being warned about, such as "clipboard" or "aFile.txt"
22+
contentanalysis-warndialogtext = Your organization uses data-loss prevention software that has flagged this content as unsafe: { $content }. Use it anyway?
23+
contentanalysis-warndialog-response-allow = Use content
24+
contentanalysis-warndialog-response-deny = Cancel
25+
1826
contentanalysis-notification-title = Content Analysis
1927
# Variables:
2028
# $content - Description of the content being reported, such as "clipboard" or "aFile.txt"

0 commit comments

Comments
 (0)