// Copyright (c) 2012 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 "ppapi/tests/test_utils.h" #include #include #include #if defined(_MSC_VER) #include #else #include #endif #include "ppapi/c/pp_errors.h" #include "ppapi/cpp/instance_handle.h" #include "ppapi/cpp/module.h" #include "ppapi/cpp/net_address.h" #include "ppapi/cpp/private/host_resolver_private.h" #include "ppapi/cpp/private/net_address_private.h" #include "ppapi/cpp/var.h" namespace { bool IsBigEndian() { union { uint32_t integer32; uint8_t integer8[4]; } data = { 0x01020304 }; return data.integer8[0] == 1; } } // namespace const int kActionTimeoutMs = 10000; const PPB_Testing_Private* GetTestingInterface() { static const PPB_Testing_Private* g_testing_interface = static_cast( pp::Module::Get()->GetBrowserInterface( PPB_TESTING_PRIVATE_INTERFACE)); return g_testing_interface; } std::string ReportError(const char* method, int32_t error) { char error_as_string[12]; sprintf(error_as_string, "%d", static_cast(error)); std::string result = method + std::string(" failed with error: ") + error_as_string; return result; } void PlatformSleep(int duration_ms) { #if defined(_MSC_VER) ::Sleep(duration_ms); #else usleep(duration_ms * 1000); #endif } bool GetLocalHostPort(PP_Instance instance, std::string* host, uint16_t* port) { if (!host || !port) return false; const PPB_Testing_Private* testing = GetTestingInterface(); if (!testing) return false; PP_URLComponents_Dev components; pp::Var pp_url(pp::PASS_REF, testing->GetDocumentURL(instance, &components)); if (!pp_url.is_string()) return false; std::string url = pp_url.AsString(); if (components.host.len < 0) return false; host->assign(url.substr(components.host.begin, components.host.len)); if (components.port.len <= 0) return false; int i = atoi(url.substr(components.port.begin, components.port.len).c_str()); if (i < 0 || i > 65535) return false; *port = static_cast(i); return true; } uint16_t ConvertFromNetEndian16(uint16_t x) { if (IsBigEndian()) return x; else return (x << 8) | (x >> 8); } uint16_t ConvertToNetEndian16(uint16_t x) { if (IsBigEndian()) return x; else return (x << 8) | (x >> 8); } bool EqualNetAddress(const pp::NetAddress& addr1, const pp::NetAddress& addr2) { if (addr1.GetFamily() == PP_NETADDRESS_FAMILY_UNSPECIFIED || addr2.GetFamily() == PP_NETADDRESS_FAMILY_UNSPECIFIED) { return false; } if (addr1.GetFamily() == PP_NETADDRESS_FAMILY_IPV4) { PP_NetAddress_IPv4 ipv4_addr1, ipv4_addr2; if (!addr1.DescribeAsIPv4Address(&ipv4_addr1) || !addr2.DescribeAsIPv4Address(&ipv4_addr2)) { return false; } return ipv4_addr1.port == ipv4_addr2.port && !memcmp(ipv4_addr1.addr, ipv4_addr2.addr, sizeof(ipv4_addr1.addr)); } else { PP_NetAddress_IPv6 ipv6_addr1, ipv6_addr2; if (!addr1.DescribeAsIPv6Address(&ipv6_addr1) || !addr2.DescribeAsIPv6Address(&ipv6_addr2)) { return false; } return ipv6_addr1.port == ipv6_addr2.port && !memcmp(ipv6_addr1.addr, ipv6_addr2.addr, sizeof(ipv6_addr1.addr)); } } bool ResolveHost(PP_Instance instance, const std::string& host, uint16_t port, pp::NetAddress* addr) { // TODO(yzshen): Change to use the public host resolver once it is supported. pp::InstanceHandle instance_handle(instance); pp::HostResolverPrivate host_resolver(instance_handle); PP_HostResolver_Private_Hint hint = { PP_NETADDRESSFAMILY_PRIVATE_UNSPECIFIED, 0 }; TestCompletionCallback callback(instance); callback.WaitForResult( host_resolver.Resolve(host, port, hint, callback.GetCallback())); PP_NetAddress_Private addr_private; if (callback.result() != PP_OK || host_resolver.GetSize() == 0 || !host_resolver.GetNetAddress(0, &addr_private)) { return false; } switch (pp::NetAddressPrivate::GetFamily(addr_private)) { case PP_NETADDRESSFAMILY_PRIVATE_IPV4: { PP_NetAddress_IPv4 ipv4_addr; ipv4_addr.port = ConvertToNetEndian16( pp::NetAddressPrivate::GetPort(addr_private)); if (!pp::NetAddressPrivate::GetAddress(addr_private, ipv4_addr.addr, sizeof(ipv4_addr.addr))) { return false; } *addr = pp::NetAddress(instance_handle, ipv4_addr); return true; } case PP_NETADDRESSFAMILY_PRIVATE_IPV6: { PP_NetAddress_IPv6 ipv6_addr; ipv6_addr.port = ConvertToNetEndian16( pp::NetAddressPrivate::GetPort(addr_private)); if (!pp::NetAddressPrivate::GetAddress(addr_private, ipv6_addr.addr, sizeof(ipv6_addr.addr))) { return false; } *addr = pp::NetAddress(instance_handle, ipv6_addr); return true; } default: { return false; } } } bool ReplacePort(PP_Instance instance, const pp::NetAddress& input_addr, uint16_t port, pp::NetAddress* output_addr) { switch (input_addr.GetFamily()) { case PP_NETADDRESS_FAMILY_IPV4: { PP_NetAddress_IPv4 ipv4_addr; if (!input_addr.DescribeAsIPv4Address(&ipv4_addr)) return false; ipv4_addr.port = ConvertToNetEndian16(port); *output_addr = pp::NetAddress(pp::InstanceHandle(instance), ipv4_addr); return true; } case PP_NETADDRESS_FAMILY_IPV6: { PP_NetAddress_IPv6 ipv6_addr; if (!input_addr.DescribeAsIPv6Address(&ipv6_addr)) return false; ipv6_addr.port = ConvertToNetEndian16(port); *output_addr = pp::NetAddress(pp::InstanceHandle(instance), ipv6_addr); return true; } default: { return false; } } } uint16_t GetPort(const pp::NetAddress& addr) { switch (addr.GetFamily()) { case PP_NETADDRESS_FAMILY_IPV4: { PP_NetAddress_IPv4 ipv4_addr; if (!addr.DescribeAsIPv4Address(&ipv4_addr)) return 0; return ConvertFromNetEndian16(ipv4_addr.port); } case PP_NETADDRESS_FAMILY_IPV6: { PP_NetAddress_IPv6 ipv6_addr; if (!addr.DescribeAsIPv6Address(&ipv6_addr)) return 0; return ConvertFromNetEndian16(ipv6_addr.port); } default: { return 0; } } } void NestedEvent::Wait() { PP_DCHECK(pp::Module::Get()->core()->IsMainThread()); // Don't allow nesting more than once; it doesn't work with the code as-is, // and probably is a bad idea most of the time anyway. PP_DCHECK(!waiting_); if (signalled_) return; waiting_ = true; while (!signalled_) GetTestingInterface()->RunMessageLoop(instance_); waiting_ = false; } void NestedEvent::Signal() { if (pp::Module::Get()->core()->IsMainThread()) SignalOnMainThread(); else PostSignal(0); } void NestedEvent::PostSignal(int32_t wait_ms) { pp::Module::Get()->core()->CallOnMainThread( wait_ms, pp::CompletionCallback(&SignalThunk, this), 0); } void NestedEvent::Reset() { PP_DCHECK(pp::Module::Get()->core()->IsMainThread()); // It doesn't make sense to reset when we're still waiting. PP_DCHECK(!waiting_); signalled_ = false; } void NestedEvent::SignalOnMainThread() { PP_DCHECK(pp::Module::Get()->core()->IsMainThread()); signalled_ = true; if (waiting_) GetTestingInterface()->QuitMessageLoop(instance_); } void NestedEvent::SignalThunk(void* event, int32_t /* result */) { static_cast(event)->SignalOnMainThread(); } TestCompletionCallback::TestCompletionCallback(PP_Instance instance) : wait_for_result_called_(false), have_result_(false), result_(PP_OK_COMPLETIONPENDING), // TODO(dmichael): The default should probably be PP_REQUIRED, but this is // what the tests currently expect. callback_type_(PP_OPTIONAL), post_quit_task_(false), instance_(instance), delegate_(NULL) { } TestCompletionCallback::TestCompletionCallback(PP_Instance instance, bool force_async) : wait_for_result_called_(false), have_result_(false), result_(PP_OK_COMPLETIONPENDING), callback_type_(force_async ? PP_REQUIRED : PP_OPTIONAL), post_quit_task_(false), instance_(instance), delegate_(NULL) { } TestCompletionCallback::TestCompletionCallback(PP_Instance instance, CallbackType callback_type) : wait_for_result_called_(false), have_result_(false), result_(PP_OK_COMPLETIONPENDING), callback_type_(callback_type), post_quit_task_(false), instance_(instance), delegate_(NULL) { } void TestCompletionCallback::WaitForResult(int32_t result) { PP_DCHECK(!wait_for_result_called_); wait_for_result_called_ = true; errors_.clear(); if (result == PP_OK_COMPLETIONPENDING) { if (!have_result_) { post_quit_task_ = true; RunMessageLoop(); } if (callback_type_ == PP_BLOCKING) { errors_.assign( ReportError("TestCompletionCallback: Call did not run synchronously " "when passed a blocking completion callback!", result_)); return; } } else { result_ = result; have_result_ = true; if (callback_type_ == PP_REQUIRED) { errors_.assign( ReportError("TestCompletionCallback: Call ran synchronously when " "passed a required completion callback!", result_)); return; } } PP_DCHECK(have_result_ == true); } void TestCompletionCallback::WaitForAbortResult(int32_t result) { WaitForResult(result); int32_t final_result = result_; if (result == PP_OK_COMPLETIONPENDING) { if (final_result != PP_ERROR_ABORTED) { errors_.assign( ReportError("TestCompletionCallback: Expected PP_ERROR_ABORTED or " "PP_OK. Ran asynchronously.", final_result)); return; } } else if (result < PP_OK) { errors_.assign( ReportError("TestCompletionCallback: Expected PP_ERROR_ABORTED or " "non-error response. Ran synchronously.", result)); return; } } pp::CompletionCallback TestCompletionCallback::GetCallback() { Reset(); int32_t flags = 0; if (callback_type_ == PP_BLOCKING) return pp::CompletionCallback(); else if (callback_type_ == PP_OPTIONAL) flags = PP_COMPLETIONCALLBACK_FLAG_OPTIONAL; target_loop_ = pp::MessageLoop::GetCurrent(); return pp::CompletionCallback(&TestCompletionCallback::Handler, const_cast(this), flags); } void TestCompletionCallback::Reset() { wait_for_result_called_ = false; result_ = PP_OK_COMPLETIONPENDING; have_result_ = false; post_quit_task_ = false; delegate_ = NULL; errors_.clear(); } // static void TestCompletionCallback::Handler(void* user_data, int32_t result) { TestCompletionCallback* callback = static_cast(user_data); // If this check fails, it means that the callback was invoked twice or that // the PPAPI call completed synchronously, but also ran the callback. PP_DCHECK(!callback->have_result_); callback->result_ = result; callback->have_result_ = true; if (callback->delegate_) callback->delegate_->OnCallback(user_data, result); if (callback->post_quit_task_) { callback->post_quit_task_ = false; callback->QuitMessageLoop(); } if (callback->target_loop_ != pp::MessageLoop::GetCurrent()) { // Note, in-process, loop_ and GetCurrent() will both be NULL, so should // still be equal. callback->errors_.assign( ReportError("TestCompletionCallback: Callback ran on the wrong message " "loop!", result)); } } void TestCompletionCallback::RunMessageLoop() { pp::MessageLoop loop(pp::MessageLoop::GetCurrent()); // If we don't have a message loop, we're probably running in process, where // PPB_MessageLoop is not supported. Just use the Testing message loop. if (loop.is_null() || loop == pp::MessageLoop::GetForMainThread()) GetTestingInterface()->RunMessageLoop(instance_); else loop.Run(); } void TestCompletionCallback::QuitMessageLoop() { pp::MessageLoop loop(pp::MessageLoop::GetCurrent()); // If we don't have a message loop, we're probably running in process, where // PPB_MessageLoop is not supported. Just use the Testing message loop. if (loop.is_null() || loop == pp::MessageLoop::GetForMainThread()) { GetTestingInterface()->QuitMessageLoop(instance_); } else { const bool should_quit = false; loop.PostQuit(should_quit); } }