[Simple_browser] Improve url sanitization
Change-Id: I37390f68de80ab3a525b2de7b495ee6f4bb6fc96
diff --git a/bin/simple_browser/BUILD.gn b/bin/simple_browser/BUILD.gn
index e689b2a..b9e4163 100644
--- a/bin/simple_browser/BUILD.gn
+++ b/bin/simple_browser/BUILD.gn
@@ -45,7 +45,9 @@
# fx run-host-tests simple_browser_unittests
flutter_test("simple_browser_unittests") {
sources = [
+ "sanitize_url_test.dart",
"simple_browser_test.dart",
+ "tld_checker_test.dart",
]
deps = [
diff --git a/bin/simple_browser/lib/main.dart b/bin/simple_browser/lib/main.dart
index e23d1a5..c27e80c 100644
--- a/bin/simple_browser/lib/main.dart
+++ b/bin/simple_browser/lib/main.dart
@@ -2,15 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:fidl_fuchsia_intl/fidl_async.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:fuchsia_internationalization_flutter/internationalization.dart';
import 'package:fuchsia_logger/logger.dart';
-import 'package:fuchsia_modular/module.dart' as modular;
-import 'package:fuchsia_services/services.dart' show StartupContext;
+import 'package:fuchsia_services/services.dart';
import 'package:internationalization/localizations_delegate.dart'
as localizations;
import 'package:internationalization/supported_locales.dart'
@@ -22,31 +20,12 @@
import 'src/blocs/tabs_bloc.dart';
import 'src/blocs/webpage_bloc.dart';
import 'src/models/tabs_action.dart';
-import 'src/models/webpage_action.dart';
-
-class RootIntentHandler extends modular.IntentHandler {
- final TabsBloc<WebPageBloc> tabsBloc;
- RootIntentHandler(this.tabsBloc);
-
- @override
- void handleIntent(modular.Intent intent) {
- /// if there are no tabs, add one
- /// otherwise add a new one only if the current tabs isn't a "New Tab"
- if (tabsBloc.tabs.isEmpty || tabsBloc.currentTab.url.isNotEmpty) {
- tabsBloc.request.add(NewTabAction<WebPageBloc>());
- }
- if (intent.action == 'NavigateToUrl') {
- intent.getEntity(name: 'url', type: 'string').getData().then((bytes) {
- final url = utf8.decode(bytes);
- tabsBloc.currentTab.request.add(NavigateToAction(url: url));
- });
- }
- }
-}
+import 'src/utils/tld_checker.dart';
void main() {
setupLogger(name: 'Browser');
final _context = createWebContext();
+ TldChecker().prefetchTlds();
// Bind |tabsBloc| here so that it can be referenced in the TabsBloc
// constructor arguments.
@@ -61,13 +40,12 @@
tab.dispose();
},
);
- modular.Module().registerIntentHandler(RootIntentHandler(tabsBloc));
-
final _intl = PropertyProviderProxy();
StartupContext.fromStartupInfo().incoming.connectToService(_intl);
final locales = LocaleSource(_intl);
+ tabsBloc.request.add(NewTabAction());
runApp(Localized(tabsBloc, locales.stream()));
}
@@ -76,12 +54,9 @@
class Localized extends StatelessWidget {
// The tabs bloc to use for the underlying widget.
final TabsBloc _tabsBloc;
-
// The stream of locale updates.
final Stream<Locale> _localeStream;
-
const Localized(this._tabsBloc, this._localeStream);
-
@override
Widget build(BuildContext context) {
return StreamBuilder<Locale>(
diff --git a/bin/simple_browser/lib/src/blocs/webpage_bloc.dart b/bin/simple_browser/lib/src/blocs/webpage_bloc.dart
index 498479f..212ab65 100644
--- a/bin/simple_browser/lib/src/blocs/webpage_bloc.dart
+++ b/bin/simple_browser/lib/src/blocs/webpage_bloc.dart
@@ -13,6 +13,7 @@
import 'package:zircon/zircon.dart';
import '../models/webpage_action.dart';
+import '../utils/sanitize_url.dart';
enum PageType { empty, normal, error }
PageType pageTypeForWebPageType(web.PageType pageType) {
@@ -158,7 +159,7 @@
case WebPageActionType.navigateTo:
final NavigateToAction navigate = action;
await _navigationController.loadUrl(
- _sanitizeUrl(navigate.url),
+ sanitizeUrl(navigate.url),
web.LoadUrlParams(type: web.LoadUrlReason.typed),
);
break;
@@ -172,16 +173,6 @@
await _navigationController.reload(web.ReloadType.partialCache);
}
}
-
- String _sanitizeUrl(String url) {
- if (url.startsWith('http')) {
- return url;
- } else if (url.endsWith('.com')) {
- return 'https://$url';
- } else {
- return 'https://www.google.com/search?q=${Uri.encodeQueryComponent(url)}';
- }
- }
}
class _PopupListener extends web.PopupFrameCreationListener {
diff --git a/bin/simple_browser/lib/src/utils/sanitize_url.dart b/bin/simple_browser/lib/src/utils/sanitize_url.dart
new file mode 100644
index 0000000..364a038
--- /dev/null
+++ b/bin/simple_browser/lib/src/utils/sanitize_url.dart
@@ -0,0 +1,56 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'tld_checker.dart';
+
+String sanitizeUrl(String url) {
+ // Checks if the input starts with a scheme.
+ String scheme;
+ try {
+ scheme = Uri.parse(url).scheme;
+ } on FormatException {
+ return googleKeyword(url);
+ }
+
+ // Checks if the scheme is valid.
+ // We currently only supports http, https and chrome.
+ // localhost is included in the validSchemePattern since it is recognized
+ // as a scheme by the dart Uri.parse method whene there is no other scheme.
+ final validSchemePattern = RegExp(r'^(https?|chrome|localhost)$');
+ if (scheme.isNotEmpty) {
+ if (validSchemePattern.hasMatch(scheme)) {
+ return url;
+ }
+ return googleKeyword(url);
+ }
+
+ // Adds a scheme to get a more accurate output from Uri.parse().host
+ String schemedUrl = 'https://$url';
+ String hostUrl = Uri.parse(schemedUrl).host;
+
+ // Checks if the host url has a valid pattern.
+ // Uri.parse().host does not check the validity.
+ final validHostPattern = RegExp(r'([a-zA-Z0-9@_-]{1,256}[\.]{1,1})+[\w]+');
+ if (validHostPattern.stringMatch(hostUrl) != hostUrl) {
+ return googleKeyword(url);
+ }
+
+ // Checks if the URL has a valid TLD.
+ String tld = hostUrl.split('.').last;
+ if (TldChecker().isValid(tld)) {
+ return schemedUrl;
+ }
+
+ // Checks if the URL is IPv4 address.
+ final validIpv4Pattern = RegExp(
+ r'\b((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3,3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$');
+ if (validIpv4Pattern.stringMatch(hostUrl) == hostUrl) {
+ return schemedUrl;
+ }
+
+ return googleKeyword(url);
+}
+
+String googleKeyword(String keyword) =>
+ 'https://www.google.com/search?q=${Uri.encodeQueryComponent(keyword)}';
diff --git a/bin/simple_browser/lib/src/utils/tld_checker.dart b/bin/simple_browser/lib/src/utils/tld_checker.dart
new file mode 100644
index 0000000..9e7c4cf
--- /dev/null
+++ b/bin/simple_browser/lib/src/utils/tld_checker.dart
@@ -0,0 +1,54 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:fuchsia_logger/logger.dart';
+import 'package:meta/meta.dart';
+import 'tlds_provider.dart';
+import 'valid_tlds.dart';
+
+class TldChecker {
+ List<String> _validTlds;
+
+ /// A flag that indicates if the valid TLD list is loaded or not.
+ ///
+ /// Its default value on the initilization is 'false'. and is set to 'true'
+ /// once the [prefetchTlds()] is called, and never changes unless the browser
+ /// is relaunched and this [TldChecker] is newly initiated.
+ bool _isIanaTldsLoaded;
+
+ static final TldChecker _tldCheckerInstance = TldChecker._create();
+ factory TldChecker() {
+ return _tldCheckerInstance;
+ }
+
+ TldChecker._create() {
+ _validTlds = kValidTlds;
+ _isIanaTldsLoaded = false;
+ log.info('A singleton TldChecker instance has been created.');
+ }
+
+ /// Fetches a valid TLD list from the IANA if it has not loaded yet.
+ ///
+ /// If a List<String> type parameter is given, it does not fetch the TLD list
+ /// from the web and instead, just uses the parameter list as the valid TLD
+ /// list. Therefore, this parameter should be given only for testing purposes.
+ void prefetchTlds({List<String> testTlds}) async {
+ if (testTlds != null) {
+ _validTlds = testTlds;
+ } else {
+ if (!_isIanaTldsLoaded) {
+ _validTlds = await TldsProvider().fetchTldsList() ?? kValidTlds;
+ _isIanaTldsLoaded = true;
+ } else {
+ log.warning(
+ 'TLD List is already loaded. You do not need to fetch it again.');
+ }
+ }
+ }
+
+ bool isValid(String tld) => _validTlds.contains(tld.toUpperCase());
+
+ @visibleForTesting
+ List<String> get validTlds => _validTlds;
+}
diff --git a/bin/simple_browser/lib/src/utils/tlds_provider.dart b/bin/simple_browser/lib/src/utils/tlds_provider.dart
new file mode 100644
index 0000000..6556cae
--- /dev/null
+++ b/bin/simple_browser/lib/src/utils/tlds_provider.dart
@@ -0,0 +1,49 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+import 'package:fuchsia_logger/logger.dart';
+import 'package:http/http.dart' as http;
+
+class TldsProvider {
+ String _data;
+
+ // Creates the TldsModel Once when the simple browser is initiated.
+ Future<String> loadIanaTldsList() async {
+ final response =
+ await http.get('http://data.iana.org/TLD/tlds-alpha-by-domain.txt');
+
+ if (response.statusCode == 200) {
+ log.info('Successfully loaded a TLD list from iana.org');
+ return response.body;
+ } else {
+ log.warning('Failed to load a TLD list from iana.org.');
+ return null;
+ }
+ }
+
+ Future<List<String>> fetchTldsList() async {
+ String data;
+
+ data = _data ?? await loadIanaTldsList();
+
+ if (data == null) {
+ return null;
+ }
+
+ List<String> tldsList = data.split('\n');
+
+ // Removes all white spaces.
+ for (int i = 0; i < tldsList.length; i++) {
+ tldsList[i] = tldsList[i].replaceAll(RegExp(r'\s+'), '');
+ }
+
+ // Removes all comments and empty elements.
+ tldsList.removeWhere((item) => item.isEmpty || item.startsWith('#'));
+
+ return tldsList;
+ }
+
+ set data(String testData) => _data = testData;
+}
diff --git a/bin/simple_browser/lib/src/utils/valid_tlds.dart b/bin/simple_browser/lib/src/utils/valid_tlds.dart
new file mode 100644
index 0000000..0605b4a
--- /dev/null
+++ b/bin/simple_browser/lib/src/utils/valid_tlds.dart
@@ -0,0 +1,1529 @@
+const kValidTlds = <String>[
+ 'AAA',
+ 'AARP',
+ 'ABARTH',
+ 'ABB',
+ 'ABBOTT',
+ 'ABBVIE',
+ 'ABC',
+ 'ABLE',
+ 'ABOGADO',
+ 'ABUDHABI',
+ 'AC',
+ 'ACADEMY',
+ 'ACCENTURE',
+ 'ACCOUNTANT',
+ 'ACCOUNTANTS',
+ 'ACO',
+ 'ACTOR',
+ 'AD',
+ 'ADAC',
+ 'ADS',
+ 'ADULT',
+ 'AE',
+ 'AEG',
+ 'AERO',
+ 'AETNA',
+ 'AF',
+ 'AFAMILYCOMPANY',
+ 'AFL',
+ 'AFRICA',
+ 'AG',
+ 'AGAKHAN',
+ 'AGENCY',
+ 'AI',
+ 'AIG',
+ 'AIGO',
+ 'AIRBUS',
+ 'AIRFORCE',
+ 'AIRTEL',
+ 'AKDN',
+ 'AL',
+ 'ALFAROMEO',
+ 'ALIBABA',
+ 'ALIPAY',
+ 'ALLFINANZ',
+ 'ALLSTATE',
+ 'ALLY',
+ 'ALSACE',
+ 'ALSTOM',
+ 'AM',
+ 'AMERICANEXPRESS',
+ 'AMERICANFAMILY',
+ 'AMEX',
+ 'AMFAM',
+ 'AMICA',
+ 'AMSTERDAM',
+ 'ANALYTICS',
+ 'ANDROID',
+ 'ANQUAN',
+ 'ANZ',
+ 'AO',
+ 'AOL',
+ 'APARTMENTS',
+ 'APP',
+ 'APPLE',
+ 'AQ',
+ 'AQUARELLE',
+ 'AR',
+ 'ARAB',
+ 'ARAMCO',
+ 'ARCHI',
+ 'ARMY',
+ 'ARPA',
+ 'ART',
+ 'ARTE',
+ 'AS',
+ 'ASDA',
+ 'ASIA',
+ 'ASSOCIATES',
+ 'AT',
+ 'ATHLETA',
+ 'ATTORNEY',
+ 'AU',
+ 'AUCTION',
+ 'AUDI',
+ 'AUDIBLE',
+ 'AUDIO',
+ 'AUSPOST',
+ 'AUTHOR',
+ 'AUTO',
+ 'AUTOS',
+ 'AVIANCA',
+ 'AW',
+ 'AWS',
+ 'AX',
+ 'AXA',
+ 'AZ',
+ 'AZURE',
+ 'BA',
+ 'BABY',
+ 'BAIDU',
+ 'BANAMEX',
+ 'BANANAREPUBLIC',
+ 'BAND',
+ 'BANK',
+ 'BAR',
+ 'BARCELONA',
+ 'BARCLAYCARD',
+ 'BARCLAYS',
+ 'BAREFOOT',
+ 'BARGAINS',
+ 'BASEBALL',
+ 'BASKETBALL',
+ 'BAUHAUS',
+ 'BAYERN',
+ 'BB',
+ 'BBC',
+ 'BBT',
+ 'BBVA',
+ 'BCG',
+ 'BCN',
+ 'BD',
+ 'BE',
+ 'BEATS',
+ 'BEAUTY',
+ 'BEER',
+ 'BENTLEY',
+ 'BERLIN',
+ 'BEST',
+ 'BESTBUY',
+ 'BET',
+ 'BF',
+ 'BG',
+ 'BH',
+ 'BHARTI',
+ 'BI',
+ 'BIBLE',
+ 'BID',
+ 'BIKE',
+ 'BING',
+ 'BINGO',
+ 'BIO',
+ 'BIZ',
+ 'BJ',
+ 'BLACK',
+ 'BLACKFRIDAY',
+ 'BLOCKBUSTER',
+ 'BLOG',
+ 'BLOOMBERG',
+ 'BLUE',
+ 'BM',
+ 'BMS',
+ 'BMW',
+ 'BN',
+ 'BNPPARIBAS',
+ 'BO',
+ 'BOATS',
+ 'BOEHRINGER',
+ 'BOFA',
+ 'BOM',
+ 'BOND',
+ 'BOO',
+ 'BOOK',
+ 'BOOKING',
+ 'BOSCH',
+ 'BOSTIK',
+ 'BOSTON',
+ 'BOT',
+ 'BOUTIQUE',
+ 'BOX',
+ 'BR',
+ 'BRADESCO',
+ 'BRIDGESTONE',
+ 'BROADWAY',
+ 'BROKER',
+ 'BROTHER',
+ 'BRUSSELS',
+ 'BS',
+ 'BT',
+ 'BUDAPEST',
+ 'BUGATTI',
+ 'BUILD',
+ 'BUILDERS',
+ 'BUSINESS',
+ 'BUY',
+ 'BUZZ',
+ 'BV',
+ 'BW',
+ 'BY',
+ 'BZ',
+ 'BZH',
+ 'CA',
+ 'CAB',
+ 'CAFE',
+ 'CAL',
+ 'CALL',
+ 'CALVINKLEIN',
+ 'CAM',
+ 'CAMERA',
+ 'CAMP',
+ 'CANCERRESEARCH',
+ 'CANON',
+ 'CAPETOWN',
+ 'CAPITAL',
+ 'CAPITALONE',
+ 'CAR',
+ 'CARAVAN',
+ 'CARDS',
+ 'CARE',
+ 'CAREER',
+ 'CAREERS',
+ 'CARS',
+ 'CARTIER',
+ 'CASA',
+ 'CASE',
+ 'CASEIH',
+ 'CASH',
+ 'CASINO',
+ 'CAT',
+ 'CATERING',
+ 'CATHOLIC',
+ 'CBA',
+ 'CBN',
+ 'CBRE',
+ 'CBS',
+ 'CC',
+ 'CD',
+ 'CEB',
+ 'CENTER',
+ 'CEO',
+ 'CERN',
+ 'CF',
+ 'CFA',
+ 'CFD',
+ 'CG',
+ 'CH',
+ 'CHANEL',
+ 'CHANNEL',
+ 'CHARITY',
+ 'CHASE',
+ 'CHAT',
+ 'CHEAP',
+ 'CHINTAI',
+ 'CHRISTMAS',
+ 'CHROME',
+ 'CHRYSLER',
+ 'CHURCH',
+ 'CI',
+ 'CIPRIANI',
+ 'CIRCLE',
+ 'CISCO',
+ 'CITADEL',
+ 'CITI',
+ 'CITIC',
+ 'CITY',
+ 'CITYEATS',
+ 'CK',
+ 'CL',
+ 'CLAIMS',
+ 'CLEANING',
+ 'CLICK',
+ 'CLINIC',
+ 'CLINIQUE',
+ 'CLOTHING',
+ 'CLOUD',
+ 'CLUB',
+ 'CLUBMED',
+ 'CM',
+ 'CN',
+ 'CO',
+ 'COACH',
+ 'CODES',
+ 'COFFEE',
+ 'COLLEGE',
+ 'COLOGNE',
+ 'COM',
+ 'COMCAST',
+ 'COMMBANK',
+ 'COMMUNITY',
+ 'COMPANY',
+ 'COMPARE',
+ 'COMPUTER',
+ 'COMSEC',
+ 'CONDOS',
+ 'CONSTRUCTION',
+ 'CONSULTING',
+ 'CONTACT',
+ 'CONTRACTORS',
+ 'COOKING',
+ 'COOKINGCHANNEL',
+ 'COOL',
+ 'COOP',
+ 'CORSICA',
+ 'COUNTRY',
+ 'COUPON',
+ 'COUPONS',
+ 'COURSES',
+ 'CPA',
+ 'CR',
+ 'CREDIT',
+ 'CREDITCARD',
+ 'CREDITUNION',
+ 'CRICKET',
+ 'CROWN',
+ 'CRS',
+ 'CRUISE',
+ 'CRUISES',
+ 'CSC',
+ 'CU',
+ 'CUISINELLA',
+ 'CV',
+ 'CW',
+ 'CX',
+ 'CY',
+ 'CYMRU',
+ 'CYOU',
+ 'CZ',
+ 'DABUR',
+ 'DAD',
+ 'DANCE',
+ 'DATA',
+ 'DATE',
+ 'DATING',
+ 'DATSUN',
+ 'DAY',
+ 'DCLK',
+ 'DDS',
+ 'DE',
+ 'DEAL',
+ 'DEALER',
+ 'DEALS',
+ 'DEGREE',
+ 'DELIVERY',
+ 'DELL',
+ 'DELOITTE',
+ 'DELTA',
+ 'DEMOCRAT',
+ 'DENTAL',
+ 'DENTIST',
+ 'DESI',
+ 'DESIGN',
+ 'DEV',
+ 'DHL',
+ 'DIAMONDS',
+ 'DIET',
+ 'DIGITAL',
+ 'DIRECT',
+ 'DIRECTORY',
+ 'DISCOUNT',
+ 'DISCOVER',
+ 'DISH',
+ 'DIY',
+ 'DJ',
+ 'DK',
+ 'DM',
+ 'DNP',
+ 'DO',
+ 'DOCS',
+ 'DOCTOR',
+ 'DODGE',
+ 'DOG',
+ 'DOMAINS',
+ 'DOT',
+ 'DOWNLOAD',
+ 'DRIVE',
+ 'DTV',
+ 'DUBAI',
+ 'DUCK',
+ 'DUNLOP',
+ 'DUPONT',
+ 'DURBAN',
+ 'DVAG',
+ 'DVR',
+ 'DZ',
+ 'EARTH',
+ 'EAT',
+ 'EC',
+ 'ECO',
+ 'EDEKA',
+ 'EDU',
+ 'EDUCATION',
+ 'EE',
+ 'EG',
+ 'EMAIL',
+ 'EMERCK',
+ 'ENERGY',
+ 'ENGINEER',
+ 'ENGINEERING',
+ 'ENTERPRISES',
+ 'EPSON',
+ 'EQUIPMENT',
+ 'ER',
+ 'ERICSSON',
+ 'ERNI',
+ 'ES',
+ 'ESQ',
+ 'ESTATE',
+ 'ESURANCE',
+ 'ET',
+ 'ETISALAT',
+ 'EU',
+ 'EUROVISION',
+ 'EUS',
+ 'EVENTS',
+ 'EVERBANK',
+ 'EXCHANGE',
+ 'EXPERT',
+ 'EXPOSED',
+ 'EXPRESS',
+ 'EXTRASPACE',
+ 'FAGE',
+ 'FAIL',
+ 'FAIRWINDS',
+ 'FAITH',
+ 'FAMILY',
+ 'FAN',
+ 'FANS',
+ 'FARM',
+ 'FARMERS',
+ 'FASHION',
+ 'FAST',
+ 'FEDEX',
+ 'FEEDBACK',
+ 'FERRARI',
+ 'FERRERO',
+ 'FI',
+ 'FIAT',
+ 'FIDELITY',
+ 'FIDO',
+ 'FILM',
+ 'FINAL',
+ 'FINANCE',
+ 'FINANCIAL',
+ 'FIRE',
+ 'FIRESTONE',
+ 'FIRMDALE',
+ 'FISH',
+ 'FISHING',
+ 'FIT',
+ 'FITNESS',
+ 'FJ',
+ 'FK',
+ 'FLICKR',
+ 'FLIGHTS',
+ 'FLIR',
+ 'FLORIST',
+ 'FLOWERS',
+ 'FLY',
+ 'FM',
+ 'FO',
+ 'FOO',
+ 'FOOD',
+ 'FOODNETWORK',
+ 'FOOTBALL',
+ 'FORD',
+ 'FOREX',
+ 'FORSALE',
+ 'FORUM',
+ 'FOUNDATION',
+ 'FOX',
+ 'FR',
+ 'FREE',
+ 'FRESENIUS',
+ 'FRL',
+ 'FROGANS',
+ 'FRONTDOOR',
+ 'FRONTIER',
+ 'FTR',
+ 'FUJITSU',
+ 'FUJIXEROX',
+ 'FUN',
+ 'FUND',
+ 'FURNITURE',
+ 'FUTBOL',
+ 'FYI',
+ 'GA',
+ 'GAL',
+ 'GALLERY',
+ 'GALLO',
+ 'GALLUP',
+ 'GAME',
+ 'GAMES',
+ 'GAP',
+ 'GARDEN',
+ 'GAY',
+ 'GB',
+ 'GBIZ',
+ 'GD',
+ 'GDN',
+ 'GE',
+ 'GEA',
+ 'GENT',
+ 'GENTING',
+ 'GEORGE',
+ 'GF',
+ 'GG',
+ 'GGEE',
+ 'GH',
+ 'GI',
+ 'GIFT',
+ 'GIFTS',
+ 'GIVES',
+ 'GIVING',
+ 'GL',
+ 'GLADE',
+ 'GLASS',
+ 'GLE',
+ 'GLOBAL',
+ 'GLOBO',
+ 'GM',
+ 'GMAIL',
+ 'GMBH',
+ 'GMO',
+ 'GMX',
+ 'GN',
+ 'GODADDY',
+ 'GOLD',
+ 'GOLDPOINT',
+ 'GOLF',
+ 'GOO',
+ 'GOODYEAR',
+ 'GOOG',
+ 'GOOGLE',
+ 'GOP',
+ 'GOT',
+ 'GOV',
+ 'GP',
+ 'GQ',
+ 'GR',
+ 'GRAINGER',
+ 'GRAPHICS',
+ 'GRATIS',
+ 'GREEN',
+ 'GRIPE',
+ 'GROCERY',
+ 'GROUP',
+ 'GS',
+ 'GT',
+ 'GU',
+ 'GUARDIAN',
+ 'GUCCI',
+ 'GUGE',
+ 'GUIDE',
+ 'GUITARS',
+ 'GURU',
+ 'GW',
+ 'GY',
+ 'HAIR',
+ 'HAMBURG',
+ 'HANGOUT',
+ 'HAUS',
+ 'HBO',
+ 'HDFC',
+ 'HDFCBANK',
+ 'HEALTH',
+ 'HEALTHCARE',
+ 'HELP',
+ 'HELSINKI',
+ 'HERE',
+ 'HERMES',
+ 'HGTV',
+ 'HIPHOP',
+ 'HISAMITSU',
+ 'HITACHI',
+ 'HIV',
+ 'HK',
+ 'HKT',
+ 'HM',
+ 'HN',
+ 'HOCKEY',
+ 'HOLDINGS',
+ 'HOLIDAY',
+ 'HOMEDEPOT',
+ 'HOMEGOODS',
+ 'HOMES',
+ 'HOMESENSE',
+ 'HONDA',
+ 'HORSE',
+ 'HOSPITAL',
+ 'HOST',
+ 'HOSTING',
+ 'HOT',
+ 'HOTELES',
+ 'HOTELS',
+ 'HOTMAIL',
+ 'HOUSE',
+ 'HOW',
+ 'HR',
+ 'HSBC',
+ 'HT',
+ 'HU',
+ 'HUGHES',
+ 'HYATT',
+ 'HYUNDAI',
+ 'IBM',
+ 'ICBC',
+ 'ICE',
+ 'ICU',
+ 'ID',
+ 'IE',
+ 'IEEE',
+ 'IFM',
+ 'IKANO',
+ 'IL',
+ 'IM',
+ 'IMAMAT',
+ 'IMDB',
+ 'IMMO',
+ 'IMMOBILIEN',
+ 'IN',
+ 'INC',
+ 'INDUSTRIES',
+ 'INFINITI',
+ 'INFO',
+ 'ING',
+ 'INK',
+ 'INSTITUTE',
+ 'INSURANCE',
+ 'INSURE',
+ 'INT',
+ 'INTEL',
+ 'INTERNATIONAL',
+ 'INTUIT',
+ 'INVESTMENTS',
+ 'IO',
+ 'IPIRANGA',
+ 'IQ',
+ 'IR',
+ 'IRISH',
+ 'IS',
+ 'ISMAILI',
+ 'IST',
+ 'ISTANBUL',
+ 'IT',
+ 'ITAU',
+ 'ITV',
+ 'IVECO',
+ 'JAGUAR',
+ 'JAVA',
+ 'JCB',
+ 'JCP',
+ 'JE',
+ 'JEEP',
+ 'JETZT',
+ 'JEWELRY',
+ 'JIO',
+ 'JLL',
+ 'JM',
+ 'JMP',
+ 'JNJ',
+ 'JO',
+ 'JOBS',
+ 'JOBURG',
+ 'JOT',
+ 'JOY',
+ 'JP',
+ 'JPMORGAN',
+ 'JPRS',
+ 'JUEGOS',
+ 'JUNIPER',
+ 'KAUFEN',
+ 'KDDI',
+ 'KE',
+ 'KERRYHOTELS',
+ 'KERRYLOGISTICS',
+ 'KERRYPROPERTIES',
+ 'KFH',
+ 'KG',
+ 'KH',
+ 'KI',
+ 'KIA',
+ 'KIM',
+ 'KINDER',
+ 'KINDLE',
+ 'KITCHEN',
+ 'KIWI',
+ 'KM',
+ 'KN',
+ 'KOELN',
+ 'KOMATSU',
+ 'KOSHER',
+ 'KP',
+ 'KPMG',
+ 'KPN',
+ 'KR',
+ 'KRD',
+ 'KRED',
+ 'KUOKGROUP',
+ 'KW',
+ 'KY',
+ 'KYOTO',
+ 'KZ',
+ 'LA',
+ 'LACAIXA',
+ 'LADBROKES',
+ 'LAMBORGHINI',
+ 'LAMER',
+ 'LANCASTER',
+ 'LANCIA',
+ 'LANCOME',
+ 'LAND',
+ 'LANDROVER',
+ 'LANXESS',
+ 'LASALLE',
+ 'LAT',
+ 'LATINO',
+ 'LATROBE',
+ 'LAW',
+ 'LAWYER',
+ 'LB',
+ 'LC',
+ 'LDS',
+ 'LEASE',
+ 'LECLERC',
+ 'LEFRAK',
+ 'LEGAL',
+ 'LEGO',
+ 'LEXUS',
+ 'LGBT',
+ 'LI',
+ 'LIAISON',
+ 'LIDL',
+ 'LIFE',
+ 'LIFEINSURANCE',
+ 'LIFESTYLE',
+ 'LIGHTING',
+ 'LIKE',
+ 'LILLY',
+ 'LIMITED',
+ 'LIMO',
+ 'LINCOLN',
+ 'LINDE',
+ 'LINK',
+ 'LIPSY',
+ 'LIVE',
+ 'LIVING',
+ 'LIXIL',
+ 'LK',
+ 'LLC',
+ 'LOAN',
+ 'LOANS',
+ 'LOCKER',
+ 'LOCUS',
+ 'LOFT',
+ 'LOL',
+ 'LONDON',
+ 'LOTTE',
+ 'LOTTO',
+ 'LOVE',
+ 'LPL',
+ 'LPLFINANCIAL',
+ 'LR',
+ 'LS',
+ 'LT',
+ 'LTD',
+ 'LTDA',
+ 'LU',
+ 'LUNDBECK',
+ 'LUPIN',
+ 'LUXE',
+ 'LUXURY',
+ 'LV',
+ 'LY',
+ 'MA',
+ 'MACYS',
+ 'MADRID',
+ 'MAIF',
+ 'MAISON',
+ 'MAKEUP',
+ 'MAN',
+ 'MANAGEMENT',
+ 'MANGO',
+ 'MAP',
+ 'MARKET',
+ 'MARKETING',
+ 'MARKETS',
+ 'MARRIOTT',
+ 'MARSHALLS',
+ 'MASERATI',
+ 'MATTEL',
+ 'MBA',
+ 'MC',
+ 'MCKINSEY',
+ 'MD',
+ 'ME',
+ 'MED',
+ 'MEDIA',
+ 'MEET',
+ 'MELBOURNE',
+ 'MEME',
+ 'MEMORIAL',
+ 'MEN',
+ 'MENU',
+ 'MERCKMSD',
+ 'METLIFE',
+ 'MG',
+ 'MH',
+ 'MIAMI',
+ 'MICROSOFT',
+ 'MIL',
+ 'MINI',
+ 'MINT',
+ 'MIT',
+ 'MITSUBISHI',
+ 'MK',
+ 'ML',
+ 'MLB',
+ 'MLS',
+ 'MM',
+ 'MMA',
+ 'MN',
+ 'MO',
+ 'MOBI',
+ 'MOBILE',
+ 'MODA',
+ 'MOE',
+ 'MOI',
+ 'MOM',
+ 'MONASH',
+ 'MONEY',
+ 'MONSTER',
+ 'MOPAR',
+ 'MORMON',
+ 'MORTGAGE',
+ 'MOSCOW',
+ 'MOTO',
+ 'MOTORCYCLES',
+ 'MOV',
+ 'MOVIE',
+ 'MOVISTAR',
+ 'MP',
+ 'MQ',
+ 'MR',
+ 'MS',
+ 'MSD',
+ 'MT',
+ 'MTN',
+ 'MTR',
+ 'MU',
+ 'MUSEUM',
+ 'MUTUAL',
+ 'MV',
+ 'MW',
+ 'MX',
+ 'MY',
+ 'MZ',
+ 'NA',
+ 'NAB',
+ 'NADEX',
+ 'NAGOYA',
+ 'NAME',
+ 'NATIONWIDE',
+ 'NATURA',
+ 'NAVY',
+ 'NBA',
+ 'NC',
+ 'NE',
+ 'NEC',
+ 'NET',
+ 'NETBANK',
+ 'NETFLIX',
+ 'NETWORK',
+ 'NEUSTAR',
+ 'NEW',
+ 'NEWHOLLAND',
+ 'NEWS',
+ 'NEXT',
+ 'NEXTDIRECT',
+ 'NEXUS',
+ 'NF',
+ 'NFL',
+ 'NG',
+ 'NGO',
+ 'NHK',
+ 'NI',
+ 'NICO',
+ 'NIKE',
+ 'NIKON',
+ 'NINJA',
+ 'NISSAN',
+ 'NISSAY',
+ 'NL',
+ 'NO',
+ 'NOKIA',
+ 'NORTHWESTERNMUTUAL',
+ 'NORTON',
+ 'NOW',
+ 'NOWRUZ',
+ 'NOWTV',
+ 'NP',
+ 'NR',
+ 'NRA',
+ 'NRW',
+ 'NTT',
+ 'NU',
+ 'NYC',
+ 'NZ',
+ 'OBI',
+ 'OBSERVER',
+ 'OFF',
+ 'OFFICE',
+ 'OKINAWA',
+ 'OLAYAN',
+ 'OLAYANGROUP',
+ 'OLDNAVY',
+ 'OLLO',
+ 'OM',
+ 'OMEGA',
+ 'ONE',
+ 'ONG',
+ 'ONL',
+ 'ONLINE',
+ 'ONYOURSIDE',
+ 'OOO',
+ 'OPEN',
+ 'ORACLE',
+ 'ORANGE',
+ 'ORG',
+ 'ORGANIC',
+ 'ORIGINS',
+ 'OSAKA',
+ 'OTSUKA',
+ 'OTT',
+ 'OVH',
+ 'PA',
+ 'PAGE',
+ 'PANASONIC',
+ 'PARIS',
+ 'PARS',
+ 'PARTNERS',
+ 'PARTS',
+ 'PARTY',
+ 'PASSAGENS',
+ 'PAY',
+ 'PCCW',
+ 'PE',
+ 'PET',
+ 'PF',
+ 'PFIZER',
+ 'PG',
+ 'PH',
+ 'PHARMACY',
+ 'PHD',
+ 'PHILIPS',
+ 'PHONE',
+ 'PHOTO',
+ 'PHOTOGRAPHY',
+ 'PHOTOS',
+ 'PHYSIO',
+ 'PIAGET',
+ 'PICS',
+ 'PICTET',
+ 'PICTURES',
+ 'PID',
+ 'PIN',
+ 'PING',
+ 'PINK',
+ 'PIONEER',
+ 'PIZZA',
+ 'PK',
+ 'PL',
+ 'PLACE',
+ 'PLAY',
+ 'PLAYSTATION',
+ 'PLUMBING',
+ 'PLUS',
+ 'PM',
+ 'PN',
+ 'PNC',
+ 'POHL',
+ 'POKER',
+ 'POLITIE',
+ 'PORN',
+ 'POST',
+ 'PR',
+ 'PRAMERICA',
+ 'PRAXI',
+ 'PRESS',
+ 'PRIME',
+ 'PRO',
+ 'PROD',
+ 'PRODUCTIONS',
+ 'PROF',
+ 'PROGRESSIVE',
+ 'PROMO',
+ 'PROPERTIES',
+ 'PROPERTY',
+ 'PROTECTION',
+ 'PRU',
+ 'PRUDENTIAL',
+ 'PS',
+ 'PT',
+ 'PUB',
+ 'PW',
+ 'PWC',
+ 'PY',
+ 'QA',
+ 'QPON',
+ 'QUEBEC',
+ 'QUEST',
+ 'QVC',
+ 'RACING',
+ 'RADIO',
+ 'RAID',
+ 'RE',
+ 'READ',
+ 'REALESTATE',
+ 'REALTOR',
+ 'REALTY',
+ 'RECIPES',
+ 'RED',
+ 'REDSTONE',
+ 'REDUMBRELLA',
+ 'REHAB',
+ 'REISE',
+ 'REISEN',
+ 'REIT',
+ 'RELIANCE',
+ 'REN',
+ 'RENT',
+ 'RENTALS',
+ 'REPAIR',
+ 'REPORT',
+ 'REPUBLICAN',
+ 'REST',
+ 'RESTAURANT',
+ 'REVIEW',
+ 'REVIEWS',
+ 'REXROTH',
+ 'RICH',
+ 'RICHARDLI',
+ 'RICOH',
+ 'RIGHTATHOME',
+ 'RIL',
+ 'RIO',
+ 'RIP',
+ 'RMIT',
+ 'RO',
+ 'ROCHER',
+ 'ROCKS',
+ 'RODEO',
+ 'ROGERS',
+ 'ROOM',
+ 'RS',
+ 'RSVP',
+ 'RU',
+ 'RUGBY',
+ 'RUHR',
+ 'RUN',
+ 'RW',
+ 'RWE',
+ 'RYUKYU',
+ 'SA',
+ 'SAARLAND',
+ 'SAFE',
+ 'SAFETY',
+ 'SAKURA',
+ 'SALE',
+ 'SALON',
+ 'SAMSCLUB',
+ 'SAMSUNG',
+ 'SANDVIK',
+ 'SANDVIKCOROMANT',
+ 'SANOFI',
+ 'SAP',
+ 'SARL',
+ 'SAS',
+ 'SAVE',
+ 'SAXO',
+ 'SB',
+ 'SBI',
+ 'SBS',
+ 'SC',
+ 'SCA',
+ 'SCB',
+ 'SCHAEFFLER',
+ 'SCHMIDT',
+ 'SCHOLARSHIPS',
+ 'SCHOOL',
+ 'SCHULE',
+ 'SCHWARZ',
+ 'SCIENCE',
+ 'SCJOHNSON',
+ 'SCOR',
+ 'SCOT',
+ 'SD',
+ 'SE',
+ 'SEARCH',
+ 'SEAT',
+ 'SECURE',
+ 'SECURITY',
+ 'SEEK',
+ 'SELECT',
+ 'SENER',
+ 'SERVICES',
+ 'SES',
+ 'SEVEN',
+ 'SEW',
+ 'SEX',
+ 'SEXY',
+ 'SFR',
+ 'SG',
+ 'SH',
+ 'SHANGRILA',
+ 'SHARP',
+ 'SHAW',
+ 'SHELL',
+ 'SHIA',
+ 'SHIKSHA',
+ 'SHOES',
+ 'SHOP',
+ 'SHOPPING',
+ 'SHOUJI',
+ 'SHOW',
+ 'SHOWTIME',
+ 'SHRIRAM',
+ 'SI',
+ 'SILK',
+ 'SINA',
+ 'SINGLES',
+ 'SITE',
+ 'SJ',
+ 'SK',
+ 'SKI',
+ 'SKIN',
+ 'SKY',
+ 'SKYPE',
+ 'SL',
+ 'SLING',
+ 'SM',
+ 'SMART',
+ 'SMILE',
+ 'SN',
+ 'SNCF',
+ 'SO',
+ 'SOCCER',
+ 'SOCIAL',
+ 'SOFTBANK',
+ 'SOFTWARE',
+ 'SOHU',
+ 'SOLAR',
+ 'SOLUTIONS',
+ 'SONG',
+ 'SONY',
+ 'SOY',
+ 'SPACE',
+ 'SPORT',
+ 'SPOT',
+ 'SPREADBETTING',
+ 'SR',
+ 'SRL',
+ 'SRT',
+ 'SS',
+ 'ST',
+ 'STADA',
+ 'STAPLES',
+ 'STAR',
+ 'STATEBANK',
+ 'STATEFARM',
+ 'STC',
+ 'STCGROUP',
+ 'STOCKHOLM',
+ 'STORAGE',
+ 'STORE',
+ 'STREAM',
+ 'STUDIO',
+ 'STUDY',
+ 'STYLE',
+ 'SU',
+ 'SUCKS',
+ 'SUPPLIES',
+ 'SUPPLY',
+ 'SUPPORT',
+ 'SURF',
+ 'SURGERY',
+ 'SUZUKI',
+ 'SV',
+ 'SWATCH',
+ 'SWIFTCOVER',
+ 'SWISS',
+ 'SX',
+ 'SY',
+ 'SYDNEY',
+ 'SYMANTEC',
+ 'SYSTEMS',
+ 'SZ',
+ 'TAB',
+ 'TAIPEI',
+ 'TALK',
+ 'TAOBAO',
+ 'TARGET',
+ 'TATAMOTORS',
+ 'TATAR',
+ 'TATTOO',
+ 'TAX',
+ 'TAXI',
+ 'TC',
+ 'TCI',
+ 'TD',
+ 'TDK',
+ 'TEAM',
+ 'TECH',
+ 'TECHNOLOGY',
+ 'TEL',
+ 'TELEFONICA',
+ 'TEMASEK',
+ 'TENNIS',
+ 'TEVA',
+ 'TF',
+ 'TG',
+ 'TH',
+ 'THD',
+ 'THEATER',
+ 'THEATRE',
+ 'TIAA',
+ 'TICKETS',
+ 'TIENDA',
+ 'TIFFANY',
+ 'TIPS',
+ 'TIRES',
+ 'TIROL',
+ 'TJ',
+ 'TJMAXX',
+ 'TJX',
+ 'TK',
+ 'TKMAXX',
+ 'TL',
+ 'TM',
+ 'TMALL',
+ 'TN',
+ 'TO',
+ 'TODAY',
+ 'TOKYO',
+ 'TOOLS',
+ 'TOP',
+ 'TORAY',
+ 'TOSHIBA',
+ 'TOTAL',
+ 'TOURS',
+ 'TOWN',
+ 'TOYOTA',
+ 'TOYS',
+ 'TR',
+ 'TRADE',
+ 'TRADING',
+ 'TRAINING',
+ 'TRAVEL',
+ 'TRAVELCHANNEL',
+ 'TRAVELERS',
+ 'TRAVELERSINSURANCE',
+ 'TRUST',
+ 'TRV',
+ 'TT',
+ 'TUBE',
+ 'TUI',
+ 'TUNES',
+ 'TUSHU',
+ 'TV',
+ 'TVS',
+ 'TW',
+ 'TZ',
+ 'UA',
+ 'UBANK',
+ 'UBS',
+ 'UCONNECT',
+ 'UG',
+ 'UK',
+ 'UNICOM',
+ 'UNIVERSITY',
+ 'UNO',
+ 'UOL',
+ 'UPS',
+ 'US',
+ 'UY',
+ 'UZ',
+ 'VA',
+ 'VACATIONS',
+ 'VANA',
+ 'VANGUARD',
+ 'VC',
+ 'VE',
+ 'VEGAS',
+ 'VENTURES',
+ 'VERISIGN',
+ 'VERSICHERUNG',
+ 'VET',
+ 'VG',
+ 'VI',
+ 'VIAJES',
+ 'VIDEO',
+ 'VIG',
+ 'VIKING',
+ 'VILLAS',
+ 'VIN',
+ 'VIP',
+ 'VIRGIN',
+ 'VISA',
+ 'VISION',
+ 'VISTAPRINT',
+ 'VIVA',
+ 'VIVO',
+ 'VLAANDEREN',
+ 'VN',
+ 'VODKA',
+ 'VOLKSWAGEN',
+ 'VOLVO',
+ 'VOTE',
+ 'VOTING',
+ 'VOTO',
+ 'VOYAGE',
+ 'VU',
+ 'VUELOS',
+ 'WALES',
+ 'WALMART',
+ 'WALTER',
+ 'WANG',
+ 'WANGGOU',
+ 'WARMAN',
+ 'WATCH',
+ 'WATCHES',
+ 'WEATHER',
+ 'WEATHERCHANNEL',
+ 'WEBCAM',
+ 'WEBER',
+ 'WEBSITE',
+ 'WED',
+ 'WEDDING',
+ 'WEIBO',
+ 'WEIR',
+ 'WF',
+ 'WHOSWHO',
+ 'WIEN',
+ 'WIKI',
+ 'WILLIAMHILL',
+ 'WIN',
+ 'WINDOWS',
+ 'WINE',
+ 'WINNERS',
+ 'WME',
+ 'WOLTERSKLUWER',
+ 'WOODSIDE',
+ 'WORK',
+ 'WORKS',
+ 'WORLD',
+ 'WOW',
+ 'WS',
+ 'WTC',
+ 'WTF',
+ 'XBOX',
+ 'XEROX',
+ 'XFINITY',
+ 'XIHUAN',
+ 'XIN',
+ 'XN--11B4C3D',
+ 'XN--1CK2E1B',
+ 'XN--1QQW23A',
+ 'XN--2SCRJ9C',
+ 'XN--30RR7Y',
+ 'XN--3BST00M',
+ 'XN--3DS443G',
+ 'XN--3E0B707E',
+ 'XN--3HCRJ9C',
+ 'XN--3OQ18VL8PN36A',
+ 'XN--3PXU8K',
+ 'XN--42C2D9A',
+ 'XN--45BR5CYL',
+ 'XN--45BRJ9C',
+ 'XN--45Q11C',
+ 'XN--4GBRIM',
+ 'XN--54B7FTA0CC',
+ 'XN--55QW42G',
+ 'XN--55QX5D',
+ 'XN--5SU34J936BGSG',
+ 'XN--5TZM5G',
+ 'XN--6FRZ82G',
+ 'XN--6QQ986B3XL',
+ 'XN--80ADXHKS',
+ 'XN--80AO21A',
+ 'XN--80AQECDR1A',
+ 'XN--80ASEHDB',
+ 'XN--80ASWG',
+ 'XN--8Y0A063A',
+ 'XN--90A3AC',
+ 'XN--90AE',
+ 'XN--90AIS',
+ 'XN--9DBQ2A',
+ 'XN--9ET52U',
+ 'XN--9KRT00A',
+ 'XN--B4W605FERD',
+ 'XN--BCK1B9A5DRE4C',
+ 'XN--C1AVG',
+ 'XN--C2BR7G',
+ 'XN--CCK2B3B',
+ 'XN--CG4BKI',
+ 'XN--CLCHC0EA0B2G2A9GCD',
+ 'XN--CZR694B',
+ 'XN--CZRS0T',
+ 'XN--CZRU2D',
+ 'XN--D1ACJ3B',
+ 'XN--D1ALF',
+ 'XN--E1A4C',
+ 'XN--ECKVDTC9D',
+ 'XN--EFVY88H',
+ 'XN--ESTV75G',
+ 'XN--FCT429K',
+ 'XN--FHBEI',
+ 'XN--FIQ228C5HS',
+ 'XN--FIQ64B',
+ 'XN--FIQS8S',
+ 'XN--FIQZ9S',
+ 'XN--FJQ720A',
+ 'XN--FLW351E',
+ 'XN--FPCRJ9C3D',
+ 'XN--FZC2C9E2C',
+ 'XN--FZYS8D69UVGM',
+ 'XN--G2XX48C',
+ 'XN--GCKR3F0F',
+ 'XN--GECRJ9C',
+ 'XN--GK3AT1E',
+ 'XN--H2BREG3EVE',
+ 'XN--H2BRJ9C',
+ 'XN--H2BRJ9C8C',
+ 'XN--HXT814E',
+ 'XN--I1B6B1A6A2E',
+ 'XN--IMR513N',
+ 'XN--IO0A7I',
+ 'XN--J1AEF',
+ 'XN--J1AMH',
+ 'XN--J6W193G',
+ 'XN--JLQ61U9W7B',
+ 'XN--JVR189M',
+ 'XN--KCRX77D1X4A',
+ 'XN--KPRW13D',
+ 'XN--KPRY57D',
+ 'XN--KPU716F',
+ 'XN--KPUT3I',
+ 'XN--L1ACC',
+ 'XN--LGBBAT1AD8J',
+ 'XN--MGB9AWBF',
+ 'XN--MGBA3A3EJT',
+ 'XN--MGBA3A4F16A',
+ 'XN--MGBA7C0BBN0A',
+ 'XN--MGBAAKC7DVF',
+ 'XN--MGBAAM7A8H',
+ 'XN--MGBAB2BD',
+ 'XN--MGBAH1A3HJKRD',
+ 'XN--MGBAI9AZGQP6J',
+ 'XN--MGBAYH7GPA',
+ 'XN--MGBBH1A',
+ 'XN--MGBBH1A71E',
+ 'XN--MGBC0A9AZCG',
+ 'XN--MGBCA7DZDO',
+ 'XN--MGBERP4A5D4AR',
+ 'XN--MGBGU82A',
+ 'XN--MGBI4ECEXP',
+ 'XN--MGBPL2FH',
+ 'XN--MGBT3DHD',
+ 'XN--MGBTX2B',
+ 'XN--MGBX4CD0AB',
+ 'XN--MIX891F',
+ 'XN--MK1BU44C',
+ 'XN--MXTQ1M',
+ 'XN--NGBC5AZD',
+ 'XN--NGBE9E0A',
+ 'XN--NGBRX',
+ 'XN--NODE',
+ 'XN--NQV7F',
+ 'XN--NQV7FS00EMA',
+ 'XN--NYQY26A',
+ 'XN--O3CW4H',
+ 'XN--OGBPF8FL',
+ 'XN--OTU796D',
+ 'XN--P1ACF',
+ 'XN--P1AI',
+ 'XN--PBT977C',
+ 'XN--PGBS0DH',
+ 'XN--PSSY2U',
+ 'XN--Q9JYB4C',
+ 'XN--QCKA1PMC',
+ 'XN--QXA6A',
+ 'XN--QXAM',
+ 'XN--RHQV96G',
+ 'XN--ROVU88B',
+ 'XN--RVC1E0AM3E',
+ 'XN--S9BRJ9C',
+ 'XN--SES554G',
+ 'XN--T60B56A',
+ 'XN--TCKWE',
+ 'XN--TIQ49XQYJ',
+ 'XN--UNUP4Y',
+ 'XN--VERMGENSBERATER-CTB',
+ 'XN--VERMGENSBERATUNG-PWB',
+ 'XN--VHQUV',
+ 'XN--VUQ861B',
+ 'XN--W4R85EL8FHU5DNRA',
+ 'XN--W4RS40L',
+ 'XN--WGBH1C',
+ 'XN--WGBL6A',
+ 'XN--XHQ521B',
+ 'XN--XKC2AL3HYE2A',
+ 'XN--XKC2DL3A5EE0H',
+ 'XN--Y9A3AQ',
+ 'XN--YFRO4I67O',
+ 'XN--YGBI2AMMX',
+ 'XN--ZFR164B',
+ 'XXX',
+ 'XYZ',
+ 'YACHTS',
+ 'YAHOO',
+ 'YAMAXUN',
+ 'YANDEX',
+ 'YE',
+ 'YODOBASHI',
+ 'YOGA',
+ 'YOKOHAMA',
+ 'YOU',
+ 'YOUTUBE',
+ 'YT',
+ 'YUN',
+ 'ZA',
+ 'ZAPPOS',
+ 'ZARA',
+ 'ZERO',
+ 'ZIP',
+ 'ZM',
+ 'ZONE',
+ 'ZUERICH',
+ 'ZW',
+];
diff --git a/bin/simple_browser/lib/src/widgets/navigation_field.dart b/bin/simple_browser/lib/src/widgets/navigation_field.dart
index 92497a5..0c3d670 100644
--- a/bin/simple_browser/lib/src/widgets/navigation_field.dart
+++ b/bin/simple_browser/lib/src/widgets/navigation_field.dart
@@ -80,6 +80,7 @@
cursorWidth: 8,
cursorRadius: Radius.zero,
cursorColor: Colors.black,
+ enableInteractiveSelection: true,
textAlign: TextAlign.center,
keyboardType: TextInputType.url,
decoration: InputDecoration(
diff --git a/bin/simple_browser/test/sanitize_url_test.dart b/bin/simple_browser/test/sanitize_url_test.dart
new file mode 100644
index 0000000..90210c6
--- /dev/null
+++ b/bin/simple_browser/test/sanitize_url_test.dart
@@ -0,0 +1,79 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter_test/flutter_test.dart';
+import 'package:fuchsia_logger/logger.dart';
+
+// ignore_for_file: implementation_imports
+import 'package:simple_browser/src/utils/sanitize_url.dart';
+
+void main() {
+ setupLogger(name: 'sanitize_url_test');
+
+ void urlSanitizationTest(String testUrl, String expectedResult) {
+ String testResult = sanitizeUrl(testUrl);
+
+ expect(testResult, expectedResult,
+ reason: 'expected "$expectedResult" but actually "$testResult".');
+ }
+
+ test('URL Sanitization: URLs with supported schemes.', () {
+ // With a URL that causes Format Exception to Dart's Uri.parse().
+ urlSanitizationTest('http ://wrongformat',
+ 'https://www.google.com/search?q=http+%3A%2F%2Fwrongformat');
+
+ // With a supported scheme(https) and a valid TLD(com).
+ urlSanitizationTest('https://google.com', 'https://google.com');
+
+ // With a supported scheme(http) and a valid TLD(org).
+ urlSanitizationTest('http://wikipedia.org', 'http://wikipedia.org');
+
+ // With a supported scheme(chrome) and a valid URL.
+ urlSanitizationTest('chrome://gpu', 'chrome://gpu');
+
+ // With a supported scheme(https) and an invalid TLD.
+ urlSanitizationTest('https://google.cooom', 'https://google.cooom');
+
+ // localhost with a port number
+ urlSanitizationTest('localhost:8000', 'localhost:8000');
+ });
+
+ test('URL Sanitization: URLs without schemes.', () {
+ const String googleSearchUrl = 'https://www.google.com/search?q=';
+
+ // With a valid host pattern and a valid TLD.
+ urlSanitizationTest('flutter.dev', 'https://flutter.dev');
+
+ // With a valid host pattern and a valid TLD and a path
+ urlSanitizationTest('flutter.dev/clock', 'https://flutter.dev/clock');
+
+ // With an invalid host pattern and a valid TLD.
+ urlSanitizationTest(
+ 'flu#%*tter.dev', '${googleSearchUrl}flu%23%25%2Atter.dev');
+
+ // With a valid host pattern and an invalid TLD.
+ urlSanitizationTest('google.cooom', '${googleSearchUrl}google.cooom');
+
+ // With a keyword.
+ urlSanitizationTest('fuchsia', '${googleSearchUrl}fuchsia');
+
+ // With a valid ip address.
+ urlSanitizationTest('255.111.18.1', 'https://255.111.18.1');
+
+ // With a valid ip address and a path
+ urlSanitizationTest('200.102.11.9/hello', 'https://200.102.11.9/hello');
+
+ // With an invalid ip address (Each number must be a value between 0 - 255).
+ urlSanitizationTest('955.111.18.1', '${googleSearchUrl}955.111.18.1');
+
+ // With an invalid ip address (The address must consist of 4 numbers).
+ urlSanitizationTest('255.111.18', '${googleSearchUrl}255.111.18');
+
+ // With an invalid ip address (Spaced not allowed).
+ urlSanitizationTest('255.111.18. 1', '${googleSearchUrl}255.111.18.+1');
+
+ // With an invalid ip address (Non-digit characters except '.' not allowed).
+ urlSanitizationTest('255.111.18.*1', '${googleSearchUrl}255.111.18.%2A1');
+ });
+}
diff --git a/bin/simple_browser/test/tld_checker_test.dart b/bin/simple_browser/test/tld_checker_test.dart
new file mode 100644
index 0000000..4050f72
--- /dev/null
+++ b/bin/simple_browser/test/tld_checker_test.dart
@@ -0,0 +1,55 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:fuchsia_logger/logger.dart';
+import 'package:test/test.dart';
+
+// ignore_for_file: implementation_imports
+import 'package:simple_browser/src/utils/tld_checker.dart';
+import 'package:simple_browser/src/utils/tlds_provider.dart';
+
+void main() {
+ setupLogger(name: 'tld_checker_test');
+ test('TLD Validity check test with local TLD list.', () {
+ expect(TldChecker().isValid('dev'), true, reason: '"dev" should be valid.');
+ expect(TldChecker().isValid('asdf'), false,
+ reason: '"asdf" should be invalid.');
+ });
+
+ test('Fetch test with two valid TLDs and one comment line.', () async {
+ String testData = '''
+ # version 2019111500, Last Updated Fri Nov 15 07:07:01 2019 UTC
+ AAA
+ BBB
+ ''';
+ TldsProvider provider = TldsProvider()..data = testData;
+ List<String> testTlds = await provider.fetchTldsList();
+ TldChecker().prefetchTlds(testTlds: testTlds);
+
+ expect(TldChecker().validTlds.length, 2,
+ reason: 'The length of valid TLD list should be two.');
+ expect(TldChecker().isValid('aaa'), true, reason: '"aaa" should be valid.');
+ expect(TldChecker().isValid('bbb'), true, reason: '"bbb should be valid.');
+ });
+
+ test('Fetch test with one valid TLDs and multiple comment lines.', () async {
+ String testData = '''
+ # version 2019111500, Last Updated Fri Nov 15 07:07:01 2019 UTC
+ # Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+ # Donec sed libero at lacus blandit scelerisque.
+ # Aliquam at felis ac orci facilisis tincidunt.
+ # Nullam eu justo faucibus, pretium urna et, pretium magna.
+ # Duis faucibus elit id felis sollicitudin, quis fermentum neque convallis.
+ QQQ
+ ''';
+
+ TldsProvider provider = TldsProvider()..data = testData;
+ List<String> testTlds = await provider.fetchTldsList();
+ TldChecker().prefetchTlds(testTlds: testTlds);
+
+ expect(TldChecker().validTlds.length, 1,
+ reason: 'The length of valid TLD list should be one.');
+ expect(TldChecker().isValid('qqq'), true, reason: '"qqq" should be valid.');
+ });
+}