Bug 1945566 - Add a moz-src protocol handler, r=necko-reviewers,firefox-desktop-core-reviewers ,extension-reviewers,robwu,mconley,valentin,tschuster

Differential Revision: https://phabricator.services.mozilla.com/D236569
This commit is contained in:
Dave Townsend
2025-03-03 12:47:46 +00:00
parent a7bf0c79f2
commit 941f1ac3b3
16 changed files with 201 additions and 24 deletions

View File

@@ -430,7 +430,6 @@ var gChromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(
);
var gChromeMap = new Map();
var gOverrideMap = new Map();
var gComponentsSet = new Set();
// In this map when the value is a Set of URLs, the file is referenced if any
// of the files in the Set is referenced.
@@ -505,8 +504,6 @@ function parseManifest(manifestUri) {
}
} else if (type == "resource") {
trackResourcePrefix(argv[0]);
} else if (type == "component") {
gComponentsSet.add(argv[1]);
}
}
});
@@ -647,6 +644,10 @@ function parseCodeFile(fileUri) {
/["'`]chrome:\/\/[a-zA-Z0-9-]+\/(content|skin|locale)\/[^"'` ]*["'`]/g
);
if (!urls) {
urls = line.match(/["']moz-src:\/\/\/[^"']+["']/g);
}
if (!urls) {
urls = line.match(/["']resource:\/\/[^"']+["']/g);
if (
@@ -734,7 +735,10 @@ function parseCodeFile(fileUri) {
if (!/\.(properties|js|jsm|mjs|json|css)$/.test(url)) {
url += ".js";
}
if (url.startsWith("resource://")) {
if (
url.startsWith("resource://") ||
url.startsWith("moz-src:///")
) {
addCodeReference(url, fileUri);
} else {
// if we end up with a chrome:// url here, it's likely because
@@ -784,13 +788,22 @@ function parseCodeFile(fileUri) {
function convertToCodeURI(fileUri) {
let baseUri = fileUri;
let path = "";
while (true) {
while (baseUri) {
let slashPos = baseUri.lastIndexOf("/", baseUri.length - 2);
if (slashPos <= 0) {
// File not accessible from chrome protocol, try resource://
for (let res of gResourceMap) {
if (fileUri.startsWith(res[1])) {
return fileUri.replace(res[1], "resource://" + res[0] + "/");
let resourceUriString = fileUri.replace(
res[1],
`resource://${res[0]}/`
);
// If inside moz-src, treat as moz-src url.
resourceUriString = resourceUriString.replace(
/^resource:\/\/gre\/moz-src\//,
"moz-src:///"
);
return resourceUriString;
}
}
// Give up and return the original URL.
@@ -802,6 +815,7 @@ function convertToCodeURI(fileUri) {
return gChromeMap.get(baseUri) + path;
}
}
throw new Error(`Unparsable URI: ${fileUri}`);
}
async function chromeFileExists(aURI) {
@@ -849,6 +863,7 @@ function findChromeUrlsFromArray(array, prefix) {
// Only keep strings that look like real chrome or resource urls.
if (
/chrome:\/\/[a-zA-Z09-]+\/(content|skin|locale)\//.test(string) ||
/moz-src:\/\/\/\w+/.test(string) ||
/resource:\/\/[a-zA-Z09-]*\/.*\.[a-z]+/.test(string)
) {
gReferencesFromCode.set(string, null);
@@ -862,10 +877,12 @@ add_task(async function checkAllTheFiles() {
const libxul = await IOUtils.read(PathUtils.xulLibraryPath);
findChromeUrlsFromArray(libxul, "chrome://");
findChromeUrlsFromArray(libxul, "resource://");
findChromeUrlsFromArray(libxul, "moz-src:///");
// Handle NS_LITERAL_STRING.
let uint16 = new Uint16Array(libxul.buffer);
findChromeUrlsFromArray(uint16, "chrome://");
findChromeUrlsFromArray(uint16, "resource://");
findChromeUrlsFromArray(uint16, "moz-src:///");
const kCodeExtensions = [
".xml",
@@ -954,6 +971,7 @@ add_task(async function checkAllTheFiles() {
// the non-devtools paths:
let devtoolsPrefixes = [
"chrome://devtools",
"moz-src:///devtools/",
"resource://devtools/",
"resource://devtools-shared-images/",
"resource://devtools-highlighter-styles/",
@@ -968,7 +986,9 @@ add_task(async function checkAllTheFiles() {
for (let uri of uris) {
uri = convertToCodeURI(uri.spec);
if (
(uri.startsWith("chrome://") || uri.startsWith("resource://")) &&
(uri.startsWith("chrome://") ||
uri.startsWith("resource://") ||
uri.startsWith("moz-src:///")) &&
isDevtools == hasDevtoolsPrefix(uri)
) {
chromeFiles.push(uri);
@@ -1025,9 +1045,6 @@ add_task(async function checkAllTheFiles() {
if (rv && f.startsWith("resource://app/")) {
rv = isUnreferenced(f.replace("resource://app/", "resource:///"));
}
if (rv && /^resource:\/\/(?:app|gre)\/components\/[^/]+\.js$/.test(f)) {
rv = !gComponentsSet.has(f.replace(/.*\//, ""));
}
if (!rv) {
foundReference = true;
if (useAllowlist) {
@@ -1108,7 +1125,9 @@ add_task(async function checkAllTheFiles() {
}
if (
(file.startsWith("chrome://") || file.startsWith("resource://")) &&
(file.startsWith("chrome://") ||
file.startsWith("resource://") ||
file.startsWith("moz-src:///")) &&
!(await chromeFileExists(file))
) {
// Ignore chrome prefixes that have been automatically expanded.

View File

@@ -27,7 +27,8 @@ static constexpr nsLiteralCString kDisallowedSchemes[] = {
"chrome"_ns, "data"_ns, "imap"_ns,
"javascript"_ns, "mailbox"_ns, "news"_ns,
"page-icon"_ns, "resource"_ns, "view-source"_ns,
"moz-extension"_ns, "moz-page-thumb"_ns, "x-moz-ews"_ns,
"moz-extension"_ns, "moz-page-thumb"_ns, "moz-src"_ns,
"x-moz-ews"_ns,
};
bool BaseHistory::CanStore(nsIURI* aURI) {

View File

@@ -89,7 +89,7 @@ bool subjectToCSP(nsIURI* aURI, nsContentPolicyType aContentType) {
if (aURI->SchemeIs("chrome") && !isImgOrStyleOrDTD) {
return true;
}
if (aURI->SchemeIs("moz-icon")) {
if (aURI->SchemeIs("moz-icon") || aURI->SchemeIs("moz-src")) {
return true;
}
bool match;

View File

@@ -1903,6 +1903,10 @@ bool nsContentSecurityUtils::ValidateScriptFilename(JSContext* cx,
// If it's a resource:// url, allow it
return true;
}
if (StringBeginsWith(filename, "moz-src://"_ns)) {
// If it's a moz-src:// url, allow it
return true;
}
if (StringBeginsWith(filename, "file://"_ns)) {
// We will temporarily allow all file:// URIs through for now
return true;

View File

@@ -226,7 +226,7 @@ void ScriptLoadRequest::MarkScriptForBytecodeEncoding(JSScript* aScript) {
static bool IsInternalURIScheme(nsIURI* uri) {
return uri->SchemeIs("moz-extension") || uri->SchemeIs("resource") ||
uri->SchemeIs("chrome");
uri->SchemeIs("moz-src") || uri->SchemeIs("chrome");
}
void ScriptLoadRequest::SetBaseURLFromChannelAndOriginalURI(

View File

@@ -1039,7 +1039,8 @@ nsresult mozJSModuleLoader::GetModuleImportStack(const nsACString& aLocation,
/* static */
bool mozJSModuleLoader::IsTrustedScheme(nsIURI* aURI) {
return aURI->SchemeIs("resource") || aURI->SchemeIs("chrome");
return aURI->SchemeIs("resource") || aURI->SchemeIs("chrome") ||
aURI->SchemeIs("moz-src");
}
nsresult mozJSModuleLoader::ImportESModule(

View File

@@ -903,7 +903,7 @@ nsresult nsIOService::AsyncOnChannelRedirect(
bool nsIOService::UsesExternalProtocolHandler(const nsACString& aScheme) {
if (aScheme == "file"_ns || aScheme == "chrome"_ns ||
aScheme == "resource"_ns) {
aScheme == "resource"_ns || aScheme == "moz-src"_ns) {
// Don't allow file:, chrome: or resource: URIs to be handled with
// nsExternalProtocolHandler, since internally we rely on being able to
// use and read from these URIs.

View File

@@ -102,6 +102,7 @@
#endif
#include "nsAboutProtocolHandler.h"
#include "nsResProtocolHandler.h"
#include "mozilla/net/MozSrcProtocolHandler.h"
#include "mozilla/net/ExtensionProtocolHandler.h"
#include "mozilla/net/PageThumbProtocolHandler.h"
#include "mozilla/net/SFVService.h"
@@ -1912,6 +1913,15 @@ nsresult NS_NewURI(nsIURI** aURI, const nsACString& aSpec,
return handler->NewURI(aSpec, aCharset, aBaseURI, aURI);
}
if (scheme.EqualsLiteral("moz-src")) {
RefPtr<MozSrcProtocolHandler> handler =
MozSrcProtocolHandler::GetSingleton();
if (!handler) {
return NS_ERROR_NOT_AVAILABLE;
}
return handler->NewURI(aSpec, aCharset, aBaseURI, aURI);
}
if (scheme.EqualsLiteral("indexeddb") || scheme.EqualsLiteral("uuid")) {
return NS_MutateURI(new nsStandardURL::Mutator())
.Apply(&nsIStandardURLMutator::Init, nsIStandardURL::URLTYPE_AUTHORITY,

View File

@@ -579,6 +579,7 @@ nsresult nsStandardURL::BuildNormalizedSpec(const char* spec,
nsDependentCSubstring tempHost(spec + mHost.mPos, mHost.mLen);
nsresult rv;
bool allowIp = !SegmentIs(spec, mScheme, "resource") &&
!SegmentIs(spec, mScheme, "moz-src") &&
!SegmentIs(spec, mScheme, "chrome");
if (tempHost.First() == '[' && allowIp) {
mCheckedIfHostA = true;

View File

@@ -466,6 +466,23 @@ Classes = [
],
},
},
{
'cid': '{4abd60aa-6b7d-4f3b-bf52-d7ce8ae6dd36}',
'contract_ids': ['@mozilla.org/network/protocol;1?name=moz-src'],
'singleton': True,
'type': 'mozilla::net::MozSrcProtocolHandler',
'headers': ['/netwerk/protocol/res/MozSrcProtocolHandler.h'],
'constructor': 'mozilla::net::MozSrcProtocolHandler::GetSingleton',
'protocol_config': {
'scheme': 'moz-src',
'flags': [
'URI_STD',
'URI_IS_UI_RESOURCE',
'URI_IS_LOCAL_RESOURCE',
'URI_IS_POTENTIALLY_TRUSTWORTHY',
],
},
},
{
'cid': '{9c7ec5d1-23f9-11d5-aea8-8fcc0793e97f}',
'contract_ids': ['@mozilla.org/network/protocol;1?name=view-source'],

View File

@@ -0,0 +1,69 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/ModuleUtils.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Omnijar.h"
#include "MozSrcProtocolHandler.h"
#define MOZSRC_SCHEME "moz-src"
namespace mozilla {
namespace net {
NS_IMPL_QUERY_INTERFACE(MozSrcProtocolHandler, nsISubstitutingProtocolHandler,
nsIProtocolHandler, nsISupportsWeakReference)
NS_IMPL_ADDREF_INHERITED(MozSrcProtocolHandler, SubstitutingProtocolHandler)
NS_IMPL_RELEASE_INHERITED(MozSrcProtocolHandler, SubstitutingProtocolHandler)
mozilla::StaticRefPtr<MozSrcProtocolHandler> MozSrcProtocolHandler::sSingleton;
already_AddRefed<MozSrcProtocolHandler> MozSrcProtocolHandler::GetSingleton() {
if (!sSingleton) {
RefPtr<MozSrcProtocolHandler> handler = new MozSrcProtocolHandler();
if (NS_WARN_IF(NS_FAILED(handler->Init()))) {
return nullptr;
}
sSingleton = handler;
ClearOnShutdown(&sSingleton);
}
return do_AddRef(sSingleton);
}
MozSrcProtocolHandler::MozSrcProtocolHandler()
: SubstitutingProtocolHandler(MOZSRC_SCHEME) {}
nsresult MozSrcProtocolHandler::Init() {
nsresult rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::GRE, mGREURI);
NS_ENSURE_SUCCESS(rv, rv);
mGREURI.AppendLiteral(MOZSRC_SCHEME);
return NS_OK;
}
bool MozSrcProtocolHandler::ResolveSpecialCases(const nsACString& aHost,
const nsACString& aPath,
const nsACString& aPathname,
nsACString& aResult) {
aResult = mGREURI;
aResult.Append(aPathname);
return true;
}
nsresult MozSrcProtocolHandler::GetSubstitutionInternal(const nsACString& aRoot,
nsIURI** aResult) {
nsAutoCString uri;
if (!ResolveSpecialCases(aRoot, "/"_ns, "/"_ns, uri)) {
return NS_ERROR_NOT_AVAILABLE;
}
return NS_NewURI(aResult, uri);
}
} // namespace net
} // namespace mozilla

View File

@@ -0,0 +1,49 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef MozSrcProtocolHandler_h___
#define MozSrcProtocolHandler_h___
#include "nsIProtocolHandler.h"
#include "nsISubstitutingProtocolHandler.h"
#include "SubstitutingProtocolHandler.h"
namespace mozilla {
namespace net {
class MozSrcProtocolHandler final : public nsISubstitutingProtocolHandler,
public SubstitutingProtocolHandler,
public nsSupportsWeakReference {
public:
NS_DECL_ISUPPORTS_INHERITED
NS_FORWARD_NSIPROTOCOLHANDLER(SubstitutingProtocolHandler::)
NS_FORWARD_NSISUBSTITUTINGPROTOCOLHANDLER(SubstitutingProtocolHandler::)
static already_AddRefed<MozSrcProtocolHandler> GetSingleton();
MozSrcProtocolHandler();
protected:
~MozSrcProtocolHandler() = default;
[[nodiscard]] virtual bool ResolveSpecialCases(const nsACString& aHost,
const nsACString& aPath,
const nsACString& aPathname,
nsACString& aResult) override;
[[nodiscard]] nsresult GetSubstitutionInternal(const nsACString& aRoot,
nsIURI** aResult) override;
private:
static mozilla::StaticRefPtr<MozSrcProtocolHandler> sSingleton;
nsresult Init();
nsCString mGREURI;
};
} // namespace net
} // namespace mozilla
#endif /* MozSrcProtocolHandler_h___ */

View File

@@ -13,6 +13,7 @@ XPIDL_MODULE = "necko_res"
EXPORTS.mozilla.net += [
"ExtensionProtocolHandler.h",
"MozSrcProtocolHandler.h",
"PageThumbProtocolHandler.h",
"RemoteStreamGetter.h",
"SubstitutingJARURI.h",
@@ -26,6 +27,7 @@ EXPORTS += [
UNIFIED_SOURCES += [
"ExtensionProtocolHandler.cpp",
"MozSrcProtocolHandler.cpp",
"nsResProtocolHandler.cpp",
"PageThumbProtocolHandler.cpp",
"RemoteStreamGetter.cpp",

View File

@@ -345,6 +345,7 @@ class OmniJarSubFormatter(PiecemealFormatter):
return True
return path[0] in [
"modules",
"moz-src",
"actors",
"dictionaries",
"hyphenation",

View File

@@ -7,7 +7,7 @@
#include "nsNetUtil.h"
#include "nsIFileURL.h"
#include "nsIJARURI.h"
#include "nsIResProtocolHandler.h"
#include "nsISubstitutingProtocolHandler.h"
#include "nsIChromeRegistry.h"
#include "nsStringStream.h"
#include "StartupCacheUtils.h"
@@ -136,17 +136,20 @@ static inline bool canonicalizeBase(nsAutoCString& spec, nsACString& out) {
nsresult ResolveURI(nsIURI* in, nsIURI** out) {
nsresult rv;
nsAutoCString scheme;
in->GetScheme(scheme);
// Resolve resource:// URIs. At the end of this if/else block, we
// have both spec and uri variables identifying the same URI.
if (in->SchemeIs("resource")) {
if (scheme.EqualsLiteral("resource") || scheme.EqualsLiteral("moz-src")) {
nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIProtocolHandler> ph;
rv = ioService->GetProtocolHandler("resource", getter_AddRefs(ph));
rv = ioService->GetProtocolHandler(scheme.get(), getter_AddRefs(ph));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIResProtocolHandler> irph(do_QueryInterface(ph, &rv));
nsCOMPtr<nsISubstitutingProtocolHandler> irph(do_QueryInterface(ph, &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString spec;
@@ -155,7 +158,7 @@ nsresult ResolveURI(nsIURI* in, nsIURI** out) {
return ioService->NewURI(spec, nullptr, nullptr, out);
}
if (in->SchemeIs("chrome")) {
if (scheme.EqualsLiteral("chrome")) {
nsCOMPtr<nsIChromeRegistry> chromeReg =
mozilla::services::GetChromeRegistry();
if (!chromeReg) return NS_ERROR_UNEXPECTED;

View File

@@ -197,14 +197,14 @@ WebNavigationContent::OnStateChange(nsIWebProgress* aWebProgress,
nsCOMPtr<nsIURI> uri;
MOZ_TRY(channel->GetURI(getter_AddRefs(uri)));
// Prevents "about", "chrome", "resource" and "moz-extension" URI schemes to
// be reported with the resolved "file" or "jar" URIs (see bug 1246125)
// Prevent "about", "chrome", "resource", "moz-src", and "moz-extension" URIs
// being reported with the resolved "file" or "jar" URIs (see bug 1246125).
if (uri->SchemeIs("file") || uri->SchemeIs("jar")) {
nsCOMPtr<nsIURI> originalURI;
MOZ_TRY(channel->GetOriginalURI(getter_AddRefs(originalURI)));
// FIXME: We probably actually want NS_GetFinalChannelURI here.
if (originalURI->SchemeIs("about") || originalURI->SchemeIs("chrome") ||
originalURI->SchemeIs("resource") ||
originalURI->SchemeIs("resource") || originalURI->SchemeIs("moz-src") ||
originalURI->SchemeIs("moz-extension")) {
uri = originalURI.forget();
}