Bug 1919544 - Do not HTTPS-First upgrade URLs with http:// scheme r=mak,necko-reviewers,urlbar-reviewers,tabbrowser-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D228588
This commit is contained in:
Simon Friedberger 2024-11-13 17:15:57 +00:00
parent 0cf5041bfe
commit ee24230231
28 changed files with 284 additions and 135 deletions

View File

@ -775,7 +775,7 @@ var gBrowserInit = {
let globalHistoryOptions = undefined;
let triggeringRemoteType = undefined;
let forceAllowDataURI = false;
let wasSchemelessInput = false;
let schemelessInput = Ci.nsILoadInfo.SchemelessInputTypeUnset;
if (window.arguments[1]) {
if (!(window.arguments[1] instanceof Ci.nsIPropertyBag2)) {
throw new Error(
@ -814,9 +814,9 @@ var gBrowserInit = {
forceAllowDataURI =
extraOptions.getPropertyAsBool("forceAllowDataURI");
}
if (extraOptions.hasKey("wasSchemelessInput")) {
wasSchemelessInput =
extraOptions.getPropertyAsBool("wasSchemelessInput");
if (extraOptions.hasKey("schemelessInput")) {
schemelessInput =
extraOptions.getPropertyAsUint32("schemelessInput");
}
}
@ -841,7 +841,7 @@ var gBrowserInit = {
fromExternal,
globalHistoryOptions,
triggeringRemoteType,
wasSchemelessInput,
schemelessInput,
});
} catch (e) {
console.error(e);

View File

@ -2661,7 +2661,7 @@
insertTab = true,
globalHistoryOptions,
triggeringRemoteType,
wasSchemelessInput,
schemelessInput,
hasValidUserGestureActivation = false,
textDirectiveUserActivation = false,
} = {}
@ -2852,7 +2852,7 @@
csp,
globalHistoryOptions,
triggeringRemoteType,
wasSchemelessInput,
schemelessInput,
hasValidUserGestureActivation:
hasValidUserGestureActivation ||
!!openWindowInfo?.hasValidUserGestureActivation,
@ -3261,7 +3261,7 @@
csp,
globalHistoryOptions,
triggeringRemoteType,
wasSchemelessInput,
schemelessInput,
hasValidUserGestureActivation,
textDirectiveUserActivation,
}
@ -3327,7 +3327,7 @@
csp,
globalHistoryOptions,
triggeringRemoteType,
wasSchemelessInput,
schemelessInput,
hasValidUserGestureActivation,
textDirectiveUserActivation,
});
@ -6428,13 +6428,14 @@
aEvent.preventDefault();
break;
}
case "visibilitychange":
case "visibilitychange": {
const inactive = document.hidden;
if (!this._switcher) {
this.selectedBrowser.preserveLayers(inactive);
this.selectedBrowser.docShellIsActive = !inactive;
}
break;
}
case "TabGroupCreate":
if (aEvent.detail.showCreateUI) {
this.tabGroupMenu.openCreateModal(aEvent.target);
@ -8462,12 +8463,13 @@ var TabContextMenu = {
this.contextTab.removeEventListener("TabAttrModified", this);
}
break;
case "TabAttrModified":
case "TabAttrModified": {
let tab = aEvent.target;
this._updateToggleMuteMenuItems(tab, attr =>
aEvent.detail.changed.includes(attr)
);
break;
}
}
},

View File

@ -865,7 +865,9 @@ export class UrlbarInput {
if (isValidUrl) {
// Annotate if the untrimmed value contained a scheme, to later potentially
// be upgraded by schemeless HTTPS-First.
openParams.wasSchemelessInput = this.#isSchemeless(this.untrimmedValue);
openParams.schemelessInput = this.#getSchemelessInput(
this.untrimmedValue
);
this._loadURL(url, event, where, openParams);
return;
}
@ -927,7 +929,7 @@ export class UrlbarInput {
// `uri` is not a search engine url, so we annotate if the untrimmed
// value contained a scheme, to potentially be later upgraded by
// schemeless HTTPS-First.
openParams.wasSchemelessInput = this.#isSchemeless(
openParams.schemelessInput = this.#getSchemelessInput(
this.untrimmedValue
);
}
@ -1143,7 +1145,7 @@ export class UrlbarInput {
}
// Annotate if the untrimmed value contained a scheme, to later potentially
// be upgraded by schemeless HTTPS-First.
openParams.wasSchemelessInput = this.#isSchemeless(
openParams.schemelessInput = this.#getSchemelessInput(
originalUntrimmedValue
);
}
@ -2603,7 +2605,8 @@ export class UrlbarInput {
result.heuristic &&
result.payload.url.startsWith("http://") &&
this.window.gBrowser.userTypedValue &&
this.#isSchemeless(this.window.gBrowser.userTypedValue);
this.#getSchemelessInput(this.window.gBrowser.userTypedValue) ==
Ci.nsILoadInfo.SchemelessInputTypeSchemeless;
if (!stripHttp) {
return url;
}
@ -3170,7 +3173,7 @@ export class UrlbarInput {
* The POST data associated with a search submission.
* @param {boolean} [params.allowInheritPrincipal]
* Whether the principal can be inherited.
* @param {boolean} [params.wasSchemelessInput]
* @param {SchemelessInputType} [params.schemelessInput]
* Whether the search/URL term was without an explicit scheme.
* @param {object} [resultDetails]
* Details of the selected result, if any.
@ -4705,14 +4708,19 @@ export class UrlbarInput {
/**
* @param {string} value A untrimmed address bar input.
* @returns {boolean}
* `true` if the input doesn't start with a scheme relevant for
* @returns {SchemelessInputType}
* Returns `Ci.nsILoadInfo.SchemelessInputTypeSchemeless` if the
* input doesn't start with a scheme relevant for
* schemeless HTTPS-First (http://, https:// and file://).
* Returns `Ci.nsILoadInfo.SchemelessInputTypeSchemeful` if it does
* have a scheme.
*/
#isSchemeless(value) {
#getSchemelessInput(value) {
return ["http://", "https://", "file://"].every(
scheme => !value.trim().startsWith(scheme)
);
)
? Ci.nsILoadInfo.SchemelessInputTypeSchemeless
: Ci.nsILoadInfo.SchemelessInputTypeSchemeful;
}
get #isOpenedPageInBlankTargetLoading() {

View File

@ -193,7 +193,9 @@ export class UrlbarValueFormatter {
// The protocol has been trimmed, so we add it back.
url = trimmedProtocol + inputValue;
trimmedLength = trimmedProtocol.length;
} else if (uriInfo.wasSchemelessInput) {
} else if (
uriInfo.schemelessInput == Ci.nsILoadInfo.SchemelessInputTypeSchemeless
) {
// The original string didn't have a protocol, but it was identified as
// a URL. It's not important which scheme we use for parsing, so we'll
// just copy URIFixup.

View File

@ -129,11 +129,8 @@ function openInWindow(url, params, sourceWindow) {
);
}
}
if (params.wasSchemelessInput !== undefined) {
extraOptions.setPropertyAsBool(
"wasSchemelessInput",
params.wasSchemelessInput
);
if (params.schemelessInput !== undefined) {
extraOptions.setPropertyAsUint32("schemelessInput", params.schemelessInput);
}
var allowThirdPartyFixupSupports = Cc[
@ -266,7 +263,7 @@ function openInCurrentTab(targetBrowser, url, uriObj, params) {
hasValidUserGestureActivation,
globalHistoryOptions,
triggeringRemoteType,
wasSchemelessInput,
schemelessInput,
} = params;
targetBrowser.fixupAndLoadURIString(url, {
@ -279,7 +276,7 @@ function openInCurrentTab(targetBrowser, url, uriObj, params) {
hasValidUserGestureActivation,
globalHistoryOptions,
triggeringRemoteType,
wasSchemelessInput,
schemelessInput,
});
params.resolveOnContentBrowserCreated?.(targetBrowser);
}
@ -371,7 +368,7 @@ export const URILoadingHelper = {
* @param {string} params.charset
* Character set to use for the load. Only honoured for tabs.
* Legacy argument - do not use.
* @param {string} params.wasSchemelessInput
* @param {SchemelessInputType} params.schemelessInput
* Whether the search/URL term was without an explicit scheme.
*
* Options relating to security, whether the load is allowed to happen,
@ -563,7 +560,7 @@ export const URILoadingHelper = {
case "tabshifted":
loadInBackground = !loadInBackground;
// fall through
case "tab":
case "tab": {
focusUrlBar =
!loadInBackground &&
w.isBlankPageURL(url) &&
@ -589,7 +586,7 @@ export const URILoadingHelper = {
openerBrowser: params.openerBrowser,
fromExternal: params.fromExternal,
globalHistoryOptions,
wasSchemelessInput: params.wasSchemelessInput,
schemelessInput: params.schemelessInput,
hasValidUserGestureActivation,
textDirectiveUserActivation,
});
@ -616,6 +613,7 @@ export const URILoadingHelper = {
);
}
break;
}
}
if (

View File

@ -396,7 +396,7 @@ URIFixup.prototype = {
if (uriWithProtocol) {
info.fixedURI = uriWithProtocol;
info.fixupChangedProtocol = true;
info.wasSchemelessInput = true;
info.schemelessInput = Ci.nsILoadInfo.SchemelessInputTypeSchemeless;
maybeSetAlternateFixedURI(info, fixupFlags);
info.preferredURI = info.fixedURI;
// Check if it's a forced visit. The user can enforce a visit by
@ -421,9 +421,9 @@ URIFixup.prototype = {
// Memoize the public suffix check, since it may be expensive and should
// only run once when necessary.
let suffixInfo;
function checkSuffix(info) {
function checkSuffix(i) {
if (!suffixInfo) {
suffixInfo = checkAndFixPublicSuffix(info);
suffixInfo = checkAndFixPublicSuffix(i);
}
return suffixInfo;
}
@ -699,11 +699,11 @@ URIFixupInfo.prototype = {
return this._keywordAsSent || "";
},
set wasSchemelessInput(changed) {
this._wasSchemelessInput = changed;
set schemelessInput(changed) {
this._schemelessInput = changed;
},
get wasSchemelessInput() {
return !!this._wasSchemelessInput;
get schemelessInput() {
return this._schemelessInput ?? Ci.nsILoadInfo.SchemelessInputTypeUnset;
},
set fixupChangedProtocol(changed) {

View File

@ -5872,7 +5872,7 @@ already_AddRefed<nsIURI> nsDocShell::AttemptURIFixup(
const mozilla::Maybe<nsCString>& aOriginalURIString, uint32_t aLoadType,
bool aIsTopFrame, bool aAllowKeywordFixup, bool aUsePrivateBrowsing,
bool aNotifyKeywordSearchLoading, nsIInputStream** aNewPostData,
bool* outWasSchemelessInput) {
nsILoadInfo::SchemelessInputType* outSchemelessInput) {
if (aStatus != NS_ERROR_UNKNOWN_HOST && aStatus != NS_ERROR_NET_RESET &&
aStatus != NS_ERROR_CONNECTION_REFUSED &&
aStatus !=
@ -5958,7 +5958,7 @@ already_AddRefed<nsIURI> nsDocShell::AttemptURIFixup(
}
if (info) {
info->GetPreferredURI(getter_AddRefs(newURI));
info->GetWasSchemelessInput(outWasSchemelessInput);
info->GetSchemelessInput(outSchemelessInput);
if (newURI) {
info->GetKeywordAsSent(keywordAsSent);
info->GetKeywordProviderName(keywordProviderName);

View File

@ -445,7 +445,7 @@ class nsDocShell final : public nsDocLoader,
bool aIsTopFrame, bool aAllowKeywordFixup, bool aUsePrivateBrowsing,
bool aNotifyKeywordSearchLoading = false,
nsIInputStream** aNewPostData = nullptr,
bool* outWasSchemelessInput = nullptr);
nsILoadInfo::SchemelessInputType* outSchemelessInput = nullptr);
static already_AddRefed<nsIURI> MaybeFixBadCertDomainErrorURI(
nsIChannel* aChannel, nsIURI* aUrl);

View File

@ -7,6 +7,7 @@
#include "nsDocShellLoadState.h"
#include "nsIDocShell.h"
#include "nsDocShell.h"
#include "nsILoadInfo.h"
#include "nsIProtocolHandler.h"
#include "nsISHEntry.h"
#include "nsIURIFixup.h"
@ -91,7 +92,7 @@ nsDocShellLoadState::nsDocShellLoadState(
mTriggeringWindowId = aLoadState.TriggeringWindowId();
mTriggeringStorageAccess = aLoadState.TriggeringStorageAccess();
mTriggeringRemoteType = aLoadState.TriggeringRemoteType();
mWasSchemelessInput = aLoadState.WasSchemelessInput();
mSchemelessInput = aLoadState.SchemelessInput();
mHttpsUpgradeTelemetry = aLoadState.HttpsUpgradeTelemetry();
mCsp = aLoadState.Csp();
mOriginalURIString = aLoadState.OriginalURIString();
@ -199,7 +200,7 @@ nsDocShellLoadState::nsDocShellLoadState(const nsDocShellLoadState& aOther)
mUnstrippedURI(aOther.mUnstrippedURI),
mRemoteTypeOverride(aOther.mRemoteTypeOverride),
mTriggeringRemoteType(aOther.mTriggeringRemoteType),
mWasSchemelessInput(aOther.mWasSchemelessInput),
mSchemelessInput(aOther.mSchemelessInput),
mHttpsUpgradeTelemetry(aOther.mHttpsUpgradeTelemetry) {
MOZ_DIAGNOSTIC_ASSERT(
XRE_IsParentProcess(),
@ -244,7 +245,7 @@ nsDocShellLoadState::nsDocShellLoadState(nsIURI* aURI, uint64_t aLoadIdentifier)
mTriggeringRemoteType(XRE_IsContentProcess()
? ContentChild::GetSingleton()->GetRemoteType()
: NOT_REMOTE_TYPE),
mWasSchemelessInput(false) {
mSchemelessInput(nsILoadInfo::SchemelessInputTypeUnset) {
MOZ_ASSERT(aURI, "Cannot create a LoadState with a null URI!");
// For https telemetry we set a flag indicating whether the load is https.
@ -501,7 +502,8 @@ nsresult nsDocShellLoadState::CreateFromLoadURIOptions(
aLoadURIOptions.mRemoteTypeOverride.Value());
}
loadState->SetWasSchemelessInput(aLoadURIOptions.mWasSchemelessInput);
loadState->SetSchemelessInput(static_cast<nsILoadInfo::SchemelessInputType>(
aLoadURIOptions.mSchemelessInput));
loadState.forget(aResult);
return NS_OK;
@ -1334,7 +1336,7 @@ DocShellLoadStateInit nsDocShellLoadState::Serialize(
loadState.TriggeringWindowId() = mTriggeringWindowId;
loadState.TriggeringStorageAccess() = mTriggeringStorageAccess;
loadState.TriggeringRemoteType() = mTriggeringRemoteType;
loadState.WasSchemelessInput() = mWasSchemelessInput;
loadState.SchemelessInput() = mSchemelessInput;
loadState.HttpsUpgradeTelemetry() = mHttpsUpgradeTelemetry;
loadState.Csp() = mCsp;
loadState.OriginalURIString() = mOriginalURIString;

View File

@ -10,6 +10,8 @@
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/SessionHistoryEntry.h"
#include "nsILoadInfo.h"
// Helper Classes
#include "mozilla/Maybe.h"
#include "nsCOMPtr.h"
@ -335,11 +337,13 @@ class nsDocShellLoadState final {
void SetRemoteTypeOverride(const nsCString& aRemoteTypeOverride);
void SetWasSchemelessInput(bool aWasSchemelessInput) {
mWasSchemelessInput = aWasSchemelessInput;
void SetSchemelessInput(nsILoadInfo::SchemelessInputType aSchemelessInput) {
mSchemelessInput = aSchemelessInput;
}
bool GetWasSchemelessInput() { return mWasSchemelessInput; }
nsILoadInfo::SchemelessInputType GetSchemelessInput() {
return mSchemelessInput;
}
void SetHttpsUpgradeTelemetry(
nsILoadInfo::HTTPSUpgradeTelemetryType aHttpsUpgradeTelemetry) {
@ -627,8 +631,9 @@ class nsDocShellLoadState final {
// Remote type of the process which originally requested the load.
nsCString mTriggeringRemoteType;
// if the to-be-loaded address had it protocol added through a fixup
bool mWasSchemelessInput = false;
// if the address had an intentional protocol
nsILoadInfo::SchemelessInputType mSchemelessInput =
nsILoadInfo::SchemelessInputTypeUnset;
// Solely for the use of collecting Telemetry for HTTPS upgrades.
nsILoadInfo::HTTPSUpgradeTelemetryType mHttpsUpgradeTelemetry =

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
#include "nsILoadInfo.idl"
interface nsIURI;
interface nsIInputStream;
@ -52,7 +53,7 @@ interface nsIURIFixupInfo : nsISupports
/**
* Whether there was no protocol at all and we had to add one in the first place.
*/
attribute boolean wasSchemelessInput;
attribute nsILoadInfo_SchemelessInputType schemelessInput;
/**
* Whether we changed the protocol instead of using one from the input as-is.

View File

@ -115,6 +115,7 @@ dictionary LoadURIOptions {
/**
* Whether the search/URL term was without an explicit scheme.
* This holds enum values for SchemelessInputType from nsILoadInfo.idl.
*/
boolean wasSchemelessInput = false;
octet schemelessInput = 0;
};

View File

@ -46,6 +46,7 @@ using nsSizeMode from "nsIWidgetListener.h";
using mozilla::ScrollbarPreference from "mozilla/ScrollbarPreferences.h";
using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
using nsILoadInfo::HTTPSUpgradeTelemetryType from "nsILoadInfo.h";
using nsILoadInfo::SchemelessInputType from "nsILoadInfo.h";
[RefCounted] using class nsIPrincipal from "nsIPrincipal.h";
using mozilla::dom::MaybeDiscardedBrowsingContext from "mozilla/dom/BrowsingContext.h";
[RefCounted] using class nsIURI from "nsIURI.h";
@ -215,7 +216,7 @@ struct DocShellLoadStateInit
bool TextDirectiveUserActivation;
bool AllowFocusMove;
bool IsFromProcessingFrameAttributes;
bool WasSchemelessInput;
SchemelessInputType SchemelessInput;
HTTPSUpgradeTelemetryType HttpsUpgradeTelemetry;
// Fields missing due to lack of need or serialization

View File

@ -20,6 +20,13 @@ struct ParamTraits<nsILoadInfo::HTTPSUpgradeTelemetryType>
nsILoadInfo::HTTPSUpgradeTelemetryType::NOT_INITIALIZED,
nsILoadInfo::HTTPSUpgradeTelemetryType::UPGRADE_EXCEPTION> {};
template <>
struct ParamTraits<nsILoadInfo::SchemelessInputType>
: public ContiguousEnumSerializerInclusive<
nsILoadInfo::SchemelessInputType,
nsILoadInfo::SchemelessInputType::SchemelessInputTypeUnset,
nsILoadInfo::SchemelessInputType::SchemelessInputTypeSchemeless> {};
} // namespace IPC
#endif // mozilla_dom_domsecurityipcutils_h

View File

@ -728,8 +728,7 @@ static void DebugDoContentSecurityCheck(nsIChannel* aChannel,
(" allowDeprecatedSystemRequests: %s\n",
aLoadInfo->GetAllowDeprecatedSystemRequests() ? "true" : "false"));
MOZ_LOG(sCSMLog, LogLevel::Verbose,
(" wasSchemeless: %s\n",
aLoadInfo->GetWasSchemelessInput() ? "true" : "false"));
(" schemelessInput: %d\n", aLoadInfo->GetSchemelessInput()));
// Log CSPrequestPrincipal
nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadInfo->GetCsp();

View File

@ -86,7 +86,8 @@ void nsHTTPSOnlyUtils::PotentiallyFireHttpRequestToShortenTimout(
// nothing to do here.
if ((!IsHttpsOnlyModeEnabled(isPrivateWin) &&
!IsHttpsFirstModeEnabled(isPrivateWin)) &&
!(loadInfo->GetWasSchemelessInput() &&
!(loadInfo->GetSchemelessInput() ==
nsILoadInfo::SchemelessInputTypeSchemeless &&
mozilla::StaticPrefs::dom_security_https_first_schemeless())) {
return;
}
@ -131,7 +132,8 @@ void nsHTTPSOnlyUtils::PotentiallyFireHttpRequestToShortenTimout(
// early if attempting to send a background request to a non standard port.
if (!mozilla::StaticPrefs::dom_security_https_first_for_custom_ports() &&
(IsHttpsFirstModeEnabled(isPrivateWin) ||
(loadInfo->GetWasSchemelessInput() &&
(loadInfo->GetSchemelessInput() ==
nsILoadInfo::SchemelessInputTypeSchemeless &&
mozilla::StaticPrefs::dom_security_https_first_schemeless()))) {
int32_t port = 0;
nsresult rv = channelURI->GetPort(&port);
@ -356,14 +358,16 @@ bool nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop(
/* static */
bool nsHTTPSOnlyUtils::ShouldUpgradeHttpsFirstRequest(nsIURI* aURI,
nsILoadInfo* aLoadInfo) {
MOZ_ASSERT(aURI->SchemeIs("http"), "how come the request is not 'http'?");
// 1. Check if HTTPS-First Mode is enabled
bool isPrivateWin = aLoadInfo->GetOriginAttributes().IsPrivateBrowsing();
if (!IsHttpsFirstModeEnabled(isPrivateWin) &&
!(aLoadInfo->GetWasSchemelessInput() &&
!(aLoadInfo->GetSchemelessInput() ==
nsILoadInfo::SchemelessInputTypeSchemeless &&
mozilla::StaticPrefs::dom_security_https_first_schemeless())) {
return false;
}
// 2. HTTPS-First only upgrades top-level loads (and speculative connections)
ExtContentPolicyType contentType = aLoadInfo->GetExternalContentPolicyType();
if (contentType != ExtContentPolicy::TYPE_DOCUMENT &&
@ -383,9 +387,17 @@ bool nsHTTPSOnlyUtils::ShouldUpgradeHttpsFirstRequest(nsIURI* aURI,
return false;
}
// 5. Make sure HTTPS-First does not upgrade custom ports when it is disabled
MOZ_ASSERT(aURI->SchemeIs("http"), "how come the request is not 'http'?");
// 5. Don't upgrade if the user explicitly provided a scheme
if (aLoadInfo->GetSchemelessInput() ==
nsILoadInfo::SchemelessInputTypeSchemeful &&
aLoadInfo->GetExternalContentPolicyType() !=
ExtContentPolicy::TYPE_SPECULATIVE &&
aURI->SchemeIs("http")) {
AddHTTPSFirstException(aURI, aLoadInfo);
return false;
}
// 6. Make sure HTTPS-First does not upgrade custom ports when it is disabled
if (!mozilla::StaticPrefs::dom_security_https_first_for_custom_ports()) {
int defaultPortforScheme = NS_GetDefaultPort("http");
// If no port is specified, then the API returns -1 to indicate the default
@ -398,14 +410,15 @@ bool nsHTTPSOnlyUtils::ShouldUpgradeHttpsFirstRequest(nsIURI* aURI,
}
}
// 6. Do not upgrade requests other than GET
// 7. Do not upgrade requests other than GET
if (!aLoadInfo->GetIsGETRequest()) {
return false;
}
// We can upgrade the request - let's log to the console and set the status
// so we know that we upgraded the request.
if (aLoadInfo->GetWasSchemelessInput() &&
if (aLoadInfo->GetSchemelessInput() ==
nsILoadInfo::SchemelessInputTypeSchemeless &&
!IsHttpsFirstModeEnabled(isPrivateWin)) {
nsAutoCString urlCString;
aURI->GetSpec(urlCString);
@ -575,7 +588,8 @@ void nsHTTPSOnlyUtils::UpdateLoadStateAfterHTTPSFirstDowngrade(
// the telemetry.
nsCOMPtr<nsIChannel> channel = aDocumentLoadListener->GetChannel();
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
if (loadInfo->GetWasSchemelessInput()) {
if (loadInfo->GetSchemelessInput() ==
nsILoadInfo::SchemelessInputTypeSchemeless) {
aLoadState->SetHttpsUpgradeTelemetry(
nsILoadInfo::HTTPS_FIRST_SCHEMELESS_UPGRADE_DOWNGRADE);
} else {
@ -593,7 +607,8 @@ void nsHTTPSOnlyUtils::UpdateLoadStateAfterHTTPSFirstDowngrade(
bool isPrivateWin = loadInfo->GetOriginAttributes().IsPrivateBrowsing();
bool isSchemeless =
loadInfo->GetWasSchemelessInput() &&
loadInfo->GetSchemelessInput() ==
nsILoadInfo::SchemelessInputTypeSchemeless &&
!nsHTTPSOnlyUtils::IsHttpsFirstModeEnabled(isPrivateWin);
nsresult channelStatus;
@ -635,7 +650,8 @@ void nsHTTPSOnlyUtils::SubmitHTTPSFirstTelemetry(
nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST) {
// Successfully upgraded load
if (aLoadInfo->GetWasSchemelessInput()) {
if (aLoadInfo->GetSchemelessInput() ==
nsILoadInfo::SchemelessInputTypeSchemeless) {
upgraded_schemeless.Add();
} else {
upgraded.Add();
@ -704,7 +720,8 @@ void nsHTTPSOnlyUtils::TestSitePermissionAndPotentiallyAddExemption(
bool isHttpsOnly = IsHttpsOnlyModeEnabled(isPrivateWin);
bool isHttpsFirst = IsHttpsFirstModeEnabled(isPrivateWin);
bool isSchemelessHttpsFirst =
(loadInfo->GetWasSchemelessInput() &&
(loadInfo->GetSchemelessInput() ==
nsILoadInfo::SchemelessInputTypeSchemeless &&
mozilla::StaticPrefs::dom_security_https_first_schemeless());
if (!isHttpsOnly && !isHttpsFirst && !isSchemelessHttpsFirst) {
return;

View File

@ -21,46 +21,7 @@ support-files = [
"file_download_attribute.sjs",
]
["browser_httpsfirst.js"]
support-files = ["file_httpsfirst_timeout_server.sjs"]
["browser_httpsfirst_console_logging.js"]
["browser_httpsfirst_speculative_connect.js"]
support-files = ["file_httpsfirst_speculative_connect.html"]
["browser_mixed_content_console.js"]
support-files = ["file_mixed_content_console.html"]
["browser_mixed_content_download.js"]
support-files = [
"download_page.html",
"download_server.sjs",
]
["browser_navigation.js"]
support-files = ["file_navigation.html"]
["browser_subdocument_downgrade.js"]
support-files = [
"file_empty.html",
"file_subdocument_downgrade.sjs",
]
["browser_schemeless.js"]
skip-if = ["!debug && tsan"] #Bug 1890170
["browser_slow_download.js"]
support-files = [
"file_slow_download.html",
"file_slow_download.sjs",
]
["browser_superfluos_auth.js"]
["browser_tlds.js"]
["browser_upgrade_onion.js"]
["browser_http_scheme_no_upgrade.js"]
["browser_https_telemetry.js"]
support-files = [
@ -82,3 +43,44 @@ skip-if = [
"http2",
"socketprocess_networking",
]
["browser_httpsfirst.js"]
support-files = ["file_httpsfirst_timeout_server.sjs"]
["browser_httpsfirst_console_logging.js"]
["browser_httpsfirst_speculative_connect.js"]
support-files = ["file_httpsfirst_speculative_connect.html"]
["browser_mixed_content_console.js"]
support-files = ["file_mixed_content_console.html"]
["browser_mixed_content_download.js"]
support-files = [
"download_page.html",
"download_server.sjs",
]
["browser_navigation.js"]
support-files = ["file_navigation.html"]
["browser_schemeless.js"]
skip-if = ["!debug && tsan"] #Bug 1890170
["browser_slow_download.js"]
support-files = [
"file_slow_download.html",
"file_slow_download.sjs",
]
["browser_subdocument_downgrade.js"]
support-files = [
"file_empty.html",
"file_subdocument_downgrade.sjs",
]
["browser_superfluos_auth.js"]
["browser_tlds.js"]
["browser_upgrade_onion.js"]

View File

@ -0,0 +1,82 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
ChromeUtils.defineLazyGetter(this, "UrlbarTestUtils", () => {
const { UrlbarTestUtils: module } = ChromeUtils.importESModule(
"resource://testing-common/UrlbarTestUtils.sys.mjs"
);
module.init(this);
return module;
});
add_task(async function test_scheme_modification() {
await SpecialPowers.pushPrefEnv({
set: [
["dom.security.https_first", true],
["dom.security.https_first_schemeless", true],
],
});
await BrowserTestUtils.withNewTab("example.net", async function (browser) {
is(browser.currentURI.schemeIs("https"), true, "Do upgrade schemeless");
{
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: "example.org",
});
const onLoad = BrowserTestUtils.browserLoaded(
gBrowser.selectedBrowser,
false,
null
);
EventUtils.synthesizeKey("KEY_Enter");
await onLoad;
is(browser.currentURI.schemeIs("https"), true, "Do upgrade schemeless");
}
{
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
// eslint-disable-next-line @microsoft/sdl/no-insecure-url
value: "http://example.com",
});
const onLoad = BrowserTestUtils.browserLoaded(
gBrowser.selectedBrowser,
false,
null
);
EventUtils.synthesizeKey("KEY_Enter");
await onLoad;
is(
browser.currentURI.schemeIs("http"),
true,
"Do not upgrade a scheme of http"
);
}
{
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: "example.com",
});
const onLoad = BrowserTestUtils.browserLoaded(
gBrowser.selectedBrowser,
false,
null
);
EventUtils.synthesizeKey("KEY_Enter");
await onLoad;
is(
browser.currentURI.schemeIs("http"),
true,
"Do not upgrade schemeless inputs after we have an exception"
);
}
});
});

View File

@ -577,7 +577,7 @@ nsresult LoadInfoToLoadInfoArgs(nsILoadInfo* aLoadInfo,
aLoadInfo->GetSendCSPViolationEvents(), aLoadInfo->GetOriginAttributes(),
redirectChainIncludingInternalRedirects, redirectChain,
aLoadInfo->GetHasInjectedCookieForCookieBannerHandling(),
aLoadInfo->GetWasSchemelessInput(), aLoadInfo->GetHttpsUpgradeTelemetry(),
aLoadInfo->GetSchemelessInput(), aLoadInfo->GetHttpsUpgradeTelemetry(),
ipcClientInfo, ipcReservedClientInfo, ipcInitialClientInfo, ipcController,
aLoadInfo->CorsUnsafeHeaders(), aLoadInfo->GetForcePreflight(),
aLoadInfo->GetIsPreflight(), aLoadInfo->GetLoadTriggeredFromExternal(),
@ -891,7 +891,7 @@ nsresult LoadInfoArgsToLoadInfo(const LoadInfoArgs& loadInfoArgs,
loadInfoArgs.originTrialCoepCredentiallessEnabledForTopLevel(),
loadInfoArgs.unstrippedURI(), interceptionInfo,
loadInfoArgs.hasInjectedCookieForCookieBannerHandling(),
loadInfoArgs.wasSchemelessInput(), loadInfoArgs.httpsUpgradeTelemetry(),
loadInfoArgs.schemelessInput(), loadInfoArgs.httpsUpgradeTelemetry(),
loadInfoArgs.isNewWindowTarget());
if (loadInfoArgs.isFromProcessingFrameAttributes()) {
@ -960,7 +960,7 @@ void LoadInfoToParentLoadInfoForwarder(
*aForwarderArgsOut = ParentLoadInfoForwarderArgs(
aLoadInfo->GetAllowInsecureRedirectToDataURI(), ipcController, tainting,
aLoadInfo->GetSkipContentSniffing(), aLoadInfo->GetHttpsOnlyStatus(),
aLoadInfo->GetWasSchemelessInput(), aLoadInfo->GetHttpsUpgradeTelemetry(),
aLoadInfo->GetSchemelessInput(), aLoadInfo->GetHttpsUpgradeTelemetry(),
aLoadInfo->GetHstsStatus(), aLoadInfo->GetHasValidUserGestureActivation(),
aLoadInfo->GetTextDirectiveUserActivation(),
aLoadInfo->GetAllowDeprecatedSystemRequests(),
@ -1008,7 +1008,7 @@ nsresult MergeParentLoadInfoForwarder(
rv = aLoadInfo->SetHttpsOnlyStatus(aForwarderArgs.httpsOnlyStatus());
NS_ENSURE_SUCCESS(rv, rv);
rv = aLoadInfo->SetWasSchemelessInput(aForwarderArgs.wasSchemelessInput());
rv = aLoadInfo->SetSchemelessInput(aForwarderArgs.schemelessInput());
NS_ENSURE_SUCCESS(rv, rv);
rv = aLoadInfo->SetHttpsUpgradeTelemetry(

View File

@ -693,7 +693,7 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
mInterceptionInfo(rhs.mInterceptionInfo),
mHasInjectedCookieForCookieBannerHandling(
rhs.mHasInjectedCookieForCookieBannerHandling),
mWasSchemelessInput(rhs.mWasSchemelessInput),
mSchemelessInput(rhs.mSchemelessInput),
mHttpsUpgradeTelemetry(rhs.mHttpsUpgradeTelemetry),
mIsNewWindowTarget(rhs.mIsNewWindowTarget) {
}
@ -744,7 +744,8 @@ LoadInfo::LoadInfo(
nsILoadInfo::CrossOriginEmbedderPolicy aLoadingEmbedderPolicy,
bool aIsOriginTrialCoepCredentiallessEnabledForTopLevel,
nsIURI* aUnstrippedURI, nsIInterceptionInfo* aInterceptionInfo,
bool aHasInjectedCookieForCookieBannerHandling, bool aWasSchemelessInput,
bool aHasInjectedCookieForCookieBannerHandling,
nsILoadInfo::SchemelessInputType aSchemelessInput,
nsILoadInfo::HTTPSUpgradeTelemetryType aHttpsUpgradeTelemetry,
bool aIsNewWindowTarget)
: mLoadingPrincipal(aLoadingPrincipal),
@ -827,7 +828,7 @@ LoadInfo::LoadInfo(
mInterceptionInfo(aInterceptionInfo),
mHasInjectedCookieForCookieBannerHandling(
aHasInjectedCookieForCookieBannerHandling),
mWasSchemelessInput(aWasSchemelessInput),
mSchemelessInput(aSchemelessInput),
mHttpsUpgradeTelemetry(aHttpsUpgradeTelemetry),
mIsNewWindowTarget(aIsNewWindowTarget) {
// Only top level TYPE_DOCUMENT loads can have a null loadingPrincipal
@ -2454,14 +2455,16 @@ LoadInfo::SetHasInjectedCookieForCookieBannerHandling(
}
NS_IMETHODIMP
LoadInfo::GetWasSchemelessInput(bool* aWasSchemelessInput) {
*aWasSchemelessInput = mWasSchemelessInput;
LoadInfo::GetSchemelessInput(
nsILoadInfo::SchemelessInputType* aSchemelessInput) {
*aSchemelessInput = mSchemelessInput;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::SetWasSchemelessInput(bool aWasSchemelessInput) {
mWasSchemelessInput = aWasSchemelessInput;
LoadInfo::SetSchemelessInput(
nsILoadInfo::SchemelessInputType aSchemelessInput) {
mSchemelessInput = aSchemelessInput;
return NS_OK;
}

View File

@ -47,6 +47,7 @@ nsresult LoadInfoArgsToLoadInfo(const mozilla::net::LoadInfoArgs& aLoadInfoArgs,
const nsACString& aOriginRemoteType,
nsINode* aCspToInheritLoadingContext,
net::LoadInfo** outLoadInfo);
} // namespace ipc
namespace net {
@ -258,7 +259,8 @@ class LoadInfo final : public nsILoadInfo {
nsILoadInfo::CrossOriginEmbedderPolicy aLoadingEmbedderPolicy,
bool aIsOriginTrialCoepCredentiallessEnabledForTopLevel,
nsIURI* aUnstrippedURI, nsIInterceptionInfo* aInterceptionInfo,
bool aHasInjectedCookieForCookieBannerHandling, bool aWasSchemelessInput,
bool aHasInjectedCookieForCookieBannerHandling,
nsILoadInfo::SchemelessInputType aSchemelessInput,
nsILoadInfo::HTTPSUpgradeTelemetryType aHttpsUpgradeTelemetry,
bool aIsNewWindowTarget);
@ -413,7 +415,8 @@ class LoadInfo final : public nsILoadInfo {
nsCOMPtr<nsIInterceptionInfo> mInterceptionInfo;
bool mHasInjectedCookieForCookieBannerHandling = false;
bool mWasSchemelessInput = false;
nsILoadInfo::SchemelessInputType mSchemelessInput =
nsILoadInfo::SchemelessInputTypeUnset;
nsILoadInfo::HTTPSUpgradeTelemetryType mHttpsUpgradeTelemetry =
nsILoadInfo::NOT_INITIALIZED;

View File

@ -888,12 +888,14 @@ TRRLoadInfo::SetHasInjectedCookieForCookieBannerHandling(
}
NS_IMETHODIMP
TRRLoadInfo::GetWasSchemelessInput(bool* aWasSchemelessInput) {
TRRLoadInfo::GetSchemelessInput(
nsILoadInfo::SchemelessInputType* aSchemelessInput) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
TRRLoadInfo::SetWasSchemelessInput(bool aWasSchemelessInput) {
TRRLoadInfo::SetSchemelessInput(
nsILoadInfo::SchemelessInputType aSchemelessInput) {
return NS_ERROR_NOT_IMPLEMENTED;
}

View File

@ -536,7 +536,7 @@ interface nsILoadInfo : nsISupports
/**
* Reflects whetehr this is an HTTP Strict Transport Security host
*/
[infallible] attribute boolean hstsStatus;
[infallible] attribute boolean hstsStatus;
/**
* Returns true if at the time of the loadinfo construction the document
@ -1568,10 +1568,15 @@ interface nsILoadInfo : nsISupports
*/
[infallible] attribute boolean hasInjectedCookieForCookieBannerHandling;
cenum SchemelessInputType : 8 {
SchemelessInputTypeUnset = 0,
SchemelessInputTypeSchemeful = 1,
SchemelessInputTypeSchemeless = 2,
};
/**
* Whether the load has gone through the URL bar, where the fixup had to add * the protocol scheme.
*/
[infallible] attribute boolean wasSchemelessInput;
[infallible] attribute nsILoadInfo_SchemelessInputType schemelessInput;
cenum HTTPSUpgradeTelemetryType : 32 {
NOT_INITIALIZED = 0,

View File

@ -2994,7 +2994,8 @@ static bool ShouldSecureUpgradeNoHSTS(nsIURI* aURI, nsILoadInfo* aLoadInfo) {
}
// 4.a Https-First
if (nsHTTPSOnlyUtils::ShouldUpgradeHttpsFirstRequest(aURI, aLoadInfo)) {
if (aLoadInfo->GetWasSchemelessInput()) {
if (aLoadInfo->GetSchemelessInput() ==
nsILoadInfo::SchemelessInputTypeSchemeless) {
aLoadInfo->SetHttpsUpgradeTelemetry(
nsILoadInfo::HTTPS_FIRST_SCHEMELESS_UPGRADE);
} else {

View File

@ -161,7 +161,7 @@ static auto CreateDocumentLoadInfo(CanonicalBrowsingContext* aBrowsingContext,
loadInfo->SetHttpsOnlyStatus(httpsOnlyStatus);
}
loadInfo->SetWasSchemelessInput(aLoadState->GetWasSchemelessInput());
loadInfo->SetSchemelessInput(aLoadState->GetSchemelessInput());
loadInfo->SetHttpsUpgradeTelemetry(aLoadState->GetHttpsUpgradeTelemetry());
loadInfo->SetTriggeringSandboxFlags(aLoadState->TriggeringSandboxFlags());
@ -2415,13 +2415,14 @@ bool DocumentLoadListener::MaybeHandleLoadErrorWithURIFixup(nsresult aStatus) {
}
nsCOMPtr<nsIInputStream> newPostData;
bool wasSchemelessInput = false;
nsILoadInfo::SchemelessInputType schemelessInput =
nsILoadInfo::SchemelessInputTypeUnset;
nsCOMPtr<nsIURI> newURI = nsDocShell::AttemptURIFixup(
mChannel, aStatus, mOriginalUriString, mLoadStateLoadType, bc->IsTop(),
mLoadStateInternalLoadFlags &
nsDocShell::INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP,
bc->UsePrivateBrowsing(), true, getter_AddRefs(newPostData),
&wasSchemelessInput);
&schemelessInput);
// Since aStatus will be NS_OK for 4xx and 5xx error codes we
// have to check each request which was upgraded by https-first.
@ -2455,7 +2456,7 @@ bool DocumentLoadListener::MaybeHandleLoadErrorWithURIFixup(nsresult aStatus) {
loadState->SetPostDataStream(newPostData);
// Record whether the protocol was added through a fixup.
loadState->SetWasSchemelessInput(wasSchemelessInput);
loadState->SetSchemelessInput(schemelessInput);
if (isHTTPSFirstFixup) {
nsHTTPSOnlyUtils::UpdateLoadStateAfterHTTPSFirstDowngrade(this, loadState);

View File

@ -41,6 +41,7 @@ using mozilla::dom::RequestMode from "mozilla/dom/RequestBinding.h";
using mozilla::net::LinkHeader from "nsNetUtil.h";
using mozilla::dom::FeaturePolicyInfo from "mozilla/dom/FeaturePolicy.h";
using nsILoadInfo::HTTPSUpgradeTelemetryType from "nsILoadInfo.h";
using nsILoadInfo::SchemelessInputType from "nsILoadInfo.h";
namespace mozilla {
namespace net {
@ -139,7 +140,7 @@ struct LoadInfoArgs
RedirectHistoryEntryInfo[] redirectChainIncludingInternalRedirects;
RedirectHistoryEntryInfo[] redirectChain;
bool hasInjectedCookieForCookieBannerHandling;
bool wasSchemelessInput;
SchemelessInputType schemelessInput;
HTTPSUpgradeTelemetryType httpsUpgradeTelemetry;
/**
@ -229,7 +230,7 @@ struct ParentLoadInfoForwarderArgs
uint32_t httpsOnlyStatus;
bool wasSchemelessInput;
SchemelessInputType schemelessInput;
HTTPSUpgradeTelemetryType httpsUpgradeTelemetry;

View File

@ -1580,7 +1580,7 @@ interface LoadURIOptions {
triggeringSandboxFlags?: number;
triggeringStorageAccess?: boolean;
triggeringWindowId?: number;
wasSchemelessInput?: boolean;
schemelessInput?: SchemelessInputType;
}
interface LockInfo {

View File

@ -1916,7 +1916,7 @@ interface nsIURIFixupInfo extends nsISupports {
fixedURI: nsIURI;
keywordProviderName: string;
keywordAsSent: string;
wasSchemelessInput: boolean;
schemelessInput: nsILoadInfo.SchemelessInputType;
fixupChangedProtocol: boolean;
fixupCreatedAlternateURI: boolean;
originalInput: string;
@ -6187,6 +6187,12 @@ enum CrossOriginEmbedderPolicy {
EMBEDDER_POLICY_CREDENTIALLESS = 2,
}
enum SchemelessInputType {
SchemelessInputTypeUnset = 0,
SchemelessInputTypeSchemeful = 1,
SchemelessInputTypeSchemeless = 2,
}
enum HTTPSUpgradeTelemetryType {
NOT_INITIALIZED = 0,
NO_UPGRADE = 1,
@ -6365,7 +6371,7 @@ interface nsILoadInfo extends nsISupports, Enums<typeof nsILoadInfo.StoragePermi
readonly shouldSkipCheckForBrokenURLOrZeroSized: boolean;
unstrippedURI: nsIURI;
hasInjectedCookieForCookieBannerHandling: boolean;
wasSchemelessInput: boolean;
schemelessInput: nsILoadInfo.SchemelessInputType;
httpsUpgradeTelemetry: nsILoadInfo.HTTPSUpgradeTelemetryType;
}