Elim cr-checkbox
[chromium-blink-merge.git] / chrome / browser / extensions / api / gcm / gcm_api.cc
blob60dd2922a90f4823e89760e402a8b04e349b9768
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/api/gcm/gcm_api.h"
7 #include <algorithm>
8 #include <map>
9 #include <vector>
11 #include "base/metrics/histogram.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/services/gcm/gcm_profile_service.h"
17 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
18 #include "chrome/common/extensions/api/gcm.h"
19 #include "components/gcm_driver/common/gcm_messages.h"
20 #include "components/gcm_driver/gcm_driver.h"
21 #include "extensions/browser/event_router.h"
22 #include "extensions/common/extension.h"
24 namespace {
26 const size_t kMaximumMessageSize = 4096; // in bytes.
27 const char kCollapseKey[] = "collapse_key";
28 const char kGoogDotRestrictedPrefix[] = "goog.";
29 const char kGoogleRestrictedPrefix[] = "google";
31 // Error messages.
32 const char kInvalidParameter[] =
33 "Function was called with invalid parameters.";
34 const char kGCMDisabled[] = "GCM is currently disabled.";
35 const char kAsyncOperationPending[] =
36 "Asynchronous operation is pending.";
37 const char kNetworkError[] = "Network error occurred.";
38 const char kServerError[] = "Server error occurred.";
39 const char kTtlExceeded[] = "Time-to-live exceeded.";
40 const char kUnknownError[] = "Unknown error occurred.";
42 const char* GcmResultToError(gcm::GCMClient::Result result) {
43 switch (result) {
44 case gcm::GCMClient::SUCCESS:
45 return "";
46 case gcm::GCMClient::INVALID_PARAMETER:
47 return kInvalidParameter;
48 case gcm::GCMClient::GCM_DISABLED:
49 return kGCMDisabled;
50 case gcm::GCMClient::ASYNC_OPERATION_PENDING:
51 return kAsyncOperationPending;
52 case gcm::GCMClient::NETWORK_ERROR:
53 return kNetworkError;
54 case gcm::GCMClient::SERVER_ERROR:
55 return kServerError;
56 case gcm::GCMClient::TTL_EXCEEDED:
57 return kTtlExceeded;
58 case gcm::GCMClient::UNKNOWN_ERROR:
59 return kUnknownError;
60 default:
61 NOTREACHED() << "Unexpected value of result cannot be converted: "
62 << result;
65 // Never reached, but prevents missing return statement warning.
66 return "";
69 bool IsMessageKeyValid(const std::string& key) {
70 std::string lower = base::ToLowerASCII(key);
71 return !key.empty() &&
72 key.compare(0, arraysize(kCollapseKey) - 1, kCollapseKey) != 0 &&
73 lower.compare(0,
74 arraysize(kGoogleRestrictedPrefix) - 1,
75 kGoogleRestrictedPrefix) != 0 &&
76 lower.compare(0,
77 arraysize(kGoogDotRestrictedPrefix),
78 kGoogDotRestrictedPrefix) != 0;
81 } // namespace
83 namespace extensions {
85 bool GcmApiFunction::RunAsync() {
86 if (!IsGcmApiEnabled())
87 return false;
89 return DoWork();
92 bool GcmApiFunction::IsGcmApiEnabled() const {
93 Profile* profile = Profile::FromBrowserContext(browser_context());
95 // GCM is not supported in incognito mode.
96 if (profile->IsOffTheRecord())
97 return false;
99 return gcm::GCMProfileService::IsGCMEnabled(profile);
102 gcm::GCMDriver* GcmApiFunction::GetGCMDriver() const {
103 return gcm::GCMProfileServiceFactory::GetForProfile(
104 Profile::FromBrowserContext(browser_context()))->driver();
107 GcmRegisterFunction::GcmRegisterFunction() {}
109 GcmRegisterFunction::~GcmRegisterFunction() {}
111 bool GcmRegisterFunction::DoWork() {
112 scoped_ptr<api::gcm::Register::Params> params(
113 api::gcm::Register::Params::Create(*args_));
114 EXTENSION_FUNCTION_VALIDATE(params.get());
116 GetGCMDriver()->Register(
117 extension()->id(),
118 params->sender_ids,
119 base::Bind(&GcmRegisterFunction::CompleteFunctionWithResult, this));
121 return true;
124 void GcmRegisterFunction::CompleteFunctionWithResult(
125 const std::string& registration_id,
126 gcm::GCMClient::Result result) {
127 SetResult(new base::StringValue(registration_id));
128 SetError(GcmResultToError(result));
129 SendResponse(gcm::GCMClient::SUCCESS == result);
132 GcmUnregisterFunction::GcmUnregisterFunction() {}
134 GcmUnregisterFunction::~GcmUnregisterFunction() {}
136 bool GcmUnregisterFunction::DoWork() {
137 UMA_HISTOGRAM_BOOLEAN("GCM.APICallUnregister", true);
139 GetGCMDriver()->Unregister(
140 extension()->id(),
141 base::Bind(&GcmUnregisterFunction::CompleteFunctionWithResult, this));
143 return true;
146 void GcmUnregisterFunction::CompleteFunctionWithResult(
147 gcm::GCMClient::Result result) {
148 SetError(GcmResultToError(result));
149 SendResponse(gcm::GCMClient::SUCCESS == result);
152 GcmSendFunction::GcmSendFunction() {}
154 GcmSendFunction::~GcmSendFunction() {}
156 bool GcmSendFunction::DoWork() {
157 scoped_ptr<api::gcm::Send::Params> params(
158 api::gcm::Send::Params::Create(*args_));
159 EXTENSION_FUNCTION_VALIDATE(params.get());
160 EXTENSION_FUNCTION_VALIDATE(
161 ValidateMessageData(params->message.data.additional_properties));
163 gcm::OutgoingMessage outgoing_message;
164 outgoing_message.id = params->message.message_id;
165 outgoing_message.data = params->message.data.additional_properties;
166 if (params->message.time_to_live.get())
167 outgoing_message.time_to_live = *params->message.time_to_live;
169 GetGCMDriver()->Send(
170 extension()->id(),
171 params->message.destination_id,
172 outgoing_message,
173 base::Bind(&GcmSendFunction::CompleteFunctionWithResult, this));
175 return true;
178 void GcmSendFunction::CompleteFunctionWithResult(
179 const std::string& message_id,
180 gcm::GCMClient::Result result) {
181 SetResult(new base::StringValue(message_id));
182 SetError(GcmResultToError(result));
183 SendResponse(gcm::GCMClient::SUCCESS == result);
186 bool GcmSendFunction::ValidateMessageData(const gcm::MessageData& data) const {
187 size_t total_size = 0u;
188 for (std::map<std::string, std::string>::const_iterator iter = data.begin();
189 iter != data.end(); ++iter) {
190 total_size += iter->first.size() + iter->second.size();
192 if (!IsMessageKeyValid(iter->first) ||
193 kMaximumMessageSize < iter->first.size() ||
194 kMaximumMessageSize < iter->second.size() ||
195 kMaximumMessageSize < total_size)
196 return false;
199 return total_size != 0;
202 GcmJsEventRouter::GcmJsEventRouter(Profile* profile) : profile_(profile) {
205 GcmJsEventRouter::~GcmJsEventRouter() {
208 void GcmJsEventRouter::OnMessage(const std::string& app_id,
209 const gcm::IncomingMessage& message) {
210 api::gcm::OnMessage::Message message_arg;
211 message_arg.data.additional_properties = message.data;
212 if (!message.sender_id.empty())
213 message_arg.from.reset(new std::string(message.sender_id));
214 if (!message.collapse_key.empty())
215 message_arg.collapse_key.reset(new std::string(message.collapse_key));
217 scoped_ptr<Event> event(
218 new Event(events::GCM_ON_MESSAGE, api::gcm::OnMessage::kEventName,
219 api::gcm::OnMessage::Create(message_arg).Pass(), profile_));
220 EventRouter::Get(profile_)->DispatchEventToExtension(app_id, event.Pass());
223 void GcmJsEventRouter::OnMessagesDeleted(const std::string& app_id) {
224 scoped_ptr<Event> event(new Event(
225 events::GCM_ON_MESSAGES_DELETED, api::gcm::OnMessagesDeleted::kEventName,
226 api::gcm::OnMessagesDeleted::Create().Pass(), profile_));
227 EventRouter::Get(profile_)->DispatchEventToExtension(app_id, event.Pass());
230 void GcmJsEventRouter::OnSendError(
231 const std::string& app_id,
232 const gcm::GCMClient::SendErrorDetails& send_error_details) {
233 api::gcm::OnSendError::Error error;
234 error.message_id.reset(new std::string(send_error_details.message_id));
235 error.error_message = GcmResultToError(send_error_details.result);
236 error.details.additional_properties = send_error_details.additional_data;
238 scoped_ptr<Event> event(
239 new Event(events::GCM_ON_SEND_ERROR, api::gcm::OnSendError::kEventName,
240 api::gcm::OnSendError::Create(error).Pass(), profile_));
241 EventRouter::Get(profile_)->DispatchEventToExtension(app_id, event.Pass());
244 } // namespace extensions