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"
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"
26 const size_t kMaximumMessageSize
= 4096; // in bytes.
27 const char kCollapseKey
[] = "collapse_key";
28 const char kGoogDotRestrictedPrefix
[] = "goog.";
29 const char kGoogleRestrictedPrefix
[] = "google";
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
) {
44 case gcm::GCMClient::SUCCESS
:
46 case gcm::GCMClient::INVALID_PARAMETER
:
47 return kInvalidParameter
;
48 case gcm::GCMClient::GCM_DISABLED
:
50 case gcm::GCMClient::ASYNC_OPERATION_PENDING
:
51 return kAsyncOperationPending
;
52 case gcm::GCMClient::NETWORK_ERROR
:
54 case gcm::GCMClient::SERVER_ERROR
:
56 case gcm::GCMClient::TTL_EXCEEDED
:
58 case gcm::GCMClient::UNKNOWN_ERROR
:
61 NOTREACHED() << "Unexpected value of result cannot be converted: "
65 // Never reached, but prevents missing return statement warning.
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 &&
74 arraysize(kGoogleRestrictedPrefix
) - 1,
75 kGoogleRestrictedPrefix
) != 0 &&
77 arraysize(kGoogDotRestrictedPrefix
),
78 kGoogDotRestrictedPrefix
) != 0;
83 namespace extensions
{
85 bool GcmApiFunction::RunAsync() {
86 if (!IsGcmApiEnabled())
92 bool GcmApiFunction::IsGcmApiEnabled() const {
93 Profile
* profile
= Profile::FromBrowserContext(browser_context());
95 // GCM is not supported in incognito mode.
96 if (profile
->IsOffTheRecord())
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(
119 base::Bind(&GcmRegisterFunction::CompleteFunctionWithResult
, this));
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(
141 base::Bind(&GcmUnregisterFunction::CompleteFunctionWithResult
, this));
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(
171 params
->message
.destination_id
,
173 base::Bind(&GcmSendFunction::CompleteFunctionWithResult
, this));
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
)
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