chromium/chrome/browser/resources/chromeos/parent_access/webview_manager.ts

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

type ShouldSendTokenToUrlFunction = (token: string) => boolean;
type AllowedRequestFunction = (request: string) => boolean;
type OnBeforeSendHeadersListener = (obj: any) =>
    chrome.webRequest.BlockingResponse|null;

/**
 * Class that handles managing and configuring a <webview> for use
 * for embedded hosted web apps in system components.  Handles
 * settings options to provide for the security and control of the
 * content in the webview.  Using this class doesn't precluding configuring
 * the webview outside of this class.  Rather, this class is intended to
 * standardize some common configurations of webview.
 */
export class WebviewManager {
  private webview_: chrome.webviewTag.WebView;
  /**
   * Tracks the current listener used to filter destinations
   * to which we send access tokens.
   */
  private shouldSendTokenToUrlListener_: OnBeforeSendHeadersListener|null;
  /**
   * Tracks the current listener used to filter destinations
   * to which we send allow requests.
   */
  private allowedRequestListener_: OnBeforeSendHeadersListener|null;

  constructor(webview: chrome.webviewTag.WebView) {
    this.webview_ = webview;
  }

  /**
   * Configures the webview to use the specified token to authenticate the user.
   * Sets the token as part of the Auhtorization: Bearer HTTP header.
   * @param {string} accessToken the access token
   * @param {!function(string):boolean} shouldSendTokenToUrlFn function that
   *     returns true if the access token should be sent to the specified host.
   */
  setAccessToken(
      accessToken: string,
      shouldSendTokenToUrlFn: ShouldSendTokenToUrlFunction) {
    if (this.shouldSendTokenToUrlListener_) {
      this.webview_.request.onBeforeSendHeaders.removeListener(
          this.shouldSendTokenToUrlListener_);
      this.shouldSendTokenToUrlListener_ = null;
    }
    this.shouldSendTokenToUrlListener_ = (details) => {
      if (shouldSendTokenToUrlFn(details.url)) {
        details.requestHeaders.push({
          name: 'Authorization',
          value: 'Bearer ' + accessToken,
        });
      }

      return {requestHeaders: details.requestHeaders};
    };

    this.webview_.request.onBeforeSendHeaders.addListener(
        this.shouldSendTokenToUrlListener_, {urls: ['<all_urls>']},
        ['blocking', 'requestHeaders']);
  }

  /**
   * Configures the webview to permit navigation only to URLs allowed
   * by the specified function.
   * @param {!function(string):boolean} allowedRequestFn function that returns
   *     true if the request to the specified URL is allowed.
   */
  setAllowRequestFn(allowedRequestFn: AllowedRequestFunction) {
    if (this.allowedRequestListener_) {
      this.webview_.request.onBeforeSendHeaders.removeListener(
          this.allowedRequestListener_);
      this.allowedRequestListener_ = null;
    }
    this.allowedRequestListener_ = (details) => {
      return {cancel: !allowedRequestFn(details.url)};
    };

    this.webview_.request.onBeforeRequest.addListener(
        this.allowedRequestListener_, {urls: ['<all_urls>']}, ['blocking']);
  }
}