зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1531405 - Gather telemetry on cross-origin style sheet Content-Type headers r=JuniorHsu
Differential Revision: https://phabricator.services.mozilla.com/D47906 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
bb14f09ee9
Коммит
bbd9200052
|
@ -1767,6 +1767,8 @@ nsresult nsHttpChannel::CallOnStartRequest() {
|
|||
return mStatus;
|
||||
}
|
||||
|
||||
ReportContentTypeTelemetryForCrossOriginStylesheets();
|
||||
|
||||
mTracingEnabled = false;
|
||||
|
||||
// Ensure mListener->OnStartRequest will be invoked before exiting
|
||||
|
@ -8074,6 +8076,88 @@ nsresult nsHttpChannel::ContinueOnStopRequestAfterAuthRetry(
|
|||
return ContinueOnStopRequest(aStatus, aIsFromNet, aContentComplete);
|
||||
}
|
||||
|
||||
class HeaderValuesVisitor final : public nsIHttpHeaderVisitor {
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIHTTPHEADERVISITOR
|
||||
|
||||
nsTArray<nsCString>& Get() { return mHeaderValues; }
|
||||
|
||||
private:
|
||||
nsTArray<nsCString> mHeaderValues;
|
||||
~HeaderValuesVisitor() = default;
|
||||
};
|
||||
|
||||
NS_IMETHODIMP HeaderValuesVisitor::VisitHeader(const nsACString& aHeader,
|
||||
const nsACString& aValue) {
|
||||
mHeaderValues.AppendElement(aValue);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(HeaderValuesVisitor, nsIHttpHeaderVisitor)
|
||||
|
||||
void nsHttpChannel::ReportContentTypeTelemetryForCrossOriginStylesheets() {
|
||||
// Only record for successful requests.
|
||||
if (NS_FAILED(mStatus)) {
|
||||
return;
|
||||
}
|
||||
// Only record for stylesheet requests
|
||||
if (!mContentTypeHint.EqualsLiteral("text/css") &&
|
||||
mLoadInfo->GetExternalContentPolicyType() !=
|
||||
nsIContentPolicy::TYPE_STYLESHEET) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> docURI = mLoadInfo->LoadingPrincipal()->GetURI();
|
||||
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
|
||||
bool isPrivateWin = mLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
|
||||
nsresult rv = ssm->CheckSameOriginURI(mURI, docURI, false, isPrivateWin);
|
||||
bool isSameOrigin = NS_SUCCEEDED(rv);
|
||||
|
||||
// Only report cross origin stylesheet requests
|
||||
if (isSameOrigin) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<HeaderValuesVisitor> visitor = new HeaderValuesVisitor();
|
||||
rv = GetOriginalResponseHeader(NS_LITERAL_CSTRING("Content-Type"), visitor);
|
||||
|
||||
auto allEmptyValues = [&visitor]() {
|
||||
for (const auto& v : visitor->Get()) {
|
||||
if (!v.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
nsAutoCString contentType;
|
||||
if (mResponseHead) {
|
||||
mResponseHead->ContentType(contentType);
|
||||
}
|
||||
|
||||
typedef Telemetry::LABELS_NETWORK_CROSS_ORIGIN_STYLESHEET_CONTENT_TYPE
|
||||
CTLabels;
|
||||
CTLabels label;
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
// No Content-Type header
|
||||
label = CTLabels::NoHeader;
|
||||
} else if (allEmptyValues()) {
|
||||
// Any/all Content-Type headers are empty
|
||||
label = CTLabels::EmptyHeader;
|
||||
} else if (contentType.IsEmpty()) {
|
||||
// failed to parse
|
||||
label = CTLabels::FailedToParse;
|
||||
} else if (contentType.EqualsLiteral("text/css")) {
|
||||
// text/css
|
||||
label = CTLabels::ParsedTextCSS;
|
||||
} else {
|
||||
// some other value
|
||||
label = CTLabels::ParsedOther;
|
||||
}
|
||||
|
||||
Telemetry::AccumulateCategorical(label);
|
||||
}
|
||||
|
||||
nsresult nsHttpChannel::ContinueOnStopRequest(nsresult aStatus, bool aIsFromNet,
|
||||
bool aContentComplete) {
|
||||
LOG(
|
||||
|
|
|
@ -504,6 +504,8 @@ class nsHttpChannel final : public HttpBaseChannel,
|
|||
rv == NS_ERROR_PORT_ACCESS_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
void ReportContentTypeTelemetryForCrossOriginStylesheets();
|
||||
|
||||
// Report net vs cache time telemetry
|
||||
void ReportNetVSCacheTelemetry();
|
||||
int64_t ComputeTelemetryBucketNumber(int64_t difftime_ms);
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
* 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/. */
|
||||
|
||||
const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
|
||||
|
||||
var charset = {};
|
||||
var hadCharset = {};
|
||||
var type;
|
||||
|
@ -21,7 +23,7 @@ function check(aType, aCharset, aHadCharset) {
|
|||
reset();
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
add_task(function test_parseResponseContentType() {
|
||||
var netutil = Cc["@mozilla.org/network/util;1"].getService(Ci.nsINetUtil);
|
||||
|
||||
type = netutil.parseRequestContentType("text/html", charset, hadCharset);
|
||||
|
@ -360,4 +362,156 @@ function run_test() {
|
|||
hadCharset
|
||||
);
|
||||
check("text/plain", "'UTF-8'", true);
|
||||
});
|
||||
|
||||
function checkSnapshotValue(s, name, expectedValue) {
|
||||
function telemetryCategoryIndex(name) {
|
||||
let labels = [
|
||||
"NoHeader",
|
||||
"EmptyHeader",
|
||||
"FailedToParse",
|
||||
"ParsedTextCSS",
|
||||
"ParsedOther",
|
||||
];
|
||||
for (let i in labels) {
|
||||
if (labels[i] == name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
ok(false, "invalid category");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
Assert.deepEqual(s.values, {}, "No probes should be recorded");
|
||||
return;
|
||||
}
|
||||
|
||||
let index = telemetryCategoryIndex(name);
|
||||
equal(
|
||||
s.values[index],
|
||||
expectedValue,
|
||||
`Checking the value of ${name} to be ${expectedValue} - recorded probes: ${JSON.stringify(
|
||||
s.values
|
||||
)} index: ${index}`
|
||||
);
|
||||
|
||||
for (let i in s.values) {
|
||||
if (i != index && s.values[i] != 0) {
|
||||
equal(s.values[i], 0, `The record at ${i} should be 0`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function makeChan(url, type = Ci.nsIContentPolicy.TYPE_STYLESHEET) {
|
||||
let principal = Services.scriptSecurityManager.createContentPrincipal(
|
||||
Services.io.newURI("http://example.com"),
|
||||
{
|
||||
privateBrowsingId: 0,
|
||||
}
|
||||
);
|
||||
|
||||
return NetUtil.newChannel({
|
||||
uri: url,
|
||||
loadingPrincipal: principal,
|
||||
contentPolicyType: type,
|
||||
securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
||||
}).QueryInterface(Ci.nsIHttpChannel);
|
||||
}
|
||||
|
||||
add_task(async function check_channels() {
|
||||
do_get_profile();
|
||||
|
||||
let contentType = "text/css;hi";
|
||||
let content = `.identity-color-blue {
|
||||
--identity-tab-color: #37adff;
|
||||
--identity-icon-color: #37adff;
|
||||
}`;
|
||||
|
||||
function handler(metadata, response) {
|
||||
if (contentType !== undefined) {
|
||||
response.setHeader("Content-Type", contentType);
|
||||
}
|
||||
response.bodyOutputStream.write(content, content.length);
|
||||
}
|
||||
let httpserv = new HttpServer();
|
||||
httpserv.registerPathHandler("/", handler);
|
||||
httpserv.start(-1);
|
||||
const URL = `http://localhost:${httpserv.identity.primaryPort}/`;
|
||||
|
||||
let h = Services.telemetry.getHistogramById(
|
||||
"NETWORK_CROSS_ORIGIN_STYLESHEET_CONTENT_TYPE"
|
||||
);
|
||||
h.clear();
|
||||
|
||||
function make_test(
|
||||
inContentType,
|
||||
expectedContentType,
|
||||
expectedProbe,
|
||||
typeHint,
|
||||
contentPolicyType
|
||||
) {
|
||||
return new Promise(resolve => {
|
||||
contentType = inContentType;
|
||||
let channel = makeChan(URL, contentPolicyType);
|
||||
if (typeHint) {
|
||||
channel.contentType = typeHint;
|
||||
}
|
||||
let p = new Promise(resolve1 =>
|
||||
channel.asyncOpen(new ChannelListener(resolve1))
|
||||
);
|
||||
p.then(() => {
|
||||
equal(channel.contentType, expectedContentType);
|
||||
checkSnapshotValue(h.snapshot(), expectedProbe, 1);
|
||||
h.clear();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// If contentTypeHint != text/css and contentPolicy is not TYPE_STYLESHEET
|
||||
// no telemetry probes should be recorded.
|
||||
await make_test(
|
||||
undefined,
|
||||
"text/plain",
|
||||
undefined,
|
||||
undefined,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER
|
||||
);
|
||||
await make_test(
|
||||
undefined,
|
||||
"text/plain",
|
||||
undefined,
|
||||
"text/plain",
|
||||
Ci.nsIContentPolicy.TYPE_OTHER
|
||||
);
|
||||
|
||||
// We should record a probe if the contentTypeHint is text/css
|
||||
await make_test(
|
||||
undefined,
|
||||
"text/css",
|
||||
"NoHeader",
|
||||
"text/css",
|
||||
Ci.nsIContentPolicy.TYPE_OTHER
|
||||
);
|
||||
|
||||
// The rest of the tests will default to contentPolicy = TYPE_STYLESHEET
|
||||
// One telemetry probe should be recorded for each.
|
||||
await make_test(undefined, "text/plain", "NoHeader");
|
||||
await make_test(undefined, "text/abc", "NoHeader", "text/abc");
|
||||
await make_test(undefined, "text/css", "NoHeader", "text/css");
|
||||
await make_test("", "text/plain", "EmptyHeader");
|
||||
await make_test("", "text/abc", "EmptyHeader", "text/abc");
|
||||
await make_test("", "text/css", "EmptyHeader", "text/css");
|
||||
await make_test("failure", "text/plain", "FailedToParse");
|
||||
await make_test("failure", "text/abc", "FailedToParse", "text/abc");
|
||||
await make_test("failure", "text/css", "FailedToParse", "text/css");
|
||||
await make_test("text/css", "text/css", "ParsedTextCSS");
|
||||
await make_test("text/css;hi", "text/css", "ParsedTextCSS");
|
||||
await make_test("text/plain", "text/plain", "ParsedOther");
|
||||
await make_test("text/abc", "text/abc", "ParsedOther");
|
||||
await make_test("text/abc;hi", "text/abc", "ParsedOther");
|
||||
await make_test("text/abc", "text/abc", "ParsedOther", "text/css");
|
||||
|
||||
await new Promise(resolve => httpserv.stop(resolve));
|
||||
});
|
||||
|
|
|
@ -2486,6 +2486,17 @@
|
|||
"description": "Count of the HTTP redirection that triggered by top-level document or by subresource, keyed by the URL scheme redirected to.",
|
||||
"labels": ["topLevel", "subresource"]
|
||||
},
|
||||
"NETWORK_CROSS_ORIGIN_STYLESHEET_CONTENT_TYPE": {
|
||||
"record_in_processes": ["main"],
|
||||
"products": ["firefox", "fennec", "geckoview"],
|
||||
"alert_emails": ["necko@mozilla.com", "vgosu@mozilla.com"],
|
||||
"bug_numbers": [1531405],
|
||||
"expires_in_version": "75",
|
||||
"releaseChannelCollection": "opt-out",
|
||||
"kind": "categorical",
|
||||
"description": "Records whether a Content-Type header was present when loading a cross origin stylesheet, and if it was parsed successfully.",
|
||||
"labels": ["NoHeader", "EmptyHeader", "FailedToParse", "ParsedTextCSS", "ParsedOther"]
|
||||
},
|
||||
"HTTP_AUTH_DIALOG_STATS_3": {
|
||||
"record_in_processes": ["main", "content"],
|
||||
"products": ["firefox", "fennec", "geckoview"],
|
||||
|
|
Загрузка…
Ссылка в новой задаче