| Index: content/browser/android/app_web_message_port.cc
|
| diff --git a/content/browser/android/app_web_message_port.cc b/content/browser/android/app_web_message_port.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..5bc71b21dca324426311ef227fb1f842eb1f0e79
|
| --- /dev/null
|
| +++ b/content/browser/android/app_web_message_port.cc
|
| @@ -0,0 +1,249 @@
|
| +// Copyright 2016 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 "content/browser/android/app_web_message_port.h"
|
| +
|
| +#include "base/android/jni_android.h"
|
| +#include "base/android/jni_string.h"
|
| +#include "base/bind.h"
|
| +#include "base/logging.h"
|
| +#include "base/strings/utf_string_conversions.h"
|
| +#include "jni/AppWebMessagePort_jni.h"
|
| +
|
| +namespace content {
|
| +namespace {
|
| +
|
| +// To avoid setting up a V8 context here, we just hard code the format for
|
| +// string type messages. /me ducks!
|
| +
|
| +const uint32_t kVarIntShift = 7;
|
| +const uint32_t kVarIntMask = (1 << kVarIntShift) - 1;
|
| +
|
| +const uint8_t kVersionTag = 0xFF;
|
| +const uint8_t kStringTag = 'S';
|
| +
|
| +const uint32_t kVersion = 9;
|
| +
|
| +void WriteUint8(uint8_t value, std::vector<uint8_t>* buffer) {
|
| + buffer->push_back(value);
|
| +}
|
| +
|
| +void WriteUint32(uint32_t value, std::vector<uint8_t>* buffer) {
|
| + for (;;) {
|
| + uint8_t b = (value & kVarIntMask);
|
| + value >>= kVarIntShift;
|
| + if (!value) {
|
| + WriteUint8(b, buffer);
|
| + break;
|
| + }
|
| + WriteUint8(b | (1 << kVarIntShift), buffer);
|
| + }
|
| +}
|
| +
|
| +void WriteBytes(const char* bytes, size_t num_bytes,
|
| + std::vector<uint8_t>* buffer) {
|
| + buffer->insert(buffer->end(), bytes, bytes + num_bytes);
|
| +}
|
| +
|
| +base::string16 EncodeStringMessage(const base::string16& data) {
|
| + std::string data_utf8(base::UTF16ToUTF8(data));
|
| +
|
| + std::vector<uint8_t> buffer;
|
| + WriteUint8(kVersionTag, &buffer);
|
| + WriteUint32(kVersion, &buffer);
|
| +
|
| + WriteUint8(kStringTag, &buffer);
|
| + WriteUint32(static_cast<uint32_t>(data_utf8.size()), &buffer);
|
| + WriteBytes(data_utf8.data(), data_utf8.size(), &buffer);
|
| +
|
| + base::string16 result;
|
| + size_t result_num_bytes = (buffer.size() + 1) & ~1;
|
| + result.resize(result_num_bytes / 2);
|
| + uint8_t* destination = reinterpret_cast<uint8_t*>(&result[0]);
|
| + memcpy(destination, &buffer[0], buffer.size());
|
| + if (result_num_bytes > buffer.size())
|
| + destination[result_num_bytes - 1] = '\0';
|
| +
|
| + return result;
|
| +}
|
| +
|
| +bool ReadUint8(const uint8_t** ptr, const uint8_t* end, uint8_t* value) {
|
| + if (*ptr >= end)
|
| + return false;
|
| + *value = *(*ptr)++;
|
| + return true;
|
| +}
|
| +
|
| +bool ReadUint32(const uint8_t** ptr, const uint8_t* end, uint32_t* value) {
|
| + *value = 0;
|
| + uint8_t current_byte;
|
| + int shift = 0;
|
| + do {
|
| + if (*ptr >= end)
|
| + return false;
|
| + current_byte = *(*ptr)++;
|
| + *value |= (static_cast<uint32_t>(current_byte & kVarIntMask) << shift);
|
| + shift += kVarIntShift;
|
| + } while (current_byte & (1 << kVarIntShift));
|
| + return true;
|
| +}
|
| +
|
| +bool DecodeStringMessage(const base::string16& encoded_data,
|
| + base::string16* result) {
|
| + size_t num_bytes = encoded_data.size() * 2;
|
| +
|
| + const uint8_t* ptr = reinterpret_cast<const uint8_t*>(&encoded_data[0]);
|
| + const uint8_t* end = ptr + num_bytes;
|
| +
|
| + uint8_t tag;
|
| + if (!ReadUint8(&ptr, end, &tag) || tag != kVersionTag)
|
| + return false;
|
| +
|
| + uint32_t version;
|
| + if (!ReadUint32(&ptr, end, &version))
|
| + return false;
|
| +
|
| + if (!ReadUint8(&ptr, end, &tag) || tag != kStringTag)
|
| + return false;
|
| +
|
| + uint32_t num_utf8_bytes;
|
| + if (!ReadUint32(&ptr, end, &num_utf8_bytes))
|
| + return false;
|
| +
|
| + const char* utf8_start = reinterpret_cast<const char*>(ptr);
|
| + return base::UTF8ToUTF16(utf8_start, num_utf8_bytes, result);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// static
|
| +void AppWebMessagePort::CreateAndBindToJavaObject(
|
| + JNIEnv* env,
|
| + mojo::ScopedMessagePipeHandle handle,
|
| + const base::android::JavaRef<jobject>& jobject) {
|
| + AppWebMessagePort* instance =
|
| + new AppWebMessagePort(env, std::move(handle), jobject);
|
| + Java_AppWebMessagePort_setNativeAppWebMessagePort(
|
| + env, jobject, reinterpret_cast<jlong>(instance));
|
| +}
|
| +
|
| +// static
|
| +std::vector<MessagePort> AppWebMessagePort::UnwrapJavaArray(
|
| + JNIEnv* env,
|
| + const base::android::JavaRef<jobjectArray>& jports) {
|
| + std::vector<MessagePort> ports;
|
| + if (!env->IsSameObject(jports.obj(), nullptr)) {
|
| + jsize num_ports = env->GetArrayLength(jports.obj());
|
| + for (jsize i = 0; i < num_ports; ++i) {
|
| + base::android::ScopedJavaLocalRef<jobject> jport(
|
| + env, env->GetObjectArrayElement(jports.obj(), i));
|
| + jlong native_port =
|
| + Java_AppWebMessagePort_releaseNativePortForTransfer(env, jport);
|
| + // Uninitialized ports should be trapper earlier at the Java layer.
|
| + DCHECK(native_port != -1);
|
| + AppWebMessagePort* instance =
|
| + reinterpret_cast<AppWebMessagePort*>(native_port);
|
| + ports.push_back(instance->port_);
|
| + }
|
| + }
|
| + return ports;
|
| +}
|
| +
|
| +void AppWebMessagePort::CloseMessagePort(
|
| + JNIEnv* env,
|
| + const base::android::JavaParamRef<jobject>& jcaller) {
|
| + // Explicitly reset the port here to ensure that OnMessagesAvailable has
|
| + // finished before we destroy this.
|
| + port_ = MessagePort();
|
| +
|
| + delete this;
|
| +}
|
| +
|
| +void AppWebMessagePort::PostMessage(
|
| + JNIEnv* env,
|
| + const base::android::JavaParamRef<jobject>& jcaller,
|
| + const base::android::JavaParamRef<jstring>& jmessage,
|
| + const base::android::JavaParamRef<jobjectArray>& jports) {
|
| + port_.PostMessage(
|
| + EncodeStringMessage(base::android::ConvertJavaStringToUTF16(jmessage)),
|
| + UnwrapJavaArray(env, jports));
|
| +}
|
| +
|
| +void AppWebMessagePort::DispatchReceivedMessages(
|
| + JNIEnv* env,
|
| + const base::android::JavaParamRef<jobject>& jcaller) {
|
| + jmethodID app_web_message_port_constructor =
|
| + base::android::MethodID::Get<base::android::MethodID::TYPE_INSTANCE>(
|
| + env, AppWebMessagePort_clazz(env), "<init>", "()V");
|
| +
|
| + // Consume all of the available messages.
|
| + // TODO(darin): Consider breaking this up across multiple PostTask calls.
|
| + for (;;) {
|
| + base::string16 encoded_message;
|
| + std::vector<MessagePort> ports;
|
| + if (!port_.GetMessage(&encoded_message, &ports))
|
| + return;
|
| +
|
| + base::string16 message;
|
| + if (!DecodeStringMessage(encoded_message, &message))
|
| + return;
|
| +
|
| + base::android::ScopedJavaLocalRef<jstring> jmessage =
|
| + base::android::ConvertUTF16ToJavaString(env, message);
|
| +
|
| + base::android::ScopedJavaLocalRef<jobjectArray> jports;
|
| + if (ports.size() > 0) {
|
| + jports = base::android::ScopedJavaLocalRef<jobjectArray>(
|
| + env,
|
| + env->NewObjectArray(ports.size(),
|
| + AppWebMessagePort_clazz(env),
|
| + nullptr));
|
| +
|
| + // Instantiate the Java and C++ wrappers for the transferred ports.
|
| + for (size_t i = 0; i < ports.size(); ++i) {
|
| + base::android::ScopedJavaLocalRef<jobject> jport(
|
| + env,
|
| + env->NewObject(AppWebMessagePort_clazz(env),
|
| + app_web_message_port_constructor));
|
| + CreateAndBindToJavaObject(env, ports[i].ReleaseHandle(), jport);
|
| +
|
| + env->SetObjectArrayElement(jports.obj(), i, jport.obj());
|
| + }
|
| + }
|
| +
|
| + Java_AppWebMessagePort_onReceivedMessage(
|
| + env, java_ref_.get(env), jmessage, jports);
|
| + }
|
| +}
|
| +
|
| +void AppWebMessagePort::StartReceivingMessages(
|
| + JNIEnv* env,
|
| + const base::android::JavaParamRef<jobject>& jcaller) {
|
| + port_.SetCallback(
|
| + base::Bind(&AppWebMessagePort::OnMessagesAvailable,
|
| + base::Unretained(this)));
|
| +}
|
| +
|
| +AppWebMessagePort::AppWebMessagePort(
|
| + JNIEnv* env,
|
| + mojo::ScopedMessagePipeHandle handle,
|
| + const base::android::JavaRef<jobject>& jobject)
|
| + : port_(std::move(handle)),
|
| + java_ref_(env, jobject) {
|
| +}
|
| +
|
| +AppWebMessagePort::~AppWebMessagePort() {
|
| +}
|
| +
|
| +void AppWebMessagePort::OnMessagesAvailable() {
|
| + // Called on the IO thread.
|
| + JNIEnv* env = base::android::AttachCurrentThread();
|
| + Java_AppWebMessagePort_onMessagesAvailable(env, java_ref_.get(env));
|
| +}
|
| +
|
| +bool RegisterAppWebMessagePort(JNIEnv* env) {
|
| + return RegisterNativesImpl(env);
|
| +}
|
| +
|
| +} // namespace content
|
|
|