1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "ExtensionProtocolHandler.h"
8
9 #include "nsIAddonPolicyService.h"
10 #include "nsServiceManagerUtils.h"
11 #include "nsIURL.h"
12 #include "nsIChannel.h"
13 #include "nsIStreamListener.h"
14 #include "nsIRequestObserver.h"
15 #include "nsIInputStreamChannel.h"
16 #include "nsIInputStream.h"
17 #include "nsIOutputStream.h"
18 #include "nsIStreamConverterService.h"
19 #include "nsIPipe.h"
20 #include "nsNetUtil.h"
21 #include "LoadInfo.h"
22
23 namespace mozilla {
24 namespace net {
25
NS_IMPL_QUERY_INTERFACE(ExtensionProtocolHandler,nsISubstitutingProtocolHandler,nsIProtocolHandler,nsIProtocolHandlerWithDynamicFlags,nsISupportsWeakReference)26 NS_IMPL_QUERY_INTERFACE(ExtensionProtocolHandler, nsISubstitutingProtocolHandler,
27 nsIProtocolHandler, nsIProtocolHandlerWithDynamicFlags,
28 nsISupportsWeakReference)
29 NS_IMPL_ADDREF_INHERITED(ExtensionProtocolHandler, SubstitutingProtocolHandler)
30 NS_IMPL_RELEASE_INHERITED(ExtensionProtocolHandler, SubstitutingProtocolHandler)
31
32 nsresult
33 ExtensionProtocolHandler::GetFlagsForURI(nsIURI* aURI, uint32_t* aFlags)
34 {
35 // In general a moz-extension URI is only loadable by chrome, but a whitelisted
36 // subset are web-accessible (and cross-origin fetchable). Check that whitelist.
37 nsCOMPtr<nsIAddonPolicyService> aps = do_GetService("@mozilla.org/addons/policy-service;1");
38 bool loadableByAnyone = false;
39 if (aps) {
40 nsresult rv = aps->ExtensionURILoadableByAnyone(aURI, &loadableByAnyone);
41 NS_ENSURE_SUCCESS(rv, rv);
42 }
43
44 *aFlags = URI_STD | URI_IS_LOCAL_RESOURCE | (loadableByAnyone ? (URI_LOADABLE_BY_ANYONE | URI_FETCHABLE_BY_ANYONE) : URI_DANGEROUS_TO_LOAD);
45 return NS_OK;
46 }
47
48 class PipeCloser : public nsIRequestObserver
49 {
50 public:
51 NS_DECL_ISUPPORTS
52
PipeCloser(nsIOutputStream * aOutputStream)53 explicit PipeCloser(nsIOutputStream* aOutputStream) :
54 mOutputStream(aOutputStream)
55 {
56 }
57
OnStartRequest(nsIRequest *,nsISupports *)58 NS_IMETHOD OnStartRequest(nsIRequest*, nsISupports*) override
59 {
60 return NS_OK;
61 }
62
OnStopRequest(nsIRequest *,nsISupports *,nsresult aStatusCode)63 NS_IMETHOD OnStopRequest(nsIRequest*, nsISupports*, nsresult aStatusCode) override
64 {
65 NS_ENSURE_TRUE(mOutputStream, NS_ERROR_UNEXPECTED);
66
67 nsresult rv = mOutputStream->Close();
68 mOutputStream = nullptr;
69 return rv;
70 }
71
72 protected:
~PipeCloser()73 virtual ~PipeCloser() {}
74
75 private:
76 nsCOMPtr<nsIOutputStream> mOutputStream;
77 };
78
NS_IMPL_ISUPPORTS(PipeCloser,nsIRequestObserver)79 NS_IMPL_ISUPPORTS(PipeCloser, nsIRequestObserver)
80
81 bool
82 ExtensionProtocolHandler::ResolveSpecialCases(const nsACString& aHost,
83 const nsACString& aPath,
84 const nsACString& aPathname,
85 nsACString& aResult)
86 {
87 // Create special moz-extension:-pages such as moz-extension://foo/_blank.html
88 // for all registered extensions. We can't just do this as a substitution
89 // because substitutions can only match on host.
90 if (!SubstitutingProtocolHandler::HasSubstitution(aHost)) {
91 return false;
92 }
93 if (aPathname.EqualsLiteral("/_blank.html")) {
94 aResult.AssignLiteral("about:blank");
95 return true;
96 }
97 if (aPathname.EqualsLiteral("/_generated_background_page.html")) {
98 nsCOMPtr<nsIAddonPolicyService> aps =
99 do_GetService("@mozilla.org/addons/policy-service;1");
100 if (!aps) {
101 return false;
102 }
103 nsresult rv = aps->GetGeneratedBackgroundPageUrl(aHost, aResult);
104 NS_ENSURE_SUCCESS(rv, false);
105 if (!aResult.IsEmpty()) {
106 MOZ_RELEASE_ASSERT(Substring(aResult, 0, 5).Equals("data:"));
107 return true;
108 }
109 }
110
111 return false;
112 }
113
114 nsresult
SubstituteChannel(nsIURI * aURI,nsILoadInfo * aLoadInfo,nsIChannel ** result)115 ExtensionProtocolHandler::SubstituteChannel(nsIURI* aURI,
116 nsILoadInfo* aLoadInfo,
117 nsIChannel** result)
118 {
119 nsresult rv;
120 nsCOMPtr<nsIURL> url = do_QueryInterface(aURI, &rv);
121 NS_ENSURE_SUCCESS(rv, rv);
122
123 nsAutoCString ext;
124 rv = url->GetFileExtension(ext);
125 NS_ENSURE_SUCCESS(rv, rv);
126
127 if (!ext.LowerCaseEqualsLiteral("css")) {
128 return NS_OK;
129 }
130
131 // Filter CSS files to replace locale message tokens with localized strings.
132
133 nsCOMPtr<nsIStreamConverterService> convService =
134 do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
135 NS_ENSURE_SUCCESS(rv, rv);
136
137 const char* kFromType = "application/vnd.mozilla.webext.unlocalized";
138 const char* kToType = "text/css";
139
140 nsCOMPtr<nsIInputStream> inputStream;
141 if (aLoadInfo &&
142 aLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
143 // If the channel needs to enforce CORS, we need to open the channel async.
144
145 nsCOMPtr<nsIOutputStream> outputStream;
146 rv = NS_NewPipe(getter_AddRefs(inputStream), getter_AddRefs(outputStream),
147 0, UINT32_MAX, true, false);
148 NS_ENSURE_SUCCESS(rv, rv);
149
150 nsCOMPtr<nsIStreamListener> listener;
151 nsCOMPtr<nsIRequestObserver> observer = new PipeCloser(outputStream);
152 rv = NS_NewSimpleStreamListener(getter_AddRefs(listener), outputStream, observer);
153 NS_ENSURE_SUCCESS(rv, rv);
154
155 nsCOMPtr<nsIStreamListener> converter;
156 rv = convService->AsyncConvertData(kFromType, kToType, listener,
157 aURI, getter_AddRefs(converter));
158 NS_ENSURE_SUCCESS(rv, rv);
159
160 nsCOMPtr<nsILoadInfo> loadInfo =
161 static_cast<LoadInfo*>(aLoadInfo)->CloneForNewRequest();
162 (*result)->SetLoadInfo(loadInfo);
163
164 rv = (*result)->AsyncOpen2(converter);
165 } else {
166 // Stylesheet loads for extension content scripts require a sync channel.
167
168 nsCOMPtr<nsIInputStream> sourceStream;
169 if (aLoadInfo && aLoadInfo->GetEnforceSecurity()) {
170 rv = (*result)->Open2(getter_AddRefs(sourceStream));
171 } else {
172 rv = (*result)->Open(getter_AddRefs(sourceStream));
173 }
174 NS_ENSURE_SUCCESS(rv, rv);
175
176 rv = convService->Convert(sourceStream, kFromType, kToType,
177 aURI, getter_AddRefs(inputStream));
178 }
179 NS_ENSURE_SUCCESS(rv, rv);
180
181 nsCOMPtr<nsIChannel> channel;
182 rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel), aURI, inputStream,
183 NS_LITERAL_CSTRING("text/css"),
184 NS_LITERAL_CSTRING("utf-8"),
185 aLoadInfo);
186 NS_ENSURE_SUCCESS(rv, rv);
187
188 channel.swap(*result);
189 return NS_OK;
190 }
191
192 } // namespace net
193 } // namespace mozilla
194