summaryrefslogtreecommitdiffstats
path: root/docshell/base/nsDefaultURIFixup.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'docshell/base/nsDefaultURIFixup.cpp')
-rw-r--r--docshell/base/nsDefaultURIFixup.cpp1152
1 files changed, 1152 insertions, 0 deletions
diff --git a/docshell/base/nsDefaultURIFixup.cpp b/docshell/base/nsDefaultURIFixup.cpp
new file mode 100644
index 000000000..e519720ab
--- /dev/null
+++ b/docshell/base/nsDefaultURIFixup.cpp
@@ -0,0 +1,1152 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsIProtocolHandler.h"
+#include "nsCRT.h"
+
+#include "nsIFile.h"
+#include <algorithm>
+
+#ifdef MOZ_TOOLKIT_SEARCH
+#include "nsIBrowserSearchService.h"
+#endif
+
+#include "nsIURIFixup.h"
+#include "nsDefaultURIFixup.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "mozilla/Tokenizer.h"
+#include "nsIObserverService.h"
+#include "nsXULAppAPI.h"
+
+// Used to check if external protocol schemes are usable
+#include "nsCExternalHandlerService.h"
+#include "nsIExternalProtocolService.h"
+
+using namespace mozilla;
+
+/* Implementation file */
+NS_IMPL_ISUPPORTS(nsDefaultURIFixup, nsIURIFixup)
+
+static bool sInitializedPrefCaches = false;
+static bool sFixTypos = true;
+static bool sDNSFirstForSingleWords = false;
+static bool sFixupKeywords = true;
+
+nsDefaultURIFixup::nsDefaultURIFixup()
+{
+}
+
+nsDefaultURIFixup::~nsDefaultURIFixup()
+{
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixup::CreateExposableURI(nsIURI* aURI, nsIURI** aReturn)
+{
+ NS_ENSURE_ARG_POINTER(aURI);
+ NS_ENSURE_ARG_POINTER(aReturn);
+
+ bool isWyciwyg = false;
+ aURI->SchemeIs("wyciwyg", &isWyciwyg);
+
+ nsAutoCString userPass;
+ aURI->GetUserPass(userPass);
+
+ // most of the time we can just AddRef and return
+ if (!isWyciwyg && userPass.IsEmpty()) {
+ *aReturn = aURI;
+ NS_ADDREF(*aReturn);
+ return NS_OK;
+ }
+
+ // Rats, we have to massage the URI
+ nsCOMPtr<nsIURI> uri;
+ if (isWyciwyg) {
+ nsAutoCString path;
+ nsresult rv = aURI->GetPath(path);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t pathLength = path.Length();
+ if (pathLength <= 2) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Path is of the form "//123/http://foo/bar", with a variable number of
+ // digits. To figure out where the "real" URL starts, search path for a '/',
+ // starting at the third character.
+ int32_t slashIndex = path.FindChar('/', 2);
+ if (slashIndex == kNotFound) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Get the charset of the original URI so we can pass it to our fixed up
+ // URI.
+ nsAutoCString charset;
+ aURI->GetOriginCharset(charset);
+
+ rv = NS_NewURI(getter_AddRefs(uri),
+ Substring(path, slashIndex + 1, pathLength - slashIndex - 1),
+ charset.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ // clone the URI so zapping user:pass doesn't change the original
+ nsresult rv = aURI->Clone(getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // hide user:pass unless overridden by pref
+ if (Preferences::GetBool("browser.fixup.hide_user_pass", true)) {
+ uri->SetUserPass(EmptyCString());
+ }
+
+ uri.forget(aReturn);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixup::CreateFixupURI(const nsACString& aStringURI,
+ uint32_t aFixupFlags,
+ nsIInputStream** aPostData, nsIURI** aURI)
+{
+ nsCOMPtr<nsIURIFixupInfo> fixupInfo;
+ nsresult rv = GetFixupURIInfo(aStringURI, aFixupFlags, aPostData,
+ getter_AddRefs(fixupInfo));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ fixupInfo->GetPreferredURI(aURI);
+ return rv;
+}
+
+// Returns true if the URL contains a user:password@ or user@
+static bool
+HasUserPassword(const nsACString& aStringURI)
+{
+ mozilla::Tokenizer parser(aStringURI);
+ mozilla::Tokenizer::Token token;
+
+ // May start with any of "protocol:", "protocol://", "//", "://"
+ if (parser.Check(Tokenizer::TOKEN_WORD, token)) { // Skip protocol if any
+ }
+ if (parser.CheckChar(':')) { // Skip colon if found
+ }
+ while (parser.CheckChar('/')) { // Skip all of the following slashes
+ }
+
+ while (parser.Next(token)) {
+ if (token.Type() == Tokenizer::TOKEN_CHAR) {
+ if (token.AsChar() == '/') {
+ return false;
+ }
+ if (token.AsChar() == '@') {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixup::GetFixupURIInfo(const nsACString& aStringURI,
+ uint32_t aFixupFlags,
+ nsIInputStream** aPostData,
+ nsIURIFixupInfo** aInfo)
+{
+ NS_ENSURE_ARG(!aStringURI.IsEmpty());
+
+ nsresult rv;
+
+ nsAutoCString uriString(aStringURI);
+
+ // Eliminate embedded newlines, which single-line text fields now allow:
+ uriString.StripChars("\r\n");
+ // Cleanup the empty spaces that might be on each end:
+ uriString.Trim(" ");
+
+ NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE);
+
+ RefPtr<nsDefaultURIFixupInfo> info = new nsDefaultURIFixupInfo(uriString);
+ NS_ADDREF(*aInfo = info);
+
+ nsCOMPtr<nsIIOService> ioService =
+ do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsAutoCString scheme;
+ ioService->ExtractScheme(aStringURI, scheme);
+
+ // View-source is a pseudo scheme. We're interested in fixing up the stuff
+ // after it. The easiest way to do that is to call this method again with the
+ // "view-source:" lopped off and then prepend it again afterwards.
+
+ if (scheme.LowerCaseEqualsLiteral("view-source")) {
+ nsCOMPtr<nsIURIFixupInfo> uriInfo;
+ // We disable keyword lookup and alternate URIs so that small typos don't
+ // cause us to look at very different domains
+ uint32_t newFixupFlags = aFixupFlags & ~FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP
+ & ~FIXUP_FLAGS_MAKE_ALTERNATE_URI;
+
+ const uint32_t viewSourceLen = sizeof("view-source:") - 1;
+ nsAutoCString innerURIString(Substring(uriString, viewSourceLen,
+ uriString.Length() -
+ viewSourceLen));
+ // Prevent recursion:
+ innerURIString.Trim(" ");
+ nsAutoCString innerScheme;
+ ioService->ExtractScheme(innerURIString, innerScheme);
+ if (innerScheme.LowerCaseEqualsLiteral("view-source")) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = GetFixupURIInfo(innerURIString, newFixupFlags, aPostData,
+ getter_AddRefs(uriInfo));
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+ nsAutoCString spec;
+ nsCOMPtr<nsIURI> uri;
+ uriInfo->GetPreferredURI(getter_AddRefs(uri));
+ if (!uri) {
+ return NS_ERROR_FAILURE;
+ }
+ uri->GetSpec(spec);
+ uriString.AssignLiteral("view-source:");
+ uriString.Append(spec);
+ } else {
+ // Check for if it is a file URL
+ nsCOMPtr<nsIURI> uri;
+ FileURIFixup(uriString, getter_AddRefs(uri));
+ // NB: FileURIFixup only returns a URI if it had to fix the protocol to
+ // do so, so passing in file:///foo/bar will not hit this path:
+ if (uri) {
+ uri.swap(info->mFixedURI);
+ info->mPreferredURI = info->mFixedURI;
+ info->mFixupChangedProtocol = true;
+ return NS_OK;
+ }
+ }
+
+ if (!sInitializedPrefCaches) {
+ // Check if we want to fix up common scheme typos.
+ rv = Preferences::AddBoolVarCache(&sFixTypos,
+ "browser.fixup.typo.scheme",
+ sFixTypos);
+ MOZ_ASSERT(NS_SUCCEEDED(rv),
+ "Failed to observe \"browser.fixup.typo.scheme\"");
+
+ rv = Preferences::AddBoolVarCache(&sDNSFirstForSingleWords,
+ "browser.fixup.dns_first_for_single_words",
+ sDNSFirstForSingleWords);
+ MOZ_ASSERT(NS_SUCCEEDED(rv),
+ "Failed to observe \"browser.fixup.dns_first_for_single_words\"");
+
+ rv = Preferences::AddBoolVarCache(&sFixupKeywords, "keyword.enabled",
+ sFixupKeywords);
+ MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to observe \"keyword.enabled\"");
+ sInitializedPrefCaches = true;
+ }
+
+ // Fix up common scheme typos.
+ if (sFixTypos && (aFixupFlags & FIXUP_FLAG_FIX_SCHEME_TYPOS)) {
+ // Fast-path for common cases.
+ if (scheme.IsEmpty() ||
+ scheme.LowerCaseEqualsLiteral("http") ||
+ scheme.LowerCaseEqualsLiteral("https") ||
+ scheme.LowerCaseEqualsLiteral("ftp") ||
+ scheme.LowerCaseEqualsLiteral("file")) {
+ // Do nothing.
+ } else if (scheme.LowerCaseEqualsLiteral("ttp")) {
+ // ttp -> http.
+ uriString.Replace(0, 3, "http");
+ scheme.AssignLiteral("http");
+ info->mFixupChangedProtocol = true;
+ } else if (scheme.LowerCaseEqualsLiteral("ttps")) {
+ // ttps -> https.
+ uriString.Replace(0, 4, "https");
+ scheme.AssignLiteral("https");
+ info->mFixupChangedProtocol = true;
+ } else if (scheme.LowerCaseEqualsLiteral("tps")) {
+ // tps -> https.
+ uriString.Replace(0, 3, "https");
+ scheme.AssignLiteral("https");
+ info->mFixupChangedProtocol = true;
+ } else if (scheme.LowerCaseEqualsLiteral("ps")) {
+ // ps -> https.
+ uriString.Replace(0, 2, "https");
+ scheme.AssignLiteral("https");
+ info->mFixupChangedProtocol = true;
+ } else if (scheme.LowerCaseEqualsLiteral("ile")) {
+ // ile -> file.
+ uriString.Replace(0, 3, "file");
+ scheme.AssignLiteral("file");
+ info->mFixupChangedProtocol = true;
+ } else if (scheme.LowerCaseEqualsLiteral("le")) {
+ // le -> file.
+ uriString.Replace(0, 2, "file");
+ scheme.AssignLiteral("file");
+ info->mFixupChangedProtocol = true;
+ }
+ }
+
+ // Now we need to check whether "scheme" is something we don't
+ // really know about.
+ nsCOMPtr<nsIProtocolHandler> ourHandler, extHandler;
+
+ ioService->GetProtocolHandler(scheme.get(), getter_AddRefs(ourHandler));
+ extHandler = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "default");
+
+ if (ourHandler != extHandler || !PossiblyHostPortUrl(uriString)) {
+ // Just try to create an URL out of it
+ rv = NS_NewURI(getter_AddRefs(info->mFixedURI), uriString, nullptr);
+
+ if (!info->mFixedURI && rv != NS_ERROR_MALFORMED_URI) {
+ return rv;
+ }
+ }
+
+ if (info->mFixedURI && ourHandler == extHandler && sFixupKeywords &&
+ (aFixupFlags & FIXUP_FLAG_FIX_SCHEME_TYPOS)) {
+ nsCOMPtr<nsIExternalProtocolService> extProtService =
+ do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
+ if (extProtService) {
+ bool handlerExists = false;
+ rv = extProtService->ExternalProtocolHandlerExists(scheme.get(),
+ &handlerExists);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ // This basically means we're dealing with a theoretically valid
+ // URI... but we have no idea how to load it. (e.g. "christmas:humbug")
+ // It's more likely the user wants to search, and so we
+ // chuck this over to their preferred search provider instead:
+ if (!handlerExists) {
+ bool hasUserPassword = HasUserPassword(uriString);
+ if (!hasUserPassword) {
+ TryKeywordFixupForURIInfo(uriString, info, aPostData);
+ } else {
+ // If the given URL has a user:password we can't just pass it to the
+ // external protocol handler; we'll try using it with http instead later
+ info->mFixedURI = nullptr;
+ }
+ }
+ }
+ }
+
+ if (info->mFixedURI) {
+ if (!info->mPreferredURI) {
+ if (aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI) {
+ info->mFixupCreatedAlternateURI = MakeAlternateURI(info->mFixedURI);
+ }
+ info->mPreferredURI = info->mFixedURI;
+ }
+ return NS_OK;
+ }
+
+ // Fix up protocol string before calling KeywordURIFixup, because
+ // it cares about the hostname of such URIs:
+ nsCOMPtr<nsIURI> uriWithProtocol;
+ bool inputHadDuffProtocol = false;
+
+ // Prune duff protocol schemes
+ //
+ // ://totallybroken.url.com
+ // //shorthand.url.com
+ //
+ if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("://"))) {
+ uriString = StringTail(uriString, uriString.Length() - 3);
+ inputHadDuffProtocol = true;
+ } else if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("//"))) {
+ uriString = StringTail(uriString, uriString.Length() - 2);
+ inputHadDuffProtocol = true;
+ }
+
+ // NB: this rv gets returned at the end of this method if we never
+ // do a keyword fixup after this (because the pref or the flags passed
+ // might not let us).
+ rv = FixupURIProtocol(uriString, info, getter_AddRefs(uriWithProtocol));
+ if (uriWithProtocol) {
+ info->mFixedURI = uriWithProtocol;
+ }
+
+ // See if it is a keyword
+ // Test whether keywords need to be fixed up
+ if (sFixupKeywords && (aFixupFlags & FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP) &&
+ !inputHadDuffProtocol) {
+ if (NS_SUCCEEDED(KeywordURIFixup(uriString, info, aPostData)) &&
+ info->mPreferredURI) {
+ return NS_OK;
+ }
+ }
+
+ // Did the caller want us to try an alternative URI?
+ // If so, attempt to fixup http://foo into http://www.foo.com
+
+ if (info->mFixedURI && aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI) {
+ info->mFixupCreatedAlternateURI = MakeAlternateURI(info->mFixedURI);
+ }
+
+ if (info->mFixedURI) {
+ info->mPreferredURI = info->mFixedURI;
+ return NS_OK;
+ }
+
+ // If we still haven't been able to construct a valid URI, try to force a
+ // keyword match. This catches search strings with '.' or ':' in them.
+ if (sFixupKeywords && (aFixupFlags & FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP)) {
+ rv = TryKeywordFixupForURIInfo(aStringURI, info, aPostData);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixup::KeywordToURI(const nsACString& aKeyword,
+ nsIInputStream** aPostData,
+ nsIURIFixupInfo** aInfo)
+{
+ RefPtr<nsDefaultURIFixupInfo> info = new nsDefaultURIFixupInfo(aKeyword);
+ NS_ADDREF(*aInfo = info);
+
+ if (aPostData) {
+ *aPostData = nullptr;
+ }
+ NS_ENSURE_STATE(Preferences::GetRootBranch());
+
+ // Strip leading "?" and leading/trailing spaces from aKeyword
+ nsAutoCString keyword(aKeyword);
+ if (StringBeginsWith(keyword, NS_LITERAL_CSTRING("?"))) {
+ keyword.Cut(0, 1);
+ }
+ keyword.Trim(" ");
+
+ if (XRE_IsContentProcess()) {
+ dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
+ if (!contentChild) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ ipc::OptionalInputStreamParams postData;
+ ipc::OptionalURIParams uri;
+ nsAutoString providerName;
+ if (!contentChild->SendKeywordToURI(keyword, &providerName, &postData,
+ &uri)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ CopyUTF8toUTF16(keyword, info->mKeywordAsSent);
+ info->mKeywordProviderName = providerName;
+
+ if (aPostData) {
+ nsTArray<ipc::FileDescriptor> fds;
+ nsCOMPtr<nsIInputStream> temp = DeserializeInputStream(postData, fds);
+ temp.forget(aPostData);
+
+ MOZ_ASSERT(fds.IsEmpty());
+ }
+
+ nsCOMPtr<nsIURI> temp = DeserializeURI(uri);
+ info->mPreferredURI = temp.forget();
+ return NS_OK;
+ }
+
+#ifdef MOZ_TOOLKIT_SEARCH
+ // Try falling back to the search service's default search engine
+ nsCOMPtr<nsIBrowserSearchService> searchSvc =
+ do_GetService("@mozilla.org/browser/search-service;1");
+ if (searchSvc) {
+ nsCOMPtr<nsISearchEngine> defaultEngine;
+ searchSvc->GetDefaultEngine(getter_AddRefs(defaultEngine));
+ if (defaultEngine) {
+ nsCOMPtr<nsISearchSubmission> submission;
+ nsAutoString responseType;
+ // We allow default search plugins to specify alternate
+ // parameters that are specific to keyword searches.
+ NS_NAMED_LITERAL_STRING(mozKeywordSearch,
+ "application/x-moz-keywordsearch");
+ bool supportsResponseType = false;
+ defaultEngine->SupportsResponseType(mozKeywordSearch,
+ &supportsResponseType);
+ if (supportsResponseType) {
+ responseType.Assign(mozKeywordSearch);
+ }
+
+ NS_ConvertUTF8toUTF16 keywordW(keyword);
+ defaultEngine->GetSubmission(keywordW,
+ responseType,
+ NS_LITERAL_STRING("keyword"),
+ getter_AddRefs(submission));
+
+ if (submission) {
+ nsCOMPtr<nsIInputStream> postData;
+ submission->GetPostData(getter_AddRefs(postData));
+ if (aPostData) {
+ postData.forget(aPostData);
+ } else if (postData) {
+ // The submission specifies POST data (i.e. the search
+ // engine's "method" is POST), but our caller didn't allow
+ // passing post data back. No point passing back a URL that
+ // won't load properly.
+ return NS_ERROR_FAILURE;
+ }
+
+ defaultEngine->GetName(info->mKeywordProviderName);
+ info->mKeywordAsSent = keywordW;
+ return submission->GetUri(getter_AddRefs(info->mPreferredURI));
+ }
+ }
+ }
+#endif
+
+ // out of options
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+// Helper to deal with passing around uri fixup stuff
+nsresult
+nsDefaultURIFixup::TryKeywordFixupForURIInfo(const nsACString& aURIString,
+ nsDefaultURIFixupInfo* aFixupInfo,
+ nsIInputStream** aPostData)
+{
+ nsCOMPtr<nsIURIFixupInfo> keywordInfo;
+ nsresult rv = KeywordToURI(aURIString, aPostData,
+ getter_AddRefs(keywordInfo));
+ if (NS_SUCCEEDED(rv)) {
+ keywordInfo->GetKeywordProviderName(aFixupInfo->mKeywordProviderName);
+ keywordInfo->GetKeywordAsSent(aFixupInfo->mKeywordAsSent);
+ keywordInfo->GetPreferredURI(getter_AddRefs(aFixupInfo->mPreferredURI));
+ }
+ return rv;
+}
+
+bool
+nsDefaultURIFixup::MakeAlternateURI(nsIURI* aURI)
+{
+ if (!Preferences::GetRootBranch()) {
+ return false;
+ }
+ if (!Preferences::GetBool("browser.fixup.alternate.enabled", true)) {
+ return false;
+ }
+
+ // Code only works for http. Not for any other protocol including https!
+ bool isHttp = false;
+ aURI->SchemeIs("http", &isHttp);
+ if (!isHttp) {
+ return false;
+ }
+
+ // Security - URLs with user / password info should NOT be fixed up
+ nsAutoCString userpass;
+ aURI->GetUserPass(userpass);
+ if (!userpass.IsEmpty()) {
+ return false;
+ }
+
+ nsAutoCString oldHost;
+ nsAutoCString newHost;
+ aURI->GetHost(oldHost);
+
+ // Count the dots
+ int32_t numDots = 0;
+ nsReadingIterator<char> iter;
+ nsReadingIterator<char> iterEnd;
+ oldHost.BeginReading(iter);
+ oldHost.EndReading(iterEnd);
+ while (iter != iterEnd) {
+ if (*iter == '.') {
+ numDots++;
+ }
+ ++iter;
+ }
+
+ // Get the prefix and suffix to stick onto the new hostname. By default these
+ // are www. & .com but they could be any other value, e.g. www. & .org
+
+ nsAutoCString prefix("www.");
+ nsAdoptingCString prefPrefix =
+ Preferences::GetCString("browser.fixup.alternate.prefix");
+ if (prefPrefix) {
+ prefix.Assign(prefPrefix);
+ }
+
+ nsAutoCString suffix(".com");
+ nsAdoptingCString prefSuffix =
+ Preferences::GetCString("browser.fixup.alternate.suffix");
+ if (prefSuffix) {
+ suffix.Assign(prefSuffix);
+ }
+
+ if (numDots == 0) {
+ newHost.Assign(prefix);
+ newHost.Append(oldHost);
+ newHost.Append(suffix);
+ } else if (numDots == 1) {
+ if (!prefix.IsEmpty() &&
+ oldHost.EqualsIgnoreCase(prefix.get(), prefix.Length())) {
+ newHost.Assign(oldHost);
+ newHost.Append(suffix);
+ } else if (!suffix.IsEmpty()) {
+ newHost.Assign(prefix);
+ newHost.Append(oldHost);
+ } else {
+ // Do nothing
+ return false;
+ }
+ } else {
+ // Do nothing
+ return false;
+ }
+
+ if (newHost.IsEmpty()) {
+ return false;
+ }
+
+ // Assign the new host string over the old one
+ aURI->SetHost(newHost);
+ return true;
+}
+
+nsresult
+nsDefaultURIFixup::FileURIFixup(const nsACString& aStringURI, nsIURI** aURI)
+{
+ nsAutoCString uriSpecOut;
+
+ nsresult rv = ConvertFileToStringURI(aStringURI, uriSpecOut);
+ if (NS_SUCCEEDED(rv)) {
+ // if this is file url, uriSpecOut is already in FS charset
+ if (NS_SUCCEEDED(NS_NewURI(aURI, uriSpecOut.get(), nullptr))) {
+ return NS_OK;
+ }
+ }
+ return NS_ERROR_FAILURE;
+}
+
+nsresult
+nsDefaultURIFixup::ConvertFileToStringURI(const nsACString& aIn,
+ nsCString& aResult)
+{
+ bool attemptFixup = false;
+
+#if defined(XP_WIN)
+ // Check for \ in the url-string or just a drive (PC)
+ if (aIn.Contains('\\') ||
+ (aIn.Length() == 2 && (aIn.Last() == ':' || aIn.Last() == '|'))) {
+ attemptFixup = true;
+ }
+#elif defined(XP_UNIX)
+ // Check if it starts with / (UNIX)
+ if (aIn.First() == '/') {
+ attemptFixup = true;
+ }
+#else
+ // Do nothing (All others for now)
+#endif
+
+ if (attemptFixup) {
+ // Test if this is a valid path by trying to create a local file
+ // object. The URL of that is returned if successful.
+
+ // NOTE: Please be sure to check that the call to NS_NewLocalFile
+ // rejects bad file paths when using this code on a new
+ // platform.
+
+ nsCOMPtr<nsIFile> filePath;
+ nsresult rv;
+
+ // this is not the real fix but a temporary fix
+ // in order to really fix the problem, we need to change the
+ // nsICmdLineService interface to use wstring to pass paramenters
+ // instead of string since path name and other argument could be
+ // in non ascii.(see bug 87127) Since it is too risky to make interface
+ // change right now, we decide not to do so now.
+ // Therefore, the aIn we receive here maybe already in damage form
+ // (e.g. treat every bytes as ISO-8859-1 and cast up to char16_t
+ // while the real data could be in file system charset )
+ // we choice the following logic which will work for most of the case.
+ // Case will still failed only if it meet ALL the following condiction:
+ // 1. running on CJK, Russian, or Greek system, and
+ // 2. user type it from URL bar
+ // 3. the file name contains character in the range of
+ // U+00A1-U+00FF but encode as different code point in file
+ // system charset (e.g. ACP on window)- this is very rare case
+ // We should remove this logic and convert to File system charset here
+ // once we change nsICmdLineService to use wstring and ensure
+ // all the Unicode data come in is correctly converted.
+ // XXXbz nsICmdLineService doesn't hand back unicode, so in some cases
+ // what we have is actually a "utf8" version of a "utf16" string that's
+ // actually byte-expanded native-encoding data. Someone upstream needs
+ // to stop using AssignWithConversion and do things correctly. See bug
+ // 58866 for what happens if we remove this
+ // PossiblyByteExpandedFileName check.
+ NS_ConvertUTF8toUTF16 in(aIn);
+ if (PossiblyByteExpandedFileName(in)) {
+ // removes high byte
+ rv = NS_NewNativeLocalFile(NS_LossyConvertUTF16toASCII(in), false,
+ getter_AddRefs(filePath));
+ } else {
+ // input is unicode
+ rv = NS_NewLocalFile(in, false, getter_AddRefs(filePath));
+ }
+
+ if (NS_SUCCEEDED(rv)) {
+ NS_GetURLSpecFromFile(filePath, aResult);
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+nsresult
+nsDefaultURIFixup::FixupURIProtocol(const nsACString& aURIString,
+ nsDefaultURIFixupInfo* aFixupInfo,
+ nsIURI** aURI)
+{
+ nsAutoCString uriString(aURIString);
+ *aURI = nullptr;
+
+ // Add ftp:// or http:// to front of url if it has no spec
+ //
+ // Should fix:
+ //
+ // no-scheme.com
+ // ftp.no-scheme.com
+ // ftp4.no-scheme.com
+ // no-scheme.com/query?foo=http://www.foo.com
+ // user:pass@no-scheme.com
+ //
+ int32_t schemeDelim = uriString.Find("://", 0);
+ int32_t firstDelim = uriString.FindCharInSet("/:");
+ if (schemeDelim <= 0 ||
+ (firstDelim != -1 && schemeDelim > firstDelim)) {
+ // find host name
+ int32_t hostPos = uriString.FindCharInSet("/:?#");
+ if (hostPos == -1) {
+ hostPos = uriString.Length();
+ }
+
+ // extract host name
+ nsAutoCString hostSpec;
+ uriString.Left(hostSpec, hostPos);
+
+ // insert url spec corresponding to host name
+ uriString.InsertLiteral("http://", 0);
+ aFixupInfo->mFixupChangedProtocol = true;
+ } // end if checkprotocol
+
+ return NS_NewURI(aURI, uriString, nullptr);
+}
+
+bool
+nsDefaultURIFixup::PossiblyHostPortUrl(const nsACString& aUrl)
+{
+ // Oh dear, the protocol is invalid. Test if the protocol might
+ // actually be a url without a protocol:
+ //
+ // http://www.faqs.org/rfcs/rfc1738.html
+ // http://www.faqs.org/rfcs/rfc2396.html
+ //
+ // e.g. Anything of the form:
+ //
+ // <hostname>:<port> or
+ // <hostname>:<port>/
+ //
+ // Where <hostname> is a string of alphanumeric characters and dashes
+ // separated by dots.
+ // and <port> is a 5 or less digits. This actually breaks the rfc2396
+ // definition of a scheme which allows dots in schemes.
+ //
+ // Note:
+ // People expecting this to work with
+ // <user>:<password>@<host>:<port>/<url-path> will be disappointed!
+ //
+ // Note: Parser could be a lot tighter, tossing out silly hostnames
+ // such as those containing consecutive dots and so on.
+
+ // Read the hostname which should of the form
+ // [a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)*:
+
+ nsACString::const_iterator iterBegin;
+ nsACString::const_iterator iterEnd;
+ aUrl.BeginReading(iterBegin);
+ aUrl.EndReading(iterEnd);
+ nsACString::const_iterator iter = iterBegin;
+
+ while (iter != iterEnd) {
+ uint32_t chunkSize = 0;
+ // Parse a chunk of the address
+ while (iter != iterEnd &&
+ (*iter == '-' ||
+ nsCRT::IsAsciiAlpha(*iter) ||
+ nsCRT::IsAsciiDigit(*iter))) {
+ ++chunkSize;
+ ++iter;
+ }
+ if (chunkSize == 0 || iter == iterEnd) {
+ return false;
+ }
+ if (*iter == ':') {
+ // Go onto checking the for the digits
+ break;
+ }
+ if (*iter != '.') {
+ // Whatever it is, it ain't a hostname!
+ return false;
+ }
+ ++iter;
+ }
+ if (iter == iterEnd) {
+ // No point continuing since there is no colon
+ return false;
+ }
+ ++iter;
+
+ // Count the number of digits after the colon and before the
+ // next forward slash (or end of string)
+
+ uint32_t digitCount = 0;
+ while (iter != iterEnd && digitCount <= 5) {
+ if (nsCRT::IsAsciiDigit(*iter)) {
+ digitCount++;
+ } else if (*iter == '/') {
+ break;
+ } else {
+ // Whatever it is, it ain't a port!
+ return false;
+ }
+ ++iter;
+ }
+ if (digitCount == 0 || digitCount > 5) {
+ // No digits or more digits than a port would have.
+ return false;
+ }
+
+ // Yes, it's possibly a host:port url
+ return true;
+}
+
+bool
+nsDefaultURIFixup::PossiblyByteExpandedFileName(const nsAString& aIn)
+{
+ // XXXXX HACK XXXXX : please don't copy this code.
+ // There are cases where aIn contains the locale byte chars padded to short
+ // (thus the name "ByteExpanded"); whereas other cases
+ // have proper Unicode code points.
+ // This is a temporary fix. Please refer to 58866, 86948
+
+ nsReadingIterator<char16_t> iter;
+ nsReadingIterator<char16_t> iterEnd;
+ aIn.BeginReading(iter);
+ aIn.EndReading(iterEnd);
+ while (iter != iterEnd) {
+ if (*iter >= 0x0080 && *iter <= 0x00FF) {
+ return true;
+ }
+ ++iter;
+ }
+ return false;
+}
+
+nsresult
+nsDefaultURIFixup::KeywordURIFixup(const nsACString& aURIString,
+ nsDefaultURIFixupInfo* aFixupInfo,
+ nsIInputStream** aPostData)
+{
+ // These are keyword formatted strings
+ // "what is mozilla"
+ // "what is mozilla?"
+ // "docshell site:mozilla.org" - has no dot/colon in the first space-separated substring
+ // "?mozilla" - anything that begins with a question mark
+ // "?site:mozilla.org docshell"
+ // Things that have a quote before the first dot/colon
+ // "mozilla" - checked against a whitelist to see if it's a host or not
+ // ".mozilla", "mozilla." - ditto
+
+ // These are not keyword formatted strings
+ // "www.blah.com" - first space-separated substring contains a dot, doesn't start with "?"
+ // "www.blah.com stuff"
+ // "nonQualifiedHost:80" - first space-separated substring contains a colon, doesn't start with "?"
+ // "nonQualifiedHost:80 args"
+ // "nonQualifiedHost?"
+ // "nonQualifiedHost?args"
+ // "nonQualifiedHost?some args"
+ // "blah.com."
+
+ // Note: uint32_t(kNotFound) is greater than any actual location
+ // in practice. So if we cast all locations to uint32_t, then a <
+ // b guarantees that either b is kNotFound and a is found, or both
+ // are found and a found before b.
+
+ uint32_t firstDotLoc = uint32_t(kNotFound);
+ uint32_t lastDotLoc = uint32_t(kNotFound);
+ uint32_t firstColonLoc = uint32_t(kNotFound);
+ uint32_t firstQuoteLoc = uint32_t(kNotFound);
+ uint32_t firstSpaceLoc = uint32_t(kNotFound);
+ uint32_t firstQMarkLoc = uint32_t(kNotFound);
+ uint32_t lastLSBracketLoc = uint32_t(kNotFound);
+ uint32_t lastSlashLoc = uint32_t(kNotFound);
+ uint32_t pos = 0;
+ uint32_t foundDots = 0;
+ uint32_t foundColons = 0;
+ uint32_t foundDigits = 0;
+ uint32_t foundRSBrackets = 0;
+ bool looksLikeIpv6 = true;
+ bool hasAsciiAlpha = false;
+
+ nsACString::const_iterator iterBegin;
+ nsACString::const_iterator iterEnd;
+ aURIString.BeginReading(iterBegin);
+ aURIString.EndReading(iterEnd);
+ nsACString::const_iterator iter = iterBegin;
+
+ while (iter != iterEnd) {
+ if (pos >= 1 && foundRSBrackets == 0) {
+ if (!(lastLSBracketLoc == 0 &&
+ (*iter == ':' ||
+ *iter == '.' ||
+ *iter == ']' ||
+ (*iter >= 'a' && *iter <= 'f') ||
+ (*iter >= 'A' && *iter <= 'F') ||
+ nsCRT::IsAsciiDigit(*iter)))) {
+ looksLikeIpv6 = false;
+ }
+ }
+
+ // If we're at the end of the string or this is the first slash,
+ // check if the thing before the slash looks like ipv4:
+ if ((iterEnd - iter == 1 ||
+ (lastSlashLoc == uint32_t(kNotFound) && *iter == '/')) &&
+ // Need 2 or 3 dots + only digits
+ (foundDots == 2 || foundDots == 3) &&
+ // and they should be all that came before now:
+ (foundDots + foundDigits == pos ||
+ // or maybe there was also exactly 1 colon that came after the last dot,
+ // and the digits, dots and colon were all that came before now:
+ (foundColons == 1 && firstColonLoc > lastDotLoc &&
+ foundDots + foundDigits + foundColons == pos))) {
+ // Hurray, we got ourselves some ipv4!
+ // At this point, there's no way we will do a keyword lookup, so just bail immediately:
+ return NS_OK;
+ }
+
+ if (*iter == '.') {
+ ++foundDots;
+ lastDotLoc = pos;
+ if (firstDotLoc == uint32_t(kNotFound)) {
+ firstDotLoc = pos;
+ }
+ } else if (*iter == ':') {
+ ++foundColons;
+ if (firstColonLoc == uint32_t(kNotFound)) {
+ firstColonLoc = pos;
+ }
+ } else if (*iter == ' ' && firstSpaceLoc == uint32_t(kNotFound)) {
+ firstSpaceLoc = pos;
+ } else if (*iter == '?' && firstQMarkLoc == uint32_t(kNotFound)) {
+ firstQMarkLoc = pos;
+ } else if ((*iter == '\'' || *iter == '"') &&
+ firstQuoteLoc == uint32_t(kNotFound)) {
+ firstQuoteLoc = pos;
+ } else if (*iter == '[') {
+ lastLSBracketLoc = pos;
+ } else if (*iter == ']') {
+ foundRSBrackets++;
+ } else if (*iter == '/') {
+ lastSlashLoc = pos;
+ } else if (nsCRT::IsAsciiAlpha(*iter)) {
+ hasAsciiAlpha = true;
+ } else if (nsCRT::IsAsciiDigit(*iter)) {
+ ++foundDigits;
+ }
+
+ pos++;
+ iter++;
+ }
+
+ if (lastLSBracketLoc > 0 || foundRSBrackets != 1) {
+ looksLikeIpv6 = false;
+ }
+
+ // If there are only colons and only hexadecimal characters ([a-z][0-9])
+ // enclosed in [], then don't do a keyword lookup
+ if (looksLikeIpv6) {
+ return NS_OK;
+ }
+
+ nsAutoCString asciiHost;
+ nsAutoCString host;
+
+ bool isValidAsciiHost =
+ aFixupInfo->mFixedURI &&
+ NS_SUCCEEDED(aFixupInfo->mFixedURI->GetAsciiHost(asciiHost)) &&
+ !asciiHost.IsEmpty();
+
+ bool isValidHost =
+ aFixupInfo->mFixedURI &&
+ NS_SUCCEEDED(aFixupInfo->mFixedURI->GetHost(host)) &&
+ !host.IsEmpty();
+
+ nsresult rv = NS_OK;
+ // We do keyword lookups if a space or quote preceded the dot, colon
+ // or question mark (or if the latter is not found, or if the input starts
+ // with a question mark)
+ if (((firstSpaceLoc < firstDotLoc || firstQuoteLoc < firstDotLoc) &&
+ (firstSpaceLoc < firstColonLoc || firstQuoteLoc < firstColonLoc) &&
+ (firstSpaceLoc < firstQMarkLoc || firstQuoteLoc < firstQMarkLoc)) ||
+ firstQMarkLoc == 0) {
+ rv = TryKeywordFixupForURIInfo(aFixupInfo->mOriginalInput, aFixupInfo,
+ aPostData);
+ // ... or when the host is the same as asciiHost and there are no
+ // characters from [a-z][A-Z]
+ } else if (isValidAsciiHost && isValidHost && !hasAsciiAlpha &&
+ host.EqualsIgnoreCase(asciiHost.get())) {
+ if (!sDNSFirstForSingleWords) {
+ rv = TryKeywordFixupForURIInfo(aFixupInfo->mOriginalInput, aFixupInfo,
+ aPostData);
+ }
+ }
+ // ... or if there is no question mark or colon, and there is either no
+ // dot, or exactly 1 and it is the first or last character of the input:
+ else if ((firstDotLoc == uint32_t(kNotFound) ||
+ (foundDots == 1 && (firstDotLoc == 0 ||
+ firstDotLoc == aURIString.Length() - 1))) &&
+ firstColonLoc == uint32_t(kNotFound) &&
+ firstQMarkLoc == uint32_t(kNotFound)) {
+ if (isValidAsciiHost && IsDomainWhitelisted(asciiHost, firstDotLoc)) {
+ return NS_OK;
+ }
+
+ // ... unless there are no dots, and a slash, and alpha characters, and
+ // this is a valid host:
+ if (firstDotLoc == uint32_t(kNotFound) &&
+ lastSlashLoc != uint32_t(kNotFound) &&
+ hasAsciiAlpha && isValidAsciiHost) {
+ return NS_OK;
+ }
+
+ // If we get here, we don't have a valid URI, or we did but the
+ // host is not whitelisted, so we do a keyword search *anyway*:
+ rv = TryKeywordFixupForURIInfo(aFixupInfo->mOriginalInput, aFixupInfo,
+ aPostData);
+ }
+ return rv;
+}
+
+bool
+nsDefaultURIFixup::IsDomainWhitelisted(const nsACString& aAsciiHost,
+ const uint32_t aDotLoc)
+{
+ if (sDNSFirstForSingleWords) {
+ return true;
+ }
+ // Check if this domain is whitelisted as an actual
+ // domain (which will prevent a keyword query)
+ // NB: any processing of the host here should stay in sync with
+ // code in the front-end(s) that set the pref.
+
+ nsAutoCString pref("browser.fixup.domainwhitelist.");
+
+ if (aDotLoc == aAsciiHost.Length() - 1) {
+ pref.Append(Substring(aAsciiHost, 0, aAsciiHost.Length() - 1));
+ } else {
+ pref.Append(aAsciiHost);
+ }
+
+ return Preferences::GetBool(pref.get(), false);
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixup::IsDomainWhitelisted(const nsACString& aDomain,
+ const uint32_t aDotLoc,
+ bool* aResult)
+{
+ *aResult = IsDomainWhitelisted(aDomain, aDotLoc);
+ return NS_OK;
+}
+
+/* Implementation of nsIURIFixupInfo */
+NS_IMPL_ISUPPORTS(nsDefaultURIFixupInfo, nsIURIFixupInfo)
+
+nsDefaultURIFixupInfo::nsDefaultURIFixupInfo(const nsACString& aOriginalInput)
+ : mFixupChangedProtocol(false)
+ , mFixupCreatedAlternateURI(false)
+{
+ mOriginalInput = aOriginalInput;
+}
+
+nsDefaultURIFixupInfo::~nsDefaultURIFixupInfo()
+{
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetConsumer(nsISupports** aConsumer)
+{
+ *aConsumer = mConsumer;
+ NS_IF_ADDREF(*aConsumer);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::SetConsumer(nsISupports* aConsumer)
+{
+ mConsumer = aConsumer;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetPreferredURI(nsIURI** aPreferredURI)
+{
+ *aPreferredURI = mPreferredURI;
+ NS_IF_ADDREF(*aPreferredURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetFixedURI(nsIURI** aFixedURI)
+{
+ *aFixedURI = mFixedURI;
+ NS_IF_ADDREF(*aFixedURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetKeywordProviderName(nsAString& aResult)
+{
+ aResult = mKeywordProviderName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetKeywordAsSent(nsAString& aResult)
+{
+ aResult = mKeywordAsSent;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetFixupChangedProtocol(bool* aResult)
+{
+ *aResult = mFixupChangedProtocol;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetFixupCreatedAlternateURI(bool* aResult)
+{
+ *aResult = mFixupCreatedAlternateURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDefaultURIFixupInfo::GetOriginalInput(nsACString& aResult)
+{
+ aResult = mOriginalInput;
+ return NS_OK;
+}