Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/chrome/RegistryMessageUtils.h"
8 #include "nsResProtocolHandler.h"
9 #include "nsIIOService.h"
10 #include "nsIFile.h"
11 #include "nsNetUtil.h"
12 #include "nsURLHelper.h"
13 #include "nsEscape.h"
15 #include "mozilla/Omnijar.h"
17 static NS_DEFINE_CID(kResURLCID, NS_RESURL_CID);
19 static nsResProtocolHandler *gResHandler = nullptr;
21 #if defined(PR_LOGGING)
22 //
23 // Log module for Resource Protocol logging...
24 //
25 // To enable logging (see prlog.h for full details):
26 //
27 // set NSPR_LOG_MODULES=nsResProtocol:5
28 // set NSPR_LOG_FILE=log.txt
29 //
30 // this enables PR_LOG_ALWAYS level information and places all output in
31 // the file log.txt
32 //
33 static PRLogModuleInfo *gResLog;
34 #endif
36 #define kAPP NS_LITERAL_CSTRING("app")
37 #define kGRE NS_LITERAL_CSTRING("gre")
39 //----------------------------------------------------------------------------
40 // nsResURL : overrides nsStandardURL::GetFile to provide nsIFile resolution
41 //----------------------------------------------------------------------------
43 nsresult
44 nsResURL::EnsureFile()
45 {
46 nsresult rv;
48 NS_ENSURE_TRUE(gResHandler, NS_ERROR_NOT_AVAILABLE);
50 nsAutoCString spec;
51 rv = gResHandler->ResolveURI(this, spec);
52 if (NS_FAILED(rv))
53 return rv;
55 nsAutoCString scheme;
56 rv = net_ExtractURLScheme(spec, nullptr, nullptr, &scheme);
57 if (NS_FAILED(rv))
58 return rv;
60 // Bug 585869:
61 // In most cases, the scheme is jar if it's not file.
62 // Regardless, net_GetFileFromURLSpec should be avoided
63 // when the scheme isn't file.
64 if (!scheme.Equals(NS_LITERAL_CSTRING("file")))
65 return NS_ERROR_NO_INTERFACE;
67 rv = net_GetFileFromURLSpec(spec, getter_AddRefs(mFile));
68 #ifdef DEBUG_bsmedberg
69 if (NS_SUCCEEDED(rv)) {
70 bool exists = true;
71 mFile->Exists(&exists);
72 if (!exists) {
73 printf("resource %s doesn't exist!\n", spec.get());
74 }
75 }
76 #endif
78 return rv;
79 }
81 /* virtual */ nsStandardURL*
82 nsResURL::StartClone()
83 {
84 nsResURL *clone = new nsResURL();
85 return clone;
86 }
88 NS_IMETHODIMP
89 nsResURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
90 {
91 *aClassIDNoAlloc = kResURLCID;
92 return NS_OK;
93 }
95 //----------------------------------------------------------------------------
96 // nsResProtocolHandler <public>
97 //----------------------------------------------------------------------------
99 nsResProtocolHandler::nsResProtocolHandler()
100 : mSubstitutions(32)
101 {
102 #if defined(PR_LOGGING)
103 gResLog = PR_NewLogModule("nsResProtocol");
104 #endif
106 NS_ASSERTION(!gResHandler, "res handler already created!");
107 gResHandler = this;
108 }
110 nsResProtocolHandler::~nsResProtocolHandler()
111 {
112 gResHandler = nullptr;
113 }
115 nsresult
116 nsResProtocolHandler::Init()
117 {
118 nsresult rv;
120 mIOService = do_GetIOService(&rv);
121 NS_ENSURE_SUCCESS(rv, rv);
123 nsAutoCString appURI, greURI;
124 rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::APP, appURI);
125 NS_ENSURE_SUCCESS(rv, rv);
126 rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::GRE, greURI);
127 NS_ENSURE_SUCCESS(rv, rv);
129 //
130 // make resource:/// point to the application directory or omnijar
131 //
132 nsCOMPtr<nsIURI> uri;
133 rv = NS_NewURI(getter_AddRefs(uri), appURI.Length() ? appURI : greURI);
134 NS_ENSURE_SUCCESS(rv, rv);
136 rv = SetSubstitution(EmptyCString(), uri);
137 NS_ENSURE_SUCCESS(rv, rv);
139 //
140 // make resource://app/ point to the application directory or omnijar
141 //
142 rv = SetSubstitution(kAPP, uri);
143 NS_ENSURE_SUCCESS(rv, rv);
145 //
146 // make resource://gre/ point to the GRE directory
147 //
148 if (appURI.Length()) { // We already have greURI in uri if appURI.Length() is 0.
149 rv = NS_NewURI(getter_AddRefs(uri), greURI);
150 NS_ENSURE_SUCCESS(rv, rv);
151 }
153 rv = SetSubstitution(kGRE, uri);
154 NS_ENSURE_SUCCESS(rv, rv);
156 //XXXbsmedberg Neil wants a resource://pchrome/ for the profile chrome dir...
157 // but once I finish multiple chrome registration I'm not sure that it is needed
159 // XXX dveditz: resource://pchrome/ defeats profile directory salting
160 // if web content can load it. Tread carefully.
162 return rv;
163 }
165 static PLDHashOperator
166 EnumerateSubstitution(const nsACString& aKey,
167 nsIURI* aURI,
168 void* aArg)
169 {
170 nsTArray<ResourceMapping>* resources =
171 static_cast<nsTArray<ResourceMapping>*>(aArg);
172 SerializedURI uri;
173 if (aURI) {
174 aURI->GetSpec(uri.spec);
175 aURI->GetOriginCharset(uri.charset);
176 }
178 ResourceMapping resource = {
179 nsCString(aKey), uri
180 };
181 resources->AppendElement(resource);
182 return (PLDHashOperator)PL_DHASH_NEXT;
183 }
185 void
186 nsResProtocolHandler::CollectSubstitutions(InfallibleTArray<ResourceMapping>& aResources)
187 {
188 mSubstitutions.EnumerateRead(&EnumerateSubstitution, &aResources);
189 }
191 //----------------------------------------------------------------------------
192 // nsResProtocolHandler::nsISupports
193 //----------------------------------------------------------------------------
195 NS_IMPL_ISUPPORTS(nsResProtocolHandler,
196 nsIResProtocolHandler,
197 nsIProtocolHandler,
198 nsISupportsWeakReference)
200 //----------------------------------------------------------------------------
201 // nsResProtocolHandler::nsIProtocolHandler
202 //----------------------------------------------------------------------------
204 NS_IMETHODIMP
205 nsResProtocolHandler::GetScheme(nsACString &result)
206 {
207 result.AssignLiteral("resource");
208 return NS_OK;
209 }
211 NS_IMETHODIMP
212 nsResProtocolHandler::GetDefaultPort(int32_t *result)
213 {
214 *result = -1; // no port for res: URLs
215 return NS_OK;
216 }
218 NS_IMETHODIMP
219 nsResProtocolHandler::GetProtocolFlags(uint32_t *result)
220 {
221 // XXXbz Is this really true for all resource: URIs? Could we
222 // somehow give different flags to some of them?
223 *result = URI_STD | URI_IS_UI_RESOURCE | URI_IS_LOCAL_RESOURCE;
224 return NS_OK;
225 }
227 NS_IMETHODIMP
228 nsResProtocolHandler::NewURI(const nsACString &aSpec,
229 const char *aCharset,
230 nsIURI *aBaseURI,
231 nsIURI **result)
232 {
233 nsresult rv;
235 nsResURL *resURL = new nsResURL();
236 if (!resURL)
237 return NS_ERROR_OUT_OF_MEMORY;
238 NS_ADDREF(resURL);
240 // unescape any %2f and %2e to make sure nsStandardURL coalesces them.
241 // Later net_GetFileFromURLSpec() will do a full unescape and we want to
242 // treat them the same way the file system will. (bugs 380994, 394075)
243 nsAutoCString spec;
244 const char *src = aSpec.BeginReading();
245 const char *end = aSpec.EndReading();
246 const char *last = src;
248 spec.SetCapacity(aSpec.Length()+1);
249 for ( ; src < end; ++src) {
250 if (*src == '%' && (src < end-2) && *(src+1) == '2') {
251 char ch = '\0';
252 if (*(src+2) == 'f' || *(src+2) == 'F')
253 ch = '/';
254 else if (*(src+2) == 'e' || *(src+2) == 'E')
255 ch = '.';
257 if (ch) {
258 if (last < src)
259 spec.Append(last, src-last);
260 spec.Append(ch);
261 src += 2;
262 last = src+1; // src will be incremented by the loop
263 }
264 }
265 }
266 if (last < src)
267 spec.Append(last, src-last);
269 rv = resURL->Init(nsIStandardURL::URLTYPE_STANDARD, -1, spec, aCharset, aBaseURI);
270 if (NS_SUCCEEDED(rv))
271 rv = CallQueryInterface(resURL, result);
272 NS_RELEASE(resURL);
273 return rv;
274 }
276 NS_IMETHODIMP
277 nsResProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
278 {
279 NS_ENSURE_ARG_POINTER(uri);
280 nsresult rv;
281 nsAutoCString spec;
283 rv = ResolveURI(uri, spec);
284 if (NS_FAILED(rv)) return rv;
286 rv = mIOService->NewChannel(spec, nullptr, nullptr, result);
287 if (NS_FAILED(rv)) return rv;
289 nsLoadFlags loadFlags = 0;
290 (*result)->GetLoadFlags(&loadFlags);
291 (*result)->SetLoadFlags(loadFlags & ~nsIChannel::LOAD_REPLACE);
292 return (*result)->SetOriginalURI(uri);
293 }
295 NS_IMETHODIMP
296 nsResProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval)
297 {
298 // don't override anything.
299 *_retval = false;
300 return NS_OK;
301 }
303 //----------------------------------------------------------------------------
304 // nsResProtocolHandler::nsIResProtocolHandler
305 //----------------------------------------------------------------------------
307 NS_IMETHODIMP
308 nsResProtocolHandler::SetSubstitution(const nsACString& root, nsIURI *baseURI)
309 {
310 if (!baseURI) {
311 mSubstitutions.Remove(root);
312 return NS_OK;
313 }
315 // If baseURI isn't a resource URI, we can set the substitution immediately.
316 nsAutoCString scheme;
317 nsresult rv = baseURI->GetScheme(scheme);
318 NS_ENSURE_SUCCESS(rv, rv);
319 if (!scheme.Equals(NS_LITERAL_CSTRING("resource"))) {
320 mSubstitutions.Put(root, baseURI);
321 return NS_OK;
322 }
324 // baseURI is a resource URI, let's resolve it first.
325 nsAutoCString newBase;
326 rv = ResolveURI(baseURI, newBase);
327 NS_ENSURE_SUCCESS(rv, rv);
329 nsCOMPtr<nsIURI> newBaseURI;
330 rv = mIOService->NewURI(newBase, nullptr, nullptr,
331 getter_AddRefs(newBaseURI));
332 NS_ENSURE_SUCCESS(rv, rv);
334 mSubstitutions.Put(root, newBaseURI);
335 return NS_OK;
336 }
338 NS_IMETHODIMP
339 nsResProtocolHandler::GetSubstitution(const nsACString& root, nsIURI **result)
340 {
341 NS_ENSURE_ARG_POINTER(result);
343 if (mSubstitutions.Get(root, result))
344 return NS_OK;
346 // try invoking the directory service for "resource:root"
348 nsAutoCString key;
349 key.AssignLiteral("resource:");
350 key.Append(root);
352 nsCOMPtr<nsIFile> file;
353 nsresult rv = NS_GetSpecialDirectory(key.get(), getter_AddRefs(file));
354 if (NS_FAILED(rv))
355 return NS_ERROR_NOT_AVAILABLE;
357 rv = mIOService->NewFileURI(file, result);
358 if (NS_FAILED(rv))
359 return NS_ERROR_NOT_AVAILABLE;
361 return NS_OK;
362 }
364 NS_IMETHODIMP
365 nsResProtocolHandler::HasSubstitution(const nsACString& root, bool *result)
366 {
367 NS_ENSURE_ARG_POINTER(result);
369 *result = mSubstitutions.Get(root, nullptr);
370 return NS_OK;
371 }
373 NS_IMETHODIMP
374 nsResProtocolHandler::ResolveURI(nsIURI *uri, nsACString &result)
375 {
376 nsresult rv;
378 nsAutoCString host;
379 nsAutoCString path;
381 rv = uri->GetAsciiHost(host);
382 if (NS_FAILED(rv)) return rv;
384 rv = uri->GetPath(path);
385 if (NS_FAILED(rv)) return rv;
387 // Unescape the path so we can perform some checks on it.
388 nsAutoCString unescapedPath(path);
389 NS_UnescapeURL(unescapedPath);
391 // Don't misinterpret the filepath as an absolute URI.
392 if (unescapedPath.FindChar(':') != -1)
393 return NS_ERROR_MALFORMED_URI;
395 if (unescapedPath.FindChar('\\') != -1)
396 return NS_ERROR_MALFORMED_URI;
398 const char *p = path.get() + 1; // path always starts with a slash
399 NS_ASSERTION(*(p-1) == '/', "Path did not begin with a slash!");
401 if (*p == '/')
402 return NS_ERROR_MALFORMED_URI;
404 nsCOMPtr<nsIURI> baseURI;
405 rv = GetSubstitution(host, getter_AddRefs(baseURI));
406 if (NS_FAILED(rv)) return rv;
408 rv = baseURI->Resolve(nsDependentCString(p, path.Length()-1), result);
410 #if defined(PR_LOGGING)
411 if (PR_LOG_TEST(gResLog, PR_LOG_DEBUG)) {
412 nsAutoCString spec;
413 uri->GetAsciiSpec(spec);
414 PR_LOG(gResLog, PR_LOG_DEBUG,
415 ("%s\n -> %s\n", spec.get(), PromiseFlatCString(result).get()));
416 }
417 #endif
418 return rv;
419 }