chrome extension native message & native client

Extensions and apps can exchange messages with native applications using an API that is similar to the other message passing APIs. Native applications that support this feature must register a native messaging host that knows how to communicate with the extension. Chrome starts the host in a separate process and communicates with it using standard input and standard output streams.

Native messaging host

In order to register a native messaging host the application must install a manifest file that defines the native messaging host configuration. Below is an example of the manifest file:

{
  "name": "com.my_company.my_application",
  "description": "My Application",
  "path": "C:\\Program Files\\My Application\\chrome_native_messaging_host.exe",
  "type": "stdio",
  "allowed_origins": [
    "chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/"
  ]
}

The native messaging host manifest file must be valid JSON and contains the following fields:

NameDescription
nameName of the native messaging host. Clients pass this string toruntime.connectNative or runtime.sendNativeMessage. This name can only contain lowercase alphanumeric characters, underscores and dots. The name cannot start or end with a dot, and a dot cannot be followed by another dot.
descriptionShort application description.
pathPath to the native messaging host binary. On Linux and OSX the path must be absolute. On Windows it can be relative to the directory in which the manifest file is located. The host process is started with the current directory set to the directory that contains the host binary. For example if this parameter is set to C:\Application\nm_host.exe then it will be started with current directory C:\Application\.
typeType of the interface used to communicate with the native messaging host. Currently there is only one possible value for this parameter: stdio. It indicates that Chrome should use stdin and stdout to communicate with the host.
allowed_originsList of extensions that should have access to the native messaging host. Wildcards such as chrome-extension://*/* are not allowed.

Native messaging host location

The location of the manifest file depends on the platform.

On Windows, the manifest file can be located anywhere in the file system. The application installer must create registry keyHKEY_LOCAL_MACHINE\SOFTWARE\Google\Chrome\NativeMessagingHosts\com.my_company.my_applicationorHKEY_CURRENT_USER\SOFTWARE\Google\Chrome\NativeMessagingHosts\com.my_company.my_application, and set default value of that key to the full path to the manifest file. For example, using the following command:

REG ADD "HKCU\Software\Google\Chrome\NativeMessagingHosts\com.my_company.my_application" /ve /t REG_SZ /d "C:\path\to\nmh-manifest.json" /f

or using the following .reg file:

Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Google\Chrome\NativeMessagingHosts\com.my_company.my_application]
@="C:\\path\\to\\nmh-manifest.json"

When Chrome looks for native messaging hosts, first the 32-bit registry is queried, then the 64-bit registry.

On OS X and Linux, the location of the native messaging host's manifest file varies by the browser (Google Chrome or Chromium). The system-wide native messaging hosts are looked up at a fixed location, while the user-level native messaging hosts are looked up in a subdirectory within the user profile directory called NativeMessagingHosts.

OS X (system-wide)

Google Chrome: /Library/Google/Chrome/NativeMessagingHosts/com.my_company.my_application.json

Chromium: /Library/Application Support/Chromium/NativeMessagingHosts/com.my_company.my_application.json

OS X (user-specific, default path)

Google Chrome: ~/Library/Application Support/Google/Chrome/NativeMessagingHosts/com.my_company.my_application.json

Chromium: ~/Library/Application Support/Chromium/NativeMessagingHosts/com.my_company.my_application.json

Linux (system-wide)

Google Chrome: /etc/opt/chrome/native-messaging-hosts/com.my_company.my_application.json

Chromium: /etc/chromium/native-messaging-hosts/com.my_company.my_application.json

Linux (user-specific, default path)

Google Chrome: ~/.config/google-chrome/NativeMessagingHosts/com.my_company.my_application.json

Chromium: ~/.config/chromium/NativeMessagingHosts/com.my_company.my_application.json

Native messaging protocol

Chrome starts each native messaging host in a separate process and communicates with it using standard input (stdin) and standard output (stdout). The same format is used to send messages in both directions: each message is serialized using JSON, UTF-8 encoded and is preceded with 32-bit message length in native byte order. The maximum size of a single message from the native messaging host is 1 MB, mainly to protect Chrome from misbehaving native applications. The maximum size of the message sent to the native messaging host is 4 GB.

The first argument to the native messaging host is the origin of the caller, usually chrome-extension://[ID of allowed extension]. This allows native messaging hosts to identify the source of the message when multiple extensions are specified in the allowed_origins key in the native messaging host manifest
Warning: In Windows, in Chrome 54 and earlier, the origin was passed as the second parameter instead of the first parameter.

When a messaging port is created using runtime.connectNative Chrome starts native messaging host process and keeps it running until the port is destroyed. On the other hand, when a message is sent usingruntime.sendNativeMessage, without creating a messaging port, Chrome starts a new native messaging host process for each message. In that case the first message generated by the host process is handled as a response to the original request, i.e. Chrome will pass it to the response callback specified whenruntime.sendNativeMessage is called. All other messages generated by the native messaging host in that case are ignored.

On Windows, the native messaging host is also passed a command line argument with a handle to the calling chrome native window: --parent-window=<decimal handle value>. This lets the native messaging host create native UI windows that are correctly focused.

Connecting to a native application

Sending and receiving messages to and from a native application is very similar to cross-extension messaging. The main difference is that runtime.connectNative is used instead of runtime.connect, and runtime.sendNativeMessage is used instead of runtime.sendMessage
These methods can only be used if the "nativeMessaging" permission is declared in your extension's manifest file.

The Following example creates a runtime.Port object that's connected to native messaging host com.my_company.my_application, starts listening for messages from that port and sends one outgoing message:

var port = chrome.runtime.connectNative('com.my_company.my_application');
port.onMessage.addListener(function(msg) {
  console.log("Received" + msg);
});
port.onDisconnect.addListener(function() {
  console.log("Disconnected");
});
port.postMessage({ text: "Hello, my_application" });

runtime.sendNativeMessage can be used to send a message to native application without creating a port, e.g.:

chrome.runtime.sendNativeMessage('com.my_company.my_application',
  { text: "Hello" },
  function(response) {
    console.log("Received " + response);
  });

Debugging native messaging

When the native messaging host fails to start, writes to stderr or when it violates the communication protocol, output is written to the error log of Chrome. On Linux and OS X, this log can easily be accessed by starting Chrome from the command line and watching its output in the terminal. On Windows, use --enable-loggingas explained at How to enable logging.

Here are some errors and tips for solving the issues:

Failed to start native messaging host.

Check whether you have sufficient permissions to execute the file.

Invalid native messaging host name specified.

Check whether the name contains any invalid characters. Only lowercase alphanumeric characters, underscores and dots are allowed. A name cannot start or end with a dot, and a dot cannot be followed by another dot.

Native host has exited.

The pipe to the native messaging host was broken before the message was read by Chrome. This is most likely initiated from your native messaging host.

Specified native messaging host not found.

  • Is the name spelled correctly in the extension and in the manifest file?
  • Is the manifest put in the right directory and with the correct name? See native messaging host location for the expected formats.
  • Is the manifest file in the correct format? In particular, is the JSON syntax correct and do the values match the definition of a native messaging host manifest?
  • Does the file specified in path exist? On Windows, paths may be relative, but on OS X and Linux, the paths must be absolute.

Native messaging host host name is not registered. (Windows-only)

The native messaging host was not found in the Windows registry. Double-check using regedit whether the key was really created and matches the required format as documented at native messaging host location.

Access to the specified native messaging host is forbidden.

Is the extension's origin listed in allowed_origins?

Error when communicating with the native messaging host.

This is a very common error and indicates an incorrect implementation of the communication protocol in the native messaging host.

  • Make sure that all output in stdout adheres to the native messaging protocol. If you want to print some data for debugging purposes, write to stderr.
  • Make sure that the 32-bit message length is in the platform's native integer format (little-endian / big-endian).
  • The message length must not exceed 1024*1024.
  • The message size must be equal to the number of bytes in the message. This may differ from the "length" of a string, because characters may be represented by multiple bytes.
  • Windows-only: Make sure that the program's I/O mode is set to O_BINARY. By default, the I/O mode is O_TEXT, which corrupts the message format as line breaks (\n = 0A) are replaced with Windows-style line endings (\r\n = 0D 0A). The I/O mode can be set using __setmode.

Examples

The examples/api/nativeMessaging directory contains an example application that uses native messaging to communicate with a Python script that serves as a native messaging host. The sample host's directory also contains scripts to install/remove the native messaging host.

To try out the example, first download and extract the sample app and sample host. Run install_host.bat(Windows) or install_host.sh (Linux / OS X) to install the native messaging host. Then load the app and interact with the app. Run uninstall_host.bat or uninstall_host.sh to unregister the native messaging host when you are done.

 

Messaging System

This section describes the messaging system used to communicate between the JavaScript code and the Native Client module’s C or C++ code in a Native Client application. It introduces the concept of asynchronous programming and the basic steps required to set up a Native Client module that sends messages to and receive messages from JavaScript. This section assumes you are familiar with the material presented in the Application Structure section.

The “Hello, World” example for getting started with NaCl is used here to illustrate basic programming techniques. You can find this code in the /getting_started/part2 directory in the Native Client SDK download.

Reference information

For reference information related to the Pepper messaging API, see the following documentation:

Introduction to the messaging system

Native Client modules and JavaScript communicate by sending messages to each other. The most basic form of a message is a string. Messages support many JavaScript types, including ints, arrays, array buffers, and dictionaries (see pp::Varpp:VarArrayBuffer, and the general messaging system documentation). It’s up to you to decide on the type of message and define how to process the messages on both the JavaScript and Native Client side. For the “Hello, World” example, we will work with string-typed messages only.

When JavaScript posts a message to the Native Client module, the Pepper HandleMessage() function is invoked on the module side. Similarly, the Native Client module can post a message to JavaScript, and this message triggers a JavaScript event listener for message events in the DOM. (See the W3C specification onDocument Object Model Events for more information.) In the “Hello, World” example, the JavaScript functions for posting and handling messages are named postMessage() and handleMessage() (but any names could be used). On the Native Client C++ side, the Pepper Library functions for posting and handling messages are:

  • void pp::Instance::PostMessage(const Var &message)
  • virtual void pp::Instance::HandleMessage(const Var &message)

If you want to receive messages from JavaScript, you need to implement thepp::Instance::HandleMessage() function in your Native Client module.

Design of the messaging system

The Native Client messaging system is analogous to the system used by the browser to allow web workers to communicate (see the W3 web worker specification). The Native Client messaging system is designed to keep the web page responsive while the Native Client module is performing potentially heavy processing in the background. When JavaScript sends a message to the Native Client module, the postMessage() call returns as soon as it sends its message to the Native Client module. The JavaScript does not wait for a reply from Native Client, thus avoiding bogging down the main JavaScript thread. On the JavaScript side, you set up an event listener to respond to the message sent by the Native Client module when it has finished the requested processing and returns a message.

This asynchronous processing model keeps the main thread free while avoiding the following problems:

  • The JavaScript engine hangs while waiting for a synchronous call to return.
  • The browser pops up a dialog when a JavaScript entry point takes longer than a few moments.
  • The application hangs while waiting for an unresponsive Native Client module.

Communication tasks in the “Hello, World” example

The following sections describe how the “Hello, World” example posts and handles messages on both the JavaScript side and the Native Client side of the application.

JavaScript code

The JavaScript code and HTML in the “Hello, World” example can be found in the example.jscommon.js, and index.html files. The important steps are:

  1. Sets up an event listener to listen for message events from the Native Client module.
  2. Implements an event handler that the event listener invokes to handle incoming message events.
  3. Calls postMessage() to communicate with the NaCl module, after the page loads.

Step 1: From common.js

function attachDefaultListeners() {
  // The NaCl module embed is created within the listenerDiv
  var listenerDiv = document.getElementById('listener');
  // ...

  // register the handleMessage function as the message event handler.
  listenerDiv.addEventListener('message', handleMessage, true);
  // ...
}

Step 2: From example.js

// This function is called by common.js when a message is received from the
// NaCl module.
function handleMessage(message) {
  // In the example, we simply log the data that's received in the message.
  var logEl = document.getElementById('log');
  logEl.textContent += message.data;
}

// In the index.html we have set up the appropriate divs:
<body {attrs}>
  <!-- ... -->
  <div id="listener"></div>
  <div id="log"></div>
</body>

Step 3: From example.js

// From example.js, Step 3:
function moduleDidLoad() {
  // After the NaCl module has loaded, common.naclModule is a reference to the
  // NaCl module's <embed> element.
  //
  // postMessage sends a message to it.
  common.naclModule.postMessage('hello');
}

Native Client module

The C++ code in the Native Client module of the “Hello, World” example:

  1. Implements pp::Instance::HandleMessage() to handle messages sent by the JavaScript.
  2. Processes incoming messages. This example simply checks that JavaScript has sent a “hello” message and not some other message.
  3. Calls PostMessage() to send an acknowledgement back to the JavaScript code. The acknowledgement is a string in the form of a Var that the JavaScript code can process. In general, a pp::Var can be several JavaScript types, see the messaging system documentation.
class HelloTutorialInstance : public pp::Instance {
 public:
  // ...

  // === Step 1: Implement the HandleMessage function. ===
  virtual void HandleMessage(const pp::Var& var_message) {

    // === Step 2: Process the incoming message. ===
    // Ignore the message if it is not a string.
    if (!var_message.is_string())
      return;

    // Get the string message and compare it to "hello".
    std::string message = var_message.AsString();
    if (message == kHelloString) {
      // === Step 3: Send the reply. ===
      // If it matches, send our response back to JavaScript.
      pp::Var var_reply(kReplyString);
      PostMessage(var_reply);
    }
  }
};

Messaging in JavaScript code: More details.

This section describes in more detail the messaging system code in the JavaScript portion of the “Hello, World” example.

Setting up an event listener and handler

The following JavaScript code sets up an event listener for messages posted by the Native Client module. It then defines a message handler that simply logs the content of messages received from the module.

Setting up the ‘message’ handler on load

// From common.js

// Listen for the DOM content to be loaded. This event is fired when
// parsing of the page's document has finished.
document.addEventListener('DOMContentLoaded', function() {
  var body = document.body;
  // ...
  var loadFunction = common.domContentLoaded;
  // ... set up parameters ...
  loadFunction(...);
}

// This function is exported as common.domContentLoaded.
function domContentLoaded(...) {
  // ...
  if (common.naclModule == null) {
    // ...
    attachDefaultListeners();
    // initialize common.naclModule ...
  } else {
    // ...
  }
}

function attachDefaultListeners() {
  var listenerDiv = document.getElementById('listener');
  // ...
  listenerDiv.addEventListener('message', handleMessage, true);
  // ...
}

Implementing the handler

// From example.js
function handleMessage(message) {
  var logEl = document.getElementById('log');
  logEl.textContent += message.data;
}

Note that the handleMessage() function is handed a message_event containing data that you can display or manipulate in JavaScript. The “Hello, World” application simply logs this data to the log div.

Messaging in the Native Client module: More details.

This section describes in more detail the messaging system code in the Native Client module portion of the “Hello, World” example.

Implementing HandleMessage()

If you want the Native Client module to receive and handle messages from JavaScript, you need to implement a HandleMessage() function for your module’s pp::Instance class. TheHelloWorldInstance::HandleMessage() function examines the message posted from JavaScript. First it examines that the type of the pp::Var is indeed a string (not a double, etc.). It then interprets the data as a string with var_message.AsString(), and checks that the string matches kHelloString. After examining the message received from JavaScript, the code calls PostMessage() to send a reply message back to the JavaScript side.

namespace {

// The expected string sent by the JavaScript.
const char* const kHelloString = "hello";
// The string sent back to the JavaScript code upon receipt of a message
// containing "hello".
const char* const kReplyString = "hello from NaCl";

}  // namespace

class HelloTutorialInstance : public pp::Instance {
 public:
  // ...
  virtual void HandleMessage(const pp::Var& var_message) {
    // Ignore the message if it is not a string.
    if (!var_message.is_string())
      return;

    // Get the string message and compare it to "hello".
    std::string message = var_message.AsString();
    if (message == kHelloString) {
      // If it matches, send our response back to JavaScript.
      pp::Var var_reply(kReplyString);
      PostMessage(var_reply);
    }
  }
};

Implementing application-specific functions

While the “Hello, World” example is very simple, your Native Client module will likely include application-specific functions to perform custom tasks in response to messages. For example the application could be a compression and decompression service (two functions exported). The application could set up an application-specific convention that messages coming from JavaScript are colon-separated pairs of the form <command>:<data>. The Native Client module message handler can then split the incoming string along the : character to determine which command to execute. If the command is “compress”, then data to process is an uncompressed string. If the command is “uncompress”, then data to process is an already-compressed string. After processing the data asynchronously, the application then returns the result to JavaScript.

Sending messages back to the JavaScript code

The Native Client module sends messages back to the JavaScript code using PostMessage(). The Native Client module always returns its values in the form of a pp::Var that can be processed by the browser’s JavaScript. In this example, the message is posted at the end of the Native Client module’s HandleMessage() function:

PostMessage(var_reply);

Sending and receiving other pp::Var types

Besides strings, pp::Var can represent other types of JavaScript objects. For example, messages can be JavaScript objects. These richer types can make it easier to implement an application’s messaging protocol.

To send a dictionary from the NaCl module to JavaScript simply create a pp::VarDictionary and then call PostMessage with the dictionary.

pp::VarDictionary dictionary;
dictionary.Set(pp::Var("command"), pp::Var(next_command));
dictionary.Set(pp::Var("param_int"), pp::Var(123));
pp::VarArray an_array;
an_array.Set(0, pp::Var("string0"));
an_array.Set(1, pp::Var("string1"))
dictionary.Set(pp::Var("param_array"), an_array);
PostMessage(dictionary);

Here is how to create a similar object in JavaScript and send it to the NaCl module:

var dictionary = {
  command: next_command,
  param_int: 123,
  param_array: ['string0', 'string1']
}
nacl_module.postMessage(dictionary);

To receive a dictionary-typed message in the NaCl module, test that the message is truly a dictionary type, then convert the message with the pp::VarDictionary class.

virtual void HandleMessage(const pp::Var& var) {
  if (var.is_dictionary()) {
    pp::VarDictionary dictionary(var);
    // Use the dictionary
    pp::VarArray keys = dictionary.GetKeys();
    // ...
  } else {
    // ...
  }
}

 

 

 

 

 

 

Welcome to Native Client

To get the SDK and
installation instructions
visit the SDK Download page.

Native Client is a sandbox for running compiled C and C++ code in the browser efficiently and securely, independent of the user’s operating system. Portable Native Client extends that technology with architecture independence, letting developers compile their code once to run in any website and on any architecture with ahead-of-time (AOT) translation.

In short, Native Client brings the performance and low-level control of native code to modern web browsers, without sacrificing the security and portability of the web. Watch the video below for an overview of Native Client, including its goals, how it works, and how Portable Native Client lets developers run native compiled code on the web.

This site uses several examples of Native Client. For the best experience, consider downloading the latest version of Chrome. When you come back, be sure to check out our demos.

 

Two Types of Modules

Native Client comes in two flavors.

  • Portable Native Client (PNaCl): Pronounced ‘pinnacle’, PNaCl runs single, portable (pexe) executables and is available in most implementations of Chrome. A translator built into Chrome translates the pexe into native code for the client hardware. The entire module is translated before any code is executed rather than as the code is executed. PNaCl modules can be hosted from any web server.
  • Native Client (NaCl): Also called traditional or non-portable Native Client, NaCl runs architecture-dependent (nexe) modules, which are packaged into an application. At runtime, the browser decides which nexe to load based on the architecture of the client machine. Apps and Extensions installed via the Chrome Web Store (CWS) can use NaCl modules without additional prompting. NaCl apps can also be installed from chrome://extensions or the command-line during development, however, this is not a recommended distribution mechanism.

These flavors are described in more depth in PNaCl and NaCl

Hello World

To jump right in take the tutorial that walks you through a basic web application for Portable Native Client (PNaCl). This is a client-side application that uses HTML, JavaScript, and a Native Client module written in C++.

A Little More Advanced

If you’ve already got the basics down, you’re probably trying to get a real application ready for production. You’re buildingdebugging or ready to distribute.

Nuts and Bolts

You’ve been working on a Native Client module for a while now and you’ve run into an arcane problem. You may need to refer to the PNaCl Bitcode Reference or the Sandbox internals.

I Want to Know Everything

So, you like to read now and try later. Start with our Technical Overview

Send us comments and feedback on the native-client-discuss mailing list, or ask questions using Stack Overflow’s google-nativeclient tag.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Native Client 驱动程序是一种用于在 Chrome 浏览器中运行本机(原生)应用程序的技术。这种技术可以让开发者使用 C/C++、汇编等语言编写高效的本机应用程序,并将其与 Chrome 浏览器的 JavaScript 环境相结合。通过 Native Client 驱动程序,这些本机应用程序可以在 Chrome 浏览器中快速且安全地运行,而无需任何插件或其他外部软件。 Native Client 驱动程序的核心是一个名为 "NaCl" 的安全沙盒环境。该环境可以限制应用程序的访问权限,防止应用程序对计算机系统造成破坏或泄漏敏感信息。与其他本机应用程序不同的是,Native Client 驱动程序中的应用程序符合 Chrome 浏览器的所有安全要求,并与 JavaScript 代码一样安全。 另外,Native Client 驱动程序还提供了许多工具和 API,可帮助开发者创建高效的应用程序。这些工具包括优化器、调试器和性能分析器等。此外,Native Client 驱动程序还具有跨平台特性,可以在多个操作系统上运行,例如 Windows、Linux 和 macOS 等。 总之,Native Client 驱动程序是一项非常有用的技术,它可以让开发者编写高效且安全的本机应用程序,并使这些程序能够在 Chrome 浏览器中无缝运行。随着技术的不断发展,Native Client 驱动程序有望在更多场景下发挥作用,帮助开发者创造出更加优秀的应用程序。 ### 回答2: Native Client驱动程序是指一种软件技术,它能够通过在本地运行的方式,实现在Web浏览器中使用本地的CPU和GPU资源。该技术可以让网页应用程序获得更高的性能和更广泛的应用领域,而不需要依赖第三方插件或者下载本地应用程序。 Native Client技术最初由Google公司发起,目的是让Web应用程序的开发者能够更方便地利用客户端硬件资源,从而提高Web应用程序的性能和体验。该技术使用一种名为NaCl的字节码格式,在Web浏览器内部执行本地计算机代码。它通过内部沙盒机制保证安全性,并且可以使用现有的C/C++代码进行开发,与传统的Web技术无缝连接。 Native Client技术的出现,使得开发人员能够利用本地的CPU和GPU资源,使Web应用程序的性能和表现更加强大。它可以在Web平台下运行各种类型的应用程序,如游戏、工具、多媒体等,为Web应用程序赋予更多的实现可能性。但需要注意的是,在使用Native Client技术时,需要开发人员具备熟练的编程技术和对特定API的了解,以确保应用程序的正确性和安全性。 总的来说,Native Client驱动程序技术给Web开发带来了新的可能,开发人员可以通过这种技术获得更好的性能和更广泛的应用领域。但需要注意,Native Client技术还处于发展阶段,需要不断地进行完善和优化,以满足日益增长的Web应用程序需求。 ### 回答3: Native Client驱动程序是一种软件组件,它允许将本地代码(如C、C++、Rust等)编写的应用程序在Web浏览器中运行。它是Google Chrome浏览器中的一个重要特性,允许开发者使用原生的编程语言来创建浏览器应用程序和浏览器扩展,而无需使用像JavaScript这样的Web语言。 Native Client驱动程序的核心是一个虚拟机,它可以在浏览器中运行本机编译的代码。它提供了一组API和运行时库,开发者可以使用这些API和运行时库来实现各种功能。Native Client驱动程序还提供了将本机代码打包到浏览器中的工具,这使得开发者可以将应用程序部署到Web上,而无需用户进行任何额外的安装或配置。 然而,Native Client驱动程序也存在一些问题。首先,由于浏览器的安全限制,它不能直接访问计算机的本地资源,这使得一些高级应用程序很难实现。其次,它只在Chrome浏览器中可用,这可能限制了其在其他浏览器上的使用。最后,由于其依赖于本机代码编写,可能对开发者来说学习门槛较高。 总之,Native Client驱动程序是一个强大的Web技术,它为开发者提供了更多的工具和选项来创建更强大的浏览器应用程序。但它也有其限制和挑战,需要开发者权衡其使用价值和可行性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值