Files
tubestation/toolkit/components/contentanalysis/ContentAnalysisUtils.sys.mjs
Greg Stoll 0f76fa2f28 Bug 1959115 - monitor pastes into GenAI custom prompt for Content Analysis r=dlp-reviewers,firefox-ai-ml-reviewers,handyman,Mardak
We handle this similarly to pasting into a prompt() dialog, so I
refactored that logic into a new ContentAnalysisUtils and call it from
both places.

Also drive-by cleanup of BUG_COMPONENT for a few Content Analysis
moz.build's

Differential Revision: https://phabricator.services.mozilla.com/D244818
2025-04-10 20:48:22 +00:00

100 lines
4.0 KiB
JavaScript

/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Contains helper methods for JS code that need to call into
* Content Analysis. Note that most JS code will not need to explicitly
* use this - this is only for edge cases that the existing C++ code
* does not handle, such as pasting into a prompt() or pasting into
* the GenAI chatbot shortcut menu.
*/
export const ContentAnalysisUtils = {
/**
* Sets up Content Analysis to monitor clipboard pastes and drag-and-drop
* and send the text on to Content Analysis for approval. This method
* will check if Content Analysis is active and if not it will return early.
*
* @param {Element} textElement The DOM element to monitor
* @param {BrowsingContext} browsingContext The browsing context that the textElement
* is part of. Used to show the "DLP busy" dialog.
* @param {Function} url An nsIURI that indicates where the content would be sent to.
* If this is undefined, this method will get the URI from the browsingContext.
*/
setupContentAnalysisEventsForTextElement(textElement, browsingContext, url) {
// Do not use a lazy service getter for this, because tests set up different mocks,
// so if multiple tests run that call into this we can end up calling into an old mock.
const contentAnalysis = Cc["@mozilla.org/contentanalysis;1"].getService(
Ci.nsIContentAnalysis
);
if (!textElement || !contentAnalysis.isActive) {
return;
}
let caEventChecker = async event => {
let isPaste = event.type == "paste";
let dataTransfer = isPaste ? event.clipboardData : event.dataTransfer;
let data = dataTransfer.getData("text/plain");
if (!data || !data.length) {
return;
}
// Prevent the paste/drop from happening until content analysis returns a response
event.preventDefault();
// Selections can be forward or backward, so use min/max
const startIndex = Math.min(
textElement.selectionStart,
textElement.selectionEnd
);
const endIndex = Math.max(
textElement.selectionStart,
textElement.selectionEnd
);
const selectionDirection = endIndex < startIndex ? "backward" : "forward";
try {
const response = await contentAnalysis.analyzeContentRequests(
[
{
analysisType: Ci.nsIContentAnalysisRequest.eBulkDataEntry,
reason: isPaste
? Ci.nsIContentAnalysisRequest.eClipboardPaste
: Ci.nsIContentAnalysisRequest.eDragAndDrop,
resources: [],
operationTypeForDisplay: isPaste
? Ci.nsIContentAnalysisRequest.eClipboard
: Ci.nsIContentAnalysisRequest.eDroppedText,
url:
url ??
contentAnalysis.getURIForBrowsingContext(browsingContext),
textContent: data,
windowGlobalParent: browsingContext.currentWindowContext,
},
],
true
);
if (response.shouldAllowContent) {
textElement.value =
textElement.value.slice(0, startIndex) +
data +
textElement.value.slice(endIndex);
textElement.focus();
if (startIndex !== endIndex) {
// Select the pasted text
textElement.setSelectionRange(
startIndex,
startIndex + data.length,
selectionDirection
);
}
}
} catch (error) {
console.error("Content analysis request returned error: ", error);
}
};
textElement.addEventListener("paste", caEventChecker);
textElement.addEventListener("drop", caEventChecker);
},
};