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..85b80258b91414538b459dbfe04eac572ce212dd |
--- /dev/null |
+++ b/content/browser/android/app_web_message_port.cc |
@@ -0,0 +1,265 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
jam
2017/01/23 18:08:56
I don't remember who worked on the initial impleme
|
+// 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 uint8_t kStringUCharTag = 'c'; |
+ |
+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()) |
jbroman
2017/01/24 22:41:51
nit: std::basic_string::resize promises to zero-in
darin (slow to review)
2017/02/04 01:43:02
Thanks
|
+ 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); |
jbroman
2017/01/24 22:41:51
nit: shifts of >=32 bits are undefined behavior.
|
+ 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)) |
+ return false; |
+ |
+ switch (tag) { |
jbroman
2017/01/24 22:41:51
You also need to be able to handle the padding tag
|
+ case kStringTag: { |
+ 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); |
+ } |
+ case kStringUCharTag: { |
+ uint32_t num_uchar_bytes; |
+ if (!ReadUint32(&ptr, end, &num_uchar_bytes)) |
+ return false; |
+ const base::char16* uchar_start = |
+ reinterpret_cast<const base::char16*>(ptr); |
+ result->assign(uchar_start, num_uchar_bytes / 2); |
+ return true; |
+ } |
+ } |
+ |
+ LOG(WARNING) << "Unexpected tag: " << tag; |
+ return false; |
+} |
+ |
+} // 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 |