- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / notifications / sync_notifier / synced_notification.cc
1 // Copyright (c) 2012 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.
4
5 #include "chrome/browser/notifications/sync_notifier/synced_notification.h"
6
7 #include "base/basictypes.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/time/time.h"
11 #include "base/values.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/notifications/notification.h"
14 #include "chrome/browser/notifications/notification_ui_manager.h"
15 #include "chrome/browser/notifications/sync_notifier/chrome_notifier_delegate.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "sync/protocol/sync.pb.h"
18 #include "sync/protocol/synced_notification_specifics.pb.h"
19 #include "ui/gfx/image/image.h"
20 #include "ui/message_center/message_center_util.h"
21 #include "ui/message_center/notification_types.h"
22
23 namespace {
24 const char kExtensionScheme[] = "synced-notification://";
25 const char kDefaultSyncedNotificationScheme[] = "https:";
26
27 // The name of our first synced notification service.
28 // TODO(petewil): remove this hardcoding once we have the synced notification
29 // signalling sync data type set up to provide this.
30 // crbug.com/248337
31 const char kFirstSyncedNotificationServiceId[] = "Google+";
32
33
34 // Today rich notifications only supports two buttons, make sure we don't
35 // try to supply them with more than this number of buttons.
36 const unsigned int kMaxNotificationButtonIndex = 2;
37
38 bool UseRichNotifications() {
39   return message_center::IsRichNotificationEnabled();
40 }
41
42 // Schema-less specs default badly in windows.  If we find one, add the schema
43 // we expect instead of allowing windows specific GURL code to make it default
44 // to "file:".
45 GURL AddDefaultSchemaIfNeeded(std::string& url_spec) {
46   if (StartsWithASCII(url_spec, std::string("//"), false))
47     return GURL(std::string(kDefaultSyncedNotificationScheme) + url_spec);
48
49   return GURL(url_spec);
50 }
51
52 }  // namespace
53
54 namespace notifier {
55
56 COMPILE_ASSERT(static_cast<sync_pb::CoalescedSyncedNotification_ReadState>(
57                    SyncedNotification::kUnread) ==
58                sync_pb::CoalescedSyncedNotification_ReadState_UNREAD,
59                local_enum_must_match_protobuf_enum);
60 COMPILE_ASSERT(static_cast<sync_pb::CoalescedSyncedNotification_ReadState>(
61                    SyncedNotification::kRead) ==
62                sync_pb::CoalescedSyncedNotification_ReadState_READ,
63                local_enum_must_match_protobuf_enum);
64 COMPILE_ASSERT(static_cast<sync_pb::CoalescedSyncedNotification_ReadState>(
65                    SyncedNotification::kDismissed) ==
66                sync_pb::CoalescedSyncedNotification_ReadState_DISMISSED,
67                local_enum_must_match_protobuf_enum);
68
69 SyncedNotification::SyncedNotification(const syncer::SyncData& sync_data)
70     : notification_manager_(NULL),
71       notifier_service_(NULL),
72       profile_(NULL),
73       active_fetcher_count_(0),
74       toast_state_(true) {
75   Update(sync_data);
76 }
77
78 SyncedNotification::~SyncedNotification() {}
79
80 void SyncedNotification::Update(const syncer::SyncData& sync_data) {
81   // TODO(petewil): Add checking that the notification looks valid.
82   specifics_.CopyFrom(sync_data.GetSpecifics().synced_notification());
83 }
84
85 sync_pb::EntitySpecifics SyncedNotification::GetEntitySpecifics() const {
86   sync_pb::EntitySpecifics entity_specifics;
87   entity_specifics.mutable_synced_notification()->CopyFrom(specifics_);
88   return entity_specifics;
89 }
90
91 // TODO(petewil): The fetch mechanism appears to be returning two bitmaps on the
92 // mac - perhaps one is regular, one is high dpi?  If so, ensure we use the high
93 // dpi bitmap when appropriate.
94 void SyncedNotification::OnFetchComplete(const GURL url,
95                                          const SkBitmap* bitmap) {
96   // TODO(petewil): Add timeout mechanism in case bitmaps take too long.  Do we
97   // already have one built into URLFetcher?
98   // Make sure we are on the thread we expect.
99   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
100
101   // Match the incoming bitmaps to URLs.  In case this is a dup, make sure to
102   // try all potentially matching urls.
103   if (GetAppIconUrl() == url && bitmap != NULL) {
104     app_icon_bitmap_ = gfx::Image::CreateFrom1xBitmap(*bitmap);
105   }
106   if (GetImageUrl() == url && bitmap != NULL) {
107     image_bitmap_ = gfx::Image::CreateFrom1xBitmap(*bitmap);
108   }
109   if (GetProfilePictureUrl(0) == url && bitmap != NULL) {
110     sender_bitmap_ = gfx::Image::CreateFrom1xBitmap(*bitmap);
111   }
112
113   // If this URL matches one or more button bitmaps, save them off.
114   for (unsigned int i = 0; i < GetButtonCount(); ++i) {
115     if (GetButtonIconUrl(i) == url && bitmap != NULL)
116       button_bitmaps_[i] = gfx::Image::CreateFrom1xBitmap(*bitmap);
117   }
118
119   // Count off the bitmaps as they arrive.
120   --active_fetcher_count_;
121
122   DVLOG(2) << __FUNCTION__ << " popping bitmap " << url;
123   DVLOG(2) << __FUNCTION__ << " size is " << bitmap->getSize();
124
125   // Check to see if all images we need are now present.
126   bool app_icon_ready = GetAppIconUrl().is_empty() ||
127       !app_icon_bitmap_.IsEmpty();
128   bool images_ready = GetImageUrl().is_empty() || !image_bitmap_.IsEmpty();
129   bool sender_picture_ready = GetProfilePictureUrl(0).is_empty() ||
130       !sender_bitmap_.IsEmpty();
131   bool button_bitmaps_ready = true;
132   for (unsigned int j = 0; j < GetButtonCount(); ++j) {
133     if (!GetButtonIconUrl(j).is_empty() && button_bitmaps_[j].IsEmpty()) {
134       button_bitmaps_ready = false;
135       break;
136     }
137   }
138   // See if all bitmaps are accounted for, if so call Show.
139   if (app_icon_ready && images_ready && sender_picture_ready &&
140       button_bitmaps_ready) {
141     Show(notification_manager_, notifier_service_, profile_);
142   }
143 }
144
145 void SyncedNotification::QueueBitmapFetchJobs(
146     NotificationUIManager* notification_manager,
147     ChromeNotifierService* notifier_service,
148     Profile* profile) {
149   // If we are not using the MessageCenter, call show now, and the existing
150   // code will handle the bitmap fetch for us.
151   if (!UseRichNotifications()) {
152     Show(notification_manager, notifier_service, profile);
153     return;
154   }
155
156   // Save off the arguments for the call to Show.
157   notification_manager_ = notification_manager;
158   notifier_service_ = notifier_service;
159   profile_ = profile;
160   DCHECK_EQ(active_fetcher_count_, 0);
161
162   // Ensure our bitmap vector has as many entries as there are buttons,
163   // so that when the bitmaps arrive the vector has a slot for them.
164   for (unsigned int i = 0; i < GetButtonCount(); ++i) {
165     button_bitmaps_.push_back(gfx::Image());
166     AddBitmapToFetchQueue(GetButtonIconUrl(i));
167   }
168
169   // If there is a profile image bitmap, fetch it
170   if (GetProfilePictureCount() > 0) {
171     // TODO(petewil): When we have the capacity to display more than one bitmap,
172     // modify this code to fetch as many as we can display
173     AddBitmapToFetchQueue(GetProfilePictureUrl(0));
174   }
175
176   // If the URL is non-empty, add it to our queue of URLs to fetch.
177   AddBitmapToFetchQueue(GetAppIconUrl());
178   AddBitmapToFetchQueue(GetImageUrl());
179
180   // If there are no bitmaps, call show now.
181   if (active_fetcher_count_ == 0) {
182     Show(notification_manager, notifier_service, profile);
183   }
184 }
185
186 void SyncedNotification::StartBitmapFetch() {
187   // Now that we have queued and counted them all, start the fetching.
188   ScopedVector<NotificationBitmapFetcher>::iterator iter;
189   for (iter = fetchers_.begin(); iter != fetchers_.end(); ++iter) {
190     (*iter)->Start(profile_);
191   }
192 }
193
194 void SyncedNotification::AddBitmapToFetchQueue(const GURL& url) {
195   // Check for dups, ignore any request for a dup.
196   ScopedVector<NotificationBitmapFetcher>::iterator iter;
197   for (iter = fetchers_.begin(); iter != fetchers_.end(); ++iter) {
198     if ((*iter)->url() == url)
199       return;
200   }
201
202   if (url.is_valid()) {
203     ++active_fetcher_count_;
204     fetchers_.push_back(new NotificationBitmapFetcher(url, this));
205     DVLOG(2) << __FUNCTION__ << "Pushing bitmap " << url;
206   }
207 }
208
209 void SyncedNotification::Show(NotificationUIManager* notification_manager,
210                               ChromeNotifierService* notifier_service,
211                               Profile* profile) {
212   // Let NotificationUIManager know that the notification has been dismissed.
213   if (SyncedNotification::kRead == GetReadState() ||
214       SyncedNotification::kDismissed == GetReadState() ) {
215     notification_manager->CancelById(GetKey());
216     DVLOG(2) << "Dismissed or read notification arrived"
217              << GetHeading() << " " << GetText();
218     return;
219   }
220
221   // Set up the fields we need to send and create a Notification object.
222   GURL image_url = GetImageUrl();
223   string16 text = UTF8ToUTF16(GetText());
224   string16 heading = UTF8ToUTF16(GetHeading());
225   string16 description = UTF8ToUTF16(GetDescription());
226   string16 annotation = UTF8ToUTF16(GetAnnotation());
227   // TODO(petewil): Eventually put the display name of the sending service here.
228   string16 display_source = UTF8ToUTF16(GetAppId());
229   string16 replace_key = UTF8ToUTF16(GetKey());
230   string16 notification_heading = heading;
231   string16 notification_text = description;
232   string16 newline = UTF8ToUTF16("\n");
233
234   // The delegate will eventually catch calls that the notification
235   // was read or deleted, and send the changes back to the server.
236   scoped_refptr<NotificationDelegate> delegate =
237       new ChromeNotifierDelegate(GetKey(), notifier_service);
238
239   // Some inputs and fields are only used if there is a notification center.
240   if (UseRichNotifications()) {
241     base::Time creation_time =
242         base::Time::FromDoubleT(static_cast<double>(GetCreationTime()));
243     int priority = GetPriority();
244     unsigned int button_count = GetButtonCount();
245
246     // Deduce which notification template to use from the data.
247     message_center::NotificationType notification_type =
248         message_center::NOTIFICATION_TYPE_BASE_FORMAT;
249     if (!image_url.is_empty()) {
250       notification_type = message_center::NOTIFICATION_TYPE_IMAGE;
251     } else if (button_count > 0) {
252       notification_type = message_center::NOTIFICATION_TYPE_BASE_FORMAT;
253     }
254
255     // Fill the optional fields with the information we need to make a
256     // notification.
257     message_center::RichNotificationData rich_notification_data;
258     rich_notification_data.timestamp = creation_time;
259     if (priority != SyncedNotification::kUndefinedPriority)
260       rich_notification_data.priority = priority;
261
262     // Fill in the button data.
263     // TODO(petewil): Today Rich notifiations are limited to two buttons.
264     // When rich notifications supports more, remove the
265     // "&& i < kMaxNotificationButtonIndex" clause below.
266     for (unsigned int i = 0;
267          i < button_count
268          && i < button_bitmaps_.size()
269          && i < kMaxNotificationButtonIndex;
270          ++i) {
271       // Stop at the first button with no title
272       std::string title = GetButtonTitle(i);
273       if (title.empty())
274         break;
275       message_center::ButtonInfo button_info(UTF8ToUTF16(title));
276       if (!button_bitmaps_[i].IsEmpty())
277         button_info.icon = button_bitmaps_[i];
278       rich_notification_data.buttons.push_back(button_info);
279     }
280
281     // Fill in the bitmap images.
282     if (!image_bitmap_.IsEmpty())
283       rich_notification_data.image = image_bitmap_;
284
285     // Set the ContextMessage inside the rich notification data for the
286     // annotation.
287     rich_notification_data.context_message = annotation;
288
289     // If there is at least one person sending, use the first picture.
290     // TODO(petewil): Someday combine multiple profile photos here.
291     gfx::Image icon_bitmap = app_icon_bitmap_;
292     if (GetProfilePictureCount() >= 1)  {
293       icon_bitmap = sender_bitmap_;
294     }
295
296     Notification ui_notification(notification_type,
297                                  GetOriginUrl(),
298                                  notification_heading,
299                                  notification_text,
300                                  icon_bitmap,
301                                  WebKit::WebTextDirectionDefault,
302                                  message_center::NotifierId(GetOriginUrl()),
303                                  display_source,
304                                  replace_key,
305                                  rich_notification_data,
306                                  delegate.get());
307
308     // In case the notification is not supposed to be toasted, pretend that it
309     // has already been shown.
310     ui_notification.set_shown_as_popup(!toast_state_);
311
312     notification_manager->Add(ui_notification, profile);
313   } else {
314     // In this case we have a Webkit Notification, not a Rich Notification.
315     Notification ui_notification(GetOriginUrl(),
316                                  GetAppIconUrl(),
317                                  notification_heading,
318                                  notification_text,
319                                  WebKit::WebTextDirectionDefault,
320                                  display_source,
321                                  replace_key,
322                                  delegate.get());
323
324     notification_manager->Add(ui_notification, profile);
325   }
326
327   DVLOG(1) << "Showing Synced Notification! " << heading << " " << text
328            << " " << GetAppIconUrl() << " " << replace_key << " "
329            << GetProfilePictureUrl(0) << " " << GetReadState();
330
331   return;
332 }
333
334 // This should detect even small changes in case the server updated the
335 // notification.  We ignore the timestamp if other fields match.
336 bool SyncedNotification::EqualsIgnoringReadState(
337     const SyncedNotification& other) const {
338   if (GetTitle() == other.GetTitle() &&
339       GetHeading() == other.GetHeading() &&
340       GetDescription() == other.GetDescription() &&
341       GetAnnotation() == other.GetAnnotation() &&
342       GetAppId() == other.GetAppId() &&
343       GetKey() == other.GetKey() &&
344       GetOriginUrl() == other.GetOriginUrl() &&
345       GetAppIconUrl() == other.GetAppIconUrl() &&
346       GetImageUrl() == other.GetImageUrl() &&
347       GetText() == other.GetText() &&
348       // We intentionally skip read state
349       GetCreationTime() == other.GetCreationTime() &&
350       GetPriority() == other.GetPriority() &&
351       GetDefaultDestinationTitle() == other.GetDefaultDestinationTitle() &&
352       GetDefaultDestinationIconUrl() == other.GetDefaultDestinationIconUrl() &&
353       GetNotificationCount() == other.GetNotificationCount() &&
354       GetButtonCount() == other.GetButtonCount() &&
355       GetProfilePictureCount() == other.GetProfilePictureCount()) {
356
357     // If all the surface data matched, check, to see if contained data also
358     // matches, titles and messages.
359     size_t count = GetNotificationCount();
360     for (size_t ii = 0; ii < count; ++ii) {
361       if (GetContainedNotificationTitle(ii) !=
362           other.GetContainedNotificationTitle(ii))
363         return false;
364       if (GetContainedNotificationMessage(ii) !=
365           other.GetContainedNotificationMessage(ii))
366         return false;
367     }
368
369     // Make sure buttons match.
370     count = GetButtonCount();
371     for (size_t jj = 0; jj < count; ++jj) {
372       if (GetButtonTitle(jj) != other.GetButtonTitle(jj))
373         return false;
374       if (GetButtonIconUrl(jj) != other.GetButtonIconUrl(jj))
375         return false;
376     }
377
378     // Make sure profile icons match
379     count = GetButtonCount();
380     for (size_t kk = 0; kk < count; ++kk) {
381       if (GetProfilePictureUrl(kk) != other.GetProfilePictureUrl(kk))
382         return false;
383     }
384
385     // If buttons and notifications matched, they are equivalent.
386     return true;
387   }
388
389   return false;
390 }
391
392 void SyncedNotification::LogNotification() {
393   std::string readStateString("Unread");
394   if (SyncedNotification::kRead == GetReadState())
395     readStateString = "Read";
396   else if (SyncedNotification::kDismissed == GetReadState())
397     readStateString = "Dismissed";
398
399   DVLOG(2) << " Notification: Heading is " << GetHeading()
400            << " description is " << GetDescription()
401            << " key is " << GetKey()
402            << " read state is " << readStateString;
403 }
404
405 // Set the read state on the notification, returns true for success.
406 void SyncedNotification::SetReadState(const ReadState& read_state) {
407
408   // Convert the read state to the protobuf type for read state.
409   if (kDismissed == read_state)
410     specifics_.mutable_coalesced_notification()->set_read_state(
411         sync_pb::CoalescedSyncedNotification_ReadState_DISMISSED);
412   else if (kUnread == read_state)
413     specifics_.mutable_coalesced_notification()->set_read_state(
414         sync_pb::CoalescedSyncedNotification_ReadState_UNREAD);
415   else if (kRead == read_state)
416     specifics_.mutable_coalesced_notification()->set_read_state(
417         sync_pb::CoalescedSyncedNotification_ReadState_READ);
418   else
419     NOTREACHED();
420 }
421
422 void SyncedNotification::NotificationHasBeenRead() {
423   SetReadState(kRead);
424 }
425
426 void SyncedNotification::NotificationHasBeenDismissed() {
427   SetReadState(kDismissed);
428 }
429
430 std::string SyncedNotification::GetTitle() const {
431   if (!specifics_.coalesced_notification().render_info().expanded_info().
432       simple_expanded_layout().has_title())
433     return std::string();
434
435   return specifics_.coalesced_notification().render_info().expanded_info().
436       simple_expanded_layout().title();
437 }
438
439 std::string SyncedNotification::GetHeading() const {
440   if (!specifics_.coalesced_notification().render_info().collapsed_info().
441       simple_collapsed_layout().has_heading())
442     return std::string();
443
444   return specifics_.coalesced_notification().render_info().collapsed_info().
445       simple_collapsed_layout().heading();
446 }
447
448 std::string SyncedNotification::GetDescription() const {
449   if (!specifics_.coalesced_notification().render_info().collapsed_info().
450       simple_collapsed_layout().has_description())
451     return std::string();
452
453   return specifics_.coalesced_notification().render_info().collapsed_info().
454       simple_collapsed_layout().description();
455 }
456
457 std::string SyncedNotification::GetAnnotation() const {
458   if (!specifics_.coalesced_notification().render_info().collapsed_info().
459       simple_collapsed_layout().has_annotation())
460     return std::string();
461
462   return specifics_.coalesced_notification().render_info().collapsed_info().
463       simple_collapsed_layout().annotation();
464 }
465
466 std::string SyncedNotification::GetAppId() const {
467   if (!specifics_.coalesced_notification().has_app_id())
468     return std::string();
469   return specifics_.coalesced_notification().app_id();
470 }
471
472 std::string SyncedNotification::GetKey() const {
473   if (!specifics_.coalesced_notification().has_key())
474     return std::string();
475   return specifics_.coalesced_notification().key();
476 }
477
478 GURL SyncedNotification::GetOriginUrl() const {
479   std::string origin_url(kExtensionScheme);
480   origin_url += GetAppId();
481   return GURL(origin_url);
482 }
483
484 GURL SyncedNotification::GetAppIconUrl() const {
485   if (!specifics_.coalesced_notification().render_info().collapsed_info().
486       simple_collapsed_layout().has_app_icon())
487     return GURL();
488
489   std::string url_spec = specifics_.coalesced_notification().render_info().
490               collapsed_info().simple_collapsed_layout().app_icon().url();
491
492   return AddDefaultSchemaIfNeeded(url_spec);
493 }
494
495 // TODO(petewil): This ignores all but the first image.  If Rich Notifications
496 // supports more images someday, then fetch all images.
497 GURL SyncedNotification::GetImageUrl() const {
498   if (specifics_.coalesced_notification().render_info().collapsed_info().
499       simple_collapsed_layout().media_size() == 0)
500     return GURL();
501
502   if (!specifics_.coalesced_notification().render_info().collapsed_info().
503       simple_collapsed_layout().media(0).image().has_url())
504     return GURL();
505
506   std::string url_spec = specifics_.coalesced_notification().render_info().
507               collapsed_info().simple_collapsed_layout().media(0).image().url();
508
509   return AddDefaultSchemaIfNeeded(url_spec);
510 }
511
512 std::string SyncedNotification::GetText() const {
513   if (!specifics_.coalesced_notification().render_info().expanded_info().
514       simple_expanded_layout().has_text())
515     return std::string();
516
517   return specifics_.coalesced_notification().render_info().expanded_info().
518       simple_expanded_layout().text();
519 }
520
521 SyncedNotification::ReadState SyncedNotification::GetReadState() const {
522   DCHECK(specifics_.coalesced_notification().has_read_state());
523
524   sync_pb::CoalescedSyncedNotification_ReadState found_read_state =
525       specifics_.coalesced_notification().read_state();
526
527   if (found_read_state ==
528       sync_pb::CoalescedSyncedNotification_ReadState_DISMISSED) {
529     return kDismissed;
530   } else if (found_read_state ==
531              sync_pb::CoalescedSyncedNotification_ReadState_UNREAD) {
532     return kUnread;
533   } else if (found_read_state ==
534              sync_pb::CoalescedSyncedNotification_ReadState_READ) {
535     return kRead;
536   } else {
537     NOTREACHED();
538     return static_cast<SyncedNotification::ReadState>(found_read_state);
539   }
540 }
541
542 // Time in milliseconds since the unix epoch, or 0 if not available.
543 uint64 SyncedNotification::GetCreationTime() const {
544   if (!specifics_.coalesced_notification().has_creation_time_msec())
545     return 0;
546
547   return specifics_.coalesced_notification().creation_time_msec();
548 }
549
550 int SyncedNotification::GetPriority() const {
551   if (!specifics_.coalesced_notification().has_priority())
552     return kUndefinedPriority;
553   int protobuf_priority = specifics_.coalesced_notification().priority();
554
555   // Convert the prioroty to the scheme used by the notification center.
556   if (protobuf_priority ==
557       sync_pb::CoalescedSyncedNotification_Priority_LOW) {
558     return message_center::LOW_PRIORITY;
559   } else if (protobuf_priority ==
560              sync_pb::CoalescedSyncedNotification_Priority_STANDARD) {
561     return message_center::DEFAULT_PRIORITY;
562   } else if (protobuf_priority ==
563              sync_pb::CoalescedSyncedNotification_Priority_HIGH) {
564     return message_center::HIGH_PRIORITY;
565   } else {
566     // Complain if this is a new priority we have not seen before.
567     DCHECK(protobuf_priority <
568            sync_pb::CoalescedSyncedNotification_Priority_LOW  ||
569            sync_pb::CoalescedSyncedNotification_Priority_HIGH <
570            protobuf_priority);
571     return kUndefinedPriority;
572   }
573 }
574
575 size_t SyncedNotification::GetNotificationCount() const {
576   return specifics_.coalesced_notification().render_info().
577       expanded_info().collapsed_info_size();
578 }
579
580 size_t SyncedNotification::GetButtonCount() const {
581   return specifics_.coalesced_notification().render_info().collapsed_info().
582       target_size();
583 }
584
585 size_t SyncedNotification::GetProfilePictureCount() const {
586   return specifics_.coalesced_notification().render_info().collapsed_info().
587       simple_collapsed_layout().profile_image_size();
588 }
589
590 GURL SyncedNotification::GetProfilePictureUrl(unsigned int which_url) const {
591   if (GetProfilePictureCount() <= which_url)
592     return GURL();
593
594   std::string url_spec = specifics_.coalesced_notification().render_info().
595       collapsed_info().simple_collapsed_layout().profile_image(which_url).
596       image_url();
597
598   return AddDefaultSchemaIfNeeded(url_spec);
599 }
600
601
602 std::string SyncedNotification::GetDefaultDestinationTitle() const {
603   if (!specifics_.coalesced_notification().render_info().collapsed_info().
604       default_destination().icon().has_alt_text()) {
605     return std::string();
606   }
607   return specifics_.coalesced_notification().render_info().collapsed_info().
608       default_destination().icon().alt_text();
609 }
610
611 GURL SyncedNotification::GetDefaultDestinationIconUrl() const {
612   if (!specifics_.coalesced_notification().render_info().collapsed_info().
613       default_destination().icon().has_url()) {
614     return GURL();
615   }
616   std::string url_spec = specifics_.coalesced_notification().render_info().
617               collapsed_info().default_destination().icon().url();
618
619   return AddDefaultSchemaIfNeeded(url_spec);
620 }
621
622 GURL SyncedNotification::GetDefaultDestinationUrl() const {
623   if (!specifics_.coalesced_notification().render_info().collapsed_info().
624       default_destination().has_url()) {
625     return GURL();
626   }
627   std::string url_spec = specifics_.coalesced_notification().render_info().
628               collapsed_info().default_destination().url();
629
630   return AddDefaultSchemaIfNeeded(url_spec);
631 }
632
633 std::string SyncedNotification::GetButtonTitle(
634     unsigned int which_button) const {
635   // Must ensure that we have a target before trying to access it.
636   if (GetButtonCount() <= which_button)
637     return std::string();
638   if (!specifics_.coalesced_notification().render_info().collapsed_info().
639       target(which_button).action().icon().has_alt_text()) {
640     return std::string();
641   }
642   return specifics_.coalesced_notification().render_info().collapsed_info().
643       target(which_button).action().icon().alt_text();
644 }
645
646 GURL SyncedNotification::GetButtonIconUrl(unsigned int which_button) const {
647   // Must ensure that we have a target before trying to access it.
648   if (GetButtonCount() <= which_button)
649     return GURL();
650   if (!specifics_.coalesced_notification().render_info().collapsed_info().
651       target(which_button).action().icon().has_url()) {
652     return GURL();
653   }
654   std::string url_spec = specifics_.coalesced_notification().render_info().
655               collapsed_info().target(which_button).action().icon().url();
656
657   return AddDefaultSchemaIfNeeded(url_spec);
658 }
659
660 GURL SyncedNotification::GetButtonUrl(unsigned int which_button) const {
661   // Must ensure that we have a target before trying to access it.
662   if (GetButtonCount() <= which_button)
663     return GURL();
664   if (!specifics_.coalesced_notification().render_info().collapsed_info().
665       target(which_button).action().has_url()) {
666     return GURL();
667   }
668   std::string url_spec = specifics_.coalesced_notification().render_info().
669               collapsed_info().target(which_button).action().url();
670
671   return AddDefaultSchemaIfNeeded(url_spec);
672 }
673
674 std::string SyncedNotification::GetContainedNotificationTitle(
675     int index) const {
676   if (specifics_.coalesced_notification().render_info().expanded_info().
677       collapsed_info_size() < index + 1)
678     return std::string();
679
680   return specifics_.coalesced_notification().render_info().expanded_info().
681       collapsed_info(index).simple_collapsed_layout().heading();
682 }
683
684 std::string SyncedNotification::GetContainedNotificationMessage(
685     int index) const {
686   if (specifics_.coalesced_notification().render_info().expanded_info().
687       collapsed_info_size() < index + 1)
688     return std::string();
689
690   return specifics_.coalesced_notification().render_info().expanded_info().
691       collapsed_info(index).simple_collapsed_layout().description();
692 }
693
694 std::string SyncedNotification::GetSendingServiceId() const {
695   // TODO(petewil): We are building a new protocol (a new sync datatype) to send
696   // the service name and icon from the server.  For now this method is
697   // hardcoded to the name of our first service using synced notifications.
698   // Once the new protocol is built, remove this hardcoding.
699   return kFirstSyncedNotificationServiceId;
700 }
701
702 void SyncedNotification::SetToastState(bool toast_state) {
703   toast_state_ = toast_state;
704 }
705
706 }  // namespace notifier