Bumping manifests a=b2g-bump
[gecko.git] / docshell / base / nsDefaultURIFixup.cpp
blob0061f3ea8863b9f94ff1afa65a6ff4c0160e7c5a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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/. */
7 #include "nsNetUtil.h"
8 #include "nsCRT.h"
10 #include "nsIFile.h"
11 #include <algorithm>
13 #ifdef MOZ_TOOLKIT_SEARCH
14 #include "nsIBrowserSearchService.h"
15 #endif
17 #include "nsIURIFixup.h"
18 #include "nsDefaultURIFixup.h"
19 #include "mozilla/Preferences.h"
20 #include "mozilla/dom/ContentChild.h"
21 #include "mozilla/ipc/InputStreamUtils.h"
22 #include "mozilla/ipc/URIUtils.h"
23 #include "nsIObserverService.h"
24 #include "nsXULAppAPI.h"
26 // Used to check if external protocol schemes are usable
27 #include "nsCExternalHandlerService.h"
28 #include "nsIExternalProtocolService.h"
30 using namespace mozilla;
32 /* Implementation file */
33 NS_IMPL_ISUPPORTS(nsDefaultURIFixup, nsIURIFixup)
35 static bool sInitializedPrefCaches = false;
36 static bool sFixTypos = true;
37 static bool sFixupKeywords = true;
39 nsDefaultURIFixup::nsDefaultURIFixup()
41 /* member initializers and constructor code */
45 nsDefaultURIFixup::~nsDefaultURIFixup()
47 /* destructor code */
50 /* nsIURI createExposableURI (in nsIURI aURI); */
51 NS_IMETHODIMP
52 nsDefaultURIFixup::CreateExposableURI(nsIURI *aURI, nsIURI **aReturn)
54 NS_ENSURE_ARG_POINTER(aURI);
55 NS_ENSURE_ARG_POINTER(aReturn);
57 bool isWyciwyg = false;
58 aURI->SchemeIs("wyciwyg", &isWyciwyg);
60 nsAutoCString userPass;
61 aURI->GetUserPass(userPass);
63 // most of the time we can just AddRef and return
64 if (!isWyciwyg && userPass.IsEmpty())
66 *aReturn = aURI;
67 NS_ADDREF(*aReturn);
68 return NS_OK;
71 // Rats, we have to massage the URI
72 nsCOMPtr<nsIURI> uri;
73 if (isWyciwyg)
75 nsAutoCString path;
76 nsresult rv = aURI->GetPath(path);
77 NS_ENSURE_SUCCESS(rv, rv);
79 uint32_t pathLength = path.Length();
80 if (pathLength <= 2)
82 return NS_ERROR_FAILURE;
85 // Path is of the form "//123/http://foo/bar", with a variable number of digits.
86 // To figure out where the "real" URL starts, search path for a '/', starting at
87 // the third character.
88 int32_t slashIndex = path.FindChar('/', 2);
89 if (slashIndex == kNotFound)
91 return NS_ERROR_FAILURE;
94 // Get the charset of the original URI so we can pass it to our fixed up URI.
95 nsAutoCString charset;
96 aURI->GetOriginCharset(charset);
98 rv = NS_NewURI(getter_AddRefs(uri),
99 Substring(path, slashIndex + 1, pathLength - slashIndex - 1),
100 charset.get());
101 NS_ENSURE_SUCCESS(rv, rv);
103 else
105 // clone the URI so zapping user:pass doesn't change the original
106 nsresult rv = aURI->Clone(getter_AddRefs(uri));
107 NS_ENSURE_SUCCESS(rv, rv);
110 // hide user:pass unless overridden by pref
111 if (Preferences::GetBool("browser.fixup.hide_user_pass", true))
113 uri->SetUserPass(EmptyCString());
116 // return the fixed-up URI
117 *aReturn = uri;
118 NS_ADDREF(*aReturn);
119 return NS_OK;
122 /* nsIURI createFixupURI (in nsAUTF8String aURIText, in unsigned long aFixupFlags); */
123 NS_IMETHODIMP
124 nsDefaultURIFixup::CreateFixupURI(const nsACString& aStringURI, uint32_t aFixupFlags,
125 nsIInputStream **aPostData, nsIURI **aURI)
127 nsCOMPtr<nsIURIFixupInfo> fixupInfo;
128 nsresult rv = GetFixupURIInfo(aStringURI, aFixupFlags, aPostData,
129 getter_AddRefs(fixupInfo));
130 NS_ENSURE_SUCCESS(rv, rv);
132 fixupInfo->GetPreferredURI(aURI);
133 return rv;
136 NS_IMETHODIMP
137 nsDefaultURIFixup::GetFixupURIInfo(const nsACString& aStringURI, uint32_t aFixupFlags,
138 nsIInputStream **aPostData, nsIURIFixupInfo **aInfo)
140 NS_ENSURE_ARG(!aStringURI.IsEmpty());
142 nsresult rv;
144 nsAutoCString uriString(aStringURI);
146 // Eliminate embedded newlines, which single-line text fields now allow:
147 uriString.StripChars("\r\n");
148 // Cleanup the empty spaces that might be on each end:
149 uriString.Trim(" ");
151 NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE);
153 nsRefPtr<nsDefaultURIFixupInfo> info = new nsDefaultURIFixupInfo(uriString);
154 NS_ADDREF(*aInfo = info);
156 nsCOMPtr<nsIIOService> ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
157 NS_ENSURE_SUCCESS(rv, rv);
158 nsAutoCString scheme;
159 ioService->ExtractScheme(aStringURI, scheme);
161 // View-source is a pseudo scheme. We're interested in fixing up the stuff
162 // after it. The easiest way to do that is to call this method again with the
163 // "view-source:" lopped off and then prepend it again afterwards.
165 if (scheme.LowerCaseEqualsLiteral("view-source"))
167 nsCOMPtr<nsIURIFixupInfo> uriInfo;
168 uint32_t newFixupFlags = aFixupFlags & ~FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
170 rv = GetFixupURIInfo(Substring(uriString,
171 sizeof("view-source:") - 1,
172 uriString.Length() -
173 (sizeof("view-source:") - 1)),
174 newFixupFlags, aPostData, getter_AddRefs(uriInfo));
175 if (NS_FAILED(rv))
176 return NS_ERROR_FAILURE;
177 nsAutoCString spec;
178 nsCOMPtr<nsIURI> uri;
179 uriInfo->GetPreferredURI(getter_AddRefs(uri));
180 if (!uri)
181 return NS_ERROR_FAILURE;
182 uri->GetSpec(spec);
183 uriString.AssignLiteral("view-source:");
184 uriString.Append(spec);
186 else {
187 // Check for if it is a file URL
188 nsCOMPtr<nsIURI> uri;
189 FileURIFixup(uriString, getter_AddRefs(uri));
190 // NB: FileURIFixup only returns a URI if it had to fix the protocol to
191 // do so, so passing in file:///foo/bar will not hit this path:
192 if (uri)
194 uri.swap(info->mFixedURI);
195 info->mPreferredURI = info->mFixedURI;
196 info->mFixupChangedProtocol = true;
197 return NS_OK;
200 #if defined(XP_WIN)
201 // Not a file URL, so translate '\' to '/' for convenience in the common protocols
202 // e.g. catch
204 // http:\\broken.com\address
205 // http:\\broken.com/blah
206 // broken.com\blah
208 // Code will also do partial fix up the following urls
210 // http:\\broken.com\address/somewhere\image.jpg (stops at first forward slash)
211 // http:\\broken.com\blah?arg=somearg\foo.jpg (stops at question mark)
212 // http:\\broken.com#odd\ref (stops at hash)
214 if (scheme.IsEmpty() ||
215 scheme.LowerCaseEqualsLiteral("http") ||
216 scheme.LowerCaseEqualsLiteral("https") ||
217 scheme.LowerCaseEqualsLiteral("ftp"))
219 // Walk the string replacing backslashes with forward slashes until
220 // the end is reached, or a question mark, or a hash, or a forward
221 // slash. The forward slash test is to stop before trampling over
222 // URIs which legitimately contain a mix of both forward and
223 // backward slashes.
224 nsAutoCString::iterator start;
225 nsAutoCString::iterator end;
226 uriString.BeginWriting(start);
227 uriString.EndWriting(end);
228 while (start != end) {
229 if (*start == '?' || *start == '#' || *start == '/')
230 break;
231 if (*start == '\\')
232 *start = '/';
233 ++start;
236 #endif
239 if (!sInitializedPrefCaches) {
240 // Check if we want to fix up common scheme typos.
241 rv = Preferences::AddBoolVarCache(&sFixTypos,
242 "browser.fixup.typo.scheme",
243 sFixTypos);
244 MOZ_ASSERT(NS_SUCCEEDED(rv),
245 "Failed to observe \"browser.fixup.typo.scheme\"");
247 rv = Preferences::AddBoolVarCache(&sFixupKeywords, "keyword.enabled",
248 sFixupKeywords);
249 MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to observe \"keyword.enabled\"");
250 sInitializedPrefCaches = true;
253 // Fix up common scheme typos.
254 if (sFixTypos && (aFixupFlags & FIXUP_FLAG_FIX_SCHEME_TYPOS)) {
256 // Fast-path for common cases.
257 if (scheme.IsEmpty() ||
258 scheme.LowerCaseEqualsLiteral("http") ||
259 scheme.LowerCaseEqualsLiteral("https") ||
260 scheme.LowerCaseEqualsLiteral("ftp") ||
261 scheme.LowerCaseEqualsLiteral("file")) {
262 // Do nothing.
263 } else if (scheme.LowerCaseEqualsLiteral("ttp")) {
264 // ttp -> http.
265 uriString.Replace(0, 3, "http");
266 scheme.AssignLiteral("http");
267 info->mFixupChangedProtocol = true;
268 } else if (scheme.LowerCaseEqualsLiteral("ttps")) {
269 // ttps -> https.
270 uriString.Replace(0, 4, "https");
271 scheme.AssignLiteral("https");
272 info->mFixupChangedProtocol = true;
273 } else if (scheme.LowerCaseEqualsLiteral("tps")) {
274 // tps -> https.
275 uriString.Replace(0, 3, "https");
276 scheme.AssignLiteral("https");
277 info->mFixupChangedProtocol = true;
278 } else if (scheme.LowerCaseEqualsLiteral("ps")) {
279 // ps -> https.
280 uriString.Replace(0, 2, "https");
281 scheme.AssignLiteral("https");
282 info->mFixupChangedProtocol = true;
283 } else if (scheme.LowerCaseEqualsLiteral("ile")) {
284 // ile -> file.
285 uriString.Replace(0, 3, "file");
286 scheme.AssignLiteral("file");
287 info->mFixupChangedProtocol = true;
288 } else if (scheme.LowerCaseEqualsLiteral("le")) {
289 // le -> file.
290 uriString.Replace(0, 2, "file");
291 scheme.AssignLiteral("file");
292 info->mFixupChangedProtocol = true;
296 // Now we need to check whether "scheme" is something we don't
297 // really know about.
298 nsCOMPtr<nsIProtocolHandler> ourHandler, extHandler;
300 ioService->GetProtocolHandler(scheme.get(), getter_AddRefs(ourHandler));
301 extHandler = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"default");
303 nsCOMPtr<nsIURI> uri;
304 if (ourHandler != extHandler || !PossiblyHostPortUrl(uriString)) {
305 // Just try to create an URL out of it
306 rv = NS_NewURI(getter_AddRefs(uri), uriString, nullptr);
307 if (NS_SUCCEEDED(rv)) {
308 info->mFixedURI = uri;
311 if (!uri && rv != NS_ERROR_MALFORMED_URI) {
312 return rv;
316 if (uri && ourHandler == extHandler && sFixupKeywords &&
317 (aFixupFlags & FIXUP_FLAG_FIX_SCHEME_TYPOS)) {
318 nsCOMPtr<nsIExternalProtocolService> extProtService =
319 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
320 if (extProtService) {
321 bool handlerExists = false;
322 rv = extProtService->ExternalProtocolHandlerExists(scheme.get(), &handlerExists);
323 if (NS_FAILED(rv)) {
324 return rv;
326 // This basically means we're dealing with a theoretically valid
327 // URI... but we have no idea how to load it. (e.g. "christmas:humbug")
328 // It's more likely the user wants to search, and so we
329 // chuck this over to their preferred search provider instead:
330 if (!handlerExists) {
331 nsresult rv = KeywordToURI(uriString, aPostData, getter_AddRefs(uri));
332 if (NS_SUCCEEDED(rv) && uri) {
333 info->mFixupUsedKeyword = true;
339 if (uri) {
340 if (aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI)
341 info->mFixupCreatedAlternateURI = MakeAlternateURI(uri);
342 info->mPreferredURI = uri;
343 return NS_OK;
346 // Fix up protocol string before calling KeywordURIFixup, because
347 // it cares about the hostname of such URIs:
348 nsCOMPtr<nsIURI> uriWithProtocol;
349 bool inputHadDuffProtocol = false;
351 // Prune duff protocol schemes
353 // ://totallybroken.url.com
354 // //shorthand.url.com
356 if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("://")))
358 uriString = StringTail(uriString, uriString.Length() - 3);
359 inputHadDuffProtocol = true;
360 } else if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("//"))) {
361 uriString = StringTail(uriString, uriString.Length() - 2);
362 inputHadDuffProtocol = true;
365 // NB: this rv gets returned at the end of this method if we never
366 // do a keyword fixup after this (because the pref or the flags passed
367 // might not let us).
368 rv = FixupURIProtocol(uriString, info, getter_AddRefs(uriWithProtocol));
369 if (uriWithProtocol) {
370 info->mFixedURI = uriWithProtocol;
373 // See if it is a keyword
374 // Test whether keywords need to be fixed up
375 if (sFixupKeywords && (aFixupFlags & FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP) &&
376 !inputHadDuffProtocol) {
377 KeywordURIFixup(uriString, info, aPostData);
378 if (info->mPreferredURI)
379 return NS_OK;
382 // Did the caller want us to try an alternative URI?
383 // If so, attempt to fixup http://foo into http://www.foo.com
385 if (info->mFixedURI && aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI) {
386 info->mFixupCreatedAlternateURI = MakeAlternateURI(info->mFixedURI);
389 // If we still haven't been able to construct a valid URI, try to force a
390 // keyword match. This catches search strings with '.' or ':' in them.
391 if (info->mFixedURI)
393 info->mPreferredURI = info->mFixedURI;
395 else if (sFixupKeywords && (aFixupFlags & FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP))
397 rv = KeywordToURI(aStringURI, aPostData, getter_AddRefs(info->mPreferredURI));
398 if (NS_SUCCEEDED(rv) && info->mPreferredURI)
400 info->mFixupUsedKeyword = true;
401 return NS_OK;
405 return rv;
408 NS_IMETHODIMP nsDefaultURIFixup::KeywordToURI(const nsACString& aKeyword,
409 nsIInputStream **aPostData,
410 nsIURI **aURI)
412 *aURI = nullptr;
413 if (aPostData) {
414 *aPostData = nullptr;
416 NS_ENSURE_STATE(Preferences::GetRootBranch());
418 // Strip leading "?" and leading/trailing spaces from aKeyword
419 nsAutoCString keyword(aKeyword);
420 if (StringBeginsWith(keyword, NS_LITERAL_CSTRING("?"))) {
421 keyword.Cut(0, 1);
423 keyword.Trim(" ");
425 if (XRE_GetProcessType() == GeckoProcessType_Content) {
426 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
427 if (!contentChild) {
428 return NS_ERROR_NOT_AVAILABLE;
431 ipc::OptionalInputStreamParams postData;
432 ipc::OptionalURIParams uri;
433 if (!contentChild->SendKeywordToURI(keyword, &postData, &uri)) {
434 return NS_ERROR_FAILURE;
437 if (aPostData) {
438 nsTArray<ipc::FileDescriptor> fds;
439 nsCOMPtr<nsIInputStream> temp = DeserializeInputStream(postData, fds);
440 temp.forget(aPostData);
442 MOZ_ASSERT(fds.IsEmpty());
445 nsCOMPtr<nsIURI> temp = DeserializeURI(uri);
446 temp.forget(aURI);
447 return NS_OK;
450 #ifdef MOZ_TOOLKIT_SEARCH
451 // Try falling back to the search service's default search engine
452 nsCOMPtr<nsIBrowserSearchService> searchSvc = do_GetService("@mozilla.org/browser/search-service;1");
453 if (searchSvc) {
454 nsCOMPtr<nsISearchEngine> defaultEngine;
455 searchSvc->GetDefaultEngine(getter_AddRefs(defaultEngine));
456 if (defaultEngine) {
457 nsCOMPtr<nsISearchSubmission> submission;
458 nsAutoString responseType;
459 // We allow default search plugins to specify alternate
460 // parameters that are specific to keyword searches.
461 NS_NAMED_LITERAL_STRING(mozKeywordSearch, "application/x-moz-keywordsearch");
462 bool supportsResponseType = false;
463 defaultEngine->SupportsResponseType(mozKeywordSearch, &supportsResponseType);
464 if (supportsResponseType) {
465 responseType.Assign(mozKeywordSearch);
468 defaultEngine->GetSubmission(NS_ConvertUTF8toUTF16(keyword),
469 responseType,
470 NS_LITERAL_STRING("keyword"),
471 getter_AddRefs(submission));
473 if (submission) {
474 nsCOMPtr<nsIInputStream> postData;
475 submission->GetPostData(getter_AddRefs(postData));
476 if (aPostData) {
477 postData.forget(aPostData);
478 } else if (postData) {
479 // The submission specifies POST data (i.e. the search
480 // engine's "method" is POST), but our caller didn't allow
481 // passing post data back. No point passing back a URL that
482 // won't load properly.
483 return NS_ERROR_FAILURE;
486 // This notification is meant for Firefox Health Report so it
487 // can increment counts from the search engine. The assumption
488 // here is that this keyword/submission will eventually result
489 // in a search. Since we only generate a URI here, there is the
490 // possibility we'll increment the counter without actually
491 // incurring a search. A robust solution would involve currying
492 // the search engine's name through various function calls.
493 nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
494 if (obsSvc) {
495 // Note that "keyword-search" refers to a search via the url
496 // bar, not a bookmarks keyword search.
497 obsSvc->NotifyObservers(defaultEngine, "keyword-search", NS_ConvertUTF8toUTF16(keyword).get());
500 return submission->GetUri(aURI);
504 #endif
506 // out of options
507 return NS_ERROR_NOT_AVAILABLE;
510 bool nsDefaultURIFixup::MakeAlternateURI(nsIURI *aURI)
512 if (!Preferences::GetRootBranch())
514 return false;
516 if (!Preferences::GetBool("browser.fixup.alternate.enabled", true))
518 return false;
521 // Code only works for http. Not for any other protocol including https!
522 bool isHttp = false;
523 aURI->SchemeIs("http", &isHttp);
524 if (!isHttp) {
525 return false;
528 // Security - URLs with user / password info should NOT be fixed up
529 nsAutoCString userpass;
530 aURI->GetUserPass(userpass);
531 if (!userpass.IsEmpty()) {
532 return false;
535 nsAutoCString oldHost;
536 nsAutoCString newHost;
537 aURI->GetHost(oldHost);
539 // Count the dots
540 int32_t numDots = 0;
541 nsReadingIterator<char> iter;
542 nsReadingIterator<char> iterEnd;
543 oldHost.BeginReading(iter);
544 oldHost.EndReading(iterEnd);
545 while (iter != iterEnd) {
546 if (*iter == '.')
547 numDots++;
548 ++iter;
552 // Get the prefix and suffix to stick onto the new hostname. By default these
553 // are www. & .com but they could be any other value, e.g. www. & .org
555 nsAutoCString prefix("www.");
556 nsAdoptingCString prefPrefix =
557 Preferences::GetCString("browser.fixup.alternate.prefix");
558 if (prefPrefix)
560 prefix.Assign(prefPrefix);
563 nsAutoCString suffix(".com");
564 nsAdoptingCString prefSuffix =
565 Preferences::GetCString("browser.fixup.alternate.suffix");
566 if (prefSuffix)
568 suffix.Assign(prefSuffix);
571 if (numDots == 0)
573 newHost.Assign(prefix);
574 newHost.Append(oldHost);
575 newHost.Append(suffix);
577 else if (numDots == 1)
579 if (!prefix.IsEmpty() &&
580 oldHost.EqualsIgnoreCase(prefix.get(), prefix.Length())) {
581 newHost.Assign(oldHost);
582 newHost.Append(suffix);
584 else if (!suffix.IsEmpty()) {
585 newHost.Assign(prefix);
586 newHost.Append(oldHost);
588 else
590 // Do nothing
591 return false;
594 else
596 // Do nothing
597 return false;
600 if (newHost.IsEmpty()) {
601 return false;
604 // Assign the new host string over the old one
605 aURI->SetHost(newHost);
606 return true;
610 * Check if the host name starts with ftp\d*\. and it's not directly followed
611 * by the tld.
613 bool nsDefaultURIFixup::IsLikelyFTP(const nsCString &aHostSpec)
615 bool likelyFTP = false;
616 if (aHostSpec.EqualsIgnoreCase("ftp", 3)) {
617 nsACString::const_iterator iter;
618 nsACString::const_iterator end;
619 aHostSpec.BeginReading(iter);
620 aHostSpec.EndReading(end);
621 iter.advance(3); // move past the "ftp" part
623 while (iter != end)
625 if (*iter == '.') {
626 // now make sure the name has at least one more dot in it
627 ++iter;
628 while (iter != end)
630 if (*iter == '.') {
631 likelyFTP = true;
632 break;
634 ++iter;
636 break;
638 else if (!nsCRT::IsAsciiDigit(*iter)) {
639 break;
641 ++iter;
644 return likelyFTP;
647 nsresult nsDefaultURIFixup::FileURIFixup(const nsACString& aStringURI,
648 nsIURI** aURI)
650 nsAutoCString uriSpecOut;
652 nsresult rv = ConvertFileToStringURI(aStringURI, uriSpecOut);
653 if (NS_SUCCEEDED(rv))
655 // if this is file url, uriSpecOut is already in FS charset
656 if(NS_SUCCEEDED(NS_NewURI(aURI, uriSpecOut.get(), nullptr)))
657 return NS_OK;
659 return NS_ERROR_FAILURE;
662 nsresult nsDefaultURIFixup::ConvertFileToStringURI(const nsACString& aIn,
663 nsCString& aOut)
665 bool attemptFixup = false;
667 #if defined(XP_WIN)
668 // Check for \ in the url-string or just a drive (PC)
669 if(kNotFound != aIn.FindChar('\\') ||
670 (aIn.Length() == 2 && (aIn.Last() == ':' || aIn.Last() == '|')))
672 attemptFixup = true;
674 #elif defined(XP_UNIX)
675 // Check if it starts with / (UNIX)
676 if(aIn.First() == '/')
678 attemptFixup = true;
680 #else
681 // Do nothing (All others for now)
682 #endif
684 if (attemptFixup)
686 // Test if this is a valid path by trying to create a local file
687 // object. The URL of that is returned if successful.
689 // NOTE: Please be sure to check that the call to NS_NewLocalFile
690 // rejects bad file paths when using this code on a new
691 // platform.
693 nsCOMPtr<nsIFile> filePath;
694 nsresult rv;
696 // this is not the real fix but a temporary fix
697 // in order to really fix the problem, we need to change the
698 // nsICmdLineService interface to use wstring to pass paramenters
699 // instead of string since path name and other argument could be
700 // in non ascii.(see bug 87127) Since it is too risky to make interface change right
701 // now, we decide not to do so now.
702 // Therefore, the aIn we receive here maybe already in damage form
703 // (e.g. treat every bytes as ISO-8859-1 and cast up to char16_t
704 // while the real data could be in file system charset )
705 // we choice the following logic which will work for most of the case.
706 // Case will still failed only if it meet ALL the following condiction:
707 // 1. running on CJK, Russian, or Greek system, and
708 // 2. user type it from URL bar
709 // 3. the file name contains character in the range of
710 // U+00A1-U+00FF but encode as different code point in file
711 // system charset (e.g. ACP on window)- this is very rare case
712 // We should remove this logic and convert to File system charset here
713 // once we change nsICmdLineService to use wstring and ensure
714 // all the Unicode data come in is correctly converted.
715 // XXXbz nsICmdLineService doesn't hand back unicode, so in some cases
716 // what we have is actually a "utf8" version of a "utf16" string that's
717 // actually byte-expanded native-encoding data. Someone upstream needs
718 // to stop using AssignWithConversion and do things correctly. See bug
719 // 58866 for what happens if we remove this
720 // PossiblyByteExpandedFileName check.
721 NS_ConvertUTF8toUTF16 in(aIn);
722 if (PossiblyByteExpandedFileName(in)) {
723 // removes high byte
724 rv = NS_NewNativeLocalFile(NS_LossyConvertUTF16toASCII(in), false, getter_AddRefs(filePath));
726 else {
727 // input is unicode
728 rv = NS_NewLocalFile(in, false, getter_AddRefs(filePath));
731 if (NS_SUCCEEDED(rv))
733 NS_GetURLSpecFromFile(filePath, aOut);
734 return NS_OK;
738 return NS_ERROR_FAILURE;
742 nsresult
743 nsDefaultURIFixup::FixupURIProtocol(const nsACString & aURIString,
744 nsDefaultURIFixupInfo* aFixupInfo,
745 nsIURI** aURI)
747 nsAutoCString uriString(aURIString);
748 *aURI = nullptr;
750 // Add ftp:// or http:// to front of url if it has no spec
752 // Should fix:
754 // no-scheme.com
755 // ftp.no-scheme.com
756 // ftp4.no-scheme.com
757 // no-scheme.com/query?foo=http://www.foo.com
759 int32_t schemeDelim = uriString.Find("://",0);
760 int32_t firstDelim = uriString.FindCharInSet("/:");
761 if (schemeDelim <= 0 ||
762 (firstDelim != -1 && schemeDelim > firstDelim)) {
763 // find host name
764 int32_t hostPos = uriString.FindCharInSet("/:?#");
765 if (hostPos == -1)
766 hostPos = uriString.Length();
768 // extract host name
769 nsAutoCString hostSpec;
770 uriString.Left(hostSpec, hostPos);
772 // insert url spec corresponding to host name
773 if (IsLikelyFTP(hostSpec))
774 uriString.InsertLiteral("ftp://", 0);
775 else
776 uriString.InsertLiteral("http://", 0);
777 aFixupInfo->mFixupChangedProtocol = true;
778 } // end if checkprotocol
780 return NS_NewURI(aURI, uriString, nullptr);
784 bool nsDefaultURIFixup::PossiblyHostPortUrl(const nsACString &aUrl)
786 // Oh dear, the protocol is invalid. Test if the protocol might
787 // actually be a url without a protocol:
789 // http://www.faqs.org/rfcs/rfc1738.html
790 // http://www.faqs.org/rfcs/rfc2396.html
792 // e.g. Anything of the form:
794 // <hostname>:<port> or
795 // <hostname>:<port>/
797 // Where <hostname> is a string of alphanumeric characters and dashes
798 // separated by dots.
799 // and <port> is a 5 or less digits. This actually breaks the rfc2396
800 // definition of a scheme which allows dots in schemes.
802 // Note:
803 // People expecting this to work with
804 // <user>:<password>@<host>:<port>/<url-path> will be disappointed!
806 // Note: Parser could be a lot tighter, tossing out silly hostnames
807 // such as those containing consecutive dots and so on.
809 // Read the hostname which should of the form
810 // [a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)*:
812 nsACString::const_iterator iterBegin;
813 nsACString::const_iterator iterEnd;
814 aUrl.BeginReading(iterBegin);
815 aUrl.EndReading(iterEnd);
816 nsACString::const_iterator iter = iterBegin;
818 while (iter != iterEnd)
820 uint32_t chunkSize = 0;
821 // Parse a chunk of the address
822 while (iter != iterEnd &&
823 (*iter == '-' ||
824 nsCRT::IsAsciiAlpha(*iter) ||
825 nsCRT::IsAsciiDigit(*iter)))
827 ++chunkSize;
828 ++iter;
830 if (chunkSize == 0 || iter == iterEnd)
832 return false;
834 if (*iter == ':')
836 // Go onto checking the for the digits
837 break;
839 if (*iter != '.')
841 // Whatever it is, it ain't a hostname!
842 return false;
844 ++iter;
846 if (iter == iterEnd)
848 // No point continuing since there is no colon
849 return false;
851 ++iter;
853 // Count the number of digits after the colon and before the
854 // next forward slash (or end of string)
856 uint32_t digitCount = 0;
857 while (iter != iterEnd && digitCount <= 5)
859 if (nsCRT::IsAsciiDigit(*iter))
861 digitCount++;
863 else if (*iter == '/')
865 break;
867 else
869 // Whatever it is, it ain't a port!
870 return false;
872 ++iter;
874 if (digitCount == 0 || digitCount > 5)
876 // No digits or more digits than a port would have.
877 return false;
880 // Yes, it's possibly a host:port url
881 return true;
884 bool nsDefaultURIFixup::PossiblyByteExpandedFileName(const nsAString& aIn)
886 // XXXXX HACK XXXXX : please don't copy this code.
887 // There are cases where aIn contains the locale byte chars padded to short
888 // (thus the name "ByteExpanded"); whereas other cases
889 // have proper Unicode code points.
890 // This is a temporary fix. Please refer to 58866, 86948
892 nsReadingIterator<char16_t> iter;
893 nsReadingIterator<char16_t> iterEnd;
894 aIn.BeginReading(iter);
895 aIn.EndReading(iterEnd);
896 while (iter != iterEnd)
898 if (*iter >= 0x0080 && *iter <= 0x00FF)
899 return true;
900 ++iter;
902 return false;
905 void nsDefaultURIFixup::KeywordURIFixup(const nsACString & aURIString,
906 nsDefaultURIFixupInfo* aFixupInfo,
907 nsIInputStream **aPostData)
909 // These are keyword formatted strings
910 // "what is mozilla"
911 // "what is mozilla?"
912 // "docshell site:mozilla.org" - has no dot/colon in the first space-separated substring
913 // "?mozilla" - anything that begins with a question mark
914 // "?site:mozilla.org docshell"
915 // Things that have a quote before the first dot/colon
916 // "mozilla" - checked against a whitelist to see if it's a host or not
917 // ".mozilla", "mozilla." - ditto
919 // These are not keyword formatted strings
920 // "www.blah.com" - first space-separated substring contains a dot, doesn't start with "?"
921 // "www.blah.com stuff"
922 // "nonQualifiedHost:80" - first space-separated substring contains a colon, doesn't start with "?"
923 // "nonQualifiedHost:80 args"
924 // "nonQualifiedHost?"
925 // "nonQualifiedHost?args"
926 // "nonQualifiedHost?some args"
927 // "blah.com."
929 // Note: uint32_t(kNotFound) is greater than any actual location
930 // in practice. So if we cast all locations to uint32_t, then a <
931 // b guarantees that either b is kNotFound and a is found, or both
932 // are found and a found before b.
933 uint32_t dotLoc = uint32_t(aURIString.FindChar('.'));
934 nsAutoCString tmpURIString(aURIString);
935 uint32_t lastDotLoc = uint32_t(tmpURIString.RFindChar('.'));
936 uint32_t colonLoc = uint32_t(aURIString.FindChar(':'));
937 uint32_t spaceLoc = uint32_t(aURIString.FindChar(' '));
938 if (spaceLoc == 0) {
939 // Treat this as not found
940 spaceLoc = uint32_t(kNotFound);
942 uint32_t qMarkLoc = uint32_t(aURIString.FindChar('?'));
943 uint32_t quoteLoc = std::min(uint32_t(aURIString.FindChar('"')),
944 uint32_t(aURIString.FindChar('\'')));
946 nsresult rv;
947 // We do keyword lookups if a space or quote preceded the dot, colon
948 // or question mark (or if the latter were not found):
949 if (((spaceLoc < dotLoc || quoteLoc < dotLoc) &&
950 (spaceLoc < colonLoc || quoteLoc < colonLoc) &&
951 (spaceLoc < qMarkLoc || quoteLoc < qMarkLoc)) ||
952 qMarkLoc == 0)
954 rv = KeywordToURI(aFixupInfo->mOriginalInput, aPostData,
955 getter_AddRefs(aFixupInfo->mPreferredURI));
956 if (NS_SUCCEEDED(rv) && aFixupInfo->mPreferredURI)
958 aFixupInfo->mFixupUsedKeyword = true;
961 // ... or if there is no question mark or colon, and there is either no
962 // dot, or exactly 1 and it is the first or last character of the input:
963 else if ((dotLoc == uint32_t(kNotFound) ||
964 (dotLoc == lastDotLoc && (dotLoc == 0 || dotLoc == aURIString.Length() - 1))) &&
965 colonLoc == uint32_t(kNotFound) && qMarkLoc == uint32_t(kNotFound))
967 nsAutoCString asciiHost;
968 if (aFixupInfo->mFixedURI &&
969 NS_SUCCEEDED(aFixupInfo->mFixedURI->GetAsciiHost(asciiHost)) &&
970 !asciiHost.IsEmpty())
972 // Check if this domain is whitelisted as an actual
973 // domain (which will prevent a keyword query)
974 // NB: any processing of the host here should stay in sync with
975 // code in the front-end(s) that set the pref.
976 nsAutoCString pref("browser.fixup.domainwhitelist.");
977 if (dotLoc == aURIString.Length() - 1) {
978 pref.Append(Substring(asciiHost, 0, asciiHost.Length() - 1));
979 } else {
980 pref.Append(asciiHost);
982 if (Preferences::GetBool(pref.get(), false))
984 return;
987 // If we get here, we don't have a valid URI, or we did but the
988 // host is not whitelisted, so we do a keyword search *anyway*:
989 rv = KeywordToURI(aFixupInfo->mOriginalInput, aPostData,
990 getter_AddRefs(aFixupInfo->mPreferredURI));
991 if (NS_SUCCEEDED(rv) && aFixupInfo->mPreferredURI)
993 aFixupInfo->mFixupUsedKeyword = true;
999 nsresult NS_NewURIFixup(nsIURIFixup **aURIFixup)
1001 nsDefaultURIFixup *fixup = new nsDefaultURIFixup;
1002 if (fixup == nullptr)
1004 return NS_ERROR_OUT_OF_MEMORY;
1006 return fixup->QueryInterface(NS_GET_IID(nsIURIFixup), (void **) aURIFixup);
1010 /* Implementation of nsIURIFixupInfo */
1011 NS_IMPL_ISUPPORTS(nsDefaultURIFixupInfo, nsIURIFixupInfo)
1013 nsDefaultURIFixupInfo::nsDefaultURIFixupInfo(const nsACString& aOriginalInput):
1014 mFixupUsedKeyword(false),
1015 mFixupChangedProtocol(false),
1016 mFixupCreatedAlternateURI(false)
1018 mOriginalInput = aOriginalInput;
1022 nsDefaultURIFixupInfo::~nsDefaultURIFixupInfo()
1026 NS_IMETHODIMP
1027 nsDefaultURIFixupInfo::GetConsumer(nsISupports** aConsumer)
1029 *aConsumer = mConsumer;
1030 NS_IF_ADDREF(*aConsumer);
1031 return NS_OK;
1034 NS_IMETHODIMP
1035 nsDefaultURIFixupInfo::SetConsumer(nsISupports* aConsumer)
1037 mConsumer = aConsumer;
1038 return NS_OK;
1041 NS_IMETHODIMP
1042 nsDefaultURIFixupInfo::GetPreferredURI(nsIURI** aPreferredURI)
1044 *aPreferredURI = mPreferredURI;
1045 NS_IF_ADDREF(*aPreferredURI);
1046 return NS_OK;
1049 NS_IMETHODIMP
1050 nsDefaultURIFixupInfo::GetFixedURI(nsIURI** aFixedURI)
1052 *aFixedURI = mFixedURI;
1053 NS_IF_ADDREF(*aFixedURI);
1054 return NS_OK;
1057 NS_IMETHODIMP
1058 nsDefaultURIFixupInfo::GetFixupUsedKeyword(bool* aOut)
1060 *aOut = mFixupUsedKeyword;
1061 return NS_OK;
1064 NS_IMETHODIMP
1065 nsDefaultURIFixupInfo::GetFixupChangedProtocol(bool* aOut)
1067 *aOut = mFixupChangedProtocol;
1068 return NS_OK;
1071 NS_IMETHODIMP
1072 nsDefaultURIFixupInfo::GetFixupCreatedAlternateURI(bool* aOut)
1074 *aOut = mFixupCreatedAlternateURI;
1075 return NS_OK;
1078 NS_IMETHODIMP
1079 nsDefaultURIFixupInfo::GetOriginalInput(nsACString& aInput)
1081 aInput = mOriginalInput;
1082 return NS_OK;