javascript_interaction_with_c_plus_plus_on_chromium

前言

chromium官方给出的制作webui的资料,共3部分信息。
* 制作web-ui
* 使web-ui中的js和chromium中的c++实现互操作
* 在web-ui中建立弹出的子web-ui
第一个实验做完了.
这周末在做第二个实验. 做实验的分支是68.0.3440.84, 发现跟着官方文档玩,根本编译不过。
去google下,发现官方文档和跟在官方文档后面的同学做的实验,都一个样子,基本就是汉化了一下。都不是完整的实验,就说了一下js和c交互的要点。做实验的chromium版本也没说.
谁知道跟在官方文档后面那些同学,有没有做实验?
做了实验的同学,他们实验有没有成功?
也不怀疑我自己,因为官方文档实验步骤写的还是蛮清晰的。 我实验思路,读代码,调试的能力也是刚刚的。
不敢怀疑官方的文档,但是有点怀疑,官方文档是针对最新版本的chromium.可能在68.0.3440.84上实现有细节的不同。但是68.0和最新的版本也没隔多长时间,因为68.0是最新的稳定版.
官方文档描述的大方向肯定是对的。但是细节确实编译不过,我自己读其他同级的web-ui实现,尝试着修改,实验自己的web-ui版本,最后有官方文档上描述的js和c++互操作的效果了。

chromium工程中,web-ui实现有很多。如果一开始,就看已有的web-ui实现,不容易看清楚。做完自己的web-ui实现后,web-ui编程的小框架清楚了,再去看已有的web-ui实现,很容易看。

官方的例子,将web-ui实现和js-c++交互放在一起。
已有的web-ui实现,有些将web-ui实现和js-c++交互分开了, 这样维护起来好些。

实验- js和c在chromium工程中交互

在chromium工程中叫做hello_world*.*的文件有好几个版本,怕被其他实现版本影响了,重新对实现文件,实现类改名.

新建Z:\chromium\src\chrome\browser\resources\my_hello_world.html

<!-- 
  @file Z:\chromium\src\chrome\browser\resources\my_hello_world.html
  @note 如果修改了html内容, 需要重新编译chromium工程才能看到效果
-->

<!DOCTYPE HTML>
<html i18n-values="dir:textdirection">
<head>
  <meta charset="utf-8">
  <title i18n-content="my_helloWorldTitle"></title>
  <link rel="stylesheet" href="my_hello_world.css">
  <script src="chrome://resources/js/cr.js"></script>
  <script src="chrome://resources/js/load_time_data.js"></script>
  <script src="chrome://resources/js/util.js"></script>
  <script src="strings.js"></script>
  <script src="my_hello_world.js"></script>
</head>
<body i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize">

  <!-- my_helloWorldTitle 是变量, 在程序中用c++和js交互的接口赋值 -->
  <h1 i18n-content="my_helloWorldTitle"></h1>

  <!-- 静态html语法,直接显示 -->
  <h1>My First Heading -- this is static html code</h1>
  <p>My first paragraph. -- this is static html code</p>

  <p>test by ls -- this is static html code</p>

  <!-- id 指示变量内容是从js文件中来的 -->
  <!-- my_welcome-message 是变量, 在程序中用c++和js交互的接口赋值 -->
  <!-- 如果这个id改名了, my_hello_world.js initialize() 中的变量my_welcome_message也要改名, 这个值是那里用js赋值的 -->
  <p id="my_welcome_message"></p>

  <!-- i18n-content 指示变量内容是从资源文件中来的 -->
  <p i18n-content="my_welcome_message"></p>
 <script src="chrome://resources/js/i18n_template.js"></script>
</body>
</html>

新建Z:\chromium\src\chrome\browser\resources\my_hello_world.css

/**
* @file Z:\chromium\src\chrome\browser\resources\my_hello_world.css
*/

p {
  white-space: pre-wrap;
}

新建Z:\chromium\src\chrome\browser\resources\my_hello_world.js

/**
* @file Z:\chromium\src\chrome\browser\resources\my_hello_world.js
*/

cr.define('my_page', function() {
  'use strict';

  /**
   * Be polite and insert translated hello world strings for the user on loading.
   */
  function initialize() {
    $('my_welcome_message').textContent = loadTimeData.getStringF('my_welcome_message',
            loadTimeData.getString('userName'));

    // 这里可以搞成按钮的onclick() 或 下列列表按钮的事件触发
    alert('begin : js send request to buind_in url');
    chrome.send('addNumbers', [66, 88]);
    alert('over : js send request to buind_in url');
  }

  // 如果js函数写错了(e.g. "addResult"), 不会报错, 但是没效果(没有调用到js文件中的addResult函数)
  // c代码中必须写 CallJavascriptFunction("my_page.addResult", result); 才会进入异步的addResult(result)函数
  function addResult(result) {
    alert('enter addResult(result)');
    alert('The result of our C++ arithmetic: 66 + 88 = ' + result);
  }

  // Return an object with all of the exports.
  return {
    addResult: addResult,
    initialize: initialize,
  };
});

/**
这里执行的函数的命令空间要和上面一样 e.g. my_page
*/
document.addEventListener('DOMContentLoaded', my_page.initialize);

编辑Z:\chromium\src\chrome\browser\browser_resources.grd

加入网页实现资源 IDR_HELLO_WORLD_X

      <structure name="IDR_SIGNIN_SHARED_CSS_HTML" file="resources\signin\signin_shared_css.html" preprocess="true" allowexternalscript="true" type="chrome_html" />
      <if expr="not is_android and not chromeos">
        <structure name="IDR_WELCOME_CSS" file="resources\welcome\welcome.css" type="chrome_html" preprocess="true"/>
        <structure name="IDR_WELCOME_HTML" file="resources\welcome\welcome.html" type="chrome_html" preprocess="true"/>
        <structure name="IDR_WELCOME_JS" file="resources\welcome\welcome.js" type="chrome_html" preprocess="true"/>
        <structure name="IDR_DICE_WELCOME_CSS" file="resources\welcome\dice_welcome\welcome.css" type="chrome_html" preprocess="true"/>
        <structure name="IDR_DICE_WELCOME_HTML" file="resources\welcome\dice_welcome\welcome.html" type="chrome_html" preprocess="true"/>
        <structure name="IDR_DICE_WELCOME_BROWSER_PROXY_HTML" file="resources\welcome\dice_welcome\welcome_browser_proxy.html" type="chrome_html" preprocess="true"/>
        <structure name="IDR_DICE_WELCOME_BROWSER_PROXY_JS" file="resources\welcome\dice_welcome\welcome_browser_proxy.js" type="chrome_html" preprocess="true"/>
        <structure name="IDR_DICE_WELCOME_APP_HTML" file="resources\welcome\dice_welcome\welcome_app.html" type="chrome_html" preprocess="true"/>
        <structure name="IDR_DICE_WELCOME_APP_JS" file="resources\welcome\dice_welcome\welcome_app.js" type="chrome_html" preprocess="true"/>
      </if>
      <if expr="is_win">
        <structure name="IDR_WELCOME_WIN10_CSS" file="resources\welcome\welcome_win10.css" type="chrome_html" />
        <structure name="IDR_WELCOME_WIN10_HTML" file="resources\welcome\welcome_win10.html" type="chrome_html" />
        <structure name="IDR_WELCOME_WIN10_JS" file="resources\welcome\welcome_win10.js" type="chrome_html" />
      </if>
    </structures>
    <includes>
      <include name="IDR_HELLO_WORLD_HTML" file="resources\my_hello_world.html" type="BINDATA" />
      <include name="IDR_HELLO_WORLD_CSS" file="resources\my_hello_world.css" type="BINDATA" />
      <include name="IDR_HELLO_WORLD_JS" file="resources\my_hello_world.js" type="BINDATA" />

      <if expr="is_win or is_macosx or desktop_linux or chromeos">
        <include name="IDR_ABOUT_DISCARDS_CSS" file="resources\discards\discards.css" type="BINDATA" />
        <include name="IDR_ABOUT_DISCARDS_HTML" file="resources\discards\discards.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
        <include name="IDR_ABOUT_DISCARDS_JS" file="resources\discards\discards.js" type="BINDATA" />
        <include name="IDR_ABOUT_DISCARDS_MOJO_JS" file="${root_gen_dir}\chrome\browser\ui\webui\discards\discards.mojom.js" use_base_dir="false" type="BINDATA" />
      </if>

编辑Z:\chromium\src\chrome\common\webui_url_constants.h

加入自己的web-url的外部声明

// Copyright 2017 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.

// Contains constants for WebUI UI/Host/SubPage constants. Anything else go in
// chrome/common/url_constants.h.

#ifndef CHROME_COMMON_WEBUI_URL_CONSTANTS_H_
#define CHROME_COMMON_WEBUI_URL_CONSTANTS_H_

#include <stddef.h>

#include "build/build_config.h"
#include "chrome/common/buildflags.h"
#include "content/public/common/url_constants.h"
#include "media/media_buildflags.h"
#include "printing/buildflags/buildflags.h"

namespace chrome {

// chrome: components (without schemes) and URLs (including schemes).
// e.g. kChromeUIFooHost = "foo" and kChromeUIFooURL = "chrome://foo/"
// Not all components have corresponding URLs and vice versa. Only add as
// needed.
// Please keep in alphabetical order, with OS/feature specific sections below.

extern const char kChromeUIHelloWorldURL[];
extern const char kChromeUIHelloWorldHost[];

extern const char kChromeUIAboutHost[];
extern const char kChromeUIAboutURL[];

编辑Z:\chromium\src\chrome\common\webui_url_constants.cc

加入自己的web-url的声明

// Copyright 2017 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.

#include "chrome/common/webui_url_constants.h"

#include "base/macros.h"
#include "components/nacl/common/buildflags.h"
#include "components/safe_browsing/web_ui/constants.h"
#include "extensions/buildflags/buildflags.h"

namespace chrome {

// Please keep this file in the same order as the header.

// Note: Add hosts to |kChromePaths| in browser_about_handler.cc to be listed by
// chrome://chrome-urls (about:about) and the built-in AutocompleteProvider.

    const char kChromeUIHelloWorldURL[] = "chrome://my_page/";
    const char kChromeUIHelloWorldHost[] = "my_page";

const char kChromeUIAboutHost[] = "about";
const char kChromeUIAboutURL[] = "chrome://about/";
const char kChromeUIAppLauncherPageHost[] = "apps";

编辑Z:\chromium\src\chrome\app\generated_resources.grd

加入一些规定的不用修改的资源字符串

      <!-- Printing specific strings -->
      <if expr="enable_printing">
        <part file="printing_strings.grdp" />
      </if>

      <!-- TODO add all of your "string table" messages here.  Remember to
      change nontranslateable parts of the messages into placeholders (using the
      <ph> element).  You can also use the 'grit add' tool to help you identify
      nontranslateable parts and create placeholders for them. -->
      <message name="IDS_LS_WEB_UI_TITLE" desc="A happy message saying hello to the world">
        &lt;&lt;lostspeed web ui&gt;&gt; -- this content from resource file : Z:\\chromium\\src\\chrome\\app\\generated_resources.grd IDS_LS_WEB_UI_TITLE
      </message>
      <message name="IDS_LS_HELLO_WORLD_WELCOME_TEXT" desc="Message welcoming the user to the hello world page">
        my Welcome to this fancy Hello World page <ph name="WELCOME_NAME">$1<ex>Chromium User</ex></ph>!
      </message>

      <if expr="is_win">
        <message name="IDS_BACKGROUND_APP_INSTALLED_BALLOON_TITLE" desc="The title of the balloon that is displayed when a background app is installed">
          New background app added
        </message>
        <message name="IDS_BACKGROUND_APP_INSTALLED_BALLOON_BODY" desc="The contents of the balloon that is displayed when a background app is installed">
          <ph name="APP_NAME">$1<ex>Background App</ex></ph> will launch at system startup and continue to run in the background even once you've closed all other <ph name="PRODUCT_NAME">$2<ex>Google Chrome</ex></ph> windows.
        </message>
      </if>

新建z:\chromium\src\chrome\browser\ui\webui\hello_world_ui.h

这里是处理网页上显示的固定资源字符串, js和c++交互的类声明
使用了多继承, 从content::WebUIController, content::WebUIMessageHandler继承一个子类.这么做不知道对不对,官方文档上没说,我这实验正常:)

// @file Z:\chromium\src\components\hello_world\hello_world_ui.h
#ifndef COMPONENTS_HELLO_WORLD_HELLO_WORLD_UI_H_
#define COMPONENTS_HELLO_WORLD_HELLO_WORLD_UI_H_
#pragma once

#include "base/macros.h"
#include "content/public/browser/web_ui_controller.h"

#include "content/public/browser/web_ui_message_handler.h"
#include "content/browser/devtools/protocol/protocol.h"
#include "chrome/browser/devtools/protocol/forward.h"

namespace base {
    class ListValue;
} // namespace base

// The WebUI for chrome://hello-world
class HelloWorldUI : public content::WebUIController, public content::WebUIMessageHandler {
 public:
    explicit HelloWorldUI(content::WebUI* web_ui);
    ~HelloWorldUI() override;

    void RegisterMessages() override;

    bool OverrideHandleWebUIMessage(const GURL& source_url,
        const std::string& name,
        const base::ListValue& args) override;

 private:
    void AddNumbers(const base::ListValue* args);
    DISALLOW_COPY_AND_ASSIGN(HelloWorldUI);
};

#endif  // COMPONENTS_HELLO_WORLD_HELLO_WORLD_UI_H_

新建z:\chromium\src\chrome\browser\ui\webui\hello_world_ui.cc

实现web-ui的资源字符串显示, js和c的交互

// @file Z:\chromium\src\chrome\browser\ui\webui\hello_world_ui.cc

#include "chrome/browser/ui/webui/hello_world_ui.h"

#include "content/public/browser/browser_context.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"

#include "chrome/browser/profiles/profile.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/browser_resources.h"
#include "chrome/grit/generated_resources.h"

HelloWorldUI::HelloWorldUI(content::WebUI* web_ui)
    : content::WebUIController(web_ui)
{
    // give web_ui to content::WebUIMessageHandler
    // IsJavascriptAllowed 和执行js函数会用到web_ui, 不能为空
    set_web_ui(web_ui);

  // Set up the chrome://hello-world source.
  content::WebUIDataSource* html_source =
      content::WebUIDataSource::Create(chrome::kChromeUIHelloWorldHost);

  // Register callback handler.
  web_ui->RegisterMessageCallback(
      "addNumbers",
      base::Bind(&HelloWorldUI::AddNumbers,
          base::Unretained(this)));

  // Localized strings.
  // html_source->AddLocalizedString是添加资源中固定的字符串
  html_source->AddLocalizedString("my_helloWorldTitle", IDS_LS_WEB_UI_TITLE);
  html_source->AddLocalizedString("my_welcome_message", IDS_LS_HELLO_WORLD_WELCOME_TEXT);

  // As a demonstration of passing a variable for JS to use we pass in the name "Bob".
  // html_source->AddString是添加动态的字符串, 这函数实用
  html_source->AddString("userName", "Bob");
  html_source->SetJsonPath("strings.js");

  // Add required resources.
  html_source->AddResourcePath("my_hello_world.css", IDR_HELLO_WORLD_CSS);
  html_source->AddResourcePath("my_hello_world.js", IDR_HELLO_WORLD_JS);
  html_source->SetDefaultResource(IDR_HELLO_WORLD_HTML);

  content::BrowserContext* browser_context =
      web_ui->GetWebContents()->GetBrowserContext();
  content::WebUIDataSource::Add(browser_context, html_source);
}

HelloWorldUI::~HelloWorldUI() {
}

void HelloWorldUI::RegisterMessages()
{
}

bool HelloWorldUI::OverrideHandleWebUIMessage(const GURL& source_url,
    const std::string& name,
    const base::ListValue& args)
{
    if (source_url.spec() != chrome::kChromeUIHelloWorldURL) {
        return false;
    }

    // on js the sender content below:
    // chrome.send('addNumbers', [66, 88]);
    if (name == "addNumbers") {
        AddNumbers(&args);
        return true;
    }
    // 如果有其他的js请求,也照"addNumbers"一并处理了

    return false;
}

void HelloWorldUI::AddNumbers(const base::ListValue* args) {
    int term1 = 0;
    int term2 = 0;

    AllowJavascript(); // 需要允许运行js

    if (!args->GetInteger(0, &term1) || !args->GetInteger(1, &term2)) {
        return;
    }

    base::Value result(term1 + term2);

    // CallJavascriptFunction 最后调用了 MessagePipeReader::Send
    // 如果js函数写错了(e.g. "addResult"), 不会报错, 但是没效果(没有调用到js文件中的addResult函数)
    CallJavascriptFunction("my_page.addResult", result);
}

编辑Z:\chromium\src\chrome\browser\ui\BUILD.gn

将hello_world_ui.*交给编译器, 编进ui_0.lib

      # TODO(estade): this class should be deleted in favor of a combobox model.
      # See crbug.com/590850
      "content_settings/content_setting_media_menu_model.cc",
      "content_settings/content_setting_media_menu_model.h",
      "javascript_dialogs/javascript_dialog_cocoa.h",
      "javascript_dialogs/javascript_dialog_cocoa.mm",
      "javascript_dialogs/javascript_dialog_mac.cc",
      "proximity_auth/proximity_auth_error_bubble_stub.cc",
      "startup/session_crashed_infobar_delegate.cc",
      "startup/session_crashed_infobar_delegate.h",
    ]
  }

  sources = [
    "accelerator_utils.h",
    "app_list/app_list_util.cc",
    "app_list/app_list_util.h",

    # All other browser/ui/app_list files go under enable_app_list below.
    "webui/hello_world_ui.cc",
    "webui/hello_world_ui.h",

    "autofill/autofill_dialog_models.cc",
    "autofill/autofill_dialog_models.h",
    "autofill/autofill_popup_controller.h",
    "autofill/autofill_popup_controller_impl.cc",
    "autofill/autofill_popup_controller_impl.h",
    "autofill/autofill_popup_controller_impl_mac.h",
    "autofill/autofill_popup_controller_impl_mac.mm",

编辑Z:\chromium\src\chrome\browser\ui\webui\chrome_web_ui_controller_factory.cc

根据输入的url,创建对应的web-ui

#include "chrome/browser/ui/webui/hello_world_ui.h"
// Returns a function that can be used to create the right type of WebUI for a
// tab, based on its URL. Returns NULL if the URL doesn't have WebUI associated
// with it.
WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
                                             Profile* profile,
                                             const GURL& url) {
  // This will get called a lot to check all URLs, so do a quick check of other
  // schemes to filter out most URLs.
  if (!url.SchemeIs(content::kChromeDevToolsScheme) &&
      !url.SchemeIs(content::kChromeUIScheme)) {
    return NULL;
  }

  // @todo ls
  if (url.host() == chrome::kChromeUIHelloWorldHost) {
     return &NewWebUI<HelloWorldUI>;
  }

  /****************************************************************************
   * Please keep this in alphabetical order. If #ifs or special logics are
   * required, add it below in the appropriate section.
   ***************************************************************************/
  // We must compare hosts only since some of the Web UIs append extra stuff
  // after the host name.
  // All platform builds of Chrome will need to have a cloud printing
  // dialog as backup.  It's just that on Chrome OS, it's the only
  // print dialog.
  if (url.host_piece() == chrome::kChromeUIBluetoothInternalsHost)
    return &NewWebUI<BluetoothInternalsUI>;
  if (url.host_piece() == chrome::kChromeUIComponentsHost)
    return &NewWebUI<ComponentsUI>;

重新生成工程文件

Z:\chromium\src>gn gen --ide=vs out\Default_68_0_3440_84

重新编译工程

Z:\chromium\src>autoninja -C out\Default_68_0_3440_84 chrome

实验运行新加入的web_ui的效果

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

猜你喜欢

转载自blog.csdn.net/LostSpeed/article/details/82083113