1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  *
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
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "base/basictypes.h"
8 
9 #include "nsJARURI.h"
10 #include "nsNetUtil.h"
11 #include "nsIClassInfoImpl.h"
12 #include "nsIIOService.h"
13 #include "nsIStandardURL.h"
14 #include "nsCRT.h"
15 #include "nsReadableUtils.h"
16 #include "nsNetCID.h"
17 #include "nsIObjectInputStream.h"
18 #include "nsIObjectOutputStream.h"
19 #include "nsQueryObject.h"
20 #include "mozilla/ipc/URIUtils.h"
21 
22 using namespace mozilla::ipc;
23 
24 static NS_DEFINE_CID(kJARURICID, NS_JARURI_CID);
25 
26 ////////////////////////////////////////////////////////////////////////////////
27 
NS_IMPL_CLASSINFO(nsJARURI,nullptr,nsIClassInfo::THREADSAFE,NS_JARURI_CID)28 NS_IMPL_CLASSINFO(nsJARURI, nullptr, nsIClassInfo::THREADSAFE, NS_JARURI_CID)
29 // Empty CI getter. We only need nsIClassInfo for Serialization
30 NS_IMPL_CI_INTERFACE_GETTER0(nsJARURI)
31 
32 nsJARURI::nsJARURI() {}
33 
~nsJARURI()34 nsJARURI::~nsJARURI() {}
35 
36 // XXX Why is this threadsafe?
37 NS_IMPL_ADDREF(nsJARURI)
NS_IMPL_RELEASE(nsJARURI)38 NS_IMPL_RELEASE(nsJARURI)
39 NS_INTERFACE_MAP_BEGIN(nsJARURI)
40   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJARURI)
41   NS_INTERFACE_MAP_ENTRY(nsIURI)
42   NS_INTERFACE_MAP_ENTRY(nsIURL)
43   NS_INTERFACE_MAP_ENTRY(nsIJARURI)
44   NS_INTERFACE_MAP_ENTRY(nsISerializable)
45   NS_IMPL_QUERY_CLASSINFO(nsJARURI)
46   NS_INTERFACE_MAP_ENTRY(nsINestedURI)
47   NS_INTERFACE_MAP_ENTRY_CONCRETE(nsJARURI)
48 NS_INTERFACE_MAP_END
49 
50 nsresult nsJARURI::Init(const char* charsetHint) {
51   mCharsetHint = charsetHint;
52   return NS_OK;
53 }
54 
55 #define NS_JAR_SCHEME "jar:"_ns
56 #define NS_JAR_DELIMITER "!/"_ns
57 #define NS_BOGUS_ENTRY_SCHEME "x:///"_ns
58 
59 // FormatSpec takes the entry spec (including the "x:///" at the
60 // beginning) and gives us a full JAR spec.
FormatSpec(const nsACString & entrySpec,nsACString & result,bool aIncludeScheme)61 nsresult nsJARURI::FormatSpec(const nsACString& entrySpec, nsACString& result,
62                               bool aIncludeScheme) {
63   // The entrySpec MUST start with "x:///"
64   NS_ASSERTION(StringBeginsWith(entrySpec, NS_BOGUS_ENTRY_SCHEME),
65                "bogus entry spec");
66 
67   nsAutoCString fileSpec;
68   nsresult rv = mJARFile->GetSpec(fileSpec);
69   if (NS_FAILED(rv)) return rv;
70 
71   if (aIncludeScheme)
72     result = NS_JAR_SCHEME;
73   else
74     result.Truncate();
75 
76   result.Append(fileSpec + NS_JAR_DELIMITER +
77                 Substring(entrySpec, 5, entrySpec.Length() - 5));
78   return NS_OK;
79 }
80 
CreateEntryURL(const nsACString & entryFilename,const char * charset,nsIURL ** url)81 nsresult nsJARURI::CreateEntryURL(const nsACString& entryFilename,
82                                   const char* charset, nsIURL** url) {
83   *url = nullptr;
84   // Flatten the concatenation, just in case.  See bug 128288
85   nsAutoCString spec(NS_BOGUS_ENTRY_SCHEME + entryFilename);
86   return NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
87       .Apply(NS_MutatorMethod(&nsIStandardURLMutator::Init,
88                               nsIStandardURL::URLTYPE_NO_AUTHORITY, -1, spec,
89                               charset, nullptr, nullptr))
90       .Finalize(url);
91 }
92 
93 ////////////////////////////////////////////////////////////////////////////////
94 // nsISerializable methods:
95 
96 NS_IMETHODIMP
Read(nsIObjectInputStream * aStream)97 nsJARURI::Read(nsIObjectInputStream* aStream) {
98   MOZ_ASSERT_UNREACHABLE("Use nsIURIMutator.read() instead");
99   return NS_ERROR_NOT_IMPLEMENTED;
100 }
101 
ReadPrivate(nsIObjectInputStream * aInputStream)102 nsresult nsJARURI::ReadPrivate(nsIObjectInputStream* aInputStream) {
103   nsresult rv;
104 
105   nsCOMPtr<nsISupports> supports;
106   rv = aInputStream->ReadObject(true, getter_AddRefs(supports));
107   NS_ENSURE_SUCCESS(rv, rv);
108 
109   mJARFile = do_QueryInterface(supports, &rv);
110   NS_ENSURE_SUCCESS(rv, rv);
111 
112   rv = aInputStream->ReadObject(true, getter_AddRefs(supports));
113   NS_ENSURE_SUCCESS(rv, rv);
114 
115   mJAREntry = do_QueryInterface(supports);
116   NS_ENSURE_SUCCESS(rv, rv);
117 
118   rv = aInputStream->ReadCString(mCharsetHint);
119   return rv;
120 }
121 
122 NS_IMETHODIMP
Write(nsIObjectOutputStream * aOutputStream)123 nsJARURI::Write(nsIObjectOutputStream* aOutputStream) {
124   nsresult rv;
125 
126   rv = aOutputStream->WriteCompoundObject(mJARFile, NS_GET_IID(nsIURI), true);
127   NS_ENSURE_SUCCESS(rv, rv);
128 
129   rv = aOutputStream->WriteCompoundObject(mJAREntry, NS_GET_IID(nsIURL), true);
130   NS_ENSURE_SUCCESS(rv, rv);
131 
132   rv = aOutputStream->WriteStringZ(mCharsetHint.get());
133   return rv;
134 }
135 
136 ////////////////////////////////////////////////////////////////////////////////
137 // nsIURI methods:
138 
139 NS_IMETHODIMP
GetSpec(nsACString & aSpec)140 nsJARURI::GetSpec(nsACString& aSpec) {
141   nsAutoCString entrySpec;
142   mJAREntry->GetSpec(entrySpec);
143   return FormatSpec(entrySpec, aSpec);
144 }
145 
146 NS_IMETHODIMP
GetSpecIgnoringRef(nsACString & aSpec)147 nsJARURI::GetSpecIgnoringRef(nsACString& aSpec) {
148   nsAutoCString entrySpec;
149   mJAREntry->GetSpecIgnoringRef(entrySpec);
150   return FormatSpec(entrySpec, aSpec);
151 }
152 
153 NS_IMETHODIMP
GetDisplaySpec(nsACString & aUnicodeSpec)154 nsJARURI::GetDisplaySpec(nsACString& aUnicodeSpec) {
155   return GetSpec(aUnicodeSpec);
156 }
157 
158 NS_IMETHODIMP
GetDisplayHostPort(nsACString & aUnicodeHostPort)159 nsJARURI::GetDisplayHostPort(nsACString& aUnicodeHostPort) {
160   return GetHostPort(aUnicodeHostPort);
161 }
162 
163 NS_IMETHODIMP
GetDisplayPrePath(nsACString & aPrePath)164 nsJARURI::GetDisplayPrePath(nsACString& aPrePath) {
165   return GetPrePath(aPrePath);
166 }
167 
168 NS_IMETHODIMP
GetDisplayHost(nsACString & aUnicodeHost)169 nsJARURI::GetDisplayHost(nsACString& aUnicodeHost) {
170   return GetHost(aUnicodeHost);
171 }
172 
173 NS_IMETHODIMP
GetHasRef(bool * result)174 nsJARURI::GetHasRef(bool* result) { return mJAREntry->GetHasRef(result); }
175 
SetSpecInternal(const nsACString & aSpec)176 nsresult nsJARURI::SetSpecInternal(const nsACString& aSpec) {
177   return SetSpecWithBase(aSpec, nullptr);
178 }
179 
180 // Queries this list of interfaces. If none match, it queries mURI.
NS_IMPL_NSIURIMUTATOR_ISUPPORTS(nsJARURI::Mutator,nsIURISetters,nsIURIMutator,nsIURLMutator,nsISerializable,nsIJARURIMutator)181 NS_IMPL_NSIURIMUTATOR_ISUPPORTS(nsJARURI::Mutator, nsIURISetters, nsIURIMutator,
182                                 nsIURLMutator, nsISerializable,
183                                 nsIJARURIMutator)
184 
185 NS_IMETHODIMP
186 nsJARURI::Mutator::SetFileName(const nsACString& aFileName,
187                                nsIURIMutator** aMutator) {
188   if (!mURI) {
189     return NS_ERROR_NULL_POINTER;
190   }
191   if (aMutator) {
192     nsCOMPtr<nsIURIMutator> mutator = this;
193     mutator.forget(aMutator);
194   }
195   return mURI->SetFileNameInternal(aFileName);
196 }
197 
198 NS_IMETHODIMP
SetFileBaseName(const nsACString & aFileBaseName,nsIURIMutator ** aMutator)199 nsJARURI::Mutator::SetFileBaseName(const nsACString& aFileBaseName,
200                                    nsIURIMutator** aMutator) {
201   if (!mURI) {
202     return NS_ERROR_NULL_POINTER;
203   }
204   if (aMutator) {
205     nsCOMPtr<nsIURIMutator> mutator = this;
206     mutator.forget(aMutator);
207   }
208   return mURI->SetFileBaseNameInternal(aFileBaseName);
209 }
210 
211 NS_IMETHODIMP
SetFileExtension(const nsACString & aFileExtension,nsIURIMutator ** aMutator)212 nsJARURI::Mutator::SetFileExtension(const nsACString& aFileExtension,
213                                     nsIURIMutator** aMutator) {
214   if (!mURI) {
215     return NS_ERROR_NULL_POINTER;
216   }
217   if (aMutator) {
218     nsCOMPtr<nsIURIMutator> mutator = this;
219     mutator.forget(aMutator);
220   }
221   return mURI->SetFileExtensionInternal(aFileExtension);
222 }
223 
224 NS_IMETHODIMP
Mutate(nsIURIMutator ** aMutator)225 nsJARURI::Mutate(nsIURIMutator** aMutator) {
226   RefPtr<nsJARURI::Mutator> mutator = new nsJARURI::Mutator();
227   nsresult rv = mutator->InitFromURI(this);
228   if (NS_FAILED(rv)) {
229     return rv;
230   }
231   mutator.forget(aMutator);
232   return NS_OK;
233 }
234 
SetSpecWithBase(const nsACString & aSpec,nsIURI * aBaseURL)235 nsresult nsJARURI::SetSpecWithBase(const nsACString& aSpec, nsIURI* aBaseURL) {
236   nsresult rv;
237 
238   nsCOMPtr<nsIIOService> ioServ(do_GetIOService(&rv));
239   NS_ENSURE_SUCCESS(rv, rv);
240 
241   nsAutoCString scheme;
242   rv = ioServ->ExtractScheme(aSpec, scheme);
243   if (NS_FAILED(rv)) {
244     // not an absolute URI
245     if (!aBaseURL) return NS_ERROR_MALFORMED_URI;
246 
247     RefPtr<nsJARURI> otherJAR = do_QueryObject(aBaseURL);
248     NS_ENSURE_TRUE(otherJAR, NS_NOINTERFACE);
249 
250     mJARFile = otherJAR->mJARFile;
251 
252     nsCOMPtr<nsIURI> entry;
253 
254     rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
255              .Apply(NS_MutatorMethod(&nsIStandardURLMutator::Init,
256                                      nsIStandardURL::URLTYPE_NO_AUTHORITY, -1,
257                                      nsCString(aSpec), mCharsetHint.get(),
258                                      otherJAR->mJAREntry, nullptr))
259              .Finalize(entry);
260     if (NS_FAILED(rv)) {
261       return rv;
262     }
263 
264     mJAREntry = do_QueryInterface(entry);
265     if (!mJAREntry) return NS_NOINTERFACE;
266 
267     return NS_OK;
268   }
269 
270   NS_ENSURE_TRUE(scheme.EqualsLiteral("jar"), NS_ERROR_MALFORMED_URI);
271 
272   nsACString::const_iterator begin, end;
273   aSpec.BeginReading(begin);
274   aSpec.EndReading(end);
275 
276   while (begin != end && *begin != ':') ++begin;
277 
278   ++begin;  // now we're past the "jar:"
279 
280   nsACString::const_iterator delim_begin = begin;
281   nsACString::const_iterator delim_end = end;
282   nsACString::const_iterator frag = begin;
283 
284   if (FindInReadable(NS_JAR_DELIMITER, delim_begin, delim_end)) {
285     frag = delim_end;
286   }
287   while (frag != end && (*frag != '#' && *frag != '?')) {
288     ++frag;
289   }
290   if (frag != end) {
291     // there was a fragment or query, mark that as the end of the URL to scan
292     end = frag;
293   }
294 
295   // Search backward from the end for the "!/" delimiter. Remember, jar URLs
296   // can nest, e.g.:
297   //    jar:jar:http://www.foo.com/bar.jar!/a.jar!/b.html
298   // This gets the b.html document from out of the a.jar file, that's
299   // contained within the bar.jar file.
300   // Also, the outermost "inner" URI may be a relative URI:
301   //   jar:../relative.jar!/a.html
302 
303   delim_begin = begin;
304   delim_end = end;
305 
306   if (!RFindInReadable(NS_JAR_DELIMITER, delim_begin, delim_end)) {
307     return NS_ERROR_MALFORMED_URI;
308   }
309 
310   rv = ioServ->NewURI(Substring(begin, delim_begin), mCharsetHint.get(),
311                       aBaseURL, getter_AddRefs(mJARFile));
312   if (NS_FAILED(rv)) return rv;
313 
314   // skip over any extra '/' chars
315   while (*delim_end == '/') ++delim_end;
316 
317   aSpec.EndReading(end);  // set to the original 'end'
318   return SetJAREntry(Substring(delim_end, end));
319 }
320 
321 NS_IMETHODIMP
GetPrePath(nsACString & prePath)322 nsJARURI::GetPrePath(nsACString& prePath) {
323   prePath = NS_JAR_SCHEME;
324   return NS_OK;
325 }
326 
327 NS_IMETHODIMP
GetScheme(nsACString & aScheme)328 nsJARURI::GetScheme(nsACString& aScheme) {
329   aScheme = "jar";
330   return NS_OK;
331 }
332 
SetScheme(const nsACString & aScheme)333 nsresult nsJARURI::SetScheme(const nsACString& aScheme) {
334   // doesn't make sense to set the scheme of a jar: URL
335   return NS_ERROR_FAILURE;
336 }
337 
338 NS_IMETHODIMP
GetUserPass(nsACString & aUserPass)339 nsJARURI::GetUserPass(nsACString& aUserPass) { return NS_ERROR_FAILURE; }
340 
SetUserPass(const nsACString & aUserPass)341 nsresult nsJARURI::SetUserPass(const nsACString& aUserPass) {
342   return NS_ERROR_FAILURE;
343 }
344 
345 NS_IMETHODIMP
GetUsername(nsACString & aUsername)346 nsJARURI::GetUsername(nsACString& aUsername) { return NS_ERROR_FAILURE; }
347 
SetUsername(const nsACString & aUsername)348 nsresult nsJARURI::SetUsername(const nsACString& aUsername) {
349   return NS_ERROR_FAILURE;
350 }
351 
352 NS_IMETHODIMP
GetPassword(nsACString & aPassword)353 nsJARURI::GetPassword(nsACString& aPassword) { return NS_ERROR_FAILURE; }
354 
SetPassword(const nsACString & aPassword)355 nsresult nsJARURI::SetPassword(const nsACString& aPassword) {
356   return NS_ERROR_FAILURE;
357 }
358 
359 NS_IMETHODIMP
GetHostPort(nsACString & aHostPort)360 nsJARURI::GetHostPort(nsACString& aHostPort) { return NS_ERROR_FAILURE; }
361 
SetHostPort(const nsACString & aHostPort)362 nsresult nsJARURI::SetHostPort(const nsACString& aHostPort) {
363   return NS_ERROR_FAILURE;
364 }
365 
366 NS_IMETHODIMP
GetHost(nsACString & aHost)367 nsJARURI::GetHost(nsACString& aHost) { return NS_ERROR_FAILURE; }
368 
SetHost(const nsACString & aHost)369 nsresult nsJARURI::SetHost(const nsACString& aHost) { return NS_ERROR_FAILURE; }
370 
371 NS_IMETHODIMP
GetPort(int32_t * aPort)372 nsJARURI::GetPort(int32_t* aPort) { return NS_ERROR_FAILURE; }
373 
SetPort(int32_t aPort)374 nsresult nsJARURI::SetPort(int32_t aPort) { return NS_ERROR_FAILURE; }
375 
GetPathQueryRef(nsACString & aPath)376 nsresult nsJARURI::GetPathQueryRef(nsACString& aPath) {
377   nsAutoCString entrySpec;
378   mJAREntry->GetSpec(entrySpec);
379   return FormatSpec(entrySpec, aPath, false);
380 }
381 
SetPathQueryRef(const nsACString & aPath)382 nsresult nsJARURI::SetPathQueryRef(const nsACString& aPath) {
383   return NS_ERROR_FAILURE;
384 }
385 
386 NS_IMETHODIMP
GetAsciiSpec(nsACString & aSpec)387 nsJARURI::GetAsciiSpec(nsACString& aSpec) {
388   // XXX Shouldn't this like... make sure it returns ASCII or something?
389   return GetSpec(aSpec);
390 }
391 
392 NS_IMETHODIMP
GetAsciiHostPort(nsACString & aHostPort)393 nsJARURI::GetAsciiHostPort(nsACString& aHostPort) { return NS_ERROR_FAILURE; }
394 
395 NS_IMETHODIMP
GetAsciiHost(nsACString & aHost)396 nsJARURI::GetAsciiHost(nsACString& aHost) { return NS_ERROR_FAILURE; }
397 
398 NS_IMETHODIMP
Equals(nsIURI * other,bool * result)399 nsJARURI::Equals(nsIURI* other, bool* result) {
400   return EqualsInternal(other, eHonorRef, result);
401 }
402 
403 NS_IMETHODIMP
EqualsExceptRef(nsIURI * other,bool * result)404 nsJARURI::EqualsExceptRef(nsIURI* other, bool* result) {
405   return EqualsInternal(other, eIgnoreRef, result);
406 }
407 
408 // Helper method:
409 /* virtual */
EqualsInternal(nsIURI * other,nsJARURI::RefHandlingEnum refHandlingMode,bool * result)410 nsresult nsJARURI::EqualsInternal(nsIURI* other,
411                                   nsJARURI::RefHandlingEnum refHandlingMode,
412                                   bool* result) {
413   *result = false;
414 
415   if (!other) return NS_OK;  // not equal
416 
417   RefPtr<nsJARURI> otherJAR = do_QueryObject(other);
418   if (!otherJAR) return NS_OK;  // not equal
419 
420   bool equal;
421   nsresult rv = mJARFile->Equals(otherJAR->mJARFile, &equal);
422   if (NS_FAILED(rv) || !equal) {
423     return rv;  // not equal
424   }
425 
426   return refHandlingMode == eHonorRef
427              ? mJAREntry->Equals(otherJAR->mJAREntry, result)
428              : mJAREntry->EqualsExceptRef(otherJAR->mJAREntry, result);
429 }
430 
431 NS_IMETHODIMP
SchemeIs(const char * i_Scheme,bool * o_Equals)432 nsJARURI::SchemeIs(const char* i_Scheme, bool* o_Equals) {
433   MOZ_ASSERT(o_Equals);
434   if (!i_Scheme) {
435     *o_Equals = false;
436     return NS_OK;
437   }
438 
439   *o_Equals = PL_strcasecmp("jar", i_Scheme) ? false : true;
440   return NS_OK;
441 }
442 
Clone(nsIURI ** result)443 nsresult nsJARURI::Clone(nsIURI** result) {
444   RefPtr<nsJARURI> uri = new nsJARURI();
445   uri->mJARFile = mJARFile;
446   uri->mJAREntry = mJAREntry;
447   uri.forget(result);
448 
449   return NS_OK;
450 }
451 
452 NS_IMETHODIMP
Resolve(const nsACString & relativePath,nsACString & result)453 nsJARURI::Resolve(const nsACString& relativePath, nsACString& result) {
454   nsresult rv;
455 
456   nsCOMPtr<nsIIOService> ioServ(do_GetIOService(&rv));
457   if (NS_FAILED(rv)) return rv;
458 
459   nsAutoCString scheme;
460   rv = ioServ->ExtractScheme(relativePath, scheme);
461   if (NS_SUCCEEDED(rv)) {
462     // then aSpec is absolute
463     result = relativePath;
464     return NS_OK;
465   }
466 
467   nsAutoCString resolvedPath;
468   mJAREntry->Resolve(relativePath, resolvedPath);
469 
470   return FormatSpec(resolvedPath, result);
471 }
472 
473 ////////////////////////////////////////////////////////////////////////////////
474 // nsIURL methods:
475 
476 NS_IMETHODIMP
GetFilePath(nsACString & filePath)477 nsJARURI::GetFilePath(nsACString& filePath) {
478   return mJAREntry->GetFilePath(filePath);
479 }
480 
SetFilePath(const nsACString & filePath)481 nsresult nsJARURI::SetFilePath(const nsACString& filePath) {
482   return NS_MutateURI(mJAREntry).SetFilePath(filePath).Finalize(mJAREntry);
483 }
484 
485 NS_IMETHODIMP
GetQuery(nsACString & query)486 nsJARURI::GetQuery(nsACString& query) { return mJAREntry->GetQuery(query); }
487 
SetQuery(const nsACString & query)488 nsresult nsJARURI::SetQuery(const nsACString& query) {
489   return NS_MutateURI(mJAREntry).SetQuery(query).Finalize(mJAREntry);
490 }
491 
SetQueryWithEncoding(const nsACString & query,const Encoding * encoding)492 nsresult nsJARURI::SetQueryWithEncoding(const nsACString& query,
493                                         const Encoding* encoding) {
494   return NS_MutateURI(mJAREntry)
495       .SetQueryWithEncoding(query, encoding)
496       .Finalize(mJAREntry);
497 }
498 
499 NS_IMETHODIMP
GetRef(nsACString & ref)500 nsJARURI::GetRef(nsACString& ref) { return mJAREntry->GetRef(ref); }
501 
SetRef(const nsACString & ref)502 nsresult nsJARURI::SetRef(const nsACString& ref) {
503   return NS_MutateURI(mJAREntry).SetRef(ref).Finalize(mJAREntry);
504 }
505 
506 NS_IMETHODIMP
GetDirectory(nsACString & directory)507 nsJARURI::GetDirectory(nsACString& directory) {
508   return mJAREntry->GetDirectory(directory);
509 }
510 
511 NS_IMETHODIMP
GetFileName(nsACString & fileName)512 nsJARURI::GetFileName(nsACString& fileName) {
513   return mJAREntry->GetFileName(fileName);
514 }
515 
SetFileNameInternal(const nsACString & fileName)516 nsresult nsJARURI::SetFileNameInternal(const nsACString& fileName) {
517   return NS_MutateURI(mJAREntry)
518       .Apply(NS_MutatorMethod(&nsIURLMutator::SetFileName, nsCString(fileName),
519                               nullptr))
520       .Finalize(mJAREntry);
521 }
522 
523 NS_IMETHODIMP
GetFileBaseName(nsACString & fileBaseName)524 nsJARURI::GetFileBaseName(nsACString& fileBaseName) {
525   return mJAREntry->GetFileBaseName(fileBaseName);
526 }
527 
SetFileBaseNameInternal(const nsACString & fileBaseName)528 nsresult nsJARURI::SetFileBaseNameInternal(const nsACString& fileBaseName) {
529   return NS_MutateURI(mJAREntry)
530       .Apply(NS_MutatorMethod(&nsIURLMutator::SetFileBaseName,
531                               nsCString(fileBaseName), nullptr))
532       .Finalize(mJAREntry);
533 }
534 
535 NS_IMETHODIMP
GetFileExtension(nsACString & fileExtension)536 nsJARURI::GetFileExtension(nsACString& fileExtension) {
537   return mJAREntry->GetFileExtension(fileExtension);
538 }
539 
SetFileExtensionInternal(const nsACString & fileExtension)540 nsresult nsJARURI::SetFileExtensionInternal(const nsACString& fileExtension) {
541   return NS_MutateURI(mJAREntry)
542       .Apply(NS_MutatorMethod(&nsIURLMutator::SetFileExtension,
543                               nsCString(fileExtension), nullptr))
544       .Finalize(mJAREntry);
545 }
546 
547 NS_IMETHODIMP
GetCommonBaseSpec(nsIURI * uriToCompare,nsACString & commonSpec)548 nsJARURI::GetCommonBaseSpec(nsIURI* uriToCompare, nsACString& commonSpec) {
549   commonSpec.Truncate();
550 
551   NS_ENSURE_ARG_POINTER(uriToCompare);
552 
553   commonSpec.Truncate();
554   nsCOMPtr<nsIJARURI> otherJARURI(do_QueryInterface(uriToCompare));
555   if (!otherJARURI) {
556     // Nothing in common
557     return NS_OK;
558   }
559 
560   nsCOMPtr<nsIURI> otherJARFile;
561   nsresult rv = otherJARURI->GetJARFile(getter_AddRefs(otherJARFile));
562   if (NS_FAILED(rv)) return rv;
563 
564   bool equal;
565   rv = mJARFile->Equals(otherJARFile, &equal);
566   if (NS_FAILED(rv)) return rv;
567 
568   if (!equal) {
569     // See what the JAR file URIs have in common
570     nsCOMPtr<nsIURL> ourJARFileURL(do_QueryInterface(mJARFile));
571     if (!ourJARFileURL) {
572       // Not a URL, so nothing in common
573       return NS_OK;
574     }
575     nsAutoCString common;
576     rv = ourJARFileURL->GetCommonBaseSpec(otherJARFile, common);
577     if (NS_FAILED(rv)) return rv;
578 
579     commonSpec = NS_JAR_SCHEME + common;
580     return NS_OK;
581   }
582 
583   // At this point we have the same JAR file.  Compare the JAREntrys
584   nsAutoCString otherEntry;
585   rv = otherJARURI->GetJAREntry(otherEntry);
586   if (NS_FAILED(rv)) return rv;
587 
588   nsCOMPtr<nsIURL> url;
589   rv = CreateEntryURL(otherEntry, nullptr, getter_AddRefs(url));
590   if (NS_FAILED(rv)) return rv;
591 
592   nsAutoCString common;
593   rv = mJAREntry->GetCommonBaseSpec(url, common);
594   if (NS_FAILED(rv)) return rv;
595 
596   rv = FormatSpec(common, commonSpec);
597   return rv;
598 }
599 
600 NS_IMETHODIMP
GetRelativeSpec(nsIURI * uriToCompare,nsACString & relativeSpec)601 nsJARURI::GetRelativeSpec(nsIURI* uriToCompare, nsACString& relativeSpec) {
602   GetSpec(relativeSpec);
603 
604   NS_ENSURE_ARG_POINTER(uriToCompare);
605 
606   nsCOMPtr<nsIJARURI> otherJARURI(do_QueryInterface(uriToCompare));
607   if (!otherJARURI) {
608     // Nothing in common
609     return NS_OK;
610   }
611 
612   nsCOMPtr<nsIURI> otherJARFile;
613   nsresult rv = otherJARURI->GetJARFile(getter_AddRefs(otherJARFile));
614   if (NS_FAILED(rv)) return rv;
615 
616   bool equal;
617   rv = mJARFile->Equals(otherJARFile, &equal);
618   if (NS_FAILED(rv)) return rv;
619 
620   if (!equal) {
621     // We live in different JAR files.  Nothing in common.
622     return rv;
623   }
624 
625   // Same JAR file.  Compare the JAREntrys
626   nsAutoCString otherEntry;
627   rv = otherJARURI->GetJAREntry(otherEntry);
628   if (NS_FAILED(rv)) return rv;
629 
630   nsCOMPtr<nsIURL> url;
631   rv = CreateEntryURL(otherEntry, nullptr, getter_AddRefs(url));
632   if (NS_FAILED(rv)) return rv;
633 
634   nsAutoCString relativeEntrySpec;
635   rv = mJAREntry->GetRelativeSpec(url, relativeEntrySpec);
636   if (NS_FAILED(rv)) return rv;
637 
638   if (!StringBeginsWith(relativeEntrySpec, NS_BOGUS_ENTRY_SCHEME)) {
639     // An actual relative spec!
640     relativeSpec = relativeEntrySpec;
641   }
642   return rv;
643 }
644 
645 ////////////////////////////////////////////////////////////////////////////////
646 // nsIJARURI methods:
647 
648 NS_IMETHODIMP
GetJARFile(nsIURI ** jarFile)649 nsJARURI::GetJARFile(nsIURI** jarFile) { return GetInnerURI(jarFile); }
650 
651 NS_IMETHODIMP
GetJAREntry(nsACString & entryPath)652 nsJARURI::GetJAREntry(nsACString& entryPath) {
653   nsAutoCString filePath;
654   mJAREntry->GetFilePath(filePath);
655   NS_ASSERTION(filePath.Length() > 0, "path should never be empty!");
656   // Trim off the leading '/'
657   entryPath = Substring(filePath, 1, filePath.Length() - 1);
658   return NS_OK;
659 }
660 
SetJAREntry(const nsACString & entryPath)661 nsresult nsJARURI::SetJAREntry(const nsACString& entryPath) {
662   return CreateEntryURL(entryPath, mCharsetHint.get(),
663                         getter_AddRefs(mJAREntry));
664 }
665 
666 ////////////////////////////////////////////////////////////////////////////////
667 
668 NS_IMETHODIMP
GetInnerURI(nsIURI ** aURI)669 nsJARURI::GetInnerURI(nsIURI** aURI) {
670   nsCOMPtr<nsIURI> uri = mJARFile;
671   uri.forget(aURI);
672   return NS_OK;
673 }
674 
675 NS_IMETHODIMP
GetInnermostURI(nsIURI ** uri)676 nsJARURI::GetInnermostURI(nsIURI** uri) {
677   return NS_ImplGetInnermostURI(this, uri);
678 }
679 
Serialize(URIParams & aParams)680 void nsJARURI::Serialize(URIParams& aParams) {
681   JARURIParams params;
682 
683   SerializeURI(mJARFile, params.jarFile());
684   SerializeURI(mJAREntry, params.jarEntry());
685   params.charset() = mCharsetHint;
686 
687   aParams = params;
688 }
689 
Deserialize(const URIParams & aParams)690 bool nsJARURI::Deserialize(const URIParams& aParams) {
691   if (aParams.type() != URIParams::TJARURIParams) {
692     NS_ERROR("Received unknown parameters from the other process!");
693     return false;
694   }
695 
696   const JARURIParams& params = aParams.get_JARURIParams();
697 
698   nsCOMPtr<nsIURI> file = DeserializeURI(params.jarFile());
699   if (!file) {
700     NS_ERROR("Couldn't deserialize jar file URI!");
701     return false;
702   }
703 
704   nsCOMPtr<nsIURI> entry = DeserializeURI(params.jarEntry());
705   if (!entry) {
706     NS_ERROR("Couldn't deserialize jar entry URI!");
707     return false;
708   }
709 
710   nsCOMPtr<nsIURL> entryURL = do_QueryInterface(entry);
711   if (!entryURL) {
712     NS_ERROR("Couldn't QI jar entry URI to nsIURL!");
713     return false;
714   }
715 
716   mJARFile.swap(file);
717   mJAREntry.swap(entryURL);
718   mCharsetHint = params.charset();
719 
720   return true;
721 }
722