gecko-dev/dom/reporting/ReportDeliver.cpp
Gabriele Svelto ace6d1063f Bug 1600545 - Remove useless inclusions of header files generated from IDL files in dom/ r=Ehsan
The inclusions were removed with the following very crude script and the
resulting breakage was fixed up by hand. The manual fixups did either
revert the changes done by the script, replace a generic header with a more
specific one or replace a header with a forward declaration.

find . -name "*.idl" | grep -v web-platform | grep -v third_party | while read path; do
    interfaces=$(grep "^\(class\|interface\).*:.*" "$path" | cut -d' ' -f2)
    if [ -n "$interfaces" ]; then
        if [[ "$interfaces" == *$'\n'* ]]; then
          regexp="\("
          for i in $interfaces; do regexp="$regexp$i\|"; done
          regexp="${regexp%%\\\|}\)"
        else
          regexp="$interfaces"
        fi
        interface=$(basename "$path")
        rg -l "#include.*${interface%%.idl}.h" . | while read path2; do
            hits=$(grep -v "#include.*${interface%%.idl}.h" "$path2" | grep -c "$regexp" )
            if [ $hits -eq 0 ]; then
                echo "Removing ${interface} from ${path2}"
                grep -v "#include.*${interface%%.idl}.h" "$path2" > "$path2".tmp
                mv -f "$path2".tmp "$path2"
            fi
        done
    fi
done

Differential Revision: https://phabricator.services.mozilla.com/D55442

--HG--
extra : moz-landing-system : lando
2019-12-06 09:24:56 +00:00

405 lines
11 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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/. */
#include "mozilla/dom/EndpointForReportChild.h"
#include "mozilla/dom/Fetch.h"
#include "mozilla/dom/Navigator.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ReportDeliver.h"
#include "mozilla/dom/Request.h"
#include "mozilla/dom/RequestBinding.h"
#include "mozilla/dom/Response.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "nsGlobalWindowInner.h"
#include "nsIGlobalObject.h"
#include "nsIXPConnect.h"
#include "nsNetUtil.h"
#include "nsStringStream.h"
namespace mozilla {
namespace dom {
namespace {
StaticRefPtr<ReportDeliver> gReportDeliver;
class ReportFetchHandler final : public PromiseNativeHandler {
public:
NS_DECL_ISUPPORTS
explicit ReportFetchHandler(const ReportDeliver::ReportData& aReportData)
: mReportData(aReportData) {}
void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
if (!gReportDeliver) {
return;
}
if (NS_WARN_IF(!aValue.isObject())) {
return;
}
JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
MOZ_ASSERT(obj);
{
Response* response = nullptr;
if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Response, &obj, response)))) {
return;
}
if (response->Status() == 410) {
mozilla::ipc::PBackgroundChild* actorChild =
mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
mozilla::ipc::PrincipalInfo principalInfo;
nsresult rv =
PrincipalToPrincipalInfo(mReportData.mPrincipal, &principalInfo);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
actorChild->SendRemoveEndpoint(mReportData.mGroupName,
mReportData.mEndpointURL, principalInfo);
}
}
}
void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
if (gReportDeliver) {
++mReportData.mFailures;
gReportDeliver->AppendReportData(mReportData);
}
}
private:
~ReportFetchHandler() = default;
ReportDeliver::ReportData mReportData;
};
NS_IMPL_ISUPPORTS0(ReportFetchHandler)
// This RAII class keeps a list of sandboxed globals for the delivering of
// reports. In this way, if we have to deliver more than 1 report to the same
// origin, we reuse the sandbox.
class MOZ_RAII SandboxGlobalHolder final {
public:
nsIGlobalObject* GetOrCreateSandboxGlobalObject(nsIPrincipal* aPrincipal) {
MOZ_ASSERT(aPrincipal);
nsAutoCString origin;
nsresult rv = aPrincipal->GetOrigin(origin);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
nsCOMPtr<nsIGlobalObject> globalObject = mGlobals.Get(origin);
if (globalObject) {
return globalObject;
}
nsIXPConnect* xpc = nsContentUtils::XPConnect();
MOZ_ASSERT(xpc, "This should never be null!");
AutoJSAPI jsapi;
jsapi.Init();
JSContext* cx = jsapi.cx();
JS::Rooted<JSObject*> sandbox(cx);
rv = xpc->CreateSandbox(cx, aPrincipal, sandbox.address());
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
// The JSContext is not in a realm, so CreateSandbox returned an unwrapped
// global.
MOZ_ASSERT(JS_IsGlobalObject(sandbox));
globalObject = xpc::NativeGlobal(sandbox);
if (NS_WARN_IF(!globalObject)) {
return nullptr;
}
if (NS_WARN_IF(!mGlobals.Put(origin, globalObject, fallible))) {
return nullptr;
}
return globalObject;
}
private:
nsInterfaceHashtable<nsCStringHashKey, nsIGlobalObject> mGlobals;
};
struct StringWriteFunc final : public JSONWriteFunc {
nsACString&
mBuffer; // The lifetime of the struct must be bound to the buffer
explicit StringWriteFunc(nsACString& aBuffer) : mBuffer(aBuffer) {}
void Write(const char* aStr) override { mBuffer.Append(aStr); }
};
class ReportJSONWriter final : public JSONWriter {
public:
explicit ReportJSONWriter(nsACString& aOutput)
: JSONWriter(MakeUnique<StringWriteFunc>(aOutput)) {}
void JSONProperty(const char* aProperty, const char* aJSON) {
Separator();
PropertyNameAndColon(aProperty);
mWriter->Write(aJSON);
}
};
void SendReport(ReportDeliver::ReportData& aReportData,
SandboxGlobalHolder& aHolder) {
nsCOMPtr<nsIGlobalObject> globalObject =
aHolder.GetOrCreateSandboxGlobalObject(aReportData.mPrincipal);
if (NS_WARN_IF(!globalObject)) {
return;
}
// The body
nsAutoCString body;
ReportJSONWriter w(body);
w.Start();
w.IntProperty(
"age", (TimeStamp::Now() - aReportData.mCreationTime).ToMilliseconds());
w.StringProperty("type", NS_ConvertUTF16toUTF8(aReportData.mType).get());
w.StringProperty("url", NS_ConvertUTF16toUTF8(aReportData.mURL).get());
w.StringProperty("user_agent",
NS_ConvertUTF16toUTF8(aReportData.mUserAgent).get());
w.JSONProperty("body", aReportData.mReportBodyJSON.get());
w.End();
// The body as stream
nsCOMPtr<nsIInputStream> streamBody;
nsresult rv = NS_NewCStringInputStream(getter_AddRefs(streamBody), body);
// Headers
IgnoredErrorResult error;
RefPtr<InternalHeaders> internalHeaders =
new InternalHeaders(HeadersGuardEnum::Request);
internalHeaders->Set(NS_LITERAL_CSTRING("Content-Type"),
NS_LITERAL_CSTRING("application/reports+json"), error);
if (NS_WARN_IF(error.Failed())) {
return;
}
// URL and fragments
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri), aReportData.mEndpointURL);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
nsCOMPtr<nsIURI> uriClone;
rv = NS_GetURIWithoutRef(uri, getter_AddRefs(uriClone));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
nsAutoCString uriSpec;
rv = uriClone->GetSpec(uriSpec);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
nsAutoCString uriFragment;
rv = uri->GetRef(uriFragment);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
RefPtr<InternalRequest> internalRequest =
new InternalRequest(uriSpec, uriFragment);
internalRequest->SetMethod(NS_LITERAL_CSTRING("POST"));
internalRequest->SetBody(streamBody, body.Length());
internalRequest->SetHeaders(internalHeaders);
internalRequest->SetSkipServiceWorker();
// TODO: internalRequest->SetContentPolicyType(TYPE_REPORT);
internalRequest->SetMode(RequestMode::Cors);
internalRequest->SetCredentialsMode(RequestCredentials::Include);
RefPtr<Request> request = new Request(globalObject, internalRequest, nullptr);
RequestOrUSVString fetchInput;
fetchInput.SetAsRequest() = request;
RefPtr<Promise> promise = FetchRequest(
globalObject, fetchInput, RequestInit(), CallerType::NonSystem, error);
if (error.Failed()) {
++aReportData.mFailures;
if (gReportDeliver) {
gReportDeliver->AppendReportData(aReportData);
}
return;
}
RefPtr<ReportFetchHandler> handler = new ReportFetchHandler(aReportData);
promise->AppendNativeHandler(handler);
}
} // namespace
/* static */
void ReportDeliver::Record(nsPIDOMWindowInner* aWindow, const nsAString& aType,
const nsAString& aGroupName, const nsAString& aURL,
ReportBody* aBody) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aWindow);
MOZ_ASSERT(aBody);
nsAutoCString reportBodyJSON;
ReportJSONWriter w(reportBodyJSON);
w.Start();
aBody->ToJSON(w);
w.End();
nsCOMPtr<nsIPrincipal> principal =
nsGlobalWindowInner::Cast(aWindow)->GetPrincipal();
if (NS_WARN_IF(!principal)) {
return;
}
mozilla::ipc::PrincipalInfo principalInfo;
nsresult rv = PrincipalToPrincipalInfo(principal, &principalInfo);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
mozilla::ipc::PBackgroundChild* actorChild =
mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
PEndpointForReportChild* actor =
actorChild->SendPEndpointForReportConstructor(nsString(aGroupName),
principalInfo);
if (NS_WARN_IF(!actor)) {
return;
}
ReportData data;
data.mType = aType;
data.mGroupName = aGroupName;
data.mURL = aURL;
data.mCreationTime = TimeStamp::Now();
data.mReportBodyJSON = reportBodyJSON;
data.mPrincipal = principal;
data.mFailures = 0;
Navigator* navigator = aWindow->Navigator();
MOZ_ASSERT(navigator);
IgnoredErrorResult error;
navigator->GetUserAgent(data.mUserAgent, CallerType::NonSystem, error);
if (NS_WARN_IF(error.Failed())) {
return;
}
static_cast<EndpointForReportChild*>(actor)->Initialize(data);
}
/* static */
void ReportDeliver::Fetch(const ReportData& aReportData) {
if (!gReportDeliver) {
RefPtr<ReportDeliver> rd = new ReportDeliver();
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (NS_WARN_IF(!obs)) {
return;
}
obs->AddObserver(rd, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
gReportDeliver = rd;
}
gReportDeliver->AppendReportData(aReportData);
}
void ReportDeliver::AppendReportData(const ReportData& aReportData) {
if (aReportData.mFailures >
StaticPrefs::dom_reporting_delivering_maxFailures()) {
return;
}
if (NS_WARN_IF(!mReportQueue.AppendElement(aReportData, fallible))) {
return;
}
while (mReportQueue.Length() >
StaticPrefs::dom_reporting_delivering_maxReports()) {
mReportQueue.RemoveElementAt(0);
}
if (!mTimer) {
uint32_t timeout = StaticPrefs::dom_reporting_delivering_timeout() * 1000;
nsresult rv = NS_NewTimerWithCallback(
getter_AddRefs(mTimer), this, timeout, nsITimer::TYPE_ONE_SHOT,
SystemGroup::EventTargetFor(TaskCategory::Other));
Unused << NS_WARN_IF(NS_FAILED(rv));
}
}
NS_IMETHODIMP
ReportDeliver::Notify(nsITimer* aTimer) {
mTimer = nullptr;
nsTArray<ReportData> reports;
reports.SwapElements(mReportQueue);
SandboxGlobalHolder holder;
for (ReportData& report : reports) {
SendReport(report, holder);
}
return NS_OK;
}
NS_IMETHODIMP
ReportDeliver::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) {
MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID));
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (NS_WARN_IF(!obs)) {
return NS_OK;
}
obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
if (mTimer) {
mTimer->Cancel();
mTimer = nullptr;
}
gReportDeliver = nullptr;
return NS_OK;
}
ReportDeliver::ReportDeliver() = default;
ReportDeliver::~ReportDeliver() = default;
NS_INTERFACE_MAP_BEGIN(ReportDeliver)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(ReportDeliver)
NS_IMPL_RELEASE(ReportDeliver)
} // namespace dom
} // namespace mozilla