mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 16:22:00 +00:00
Bug 1879181
: Allow skipping content analysis for requests that match url list r=gstoll,win-reviewers
Check the URLs in the request against the prefs browser.contentanalysis.allow_url_regex_list and browser.contentanalysis.deny_url_regex_list, which are space-separated lists of ECMAscript regexs that match against ASCII-encoded URLs. Differential Revision: https://phabricator.services.mozilla.com/D203508
This commit is contained in:
parent
99a762fcd7
commit
c76a1ddc8d
@ -1192,6 +1192,22 @@
|
||||
value: "path_user"
|
||||
mirror: never
|
||||
|
||||
# Space-separated list of regexs that are compared to URLs of resources
|
||||
# being checked by content-analysis. Resources that match are not checked
|
||||
# and are always permitted.
|
||||
- name: browser.contentanalysis.allow_url_regex_list
|
||||
type: String
|
||||
value: ""
|
||||
mirror: never
|
||||
|
||||
# Space-separated list of regexs that are compared to URLs of resources
|
||||
# being checked by content-analysis. Resources that match are not checked
|
||||
# and are always denied.
|
||||
- name: browser.contentanalysis.deny_url_regex_list
|
||||
type: String
|
||||
value: ""
|
||||
mirror: never
|
||||
|
||||
# Should CA ignore the system setting and use silent notifications?
|
||||
- name: browser.contentanalysis.silent_notifications
|
||||
type: bool
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "GMPUtils.h" // ToHexString
|
||||
#include "mozilla/Components.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/WindowGlobalParent.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/Services.h"
|
||||
@ -28,6 +29,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#ifdef XP_WIN
|
||||
# include <windows.h>
|
||||
@ -56,6 +58,8 @@ const char* kIsPerUserPref = "browser.contentanalysis.is_per_user";
|
||||
const char* kPipePathNamePref = "browser.contentanalysis.pipe_path_name";
|
||||
const char* kDefaultAllowPref = "browser.contentanalysis.default_allow";
|
||||
const char* kClientSignature = "browser.contentanalysis.client_signature";
|
||||
const char* kAllowUrlPref = "browser.contentanalysis.allow_url_regex_list";
|
||||
const char* kDenyUrlPref = "browser.contentanalysis.deny_url_regex_list";
|
||||
|
||||
nsresult MakePromise(JSContext* aCx, RefPtr<mozilla::dom::Promise>* aPromise) {
|
||||
nsIGlobalObject* go = xpc::CurrentNativeGlobal(aCx);
|
||||
@ -480,8 +484,7 @@ static void LogRequest(
|
||||
}
|
||||
|
||||
ContentAnalysisResponse::ContentAnalysisResponse(
|
||||
content_analysis::sdk::ContentAnalysisResponse&& aResponse)
|
||||
: mHasAcknowledged(false) {
|
||||
content_analysis::sdk::ContentAnalysisResponse&& aResponse) {
|
||||
mAction = Action::eUnspecified;
|
||||
for (const auto& result : aResponse.results()) {
|
||||
if (!result.has_status() ||
|
||||
@ -509,7 +512,7 @@ ContentAnalysisResponse::ContentAnalysisResponse(
|
||||
|
||||
ContentAnalysisResponse::ContentAnalysisResponse(
|
||||
Action aAction, const nsACString& aRequestToken)
|
||||
: mAction(aAction), mRequestToken(aRequestToken), mHasAcknowledged(false) {}
|
||||
: mAction(aAction), mRequestToken(aRequestToken) {}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<ContentAnalysisResponse> ContentAnalysisResponse::FromProtobuf(
|
||||
@ -698,6 +701,122 @@ NS_IMETHODIMP ContentAnalysisResult::GetShouldAllowContent(
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void ContentAnalysis::EnsureParsedUrlFilters() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (mParsedUrlLists) {
|
||||
return;
|
||||
}
|
||||
|
||||
mParsedUrlLists = true;
|
||||
nsAutoCString allowList;
|
||||
MOZ_ALWAYS_SUCCEEDS(Preferences::GetCString(kAllowUrlPref, allowList));
|
||||
for (const nsACString& regexSubstr : allowList.Split(u' ')) {
|
||||
if (!regexSubstr.IsEmpty()) {
|
||||
auto flatStr = PromiseFlatCString(regexSubstr);
|
||||
const char* regex = flatStr.get();
|
||||
LOGD("CA will allow URLs that match %s", regex);
|
||||
mAllowUrlList.push_back(std::regex(regex));
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoCString denyList;
|
||||
MOZ_ALWAYS_SUCCEEDS(Preferences::GetCString(kDenyUrlPref, denyList));
|
||||
for (const nsACString& regexSubstr : denyList.Split(u' ')) {
|
||||
if (!regexSubstr.IsEmpty()) {
|
||||
auto flatStr = PromiseFlatCString(regexSubstr);
|
||||
const char* regex = flatStr.get();
|
||||
LOGD("CA will block URLs that match %s", regex);
|
||||
mDenyUrlList.push_back(std::regex(regex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentAnalysis::UrlFilterResult ContentAnalysis::FilterByUrlLists(
|
||||
nsIContentAnalysisRequest* aRequest) {
|
||||
EnsureParsedUrlFilters();
|
||||
|
||||
nsIURI* nsiUrl = nullptr;
|
||||
MOZ_ALWAYS_SUCCEEDS(aRequest->GetUrl(&nsiUrl));
|
||||
nsCString urlString;
|
||||
nsresult rv = nsiUrl->GetSpec(urlString);
|
||||
NS_ENSURE_SUCCESS(rv, UrlFilterResult::eDeny);
|
||||
MOZ_ASSERT(!urlString.IsEmpty());
|
||||
std::string url = urlString.BeginReading();
|
||||
size_t count = 0;
|
||||
for (const auto& denyFilter : mDenyUrlList) {
|
||||
if (std::regex_search(url, denyFilter)) {
|
||||
LOGD("Denying CA request : Deny filter %zu matched url %s", count,
|
||||
url.c_str());
|
||||
return UrlFilterResult::eDeny;
|
||||
}
|
||||
++count;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
UrlFilterResult result = UrlFilterResult::eCheck;
|
||||
for (const auto& allowFilter : mAllowUrlList) {
|
||||
if (std::regex_match(url, allowFilter)) {
|
||||
LOGD("CA request : Allow filter %zu matched %s", count, url.c_str());
|
||||
result = UrlFilterResult::eAllow;
|
||||
break;
|
||||
}
|
||||
++count;
|
||||
}
|
||||
|
||||
// The rest only applies to download resources.
|
||||
nsIContentAnalysisRequest::AnalysisType analysisType;
|
||||
MOZ_ALWAYS_SUCCEEDS(aRequest->GetAnalysisType(&analysisType));
|
||||
if (analysisType != ContentAnalysisRequest::AnalysisType::eFileDownloaded) {
|
||||
MOZ_ASSERT(result == UrlFilterResult::eCheck ||
|
||||
result == UrlFilterResult::eAllow);
|
||||
LOGD("CA request filter result: %s",
|
||||
result == UrlFilterResult::eCheck ? "check" : "allow");
|
||||
return result;
|
||||
}
|
||||
|
||||
nsTArray<RefPtr<nsIClientDownloadResource>> resources;
|
||||
MOZ_ALWAYS_SUCCEEDS(aRequest->GetResources(resources));
|
||||
for (size_t resourceIdx = 0; resourceIdx < resources.Length();
|
||||
/* noop */) {
|
||||
auto& resource = resources[resourceIdx];
|
||||
nsAutoString nsUrl;
|
||||
MOZ_ALWAYS_SUCCEEDS(resource->GetUrl(nsUrl));
|
||||
std::string url = NS_ConvertUTF16toUTF8(nsUrl).get();
|
||||
count = 0;
|
||||
for (auto& denyFilter : mDenyUrlList) {
|
||||
if (std::regex_search(url, denyFilter)) {
|
||||
LOGD(
|
||||
"Denying CA request : Deny filter %zu matched download resource "
|
||||
"at url %s",
|
||||
count, url.c_str());
|
||||
return UrlFilterResult::eDeny;
|
||||
}
|
||||
++count;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
bool removed = false;
|
||||
for (auto& allowFilter : mAllowUrlList) {
|
||||
if (std::regex_search(url, allowFilter)) {
|
||||
LOGD(
|
||||
"CA request : Allow filter %zu matched download resource "
|
||||
"at url %s",
|
||||
count, url.c_str());
|
||||
resources.RemoveElementAt(resourceIdx);
|
||||
removed = true;
|
||||
break;
|
||||
}
|
||||
++count;
|
||||
}
|
||||
if (!removed) {
|
||||
++resourceIdx;
|
||||
}
|
||||
}
|
||||
|
||||
// Check unless all were allowed.
|
||||
return resources.Length() ? UrlFilterResult::eCheck : UrlFilterResult::eAllow;
|
||||
}
|
||||
|
||||
NS_IMPL_CLASSINFO(ContentAnalysisRequest, nullptr, 0, {0});
|
||||
NS_IMPL_ISUPPORTS_CI(ContentAnalysisRequest, nsIContentAnalysisRequest);
|
||||
NS_IMPL_CLASSINFO(ContentAnalysisResponse, nullptr, 0, {0});
|
||||
@ -866,6 +985,8 @@ nsresult ContentAnalysis::RunAnalyzeRequestTask(
|
||||
const RefPtr<nsIContentAnalysisRequest>& aRequest, bool aAutoAcknowledge,
|
||||
int64_t aRequestCount,
|
||||
const RefPtr<nsIContentAnalysisCallback>& aCallback) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
auto callbackCopy = aCallback;
|
||||
auto se = MakeScopeExit([&] {
|
||||
@ -875,24 +996,51 @@ nsresult ContentAnalysis::RunAnalyzeRequestTask(
|
||||
}
|
||||
});
|
||||
|
||||
content_analysis::sdk::ContentAnalysisRequest pbRequest;
|
||||
rv =
|
||||
ConvertToProtobuf(aRequest, GetUserActionId(), aRequestCount, &pbRequest);
|
||||
nsCString requestToken;
|
||||
rv = aRequest->GetRequestToken(requestToken);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCString requestToken;
|
||||
nsMainThreadPtrHandle<nsIContentAnalysisCallback> callbackHolderCopy(
|
||||
new nsMainThreadPtrHolder<nsIContentAnalysisCallback>(
|
||||
"content analysis callback", aCallback));
|
||||
CallbackData callbackData(std::move(callbackHolderCopy), aAutoAcknowledge);
|
||||
rv = aRequest->GetRequestToken(requestToken);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
{
|
||||
auto lock = mCallbackMap.Lock();
|
||||
lock->InsertOrUpdate(requestToken, std::move(callbackData));
|
||||
}
|
||||
|
||||
// Check URLs of requested info against
|
||||
// browser.contentanalysis.allow_url_regex_list/deny_url_regex_list.
|
||||
// Build the list once since creating regexs is slow.
|
||||
// URLs that match the allow list are removed from the check. There is
|
||||
// only one URL in all cases except downloads. If all contents are removed
|
||||
// or the page URL is allowed (for downloads) then the operation is allowed.
|
||||
// URLs that match the deny list block the entire operation.
|
||||
// If the request is completely covered by this filter then flag it as
|
||||
// not needing to send an Acknowledge.
|
||||
auto filterResult = FilterByUrlLists(aRequest);
|
||||
if (filterResult == ContentAnalysis::UrlFilterResult::eDeny) {
|
||||
LOGD("Blocking request due to deny URL filter.");
|
||||
auto response = ContentAnalysisResponse::FromAction(
|
||||
nsIContentAnalysisResponse::Action::eBlock, requestToken);
|
||||
response->DoNotAcknowledge();
|
||||
IssueResponse(response);
|
||||
return NS_OK;
|
||||
} else if (filterResult == ContentAnalysis::UrlFilterResult::eAllow) {
|
||||
LOGD("Allowing request -- all operations match allow URL filter.");
|
||||
auto response = ContentAnalysisResponse::FromAction(
|
||||
nsIContentAnalysisResponse::Action::eAllow, requestToken);
|
||||
response->DoNotAcknowledge();
|
||||
IssueResponse(response);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
LOGD("Issuing ContentAnalysisRequest for token %s", requestToken.get());
|
||||
|
||||
content_analysis::sdk::ContentAnalysisRequest pbRequest;
|
||||
rv = ConvertToProtobuf(aRequest, GetUserActionId(), aRequestCount,
|
||||
&pbRequest);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
LogRequest(&pbRequest);
|
||||
|
||||
mCaClientPromise->Then(
|
||||
@ -994,77 +1142,77 @@ void ContentAnalysis::DoAnalyzeRequest(
|
||||
LOGE("Content analysis got invalid response!");
|
||||
return;
|
||||
}
|
||||
nsCString responseRequestToken;
|
||||
nsresult requestRv = response->GetRequestToken(responseRequestToken);
|
||||
if (NS_FAILED(requestRv)) {
|
||||
LOGE(
|
||||
"Content analysis couldn't get request token "
|
||||
"from response!");
|
||||
return;
|
||||
}
|
||||
|
||||
Maybe<CallbackData> maybeCallbackData;
|
||||
{
|
||||
auto callbackMap = owner->mCallbackMap.Lock();
|
||||
maybeCallbackData = callbackMap->Extract(responseRequestToken);
|
||||
}
|
||||
if (maybeCallbackData.isNothing()) {
|
||||
LOGD(
|
||||
"Content analysis did not find callback for "
|
||||
"token %s",
|
||||
responseRequestToken.get());
|
||||
return;
|
||||
}
|
||||
response->SetOwner(owner);
|
||||
if (maybeCallbackData->Canceled()) {
|
||||
// request has already been cancelled, so there's
|
||||
// nothing to do
|
||||
LOGD(
|
||||
"Content analysis got response but ignoring "
|
||||
"because it was already cancelled for token %s",
|
||||
responseRequestToken.get());
|
||||
// Note that we always acknowledge here, even if
|
||||
// autoAcknowledge isn't set, since we raise an exception
|
||||
// at the caller on cancellation.
|
||||
auto acknowledgement = MakeRefPtr<ContentAnalysisAcknowledgement>(
|
||||
nsIContentAnalysisAcknowledgement::Result::eTooLate,
|
||||
nsIContentAnalysisAcknowledgement::FinalAction::eBlock);
|
||||
response->Acknowledge(acknowledgement);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGD(
|
||||
"Content analysis resolving response promise for "
|
||||
"token %s",
|
||||
responseRequestToken.get());
|
||||
nsIContentAnalysisResponse::Action action = response->GetAction();
|
||||
nsCOMPtr<nsIObserverService> obsServ =
|
||||
mozilla::services::GetObserverService();
|
||||
if (action == nsIContentAnalysisResponse::Action::eWarn) {
|
||||
{
|
||||
auto warnResponseDataMap = owner->mWarnResponseDataMap.Lock();
|
||||
warnResponseDataMap->InsertOrUpdate(
|
||||
responseRequestToken,
|
||||
WarnResponseData(std::move(*maybeCallbackData), response));
|
||||
}
|
||||
obsServ->NotifyObservers(response, "dlp-response", nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
obsServ->NotifyObservers(response, "dlp-response", nullptr);
|
||||
if (maybeCallbackData->AutoAcknowledge()) {
|
||||
auto acknowledgement = MakeRefPtr<ContentAnalysisAcknowledgement>(
|
||||
nsIContentAnalysisAcknowledgement::Result::eSuccess,
|
||||
ConvertResult(action));
|
||||
response->Acknowledge(acknowledgement);
|
||||
}
|
||||
|
||||
nsMainThreadPtrHandle<nsIContentAnalysisCallback> callbackHolder =
|
||||
maybeCallbackData->TakeCallbackHolder();
|
||||
callbackHolder->ContentResult(response);
|
||||
owner->IssueResponse(response);
|
||||
}));
|
||||
}
|
||||
|
||||
void ContentAnalysis::IssueResponse(RefPtr<ContentAnalysisResponse>& response) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCString responseRequestToken;
|
||||
nsresult requestRv = response->GetRequestToken(responseRequestToken);
|
||||
if (NS_FAILED(requestRv)) {
|
||||
LOGE("Content analysis couldn't get request token from response!");
|
||||
return;
|
||||
}
|
||||
|
||||
Maybe<CallbackData> maybeCallbackData;
|
||||
{
|
||||
auto callbackMap = mCallbackMap.Lock();
|
||||
maybeCallbackData = callbackMap->Extract(responseRequestToken);
|
||||
}
|
||||
if (maybeCallbackData.isNothing()) {
|
||||
LOGD("Content analysis did not find callback for token %s",
|
||||
responseRequestToken.get());
|
||||
return;
|
||||
}
|
||||
response->SetOwner(this);
|
||||
if (maybeCallbackData->Canceled()) {
|
||||
// request has already been cancelled, so there's
|
||||
// nothing to do
|
||||
LOGD(
|
||||
"Content analysis got response but ignoring "
|
||||
"because it was already cancelled for token %s",
|
||||
responseRequestToken.get());
|
||||
// Note that we always acknowledge here, even if
|
||||
// autoAcknowledge isn't set, since we raise an exception
|
||||
// at the caller on cancellation.
|
||||
auto acknowledgement = MakeRefPtr<ContentAnalysisAcknowledgement>(
|
||||
nsIContentAnalysisAcknowledgement::Result::eTooLate,
|
||||
nsIContentAnalysisAcknowledgement::FinalAction::eBlock);
|
||||
response->Acknowledge(acknowledgement);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGD("Content analysis resolving response promise for token %s",
|
||||
responseRequestToken.get());
|
||||
nsIContentAnalysisResponse::Action action = response->GetAction();
|
||||
nsCOMPtr<nsIObserverService> obsServ =
|
||||
mozilla::services::GetObserverService();
|
||||
if (action == nsIContentAnalysisResponse::Action::eWarn) {
|
||||
{
|
||||
auto warnResponseDataMap = mWarnResponseDataMap.Lock();
|
||||
warnResponseDataMap->InsertOrUpdate(
|
||||
responseRequestToken,
|
||||
WarnResponseData(std::move(*maybeCallbackData), response));
|
||||
}
|
||||
obsServ->NotifyObservers(response, "dlp-response", nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
obsServ->NotifyObservers(response, "dlp-response", nullptr);
|
||||
if (maybeCallbackData->AutoAcknowledge()) {
|
||||
auto acknowledgement = MakeRefPtr<ContentAnalysisAcknowledgement>(
|
||||
nsIContentAnalysisAcknowledgement::Result::eSuccess,
|
||||
ConvertResult(action));
|
||||
response->Acknowledge(acknowledgement);
|
||||
}
|
||||
|
||||
nsMainThreadPtrHandle<nsIContentAnalysisCallback> callbackHolder =
|
||||
maybeCallbackData->TakeCallbackHolder();
|
||||
callbackHolder->ContentResult(response);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContentAnalysis::AnalyzeContentRequest(nsIContentAnalysisRequest* aRequest,
|
||||
bool aAutoAcknowledge, JSContext* aCx,
|
||||
@ -1218,6 +1366,10 @@ ContentAnalysisResponse::Acknowledge(
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mHasAcknowledged = true;
|
||||
|
||||
if (mDoNotAcknowledge) {
|
||||
return NS_OK;
|
||||
}
|
||||
return mOwner->RunAcknowledgeTask(aAcknowledgement, mRequestToken);
|
||||
};
|
||||
|
||||
|
@ -7,15 +7,25 @@
|
||||
#define mozilla_contentanalysis_h
|
||||
|
||||
#include "mozilla/DataMutex.h"
|
||||
#include "mozilla/dom/WindowGlobalParent.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "nsIContentAnalysis.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTHashMap.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
class nsIPrincipal;
|
||||
class ContentAnalysisTest;
|
||||
|
||||
namespace mozilla::dom {
|
||||
class DataTransfer;
|
||||
class WindowGlobalParent;
|
||||
} // namespace mozilla::dom
|
||||
|
||||
namespace content_analysis::sdk {
|
||||
class Client;
|
||||
class ContentAnalysisRequest;
|
||||
@ -116,6 +126,14 @@ class ContentAnalysis final : public nsIContentAnalysis {
|
||||
nsCString aRequestToken,
|
||||
content_analysis::sdk::ContentAnalysisRequest&& aRequest,
|
||||
const std::shared_ptr<content_analysis::sdk::Client>& aClient);
|
||||
void IssueResponse(RefPtr<ContentAnalysisResponse>& response);
|
||||
|
||||
// Did the URL filter completely handle the request or do we need to check
|
||||
// with the agent.
|
||||
enum UrlFilterResult { eCheck, eDeny, eAllow };
|
||||
|
||||
UrlFilterResult FilterByUrlLists(nsIContentAnalysisRequest* aRequest);
|
||||
void EnsureParsedUrlFilters();
|
||||
|
||||
using ClientPromise =
|
||||
MozPromise<std::shared_ptr<content_analysis::sdk::Client>, nsresult,
|
||||
@ -158,6 +176,10 @@ class ContentAnalysis final : public nsIContentAnalysis {
|
||||
};
|
||||
DataMutex<nsTHashMap<nsCString, WarnResponseData>> mWarnResponseDataMap;
|
||||
|
||||
std::vector<std::regex> mAllowUrlList;
|
||||
std::vector<std::regex> mDenyUrlList;
|
||||
bool mParsedUrlLists;
|
||||
|
||||
friend class ContentAnalysisResponse;
|
||||
};
|
||||
|
||||
@ -172,6 +194,7 @@ class ContentAnalysisResponse final : public nsIContentAnalysisResponse {
|
||||
Action aAction, const nsACString& aRequestToken);
|
||||
|
||||
void SetOwner(RefPtr<ContentAnalysis> aOwner);
|
||||
void DoNotAcknowledge() { mDoNotAcknowledge = true; }
|
||||
|
||||
private:
|
||||
~ContentAnalysisResponse() = default;
|
||||
@ -197,7 +220,11 @@ class ContentAnalysisResponse final : public nsIContentAnalysisResponse {
|
||||
RefPtr<ContentAnalysis> mOwner;
|
||||
|
||||
// Whether the response has been acknowledged
|
||||
bool mHasAcknowledged;
|
||||
bool mHasAcknowledged = false;
|
||||
|
||||
// If true, the request was completely handled by URL filter lists, so it
|
||||
// was not sent to the agent and should not send an Acknowledge.
|
||||
bool mDoNotAcknowledge = false;
|
||||
|
||||
friend class ContentAnalysis;
|
||||
};
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "mozilla/BackgroundHangMonitor.h"
|
||||
#include "mozilla/Components.h"
|
||||
#include "mozilla/dom/BrowsingContext.h"
|
||||
#include "mozilla/dom/CanonicalBrowsingContext.h"
|
||||
#include "mozilla/dom/Directory.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/ipc/UtilityProcessManager.h"
|
||||
|
Loading…
Reference in New Issue
Block a user