Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5247)

Unified Diff: chrome/browser/sync/notifier/communicator/mailbox.cc

Issue 1956001: Moved XMPP notifier library from chrome/browser/sync to chrome/common.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 10 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/sync/notifier/communicator/mailbox.cc
===================================================================
--- chrome/browser/sync/notifier/communicator/mailbox.cc (revision 46353)
+++ chrome/browser/sync/notifier/communicator/mailbox.cc (working copy)
@@ -1,675 +0,0 @@
-// Copyright (c) 2009 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/sync/notifier/communicator/mailbox.h"
-
-#include <assert.h>
-#include <stdlib.h>
-
-#include <stack>
-#include <vector>
-
-#include "chrome/browser/sync/notifier/base/string.h"
-#include "chrome/browser/sync/notifier/base/utils.h"
-#include "chrome/browser/sync/notifier/communicator/xml_parse_helpers.h"
-#include "talk/base/basictypes.h"
-#include "talk/base/common.h"
-#include "talk/base/stringutils.h"
-#include "talk/xmllite/xmlelement.h"
-#include "talk/xmpp/constants.h"
-
-namespace notifier {
-
-// Labels are a list of strings seperated by a '|' character. The '|' character
-// is escaped with a backslash ('\\') and the backslash is also escaped with a
-// backslash.
-static void ParseLabelSet(const std::string& text,
- MessageThread::StringSet* labels) {
- const char* input_cur = text.c_str();
- const char* input_end = input_cur + text.size();
- char* result = new char[text.size() + 1];
- char* next_write = result;
-
- while (input_cur < input_end) {
- if (*input_cur == '|') {
- if (next_write != result) {
- *next_write = '\0';
- labels->insert(std::string(result));
- next_write = result;
- }
- input_cur++;
- continue;
- }
-
- if (*input_cur == '\\') {
- // Skip a character in the input and break if we are at the end.
- input_cur++;
- if (input_cur >= input_end)
- break;
- }
- *next_write = *input_cur;
- next_write++;
- input_cur++;
- }
-
- if (next_write != result) {
- *next_write = '\0';
- labels->insert(std::string(result));
- }
-
- delete [] result;
-}
-
-// -----------------------------------------------------------------------------
-
-std::string MailAddress::safe_name() const {
- if (!name().empty()) {
- return name();
- }
-
- if (!address().empty()) {
- size_t at = address().find('@');
- if (at == std::string::npos) {
- return address();
- }
-
- if (at != 0) {
- return address().substr(0, at);
- }
- }
-
- return std::string("(unknown)");
-}
-
-// -----------------------------------------------------------------------------
-MessageThread::~MessageThread() {
- Clear();
-}
-
-void MessageThread::Clear() {
- delete labels_;
- labels_ = NULL;
-
- delete senders_;
- senders_ = NULL;
-}
-
-MessageThread& MessageThread::operator=(const MessageThread& r) {
- if (&r != this) {
- Clear();
- // Copy everything.
- r.AssertValid();
- thread_id_ = r.thread_id_;
- date64_ = r.date64_;
- message_count_ = r.message_count_;
- personal_level_ = r.personal_level_;
- subject_ = r.subject_;
- snippet_ = r.snippet_;
-
- if (r.labels_)
- labels_ = new StringSet(*r.labels_);
- else
- labels_ = new StringSet;
- if (r.senders_)
- senders_ = new MailSenderList(*r.senders_);
- else
- senders_ = new MailSenderList;
- }
- AssertValid();
- return *this;
-}
-
-MessageThread* MessageThread::CreateFromXML(
- const buzz::XmlElement* src) {
- MessageThread* info = new MessageThread();
- if (!info || !info->InitFromXml(src)) {
- delete info;
- return NULL;
- }
- return info;
-}
-
-// Init from a chunk of XML.
-bool MessageThread::InitFromXml(const buzz::XmlElement* src) {
- labels_ = new StringSet;
- senders_ = new MailSenderList;
-
- if (src->Name() != buzz::kQnMailThreadInfo)
- return false;
-
- if (!ParseInt64Attr(src, buzz::kQnMailTid, &thread_id_))
- return false;
- if (!ParseInt64Attr(src, buzz::kQnMailDate, &date64_))
- return false;
- if (!ParseIntAttr(src, buzz::kQnMailMessages, &message_count_))
- return false;
- if (!ParseIntAttr(src, buzz::kQnMailParticipation, &personal_level_))
- return false;
-
- const buzz::XmlElement* senders = src->FirstNamed(buzz::kQnMailSenders);
- if (!senders)
- return false;
- for (const buzz::XmlElement* child = senders->FirstElement();
- child != NULL;
- child = child->NextElement()) {
- if (child->Name() != buzz::kQnMailSender)
- continue;
- std::string address;
- if (!ParseStringAttr(child, buzz::kQnMailAddress, &address))
- continue;
- std::string name;
- ParseStringAttr(child, buzz::kQnMailName, &name);
- bool originator = false;
- ParseBoolAttr(child, buzz::kQnMailOriginator, &originator);
- bool unread = false;
- ParseBoolAttr(child, buzz::kQnMailUnread, &unread);
-
- senders_->push_back(MailSender(name, address, unread, originator));
- }
-
- const buzz::XmlElement* labels = src->FirstNamed(buzz::kQnMailLabels);
- if (!labels)
- return false;
- ParseLabelSet(labels->BodyText(), labels_);
-
- const buzz::XmlElement* subject = src->FirstNamed(buzz::kQnMailSubject);
- if (!subject)
- return false;
- subject_ = subject->BodyText();
-
- const buzz::XmlElement* snippet = src->FirstNamed(buzz::kQnMailSnippet);
- if (!snippet)
- return false;
- snippet_ = snippet->BodyText();
-
- AssertValid();
- return true;
-}
-
-bool MessageThread::starred() const {
- return (labels_->find("^t") != labels_->end());
-}
-
-bool MessageThread::unread() const {
- return (labels_->find("^u") != labels_->end());
-}
-
-#if defined(DEBUG)
-// Non-debug version is inline and empty.
-void MessageThread::AssertValid() const {
- assert(thread_id_ != 0);
- assert(senders_ != NULL);
- // In some (odd) cases, gmail may send email with no sender.
- // assert(!senders_->empty());
- assert(message_count_ > 0);
- assert(labels_ != NULL);
-}
-#endif
-
-MailBox* MailBox::CreateFromXML(const buzz::XmlElement* src) {
- MailBox* mail_box = new MailBox();
- if (!mail_box || !mail_box->InitFromXml(src)) {
- delete mail_box;
- return NULL;
- }
- return mail_box;
-}
-
-// Init from a chunk of XML.
-bool MailBox::InitFromXml(const buzz::XmlElement* src) {
- if (src->Name() != buzz::kQnMailBox)
- return false;
-
- if (!ParseIntAttr(src, buzz::kQnMailTotalMatched, &mailbox_size_))
- return false;
-
- estimate_ = false;
- ParseBoolAttr(src, buzz::kQnMailTotalEstimate, &estimate_);
-
- first_index_ = 0;
- ParseIntAttr(src, buzz::kQnMailFirstIndex, &first_index_);
-
- result_time_ = 0;
- ParseInt64Attr(src, buzz::kQnMailResultTime, &result_time_);
-
- highest_thread_id_ = 0;
-
- const buzz::XmlElement* thread_element = src->FirstNamed(buzz::kQnMailThreadInfo);
- while (thread_element) {
- MessageThread* thread = MessageThread::CreateFromXML(thread_element);
- if (thread) {
- if (thread->thread_id() > highest_thread_id_)
- highest_thread_id_ = thread->thread_id();
- threads_.push_back(MessageThreadPointer(thread));
- }
- thread_element = thread_element->NextNamed(buzz::kQnMailThreadInfo);
- }
- return true;
-}
-
-const size_t kMaxShortnameLength = 12;
-
-// Tip: If you extend this list of chars, do not include '-'.
-const char name_delim[] = " ,.:;\'\"()[]{}<>*@";
-
-class SenderFormatter {
- public:
- // sender should not be deleted while this class is in use.
- SenderFormatter(const MailSender& sender,
- const std::string& me_address)
- : sender_(sender),
- visible_(false),
- short_format_(true),
- space_(kMaxShortnameLength) {
- me_ = talk_base::ascicmp(me_address.c_str(),
- sender.address().c_str()) == 0;
- }
-
- bool visible() const {
- return visible_;
- }
-
- bool is_unread() const {
- return sender_.unread();
- }
-
- const std::string name() const {
- return name_;
- }
-
- void set_short_format(bool short_format) {
- short_format_ = short_format;
- UpdateName();
- }
-
- void set_visible(bool visible) {
- visible_ = visible;
- UpdateName();
- }
-
- void set_space(size_t space) {
- space_ = space;
- UpdateName();
- }
-
- private:
- // Attempt to shorten to the first word in a person's name We could revisit
- // and do better at international punctuation, but this is what cricket did,
- // and it should be removed soon when gmail does the notification instead of
- // us forming it on the client.
- static void ShortenName(std::string* name) {
- size_t start = name->find_first_not_of(name_delim);
- if (start != std::string::npos && start > 0) {
- name->erase(0, start);
- }
- start = name->find_first_of(name_delim);
- if (start != std::string::npos) {
- name->erase(start);
- }
- }
-
- void UpdateName() {
- // Update the name if is going to be used.
- if (!visible_) {
- return;
- }
-
- if (me_) {
- name_ = "me";
- return;
- }
-
- if (sender_.name().empty() && sender_.address().empty()) {
- name_ = "";
- return;
- }
-
- name_ = sender_.name();
- // Handle the case of no name or a name looks like an email address. When
- // mail is sent to "Quality@example.com" <quality-team@example.com>, we
- // shouldn't show "Quality@example.com" as the name. Instead, use the email
- // address (without the @...)
- if (name_.empty() || name_.find_first_of("@") != std::string::npos) {
- name_ = sender_.address();
- size_t at_index = name_.find_first_of("@");
- if (at_index != std::string::npos) {
- name_.erase(at_index);
- }
- } else if (short_format_) {
- ShortenName(&name_);
- }
-
- if (name_.empty()) {
- name_ = "(unknown)";
- }
-
- // Abbreviate if too long.
- if (name_.size() > space_) {
- name_.replace(space_ - 1, std::string::npos, ".");
- }
- }
-
- const MailSender& sender_;
- std::string name_;
- bool visible_;
- bool short_format_;
- size_t space_;
- bool me_;
- DISALLOW_COPY_AND_ASSIGN(SenderFormatter);
-};
-
-const char kNormalSeparator[] = ",&nbsp;";
-const char kEllidedSeparator[] = "&nbsp;..";
-
-std::string FormatName(const std::string& name, bool bold) {
- std::string formatted_name;
- if (bold) {
- formatted_name.append("<b>");
- }
- formatted_name.append(HtmlEncode(name));
- if (bold) {
- formatted_name.append("</b>");
- }
- return formatted_name;
-}
-
-class SenderFormatterList {
- public:
- // sender_list must not be deleted while this class is being used.
- SenderFormatterList(const MailSenderList& sender_list,
- const std::string& me_address)
- : state_(INITIAL_STATE),
- are_any_read_(false),
- index_(-1),
- first_unread_index_(-1) {
- // Add all read messages.
- const MailSender* originator = NULL;
- bool any_unread = false;
- for (size_t i = 0; i < sender_list.size(); ++i) {
- if (sender_list[i].originator()) {
- originator = &sender_list[i];
- }
- if (sender_list[i].unread()) {
- any_unread = true;
- continue;
- }
- are_any_read_ = true;
- if (!sender_list[i].originator()) {
- senders_.push_back(new SenderFormatter(sender_list[i],
- me_address));
- }
- }
-
- // There may not be an orignator (if there no senders).
- if (originator) {
- senders_.insert(senders_.begin(), new SenderFormatter(*originator,
- me_address));
- }
-
- // Add all unread messages.
- if (any_unread) {
- // It should be rare, but there may be cases when all of the senders
- // appear to have read the message.
- first_unread_index_ = is_first_unread() ? 0 : senders_.size();
- for (size_t i = 0; i < sender_list.size(); ++i) {
- if (!sender_list[i].unread()) {
- continue;
- }
- // Don't add the originator if it is already at the start of the
- // "unread" list.
- if (sender_list[i].originator() && is_first_unread()) {
- continue;
- }
- senders_.push_back(new SenderFormatter(sender_list[i], me_address));
- }
- }
- }
-
- ~SenderFormatterList() {
- CleanupSequence(&senders_);
- }
-
- std::string GetHtml(int space) {
- index_ = -1;
- state_ = INITIAL_STATE;
- while (!added_.empty()) {
- added_.pop();
- }
-
- // If there is only one sender, let it take up all of the space.
- if (senders_.size() == 1) {
- senders_[0]->set_space(space);
- senders_[0]->set_short_format(false);
- }
-
- int length = 1;
- // Add as many senders as we can in the given space. Computes the visible
- // length at each iteration, but does not construct the actual html.
- while (length < space && AddNextSender()) {
- int new_length = ConstructHtml(is_first_unread(), NULL);
- // Remove names to avoid truncating
- // * if there will be at least 2 left or
- // * if the spacing <= 2 characters per sender and there
- // is at least one left.
- if ((new_length > space &&
- (visible_count() > 2 ||
- (ComputeSpacePerSender(space) <= 2 && visible_count() > 1)))) {
- RemoveLastAddedSender();
- break;
- }
- length = new_length;
- }
-
- if (length > space) {
- int max = ComputeSpacePerSender(space);
- for (size_t i = 0; i < senders_.size(); ++i) {
- if (senders_[i]->visible()) {
- senders_[i]->set_space(max);
- }
- }
- }
-
- // Now construct the actual html.
- std::string html_list;
- length = ConstructHtml(is_first_unread(), &html_list);
- if (length > space) {
- LOG(LS_WARNING) << "LENGTH: " << length << " exceeds "
- << space << " " << html_list;
- }
- return html_list;
- }
-
- private:
- int ComputeSpacePerSender(int space) const {
- // Why the "- 2"? To allow for the " .. " which may occur after the names,
- // and no matter what always allow at least 2 characters per sender.
- return talk_base::_max<int>(space / visible_count() - 2, 2);
- }
-
- // Finds the next sender that should be added to the "from" list and sets it
- // to visible.
- //
- // This method may be called until it returns false or until
- // RemoveLastAddedSender is called.
- bool AddNextSender() {
- // The progression is:
- // 1. Add the person who started the thread, which is the first message.
- // 2. Add the first unread message (unless it has already been added).
- // 3. Add the last message (unless it has already been added).
- // 4. Add the message that is just before the last message processed
- // (unless it has already been added).
- // If there is no message (i.e. at index -1), return false.
- //
- // Typically, this method is called until it returns false or all of the
- // space available is used.
- switch (state_) {
- case INITIAL_STATE:
- state_ = FIRST_MESSAGE;
- index_ = 0;
- // If the server behaves odd and doesn't send us any senders, do
- // something graceful.
- if (senders_.size() == 0) {
- return false;
- }
- break;
-
- case FIRST_MESSAGE:
- if (first_unread_index_ >= 0) {
- state_ = FIRST_UNREAD_MESSAGE;
- index_ = first_unread_index_;
- break;
- }
- // Fall through.
- case FIRST_UNREAD_MESSAGE:
- state_ = LAST_MESSAGE;
- index_ = senders_.size() - 1;
- break;
-
- case LAST_MESSAGE:
- case PREVIOUS_MESSAGE:
- state_ = PREVIOUS_MESSAGE;
- index_--;
- break;
-
- case REMOVED_MESSAGE:
- default:
- ASSERT(false);
- return false;
- }
-
- if (index_ < 0) {
- return false;
- }
-
- if (!senders_[index_]->visible()) {
- added_.push(index_);
- senders_[index_]->set_visible(true);
- }
- return true;
- }
-
- // Makes the last added sender not visible.
- //
- // May be called while visible_count() > 0.
- void RemoveLastAddedSender() {
- state_ = REMOVED_MESSAGE;
- int index = added_.top();
- added_.pop();
- senders_[index]->set_visible(false);
- }
-
- // Constructs the html of the SenderList and returns the length of the
- // visible text.
- //
- // The algorithm simply walks down the list of Senders, appending the html
- // for each visible sender, and adding ellipsis or commas in between,
- // whichever is appropriate.
- //
- // html Filled with html. Maybe NULL if the html doesn't need to be
- // constructed yet (useful for simply determining the length of the
- // visible text).
- //
- // returns The approximate visible length of the html.
- int ConstructHtml(bool first_is_unread,
- std::string* html) const {
- if (senders_.empty()) {
- return 0;
- }
-
- int length = 0;
-
- // The first is always visible.
- const SenderFormatter* sender = senders_[0];
- const std::string& originator_name = sender->name();
- length += originator_name.length();
- if (html) {
- html->append(FormatName(originator_name, first_is_unread));
- }
-
- bool elided = false;
- const char* between = "";
- for (size_t i = 1; i < senders_.size(); i++) {
- sender = senders_[i];
-
- if (sender->visible()) {
- // Handle the separator.
- between = elided ? "&nbsp;" : kNormalSeparator;
- // Ignore the , for length because it is so narrow, so in both cases
- // above the space is the only things that counts for spaces.
- length++;
-
- // Handle the name.
- const std::string name = sender->name();
- length += name.size();
-
- // Construct the html.
- if (html) {
- html->append(between);
- html->append(FormatName(name, sender->is_unread()));
- }
- elided = false;
- } else if (!elided) {
- between = kEllidedSeparator;
- length += 2; // ".." is narrow.
- if (html) {
- html->append(between);
- }
- elided = true;
- }
- }
- return length;
- }
-
- bool is_first_unread() const {
- return !are_any_read_;
- }
-
- size_t visible_count() const {
- return added_.size();
- }
-
- enum MessageState {
- INITIAL_STATE,
- FIRST_MESSAGE,
- FIRST_UNREAD_MESSAGE,
- LAST_MESSAGE,
- PREVIOUS_MESSAGE,
- REMOVED_MESSAGE,
- };
-
- // What state we were in during the last "stateful" function call.
- MessageState state_;
- bool are_any_read_;
- std::vector<SenderFormatter*> senders_;
- std::stack<int> added_;
- int index_;
- int first_unread_index_;
- DISALLOW_COPY_AND_ASSIGN(SenderFormatterList);
-};
-
-
-std::string GetSenderHtml(const MailSenderList& sender_list,
- int message_count,
- const std::string& me_address,
- int space) {
- // There has to be at least 9 spaces to show something reasonable.
- ASSERT(space >= 10);
- std::string count_html;
- if (message_count > 1) {
- std::string count(IntToString(message_count));
- space -= (count.size());
- count_html.append("&nbsp;(");
- count_html.append(count);
- count_html.append(")");
- // Reduce space due to parenthesis and &nbsp;.
- space -= 2;
- }
-
- SenderFormatterList senders(sender_list, me_address);
- std::string html_list(senders.GetHtml(space));
- html_list.append(count_html);
- return html_list;
-}
-
-} // namespace notifier
« no previous file with comments | « chrome/browser/sync/notifier/communicator/mailbox.h ('k') | chrome/browser/sync/notifier/communicator/mailbox_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698