Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / notifications / sync_notifier / synced_notification.cc
index cddced0..ad795a0 100644 (file)
@@ -15,7 +15,9 @@
 #include "chrome/browser/notifications/notification_ui_manager.h"
 #include "chrome/browser/notifications/sync_notifier/chrome_notifier_delegate.h"
 #include "chrome/browser/notifications/sync_notifier/chrome_notifier_service.h"
+#include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/browser_thread.h"
+#include "net/base/load_flags.h"
 #include "skia/ext/image_operations.h"
 #include "sync/protocol/sync.pb.h"
 #include "sync/protocol/synced_notification_specifics.pb.h"
@@ -26,7 +28,6 @@
 #include "ui/gfx/size.h"
 #include "ui/gfx/skbitmap_operations.h"
 #include "ui/message_center/message_center_style.h"
-#include "ui/message_center/message_center_util.h"
 #include "ui/message_center/notification_types.h"
 
 namespace {
@@ -37,10 +38,6 @@ const char kDefaultSyncedNotificationScheme[] = "https:";
 // try to supply them with more than this number of buttons.
 const unsigned int kMaxNotificationButtonIndex = 2;
 
-bool UseRichNotifications() {
-  return message_center::IsRichNotificationEnabled();
-}
-
 // Schema-less specs default badly in windows.  If we find one, add the schema
 // we expect instead of allowing windows specific GURL code to make it default
 // to "file:".
@@ -68,9 +65,12 @@ COMPILE_ASSERT(static_cast<sync_pb::CoalescedSyncedNotification_ReadState>(
                sync_pb::CoalescedSyncedNotification_ReadState_DISMISSED,
                local_enum_must_match_protobuf_enum);
 
-SyncedNotification::SyncedNotification(const syncer::SyncData& sync_data)
-    : notification_manager_(NULL),
-      notifier_service_(NULL),
+SyncedNotification::SyncedNotification(
+    const syncer::SyncData& sync_data,
+    ChromeNotifierService* notifier_service,
+    NotificationUIManager* notification_manager)
+    : notification_manager_(notification_manager),
+      notifier_service_(notifier_service),
       profile_(NULL),
       toast_state_(true),
       app_icon_bitmap_fetch_pending_(true),
@@ -86,98 +86,169 @@ void SyncedNotification::Update(const syncer::SyncData& sync_data) {
   specifics_.CopyFrom(sync_data.GetSpecifics().synced_notification());
 }
 
-sync_pb::EntitySpecifics SyncedNotification::GetEntitySpecifics() const {
-  sync_pb::EntitySpecifics entity_specifics;
-  entity_specifics.mutable_synced_notification()->CopyFrom(specifics_);
-  return entity_specifics;
-}
-
-// Check that we have either fetched or gotten an error on all the bitmaps we
-// asked for.
-bool SyncedNotification::AreAllBitmapsFetched() {
-  bool app_icon_ready = GetAppIconUrl().is_empty() ||
-      !app_icon_bitmap_.IsEmpty() || !app_icon_bitmap_fetch_pending_;
-  bool images_ready = GetImageUrl().is_empty() || !image_bitmap_.IsEmpty() ||
-      !image_bitmap_fetch_pending_;
-  bool sender_picture_ready = GetProfilePictureUrl(0).is_empty() ||
-      !sender_bitmap_.IsEmpty() || !sender_bitmap_fetch_pending_;
-  bool button_bitmaps_ready = true;
-  for (unsigned int j = 0; j < GetButtonCount(); ++j) {
-    if (!GetButtonIconUrl(j).is_empty()
-        && button_bitmaps_[j].IsEmpty()
-        && button_bitmaps_fetch_pending_[j]) {
-      button_bitmaps_ready = false;
-      break;
-    }
+void SyncedNotification::Show(Profile* profile) {
+  // Let NotificationUIManager know that the notification has been dismissed.
+  if (SyncedNotification::kRead == GetReadState() ||
+      SyncedNotification::kDismissed == GetReadState() ) {
+    notification_manager_->CancelById(GetKey());
+    DVLOG(2) << "Dismissed or read notification arrived"
+             << GetHeading() << " " << GetText();
+    return;
   }
 
-  return app_icon_ready && images_ready && sender_picture_ready &&
-      button_bitmaps_ready;
-}
+  // |notifier_service| can be NULL in tests.
+  if (notifier_service_) {
+    notifier_service_->ShowWelcomeToastIfNecessary(this, notification_manager_);
+  }
 
-// TODO(petewil): The fetch mechanism appears to be returning two bitmaps on the
-// mac - perhaps one is regular, one is high dpi?  If so, ensure we use the high
-// dpi bitmap when appropriate.
-void SyncedNotification::OnFetchComplete(const GURL url,
-                                         const SkBitmap* bitmap) {
-  // TODO(petewil): Add timeout mechanism in case bitmaps take too long.  Do we
-  // already have one built into URLFetcher?
-  // Make sure we are on the thread we expect.
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  // Set up the fields we need to send and create a Notification object.
+  GURL image_url = GetImageUrl();
+  base::string16 text = base::UTF8ToUTF16(GetText());
+  base::string16 heading = base::UTF8ToUTF16(GetHeading());
+  base::string16 description = base::UTF8ToUTF16(GetDescription());
+  base::string16 annotation = base::UTF8ToUTF16(GetAnnotation());
+  // TODO(petewil): Eventually put the display name of the sending service here.
+  base::string16 display_source = base::UTF8ToUTF16(GetAppId());
+  base::string16 replace_key = base::UTF8ToUTF16(GetKey());
+  base::string16 notification_heading = heading;
+  base::string16 notification_text = description;
+  base::string16 newline = base::UTF8ToUTF16("\n");
 
-  gfx::Image downloaded_image;
-  if (bitmap != NULL)
-    downloaded_image = gfx::Image::CreateFrom1xBitmap(*bitmap);
+  // The delegate will eventually catch calls that the notification
+  // was read or deleted, and send the changes back to the server.
+  scoped_refptr<NotificationDelegate> delegate =
+      new ChromeNotifierDelegate(GetKey(), notifier_service_);
 
-  // Match the incoming bitmaps to URLs.  In case this is a dup, make sure to
-  // try all potentially matching urls.
-  if (GetAppIconUrl() == url) {
-    app_icon_bitmap_ = downloaded_image;
-    if (app_icon_bitmap_.IsEmpty())
-      app_icon_bitmap_fetch_pending_ = false;
+  // Some inputs and fields are only used if there is a notification center.
+  base::Time creation_time =
+      base::Time::FromDoubleT(static_cast<double>(GetCreationTime()));
+  int priority = GetPriority();
+  unsigned int button_count = GetButtonCount();
+
+  // Deduce which notification template to use from the data.
+  message_center::NotificationType notification_type =
+      message_center::NOTIFICATION_TYPE_BASE_FORMAT;
+  if (!image_url.is_empty()) {
+    notification_type = message_center::NOTIFICATION_TYPE_IMAGE;
+  } else if (button_count > 0) {
+    notification_type = message_center::NOTIFICATION_TYPE_BASE_FORMAT;
   }
-  if (GetImageUrl() == url) {
-    image_bitmap_ = downloaded_image;
-    if (image_bitmap_.IsEmpty())
-      image_bitmap_fetch_pending_ = false;
+
+  // Fill the optional fields with the information we need to make a
+  // notification.
+  message_center::RichNotificationData rich_notification_data;
+  rich_notification_data.timestamp = creation_time;
+  if (priority != SyncedNotification::kUndefinedPriority)
+    rich_notification_data.priority = priority;
+
+  // Fill in the button data.
+  // TODO(petewil): Today Rich notifiations are limited to two buttons.
+  // When rich notifications supports more, remove the
+  // "&& i < kMaxNotificationButtonIndex" clause below.
+  for (unsigned int i = 0;
+        i < button_count
+        && i < button_bitmaps_.size()
+        && i < kMaxNotificationButtonIndex;
+        ++i) {
+    // Stop at the first button with no title
+    std::string title = GetButtonTitle(i);
+    if (title.empty())
+      break;
+    message_center::ButtonInfo button_info(base::UTF8ToUTF16(title));
+    if (!button_bitmaps_[i].IsEmpty())
+      button_info.icon = button_bitmaps_[i];
+    rich_notification_data.buttons.push_back(button_info);
   }
-  if (GetProfilePictureUrl(0) == url) {
-    sender_bitmap_ = downloaded_image;
-    if (sender_bitmap_.IsEmpty())
-      sender_bitmap_fetch_pending_ = false;
+
+  // Fill in the bitmap images.
+  if (!image_bitmap_.IsEmpty())
+    rich_notification_data.image = image_bitmap_;
+
+  if (!app_icon_bitmap_.IsEmpty()) {
+    // Since we can't control the size of images we download, resize using a
+    // high quality filter down to the appropriate icon size.
+    // TODO(dewittj): Remove this when correct resources are sent via the
+    // protobuf.
+    SkBitmap new_app_icon =
+        skia::ImageOperations::Resize(app_icon_bitmap_.AsBitmap(),
+                                      skia::ImageOperations::RESIZE_BEST,
+                                      message_center::kSmallImageSize,
+                                      message_center::kSmallImageSize);
+
+    // The app icon should be in grayscale.
+    // TODO(dewittj): Remove this when correct resources are sent via the
+    // protobuf.
+    color_utils::HSL shift = {-1, 0, 0.6};
+    SkBitmap grayscale =
+        SkBitmapOperations::CreateHSLShiftedBitmap(new_app_icon, shift);
+    gfx::Image small_image =
+        gfx::Image(gfx::ImageSkia(gfx::ImageSkiaRep(grayscale, 1.0f)));
+    rich_notification_data.small_image = small_image;
   }
 
-  // If this URL matches one or more button bitmaps, save them off.
-  for (unsigned int i = 0; i < GetButtonCount(); ++i) {
-    if (GetButtonIconUrl(i) == url) {
-      if (bitmap != NULL) {
-        button_bitmaps_[i] = gfx::Image::CreateFrom1xBitmap(*bitmap);
-      }
-      button_bitmaps_fetch_pending_[i] = false;
-    }
+  // Set the ContextMessage inside the rich notification data for the
+  // annotation.
+  rich_notification_data.context_message = annotation;
+
+  // Set the clickable flag to change the cursor on hover if a valid
+  // destination is found.
+  rich_notification_data.clickable = GetDefaultDestinationUrl().is_valid();
+
+  // If there is at least one person sending, use the first picture.
+  // TODO(petewil): Someday combine multiple profile photos here.
+  gfx::Image icon_bitmap = app_icon_bitmap_;
+  if (GetProfilePictureCount() >= 1)  {
+    icon_bitmap = sender_bitmap_;
   }
 
-  DVLOG(2) << __FUNCTION__ << " popping bitmap " << url;
+  Notification ui_notification(notification_type,
+                                GetOriginUrl(),
+                                notification_heading,
+                                notification_text,
+                                icon_bitmap,
+                                blink::WebTextDirectionDefault,
+                                message_center::NotifierId(GetOriginUrl()),
+                                display_source,
+                                replace_key,
+                                rich_notification_data,
+                                delegate.get());
+  // In case the notification is not supposed to be toasted, pretend that it
+  // has already been shown.
+  ui_notification.set_shown_as_popup(!toast_state_);
+
+  notification_manager_->Add(ui_notification, profile);
 
-  // See if all bitmaps are already accounted for, if so call Show.
-  if (AreAllBitmapsFetched()) {
-    Show(notification_manager_, notifier_service_, profile_);
+  DVLOG(1) << "Showing Synced Notification! " << heading << " " << text
+           << " " << GetAppIconUrl() << " " << replace_key << " "
+           << GetProfilePictureUrl(0) << " " << GetReadState();
+
+  return;
+}
+
+sync_pb::EntitySpecifics SyncedNotification::GetEntitySpecifics() const {
+  sync_pb::EntitySpecifics entity_specifics;
+  entity_specifics.mutable_synced_notification()->CopyFrom(specifics_);
+  return entity_specifics;
+}
+
+// Display the notification if it has the specified app_id_name.
+void SyncedNotification::ShowAllForAppId(Profile* profile,
+                                         std::string app_id_name) {
+  if (app_id_name == GetAppId())
+    Show(profile);
+}
+
+// Remove the notification if it has the specified app_id_name.
+void SyncedNotification::HideAllForAppId(std::string app_id_name) {
+  if (app_id_name == GetAppId()) {
+    notification_manager_->CancelById(GetKey());
   }
 }
 
 void SyncedNotification::QueueBitmapFetchJobs(
-    NotificationUIManager* notification_manager,
     ChromeNotifierService* notifier_service,
     Profile* profile) {
-  // If we are not using the MessageCenter, call show now, and the existing
-  // code will handle the bitmap fetch for us.
-  if (!UseRichNotifications()) {
-    Show(notification_manager, notifier_service, profile);
-    return;
-  }
-
   // Save off the arguments for the call to Show.
-  notification_manager_ = notification_manager;
   notifier_service_ = notifier_service;
   profile_ = profile;
 
@@ -186,26 +257,26 @@ void SyncedNotification::QueueBitmapFetchJobs(
   for (unsigned int i = 0; i < GetButtonCount(); ++i) {
     button_bitmaps_.push_back(gfx::Image());
     button_bitmaps_fetch_pending_.push_back(true);
-    AddBitmapToFetchQueue(GetButtonIconUrl(i));
+    CreateBitmapFetcher(GetButtonIconUrl(i));
   }
 
   // If there is a profile image bitmap, fetch it
   if (GetProfilePictureCount() > 0) {
     // TODO(petewil): When we have the capacity to display more than one bitmap,
     // modify this code to fetch as many as we can display
-    AddBitmapToFetchQueue(GetProfilePictureUrl(0));
+    CreateBitmapFetcher(GetProfilePictureUrl(0));
   }
 
   // If the URL is non-empty, add it to our queue of URLs to fetch.
-  AddBitmapToFetchQueue(GetAppIconUrl());
-  AddBitmapToFetchQueue(GetImageUrl());
+  CreateBitmapFetcher(GetAppIconUrl());
+  CreateBitmapFetcher(GetImageUrl());
 
   // Check to see if we don't need to fetch images, either because we already
   // did, or because the URLs are empty. If so, we can display the notification.
 
   // See if all bitmaps are accounted for, if so call Show().
   if (AreAllBitmapsFetched()) {
-    Show(notification_manager_, notifier_service_, profile_);
+    Show(profile_);
   }
 }
 
@@ -213,177 +284,12 @@ void SyncedNotification::StartBitmapFetch() {
   // Now that we have queued and counted them all, start the fetching.
   ScopedVector<chrome::BitmapFetcher>::iterator iter;
   for (iter = fetchers_.begin(); iter != fetchers_.end(); ++iter) {
-    (*iter)->Start(profile_);
-  }
-}
-
-void SyncedNotification::AddBitmapToFetchQueue(const GURL& url) {
-  // Check for dups, ignore any request for a dup.
-  ScopedVector<chrome::BitmapFetcher>::iterator iter;
-  for (iter = fetchers_.begin(); iter != fetchers_.end(); ++iter) {
-    if ((*iter)->url() == url)
-      return;
-  }
-
-  if (url.is_valid()) {
-    fetchers_.push_back(new chrome::BitmapFetcher(url, this));
-    DVLOG(2) << __FUNCTION__ << "Pushing bitmap " << url;
-  }
-}
-
-void SyncedNotification::Show(NotificationUIManager* notification_manager,
-                              ChromeNotifierService* notifier_service,
-                              Profile* profile) {
-  // Let NotificationUIManager know that the notification has been dismissed.
-  if (SyncedNotification::kRead == GetReadState() ||
-      SyncedNotification::kDismissed == GetReadState() ) {
-    notification_manager->CancelById(GetKey());
-    DVLOG(2) << "Dismissed or read notification arrived"
-             << GetHeading() << " " << GetText();
-    return;
-  }
-
-  // |notifier_service| can be NULL in tests.
-  if (UseRichNotifications() && notifier_service) {
-    notifier_service->ShowWelcomeToastIfNecessary(this, notification_manager);
-  }
-
-  // Set up the fields we need to send and create a Notification object.
-  GURL image_url = GetImageUrl();
-  base::string16 text = base::UTF8ToUTF16(GetText());
-  base::string16 heading = base::UTF8ToUTF16(GetHeading());
-  base::string16 description = base::UTF8ToUTF16(GetDescription());
-  base::string16 annotation = base::UTF8ToUTF16(GetAnnotation());
-  // TODO(petewil): Eventually put the display name of the sending service here.
-  base::string16 display_source = base::UTF8ToUTF16(GetAppId());
-  base::string16 replace_key = base::UTF8ToUTF16(GetKey());
-  base::string16 notification_heading = heading;
-  base::string16 notification_text = description;
-  base::string16 newline = base::UTF8ToUTF16("\n");
-
-  // The delegate will eventually catch calls that the notification
-  // was read or deleted, and send the changes back to the server.
-  scoped_refptr<NotificationDelegate> delegate =
-      new ChromeNotifierDelegate(GetKey(), notifier_service);
-
-  // Some inputs and fields are only used if there is a notification center.
-  if (UseRichNotifications()) {
-    base::Time creation_time =
-        base::Time::FromDoubleT(static_cast<double>(GetCreationTime()));
-    int priority = GetPriority();
-    unsigned int button_count = GetButtonCount();
-
-    // Deduce which notification template to use from the data.
-    message_center::NotificationType notification_type =
-        message_center::NOTIFICATION_TYPE_BASE_FORMAT;
-    if (!image_url.is_empty()) {
-      notification_type = message_center::NOTIFICATION_TYPE_IMAGE;
-    } else if (button_count > 0) {
-      notification_type = message_center::NOTIFICATION_TYPE_BASE_FORMAT;
-    }
-
-    // Fill the optional fields with the information we need to make a
-    // notification.
-    message_center::RichNotificationData rich_notification_data;
-    rich_notification_data.timestamp = creation_time;
-    if (priority != SyncedNotification::kUndefinedPriority)
-      rich_notification_data.priority = priority;
-
-    // Fill in the button data.
-    // TODO(petewil): Today Rich notifiations are limited to two buttons.
-    // When rich notifications supports more, remove the
-    // "&& i < kMaxNotificationButtonIndex" clause below.
-    for (unsigned int i = 0;
-         i < button_count
-         && i < button_bitmaps_.size()
-         && i < kMaxNotificationButtonIndex;
-         ++i) {
-      // Stop at the first button with no title
-      std::string title = GetButtonTitle(i);
-      if (title.empty())
-        break;
-      message_center::ButtonInfo button_info(base::UTF8ToUTF16(title));
-      if (!button_bitmaps_[i].IsEmpty())
-        button_info.icon = button_bitmaps_[i];
-      rich_notification_data.buttons.push_back(button_info);
-    }
-
-    // Fill in the bitmap images.
-    if (!image_bitmap_.IsEmpty())
-      rich_notification_data.image = image_bitmap_;
-
-    if (!app_icon_bitmap_.IsEmpty()) {
-      // Since we can't control the size of images we download, resize using a
-      // high quality filter down to the appropriate icon size.
-      // TODO(dewittj): Remove this when correct resources are sent via the
-      // protobuf.
-      SkBitmap new_app_icon =
-          skia::ImageOperations::Resize(app_icon_bitmap_.AsBitmap(),
-                                        skia::ImageOperations::RESIZE_BEST,
-                                        message_center::kSmallImageSize,
-                                        message_center::kSmallImageSize);
-
-      // The app icon should be in grayscale.
-      // TODO(dewittj): Remove this when correct resources are sent via the
-      // protobuf.
-      color_utils::HSL shift = {-1, 0, 0.6};
-      SkBitmap grayscale =
-          SkBitmapOperations::CreateHSLShiftedBitmap(new_app_icon, shift);
-      gfx::Image small_image =
-          gfx::Image(gfx::ImageSkia(gfx::ImageSkiaRep(grayscale, 1.0f)));
-      rich_notification_data.small_image = small_image;
-    }
-
-    // Set the ContextMessage inside the rich notification data for the
-    // annotation.
-    rich_notification_data.context_message = annotation;
-
-    // Set the clickable flag to change the cursor on hover if a valid
-    // destination is found.
-    rich_notification_data.clickable = GetDefaultDestinationUrl().is_valid();
-
-    // If there is at least one person sending, use the first picture.
-    // TODO(petewil): Someday combine multiple profile photos here.
-    gfx::Image icon_bitmap = app_icon_bitmap_;
-    if (GetProfilePictureCount() >= 1)  {
-      icon_bitmap = sender_bitmap_;
-    }
-
-    Notification ui_notification(notification_type,
-                                 GetOriginUrl(),
-                                 notification_heading,
-                                 notification_text,
-                                 icon_bitmap,
-                                 blink::WebTextDirectionDefault,
-                                 message_center::NotifierId(GetOriginUrl()),
-                                 display_source,
-                                 replace_key,
-                                 rich_notification_data,
-                                 delegate.get());
-    // In case the notification is not supposed to be toasted, pretend that it
-    // has already been shown.
-    ui_notification.set_shown_as_popup(!toast_state_);
-
-    notification_manager->Add(ui_notification, profile);
-  } else {
-    // In this case we have a Webkit Notification, not a Rich Notification.
-    Notification ui_notification(GetOriginUrl(),
-                                 GetAppIconUrl(),
-                                 notification_heading,
-                                 notification_text,
-                                 blink::WebTextDirectionDefault,
-                                 display_source,
-                                 replace_key,
-                                 delegate.get());
-
-    notification_manager->Add(ui_notification, profile);
+    (*iter)->Start(
+        profile_->GetRequestContext(),
+        std::string(),
+        net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+        net::LOAD_NORMAL);
   }
-
-  DVLOG(1) << "Showing Synced Notification! " << heading << " " << text
-           << " " << GetAppIconUrl() << " " << replace_key << " "
-           << GetProfilePictureUrl(0) << " " << GetReadState();
-
-  return;
 }
 
 // This should detect even small changes in case the server updated the
@@ -444,36 +350,6 @@ bool SyncedNotification::EqualsIgnoringReadState(
   return false;
 }
 
-void SyncedNotification::LogNotification() {
-  std::string readStateString("Unread");
-  if (SyncedNotification::kRead == GetReadState())
-    readStateString = "Read";
-  else if (SyncedNotification::kDismissed == GetReadState())
-    readStateString = "Dismissed";
-
-  DVLOG(2) << " Notification: Heading is " << GetHeading()
-           << " description is " << GetDescription()
-           << " key is " << GetKey()
-           << " read state is " << readStateString;
-}
-
-// Set the read state on the notification, returns true for success.
-void SyncedNotification::SetReadState(const ReadState& read_state) {
-
-  // Convert the read state to the protobuf type for read state.
-  if (kDismissed == read_state)
-    specifics_.mutable_coalesced_notification()->set_read_state(
-        sync_pb::CoalescedSyncedNotification_ReadState_DISMISSED);
-  else if (kUnread == read_state)
-    specifics_.mutable_coalesced_notification()->set_read_state(
-        sync_pb::CoalescedSyncedNotification_ReadState_UNREAD);
-  else if (kRead == read_state)
-    specifics_.mutable_coalesced_notification()->set_read_state(
-        sync_pb::CoalescedSyncedNotification_ReadState_READ);
-  else
-    NOTREACHED();
-}
-
 void SyncedNotification::NotificationHasBeenRead() {
   SetReadState(kRead);
 }
@@ -609,10 +485,10 @@ int SyncedNotification::GetPriority() const {
 
   // Convert the prioroty to the scheme used by the notification center.
   if (protobuf_priority ==
-      sync_pb::CoalescedSyncedNotification_Priority_LOW) {
+      sync_pb::CoalescedSyncedNotification_Priority_INVISIBLE) {
     return message_center::LOW_PRIORITY;
   } else if (protobuf_priority ==
-             sync_pb::CoalescedSyncedNotification_Priority_STANDARD) {
+             sync_pb::CoalescedSyncedNotification_Priority_LOW) {
     return message_center::DEFAULT_PRIORITY;
   } else if (protobuf_priority ==
              sync_pb::CoalescedSyncedNotification_Priority_HIGH) {
@@ -620,40 +496,13 @@ int SyncedNotification::GetPriority() const {
   } else {
     // Complain if this is a new priority we have not seen before.
     DCHECK(protobuf_priority <
-           sync_pb::CoalescedSyncedNotification_Priority_LOW  ||
+           sync_pb::CoalescedSyncedNotification_Priority_INVISIBLE  ||
            sync_pb::CoalescedSyncedNotification_Priority_HIGH <
            protobuf_priority);
     return kUndefinedPriority;
   }
 }
 
-size_t SyncedNotification::GetNotificationCount() const {
-  return specifics_.coalesced_notification().render_info().
-      expanded_info().collapsed_info_size();
-}
-
-size_t SyncedNotification::GetButtonCount() const {
-  return specifics_.coalesced_notification().render_info().collapsed_info().
-      target_size();
-}
-
-size_t SyncedNotification::GetProfilePictureCount() const {
-  return specifics_.coalesced_notification().render_info().collapsed_info().
-      simple_collapsed_layout().profile_image_size();
-}
-
-GURL SyncedNotification::GetProfilePictureUrl(unsigned int which_url) const {
-  if (GetProfilePictureCount() <= which_url)
-    return GURL();
-
-  std::string url_spec = specifics_.coalesced_notification().render_info().
-      collapsed_info().simple_collapsed_layout().profile_image(which_url).
-      image_url();
-
-  return AddDefaultSchemaIfNeeded(url_spec);
-}
-
-
 std::string SyncedNotification::GetDefaultDestinationTitle() const {
   if (!specifics_.coalesced_notification().render_info().collapsed_info().
       default_destination().icon().has_alt_text()) {
@@ -726,6 +575,31 @@ GURL SyncedNotification::GetButtonUrl(unsigned int which_button) const {
   return AddDefaultSchemaIfNeeded(url_spec);
 }
 
+GURL SyncedNotification::GetProfilePictureUrl(unsigned int which_url) const {
+  if (GetProfilePictureCount() <= which_url)
+    return GURL();
+
+  std::string url_spec = specifics_.coalesced_notification().render_info().
+      collapsed_info().simple_collapsed_layout().profile_image(which_url).
+      image_url();
+
+  return AddDefaultSchemaIfNeeded(url_spec);
+}
+
+size_t SyncedNotification::GetProfilePictureCount() const {
+  return specifics_.coalesced_notification().render_info().collapsed_info().
+      simple_collapsed_layout().profile_image_size();
+}
+
+size_t SyncedNotification::GetNotificationCount() const {
+  return specifics_.coalesced_notification().render_info().
+      expanded_info().collapsed_info_size();
+}
+
+size_t SyncedNotification::GetButtonCount() const {
+  return specifics_.coalesced_notification().render_info().collapsed_info().
+      target_size();
+}
 std::string SyncedNotification::GetContainedNotificationTitle(
     int index) const {
   if (specifics_.coalesced_notification().render_info().expanded_info().
@@ -746,20 +620,127 @@ std::string SyncedNotification::GetContainedNotificationMessage(
       collapsed_info(index).simple_collapsed_layout().description();
 }
 
-std::string SyncedNotification::GetSendingServiceId() const {
-  // TODO(petewil): We are building a new protocol (a new sync datatype) to send
-  // the service name and icon from the server.  For now this method is
-  // hardcoded to the name of our first service using synced notifications.
-  // Once the new protocol is built, remove this hardcoding.
-  return kFirstSyncedNotificationServiceId;
-}
-
 const gfx::Image& SyncedNotification::GetAppIcon() const {
   return app_icon_bitmap_;
 }
 
-void SyncedNotification::SetToastState(bool toast_state) {
+void SyncedNotification::set_toast_state(bool toast_state) {
   toast_state_ = toast_state;
 }
 
+void SyncedNotification::LogNotification() {
+  std::string readStateString("Unread");
+  if (SyncedNotification::kRead == GetReadState())
+    readStateString = "Read";
+  else if (SyncedNotification::kDismissed == GetReadState())
+    readStateString = "Dismissed";
+
+  DVLOG(2) << " Notification: Heading is " << GetHeading()
+           << " description is " << GetDescription()
+           << " key is " << GetKey()
+           << " read state is " << readStateString;
+}
+
+// TODO(petewil): The fetch mechanism appears to be returning two bitmaps on the
+// mac - perhaps one is regular, one is high dpi?  If so, ensure we use the high
+// dpi bitmap when appropriate.
+void SyncedNotification::OnFetchComplete(const GURL url,
+                                         const SkBitmap* bitmap) {
+  // Make sure we are on the thread we expect.
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+  gfx::Image downloaded_image;
+  if (bitmap != NULL)
+    downloaded_image = gfx::Image::CreateFrom1xBitmap(*bitmap);
+
+  // Match the incoming bitmaps to URLs.  In case this is a dup, make sure to
+  // try all potentially matching urls.
+  if (GetAppIconUrl() == url) {
+    app_icon_bitmap_ = downloaded_image;
+    if (app_icon_bitmap_.IsEmpty())
+      app_icon_bitmap_fetch_pending_ = false;
+  }
+  if (GetImageUrl() == url) {
+    image_bitmap_ = downloaded_image;
+    if (image_bitmap_.IsEmpty())
+      image_bitmap_fetch_pending_ = false;
+  }
+  if (GetProfilePictureUrl(0) == url) {
+    sender_bitmap_ = downloaded_image;
+    if (sender_bitmap_.IsEmpty())
+      sender_bitmap_fetch_pending_ = false;
+  }
+
+  // If this URL matches one or more button bitmaps, save them off.
+  for (unsigned int i = 0; i < GetButtonCount(); ++i) {
+    if (GetButtonIconUrl(i) == url) {
+      if (bitmap != NULL) {
+        button_bitmaps_[i] = gfx::Image::CreateFrom1xBitmap(*bitmap);
+      }
+      button_bitmaps_fetch_pending_[i] = false;
+    }
+  }
+
+  DVLOG(2) << __FUNCTION__ << " popping bitmap " << url;
+
+  // See if all bitmaps are already accounted for, if so call Show.
+  if (AreAllBitmapsFetched()) {
+    Show(profile_);
+  }
+}
+
+void SyncedNotification::CreateBitmapFetcher(const GURL& url) {
+  // Check for dups, ignore any request for a dup.
+  ScopedVector<chrome::BitmapFetcher>::iterator iter;
+  for (iter = fetchers_.begin(); iter != fetchers_.end(); ++iter) {
+    if ((*iter)->url() == url)
+      return;
+  }
+
+  if (url.is_valid()) {
+    fetchers_.push_back(new chrome::BitmapFetcher(url, this));
+    DVLOG(2) << __FUNCTION__ << "Pushing bitmap " << url;
+  }
+}
+
+// Check that we have either fetched or gotten an error on all the bitmaps we
+// asked for.
+bool SyncedNotification::AreAllBitmapsFetched() {
+  bool app_icon_ready = GetAppIconUrl().is_empty() ||
+      !app_icon_bitmap_.IsEmpty() || !app_icon_bitmap_fetch_pending_;
+  bool images_ready = GetImageUrl().is_empty() || !image_bitmap_.IsEmpty() ||
+      !image_bitmap_fetch_pending_;
+  bool sender_picture_ready = GetProfilePictureUrl(0).is_empty() ||
+      !sender_bitmap_.IsEmpty() || !sender_bitmap_fetch_pending_;
+  bool button_bitmaps_ready = true;
+  for (unsigned int j = 0; j < GetButtonCount(); ++j) {
+    if (!GetButtonIconUrl(j).is_empty()
+        && button_bitmaps_[j].IsEmpty()
+        && button_bitmaps_fetch_pending_[j]) {
+      button_bitmaps_ready = false;
+      break;
+    }
+  }
+
+  return app_icon_ready && images_ready && sender_picture_ready &&
+      button_bitmaps_ready;
+}
+
+// Set the read state on the notification, returns true for success.
+void SyncedNotification::SetReadState(const ReadState& read_state) {
+
+  // Convert the read state to the protobuf type for read state.
+  if (kDismissed == read_state)
+    specifics_.mutable_coalesced_notification()->set_read_state(
+        sync_pb::CoalescedSyncedNotification_ReadState_DISMISSED);
+  else if (kUnread == read_state)
+    specifics_.mutable_coalesced_notification()->set_read_state(
+        sync_pb::CoalescedSyncedNotification_ReadState_UNREAD);
+  else if (kRead == read_state)
+    specifics_.mutable_coalesced_notification()->set_read_state(
+        sync_pb::CoalescedSyncedNotification_ReadState_READ);
+  else
+    NOTREACHED();
+}
+
 }  // namespace notifier