summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/DEPS1
-rw-r--r--chrome/browser/browser_resources.grd1
-rw-r--r--chrome/browser/copresence/OWNERS3
-rw-r--r--chrome/browser/copresence/chrome_whispernet_client.cc200
-rw-r--r--chrome/browser/copresence/chrome_whispernet_client.h84
-rw-r--r--chrome/browser/copresence/chrome_whispernet_client_browsertest.cc179
-rw-r--r--chrome/browser/extensions/api/copresence_private/copresence_private_api.cc105
-rw-r--r--chrome/browser/extensions/api/copresence_private/copresence_private_api.h69
-rw-r--r--chrome/browser/resources/component_extension_resources.grd11
-rw-r--r--chrome/browser/resources/whispernet_proxy/OWNERS3
-rw-r--r--chrome/browser/resources/whispernet_proxy/background.html11
-rw-r--r--chrome/browser/resources/whispernet_proxy/js/init.js104
-rw-r--r--chrome/browser/resources/whispernet_proxy/js/nacl.js102
-rw-r--r--chrome/browser/resources/whispernet_proxy/js/wrapper.js224
-rw-r--r--chrome/browser/resources/whispernet_proxy/manifest.json13
-rw-r--r--chrome/chrome_browser_extensions.gypi4
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/common/extensions/api/_api_features.json4
-rw-r--r--chrome/common/extensions/api/_permission_features.json8
-rw-r--r--chrome/common/extensions/api/api.gyp1
-rw-r--r--chrome/common/extensions/api/copresence_private.idl49
-rw-r--r--chrome/common/extensions/permissions/chrome_api_permissions.cc1
-rw-r--r--chrome/common/extensions/permissions/permission_set_unittest.cc1
23 files changed, 1179 insertions, 0 deletions
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 797e41f..6b4f26e 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -20,6 +20,7 @@ include_rules = [
"+components/cdm/browser",
"+components/cloud_devices/common",
"+components/content_settings",
+ "+components/copresence",
"+components/data_reduction_proxy",
"+components/dom_distiller",
"+components/domain_reliability",
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index d2b7ed8..0e975f7 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -436,6 +436,7 @@
<include name="IDR_HANGUL_MANIFEST" file="resources\chromeos\input_method\hangul_manifest.json" type="BINDATA" />
<include name="IDR_BRAILLE_MANIFEST" file="resources\chromeos\braille_ime\manifest.json" type="BINDATA" />
</if>
+ <include name="IDR_WHISPERNET_PROXY_MANIFEST" file="resources\whispernet_proxy\manifest.json" type="BINDATA" />
</includes>
</release>
</grit>
diff --git a/chrome/browser/copresence/OWNERS b/chrome/browser/copresence/OWNERS
new file mode 100644
index 0000000..2bc8d07
--- /dev/null
+++ b/chrome/browser/copresence/OWNERS
@@ -0,0 +1,3 @@
+rkc@chromium.org
+ckehoe@chromium.org
+xiyuan@chromium.org
diff --git a/chrome/browser/copresence/chrome_whispernet_client.cc b/chrome/browser/copresence/chrome_whispernet_client.cc
new file mode 100644
index 0000000..3fa8bbb
--- /dev/null
+++ b/chrome/browser/copresence/chrome_whispernet_client.cc
@@ -0,0 +1,200 @@
+// Copyright 2014 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/browser/copresence/chrome_whispernet_client.h"
+
+#include "chrome/browser/extensions/api/copresence_private/copresence_private_api.h"
+#include "chrome/browser/extensions/component_loader.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/common/extensions/api/copresence_private.h"
+#include "content/public/browser/browser_context.h"
+#include "extensions/browser/event_router.h"
+#include "extensions/browser/extension_system.h"
+#include "grit/browser_resources.h"
+
+namespace copresence {
+
+// Once the CL for the copresence component lands, these constants will be
+// need to be picked up from components/copresence/copresence_constants.h
+const int kDefaultRepetitions = 5;
+const float kDefaultSampleRate = 48000.0;
+const int kDefaultBitsPerSample = 16;
+const float kDefaultCarrierFrequency = 18500.0;
+const int kDefaultChannels = 2;
+
+} // namespace copresence
+
+// static
+const char ChromeWhispernetClient::kWhispernetProxyExtensionId[] =
+ "bpfmnplchembfbdgieamdodgaencleal";
+
+// Public:
+
+ChromeWhispernetClient::ChromeWhispernetClient(
+ content::BrowserContext* browser_context)
+ : browser_context_(browser_context), extension_loaded_(false) {
+}
+
+ChromeWhispernetClient::~ChromeWhispernetClient() {
+}
+
+void ChromeWhispernetClient::Initialize(const SuccessCallback& init_callback) {
+ DVLOG(3) << "Initializing whispernet proxy client.";
+ init_callback_ = init_callback;
+
+ extensions::ExtensionSystem* es =
+ extensions::ExtensionSystem::Get(browser_context_);
+ DCHECK(es);
+ ExtensionService* service = es->extension_service();
+ DCHECK(service);
+ extensions::ComponentLoader* loader = service->component_loader();
+ DCHECK(loader);
+
+ // This callback is cancelled in Shutdown().
+ extension_loaded_callback_ = base::Bind(
+ &ChromeWhispernetClient::OnExtensionLoaded, base::Unretained(this));
+
+ if (!loader->Exists(kWhispernetProxyExtensionId)) {
+ DVLOG(3) << "Loading Whispernet proxy.";
+ loader->Add(IDR_WHISPERNET_PROXY_MANIFEST,
+ base::FilePath(FILE_PATH_LITERAL("whispernet_proxy")));
+ } else {
+ init_callback_.Run(true);
+ }
+}
+
+void ChromeWhispernetClient::Shutdown() {
+ extension_loaded_callback_.Reset();
+ init_callback_.Reset();
+ tokens_callback_.Reset();
+ samples_callback_.Reset();
+ db_callback_.Reset();
+}
+
+// Fire an event to request a token encode.
+void ChromeWhispernetClient::EncodeToken(const std::string& token) {
+ DCHECK(extension_loaded_);
+ DCHECK(browser_context_);
+ DCHECK(extensions::EventRouter::Get(browser_context_));
+
+ scoped_ptr<extensions::Event> event(new extensions::Event(
+ extensions::api::copresence_private::OnEncodeTokenRequest::kEventName,
+ extensions::api::copresence_private::OnEncodeTokenRequest::Create(token),
+ browser_context_));
+
+ extensions::EventRouter::Get(browser_context_)
+ ->DispatchEventToExtension(kWhispernetProxyExtensionId, event.Pass());
+}
+
+// Fire an event to request a decode for the given samples.
+void ChromeWhispernetClient::DecodeSamples(const std::string& samples) {
+ DCHECK(extension_loaded_);
+ DCHECK(browser_context_);
+ DCHECK(extensions::EventRouter::Get(browser_context_));
+
+ scoped_ptr<extensions::Event> event(new extensions::Event(
+ extensions::api::copresence_private::OnDecodeSamplesRequest::kEventName,
+ extensions::api::copresence_private::OnDecodeSamplesRequest::Create(
+ samples),
+ browser_context_));
+
+ extensions::EventRouter::Get(browser_context_)
+ ->DispatchEventToExtension(kWhispernetProxyExtensionId, event.Pass());
+}
+
+void ChromeWhispernetClient::DetectBroadcast() {
+ DCHECK(extension_loaded_);
+ DCHECK(browser_context_);
+ DCHECK(extensions::EventRouter::Get(browser_context_));
+
+ scoped_ptr<extensions::Event> event(new extensions::Event(
+ extensions::api::copresence_private::OnDetectBroadcastRequest::kEventName,
+ make_scoped_ptr(new base::ListValue()),
+ browser_context_));
+
+ extensions::EventRouter::Get(browser_context_)
+ ->DispatchEventToExtension(kWhispernetProxyExtensionId, event.Pass());
+}
+
+void ChromeWhispernetClient::RegisterTokensCallback(
+ const TokensCallback& tokens_callback) {
+ tokens_callback_ = tokens_callback;
+}
+
+void ChromeWhispernetClient::RegisterSamplesCallback(
+ const SamplesCallback& samples_callback) {
+ samples_callback_ = samples_callback;
+}
+
+void ChromeWhispernetClient::RegisterDetectBroadcastCallback(
+ const SuccessCallback& db_callback) {
+ db_callback_ = db_callback;
+}
+
+ChromeWhispernetClient::TokensCallback
+ChromeWhispernetClient::GetTokensCallback() {
+ return tokens_callback_;
+}
+
+ChromeWhispernetClient::SamplesCallback
+ChromeWhispernetClient::GetSamplesCallback() {
+ return samples_callback_;
+}
+
+ChromeWhispernetClient::SuccessCallback
+ChromeWhispernetClient::GetDetectBroadcastCallback() {
+ return db_callback_;
+}
+
+ChromeWhispernetClient::SuccessCallback
+ChromeWhispernetClient::GetInitializedCallback() {
+ return extension_loaded_callback_;
+}
+
+// Private:
+
+// Fire an event to initialize whispernet with the given parameters.
+void ChromeWhispernetClient::InitializeWhispernet(
+ const extensions::api::copresence_private::AudioParameters& params) {
+ DCHECK(browser_context_);
+ DCHECK(extensions::EventRouter::Get(browser_context_));
+
+ scoped_ptr<extensions::Event> event(new extensions::Event(
+ extensions::api::copresence_private::OnInitialize::kEventName,
+ extensions::api::copresence_private::OnInitialize::Create(params),
+ browser_context_));
+
+ extensions::EventRouter::Get(browser_context_)
+ ->DispatchEventToExtension(kWhispernetProxyExtensionId, event.Pass());
+}
+
+void ChromeWhispernetClient::OnExtensionLoaded(bool success) {
+ if (extension_loaded_) {
+ if (!init_callback_.is_null())
+ init_callback_.Run(success);
+ return;
+ }
+
+ // Our extension just got loaded, initialize whispernet.
+ extension_loaded_ = true;
+
+ // This will fire another OnExtensionLoaded call once the initialization is
+ // done, which means we've initialized for realz, hence call the init
+ // callback.
+
+ // At this point, we have the same parameters for record and play. This
+ // may change later though (ongoing discussion with research).
+ extensions::api::copresence_private::AudioParameters params;
+ params.play.sample_rate = copresence::kDefaultSampleRate;
+ params.play.bits_per_sample = copresence::kDefaultBitsPerSample;
+ params.play.carrier_frequency = copresence::kDefaultCarrierFrequency;
+ params.play.repetitions = copresence::kDefaultRepetitions;
+
+ params.record.sample_rate = copresence::kDefaultSampleRate;
+ params.record.bits_per_sample = copresence::kDefaultBitsPerSample;
+ params.record.carrier_frequency = copresence::kDefaultCarrierFrequency;
+ params.record.channels = copresence::kDefaultChannels;
+
+ InitializeWhispernet(params);
+}
diff --git a/chrome/browser/copresence/chrome_whispernet_client.h b/chrome/browser/copresence/chrome_whispernet_client.h
new file mode 100644
index 0000000..a644a1a
--- /dev/null
+++ b/chrome/browser/copresence/chrome_whispernet_client.h
@@ -0,0 +1,84 @@
+// Copyright 2014 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.
+
+#ifndef CHROME_BROWSER_COPRESENCE_CHROME_WHISPERNET_CLIENT_H_
+#define CHROME_BROWSER_COPRESENCE_CHROME_WHISPERNET_CLIENT_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "components/copresence/public/whispernet_client.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace extensions {
+namespace api {
+namespace copresence_private {
+struct AudioParameters;
+}
+}
+}
+
+namespace media {
+class AudioBusRefCounted;
+}
+
+// This class is responsible for communication with our ledger_proxy extension
+// that talks to the whispernet audio library.
+class ChromeWhispernetClient : public copresence::WhispernetClient {
+ public:
+ // The browser context needs to outlive this class.
+ explicit ChromeWhispernetClient(content::BrowserContext* browser_context);
+ virtual ~ChromeWhispernetClient();
+
+ // WhispernetClient overrides:
+ virtual void Initialize(const SuccessCallback& init_callback) OVERRIDE;
+ virtual void Shutdown() OVERRIDE;
+
+ virtual void EncodeToken(const std::string& token) OVERRIDE;
+ virtual void DecodeSamples(const std::string& samples) OVERRIDE;
+ virtual void DetectBroadcast() OVERRIDE;
+
+ virtual void RegisterTokensCallback(
+ const TokensCallback& tokens_callback) OVERRIDE;
+ virtual void RegisterSamplesCallback(
+ const SamplesCallback& samples_callback) OVERRIDE;
+ virtual void RegisterDetectBroadcastCallback(
+ const SuccessCallback& db_callback) OVERRIDE;
+
+ virtual TokensCallback GetTokensCallback() OVERRIDE;
+ virtual SamplesCallback GetSamplesCallback() OVERRIDE;
+ virtual SuccessCallback GetDetectBroadcastCallback() OVERRIDE;
+ virtual SuccessCallback GetInitializedCallback() OVERRIDE;
+
+ static const char kWhispernetProxyExtensionId[];
+
+ private:
+ // Fire an event to initialize whispernet with the given parameters.
+ void InitializeWhispernet(
+ const extensions::api::copresence_private::AudioParameters& params);
+
+ // This gets called twice; once when the proxy extension loads, the second
+ // time when we have initialized the proxy extension's encoder and decoder.
+ void OnExtensionLoaded(bool success);
+
+ content::BrowserContext* browser_context_;
+
+ SuccessCallback extension_loaded_callback_;
+ SuccessCallback init_callback_;
+
+ TokensCallback tokens_callback_;
+ SamplesCallback samples_callback_;
+ SuccessCallback db_callback_;
+
+ bool extension_loaded_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeWhispernetClient);
+};
+
+#endif // CHROME_BROWSER_COPRESENCE_CHROME_WHISPERNET_CLIENT_H_
diff --git a/chrome/browser/copresence/chrome_whispernet_client_browsertest.cc b/chrome/browser/copresence/chrome_whispernet_client_browsertest.cc
new file mode 100644
index 0000000..469a42d
--- /dev/null
+++ b/chrome/browser/copresence/chrome_whispernet_client_browsertest.cc
@@ -0,0 +1,179 @@
+// Copyright 2014 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/browser/copresence/chrome_whispernet_client.h"
+
+#include <cstdlib>
+#include <string>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/run_loop.h"
+#include "base/stl_util.h"
+#include "chrome/browser/extensions/api/copresence_private/copresence_private_api.h"
+#include "chrome/browser/extensions/extension_browsertest.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "media/base/audio_bus.h"
+
+namespace {
+
+// Copied from src/components/copresence/mediums/audio/audio_recorder.cc
+std::string AudioBusToString(scoped_refptr<media::AudioBusRefCounted> source) {
+ std::string buffer;
+ buffer.resize(source->frames() * source->channels() * sizeof(float));
+ float* buffer_view = reinterpret_cast<float*>(string_as_array(&buffer));
+
+ const int channels = source->channels();
+ for (int ch = 0; ch < channels; ++ch) {
+ for (int si = 0, di = ch; si < source->frames(); ++si, di += channels)
+ buffer_view[di] = source->channel(ch)[si];
+ }
+
+ return buffer;
+}
+
+} // namespace
+
+class ChromeWhispernetClientTest : public ExtensionBrowserTest {
+ public:
+ ChromeWhispernetClientTest() : initialized_(false) {}
+
+ virtual ~ChromeWhispernetClientTest() {
+ if (client_)
+ extensions::SetWhispernetClientForTesting(NULL);
+ }
+
+ void InitializeWhispernet() {
+ run_loop_.reset(new base::RunLoop());
+ client_.reset(new ChromeWhispernetClient(browser()->profile()));
+ extensions::SetWhispernetClientForTesting(client_.get());
+
+ client_->Initialize(base::Bind(&ChromeWhispernetClientTest::InitCallback,
+ base::Unretained(this)));
+ run_loop_->Run();
+
+ EXPECT_TRUE(initialized_);
+ }
+
+ void EncodeTokenAndSaveSamples() {
+ ASSERT_TRUE(client_);
+
+ // This is the base64 encoding for 000000.
+ const std::string kZeroToken = "MDAwMDAw";
+
+ run_loop_.reset(new base::RunLoop());
+ client_->RegisterSamplesCallback(base::Bind(
+ &ChromeWhispernetClientTest::SamplesCallback, base::Unretained(this)));
+ expected_token_ = kZeroToken;
+
+ client_->EncodeToken(kZeroToken);
+ run_loop_->Run();
+
+ EXPECT_GT(saved_samples_->frames(), 0);
+ }
+
+ void DecodeSamplesAndVerifyToken() {
+ ASSERT_TRUE(client_);
+
+ const std::string kZeroToken = "MDAwMDAw";
+
+ run_loop_.reset(new base::RunLoop());
+ client_->RegisterTokensCallback(base::Bind(
+ &ChromeWhispernetClientTest::TokensCallback, base::Unretained(this)));
+ expected_token_ = kZeroToken;
+
+ ASSERT_GT(saved_samples_->frames(), 0);
+
+ // Convert our single channel samples to two channel. Decode samples
+ // expects 2 channel data.
+ scoped_refptr<media::AudioBusRefCounted> samples_bus =
+ media::AudioBusRefCounted::Create(2, saved_samples_->frames());
+ memcpy(samples_bus->channel(0),
+ saved_samples_->channel(0),
+ sizeof(float) * saved_samples_->frames());
+ memcpy(samples_bus->channel(1),
+ saved_samples_->channel(0),
+ sizeof(float) * saved_samples_->frames());
+
+ client_->DecodeSamples(AudioBusToString(samples_bus));
+ run_loop_->Run();
+ }
+
+ void DetectBroadcast() {
+ ASSERT_TRUE(client_);
+
+ run_loop_.reset(new base::RunLoop());
+ client_->RegisterDetectBroadcastCallback(
+ base::Bind(&ChromeWhispernetClientTest::DetectBroadcastCallback,
+ base::Unretained(this)));
+ client_->DetectBroadcast();
+ run_loop_->Run();
+ }
+
+ protected:
+ void InitCallback(bool success) {
+ EXPECT_TRUE(success);
+ initialized_ = true;
+ ASSERT_TRUE(run_loop_);
+ run_loop_->Quit();
+ }
+
+ void SamplesCallback(
+ const std::string& token,
+ const scoped_refptr<media::AudioBusRefCounted>& samples) {
+ EXPECT_EQ(expected_token_, token);
+ saved_samples_ = samples;
+ ASSERT_TRUE(run_loop_);
+ run_loop_->Quit();
+ }
+
+ void TokensCallback(const std::vector<std::string>& tokens) {
+ ASSERT_TRUE(run_loop_);
+ run_loop_->Quit();
+
+ EXPECT_EQ(expected_token_, tokens[0]);
+ }
+
+ void DetectBroadcastCallback(bool success) {
+ EXPECT_TRUE(success);
+ ASSERT_TRUE(run_loop_);
+ run_loop_->Quit();
+ }
+
+ private:
+ scoped_ptr<base::RunLoop> run_loop_;
+ scoped_ptr<ChromeWhispernetClient> client_;
+
+ std::string expected_token_;
+ scoped_refptr<media::AudioBusRefCounted> saved_samples_;
+
+ bool initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeWhispernetClientTest);
+};
+
+IN_PROC_BROWSER_TEST_F(ChromeWhispernetClientTest, Initialize) {
+ InitializeWhispernet();
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeWhispernetClientTest, EncodeToken) {
+ InitializeWhispernet();
+ EncodeTokenAndSaveSamples();
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeWhispernetClientTest, DecodeSamples) {
+ InitializeWhispernet();
+ EncodeTokenAndSaveSamples();
+ DecodeSamplesAndVerifyToken();
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeWhispernetClientTest, DetectBroadcast) {
+ InitializeWhispernet();
+ EncodeTokenAndSaveSamples();
+ DecodeSamplesAndVerifyToken();
+ DetectBroadcast();
+}
diff --git a/chrome/browser/extensions/api/copresence_private/copresence_private_api.cc b/chrome/browser/extensions/api/copresence_private/copresence_private_api.cc
new file mode 100644
index 0000000..2dcc52e
--- /dev/null
+++ b/chrome/browser/extensions/api/copresence_private/copresence_private_api.cc
@@ -0,0 +1,105 @@
+// Copyright 2014 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/browser/extensions/api/copresence_private/copresence_private_api.h"
+
+#include "base/lazy_instance.h"
+#include "base/stl_util.h"
+#include "chrome/browser/copresence/chrome_whispernet_client.h"
+#include "chrome/common/extensions/api/copresence_private.h"
+#include "components/copresence/public/whispernet_client.h"
+#include "media/base/audio_bus.h"
+
+namespace extensions {
+
+// This code is only for testing while we don't have the rest of the
+// CopresenceAPI service which will actually give us the whispernet client.
+// Once we add that code, both the g_whispernet_client and the
+// GetWhispernetClient function will go away, to be replaced by the
+// GetWhispernetClient function that will fetch our active whispernet client
+// from the CopresenceAPI profile keyed service.
+copresence::WhispernetClient* g_whispernet_client = NULL;
+
+// Copresence Private functions.
+
+copresence::WhispernetClient* CopresencePrivateFunction::GetWhispernetClient() {
+ // This is temporary code, this needs to be replaced by the real
+ // GetWhispernetClient code from c/b/e/api/copresence/copresence_util.h
+ DCHECK(g_whispernet_client);
+ return g_whispernet_client;
+}
+
+// CopresenceSendFoundFunction implementation:
+ExtensionFunction::ResponseAction CopresencePrivateSendFoundFunction::Run() {
+ if (!GetWhispernetClient() ||
+ GetWhispernetClient()->GetTokensCallback().is_null()) {
+ return RespondNow(NoArguments());
+ }
+
+ scoped_ptr<api::copresence_private::SendFound::Params> params(
+ api::copresence_private::SendFound::Params::Create(*args_));
+ EXTENSION_FUNCTION_VALIDATE(params.get());
+ GetWhispernetClient()->GetTokensCallback().Run(params->tokens);
+ return RespondNow(NoArguments());
+}
+
+// CopresenceSendEncodedFunction implementation:
+ExtensionFunction::ResponseAction CopresencePrivateSendSamplesFunction::Run() {
+ if (!GetWhispernetClient() ||
+ GetWhispernetClient()->GetSamplesCallback().is_null()) {
+ return RespondNow(NoArguments());
+ }
+
+ scoped_ptr<api::copresence_private::SendSamples::Params> params(
+ api::copresence_private::SendSamples::Params::Create(*args_));
+ EXTENSION_FUNCTION_VALIDATE(params.get());
+
+ scoped_refptr<media::AudioBusRefCounted> samples =
+ media::AudioBusRefCounted::Create(1,
+ params->samples.size() / sizeof(float));
+
+ memcpy(samples->channel(0),
+ string_as_array(&params->samples),
+ params->samples.size());
+
+ GetWhispernetClient()->GetSamplesCallback().Run(params->token, samples);
+ return RespondNow(NoArguments());
+}
+
+// CopresenceSendDetectFunction implementation:
+ExtensionFunction::ResponseAction CopresencePrivateSendDetectFunction::Run() {
+ if (!GetWhispernetClient() ||
+ GetWhispernetClient()->GetDetectBroadcastCallback().is_null()) {
+ return RespondNow(NoArguments());
+ }
+
+ scoped_ptr<api::copresence_private::SendDetect::Params> params(
+ api::copresence_private::SendDetect::Params::Create(*args_));
+ EXTENSION_FUNCTION_VALIDATE(params.get());
+
+ GetWhispernetClient()->GetDetectBroadcastCallback().Run(params->detected);
+ return RespondNow(NoArguments());
+}
+
+// CopresenceSendInitializedFunction implementation:
+ExtensionFunction::ResponseAction
+CopresencePrivateSendInitializedFunction::Run() {
+ if (!GetWhispernetClient() ||
+ GetWhispernetClient()->GetInitializedCallback().is_null()) {
+ return RespondNow(NoArguments());
+ }
+
+ scoped_ptr<api::copresence_private::SendInitialized::Params> params(
+ api::copresence_private::SendInitialized::Params::Create(*args_));
+ EXTENSION_FUNCTION_VALIDATE(params.get());
+
+ GetWhispernetClient()->GetInitializedCallback().Run(params->success);
+ return RespondNow(NoArguments());
+}
+
+void SetWhispernetClientForTesting(copresence::WhispernetClient* client) {
+ g_whispernet_client = client;
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/copresence_private/copresence_private_api.h b/chrome/browser/extensions/api/copresence_private/copresence_private_api.h
new file mode 100644
index 0000000..2eb0265
--- /dev/null
+++ b/chrome/browser/extensions/api/copresence_private/copresence_private_api.h
@@ -0,0 +1,69 @@
+// Copyright 2014 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.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_COPRESENCE_PRIVATE_COPRESENCE_PRIVATE_API_H_
+#define CHROME_BROWSER_EXTENSIONS_API_COPRESENCE_PRIVATE_COPRESENCE_PRIVATE_API_H_
+
+#include "chrome/browser/extensions/chrome_extension_function.h"
+
+namespace copresence {
+class WhispernetClient;
+}
+
+namespace extensions {
+
+class CopresencePrivateFunction : public ChromeUIThreadExtensionFunction {
+ protected:
+ copresence::WhispernetClient* GetWhispernetClient();
+ virtual ~CopresencePrivateFunction() {}
+};
+
+class CopresencePrivateSendFoundFunction : public CopresencePrivateFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("copresencePrivate.sendFound",
+ COPRESENCEPRIVATE_SENDFOUND);
+
+ protected:
+ virtual ~CopresencePrivateSendFoundFunction() {}
+ virtual ExtensionFunction::ResponseAction Run() OVERRIDE;
+};
+
+class CopresencePrivateSendSamplesFunction : public CopresencePrivateFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("copresencePrivate.sendSamples",
+ COPRESENCEPRIVATE_SENDSAMPLES);
+
+ protected:
+ virtual ~CopresencePrivateSendSamplesFunction() {}
+ virtual ExtensionFunction::ResponseAction Run() OVERRIDE;
+};
+
+class CopresencePrivateSendDetectFunction : public CopresencePrivateFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("copresencePrivate.sendDetect",
+ COPRESENCEPRIVATE_SENDDETECT);
+
+ protected:
+ virtual ~CopresencePrivateSendDetectFunction() {}
+ virtual ExtensionFunction::ResponseAction Run() OVERRIDE;
+};
+
+class CopresencePrivateSendInitializedFunction
+ : public CopresencePrivateFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("copresencePrivate.sendInitialized",
+ COPRESENCEPRIVATE_SENDINITIALIZED);
+
+ protected:
+ virtual ~CopresencePrivateSendInitializedFunction() {}
+ virtual ExtensionFunction::ResponseAction Run() OVERRIDE;
+};
+
+// This will go away once we check in the code for the CopresenceAPI BCK
+// service which lets us inject a whispernet client.
+void SetWhispernetClientForTesting(copresence::WhispernetClient* client);
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_COPRESENCE_PRIVATE_COPRESENCE_PRIVATE_API_H_
diff --git a/chrome/browser/resources/component_extension_resources.grd b/chrome/browser/resources/component_extension_resources.grd
index 8c6d093d..c2608f2 100644
--- a/chrome/browser/resources/component_extension_resources.grd
+++ b/chrome/browser/resources/component_extension_resources.grd
@@ -172,6 +172,17 @@
<include name="IDR_LEDGER_LEDGER_PROXY_NMF" file="../extensions/api/ledger/ledger_proxy/ledger_proxy.nmf.js" type="BINDATA" />
<include name="IDR_LEDGER_LEDGER_PROXY_PEXE" file="../extensions/api/ledger/ledger_proxy/ledger_proxy_pnacl.pexe.js" type="BINDATA" />
</if>
+ <include name="IDR_WHISPERNET_PROXY_BACKGROUND_HTML" file="whispernet_proxy/background.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
+ <include name="IDR_WHISPERNET_PROXY_INIT_JS" file="whispernet_proxy/js/init.js" type="BINDATA" />
+ <include name="IDR_WHISPERNET_PROXY_NACL_JS" file="whispernet_proxy/js/nacl.js" type="BINDATA" />
+ <include name="IDR_WHISPERNET_PROXY_WRAPPER_JS" file="whispernet_proxy/js/wrapper.js" type="BINDATA" />
+ <!-- The next two files have a .png extension since grit for some reason
+ won't compile these files in if they are named .nmf/.pexe or
+ anything remotely similar to their actual extension. Naming these
+ .js or .html breaks all kinds of presubmit checks, hence .png seems
+ to be the only viable option at the moment. -->
+ <include name="IDR_WHISPERNET_PROXY_WHISPERNET_PROXY_PROXY_NMF" file="whispernet_proxy/whispernet_proxy.nmf.png" type="BINDATA" />
+ <include name="IDR_WHISPERNET_PROXY_WHISPERNET_PROXY_PROXY_PEXE" file="whispernet_proxy/whispernet_proxy_pnacl.pexe.png" type="BINDATA" />
</includes>
</release>
</grit>
diff --git a/chrome/browser/resources/whispernet_proxy/OWNERS b/chrome/browser/resources/whispernet_proxy/OWNERS
new file mode 100644
index 0000000..2bc8d07
--- /dev/null
+++ b/chrome/browser/resources/whispernet_proxy/OWNERS
@@ -0,0 +1,3 @@
+rkc@chromium.org
+ckehoe@chromium.org
+xiyuan@chromium.org
diff --git a/chrome/browser/resources/whispernet_proxy/background.html b/chrome/browser/resources/whispernet_proxy/background.html
new file mode 100644
index 0000000..06a8476
--- /dev/null
+++ b/chrome/browser/resources/whispernet_proxy/background.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="js/nacl.js"></script>
+<script src="js/wrapper.js"></script>
+<script src="js/init.js"></script>
+</head>
+<body>
+</body>
+</html>
diff --git a/chrome/browser/resources/whispernet_proxy/js/init.js b/chrome/browser/resources/whispernet_proxy/js/init.js
new file mode 100644
index 0000000..e346f206
--- /dev/null
+++ b/chrome/browser/resources/whispernet_proxy/js/init.js
@@ -0,0 +1,104 @@
+// Copyright 2014 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.
+
+'use strict';
+
+// Globals holding our encoder and decoder. We will never have more than one
+// Global variable that will be used to access this Nacl bridge.
+var whispernetNacl = null;
+
+// copy of an encoder or a decoder at a time.
+var whisperEncoder = null;
+var whisperDecoder = null;
+
+/**
+ * Initialize the whispernet encoder and decoder.
+ * @param {Object} audioParams Audio parameters used to initialize the encoder
+ * and decoder.
+ */
+function initialize(audioParams) {
+ if (!whispernetNacl) {
+ chrome.copresencePrivate.sendInitialized(false);
+ return;
+ }
+
+ console.log('init: creating encoder!');
+ whisperEncoder = new WhisperEncoder(audioParams.play, whispernetNacl);
+ whisperEncoder.setAudioDataCallback(chrome.copresencePrivate.sendSamples);
+
+ console.log('init: creating decoder!');
+ whisperDecoder = new WhisperDecoder(audioParams.record, whispernetNacl);
+ whisperDecoder.setReceiveCallback(chrome.copresencePrivate.sendFound);
+ whisperDecoder.onDetectBroadcast(chrome.copresencePrivate.sendDetect);
+
+ chrome.copresencePrivate.sendInitialized(true);
+}
+
+/**
+ * Sends a request to whispernet to encode a token.
+ * @param {string} token Token to encode. This needs to be a base64 string.
+ */
+function encodeTokenRequest(token) {
+ if (whisperEncoder) {
+ whisperEncoder.encode(atob(token), true);
+ } else {
+ console.error('encodeTokenRequest: Whisper not initialized!');
+ }
+}
+
+/**
+ * Sends a request to whispernet to decode samples.
+ * @param {ArrayBuffer} samples Array of samples to decode.
+ */
+function decodeSamplesRequest(samples) {
+ if (whisperDecoder) {
+ whisperDecoder.processSamples(samples);
+ } else {
+ console.error('decodeSamplesRequest: Whisper not initialized!');
+ }
+}
+
+/**
+ * Sends a request to whispernet to detect broadcast.
+ */
+function detectBroadcastRequest() {
+ if (whisperDecoder) {
+ whisperDecoder.detectBroadcast();
+ } else {
+ console.error('detectBroadcastRequest: Whisper not initialized!');
+ }
+}
+
+/**
+ * Initialize our listerners and signal that the extension is loaded.
+ */
+function onWhispernetLoaded() {
+ console.log('init: Nacl ready!');
+
+ // Setup all the listeners for the private API.
+ chrome.copresencePrivate.onInitialize.addListener(initialize);
+ chrome.copresencePrivate.onEncodeTokenRequest.addListener(encodeTokenRequest);
+ chrome.copresencePrivate.onDecodeSamplesRequest.addListener(
+ decodeSamplesRequest);
+ chrome.copresencePrivate.onDetectBroadcastRequest.addListener(
+ detectBroadcastRequest);
+
+ // This first initialized is sent to indicate that the library is loaded.
+ // Every other time, it will be sent only when Chrome wants to reinitialize
+ // the encoder and decoder.
+ chrome.copresencePrivate.sendInitialized(true);
+}
+
+/**
+ * Initialize the whispernet Nacl bridge.
+ */
+function initWhispernet() {
+ console.log('init: Starting Nacl bridge.');
+ // TODO(rkc): Figure out how to embed the .nmf and the .pexe into component
+ // resources without having to rename them to .js.
+ whispernetNacl = new NaclBridge('whispernet_proxy.nmf.png',
+ onWhispernetLoaded);
+}
+
+window.addEventListener('DOMContentLoaded', initWhispernet);
diff --git a/chrome/browser/resources/whispernet_proxy/js/nacl.js b/chrome/browser/resources/whispernet_proxy/js/nacl.js
new file mode 100644
index 0000000..6505728
--- /dev/null
+++ b/chrome/browser/resources/whispernet_proxy/js/nacl.js
@@ -0,0 +1,102 @@
+// Copyright 2014 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.
+
+'use strict';
+
+/**
+ * Constructor for the Nacl bridge to the whispernet wrapper.
+ * @param {string} nmf The relative path to the nmf containing the location of
+ * the whispernet Nacl wrapper.
+ * @param {function()} readyCallback Callback to be called once we've loaded the
+ * whispernet wrapper.
+ */
+function NaclBridge(nmf, readyCallback) {
+ this.readyCallback_ = readyCallback;
+ this.callbacks_ = [];
+ this.isEnabled_ = false;
+ this.naclId_ = this.loadNacl_(nmf);
+}
+
+/**
+ * Method to send generic byte data to the whispernet wrapper.
+ * @param {string} data Raw data to send to the whispernet wrapper.
+ */
+NaclBridge.prototype.send = function(data) {
+ if (this.isEnabled_) {
+ this.embed_.postMessage(data);
+ } else {
+ console.error('Whisper Nacl Bridge not initialized!');
+ }
+};
+
+/**
+ * Method to add a listener to Nacl messages received by this bridge.
+ * @param {function(Event)} messageCallback Callback to receive the messsage.
+ */
+NaclBridge.prototype.addListener = function(messageCallback) {
+ this.callbacks_.push(messageCallback);
+};
+
+/**
+ * Method that receives Nacl messages and forwards them to registered
+ * callbacks.
+ * @param {Event} e Event from the whispernet wrapper.
+ * @private
+ */
+NaclBridge.prototype.onMessage_ = function(e) {
+ if (this.isEnabled_) {
+ this.callbacks_.forEach(function(callback) {
+ callback(e);
+ });
+ }
+};
+
+/**
+ * Injects the <embed> for this nacl manifest URL, generating a unique ID.
+ * @param {string} manifestUrl Url to the nacl manifest to load.
+ * @return {number} generated ID.
+ * @private
+ */
+NaclBridge.prototype.loadNacl_ = function(manifestUrl) {
+ var id = 'nacl-' + Math.floor(Math.random() * 10000);
+ this.embed_ = document.createElement('embed');
+ this.embed_.name = 'nacl_module';
+ this.embed_.width = 1;
+ this.embed_.height = 1;
+ this.embed_.src = manifestUrl;
+ this.embed_.id = id;
+ this.embed_.type = 'application/x-pnacl';
+
+ // Wait for the element to load and callback.
+ this.embed_.addEventListener('load', this.onNaclReady_.bind(this));
+ this.embed_.addEventListener('error', this.onNaclError_.bind(this));
+
+ // Inject the embed string into the page.
+ document.body.appendChild(this.embed_);
+
+ // Listen for messages from the NaCl module.
+ window.addEventListener('message', this.onMessage_.bind(this), true);
+ return id;
+};
+
+/**
+ * Callback that is called when the Whispernet wrapper is loaded and forward
+ * that status to the callback registered with us in the constructor.
+ * @private
+ */
+NaclBridge.prototype.onNaclReady_ = function() {
+ if (this.readyCallback_)
+ this.readyCallback_();
+ this.isEnabled_ = true;
+};
+
+/**
+ * Callback that handles Nacl errors.
+ * @param {string} msg Error string.
+ * @private
+ */
+NaclBridge.prototype.onNaclError_ = function(msg) {
+ // TODO(rkc): Handle error from NaCl better.
+ console.error('NaCl error', msg);
+};
diff --git a/chrome/browser/resources/whispernet_proxy/js/wrapper.js b/chrome/browser/resources/whispernet_proxy/js/wrapper.js
new file mode 100644
index 0000000..3613fd9
--- /dev/null
+++ b/chrome/browser/resources/whispernet_proxy/js/wrapper.js
@@ -0,0 +1,224 @@
+// Copyright 2014 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.
+
+'use strict';
+
+/**
+ * Function to convert an array of bytes to a base64 string
+ * TODO(rkc): Change this to use a Uint8array instead of a string.
+ * @param {string} bytes String containing the bytes we want to convert.
+ * @return {string} String containing the base64 representation.
+ */
+function bytesToBase64(bytes) {
+ var bstr = '';
+ for (var i = 0; i < bytes.length; ++i)
+ bstr += String.fromCharCode(bytes[i]);
+ return btoa(bstr);
+}
+
+/**
+ * Function to convert a string to an array of bytes.
+ * @param {string} str String to convert.
+ * @return {Array} Array containing the string.
+ */
+function stringToArray(str) {
+ var buffer = [];
+ for (var i = 0; i < str.length; ++i)
+ buffer[i] = str.charCodeAt(i);
+ return buffer;
+}
+
+/**
+ * Creates a whispernet encoder.
+ * @constructor
+ * @param {Object} params Dictionary of parameters used to initialize the
+ * whispernet encoder.
+ * @param {Object} whisperNacl The NaclBridge object to use to communicate with
+ * the whispernet wrapper.
+ */
+function WhisperEncoder(params, whisperNacl) {
+ params = params || {};
+ this.repetitions_ = params.repetitions || 3;
+
+ this.whisperNacl_ = whisperNacl;
+ this.whisperNacl_.addListener(this.onNaclMessage_.bind(this));
+
+ var symbolCoder = {};
+ symbolCoder.sample_rate = params.sampleRate || 48000.0;
+ symbolCoder.upsampling_factor = params.bitsPerSample || 16;
+ symbolCoder.desired_carrier_frequency = params.carrierFrequency || 18500.0;
+ symbolCoder.bits_per_symbol = 4;
+ symbolCoder.min_cycles_per_frame = 4;
+ symbolCoder.baseband_decimation_factor = 4;
+
+ var msg = {
+ type: 'initialize_encoder',
+ symbol_coder: symbolCoder,
+ encoder_params: {
+ bytes_per_token: 6,
+ include_parity_symbol: true,
+ single_sideband: true
+ }
+ };
+ this.whisperNacl_.send(JSON.stringify(msg));
+}
+
+/**
+ * Method to encode a token.
+ * @param {string} token Token to encode.
+ * @param {boolean} raw Whether we should return the encoded samples in raw
+ * format or as a Wave file.
+ */
+WhisperEncoder.prototype.encode = function(token, raw) {
+ var msg = {
+ type: 'encode_token',
+ // Trying to send the token in binary form to Nacl doesn't work correctly.
+ // We end up with the correct string + a bunch of extra characters. This is
+ // true of returning a binary string too; hence we communicate back and
+ // forth by converting the bytes into an array of integers.
+ token: stringToArray(token),
+ repetitions: this.repetitions_,
+ return_raw_samples: raw
+ };
+ this.whisperNacl_.send(JSON.stringify(msg));
+};
+
+/**
+ * Method to set the callback for encoded audio data received from the encoder
+ * when we finish encoding a token.
+ * @param {function(string, ArrayBuffer)} callback Callback which will receive
+ * the audio samples.
+ */
+WhisperEncoder.prototype.setAudioDataCallback = function(callback) {
+ this.audioDataCallback_ = callback;
+};
+
+/**
+ * Method to handle messages from the whispernet NaCl wrapper.
+ * @param {Event} e Event from the whispernet wrapper.
+ * @private
+ */
+WhisperEncoder.prototype.onNaclMessage_ = function(e) {
+ var msg = e.data;
+ if (msg.type == 'encode_token_response') {
+ this.audioDataCallback_(bytesToBase64(msg.token), msg.samples);
+ }
+};
+
+/**
+ * Creates a whispernet decoder.
+ * @constructor
+ * @param {Object} params Dictionary of parameters used to initialize the
+ * whispernet decoder.
+ * @param {Object} whisperNacl The NaclBridge object to use to communicate with
+ * the whispernet wrapper.
+ */
+function WhisperDecoder(params, whisperNacl) {
+ params = params || {};
+
+ this.whisperNacl_ = whisperNacl;
+ this.whisperNacl_.addListener(this.onNaclMessage_.bind(this));
+
+ var msg = {
+ type: 'initialize_decoder',
+ num_channels: params.channels,
+ symbol_coder: {
+ sample_rate: params.sampleRate || 48000.0,
+ upsampling_factor: params.bitsPerSample || 16,
+ desired_carrier_frequency: params.carrierFrequency || 18500.0,
+ bits_per_symbol: 4,
+ min_cycles_per_frame: 4,
+ baseband_decimation_factor: 4
+ },
+ decoder_params: {
+ bytes_per_token: 6,
+ include_parity_symbol: true,
+ max_candidates: 1,
+ broadcaster_stopped_threshold_in_seconds: 10
+ },
+ acquisition_params: {
+ max_buffer_duration_in_seconds: 3
+ }
+ };
+ this.whisperNacl_.send(JSON.stringify(msg));
+}
+
+/**
+ * Method to request the decoder to wipe its internal buffer.
+ */
+WhisperDecoder.prototype.wipeDecoder = function() {
+ var msg = {
+ type: 'wipe_decode_buffer'
+ };
+ this.whisperNacl_.send(JSON.stringify(msg));
+};
+
+/**
+ * Method to request the decoder to detect a broadcast.
+ */
+WhisperDecoder.prototype.detectBroadcast = function() {
+ var msg = {
+ type: 'detect_broadcast'
+ };
+ this.whisperNacl_.send(JSON.stringify(msg));
+};
+
+/**
+ * Method to request the decoder to process samples.
+ * @param {ArrayBuffer} samples Array of samples to process.
+ */
+WhisperDecoder.prototype.processSamples = function(samples) {
+ // For sample processing, the Nacl module doesn't expect any frills in the
+ // message, just send the samples directly.
+ this.whisperNacl_.send(samples);
+};
+
+/**
+ * Method to set the callback for decoded tokens received from the decoder.
+ * @param {function(!Array.string)} callback Callback to receive the list of
+ * decoded tokens.
+ */
+WhisperDecoder.prototype.setReceiveCallback = function(callback) {
+ this.tokenCallback_ = callback;
+};
+
+/**
+ * Method to set the callback for receiving the detect callback status received
+ * from the decoder.
+ * @param {function()} callback Callback to set to receive the detect broadcast
+ * status.
+ */
+WhisperDecoder.prototype.onDetectBroadcast = function(callback) {
+ this.detectBroadcastCallback_ = callback;
+};
+
+/**
+ * Method to handle messages from the whispernet NaCl wrapper.
+ * @param {Event} e Event from the whispernet wrapper.
+ * @private
+ */
+WhisperDecoder.prototype.onNaclMessage_ = function(e) {
+ var msg = e.data;
+ if (msg.type == 'decode_tokens_response') {
+ this.handleCandidates_(JSON.parse(msg.tokens));
+ } else if (msg.type == 'detect_broadcast_response') {
+ this.detectBroadcastCallback_(msg.detected);
+ }
+};
+
+/**
+ * Method to receive tokens from the decoder and process and forward them to the
+ * token callback registered with us.
+ * @param {!Array.string} candidates Array of token candidates.
+ * @private
+ */
+WhisperDecoder.prototype.handleCandidates_ = function(candidates) {
+ if (!this.tokenCallback_ || !candidates || candidates.length == 0)
+ return;
+
+ var returnCandidates = [];
+ for (var i = 0; i < candidates.length; ++i)
+ returnCandidates[i] = bytesToBase64(candidates[i]);
+ this.tokenCallback_(returnCandidates);
+};
diff --git a/chrome/browser/resources/whispernet_proxy/manifest.json b/chrome/browser/resources/whispernet_proxy/manifest.json
new file mode 100644
index 0000000..6b15ef9
--- /dev/null
+++ b/chrome/browser/resources/whispernet_proxy/manifest.json
@@ -0,0 +1,13 @@
+{
+ // chrome-extension://bpfmnplchembfbdgieamdodgaencleal/
+ "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxY7MIAZJdh8y5zmU3kRtxMtOWwHl/Yu+c700YQncON0OaqNLcWD/vrPBsMW/4SkkHIBtJDUmLAzPfXyUtbxYo3Vi3E7xJ028BN0Z+KMhPMvFYnHiS4NlFTRDHvQbWi7P+4bvOvS8RZe1VBgSQMhaJSsa6zzE+UNRPsiWNOaX2O08N3oyxo1Z2SU8Q9eE283NOgY8fVg8TJFVqGHPmbcblpM2A+dk7YFBIT1dFemRxJFW2DdLoCXojrNWXqzYstj/muuefRTgnNmwlCjCeDyK6R6x71GRTUnkVOiBgETgsTfOJRvKQCy65/EENZdX/u4e1LM1PWq0ePJ+xMwEXfuHtQIDAQAB",
+ "name": "Whispernet Proxy",
+ "version": "1.0",
+ "manifest_version": 2,
+ "description": "Proxy whispernet calls.",
+ "permissions": [ "copresencePrivate" ],
+ "background": {
+ "page": "background.html",
+ "persistent": true
+ }
+}
diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi
index ed1ce27..58098be 100644
--- a/chrome/chrome_browser_extensions.gypi
+++ b/chrome/chrome_browser_extensions.gypi
@@ -353,6 +353,8 @@
'browser/apps/shortcut_manager.h',
'browser/apps/shortcut_manager_factory.cc',
'browser/apps/shortcut_manager_factory.h',
+ 'browser/copresence/chrome_whispernet_client.cc',
+ 'browser/copresence/chrome_whispernet_client.h',
'browser/extensions/active_script_controller.cc',
'browser/extensions/active_script_controller.h',
'browser/extensions/active_tab_permission_granter.cc',
@@ -483,6 +485,8 @@
'browser/extensions/api/cookies/cookies_api_constants.h',
'browser/extensions/api/cookies/cookies_helpers.cc',
'browser/extensions/api/cookies/cookies_helpers.h',
+ 'browser/extensions/api/copresence_private/copresence_private_api.cc',
+ 'browser/extensions/api/copresence_private/copresence_private_api.h',
'browser/extensions/api/debugger/debugger_api.cc',
'browser/extensions/api/debugger/debugger_api.h',
'browser/extensions/api/debugger/debugger_api_constants.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 37e8e82..3f30b4e 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1026,6 +1026,7 @@
'browser/chromeos/ui/idle_logout_dialog_view_browsertest.cc',
'browser/collected_cookies_browsertest.cc',
'browser/content_settings/content_settings_browsertest.cc',
+ 'browser/copresence/chrome_whispernet_client_browsertest.cc',
'browser/crash_recovery_browsertest.cc',
'browser/custom_handlers/protocol_handler_registry_browsertest.cc',
'browser/devtools/device/adb/adb_client_socket_browsertest.cc',
diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json
index 0280fca..cd7b197 100644
--- a/chrome/common/extensions/api/_api_features.json
+++ b/chrome/common/extensions/api/_api_features.json
@@ -230,6 +230,10 @@
"dependencies": ["permission:cookies"],
"contexts": ["blessed_extension"]
},
+ "copresencePrivate": {
+ "dependencies": ["permission:copresencePrivate"],
+ "contexts": ["blessed_extension"]
+ },
"debugger": {
"dependencies": ["permission:debugger"],
"contexts": ["blessed_extension"]
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
index a3e8662..b313858 100644
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -313,6 +313,14 @@
"channel": "stable",
"extension_types": ["extension", "legacy_packaged_app"]
},
+ "copresencePrivate": {
+ "channel": "stable",
+ "extension_types": ["extension"],
+ "location": "component",
+ "whitelist": [
+ "AFA728615D3A82D4017BDECEE86978543194D198" // Whispernet Proxy
+ ]
+ },
"diagnostics": [
{
"channel": "dev",
diff --git a/chrome/common/extensions/api/api.gyp b/chrome/common/extensions/api/api.gyp
index d995e1a..522aba2 100644
--- a/chrome/common/extensions/api/api.gyp
+++ b/chrome/common/extensions/api/api.gyp
@@ -43,6 +43,7 @@
'context_menus_internal.json',
'context_menus.json',
'cookies.json',
+ 'copresence_private.idl',
'debugger.json',
'desktop_capture.json',
'developer_private.idl',
diff --git a/chrome/common/extensions/api/copresence_private.idl b/chrome/common/extensions/api/copresence_private.idl
new file mode 100644
index 0000000..1f9634a
--- /dev/null
+++ b/chrome/common/extensions/api/copresence_private.idl
@@ -0,0 +1,49 @@
+// Copyright 2014 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.
+
+// Use the <code>chrome.copresencePrivate</code> API to interface with Chrome
+// from the whispernet_proxy extension.
+namespace copresencePrivate {
+
+ dictionary PlayParameters {
+ double sampleRate;
+ long bitsPerSample;
+ double carrierFrequency;
+ long repetitions;
+ };
+
+ dictionary RecordParameters {
+ double sampleRate;
+ long bitsPerSample;
+ long channels;
+ double carrierFrequency;
+ };
+
+ dictionary AudioParameters {
+ PlayParameters play;
+ RecordParameters record;
+ };
+
+ interface Functions {
+ // Send a boolean indicating whether our initialization was successful.
+ static void sendInitialized(boolean success);
+ // Sends an array of found tokens to Chrome.
+ static void sendFound(DOMString[] tokens);
+ // Send an array buffer of samples encoded for the specified token.
+ static void sendSamples(DOMString token, ArrayBuffer samples);
+ // Send a boolean indicating whether we detected a broadcast or not.
+ static void sendDetect(boolean detected);
+ };
+
+ interface Events {
+ // Fired to request initialization of the whisper.net library.
+ static void onInitialize(AudioParameters audioParams);
+ // Fired to request encoding of the given token.
+ static void onEncodeTokenRequest(DOMString base64Token);
+ // Fired when we have new samples to decode.
+ static void onDecodeSamplesRequest(ArrayBuffer audioSamples);
+ // Fired to request a DetectBroadcast.
+ static void onDetectBroadcastRequest();
+ };
+};
diff --git a/chrome/common/extensions/permissions/chrome_api_permissions.cc b/chrome/common/extensions/permissions/chrome_api_permissions.cc
index 64258b8..81a3346 100644
--- a/chrome/common/extensions/permissions/chrome_api_permissions.cc
+++ b/chrome/common/extensions/permissions/chrome_api_permissions.cc
@@ -109,6 +109,7 @@ std::vector<APIPermissionInfo*> ChromeAPIPermissions::GetAllPermissions()
PermissionMessage::kContentSettings},
{APIPermission::kContextMenus, "contextMenus"},
{APIPermission::kCookie, "cookies"},
+ {APIPermission::kCopresencePrivate, "copresencePrivate"},
{APIPermission::kEnterprisePlatformKeys, "enterprise.platformKeys"},
{APIPermission::kFileBrowserHandler, "fileBrowserHandler",
APIPermissionInfo::kFlagCannotBeOptional},
diff --git a/chrome/common/extensions/permissions/permission_set_unittest.cc b/chrome/common/extensions/permissions/permission_set_unittest.cc
index 3c5a3c5..908d597 100644
--- a/chrome/common/extensions/permissions/permission_set_unittest.cc
+++ b/chrome/common/extensions/permissions/permission_set_unittest.cc
@@ -644,6 +644,7 @@ TEST(PermissionsTest, PermissionMessages) {
skip.insert(APIPermission::kBrowsingData);
skip.insert(APIPermission::kCastStreaming);
skip.insert(APIPermission::kContextMenus);
+ skip.insert(APIPermission::kCopresencePrivate);
skip.insert(APIPermission::kDiagnostics);
skip.insert(APIPermission::kDns);
skip.insert(APIPermission::kDownloadsShelf);