Migrate chrome://browser-switch to TypeScript.
Bug: 1189595
Change-Id: Ia5e235b41e7ca3094e2e0a24b4836052a22b5609
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2954438
Commit-Queue: dpapad <dpapad@chromium.org>
Reviewed-by: John Lee <johntlee@chromium.org>
Cr-Commit-Position: refs/heads/master@{#892021}
diff --git a/chrome/browser/resources/BUILD.gn b/chrome/browser/resources/BUILD.gn
index 8859049..4d0684a 100644
--- a/chrome/browser/resources/BUILD.gn
+++ b/chrome/browser/resources/BUILD.gn
@@ -176,9 +176,6 @@
"webapks:closure_compile",
]
}
- if (is_win || is_mac || is_linux || is_chromeos_lacros) {
- deps += [ "browser_switch:closure_compile" ]
- }
# Note: The following targets should only be type-checked on Windows builds,
# but because Window bots don't depend on Java, |closure_compile| is off on
diff --git a/chrome/browser/resources/browser_switch/BUILD.gn b/chrome/browser/resources/browser_switch/BUILD.gn
index 60dd306..ddac49fe 100644
--- a/chrome/browser/resources/browser_switch/BUILD.gn
+++ b/chrome/browser/resources/browser_switch/BUILD.gn
@@ -3,11 +3,13 @@
# found in the LICENSE file.
import("//chrome/common/features.gni")
-import("//third_party/closure_compiler/compile_js.gni")
import("//tools/grit/grit_rule.gni")
import("//tools/polymer/html_to_js.gni")
+import("//tools/typescript/ts_library.gni")
import("//ui/webui/resources/tools/generate_grd.gni")
+assert(is_win || is_mac || is_linux || is_chromeos_lacros)
+
grit("resources") {
defines = chrome_grit_defines
@@ -27,60 +29,48 @@
generate_grd("build_grd") {
grd_prefix = "browser_switch"
- input_files = [
- "app.js",
- "browser_switch.html",
- "browser_switch_proxy.js",
- "internals/browser_switch_internals.html",
- "internals/browser_switch_internals.js",
- ]
- input_files_base_dir = rebase_path(target_gen_dir, root_build_dir)
-
out_grd = "$target_gen_dir/resources.grd"
+ input_files = [
+ "browser_switch.html",
+ "internals/browser_switch_internals.html",
+ ]
+ input_files_base_dir = rebase_path(".", "//")
+
+ deps = [ ":build_ts" ]
+ manifest_files = [ "$target_gen_dir/tsconfig.manifest" ]
+}
+
+ts_library("build_ts") {
+ root_dir = target_gen_dir
+ out_dir = "$target_gen_dir/tsc"
+ tsconfig_base = "tsconfig_base.json"
+ in_files = [
+ "app.ts",
+ "browser_switch_proxy.ts",
+ "internals/browser_switch_internals.ts",
+ ]
+ definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
deps = [
- ":copy_files",
- ":copy_files_internals",
+ "//third_party/polymer/v3_0:library",
+ "//ui/webui/resources:library",
+ ]
+ extra_deps = [
+ ":copy_browser_proxy",
+ ":copy_internals",
":web_components",
]
}
-copy("copy_files") {
- sources = [
- "browser_switch.html",
- "browser_switch_proxy.js",
- ]
+copy("copy_browser_proxy") {
+ sources = [ "browser_switch_proxy.ts" ]
outputs = [ "$target_gen_dir/{{source_file_part}}" ]
}
-copy("copy_files_internals") {
- sources = [
- "internals/browser_switch_internals.html",
- "internals/browser_switch_internals.js",
- ]
+copy("copy_internals") {
+ sources = [ "internals/browser_switch_internals.ts" ]
outputs = [ "$target_gen_dir/internals/{{source_file_part}}" ]
}
-js_type_check("closure_compile") {
- is_polymer3 = true
- deps = [
- ":app",
- ":browser_switch_proxy",
- "internals:browser_switch_internals",
- ]
-}
-
-js_library("app") {
- deps = [
- "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
- "//ui/webui/resources/js:i18n_behavior.m",
- "//ui/webui/resources/js:load_time_data.m",
- ]
-}
-
-js_library("browser_switch_proxy") {
- deps = [ "//ui/webui/resources/js:cr.m" ]
-}
-
html_to_js("web_components") {
- js_files = [ "app.js" ]
+ js_files = [ "app.ts" ]
}
diff --git a/chrome/browser/resources/browser_switch/app.js b/chrome/browser/resources/browser_switch/app.ts
similarity index 79%
rename from chrome/browser/resources/browser_switch/app.js
rename to chrome/browser/resources/browser_switch/app.ts
index 5cb79eb..4e70997 100644
--- a/chrome/browser/resources/browser_switch/app.js
+++ b/chrome/browser/resources/browser_switch/app.ts
@@ -7,30 +7,23 @@
import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
import './strings.m.js';
-import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
+import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {BrowserSwitchProxy, BrowserSwitchProxyImpl} from './browser_switch_proxy.js';
-/** @type {number} */
-const MS_PER_SECOND = 1000;
+const MS_PER_SECOND: number = 1000;
-/** @enum {string} */
-const LaunchError = {
- GENERIC_ERROR: 'genericError',
- PROTOCOL_ERROR: 'protocolError',
-};
+enum LaunchError {
+ GENERIC_ERROR = 'genericError',
+ PROTOCOL_ERROR = 'protocolError',
+}
-/**
- * @constructor
- * @extends {PolymerElement}
- * @implements {I18nBehaviorInterface}
- */
const BrowserSwitchAppElementBase =
- mixinBehaviors([I18nBehavior], PolymerElement);
+ mixinBehaviors([I18nBehavior], PolymerElement) as
+ {new (): PolymerElement & I18nBehavior};
-/** @polymer */
class BrowserSwitchAppElement extends BrowserSwitchAppElementBase {
static get is() {
return 'browser-switch-app';
@@ -44,7 +37,6 @@
return {
/**
* URL to launch in the alternative browser.
- * @private
*/
url_: {
type: String,
@@ -55,7 +47,6 @@
/**
* Error message, or empty string if no error has occurred (yet).
- * @private
*/
error_: {
type: String,
@@ -65,7 +56,6 @@
/**
* Countdown displayed to the user, number of seconds until launching. If
* 0 or less, doesn't get displayed at all.
- * @private
*/
secondCounter_: {
type: Number,
@@ -74,6 +64,10 @@
};
}
+ private url_: string;
+ private error_: string;
+ private secondCounter_: number;
+
/** @override */
connectedCallback() {
super.connectedCallback();
@@ -100,8 +94,7 @@
this.startCountdown_(Math.floor(milliseconds / 1000));
}
- /** @private */
- launchAndCloseTab_() {
+ private launchAndCloseTab_() {
// Mark this page with '?done=...' so that restoring the tab doesn't
// immediately re-trigger LBS.
history.pushState({}, '', '/?done=true');
@@ -111,11 +104,7 @@
});
}
- /**
- * @param {number} seconds
- * @private
- */
- startCountdown_(seconds) {
+ private startCountdown_(seconds: number) {
this.secondCounter_ = seconds;
const intervalId = setInterval(() => {
this.secondCounter_--;
@@ -125,11 +114,7 @@
}, 1 * MS_PER_SECOND);
}
- /**
- * @return {string}
- * @private
- */
- computeTitle_() {
+ private computeTitle_(): string {
if (this.error_) {
return this.i18n('errorTitle', getBrowserName());
}
@@ -139,11 +124,7 @@
return this.i18n('openingTitle', getBrowserName());
}
- /**
- * @return {string}
- * @private
- */
- computeDescription_() {
+ private computeDescription_(): string {
if (this.error_) {
return this.i18n(
this.error_, getUrlHostname(this.url_), getBrowserName());
@@ -155,23 +136,17 @@
customElements.define(BrowserSwitchAppElement.is, BrowserSwitchAppElement);
-/** @return {string} */
-function getBrowserName() {
+function getBrowserName(): string {
return loadTimeData.getString('browserName');
}
-/**
- * @param {string} url
- * @return {string}
- */
-function getUrlHostname(url) {
+function getUrlHostname(url: string): string {
const anchor = document.createElement('a');
anchor.href = url;
// Return entire url if parsing failed (which means the URL is bogus).
return anchor.hostname || url;
}
-/** @return {!BrowserSwitchProxy} */
-function getProxy() {
+function getProxy(): BrowserSwitchProxy {
return BrowserSwitchProxyImpl.getInstance();
}
diff --git a/chrome/browser/resources/browser_switch/browser_switch_proxy.js b/chrome/browser/resources/browser_switch/browser_switch_proxy.js
deleted file mode 100644
index c9c5ddb..0000000
--- a/chrome/browser/resources/browser_switch/browser_switch_proxy.js
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2019 The Chromium 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 {sendWithPromise} from 'chrome://resources/js/cr.m.js';
-
-/** @interface */
-export class BrowserSwitchProxy {
- /**
- * @param {string} url URL to open in alternative browser.
- * @return {Promise} A promise that can fail if unable to launch. It will
- * never resolve, because the tab closes if this succeeds.
- */
- launchAlternativeBrowserAndCloseTab(url) {}
-
- gotoNewTabPage() {}
-}
-
-/** @implements {BrowserSwitchProxy} */
-export class BrowserSwitchProxyImpl {
- /** @override */
- launchAlternativeBrowserAndCloseTab(url) {
- return sendWithPromise('launchAlternativeBrowserAndCloseTab', url);
- }
-
- /** @override */
- gotoNewTabPage() {
- chrome.send('gotoNewTabPage');
- }
-
- /** @return {!BrowserSwitchProxy} */
- static getInstance() {
- return instance || (instance = new BrowserSwitchProxyImpl());
- }
-}
-
-/** @type {?BrowserSwitchProxy} */
-let instance = null;
diff --git a/chrome/browser/resources/browser_switch/browser_switch_proxy.ts b/chrome/browser/resources/browser_switch/browser_switch_proxy.ts
new file mode 100644
index 0000000..ff612cb0
--- /dev/null
+++ b/chrome/browser/resources/browser_switch/browser_switch_proxy.ts
@@ -0,0 +1,33 @@
+// Copyright 2019 The Chromium 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 {sendWithPromise} from 'chrome://resources/js/cr.m.js';
+
+/** @interface */
+export interface BrowserSwitchProxy {
+ /**
+ * @param URL to open in alternative browser.
+ * @return A promise that can fail if unable to launch. It will never resolve,
+ * because the tab closes if this succeeds.
+ */
+ launchAlternativeBrowserAndCloseTab(url: string): Promise<void>;
+
+ gotoNewTabPage(): void;
+}
+
+export class BrowserSwitchProxyImpl implements BrowserSwitchProxy {
+ launchAlternativeBrowserAndCloseTab(url: string) {
+ return sendWithPromise('launchAlternativeBrowserAndCloseTab', url);
+ }
+
+ gotoNewTabPage() {
+ chrome.send('gotoNewTabPage');
+ }
+
+ static getInstance(): BrowserSwitchProxy {
+ return instance || (instance = new BrowserSwitchProxyImpl());
+ }
+}
+
+let instance: BrowserSwitchProxy|null = null;
diff --git a/chrome/browser/resources/browser_switch/internals/browser_switch_internals.js b/chrome/browser/resources/browser_switch/internals/browser_switch_internals.ts
similarity index 68%
rename from chrome/browser/resources/browser_switch/internals/browser_switch_internals.js
rename to chrome/browser/resources/browser_switch/internals/browser_switch_internals.ts
index b79d221..7c899e3 100644
--- a/chrome/browser/resources/browser_switch/internals/browser_switch_internals.js
+++ b/chrome/browser/resources/browser_switch/internals/browser_switch_internals.ts
@@ -5,46 +5,36 @@
import {sendWithPromise} from 'chrome://resources/js/cr.m.js';
import {$} from 'chrome://resources/js/util.m.js';
-/**
- * @typedef {{
- * sitelist: Array<string>,
- * greylist: Array<string>,
- * }}
- */
-let RuleSet;
+type RuleSet = {
+ sitelist: Array<string>;
+ greylist: Array<string>;
+};
-/**
- * @typedef {{
- * gpo: RuleSet,
- * ieem: (RuleSet|undefined),
- * external: (RuleSet|undefined),
- * }}
- */
-let RuleSetList;
+type RuleSetList = {
+ gpo: RuleSet;
+ ieem?: RuleSet;
+ external?: RuleSet;
+};
/**
* Returned by getRulesetSources().
- * @typedef {{
- * browser_switcher: Object<string, string>!,
- * }}
*/
-let RulesetSources;
+type RulesetSources = {
+ browser_switcher: {[k: string]: string};
+};
/**
* Returned by getTimestamps().
- * @typedef {{
- * last_fetch: number,
- * next_fetch: number,
- * }}
*/
-let TimestampPair;
+type TimestampPair = {
+ last_fetch: number;
+ next_fetch: number;
+};
/**
* Converts 'this_word' to 'ThisWord'
- * @param {string} symbol
- * @return {string}
*/
-function snakeCaseToUpperCamelCase(symbol) {
+function snakeCaseToUpperCamelCase(symbol: string): string {
if (!symbol) {
return symbol;
}
@@ -55,21 +45,19 @@
/**
* Clears the table, and inserts a header row.
- * @param {HTMLTableElement} table
- * @param {HTMLTemplateElement} headerTemplate
- * Template to use to re-create the header row.
+ * @param headerTemplate Template to use to re-create the header row.
*/
-function clearTable(table, headerTemplate) {
+function clearTable(
+ table: HTMLTableElement, headerTemplate: HTMLTemplateElement) {
table.innerHTML = '';
const headerRow = document.importNode(headerTemplate.content, true);
table.appendChild(headerRow);
}
/**
- * @param {string} rule
- * @return {string} String describing the rule type.
+ * @return String describing the rule type.
*/
-function getRuleType(rule) {
+function getRuleType(rule: string): string {
if (rule == '*') {
return 'wildcard';
}
@@ -81,33 +69,31 @@
/**
* Creates and returns a <tr> element for the given rule.
- * @param {string} rule
- * @param {string} rulesetName
- * @return {HTMLTableRowElement}
*/
-function createRowForRule(rule, rulesetName) {
- const row = document.importNode($('rule-row-template').content, true);
+function createRowForRule(
+ rule: string, rulesetName: string): HTMLTableRowElement {
+ const row = document.importNode(
+ ($('rule-row-template') as HTMLTemplateElement).content,
+ true) as unknown as HTMLTableRowElement;
const cells = row.querySelectorAll('td');
cells[0].innerText = rule;
cells[0].className = 'url';
cells[1].innerText = rulesetName;
cells[2].innerText = getRuleType(rule);
cells[3].innerText = /^!/.test(rule) ? 'yes' : 'no';
- return /** @type {HTMLTableRowElement} */ (row);
+ return row;
}
/**
* Updates the content of all tables after receiving data from the backend.
- * @param {RuleSetList} rulesets
*/
-function updateTables(rulesets) {
- const headerTemplate =
- /** @type {HTMLTemplateElement} */ ($('header-row-template'));
- clearTable(/** @type {HTMLTableElement} */ ($('sitelist')), headerTemplate);
- clearTable(/** @type {HTMLTableElement} */ ($('greylist')), headerTemplate);
+function updateTables(rulesets: RuleSetList) {
+ const headerTemplate = $('header-row-template') as HTMLTemplateElement;
+ clearTable($('sitelist') as HTMLTableElement, headerTemplate);
+ clearTable($('greylist') as HTMLTableElement, headerTemplate);
for (const [rulesetName, ruleset] of Object.entries(rulesets)) {
- for (const [listName, rules] of Object.entries(ruleset)) {
+ for (const [listName, rules] of Object.entries(ruleset as RuleSet)) {
const table = $(listName);
for (const rule of rules) {
table.appendChild(createRowForRule(rule, rulesetName));
@@ -117,7 +103,7 @@
}
function checkUrl() {
- const url = $('url-checker-input').value;
+ const url = ($('url-checker-input') as HTMLInputElement).value;
if (!url) {
$('output').innerText = '';
return;
@@ -127,7 +113,7 @@
// URL is valid.
$('output').innerText = JSON.stringify(decision, null, 2);
})
- .catch(err => {
+ .catch(() => {
// URL is invalid.
$('output').innerText =
'Invalid URL. Make sure it is formatted properly.';
@@ -138,10 +124,8 @@
/**
* Formats |date| as "HH:MM:SS".
- * @param {Date} date
- * @return {string}
*/
-function formatTime(date) {
+function formatTime(date: Date): string {
const hh = date.getHours().toString().padStart(2, '0');
const mm = date.getMinutes().toString().padStart(2, '0');
const ss = date.getSeconds().toString().padStart(2, '0');
@@ -150,9 +134,8 @@
/**
* Update the paragraphs under the "XML sitelists" section.
- * @param {TimestampPair?} timestamps
*/
-function updateTimestamps(timestamps) {
+function updateTimestamps(timestamps: TimestampPair|null) {
if (!timestamps) {
return;
}
@@ -178,20 +161,18 @@
/**
* Update the table under the "XML sitelists" section.
- * @param {RulesetSources} sources
*/
-function updateXmlTable({browser_switcher: sources}) {
- const headerTemplate =
- /** @type {HTMLTemplateElement} */ ($('xml-header-row-template'));
- clearTable(
- /** @type {HTMLTableElement} */ ($('xml-sitelists')), headerTemplate);
+function updateXmlTable({browser_switcher: sources}: RulesetSources) {
+ const headerTemplate = $('xml-header-row-template') as HTMLTemplateElement;
+ clearTable($('xml-sitelists') as HTMLTableElement, headerTemplate);
for (const [prefName, url] of Object.entries(sources)) {
// Hacky: guess the policy name from the pref name by converting 'foo_bar'
// to 'BrowserSwitcherFooBar'. This relies on prefs having the same name as
// the associated policy.
const policyName = 'BrowserSwitcher' + snakeCaseToUpperCamelCase(prefName);
- const row = document.importNode($('xml-row-template').content, true);
+ const row = document.importNode(
+ ($('xml-row-template') as HTMLTemplateElement).content, true);
const cells = row.querySelectorAll('td');
cells[0].innerText = policyName;
cells[1].innerText = url || '(not configured)';
diff --git a/chrome/browser/resources/browser_switch/tsconfig_base.json b/chrome/browser/resources/browser_switch/tsconfig_base.json
new file mode 100644
index 0000000..afa07315
--- /dev/null
+++ b/chrome/browser/resources/browser_switch/tsconfig_base.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../../../tools/typescript/tsconfig_base.json",
+ "compilerOptions": {
+ "noUncheckedIndexedAccess": false,
+ "noUnusedLocals": false,
+ "strictPropertyInitialization": false
+ }
+}