Commit f978b9cd authored by Anastasiia Nikolaienko's avatar Anastasiia Nikolaienko Committed by Commit Bot

Reland "Migrate authenticator.js and it's dependencies to JS modules"

This is a reland of e78513d7

Original change's description:
> Migrate authenticator.js and it's dependencies to JS modules
> 
> Bug: 1054394
> Change-Id: I106bd0edc547dc6eb2d680b409d1348d5b964079
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2066548
> Commit-Queue: Anastasiia Nikolaienko <anastasiian@chromium.org>
> Reviewed-by: Lei Zhang <thestig@chromium.org>
> Reviewed-by: dpapad <dpapad@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#745762}

Bug: 1054394
Change-Id: Ie1a63f522f5e29b2af327010236f962836a36074
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2083317Reviewed-by: default avatardpapad <dpapad@chromium.org>
Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Commit-Queue: Anastasiia Nikolaienko <anastasiian@chromium.org>
Cr-Commit-Position: refs/heads/master@{#746703}
parent 2cb6f64a
...@@ -1395,6 +1395,7 @@ group("extra_resources") { ...@@ -1395,6 +1395,7 @@ group("extra_resources") {
"//chrome/browser/resources:component_extension_resources", "//chrome/browser/resources:component_extension_resources",
"//chrome/browser/resources:dev_ui_paks", "//chrome/browser/resources:dev_ui_paks",
"//chrome/browser/resources:downloads_resources", "//chrome/browser/resources:downloads_resources",
"//chrome/browser/resources:gaia_auth_host_resources",
"//chrome/browser/resources:history_resources", "//chrome/browser/resources:history_resources",
"//chrome/browser/resources:local_ntp_resources", "//chrome/browser/resources:local_ntp_resources",
"//chrome/browser/resources:new_tab_page_resources", "//chrome/browser/resources:new_tab_page_resources",
......
...@@ -5564,6 +5564,7 @@ grit("resources") { ...@@ -5564,6 +5564,7 @@ grit("resources") {
if (is_win || is_mac || is_desktop_linux || is_chromeos) { if (is_win || is_mac || is_desktop_linux || is_chromeos) {
deps += [ deps += [
"//chrome/browser/resources/discards:discards_resources_gen", "//chrome/browser/resources/discards:discards_resources_gen",
"//chrome/browser/resources/gaia_auth_host:modulize",
"//chrome/browser/resources/management:polymer3_elements", "//chrome/browser/resources/management:polymer3_elements",
"//chrome/browser/resources/signin:polymer3_elements", "//chrome/browser/resources/signin:polymer3_elements",
"//chrome/browser/ui/webui/discards:mojo_bindings_js", "//chrome/browser/ui/webui/discards:mojo_bindings_js",
......
...@@ -26,6 +26,7 @@ if (closure_compile) { ...@@ -26,6 +26,7 @@ if (closure_compile) {
"discards:closure_compile", "discards:closure_compile",
"download_internals:closure_compile", "download_internals:closure_compile",
"downloads:closure_compile", "downloads:closure_compile",
"gaia_auth_host:closure_compile",
"history:closure_compile", "history:closure_compile",
"local_ntp:closure_compile", "local_ntp:closure_compile",
"local_state:closure_compile", "local_state:closure_compile",
...@@ -138,6 +139,27 @@ if (!is_android) { ...@@ -138,6 +139,27 @@ if (!is_android) {
output_dir = "$root_gen_dir/chrome" output_dir = "$root_gen_dir/chrome"
} }
grit("gaia_auth_host_resources") {
# The .grd contains references to generated files.
source_is_generated = true
grit_flags = [
"-E",
"root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir),
]
source = "gaia_auth_host/gaia_auth_host_resources.grd"
deps = [ "//chrome/browser/resources/gaia_auth_host:modulize" ]
defines = chrome_grit_defines
outputs = [
"grit/gaia_auth_host_resources.h",
"grit/gaia_auth_host_resources_map.cc",
"grit/gaia_auth_host_resources_map.h",
"gaia_auth_host_resources.pak",
]
output_dir = "$root_gen_dir/chrome"
}
grit("history_resources") { grit("history_resources") {
# The .grd contains references to generated files. # The .grd contains references to generated files.
source_is_generated = true source_is_generated = true
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
# found in the LICENSE file. # found in the LICENSE file.
import("//chrome/test/base/js2gtest.gni") import("//chrome/test/base/js2gtest.gni")
import("//third_party/closure_compiler/compile_js.gni")
import("//ui/webui/resources/tools/js_modulizer.gni")
js2gtest("login_unitjs_tests") { js2gtest("login_unitjs_tests") {
# These could be unit tests, except they need a browser context in order # These could be unit tests, except they need a browser context in order
...@@ -24,3 +26,95 @@ source_set("browser_tests") { ...@@ -24,3 +26,95 @@ source_set("browser_tests") {
testonly = true testonly = true
deps = [ ":login_unitjs_tests" ] deps = [ ":login_unitjs_tests" ]
} }
js_type_check("closure_compile") {
uses_js_modules = true
deps = [
":authenticator.m",
":channel.m",
":post_message_channel.m",
":saml_handler.m",
":saml_password_attributes.m",
":saml_timestamps.m",
":webview_event_manager.m",
]
}
js_library("channel.m") {
sources =
[ "$root_gen_dir/chrome/browser/resources/gaia_auth_host/channel.m.js" ]
extra_deps = [ ":modulize" ]
}
js_library("webview_event_manager.m") {
sources = [ "$root_gen_dir/chrome/browser/resources/gaia_auth_host/webview_event_manager.m.js" ]
extra_deps = [ ":modulize" ]
}
js_library("post_message_channel.m") {
sources = [ "$root_gen_dir/chrome/browser/resources/gaia_auth_host/post_message_channel.m.js" ]
deps = [ ":channel.m" ]
extra_deps = [ ":modulize" ]
}
js_library("saml_password_attributes.m") {
sources = [ "$root_gen_dir/chrome/browser/resources/gaia_auth_host/saml_password_attributes.m.js" ]
deps = [ ":saml_timestamps.m" ]
extra_deps = [ ":modulize" ]
}
js_library("saml_timestamps.m") {
sources = [ "$root_gen_dir/chrome/browser/resources/gaia_auth_host/saml_timestamps.m.js" ]
extra_deps = [ ":modulize" ]
}
js_library("saml_handler.m") {
sources = [
"$root_gen_dir/chrome/browser/resources/gaia_auth_host/saml_handler.m.js",
]
deps = [
":channel.m",
":post_message_channel.m",
":webview_event_manager.m",
]
extra_deps = [ ":modulize" ]
}
js_library("authenticator.m") {
sources = [
"$root_gen_dir/chrome/browser/resources/gaia_auth_host/authenticator.m.js",
]
deps = [
":saml_handler.m",
"//ui/webui/resources/js:assert.m",
"//ui/webui/resources/js:cr.m",
"//ui/webui/resources/js:util.m",
"//ui/webui/resources/js/cr:event_target.m",
]
externs_list = [
"$externs_path/chrome_extensions.js",
"$externs_path/webview_tag.js",
]
extra_deps = [ ":modulize" ]
}
js_modulizer("modulize") {
input_files = [
"authenticator.js",
"channel.js",
"post_message_channel.js",
"saml_handler.js",
"saml_password_attributes.js",
"saml_timestamps.js",
"webview_event_manager.js",
]
namespace_rewrites = [
"DOMWindow|Object",
"cr.EventTarget|EventTarget",
"cr.login.SamlHandler|SamlHandler",
"samlPasswordAttributes.PasswordAttributes|PasswordAttributes",
"samlPasswordAttributes.readPasswordAttributes|readPasswordAttributes",
"samlTimestamps.decodeTimestamp|decodeTimestamp",
"XMLDocument|Object",
]
}
...@@ -5,6 +5,16 @@ ...@@ -5,6 +5,16 @@
// <include src="saml_handler.js"> // <include src="saml_handler.js">
// Note: webview_event_manager.js is already included by saml_handler.js. // Note: webview_event_manager.js is already included by saml_handler.js.
// clang-format off
// #import {NativeEventTarget as EventTarget} from 'chrome://resources/js/cr/event_target.m.js'
// #import {assert} from 'chrome://resources/js/assert.m.js';
// #import {$, appendParam} from 'chrome://resources/js/util.m.js';
// #import {sendWithPromise} from 'chrome://resources/js/cr.m.js';
// #import {SamlHandler, OnHeadersReceivedDetails} from './saml_handler.m.js';
// #import {WebviewEventManager} from './webview_event_manager.m.js';
// clang-format on
/** /**
* @fileoverview An UI component to authenticate to Chrome. The component hosts * @fileoverview An UI component to authenticate to Chrome. The component hosts
* IdP web pages in a webview. A client who is interested in monitoring * IdP web pages in a webview. A client who is interested in monitoring
...@@ -15,7 +25,33 @@ ...@@ -15,7 +25,33 @@
*/ */
cr.define('cr.login', function() { cr.define('cr.login', function() {
'use strict'; /* #ignore */ 'use strict';
/**
* Parameters for the authorization flow.
* @typedef {{
* hl: string,
* gaiaUrl: string,
* authMode: Number,
* isLoginPrimaryAccount: boolean,
* email: string,
* constrained: string,
* readOnlyEmail: boolean,
* service: string,
* dontResizeNonEmbeddedPages: boolean,
* clientId: string,
* gaiaPath: string,
* emailDomain: string,
* showTos: string,
* extractSamlPasswordAttributes: boolean,
* flow: string,
* ignoreCrOSIdpSetting: boolean,
* enableGaiaActionButtons: boolean,
* enterpriseEnrollmentDomain: string,
* samlAclUrl: string
* }}
*/
/* #export */ let AuthParams;
// TODO(rogerta): should use gaia URL from GaiaUrls::gaia_url() instead // TODO(rogerta): should use gaia URL from GaiaUrls::gaia_url() instead
// of hardcoding the prod URL here. As is, this does not work with staging // of hardcoding the prod URL here. As is, this does not work with staging
...@@ -32,20 +68,20 @@ cr.define('cr.login', function() { ...@@ -32,20 +68,20 @@ cr.define('cr.login', function() {
/** /**
* The source URL parameter for the constrained signin flow. * The source URL parameter for the constrained signin flow.
*/ */
const CONSTRAINED_FLOW_SOURCE = 'chrome'; /* #export */ const CONSTRAINED_FLOW_SOURCE = 'chrome';
/** /**
* Enum for the authorization mode, must match AuthMode defined in * Enum for the authorization mode, must match AuthMode defined in
* chrome/browser/ui/webui/inline_login_ui.cc. * chrome/browser/ui/webui/inline_login_ui.cc.
* @enum {number} * @enum {number}
*/ */
const AuthMode = {DEFAULT: 0, OFFLINE: 1, DESKTOP: 2}; /* #export */ const AuthMode = {DEFAULT: 0, OFFLINE: 1, DESKTOP: 2};
/** /**
* Enum for the authorization type. * Enum for the authorization type.
* @enum {number} * @enum {number}
*/ */
const AuthFlow = {DEFAULT: 0, SAML: 1}; /* #export */ const AuthFlow = {DEFAULT: 0, SAML: 1};
/** /**
* Supported Authenticator params. * Supported Authenticator params.
...@@ -215,7 +251,7 @@ cr.define('cr.login', function() { ...@@ -215,7 +251,7 @@ cr.define('cr.login', function() {
/** /**
* Initializes the authenticator component. * Initializes the authenticator component.
*/ */
class Authenticator extends cr.EventTarget { /* #export */ class Authenticator extends cr.EventTarget {
/** /**
* @param {!WebView|string} webview The webview element or its ID to host * @param {!WebView|string} webview The webview element or its ID to host
* IdP web pages. * IdP web pages.
...@@ -230,6 +266,10 @@ cr.define('cr.login', function() { ...@@ -230,6 +266,10 @@ cr.define('cr.login', function() {
this.chooseWhatToSync_ = false; this.chooseWhatToSync_ = false;
this.skipForNow_ = false; this.skipForNow_ = false;
this.authFlow = AuthFlow.DEFAULT; this.authFlow = AuthFlow.DEFAULT;
/** @type {AuthMode} */
this.authMode = AuthMode.DEFAULT;
this.dontResizeNonEmbeddedPages = false;
this.authDomain = ''; this.authDomain = '';
/** /**
* @type {!cr.login.SamlHandler|undefined} * @type {!cr.login.SamlHandler|undefined}
...@@ -243,7 +283,12 @@ cr.define('cr.login', function() { ...@@ -243,7 +283,12 @@ cr.define('cr.login', function() {
this.trusted_ = true; this.trusted_ = true;
this.readyFired_ = false; this.readyFired_ = false;
this.authCompletedFired_ = false; this.authCompletedFired_ = false;
this.webview_ = typeof webview == 'string' ? $(webview) : webview; /**
* @private {WebView|undefined}
*/
this.webview_ = typeof webview == 'string' ?
/** @type {WebView} */ ($(webview)) :
webview;
assert(this.webview_); assert(this.webview_);
this.enableGaiaActionButtons_ = false; this.enableGaiaActionButtons_ = false;
this.webviewEventManager_ = WebviewEventManager.create(); this.webviewEventManager_ = WebviewEventManager.create();
...@@ -260,7 +305,7 @@ cr.define('cr.login', function() { ...@@ -260,7 +305,7 @@ cr.define('cr.login', function() {
* Callback allowing to request whether the specified user which * Callback allowing to request whether the specified user which
* authenticates via SAML is a user without a password (neither a manually * authenticates via SAML is a user without a password (neither a manually
* entered one nor one provided via Credentials Passing API). * entered one nor one provided via Credentials Passing API).
* @type {function(string, string, function(boolean))} Arguments are the * @type {?function(string, string, function(boolean))} Arguments are the
* e-mail, the GAIA ID, and the response callback. * e-mail, the GAIA ID, and the response callback.
*/ */
this.getIsSamlUserPasswordlessCallback = null; this.getIsSamlUserPasswordlessCallback = null;
...@@ -273,6 +318,8 @@ cr.define('cr.login', function() { ...@@ -273,6 +318,8 @@ cr.define('cr.login', function() {
* @private * @private
*/ */
this.isSamlUserPasswordless_ = null; this.isSamlUserPasswordless_ = null;
/** @private {boolean} */
this.isConstrainedWindow_ = false;
this.samlAclUrl_ = null; this.samlAclUrl_ = null;
window.addEventListener( window.addEventListener(
...@@ -398,7 +445,8 @@ cr.define('cr.login', function() { ...@@ -398,7 +445,8 @@ cr.define('cr.login', function() {
/** /**
* Re-binds to another webview. * Re-binds to another webview.
* @param {Object} webview the new webview to be used by this Authenticator. * @param {WebView} webview the new webview to be used by this
* Authenticator.
* @private * @private
*/ */
rebindWebview_(webview) { rebindWebview_(webview) {
...@@ -455,14 +503,14 @@ cr.define('cr.login', function() { ...@@ -455,14 +503,14 @@ cr.define('cr.login', function() {
webivewParent.replaceChild(newWebview, this.webview_); webivewParent.replaceChild(newWebview, this.webview_);
this.rebindWebview_(newWebview); this.rebindWebview_(/** @type {WebView} */ (newWebview));
} }
} }
/** /**
* Loads the authenticator component with the given parameters. * Loads the authenticator component with the given parameters.
* @param {AuthMode} authMode Authorization mode. * @param {AuthMode} authMode Authorization mode.
* @param {Object} data Parameters for the authorization flow. * @param {AuthParams} data Parameters for the authorization flow.
*/ */
load(authMode, data) { load(authMode, data) {
this.authMode = authMode; this.authMode = authMode;
...@@ -576,7 +624,7 @@ cr.define('cr.login', function() { ...@@ -576,7 +624,7 @@ cr.define('cr.login', function() {
} }
if (data.isFirstUser) { if (data.isFirstUser) {
url = appendParam(url, 'is_first_user', true); url = appendParam(url, 'is_first_user', 'true');
if (data.lsbReleaseBoard) { if (data.lsbReleaseBoard) {
url = appendParam(url, 'chromeos_board', data.lsbReleaseBoard); url = appendParam(url, 'chromeos_board', data.lsbReleaseBoard);
...@@ -615,7 +663,7 @@ cr.define('cr.login', function() { ...@@ -615,7 +663,7 @@ cr.define('cr.login', function() {
url = appendParam(url, 'ignoreCrOSIdpSetting', 'true'); url = appendParam(url, 'ignoreCrOSIdpSetting', 'true');
} }
if (data.enableGaiaActionButtons) { if (data.enableGaiaActionButtons) {
url = appendParam(url, 'use_native_navigation', 1); url = appendParam(url, 'use_native_navigation', '1');
} }
return url; return url;
} }
...@@ -684,10 +732,9 @@ cr.define('cr.login', function() { ...@@ -684,10 +732,9 @@ cr.define('cr.login', function() {
/** /**
* Invoked when the sign-in page takes focus. * Invoked when the sign-in page takes focus.
* @param {object} e The focus event being triggered.
* @private * @private
*/ */
onFocus_(e) { onFocus_() {
if (this.authMode == AuthMode.DESKTOP && if (this.authMode == AuthMode.DESKTOP &&
document.activeElement == document.body) { document.activeElement == document.body) {
this.webview_.focus(); this.webview_.focus();
...@@ -696,7 +743,7 @@ cr.define('cr.login', function() { ...@@ -696,7 +743,7 @@ cr.define('cr.login', function() {
/** /**
* Invoked when the history state is changed. * Invoked when the history state is changed.
* @param {object} e The popstate event being triggered. * @param {!Event} e The popstate event being triggered.
* @private * @private
*/ */
onPopState_(e) { onPopState_(e) {
...@@ -710,7 +757,7 @@ cr.define('cr.login', function() { ...@@ -710,7 +757,7 @@ cr.define('cr.login', function() {
* Invoked when headers are received in the main frame of the webview. It * Invoked when headers are received in the main frame of the webview. It
* 1) reads the authenticated user info from a signin header, * 1) reads the authenticated user info from a signin header,
* 2) signals the start of a saml flow upon receiving a saml header. * 2) signals the start of a saml flow upon receiving a saml header.
* @return {!Object} Modified request headers. * @param {OnHeadersReceivedDetails} details
* @private * @private
*/ */
onHeadersReceived_(details) { onHeadersReceived_(details) {
...@@ -745,6 +792,7 @@ cr.define('cr.login', function() { ...@@ -745,6 +792,7 @@ cr.define('cr.login', function() {
} else if (headerName == LOCATION_HEADER) { } else if (headerName == LOCATION_HEADER) {
// If the "choose what to sync" checkbox was clicked, then the // If the "choose what to sync" checkbox was clicked, then the
// continue URL will contain a source=3 field. // continue URL will contain a source=3 field.
assert(header.value);
const location = decodeURIComponent(header.value); const location = decodeURIComponent(header.value);
this.chooseWhatToSync_ = !!location.match(/(\?|&)source=3($|&)/); this.chooseWhatToSync_ = !!location.match(/(\?|&)source=3($|&)/);
} }
...@@ -753,7 +801,7 @@ cr.define('cr.login', function() { ...@@ -753,7 +801,7 @@ cr.define('cr.login', function() {
/** /**
* Returns true if given HTML5 message is received from the webview element. * Returns true if given HTML5 message is received from the webview element.
* @param {object} e Payload of the received HTML5 message. * @param {Object} e Payload of the received HTML5 message.
*/ */
isGaiaMessage(e) { isGaiaMessage(e) {
if (!this.isWebviewEvent_(e)) { if (!this.isWebviewEvent_(e)) {
...@@ -775,7 +823,7 @@ cr.define('cr.login', function() { ...@@ -775,7 +823,7 @@ cr.define('cr.login', function() {
/** /**
* Invoked when an HTML5 message is received from the webview element. * Invoked when an HTML5 message is received from the webview element.
* @param {object} e Payload of the received HTML5 message. * @param {Object} e Payload of the received HTML5 message.
* @private * @private
*/ */
onMessageFromWebview_(e) { onMessageFromWebview_(e) {
...@@ -796,7 +844,7 @@ cr.define('cr.login', function() { ...@@ -796,7 +844,7 @@ cr.define('cr.login', function() {
/** /**
* Invoked to send a HTML5 message to the webview element. * Invoked to send a HTML5 message to the webview element.
* @param {object} e Payload of the HTML5 message. * @param {Object} payload Payload of the HTML5 message.
*/ */
sendMessageToWebview(payload) { sendMessageToWebview(payload) {
const currentUrl = this.webview_.src; const currentUrl = this.webview_.src;
...@@ -1196,6 +1244,7 @@ cr.define('cr.login', function() { ...@@ -1196,6 +1244,7 @@ cr.define('cr.login', function() {
} }
} }
// #cr_define_end
/** /**
* The current auth flow of the hosted auth page. * The current auth flow of the hosted auth page.
* @type {AuthFlow} * @type {AuthFlow}
......
...@@ -4,8 +4,9 @@ ...@@ -4,8 +4,9 @@
/** /**
* Channel to the background script. * Channel to the background script.
* @constructor
*/ */
function Channel() { /* #export */ function Channel() {
this.messageCallbacks_ = {}; this.messageCallbacks_ = {};
this.internalRequestCallbacks_ = {}; this.internalRequestCallbacks_ = {};
} }
......
<?xml version="1.0" encoding="UTF-8"?>
<grit latest_public_release="0" current_release="1" output_all_resource_defines="false">
<outputs>
<output filename="grit/gaia_auth_host_resources.h" type="rc_header">
<emit emit_type='prepend'></emit>
</output>
<output filename="grit/gaia_auth_host_resources_map.cc"
type="resource_file_map_source" />
<output filename="grit/gaia_auth_host_resources_map.h"
type="resource_map_header" />
<output filename="gaia_auth_host_resources.pak" type="data_package" />
</outputs>
<release seq="1">
<includes>
<include name="IDR_GAIA_AUTH_AUTHENTICATOR_M_JS"
file="${root_gen_dir}/chrome/browser/resources/gaia_auth_host/authenticator.m.js"
use_base_dir="false"
type ="BINDATA"
compress="gzip" />
<include name="IDR_GAIA_AUTH_SAML_HANDLER_M_JS"
file="${root_gen_dir}/chrome/browser/resources/gaia_auth_host/saml_handler.m.js"
use_base_dir="false"
type ="BINDATA"
compress="gzip" />
<include name="IDR_GAIA_AUTH_CHANNEL_M_JS"
file="${root_gen_dir}/chrome/browser/resources/gaia_auth_host/channel.m.js"
use_base_dir="false"
type ="BINDATA"
compress="gzip" />
<include name="IDR_GAIA_AUTH_POST_MESSAGE_CHANNEL_M_JS"
file="${root_gen_dir}/chrome/browser/resources/gaia_auth_host/post_message_channel.m.js"
use_base_dir="false"
type ="BINDATA"
compress="gzip" />
<include name="IDR_GAIA_AUTH_WEBVIEW_EVENT_MANAGER_M_JS"
file="${root_gen_dir}/chrome/browser/resources/gaia_auth_host/webview_event_manager.m.js"
use_base_dir="false"
type ="BINDATA"
compress="gzip" />
<include name="IDR_GAIA_AUTH_SAML_PASSWORD_ATTRIBUTES_M_JS"
file="${root_gen_dir}/chrome/browser/resources/gaia_auth_host/saml_password_attributes.m.js"
use_base_dir="false"
type ="BINDATA"
compress="gzip" />
<include name="IDR_GAIA_AUTH_SAML_TIMESTAMPS_M_JS"
file="${root_gen_dir}/chrome/browser/resources/gaia_auth_host/saml_timestamps.m.js"
use_base_dir="false"
type ="BINDATA"
compress="gzip" />
</includes>
</release>
</grit>
...@@ -11,7 +11,11 @@ ...@@ -11,7 +11,11 @@
// <include src="channel.js"> // <include src="channel.js">
const PostMessageChannel = (function() { // clang-format off
// #import {Channel} from './channel.m.js';
// clang-format on
/* #export */ const PostMessageChannel = (function() {
/** /**
* Allowed origins of the hosting page. * Allowed origins of the hosting page.
* @type {Array<string>} * @type {Array<string>}
...@@ -37,6 +41,7 @@ const PostMessageChannel = (function() { ...@@ -37,6 +41,7 @@ const PostMessageChannel = (function() {
/** /**
* A simple event target. * A simple event target.
* @constructor
*/ */
function EventTarget() { function EventTarget() {
this.listeners_ = []; this.listeners_ = [];
...@@ -136,7 +141,8 @@ const PostMessageChannel = (function() { ...@@ -136,7 +141,8 @@ const PostMessageChannel = (function() {
createPort(channelId, channelName, opt_targetWindow, opt_targetOrigin) { createPort(channelId, channelName, opt_targetWindow, opt_targetOrigin) {
const port = new PostMessagePort(channelId, channelName); const port = new PostMessagePort(channelId, channelName);
if (opt_targetWindow) { if (opt_targetWindow) {
port.setTarget(opt_targetWindow, opt_targetOrigin); port.setTarget(
opt_targetWindow, /** @type {string} */ (opt_targetOrigin));
} }
this.channels_[channelId] = port; this.channels_[channelId] = port;
return port; return port;
...@@ -169,13 +175,13 @@ const PostMessageChannel = (function() { ...@@ -169,13 +175,13 @@ const PostMessageChannel = (function() {
/** /**
* Creates a connecting port to the daemon and request connection. * Creates a connecting port to the daemon and request connection.
* @param {string} name * @param {string} name
* @return {PostMessagePort} * @return {?PostMessagePort}
*/ */
connectToDaemon(name) { connectToDaemon(name) {
if (this.isDaemon) { if (this.isDaemon) {
console.error( console.error(
'Error: Connecting from the daemon page is not supported.'); 'Error: Connecting from the daemon page is not supported.');
return; return null;
} }
const port = this.createPort(this.createChannelId_(), name); const port = this.createPort(this.createChannelId_(), name);
...@@ -270,6 +276,7 @@ const PostMessageChannel = (function() { ...@@ -270,6 +276,7 @@ const PostMessageChannel = (function() {
/** /**
* A HTML5 postMessage based port that provides the same port interface * A HTML5 postMessage based port that provides the same port interface
* as the messaging API port. * as the messaging API port.
* @constructor
* @param {number} channelId * @param {number} channelId
* @param {string} name * @param {string} name
*/ */
...@@ -362,7 +369,6 @@ const PostMessageChannel = (function() { ...@@ -362,7 +369,6 @@ const PostMessageChannel = (function() {
return PostMessageChannel; return PostMessageChannel;
})(); })();
/** @override */
Channel.create = function() { Channel.create = function() {
return new PostMessageChannel(); return new PostMessageChannel();
}; };
...@@ -6,12 +6,20 @@ ...@@ -6,12 +6,20 @@
// <include src="webview_event_manager.js"> // <include src="webview_event_manager.js">
// <include src="saml_password_attributes.js"> // <include src="saml_password_attributes.js">
// clang-format off
// #import {Channel} from './channel.m.js';
// #import {PostMessageChannel} from './post_message_channel.m.js';
// #import {WebviewEventManager} from './webview_event_manager.m.js';
// #import {NativeEventTarget as EventTarget} from 'chrome://resources/js/cr/event_target.m.js'
// #import {PasswordAttributes, readPasswordAttributes} from './saml_password_attributes.m.js';
// clang-format on
/** /**
* @fileoverview Saml support for webview based auth. * @fileoverview Saml support for webview based auth.
*/ */
cr.define('cr.login', function() { cr.define('cr.login', function() {
'use strict'; /* #ignore */ 'use strict';
/** /**
* The lowest version of the credentials passing API supported. * The lowest version of the credentials passing API supported.
...@@ -53,6 +61,47 @@ cr.define('cr.login', function() { ...@@ -53,6 +61,47 @@ cr.define('cr.login', function() {
// <include src="webview_saml_injected.js"> // <include src="webview_saml_injected.js">
`; `;
/**
* @typedef {{
* method: string,
* requestedVersion: number,
* keyType: string,
* token: string,
* passwordBytes: string
* }}
*/
let ApiCallMessageCall;
/**
* @typedef {{
* name: string,
* call: ApiCallMessageCall
* }}
*/
let ApiCallMessage;
/**
* Details about the request.
* @typedef {{
* method: string,
* requestBody: Object,
* url: string
* }}
* @see https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/onBeforeRequest#details
*/
/* #export */ let OnBeforeRequestDetails;
/**
* Details of the request.
* @typedef {{
* responseHeaders: Array<HttpHeader>,
* statusCode: number,
* url: string,
* }}
* @see https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/onHeadersReceived#details
*/
/* #export */ let OnHeadersReceivedDetails;
/** /**
* Creates a new URL by striping all query parameters. * Creates a new URL by striping all query parameters.
* @param {string} url The original URL. * @param {string} url The original URL.
...@@ -77,9 +126,9 @@ cr.define('cr.login', function() { ...@@ -77,9 +126,9 @@ cr.define('cr.login', function() {
* A handler to provide saml support for the given webview that hosts the * A handler to provide saml support for the given webview that hosts the
* auth IdP pages. * auth IdP pages.
*/ */
class SamlHandler extends cr.EventTarget { /* #export */ class SamlHandler extends cr.EventTarget {
/** /**
* @param {webview} webview * @param {!WebView} webview
* @param {boolean} startsOnSamlPage - whether initial URL is already SAML * @param {boolean} startsOnSamlPage - whether initial URL is already SAML
* page * page
* */ * */
...@@ -108,25 +157,26 @@ cr.define('cr.login', function() { ...@@ -108,25 +157,26 @@ cr.define('cr.login', function() {
/** /**
* The webview that serves IdP pages. * The webview that serves IdP pages.
* @type {webview} * @private {!WebView}
*/ */
this.webview_ = webview; this.webview_ = webview;
/** /**
* Whether a Saml page is in the webview from the start. * Whether a Saml page is in the webview from the start.
* @private {boolean}
*/ */
this.startsOnSamlPage_ = startsOnSamlPage; this.startsOnSamlPage_ = startsOnSamlPage;
/** /**
* Whether a Saml IdP page is display in the webview. * Whether a Saml IdP page is display in the webview.
* @type {boolean} * @private {boolean}
*/ */
this.isSamlPage_ = this.startsOnSamlPage_; this.isSamlPage_ = this.startsOnSamlPage_;
/** /**
* Pending Saml IdP page flag that is set when a SAML_HEADER is received * Pending Saml IdP page flag that is set when a SAML_HEADER is received
* and is copied to |isSamlPage_| in loadcommit. * and is copied to |isSamlPage_| in loadcommit.
* @type {boolean} * @private {boolean}
*/ */
this.pendingIsSamlPage_ = this.startsOnSamlPage_; this.pendingIsSamlPage_ = this.startsOnSamlPage_;
...@@ -134,7 +184,7 @@ cr.define('cr.login', function() { ...@@ -134,7 +184,7 @@ cr.define('cr.login', function() {
* The last aborted top level url. It is recorded in loadabort event and * The last aborted top level url. It is recorded in loadabort event and
* used to skip injection into Chrome's error page in the following * used to skip injection into Chrome's error page in the following
* loadcommit event. * loadcommit event.
* @type {string} * @private {?string}
*/ */
this.abortedTopLevelUrl_ = null; this.abortedTopLevelUrl_ = null;
...@@ -146,38 +196,38 @@ cr.define('cr.login', function() { ...@@ -146,38 +196,38 @@ cr.define('cr.login', function() {
/** /**
* Scraped password stored in an id to password field value map. * Scraped password stored in an id to password field value map.
* @type {Object<string, string>} * @private {!Object<string, string>}
* @private
*/ */
this.passwordStore_ = {}; this.passwordStore_ = {};
/** /**
* Whether Saml API is initialized. * Whether Saml API is initialized.
* @type {boolean} * @private {boolean}
*/ */
this.apiInitialized_ = false; this.apiInitialized_ = false;
/** /**
* Saml API version to use. * Saml API version to use.
* @type {number} * @private {number}
*/ */
this.apiVersion_ = 0; this.apiVersion_ = 0;
/** /**
* Saml API tokens received. * Saml API tokens received.
* @type {Object} * @private {!Object}
*/ */
this.apiTokenStore_ = {}; this.apiTokenStore_ = {};
/** /**
* Saml API confirmation token. Set by last 'confirm' call. * Saml API confirmation token. Set by last 'confirm' call.
* @private {?string}
*/ */
this.confirmToken_ = null; this.confirmToken_ = null;
/** /**
* Saml API password bytes set by last 'add' call. Needed to not break * Saml API password bytes set by last 'add' call. Needed to not break
* existing behavior. * existing behavior.
* @type {string} * @private {?string}
*/ */
this.lastApiPasswordBytes_ = null; this.lastApiPasswordBytes_ = null;
...@@ -197,29 +247,26 @@ cr.define('cr.login', function() { ...@@ -197,29 +247,26 @@ cr.define('cr.login', function() {
/** /**
* Current stage of device attestation flow. * Current stage of device attestation flow.
* @type {DeviceAttestationStage} * @private {!SamlHandler.DeviceAttestationStage}
* @private
*/ */
this.deviceAttestationStage_ = SamlHandler.DeviceAttestationStage.NONE; this.deviceAttestationStage_ = SamlHandler.DeviceAttestationStage.NONE;
/** /**
* Challenge from IdP to perform device attestation. * Challenge from IdP to perform device attestation.
* @type {?string} * @private {?string}
* @private
*/ */
this.verifiedAccessChallenge_ = null; this.verifiedAccessChallenge_ = null;
/** /**
* Response for a device attestation challenge. * Response for a device attestation challenge.
* @type {?string} * @private {?string}
* @private
*/ */
this.verifiedAccessChallengeResponse_ = null; this.verifiedAccessChallengeResponse_ = null;
/** /**
* The password-attributes that were extracted from the SAMLResponse, if * The password-attributes that were extracted from the SAMLResponse, if
* any. (Doesn't contain the password itself). * any. (Doesn't contain the password itself).
* @type {PasswordAttributes} * @private {!PasswordAttributes}
*/ */
this.passwordAttributes_ = this.passwordAttributes_ =
samlPasswordAttributes.PasswordAttributes.EMPTY; samlPasswordAttributes.PasswordAttributes.EMPTY;
...@@ -289,7 +336,7 @@ cr.define('cr.login', function() { ...@@ -289,7 +336,7 @@ cr.define('cr.login', function() {
/** /**
* Returns the Saml API password bytes. * Returns the Saml API password bytes.
* @return {string} * @return {?string}
*/ */
get apiPasswordBytes() { get apiPasswordBytes() {
if (this.confirmToken_ != null && if (this.confirmToken_ != null &&
...@@ -350,7 +397,7 @@ cr.define('cr.login', function() { ...@@ -350,7 +397,7 @@ cr.define('cr.login', function() {
/** /**
* Gets the password attributes extracted from SAML Response. * Gets the password attributes extracted from SAML Response.
* @return {PasswordAttributes} * @return {Object}
*/ */
get passwordAttributes() { get passwordAttributes() {
return this.passwordAttributes_; return this.passwordAttributes_;
...@@ -483,18 +530,24 @@ cr.define('cr.login', function() { ...@@ -483,18 +530,24 @@ cr.define('cr.login', function() {
* Handler for webRequest.onBeforeRequest that looks for the Base64 * Handler for webRequest.onBeforeRequest that looks for the Base64
* encoded SAMLResponse in the POST-ed formdata sent from the SAML page. * encoded SAMLResponse in the POST-ed formdata sent from the SAML page.
* Non-blocking. * Non-blocking.
* @param {Object} details The web-request details. * @param {OnBeforeRequestDetails} details The web-request details.
*/ */
onMainFrameWebRequest(details) { onMainFrameWebRequest(details) {
if (!this.extractSamlPasswordAttributes) return; if (!this.extractSamlPasswordAttributes) {
if (!this.isSamlPage_ || details.method != 'POST') return; return;
}
if (!this.isSamlPage_ || details.method != 'POST') {
return;
}
const formData = details.requestBody.formData; const formData = details.requestBody.formData;
let samlResponse = (formData && formData.SAMLResponse); let samlResponse = (formData && formData.SAMLResponse);
if (!samlResponse) { if (!samlResponse) {
samlResponse = new URL(details.url).searchParams.get('SAMLResponse'); samlResponse = new URL(details.url).searchParams.get('SAMLResponse');
} }
if (!samlResponse) return; if (!samlResponse) {
return;
}
try { try {
// atob means asciiToBinary, which actually means base64Decode: // atob means asciiToBinary, which actually means base64Decode:
...@@ -705,7 +758,7 @@ cr.define('cr.login', function() { ...@@ -705,7 +758,7 @@ cr.define('cr.login', function() {
/** /**
* Handlers for channel messages. * Handlers for channel messages.
* @param {Channel} channel A channel to send back response. * @param {Channel} channel A channel to send back response.
* @param {Object} msg Received message. * @param {ApiCallMessage} msg Received message.
* @private * @private
*/ */
onAPICall_(channel, msg) { onAPICall_(channel, msg) {
...@@ -736,7 +789,7 @@ cr.define('cr.login', function() { ...@@ -736,7 +789,7 @@ cr.define('cr.login', function() {
this.dispatchEvent(new CustomEvent('apiPasswordAdded')); this.dispatchEvent(new CustomEvent('apiPasswordAdded'));
} else if (call.method == 'confirm') { } else if (call.method == 'confirm') {
if (!call.token in this.apiTokenStore_) { if (!(call.token in this.apiTokenStore_)) {
console.error('SamlHandler.onAPICall_: token mismatch'); console.error('SamlHandler.onAPICall_: token mismatch');
} else { } else {
this.confirmToken_ = call.token; this.confirmToken_ = call.token;
...@@ -777,5 +830,6 @@ cr.define('cr.login', function() { ...@@ -777,5 +830,6 @@ cr.define('cr.login', function() {
} }
} }
// #cr_define_end
return {SamlHandler: SamlHandler}; return {SamlHandler: SamlHandler};
}); });
...@@ -4,6 +4,10 @@ ...@@ -4,6 +4,10 @@
// <include src="saml_timestamps.js"> // <include src="saml_timestamps.js">
// clang-format off;
// #import {decodeTimestamp} from './saml_timestamps.m.js';
// clang-format on
/** /**
* @fileoverview A utility for extracting password information from SAML * @fileoverview A utility for extracting password information from SAML
* authorization response. This requires that the SAML IDP administrator * authorization response. This requires that the SAML IDP administrator
...@@ -11,7 +15,7 @@ ...@@ -11,7 +15,7 @@
*/ */
cr.define('samlPasswordAttributes', function() { cr.define('samlPasswordAttributes', function() {
'use strict'; /* #ignore */ 'use strict';
/** @const @private {number} The shortest XML string that could be useful. */ /** @const @private {number} The shortest XML string that could be useful. */
const MIN_SANE_XML_LENGTH = 100; const MIN_SANE_XML_LENGTH = 100;
...@@ -63,7 +67,7 @@ cr.define('samlPasswordAttributes', function() { ...@@ -63,7 +67,7 @@ cr.define('samlPasswordAttributes', function() {
* could be extracted, formatted as strings. Some or all of the strings can * could be extracted, formatted as strings. Some or all of the strings can
* be empty if some or all of the attributes could not be extracted. * be empty if some or all of the attributes could not be extracted.
*/ */
function readPasswordAttributes(xmlStr) { /* #export */ function readPasswordAttributes(xmlStr) {
// Don't throw any exception that could cause login to fail - extracting // Don't throw any exception that could cause login to fail - extracting
// these attributes can fail, but login should not be interrupted. // these attributes can fail, but login should not be interrupted.
try { try {
...@@ -135,7 +139,7 @@ cr.define('samlPasswordAttributes', function() { ...@@ -135,7 +139,7 @@ cr.define('samlPasswordAttributes', function() {
* saml_password_attributes.cc must also be changed. * saml_password_attributes.cc must also be changed.
* @export @final * @export @final
*/ */
class PasswordAttributes { /* #export */ class PasswordAttributes {
constructor(modifiedTime, expirationTime, passwordChangeUrl) { constructor(modifiedTime, expirationTime, passwordChangeUrl) {
/** @type {string} Password last-modified timestamp. */ /** @type {string} Password last-modified timestamp. */
this.modifiedTime = modifiedTime; this.modifiedTime = modifiedTime;
...@@ -153,7 +157,7 @@ cr.define('samlPasswordAttributes', function() { ...@@ -153,7 +157,7 @@ cr.define('samlPasswordAttributes', function() {
/** An immutable and empty PasswordAttributes struct. */ /** An immutable and empty PasswordAttributes struct. */
PasswordAttributes.EMPTY = new PasswordAttributes('', '', ''); PasswordAttributes.EMPTY = new PasswordAttributes('', '', '');
// Public functions: // #cr_define_end
return { return {
readPasswordAttributes: readPasswordAttributes, readPasswordAttributes: readPasswordAttributes,
PasswordAttributes: PasswordAttributes, PasswordAttributes: PasswordAttributes,
......
...@@ -44,14 +44,14 @@ cr.define('samlTimestamps', function() { ...@@ -44,14 +44,14 @@ cr.define('samlTimestamps', function() {
* @param {string} str A timestamp formatted as a string. * @param {string} str A timestamp formatted as a string.
* @return {?Date} A valid decoded timestamp, or null. * @return {?Date} A valid decoded timestamp, or null.
*/ */
function decodeTimestamp(str) { /* #export */ function decodeTimestamp(str) {
str = str.trim(); str = str.trim();
if (str.length == 0 || str.length > MAX_SANE_LENGTH) { if (str.length == 0 || str.length > MAX_SANE_LENGTH) {
return null; return null;
} }
if (INTEGER_PATTERN.test(str)) { if (INTEGER_PATTERN.test(str)) {
return decodeIntegerTimestamp(parseInt(str)); return decodeIntegerTimestamp(parseInt(str, 10));
} else if (ISO_8601_PATTERN.test(str)) { } else if (ISO_8601_PATTERN.test(str)) {
return decodeIso8601(str); return decodeIso8601(str);
} }
...@@ -134,6 +134,7 @@ cr.define('samlTimestamps', function() { ...@@ -134,6 +134,7 @@ cr.define('samlTimestamps', function() {
return isNaN(date) ? null : date; return isNaN(date) ? null : date;
} }
// #cr_define_end
// Public functions: // Public functions:
return {decodeTimestamp: decodeTimestamp}; return {decodeTimestamp: decodeTimestamp};
}); });
...@@ -11,8 +11,9 @@ ...@@ -11,8 +11,9 @@
/** /**
* Creates a new WebviewEventManager. * Creates a new WebviewEventManager.
* @constructor
*/ */
function WebviewEventManager() { /* #export */ function WebviewEventManager() {
this.unbindWebviewCleanupFunctions_ = []; this.unbindWebviewCleanupFunctions_ = [];
} }
...@@ -20,10 +21,9 @@ WebviewEventManager.prototype = { ...@@ -20,10 +21,9 @@ WebviewEventManager.prototype = {
/** /**
* Adds a EventListener to |eventTarget| and adds a clean-up function so we * Adds a EventListener to |eventTarget| and adds a clean-up function so we
* can remove the listener in unbindFromWebview. * can remove the listener in unbindFromWebview.
* @param {Object} webview the object to add the listener to * @param {Object} eventTarget the object to add the listener to
* @param {string} type the event type * @param {string} type the event type
* @param {Function} listener the event listener * @param {Function} listener the event listener
* @private
*/ */
addEventListener(eventTarget, type, listener) { addEventListener(eventTarget, type, listener) {
eventTarget.addEventListener(type, listener); eventTarget.addEventListener(type, listener);
...@@ -34,10 +34,12 @@ WebviewEventManager.prototype = { ...@@ -34,10 +34,12 @@ WebviewEventManager.prototype = {
/** /**
* Adds a listener to |webRequestEvent| and adds a clean-up function so we can * Adds a listener to |webRequestEvent| and adds a clean-up function so we can
* remove the listener in unbindFromWebview. * remove the listener in unbindFromWebview.
* @param {Object} webRequestEvent the object to add the listener to * @param {Object} webRequestEvent the object to add the listener to.
* @param {string} type the event type
* @param {Function} listener the event listener * @param {Function} listener the event listener
* @private * @param {RequestFilter} filter the object describing filters to apply to
* webRequest events.
* @param {?Object} extraInfoSpec the object to pass additional event-specific
* instructions.
*/ */
addWebRequestEventListener(webRequestEvent, listener, filter, extraInfoSpec) { addWebRequestEventListener(webRequestEvent, listener, filter, extraInfoSpec) {
webRequestEvent.addListener(listener, filter, extraInfoSpec); webRequestEvent.addListener(listener, filter, extraInfoSpec);
...@@ -47,7 +49,6 @@ WebviewEventManager.prototype = { ...@@ -47,7 +49,6 @@ WebviewEventManager.prototype = {
/** /**
* Unbinds this Authenticator from the currently bound webview. * Unbinds this Authenticator from the currently bound webview.
* @private
*/ */
removeAllListeners() { removeAllListeners() {
for (let i = 0; i < this.unbindWebviewCleanupFunctions_.length; i++) { for (let i = 0; i < this.unbindWebviewCleanupFunctions_.length; i++) {
......
...@@ -128,6 +128,7 @@ template("chrome_extra_paks") { ...@@ -128,6 +128,7 @@ template("chrome_extra_paks") {
"$root_gen_dir/chrome/component_extension_resources.pak", "$root_gen_dir/chrome/component_extension_resources.pak",
"$root_gen_dir/chrome/dev_ui_resources.pak", "$root_gen_dir/chrome/dev_ui_resources.pak",
"$root_gen_dir/chrome/downloads_resources.pak", "$root_gen_dir/chrome/downloads_resources.pak",
"$root_gen_dir/chrome/gaia_auth_host_resources.pak",
"$root_gen_dir/chrome/history_resources.pak", "$root_gen_dir/chrome/history_resources.pak",
"$root_gen_dir/chrome/local_ntp_resources.pak", "$root_gen_dir/chrome/local_ntp_resources.pak",
"$root_gen_dir/chrome/new_tab_page_resources.pak", "$root_gen_dir/chrome/new_tab_page_resources.pak",
...@@ -140,6 +141,7 @@ template("chrome_extra_paks") { ...@@ -140,6 +141,7 @@ template("chrome_extra_paks") {
"//chrome/browser/resources:component_extension_resources", "//chrome/browser/resources:component_extension_resources",
"//chrome/browser/resources:dev_ui_paks", "//chrome/browser/resources:dev_ui_paks",
"//chrome/browser/resources:downloads_resources", "//chrome/browser/resources:downloads_resources",
"//chrome/browser/resources:gaia_auth_host_resources",
"//chrome/browser/resources:history_resources", "//chrome/browser/resources:history_resources",
"//chrome/browser/resources:local_ntp_resources", "//chrome/browser/resources:local_ntp_resources",
"//chrome/browser/resources:new_tab_page_resources", "//chrome/browser/resources:new_tab_page_resources",
......
...@@ -225,6 +225,53 @@ WebView.prototype.contentWindow; ...@@ -225,6 +225,53 @@ WebView.prototype.contentWindow;
*/ */
WebView.prototype.request; WebView.prototype.request;
/**
* @constructor
* @see https://developer.chrome.com/apps/tags/webview#type-ContextMenus
*/
function ContextMenus() {}
ContextMenus.prototype.onShow;
ContextMenus.prototype.onShow.addListener = function() {};
/**
* @type {ContextMenus}
* @see https://developer.chrome.com/apps/tags/webview#property-contextMenus
*/
WebView.prototype.contextMenus;
/**
* @typedef{{
* code: ?string,
* files: ?Array<string>
* }}
* @see https://developer.chrome.com/apps/tags/webview#type-InjectionItems
*/
var InjectionItems;
/**
* Details of the content script to inject.
* @typedef{{
* name: string,
* matches: Array<string>,
* exclude_matches: ?Array<string>,
* match_about_blank: ?boolean,
* css: ?InjectionItems,
* js: ?InjectionItems,
* run_at: ?string,
* all_frames: ?boolean,
* include_globs: ?Array<string>,
* exclude_globs: ?Array<string>,
* }}
* @see https://developer.chrome.com/apps/tags/webview#type-ContentScriptDetails
*/
var ContentScriptDetails;
/**
* @param {Array<ContentScriptDetails>} contentScriptList
* @see https://developer.chrome.com/apps/tags/webview#method-addContentScripts
*/
WebView.prototype.addContentScripts = function(contentScriptList) {};
/** /**
* @see https://developer.chrome.com/apps/tags/webview#method-back * @see https://developer.chrome.com/apps/tags/webview#method-back
*/ */
...@@ -318,6 +365,12 @@ WebView.prototype.print = function() {}; ...@@ -318,6 +365,12 @@ WebView.prototype.print = function() {};
*/ */
WebView.prototype.reload = function() {}; WebView.prototype.reload = function() {};
/**
* @param {Array<string>} contentScriptList
* @see https://developer.chrome.com/apps/tags/webview#method-removeContentScripts
*/
WebView.prototype.removeContentScripts = function(contentScriptList) {};
/** /**
* @param {string} userAgent * @param {string} userAgent
* @see https://developer.chrome.com/apps/tags/webview#method-setUserAgentOverride * @see https://developer.chrome.com/apps/tags/webview#method-setUserAgentOverride
...@@ -331,6 +384,14 @@ WebView.prototype.setUserAgentOverride = function(userAgent) {}; ...@@ -331,6 +384,14 @@ WebView.prototype.setUserAgentOverride = function(userAgent) {};
*/ */
WebView.prototype.setZoom = function(zoomFactor, opt_callback) {}; WebView.prototype.setZoom = function(zoomFactor, opt_callback) {};
/**
* @param {string} zoomMode Allowed values: "per-origin", "per-view", "disabled"
* @see https://developer.chrome.com/apps/tags/webview#type-ZoomMode
* @param {Function=} opt_callback
* @see https://developer.chrome.com/apps/tags/webview#method-setZoomMode
*/
WebView.prototype.setZoomMode = function(zoomMode, opt_callback) {};
/** /**
* @see https://developer.chrome.com/apps/tags/webview#method-stop * @see https://developer.chrome.com/apps/tags/webview#method-stop
*/ */
......
...@@ -200,6 +200,9 @@ ...@@ -200,6 +200,9 @@
"chrome/browser/resources/bluetooth_internals/resources.grd": { "chrome/browser/resources/bluetooth_internals/resources.grd": {
"includes": [2020], "includes": [2020],
}, },
"chrome/browser/resources/gaia_auth_host/gaia_auth_host_resources.grd": {
"includes": [2030],
},
"chrome/browser/resources/invalidations/invalidations_resources.grd": { "chrome/browser/resources/invalidations/invalidations_resources.grd": {
"includes": [2040], "includes": [2040],
}, },
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment