Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "nsNetCID.h"
8
#include "nsNetUtil.h"
9
#include "nsIProtocolHandler.h"
10
11
#include "nsIFile.h"
12
#include <algorithm>
13
14
#include "nsISearchService.h"
15
#include "nsIURIFixup.h"
16
#include "nsIURIMutator.h"
17
#include "nsIWebNavigation.h"
18
#include "nsDefaultURIFixup.h"
19
#include "mozilla/Preferences.h"
20
#include "mozilla/dom/ContentChild.h"
21
#include "mozilla/ipc/IPCStreamUtils.h"
22
#include "mozilla/ipc/URIUtils.h"
23
#include "mozilla/TextUtils.h"
24
#include "mozilla/Tokenizer.h"
25
#include "mozilla/Unused.h"
26
#include "nsXULAppAPI.h"
27
#include "mozilla/StaticPrefs_browser.h"
28
#include "mozilla/StaticPrefs_keyword.h"
29
30
// Used to check if external protocol schemes are usable
31
#include "nsCExternalHandlerService.h"
32
#include "nsIExternalProtocolService.h"
33
34
using namespace mozilla;
35
36
/* Implementation file */
37
NS_IMPL_ISUPPORTS(nsDefaultURIFixup, nsIURIFixup)
38
39
nsDefaultURIFixup::nsDefaultURIFixup() {}
40
41
nsDefaultURIFixup::~nsDefaultURIFixup() {}
42
43
NS_IMETHODIMP
44
nsDefaultURIFixup::CreateFixupURI(const nsACString& aStringURI,
45
uint32_t aFixupFlags,
46
nsIInputStream** aPostData, nsIURI** aURI) {
47
nsCOMPtr<nsIURIFixupInfo> fixupInfo;
48
nsresult rv = GetFixupURIInfo(aStringURI, aFixupFlags, aPostData,
49
getter_AddRefs(fixupInfo));
50
NS_ENSURE_SUCCESS(rv, rv);
51
52
fixupInfo->GetPreferredURI(aURI);
53
return rv;
54
}
55
56
// Returns true if the URL contains a user:password@ or user@
57
static bool HasUserPassword(const nsACString& aStringURI) {
58
mozilla::Tokenizer parser(aStringURI);
59
mozilla::Tokenizer::Token token;
60
61
// May start with any of "protocol:", "protocol://", "//", "://"
62
if (parser.Check(Tokenizer::TOKEN_WORD, token)) { // Skip protocol if any
63
}
64
if (parser.CheckChar(':')) { // Skip colon if found
65
}
66
while (parser.CheckChar('/')) { // Skip all of the following slashes
67
}
68
69
while (parser.Next(token)) {
70
if (token.Type() == Tokenizer::TOKEN_CHAR) {
71
if (token.AsChar() == '/') {
72
return false;
73
}
74
if (token.AsChar() == '@') {
75
return true;
76
}
77
}
78
}
79
80
return false;
81
}
82
83
// Assume that 1 tab is accidental, but more than 1 implies this is
84
// supposed to be tab-separated content.
85
static bool MaybeTabSeparatedContent(const nsCString& aStringURI) {
86
auto firstTab = aStringURI.FindChar('\t');
87
return firstTab != kNotFound && aStringURI.RFindChar('\t') != firstTab;
88
}
89
90
NS_IMETHODIMP
91
nsDefaultURIFixup::GetFixupURIInfo(const nsACString& aStringURI,
92
uint32_t aFixupFlags,
93
nsIInputStream** aPostData,
94
nsIURIFixupInfo** aInfo) {
95
NS_ENSURE_ARG(!aStringURI.IsEmpty());
96
97
nsresult rv;
98
bool isPrivateContext = aFixupFlags & FIXUP_FLAG_PRIVATE_CONTEXT;
99
nsAutoCString uriString(aStringURI);
100
101
// Eliminate embedded newlines, which single-line text fields now allow:
102
uriString.StripCRLF();
103
// Cleanup the empty spaces and tabs that might be on each end:
104
uriString.Trim(" \t");
105
106
NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE);
107
108
RefPtr<nsDefaultURIFixupInfo> info = new nsDefaultURIFixupInfo(uriString);
109
NS_ADDREF(*aInfo = info);
110
111
nsCOMPtr<nsIIOService> ioService =
112
do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
113
NS_ENSURE_SUCCESS(rv, rv);
114
nsAutoCString scheme;
115
ioService->ExtractScheme(aStringURI, scheme);
116
117
// View-source is a pseudo scheme. We're interested in fixing up the stuff
118
// after it. The easiest way to do that is to call this method again with the
119
// "view-source:" lopped off and then prepend it again afterwards.
120
121
if (scheme.EqualsLiteral("view-source")) {
122
nsCOMPtr<nsIURIFixupInfo> uriInfo;
123
// We disable keyword lookup and alternate URIs so that small typos don't
124
// cause us to look at very different domains
125
uint32_t newFixupFlags = aFixupFlags & ~FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP &
126
~FIXUP_FLAGS_MAKE_ALTERNATE_URI;
127
128
const uint32_t viewSourceLen = sizeof("view-source:") - 1;
129
nsAutoCString innerURIString(Substring(uriString, viewSourceLen,
130
uriString.Length() - viewSourceLen));
131
// Prevent recursion:
132
innerURIString.Trim(" ");
133
nsAutoCString innerScheme;
134
ioService->ExtractScheme(innerURIString, innerScheme);
135
if (innerScheme.EqualsLiteral("view-source")) {
136
return NS_ERROR_FAILURE;
137
}
138
139
rv = GetFixupURIInfo(innerURIString, newFixupFlags, aPostData,
140
getter_AddRefs(uriInfo));
141
if (NS_FAILED(rv)) {
142
return NS_ERROR_FAILURE;
143
}
144
nsAutoCString spec;
145
nsCOMPtr<nsIURI> uri;
146
uriInfo->GetPreferredURI(getter_AddRefs(uri));
147
if (!uri) {
148
return NS_ERROR_FAILURE;
149
}
150
uri->GetSpec(spec);
151
uriString.AssignLiteral("view-source:");
152
uriString.Append(spec);
153
} else {
154
// Check for if it is a file URL
155
nsCOMPtr<nsIURI> uri;
156
FileURIFixup(uriString, getter_AddRefs(uri));
157
// NB: FileURIFixup only returns a URI if it had to fix the protocol to
158
// do so, so passing in file:///foo/bar will not hit this path:
159
if (uri) {
160
uri.swap(info->mFixedURI);
161
info->mPreferredURI = info->mFixedURI;
162
info->mFixupChangedProtocol = true;
163
return NS_OK;
164
}
165
}
166
167
// Fix up common scheme typos.
168
if (StaticPrefs::browser_fixup_typo_scheme() &&
169
(aFixupFlags & FIXUP_FLAG_FIX_SCHEME_TYPOS)) {
170
// Fast-path for common cases.
171
if (scheme.IsEmpty() || scheme.EqualsLiteral("http") ||
172
scheme.EqualsLiteral("https") || scheme.EqualsLiteral("ftp") ||
173
scheme.EqualsLiteral("file")) {
174
// Do nothing.
175
} else if (scheme.EqualsLiteral("ttp")) {
176
// ttp -> http.
177
uriString.ReplaceLiteral(0, 3, "http");
178
scheme.AssignLiteral("http");
179
info->mFixupChangedProtocol = true;
180
} else if (scheme.EqualsLiteral("htp")) {
181
// htp -> http.
182
uriString.ReplaceLiteral(0, 3, "http");
183
scheme.AssignLiteral("http");
184
info->mFixupChangedProtocol = true;
185
} else if (scheme.EqualsLiteral("ttps")) {
186
// ttps -> https.
187
uriString.ReplaceLiteral(0, 4, "https");
188
scheme.AssignLiteral("https");
189
info->mFixupChangedProtocol = true;
190
} else if (scheme.EqualsLiteral("tps")) {
191
// tps -> https.
192
uriString.ReplaceLiteral(0, 3, "https");
193
scheme.AssignLiteral("https");
194
info->mFixupChangedProtocol = true;
195
} else if (scheme.EqualsLiteral("ps")) {
196
// ps -> https.
197
uriString.ReplaceLiteral(0, 2, "https");
198
scheme.AssignLiteral("https");
199
info->mFixupChangedProtocol = true;
200
} else if (scheme.EqualsLiteral("htps")) {
201
// htps -> https.
202
uriString.ReplaceLiteral(0, 4, "https");
203
scheme.AssignLiteral("https");
204
info->mFixupChangedProtocol = true;
205
} else if (scheme.EqualsLiteral("ile")) {
206
// ile -> file.
207
uriString.ReplaceLiteral(0, 3, "file");
208
scheme.AssignLiteral("file");
209
info->mFixupChangedProtocol = true;
210
} else if (scheme.EqualsLiteral("le")) {
211
// le -> file.
212
uriString.ReplaceLiteral(0, 2, "file");
213
scheme.AssignLiteral("file");
214
info->mFixupChangedProtocol = true;
215
}
216
}
217
218
// Now we need to check whether "scheme" is something we don't
219
// really know about.
220
nsCOMPtr<nsIProtocolHandler> ourHandler, extHandler;
221
222
extHandler = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "default");
223
if (!scheme.IsEmpty()) {
224
ioService->GetProtocolHandler(scheme.get(), getter_AddRefs(ourHandler));
225
} else {
226
ourHandler = extHandler;
227
}
228
229
if (ourHandler != extHandler || !PossiblyHostPortUrl(uriString)) {
230
// Just try to create an URL out of it
231
rv = NS_NewURI(getter_AddRefs(info->mFixedURI), uriString);
232
233
if (!info->mFixedURI && rv != NS_ERROR_MALFORMED_URI) {
234
return rv;
235
}
236
}
237
238
if (info->mFixedURI && ourHandler == extHandler &&
239
StaticPrefs::keyword_enabled() &&
240
(aFixupFlags & FIXUP_FLAG_FIX_SCHEME_TYPOS)) {
241
nsCOMPtr<nsIExternalProtocolService> extProtService =
242
do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
243
if (extProtService) {
244
bool handlerExists = false;
245
rv = extProtService->ExternalProtocolHandlerExists(scheme.get(),
246
&handlerExists);
247
if (NS_FAILED(rv)) {
248
return rv;
249
}
250
// This basically means we're dealing with a theoretically valid
251
// URI... but we have no idea how to load it. (e.g. "christmas:humbug")
252
// It's more likely the user wants to search, and so we
253
// chuck this over to their preferred search provider instead:
254
if (!handlerExists) {
255
bool hasUserPassword = HasUserPassword(uriString);
256
if (!hasUserPassword) {
257
TryKeywordFixupForURIInfo(uriString, info, isPrivateContext,
258
aPostData);
259
} else {
260
// If the given URL has a user:password we can't just pass it to the
261
// external protocol handler; we'll try using it with http instead
262
// later
263
info->mFixedURI = nullptr;
264
}
265
}
266
}
267
}
268
269
if (info->mFixedURI) {
270
if (!info->mPreferredURI) {
271
if (aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI) {
272
info->mFixupCreatedAlternateURI = MakeAlternateURI(info->mFixedURI);
273
}
274
info->mPreferredURI = info->mFixedURI;
275
}
276
return NS_OK;
277
}
278
279
// Fix up protocol string before calling KeywordURIFixup, because
280
// it cares about the hostname of such URIs:
281
nsCOMPtr<nsIURI> uriWithProtocol;
282
bool inputHadDuffProtocol = false;
283
284
// Prune duff protocol schemes
285
//
286
// ://totallybroken.url.com
287
// //shorthand.url.com
288
//
289
if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("://"))) {
290
uriString = StringTail(uriString, uriString.Length() - 3);
291
inputHadDuffProtocol = true;
292
} else if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("//"))) {
293
uriString = StringTail(uriString, uriString.Length() - 2);
294
inputHadDuffProtocol = true;
295
}
296
297
// Note: this rv gets returned at the end of this method if we don't fix up
298
// the protocol and don't do a keyword fixup after this (because the pref
299
// or the flags passed might not let us).
300
rv = NS_OK;
301
// Avoid fixing up content that looks like tab-separated values
302
if (!MaybeTabSeparatedContent(uriString)) {
303
rv = FixupURIProtocol(uriString, info, getter_AddRefs(uriWithProtocol));
304
if (uriWithProtocol) {
305
info->mFixedURI = uriWithProtocol;
306
}
307
}
308
309
// See if it is a keyword
310
// Test whether keywords need to be fixed up
311
if (StaticPrefs::keyword_enabled() &&
312
(aFixupFlags & FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP) &&
313
!inputHadDuffProtocol) {
314
if (NS_SUCCEEDED(
315
KeywordURIFixup(uriString, info, isPrivateContext, aPostData)) &&
316
info->mPreferredURI) {
317
return NS_OK;
318
}
319
}
320
321
// Did the caller want us to try an alternative URI?
322
// If so, attempt to fixup http://foo into http://www.foo.com
323
324
if (info->mFixedURI && aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI) {
325
info->mFixupCreatedAlternateURI = MakeAlternateURI(info->mFixedURI);
326
}
327
328
if (info->mFixedURI) {
329
info->mPreferredURI = info->mFixedURI;
330
return NS_OK;
331
}
332
333
// If we still haven't been able to construct a valid URI, try to force a
334
// keyword match. This catches search strings with '.' or ':' in them.
335
if (StaticPrefs::keyword_enabled() &&
336
(aFixupFlags & FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP)) {
337
rv = TryKeywordFixupForURIInfo(aStringURI, info, isPrivateContext,
338
aPostData);
339
}
340
341
return rv;
342
}
343
344
NS_IMETHODIMP
345
nsDefaultURIFixup::WebNavigationFlagsToFixupFlags(const nsACString& aStringURI,
346
uint32_t aDocShellFlags,
347
uint32_t* aFixupFlags) {
348
nsCOMPtr<nsIURI> uri;
349
NS_NewURI(getter_AddRefs(uri), aStringURI);
350
if (uri) {
351
aDocShellFlags &= ~nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
352
}
353
354
*aFixupFlags = 0;
355
if (aDocShellFlags & nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
356
*aFixupFlags |= FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
357
}
358
if (aDocShellFlags & nsIWebNavigation::LOAD_FLAGS_FIXUP_SCHEME_TYPOS) {
359
*aFixupFlags |= FIXUP_FLAG_FIX_SCHEME_TYPOS;
360
}
361
362
return NS_OK;
363
}
364
365
NS_IMETHODIMP
366
nsDefaultURIFixup::KeywordToURI(const nsACString& aKeyword,
367
bool aIsPrivateContext,
368
nsIInputStream** aPostData,
369
nsIURIFixupInfo** aInfo) {
370
RefPtr<nsDefaultURIFixupInfo> info = new nsDefaultURIFixupInfo(aKeyword);
371
NS_ADDREF(*aInfo = info);
372
373
if (aPostData) {
374
*aPostData = nullptr;
375
}
376
NS_ENSURE_STATE(Preferences::GetRootBranch());
377
378
// Strip leading "?" and leading/trailing spaces from aKeyword
379
nsAutoCString keyword(aKeyword);
380
if (StringBeginsWith(keyword, NS_LITERAL_CSTRING("?"))) {
381
keyword.Cut(0, 1);
382
}
383
keyword.Trim(" ");
384
385
if (XRE_IsContentProcess()) {
386
dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
387
if (!contentChild) {
388
return NS_ERROR_NOT_AVAILABLE;
389
}
390
391
RefPtr<nsIInputStream> postData;
392
RefPtr<nsIURI> uri;
393
nsAutoString providerName;
394
if (!contentChild->SendKeywordToURI(keyword, aIsPrivateContext,
395
&providerName, &postData, &uri)) {
396
return NS_ERROR_FAILURE;
397
}
398
399
CopyUTF8toUTF16(keyword, info->mKeywordAsSent);
400
info->mKeywordProviderName = providerName;
401
402
if (aPostData) {
403
postData.forget(aPostData);
404
}
405
406
info->mPreferredURI = uri.forget();
407
return NS_OK;
408
}
409
410
// Try falling back to the search service's default search engine
411
nsCOMPtr<nsISearchService> searchSvc =
412
do_GetService("@mozilla.org/browser/search-service;1");
413
if (searchSvc) {
414
// We must use an appropriate search engine depending on the private
415
// context.
416
nsCOMPtr<nsISearchEngine> defaultEngine;
417
if (aIsPrivateContext) {
418
searchSvc->GetDefaultPrivateEngine(getter_AddRefs(defaultEngine));
419
} else {
420
searchSvc->GetDefaultEngine(getter_AddRefs(defaultEngine));
421
}
422
if (defaultEngine) {
423
nsCOMPtr<nsISearchSubmission> submission;
424
nsAutoString responseType;
425
// We allow default search plugins to specify alternate
426
// parameters that are specific to keyword searches.
427
NS_NAMED_LITERAL_STRING(mozKeywordSearch,
428
"application/x-moz-keywordsearch");
429
bool supportsResponseType = false;
430
defaultEngine->SupportsResponseType(mozKeywordSearch,
431
&supportsResponseType);
432
if (supportsResponseType) {
433
responseType.Assign(mozKeywordSearch);
434
}
435
436
NS_ConvertUTF8toUTF16 keywordW(keyword);
437
defaultEngine->GetSubmission(keywordW, responseType,
438
NS_LITERAL_STRING("keyword"),
439
getter_AddRefs(submission));
440
441
if (submission) {
442
nsCOMPtr<nsIInputStream> postData;
443
submission->GetPostData(getter_AddRefs(postData));
444
if (aPostData) {
445
postData.forget(aPostData);
446
} else if (postData) {
447
// The submission specifies POST data (i.e. the search
448
// engine's "method" is POST), but our caller didn't allow
449
// passing post data back. No point passing back a URL that
450
// won't load properly.
451
return NS_ERROR_FAILURE;
452
}
453
454
defaultEngine->GetName(info->mKeywordProviderName);
455
info->mKeywordAsSent = keywordW;
456
return submission->GetUri(getter_AddRefs(info->mPreferredURI));
457
}
458
}
459
}
460
461
// out of options
462
return NS_ERROR_NOT_AVAILABLE;
463
}
464
465
// Helper to deal with passing around uri fixup stuff
466
nsresult nsDefaultURIFixup::TryKeywordFixupForURIInfo(
467
const nsACString& aURIString, nsDefaultURIFixupInfo* aFixupInfo,
468
bool aIsPrivateContext, nsIInputStream** aPostData) {
469
nsCOMPtr<nsIURIFixupInfo> keywordInfo;
470
nsresult rv = KeywordToURI(aURIString, aIsPrivateContext, aPostData,
471
getter_AddRefs(keywordInfo));
472
if (NS_SUCCEEDED(rv)) {
473
keywordInfo->GetKeywordProviderName(aFixupInfo->mKeywordProviderName);
474
keywordInfo->GetKeywordAsSent(aFixupInfo->mKeywordAsSent);
475
keywordInfo->GetPreferredURI(getter_AddRefs(aFixupInfo->mPreferredURI));
476
}
477
return rv;
478
}
479
480
bool nsDefaultURIFixup::MakeAlternateURI(nsCOMPtr<nsIURI>& aURI) {
481
if (!Preferences::GetRootBranch()) {
482
return false;
483
}
484
if (!Preferences::GetBool("browser.fixup.alternate.enabled", true)) {
485
return false;
486
}
487
488
// Code only works for http. Not for any other protocol including https!
489
if (!net::SchemeIsHTTP(aURI)) {
490
return false;
491
}
492
493
// Security - URLs with user / password info should NOT be fixed up
494
nsAutoCString userpass;
495
aURI->GetUserPass(userpass);
496
if (!userpass.IsEmpty()) {
497
return false;
498
}
499
// Don't fix up hosts with ports
500
int32_t port;
501
aURI->GetPort(&port);
502
if (port != -1) {
503
return false;
504
}
505
506
nsAutoCString oldHost;
507
aURI->GetHost(oldHost);
508
509
// Don't fix up 'localhost' because that's confusing:
510
if (oldHost.EqualsLiteral("localhost")) {
511
return false;
512
}
513
514
nsAutoCString newHost;
515
// Count the dots
516
int32_t numDots = 0;
517
nsReadingIterator<char> iter;
518
nsReadingIterator<char> iterEnd;
519
oldHost.BeginReading(iter);
520
oldHost.EndReading(iterEnd);
521
while (iter != iterEnd) {
522
if (*iter == '.') {
523
numDots++;
524
}
525
++iter;
526
}
527
528
// Get the prefix and suffix to stick onto the new hostname. By default these
529
// are www. & .com but they could be any other value, e.g. www. & .org
530
531
nsAutoCString prefix("www.");
532
nsAutoCString prefPrefix;
533
nsresult rv =
534
Preferences::GetCString("browser.fixup.alternate.prefix", prefPrefix);
535
if (NS_SUCCEEDED(rv)) {
536
prefix.Assign(prefPrefix);
537
}
538
539
nsAutoCString suffix(".com");
540
nsAutoCString prefSuffix;
541
rv = Preferences::GetCString("browser.fixup.alternate.suffix", prefSuffix);
542
if (NS_SUCCEEDED(rv)) {
543
suffix.Assign(prefSuffix);
544
}
545
546
if (numDots == 0) {
547
newHost.Assign(prefix);
548
newHost.Append(oldHost);
549
newHost.Append(suffix);
550
} else if (numDots == 1) {
551
if (!prefix.IsEmpty() &&
552
oldHost.EqualsIgnoreCase(prefix.get(), prefix.Length())) {
553
newHost.Assign(oldHost);
554
newHost.Append(suffix);
555
} else if (!suffix.IsEmpty()) {
556
newHost.Assign(prefix);
557
newHost.Append(oldHost);
558
} else {
559
// Do nothing
560
return false;
561
}
562
} else {
563
// Do nothing
564
return false;
565
}
566
567
if (newHost.IsEmpty()) {
568
return false;
569
}
570
571
// Assign the new host string over the old one
572
Unused << NS_MutateURI(aURI).SetHost(newHost).Finalize(aURI);
573
574
return true;
575
}
576
577
nsresult nsDefaultURIFixup::FileURIFixup(const nsACString& aStringURI,
578
nsIURI** aURI) {
579
nsAutoCString uriSpecOut;
580
581
nsresult rv = ConvertFileToStringURI(aStringURI, uriSpecOut);
582
if (NS_SUCCEEDED(rv)) {
583
// if this is file url, uriSpecOut is already in FS charset
584
if (NS_SUCCEEDED(NS_NewURI(aURI, uriSpecOut.get()))) {
585
return NS_OK;
586
}
587
}
588
return NS_ERROR_FAILURE;
589
}
590
591
nsresult nsDefaultURIFixup::ConvertFileToStringURI(const nsACString& aIn,
592
nsCString& aResult) {
593
bool attemptFixup = false;
594
595
#if defined(XP_WIN)
596
// Check for \ in the url-string or just a drive (PC)
597
if (aIn.Contains('\\') ||
598
(aIn.Length() == 2 && (aIn.Last() == ':' || aIn.Last() == '|'))) {
599
attemptFixup = true;
600
}
601
#elif defined(XP_UNIX)
602
// Check if it starts with / (UNIX)
603
if (aIn.First() == '/') {
604
attemptFixup = true;
605
}
606
#else
607
// Do nothing (All others for now)
608
#endif
609
610
if (attemptFixup) {
611
// Test if this is a valid path by trying to create a local file
612
// object. The URL of that is returned if successful.
613
614
nsCOMPtr<nsIFile> filePath;
615
nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(aIn), false,
616
getter_AddRefs(filePath));
617
618
if (NS_SUCCEEDED(rv)) {
619
NS_GetURLSpecFromFile(filePath, aResult);
620
return NS_OK;
621
}
622
}
623
624
return NS_ERROR_FAILURE;
625
}
626
627
nsresult nsDefaultURIFixup::FixupURIProtocol(const nsACString& aURIString,
628
nsDefaultURIFixupInfo* aFixupInfo,
629
nsIURI** aURI) {
630
nsAutoCString uriString(aURIString);
631
*aURI = nullptr;
632
633
// Add ftp:// or http:// to front of url if it has no spec
634
//
635
// Should fix:
636
//
637
// no-scheme.com
638
// ftp.no-scheme.com
639
// ftp4.no-scheme.com
640
// no-scheme.com/query?foo=http://www.foo.com
641
// user:pass@no-scheme.com
642
//
643
int32_t schemeDelim = uriString.Find("://");
644
int32_t firstDelim = uriString.FindCharInSet("/:");
645
if (schemeDelim <= 0 || (firstDelim != -1 && schemeDelim > firstDelim)) {
646
// find host name
647
int32_t hostPos = uriString.FindCharInSet("/:?#");
648
if (hostPos == -1) {
649
hostPos = uriString.Length();
650
}
651
652
// extract host name
653
nsAutoCString hostSpec;
654
uriString.Left(hostSpec, hostPos);
655
656
// insert url spec corresponding to host name
657
uriString.InsertLiteral("http://", 0);
658
aFixupInfo->mFixupChangedProtocol = true;
659
} // end if checkprotocol
660
661
return NS_NewURI(aURI, uriString);
662
}
663
664
bool nsDefaultURIFixup::PossiblyHostPortUrl(const nsACString& aUrl) {
665
// Oh dear, the protocol is invalid. Test if the protocol might
666
// actually be a url without a protocol:
667
//
670
//
671
// e.g. Anything of the form:
672
//
673
// <hostname>:<port> or
674
// <hostname>:<port>/
675
//
676
// Where <hostname> is a string of alphanumeric characters and dashes
677
// separated by dots.
678
// and <port> is a 5 or less digits. This actually breaks the rfc2396
679
// definition of a scheme which allows dots in schemes.
680
//
681
// Note:
682
// People expecting this to work with
683
// <user>:<password>@<host>:<port>/<url-path> will be disappointed!
684
//
685
// Note: Parser could be a lot tighter, tossing out silly hostnames
686
// such as those containing consecutive dots and so on.
687
688
// Read the hostname which should of the form
689
// [a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)*:
690
691
nsACString::const_iterator iterBegin;
692
nsACString::const_iterator iterEnd;
693
aUrl.BeginReading(iterBegin);
694
aUrl.EndReading(iterEnd);
695
nsACString::const_iterator iter = iterBegin;
696
697
while (iter != iterEnd) {
698
uint32_t chunkSize = 0;
699
// Parse a chunk of the address
700
while (iter != iterEnd &&
701
(*iter == '-' || IsAsciiAlpha(*iter) || IsAsciiDigit(*iter))) {
702
++chunkSize;
703
++iter;
704
}
705
if (chunkSize == 0 || iter == iterEnd) {
706
return false;
707
}
708
if (*iter == ':') {
709
// Go onto checking the for the digits
710
break;
711
}
712
if (*iter != '.') {
713
// Whatever it is, it ain't a hostname!
714
return false;
715
}
716
++iter;
717
}
718
if (iter == iterEnd) {
719
// No point continuing since there is no colon
720
return false;
721
}
722
++iter;
723
724
// Count the number of digits after the colon and before the
725
// next forward slash, question mark, hash sign, or end of string.
726
727
uint32_t digitCount = 0;
728
while (iter != iterEnd && digitCount <= 5) {
729
if (IsAsciiDigit(*iter)) {
730
digitCount++;
731
} else if (*iter == '/' || *iter == '?' || *iter == '#') {
732
break;
733
} else {
734
// Whatever it is, it ain't a port!
735
return false;
736
}
737
++iter;
738
}
739
if (digitCount == 0 || digitCount > 5) {
740
// No digits or more digits than a port would have.
741
return false;
742
}
743
744
// Yes, it's possibly a host:port url
745
return true;
746
}
747
748
nsresult nsDefaultURIFixup::KeywordURIFixup(const nsACString& aURIString,
749
nsDefaultURIFixupInfo* aFixupInfo,
750
bool aIsPrivateContext,
751
nsIInputStream** aPostData) {
752
// These are keyword formatted strings
753
// "what is mozilla"
754
// "what is mozilla?"
755
// "docshell site:mozilla.org" - has no dot/colon in the first space-separated
756
// substring
757
// "?mozilla" - anything that begins with a question mark
758
// "?site:mozilla.org docshell"
759
// Things that have a quote before the first dot/colon
760
// "mozilla" - checked against a whitelist to see if it's a host or not
761
// ".mozilla", "mozilla." - ditto
762
763
// These are not keyword formatted strings
764
// "www.blah.com" - first space-separated substring contains a dot, doesn't
765
// start with "?" "www.blah.com stuff" "nonQualifiedHost:80" - first
766
// space-separated substring contains a colon, doesn't start with "?"
767
// "nonQualifiedHost:80 args"
768
// "nonQualifiedHost?"
769
// "nonQualifiedHost?args"
770
// "nonQualifiedHost?some args"
771
// "blah.com."
772
773
// Note: uint32_t(kNotFound) is greater than any actual location
774
// in practice. So if we cast all locations to uint32_t, then a <
775
// b guarantees that either b is kNotFound and a is found, or both
776
// are found and a found before b.
777
778
uint32_t firstDotLoc = uint32_t(kNotFound);
779
uint32_t lastDotLoc = uint32_t(kNotFound);
780
uint32_t firstColonLoc = uint32_t(kNotFound);
781
uint32_t firstQuoteLoc = uint32_t(kNotFound);
782
uint32_t firstSpaceLoc = uint32_t(kNotFound);
783
uint32_t firstQMarkLoc = uint32_t(kNotFound);
784
uint32_t lastLSBracketLoc = uint32_t(kNotFound);
785
uint32_t lastSlashLoc = uint32_t(kNotFound);
786
uint32_t pos = 0;
787
uint32_t foundDots = 0;
788
uint32_t foundColons = 0;
789
uint32_t foundDigits = 0;
790
uint32_t foundRSBrackets = 0;
791
bool looksLikeIpv6 = true;
792
bool hasAsciiAlpha = false;
793
794
nsACString::const_iterator iterBegin;
795
nsACString::const_iterator iterEnd;
796
aURIString.BeginReading(iterBegin);
797
aURIString.EndReading(iterEnd);
798
nsACString::const_iterator iter = iterBegin;
799
800
while (iter != iterEnd) {
801
if (pos >= 1 && foundRSBrackets == 0) {
802
if (!(lastLSBracketLoc == 0 &&
803
(*iter == ':' || *iter == '.' || *iter == ']' ||
804
(*iter >= 'a' && *iter <= 'f') || (*iter >= 'A' && *iter <= 'F') ||
805
IsAsciiDigit(*iter)))) {
806
looksLikeIpv6 = false;
807
}
808
}
809
810
// If we're at the end of the string or this is the first slash,
811
// check if the thing before the slash looks like ipv4:
812
if ((iterEnd - iter == 1 ||
813
(lastSlashLoc == uint32_t(kNotFound) && *iter == '/')) &&
814
// Need 2 or 3 dots + only digits
815
(foundDots == 2 || foundDots == 3) &&
816
// and they should be all that came before now:
817
(foundDots + foundDigits == pos ||
818
// or maybe there was also exactly 1 colon that came after the last
819
// dot, and the digits, dots and colon were all that came before now:
820
(foundColons == 1 && firstColonLoc > lastDotLoc &&
821
foundDots + foundDigits + foundColons == pos))) {
822
// Hurray, we got ourselves some ipv4!
823
// At this point, there's no way we will do a keyword lookup, so just bail
824
// immediately:
825
return NS_OK;
826
}
827
828
if (*iter == '.') {
829
++foundDots;
830
lastDotLoc = pos;
831
if (firstDotLoc == uint32_t(kNotFound)) {
832
firstDotLoc = pos;
833
}
834
} else if (*iter == ':') {
835
++foundColons;
836
if (firstColonLoc == uint32_t(kNotFound)) {
837
firstColonLoc = pos;
838
}
839
} else if (*iter == ' ' && firstSpaceLoc == uint32_t(kNotFound)) {
840
firstSpaceLoc = pos;
841
} else if (*iter == '?' && firstQMarkLoc == uint32_t(kNotFound)) {
842
firstQMarkLoc = pos;
843
} else if ((*iter == '\'' || *iter == '"') &&
844
firstQuoteLoc == uint32_t(kNotFound)) {
845
firstQuoteLoc = pos;
846
} else if (*iter == '[') {
847
lastLSBracketLoc = pos;
848
} else if (*iter == ']') {
849
foundRSBrackets++;
850
} else if (*iter == '/') {
851
lastSlashLoc = pos;
852
} else if (IsAsciiAlpha(*iter)) {
853
hasAsciiAlpha = true;
854
} else if (IsAsciiDigit(*iter)) {
855
++foundDigits;
856
}
857
858
pos++;
859
iter++;
860
}
861
862
if (lastLSBracketLoc > 0 || foundRSBrackets != 1) {
863
looksLikeIpv6 = false;
864
}
865
866
// If there are only colons and only hexadecimal characters ([a-z][0-9])
867
// enclosed in [], then don't do a keyword lookup
868
if (looksLikeIpv6) {
869
return NS_OK;
870
}
871
872
nsAutoCString asciiHost;
873
nsAutoCString displayHost;
874
875
bool isValidHost =
876
aFixupInfo->mFixedURI &&
877
NS_SUCCEEDED(aFixupInfo->mFixedURI->GetAsciiHost(asciiHost)) &&
878
!asciiHost.IsEmpty();
879
880
bool isValidDisplayHost =
881
aFixupInfo->mFixedURI &&
882
NS_SUCCEEDED(aFixupInfo->mFixedURI->GetDisplayHost(displayHost)) &&
883
!displayHost.IsEmpty();
884
885
nsresult rv = NS_OK;
886
// We do keyword lookups if a space or quote preceded the dot, colon
887
// or question mark (or if the latter is not found, or if the input starts
888
// with a question mark)
889
if (((firstSpaceLoc < firstDotLoc || firstQuoteLoc < firstDotLoc) &&
890
(firstSpaceLoc < firstColonLoc || firstQuoteLoc < firstColonLoc) &&
891
(firstSpaceLoc < firstQMarkLoc || firstQuoteLoc < firstQMarkLoc)) ||
892
firstQMarkLoc == 0) {
893
rv = TryKeywordFixupForURIInfo(aFixupInfo->mOriginalInput, aFixupInfo,
894
aIsPrivateContext, aPostData);
895
// ... or when the asciiHost is the same as displayHost and there are no
896
// characters from [a-z][A-Z]
897
} else if (isValidHost && isValidDisplayHost && !hasAsciiAlpha &&
898
asciiHost.EqualsIgnoreCase(displayHost.get())) {
899
if (!StaticPrefs::browser_fixup_dns_first_for_single_words()) {
900
rv = TryKeywordFixupForURIInfo(aFixupInfo->mOriginalInput, aFixupInfo,
901
aIsPrivateContext, aPostData);
902
}
903
}
904
// ... or if there is no question mark or colon, and there is either no
905
// dot, or exactly 1 and it is the first or last character of the input:
906
else if ((firstDotLoc == uint32_t(kNotFound) ||
907
(foundDots == 1 &&
908
(firstDotLoc == 0 || firstDotLoc == aURIString.Length() - 1))) &&
909
firstColonLoc == uint32_t(kNotFound) &&
910
firstQMarkLoc == uint32_t(kNotFound)) {
911
if (isValidHost && IsDomainWhitelisted(asciiHost, firstDotLoc)) {
912
return NS_OK;
913
}
914
915
// ... unless there are no dots, and a slash, and alpha characters, and
916
// this is a valid host:
917
if (firstDotLoc == uint32_t(kNotFound) &&
918
lastSlashLoc != uint32_t(kNotFound) && hasAsciiAlpha && isValidHost) {
919
return NS_OK;
920
}
921
922
// If we get here, we don't have a valid URI, or we did but the
923
// host is not whitelisted, so we do a keyword search *anyway*:
924
rv = TryKeywordFixupForURIInfo(aFixupInfo->mOriginalInput, aFixupInfo,
925
aIsPrivateContext, aPostData);
926
}
927
return rv;
928
}
929
930
bool nsDefaultURIFixup::IsDomainWhitelisted(const nsACString& aAsciiHost,
931
const uint32_t aDotLoc) {
932
if (StaticPrefs::browser_fixup_dns_first_for_single_words()) {
933
return true;
934
}
935
// Check if this domain is whitelisted as an actual
936
// domain (which will prevent a keyword query)
937
// NB: any processing of the host here should stay in sync with
938
// code in the front-end(s) that set the pref.
939
940
nsAutoCString pref("browser.fixup.domainwhitelist.");
941
942
if (aDotLoc == aAsciiHost.Length() - 1) {
943
pref.Append(Substring(aAsciiHost, 0, aAsciiHost.Length() - 1));
944
} else {
945
pref.Append(aAsciiHost);
946
}
947
948
return Preferences::GetBool(pref.get(), false);
949
}
950
951
NS_IMETHODIMP
952
nsDefaultURIFixup::IsDomainWhitelisted(const nsACString& aDomain,
953
const uint32_t aDotLoc, bool* aResult) {
954
*aResult = IsDomainWhitelisted(aDomain, aDotLoc);
955
return NS_OK;
956
}
957
958
/* Implementation of nsIURIFixupInfo */
959
NS_IMPL_ISUPPORTS(nsDefaultURIFixupInfo, nsIURIFixupInfo)
960
961
nsDefaultURIFixupInfo::nsDefaultURIFixupInfo(const nsACString& aOriginalInput)
962
: mFixupChangedProtocol(false), mFixupCreatedAlternateURI(false) {
963
mOriginalInput = aOriginalInput;
964
}
965
966
nsDefaultURIFixupInfo::~nsDefaultURIFixupInfo() {}
967
968
NS_IMETHODIMP
969
nsDefaultURIFixupInfo::GetConsumer(nsISupports** aConsumer) {
970
*aConsumer = mConsumer;
971
NS_IF_ADDREF(*aConsumer);
972
return NS_OK;
973
}
974
975
NS_IMETHODIMP
976
nsDefaultURIFixupInfo::SetConsumer(nsISupports* aConsumer) {
977
mConsumer = aConsumer;
978
return NS_OK;
979
}
980
981
NS_IMETHODIMP
982
nsDefaultURIFixupInfo::GetPreferredURI(nsIURI** aPreferredURI) {
983
*aPreferredURI = mPreferredURI;
984
NS_IF_ADDREF(*aPreferredURI);
985
return NS_OK;
986
}
987
988
NS_IMETHODIMP
989
nsDefaultURIFixupInfo::GetFixedURI(nsIURI** aFixedURI) {
990
*aFixedURI = mFixedURI;
991
NS_IF_ADDREF(*aFixedURI);
992
return NS_OK;
993
}
994
995
NS_IMETHODIMP
996
nsDefaultURIFixupInfo::GetKeywordProviderName(nsAString& aResult) {
997
aResult = mKeywordProviderName;
998
return NS_OK;
999
}
1000
1001
NS_IMETHODIMP
1002
nsDefaultURIFixupInfo::GetKeywordAsSent(nsAString& aResult) {
1003
aResult = mKeywordAsSent;
1004
return NS_OK;
1005
}
1006
1007
NS_IMETHODIMP
1008
nsDefaultURIFixupInfo::GetFixupChangedProtocol(bool* aResult) {
1009
*aResult = mFixupChangedProtocol;
1010
return NS_OK;
1011
}
1012
1013
NS_IMETHODIMP
1014
nsDefaultURIFixupInfo::GetFixupCreatedAlternateURI(bool* aResult) {
1015
*aResult = mFixupCreatedAlternateURI;
1016
return NS_OK;
1017
}
1018
1019
NS_IMETHODIMP
1020
nsDefaultURIFixupInfo::GetOriginalInput(nsACString& aResult) {
1021
aResult = mOriginalInput;
1022
return NS_OK;
1023
}