1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139

content / public / browser / web_ui_message_handler.h [blame]

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

#ifndef CONTENT_PUBLIC_BROWSER_WEB_UI_MESSAGE_HANDLER_H_
#define CONTENT_PUBLIC_BROWSER_WEB_UI_MESSAGE_HANDLER_H_

#include <ostream>
#include <string_view>

#include "base/check.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/values.h"
#include "content/common/content_export.h"
#include "content/public/browser/web_ui.h"

class WebUIBrowserTest;
class MojoWebUIBrowserTest;
class CertificateHandlerTest;

namespace content {

class TestWebUI;
class WebUI;

// Messages sent from the DOM are forwarded via the WebUI to handler
// classes. These objects are owned by WebUI and destroyed when the
// host is destroyed.
class CONTENT_EXPORT WebUIMessageHandler {
 public:
  WebUIMessageHandler() = default;
  virtual ~WebUIMessageHandler() = default;

  // Call this when a page should not receive JavaScript messages.
  void DisallowJavascript();

  // Called from tests to toggle JavaScript to catch bugs. If AllowJavascript()
  // is needed from production code, just publicize AllowJavascript() instead.
  void AllowJavascriptForTesting();

  bool IsJavascriptAllowed();

 protected:
  // This method must be called once the handler's corresponding JavaScript
  // component is initialized. In practice, it should be called from a WebUI
  // message handler similar to: 'initializeFooPage' or 'getInitialState'.
  //
  // There should be ideally one or two calls to this per handler, as JavaScript
  // components should have a specific message that signals that it's initalized
  // and ready to receive events from the C++ handler.
  //
  // This should never be called from a function that is not a message handler.
  // This should never be called from a C++ callback used as a reply for a
  // posted task or asynchronous operation.
  void AllowJavascript();

  // This is where subclasses specify which messages they'd like to handle and
  // perform any additional initialization. At this point web_ui() will return
  // the associated WebUI object.
  virtual void RegisterMessages() = 0;

  // Will be called whenever JavaScript from this handler becomes allowed from
  // the disallowed state. Subclasses should override this method to register
  // observers that push JavaScript calls to the page.
  virtual void OnJavascriptAllowed() {}

  // Will be called whenever JavaScript from this handler becomes disallowed
  // from the allowed state. This will never be called before
  // OnJavascriptAllowed has been called. Subclasses should override this method
  // to deregister or disabled observers that push JavaScript calls to the page.
  // Any WeakPtrFactory used for async tasks that would talk to the renderer
  // must by invalidated by the subclass when this is called.
  virtual void OnJavascriptDisallowed() {}

  // Helper method for responding to Javascript requests initiated with
  // cr.sendWithPromise() (defined in cr.js) for the case where the returned
  // promise should be resolved (request succeeded).
  void ResolveJavascriptCallback(const base::ValueView callback_id,
                                 const base::ValueView response);

  // Helper method for responding to Javascript requests initiated with
  // cr.sendWithPromise() (defined in cr.js), for the case where the returned
  // promise should be rejected (request failed).
  void RejectJavascriptCallback(const base::ValueView callback_id,
                                const base::ValueView response);

  // Helper method for notifying Javascript listeners added with
  // cr.addWebUIListener() (defined in cr.js).
  template <typename... Values>
  void FireWebUIListener(std::string_view event_name, const Values&... values) {
    // cr.webUIListenerCallback is a global JS function exposed from cr.js.
    CallJavascriptFunction("cr.webUIListenerCallback", base::Value(event_name),
                           values...);
  }

  // Call a Javascript function by sending its name and arguments down to
  // the renderer.  This is asynchronous; there's no way to get the result
  // of the call, and should be thought of more like sending a message to
  // the page.
  // All function names in WebUI must consist of only ASCII characters.
  // These functions will crash if JavaScript is not currently allowed.
  template <typename... Values>
  void CallJavascriptFunction(std::string_view function_name,
                              const Values&... values) {
    CHECK(IsJavascriptAllowed()) << "Cannot CallJavascriptFunction before "
                                    "explicitly allowing JavaScript.";

    // The CHECK above makes this call safe.
    web_ui()->CallJavascriptFunctionUnsafe(function_name, values...);
  }

  // Returns the attached WebUI for this handler.
  WebUI* web_ui() { return web_ui_; }

  // Sets the attached WebUI - exposed to subclasses for testing purposes.
  void set_web_ui(WebUI* web_ui) { web_ui_ = web_ui; }

 private:
  // Provide external classes access to web_ui(), set_web_ui(), and
  // RenderViewReused.
  friend class TestWebUI;
  friend class WebUIImpl;
  friend class ::WebUIBrowserTest;
  friend class ::MojoWebUIBrowserTest;
  friend class ::CertificateHandlerTest;

  // TODO(dbeam): disallow JavaScript when a renderer process crashes.
  // http://crbug.com/610450

  // True if the page is for JavaScript calls from this handler.
  bool javascript_allowed_ = false;
  raw_ptr<WebUI, DanglingUntriaged> web_ui_ = nullptr;
};

}  // namespace content

#endif  // CONTENT_PUBLIC_BROWSER_WEB_UI_MESSAGE_HANDLER_H_