diff options
author | msw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-13 06:08:46 +0000 |
---|---|---|
committer | msw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-13 06:08:46 +0000 |
commit | b106ca53e5f9159acfd33498850a535dcf7cfbb7 (patch) | |
tree | 1b8bb9a3844cfebe8ef2737baf5673728763ebe6 /chrome | |
parent | 653007448cee9796264581fdd087d2353c3e03a6 (diff) | |
download | chromium_src-b106ca53e5f9159acfd33498850a535dcf7cfbb7.zip chromium_src-b106ca53e5f9159acfd33498850a535dcf7cfbb7.tar.gz chromium_src-b106ca53e5f9159acfd33498850a535dcf7cfbb7.tar.bz2 |
Add Chrome To Mobile Service and Views Page Action.
Implements the Chrome To Mobile extension in Chrome.
List the user's mobile devices via the Cloud Print server.
Add a page action icon when the service reports 1+ devices.
Add a bubble to send the current page URL / MHTML snapshot.
The bubble shows a radio group for multiple devices.
(or it shows a single device as part of the title label)
The bubble also shows a checkbox to send an offline copy.
Send URLFetcher requests to GET/POST the URL/Snapshot.
The bubble shows "Sending..."/"Sent"/ error request status.
BUG=102709
TEST=New Chrome To Mobile bubble works as expected :)
Review URL: http://codereview.chromium.org/9443007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@126343 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
39 files changed, 1474 insertions, 187 deletions
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h index d7422c7..902f79a 100644 --- a/chrome/app/chrome_command_ids.h +++ b/chrome/app/chrome_command_ids.h @@ -68,6 +68,7 @@ #define IDC_ENCODING_MENU 35005 #define IDC_EMAIL_PAGE_LOCATION 35006 #define IDC_ADVANCED_PRINT 35007 +#define IDC_CHROME_TO_MOBILE_PAGE 35008 // When adding a new encoding to this list, be sure to append it to the // EncodingMenuController::kValidEncodingIds array in diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 3bfdc9c..5eeae70 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -5782,6 +5782,9 @@ Because search results are requested even while you're typing your query, your d <message name="IDS_ACCNAME_STAR" desc="The accessible name for the bookmark button."> Bookmark </message> + <message name="IDS_ACCNAME_CHROME_TO_MOBILE" desc="The accessible name for the Chrome To Mobile button."> + Chrome To Mobile + </message> <message name="IDS_ACCNAME_FIND" desc="The accessible name for the find button."> Find </message> @@ -6982,6 +6985,47 @@ The following plug-in is unresponsive: <ph name="PLUGIN_NAME">$1 </message> </if> + <!-- Chrome To Mobile bubble messages --> + <message name="IDS_CHROME_TO_MOBILE_BUBBLE_SINGLE_TITLE" desc="Title of the bubble when a single mobile device is available."> + Send this page to <ph name="DEVICE_NAME">$1<ex>Galaxy Nexus</ex></ph>. + </message> + <message name="IDS_CHROME_TO_MOBILE_BUBBLE_MULTI_TITLE" desc="Title of the bubble when multiple mobile devices are available."> + Send this page to: + </message> + <message name="IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY" desc="Checkbox for optionally sending an offline copy of the page to the mobile device."> + Also send a copy for offline viewing (<ph name="PAGE_SIZE">$1<ex>111kB</ex></ph>) + </message> + <message name="IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY_GENERATING" desc="Placeholder PAGE_SIZE text indicating that the offline copy is being generated."> + generating... + </message> + <message name="IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY_FAILED" desc="Text indicating that the offline copy generation failed."> + An error occured while generating the offline copy + </message> + <message name="IDS_CHROME_TO_MOBILE_BUBBLE_SEND" desc="Button to send the page to the target mobile device."> + Send + </message> + <message name="IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_0" desc="First label indicating that the page is being sent to the device."> + Sending + </message> + <message name="IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_1" desc="Second label indicating that the page is being sent to the device."> + Sending. + </message> + <message name="IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_2" desc="Third label indicating that the page is being sent to the device."> + Sending.. + </message> + <message name="IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_3" desc="Fourth label indicating that the page is being sent to the device."> + Sending... + </message> + <message name="IDS_CHROME_TO_MOBILE_BUBBLE_SENT" desc="Label indicating that the page has been sent to the device."> + Sent! + </message> + <message name="IDS_CHROME_TO_MOBILE_BUBBLE_ERROR" desc="Label indicating that an error has occured."> + An error occured while sending the page + </message> + <message name="IDS_CHROME_TO_MOBILE_BUBBLE_TOOLTIP" desc="The tooltip for the Chrome To Mobile button."> + Send this page to your mobile device + </message> + <!-- Application window menu --> <if expr="not pp_ifdef('use_titlecase')"> <message name="IDS_APP_MENU_RELOAD" desc="The reload menu in application windows"> diff --git a/chrome/browser/chrome_to_mobile_service.cc b/chrome/browser/chrome_to_mobile_service.cc new file mode 100755 index 0000000..edd2f24 --- /dev/null +++ b/chrome/browser/chrome_to_mobile_service.cc @@ -0,0 +1,336 @@ +// Copyright (c) 2012 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/chrome_to_mobile_service.h" + +#include "base/bind.h" +#include "base/json/json_writer.h" +#include "base/stringprintf.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/app/chrome_command_ids.h" +#include "chrome/browser/printing/cloud_print/cloud_print_url.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/signin/token_service.h" +#include "chrome/browser/signin/token_service_factory.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_list.h" +#include "chrome/common/cloud_print/cloud_print_helpers.h" +#include "chrome/common/guid.h" +#include "chrome/common/net/gaia/gaia_urls.h" +#include "chrome/common/net/gaia/oauth2_access_token_fetcher.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/url_fetcher.h" +#include "net/base/escape.h" +#include "net/base/load_flags.h" +#include "net/url_request/url_request_context_getter.h" + +namespace { + +// The maximum number of retries for the URLFetcher requests. +size_t kMaxRetries = 10; + +// Seconds between automatically retrying authentication (on failure). +int kAuthRetryDelay = 30; + +// Seconds between automatically updating the mobile device list. +int kAutoSearchRetryDelay = 300; + +// The cloud print Oath2 scope and 'printer' type of compatible mobile devices. +const char kOAuth2Scope[] = "https://www.googleapis.com/auth/cloudprint"; +const char kTypeAndroidChromeSnapshot[] = "ANDROID_CHROME_SNAPSHOT"; + +// The types of Chrome To Mobile requests sent to the cloud print service. +const char kRequestTypeURL[] = "url"; +const char kRequestTypeDelayedSnapshot[] = "url_with_delayed_snapshot"; +const char kRequestTypeSnapshot[] = "snapshot"; + +// The snapshot path constants; used with a guid for each MHTML snapshot file. +const FilePath::CharType kSnapshotPath[] = + FILE_PATH_LITERAL("chrome_to_mobile_snapshot_.mht"); + +// Get the "__c2dm__job_data" tag JSON data for the cloud print job submission. +std::string GetJobString(const ChromeToMobileService::RequestData& data) { + scoped_ptr<DictionaryValue> job(new DictionaryValue()); + job->SetString("url", data.url.spec()); + if (data.type == ChromeToMobileService::URL) { + job->SetString("type", kRequestTypeURL); + } else { + job->SetString("snapID", data.snapshot_id); + job->SetString("type", (data.type == ChromeToMobileService::SNAPSHOT) ? + kRequestTypeSnapshot : kRequestTypeDelayedSnapshot); + } + std::string job_string; + base::JSONWriter::Write(job.get(), false, &job_string); + return job_string; +} + +// Get the URL for cloud print job submission; appends query params if needed. +GURL GetSubmitURL(GURL service_url, + const ChromeToMobileService::RequestData& data) { + GURL submit_url = cloud_print::GetUrlForSubmit(service_url); + if (data.type == ChromeToMobileService::SNAPSHOT) + return submit_url; + + // Append form data to the URL's query for |URL| and |DELAYED_SNAPSHOT| jobs. + static const bool kUsePlus = true; + std::string tag_string = net::EscapeQueryParamValue( + "__c2dm__job_data=" + GetJobString(data), kUsePlus); + GURL::Replacements replacements; + // Provide dummy content to workaround |errorCode| 412 'Document missing'. + std::string query = StringPrintf("printerid=%s&tag=%s&title=%s" + "&contentType=text/plain&content=dummy", + net::EscapeQueryParamValue(UTF16ToUTF8(data.mobile_id), kUsePlus).c_str(), + net::EscapeQueryParamValue(tag_string, kUsePlus).c_str(), + net::EscapeQueryParamValue(UTF16ToUTF8(data.title), kUsePlus).c_str()); + replacements.SetQueryStr(query); + return submit_url.ReplaceComponents(replacements); +} + +// Delete the specified file; called as a BlockingPoolTask. +void DeleteFilePath(const FilePath& file_path) { + bool success = file_util::Delete(file_path, false); + DCHECK(success); +} + +// Construct POST data and submit the MHTML snapshot file; deletes the snapshot. +void SubmitSnapshot(content::URLFetcher* request, + const ChromeToMobileService::RequestData& data) { + std::string file; + if (file_util::ReadFileToString(data.snapshot_path, &file) && !file.empty()) { + std::string post_data, mime_boundary; + cloud_print::CreateMimeBoundaryForUpload(&mime_boundary); + cloud_print::AddMultipartValueForUpload("printerid", + UTF16ToUTF8(data.mobile_id), mime_boundary, std::string(), &post_data); + cloud_print::AddMultipartValueForUpload("tag", "__c2dm__job_data=" + + GetJobString(data), mime_boundary, std::string(), &post_data); + cloud_print::AddMultipartValueForUpload("title", UTF16ToUTF8(data.title), + mime_boundary, std::string(), &post_data); + cloud_print::AddMultipartValueForUpload("contentType", "multipart/related", + mime_boundary, std::string(), &post_data); + + // Append the snapshot MHTML content and terminate the request body. + post_data.append("--" + mime_boundary + "\r\n" + "Content-Disposition: form-data; " + "name=\"content\"; filename=\"blob\"\r\n" + "Content-Type: text/mhtml\r\n" + "\r\n" + file + "\r\n" "--" + mime_boundary + "--\r\n"); + std::string content_type = "multipart/form-data; boundary=" + mime_boundary; + request->SetUploadData(content_type, post_data); + request->Start(); + } + + content::BrowserThread::PostBlockingPoolTask(FROM_HERE, + base::Bind(&DeleteFilePath, data.snapshot_path)); +} + +} // namespace + +ChromeToMobileService::Observer::~Observer() {} + +ChromeToMobileService::RequestData::RequestData() {} + +ChromeToMobileService::RequestData::~RequestData() {} + +ChromeToMobileService::ChromeToMobileService(Profile* profile) + : profile_(profile), + cloud_print_url_(new CloudPrintURL(profile)), + oauth2_retry_count_(0) { + content::BrowserThread::PostBlockingPoolTask(FROM_HERE, + base::Bind(&ChromeToMobileService::CreateUniqueTempDir, + base::Unretained(this))); + RequestMobileListUpdate(); +} + +ChromeToMobileService::~ChromeToMobileService() {} + +void ChromeToMobileService::OnURLFetchComplete( + const content::URLFetcher* source) { + if (source == search_request_.get()) + HandleSearchResponse(); + else + HandleSubmitResponse(source); +} + +void ChromeToMobileService::OnGetTokenSuccess( + const std::string& access_token) { + DCHECK(!access_token.empty()); + oauth2_request_.reset(); + request_timer_.Stop(); + oauth2_token_ = access_token; + RequestMobileListUpdate(); +} + +void ChromeToMobileService::OnGetTokenFailure( + const GoogleServiceAuthError& error) { + oauth2_request_.reset(); + if (request_timer_.IsRunning() || (oauth2_retry_count_++ > kMaxRetries)) + return; + request_timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kAuthRetryDelay), + this, &ChromeToMobileService::RequestAuth); +} + +void ChromeToMobileService::RequestMobileListUpdate() { + if (oauth2_token_.empty()) + RequestAuth(); + else + RequestSearch(); +} + +void ChromeToMobileService::GenerateSnapshot(base::WeakPtr<Observer> observer) { + FilePath path(temp_dir_.path().Append(kSnapshotPath)); + BrowserList::GetLastActiveWithProfile(profile_)->GetSelectedWebContents()-> + GenerateMHTML(path.InsertBeforeExtensionASCII(guid::GenerateGUID()), + base::Bind(&Observer::SnapshotGenerated, observer)); +} + +void ChromeToMobileService::SendToMobile(const string16& mobile_id, + const FilePath& snapshot, + base::WeakPtr<Observer> observer) { + DCHECK(!oauth2_token_.empty()); + RequestData data; + data.mobile_id = mobile_id; + content::WebContents* web_contents = + BrowserList::GetLastActiveWithProfile(profile_)->GetSelectedWebContents(); + data.url = web_contents->GetURL(); + data.title = web_contents->GetTitle(); + data.snapshot_path = snapshot; + bool send_snapshot = !snapshot.empty(); + data.snapshot_id = send_snapshot ? guid::GenerateGUID() : std::string(); + data.type = send_snapshot ? DELAYED_SNAPSHOT : URL; + + content::URLFetcher* submit_url = CreateRequest(data); + request_observer_map_[submit_url] = observer; + submit_url->Start(); + + if (send_snapshot) { + data.type = SNAPSHOT; + content::URLFetcher* submit_snapshot = CreateRequest(data); + request_observer_map_[submit_snapshot] = observer; + content::BrowserThread::PostBlockingPoolTask(FROM_HERE, + base::Bind(&SubmitSnapshot, submit_snapshot, data)); + } +} + +void ChromeToMobileService::CreateUniqueTempDir() { + bool success = temp_dir_.CreateUniqueTempDir(); + DCHECK(success); +} + +content::URLFetcher* ChromeToMobileService::CreateRequest( + const RequestData& data) { + bool get = data.type != SNAPSHOT; + GURL service_url(cloud_print_url_->GetCloudPrintServiceURL()); + content::URLFetcher* request = content::URLFetcher::Create( + data.type == SEARCH ? cloud_print::GetUrlForSearch(service_url) : + GetSubmitURL(service_url, data), + get ? content::URLFetcher::GET : content::URLFetcher::POST, this); + request->SetRequestContext(profile_->GetRequestContext()); + request->SetMaxRetries(kMaxRetries); + request->SetExtraRequestHeaders("Authorization: OAuth " + + oauth2_token_ + "\r\n" + cloud_print::kChromeCloudPrintProxyHeader); + request->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | + net::LOAD_DO_NOT_SAVE_COOKIES); + return request; +} + +void ChromeToMobileService::RequestAuth() { + DCHECK(oauth2_token_.empty()); + if (oauth2_request_.get()) + return; + + std::string token = TokenServiceFactory::GetForProfile(profile_)-> + GetOAuth2LoginRefreshToken(); + if (token.empty()) + return; + + oauth2_request_.reset( + new OAuth2AccessTokenFetcher(this, profile_->GetRequestContext())); + std::vector<std::string> scopes(1, kOAuth2Scope); + oauth2_request_->Start(GaiaUrls::GetInstance()->oauth2_chrome_client_id(), + GaiaUrls::GetInstance()->oauth2_chrome_client_secret(), token, scopes); +} + +void ChromeToMobileService::RequestSearch() { + DCHECK(!oauth2_token_.empty()); + if (search_request_.get()) + return; + + RequestData data; + data.type = SEARCH; + search_request_.reset(CreateRequest(data)); + search_request_->Start(); +} + +void ChromeToMobileService::HandleSearchResponse() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + + std::string data; + search_request_->GetResponseAsString(&data); + search_request_.reset(); + + DictionaryValue* json_data = NULL; + cloud_print::ParseResponseJSON(data, NULL, &json_data); + + ListValue* list = NULL; + if (json_data && json_data->GetList(cloud_print::kPrinterListValue, &list)) { + std::vector<base::DictionaryValue*> mobiles; + for (size_t index = 0; index < list->GetSize(); index++) { + DictionaryValue* mobile_data = NULL; + if (list->GetDictionary(index, &mobile_data)) { + std::string mobile_type; + mobile_data->GetString("type", &mobile_type); + if (mobile_type.compare(kTypeAndroidChromeSnapshot) == 0) + mobiles.push_back(mobile_data); + } + } + mobiles_ = mobiles; + } + + BrowserList::GetLastActiveWithProfile(profile_)->command_updater()-> + UpdateCommandEnabled(IDC_CHROME_TO_MOBILE_PAGE, !mobiles_.empty()); + + if (!request_timer_.IsRunning()) + request_timer_.Start(FROM_HERE, + base::TimeDelta::FromSeconds(kAutoSearchRetryDelay), + this, &ChromeToMobileService::RequestSearch); +} + +void ChromeToMobileService::HandleSubmitResponse( + const content::URLFetcher* source) { + // Get the observer for this response; bail if there is none or it is NULL. + RequestObserverMap::iterator i = request_observer_map_.find(source); + if (i == request_observer_map_.end()) + return; + base::WeakPtr<Observer> observer = i->second; + request_observer_map_.erase(i); + if (!observer.get()) + return; + + // Get the success value from the CloudPrint server response data. + std::string data; + source->GetResponseAsString(&data); + DictionaryValue* json_data = NULL; + cloud_print::ParseResponseJSON(data, NULL, &json_data); + bool success = false; + if (json_data) + json_data->GetBoolean("success", &success); + + // Check if the observer is waiting on a second response (url and snapshot). + RequestObserverMap::iterator other = request_observer_map_.begin(); + for (; other != request_observer_map_.end(); ++other) { + if (other->second == observer) { + // Do not call OnSendComplete for observers waiting on a second response. + if (success) + return; + + // Ensure a second response is not sent after reporting failure below. + request_observer_map_.erase(other); + break; + } + } + + observer->OnSendComplete(success); +} diff --git a/chrome/browser/chrome_to_mobile_service.h b/chrome/browser/chrome_to_mobile_service.h new file mode 100755 index 0000000..6a70094 --- /dev/null +++ b/chrome/browser/chrome_to_mobile_service.h @@ -0,0 +1,133 @@ +// Copyright (c) 2012 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. + +#ifndef CHROME_BROWSER_CHROME_TO_MOBILE_SERVICE_H_ +#define CHROME_BROWSER_CHROME_TO_MOBILE_SERVICE_H_ +#pragma once + +#include <map> +#include <string> +#include <vector> + +#include "base/file_util.h" +#include "base/memory/weak_ptr.h" +#include "base/scoped_temp_dir.h" +#include "base/string16.h" +#include "base/timer.h" +#include "chrome/browser/profiles/profile_keyed_service.h" +#include "chrome/common/net/gaia/oauth2_access_token_consumer.h" +#include "content/public/common/url_fetcher_delegate.h" +#include "googleurl/src/gurl.h" + +class OAuth2AccessTokenFetcher; +class CloudPrintURL; +class Profile; + +// ChromeToMobileService connects to the cloud print service to enumerate +// compatible mobiles owned by its profile and send URLs and MHTML snapshots. +// The mobile list updates regularly, and explicitly by RequestMobileListUpdate. +class ChromeToMobileService : public ProfileKeyedService, + public content::URLFetcherDelegate, + public OAuth2AccessTokenConsumer { + public: + class Observer { + public: + virtual ~Observer(); + + // Called on generation of the page's MHTML snapshot. + virtual void SnapshotGenerated(const FilePath& path, int64 bytes) = 0; + + // Called after URLFetcher responses from sending the URL (and snapshot). + virtual void OnSendComplete(bool success) = 0; + }; + + // The URLFetcher request types. + enum RequestType { + SEARCH, + URL, + DELAYED_SNAPSHOT, + SNAPSHOT, + }; + + // The aggregated URLFetcher submission data. + struct RequestData { + RequestData(); + ~RequestData(); + + string16 mobile_id; + GURL url; + string16 title; + FilePath snapshot_path; + std::string snapshot_id; + RequestType type; + }; + + explicit ChromeToMobileService(Profile* profile); + virtual ~ChromeToMobileService(); + + // content::URLFetcherDelegate methods. + virtual void OnURLFetchComplete(const content::URLFetcher* source) OVERRIDE; + + // OAuth2AccessTokenConsumer methods. + virtual void OnGetTokenSuccess(const std::string& access_token) OVERRIDE; + virtual void OnGetTokenFailure(const GoogleServiceAuthError& error) OVERRIDE; + + // Get the list of mobile devices. + const std::vector<base::DictionaryValue*>& mobiles() { return mobiles_; } + + // Request an updated mobile device list, request auth first if needed. + void RequestMobileListUpdate(); + + // Callback with an MHTML snapshot of the profile's selected WebContents. + void GenerateSnapshot(base::WeakPtr<Observer> observer); + + // Send the profile's selected WebContents to the specified mobile device. + void SendToMobile(const string16& mobile_id, + const FilePath& snapshot, + base::WeakPtr<Observer> observer); + + private: + // Utility function to initialize the ScopedTempDir. + void CreateUniqueTempDir(); + + // Utility function to create URLFetcher requests. + content::URLFetcher* CreateRequest(const RequestData& data); + + void RequestAuth(); + void RequestSearch(); + + void HandleSearchResponse(); + void HandleSubmitResponse(const content::URLFetcher* source); + + Profile* profile_; + + // A utility class for accessing the cloud print service. + scoped_ptr<CloudPrintURL> cloud_print_url_; + + // The list of mobile devices retrieved from the cloud print service. + std::vector<base::DictionaryValue*> mobiles_; + + // The temporary directory for MHTML snapshot files. + ScopedTempDir temp_dir_; + + // Map URLFetchers to observers for reporting OnSendComplete. + typedef std::map<const content::URLFetcher*, base::WeakPtr<Observer> > + RequestObserverMap; + RequestObserverMap request_observer_map_; + + // The OAuth2 token and retry count. + std::string oauth2_token_; + size_t oauth2_retry_count_; + + // The pending URL requests. + scoped_ptr<OAuth2AccessTokenFetcher> oauth2_request_; + scoped_ptr<content::URLFetcher> search_request_; + + // A timer for authentication retries and mobile device list updates. + base::OneShotTimer<ChromeToMobileService> request_timer_; + + DISALLOW_COPY_AND_ASSIGN(ChromeToMobileService); +}; + +#endif // CHROME_BROWSER_CHROME_TO_MOBILE_SERVICE_H_ diff --git a/chrome/browser/chrome_to_mobile_service_factory.cc b/chrome/browser/chrome_to_mobile_service_factory.cc new file mode 100755 index 0000000..664a94a --- /dev/null +++ b/chrome/browser/chrome_to_mobile_service_factory.cc @@ -0,0 +1,32 @@ +// Copyright (c) 2012 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/chrome_to_mobile_service_factory.h" + +#include "chrome/browser/chrome_to_mobile_service.h" +#include "chrome/browser/profiles/profile_dependency_manager.h" + +// static +ChromeToMobileServiceFactory* ChromeToMobileServiceFactory::GetInstance() { + return Singleton<ChromeToMobileServiceFactory>::get(); +} + +// static +ChromeToMobileService* ChromeToMobileServiceFactory::GetForProfile( + Profile* profile) { + return static_cast<ChromeToMobileService*>( + GetInstance()->GetServiceForProfile(profile, true)); +} + +ProfileKeyedService* ChromeToMobileServiceFactory::BuildServiceInstanceFor( + Profile* profile) const { + return new ChromeToMobileService(profile); +} + +ChromeToMobileServiceFactory::ChromeToMobileServiceFactory() + : ProfileKeyedServiceFactory("ChromeToMobileService", + ProfileDependencyManager::GetInstance()) { +} + +ChromeToMobileServiceFactory::~ChromeToMobileServiceFactory() {} diff --git a/chrome/browser/chrome_to_mobile_service_factory.h b/chrome/browser/chrome_to_mobile_service_factory.h new file mode 100755 index 0000000..fa8f200 --- /dev/null +++ b/chrome/browser/chrome_to_mobile_service_factory.h @@ -0,0 +1,36 @@ +// Copyright (c) 2012 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. + +#ifndef CHROME_BROWSER_CHROME_TO_MOBILE_SERVICE_FACTORY_H_ +#define CHROME_BROWSER_CHROME_TO_MOBILE_SERVICE_FACTORY_H_ +#pragma once + +#include "base/memory/singleton.h" +#include "chrome/browser/profiles/profile_keyed_service_factory.h" + +class ChromeToMobileService; + +class ChromeToMobileServiceFactory : public ProfileKeyedServiceFactory { + public: + // Get the singleton ChromeToMobileServiceFactory instance. + static ChromeToMobileServiceFactory* GetInstance(); + + // Get |profile|'s ChromeToMobileService, creating one if needed. + static ChromeToMobileService* GetForProfile(Profile* profile); + + protected: + // ProfileKeyedServiceFactory overrides: + virtual ProfileKeyedService* BuildServiceInstanceFor( + Profile* profile) const OVERRIDE; + + private: + friend struct DefaultSingletonTraits<ChromeToMobileServiceFactory>; + + explicit ChromeToMobileServiceFactory(); + virtual ~ChromeToMobileServiceFactory(); + + DISALLOW_COPY_AND_ASSIGN(ChromeToMobileServiceFactory); +}; + +#endif // CHROME_BROWSER_CHROME_TO_MOBILE_SERVICE_FACTORY_H_ diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc index 01774f16..95b3697 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc @@ -1947,7 +1947,7 @@ void Browser::BookmarkCurrentPage() { // bookmark isn't created if the url is invalid. if (window_->IsActive() && model->IsBookmarked(url)) { // Only show the bubble if the window is active, otherwise we may get into - // weird situations were the bubble is deleted as soon as it is shown. + // weird situations where the bubble is deleted as soon as it is shown. window_->ShowBookmarkBubble(url, was_bookmarked); } } @@ -3033,6 +3033,7 @@ void Browser::ExecuteCommandWithDisposition( case IDC_EMAIL_PAGE_LOCATION: EmailPageLocation(); break; case IDC_PRINT: Print(); break; case IDC_ADVANCED_PRINT: AdvancedPrint(); break; + case IDC_CHROME_TO_MOBILE_PAGE: ShowChromeToMobileBubble(); break; case IDC_ENCODING_AUTO_DETECT: ToggleEncodingAutoDetect(); break; case IDC_ENCODING_UTF8: case IDC_ENCODING_UTF16LE: @@ -4020,6 +4021,13 @@ void Browser::ShowPageInfo(content::WebContents* web_contents, } } +void Browser::ShowChromeToMobileBubble() { + // Only show the bubble if the window is active, otherwise we may get into + // weird situations where the bubble is deleted as soon as it is shown. + if (window_->IsActive()) + window_->ShowChromeToMobileBubble(); +} + void Browser::ViewSourceForTab(WebContents* source, const GURL& page_url) { DCHECK(source); TabContentsWrapper* wrapper = GetTabContentsWrapperAt( diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h index 94e3379..9f0c5c3 100644 --- a/chrome/browser/ui/browser.h +++ b/chrome/browser/ui/browser.h @@ -576,6 +576,7 @@ class Browser : public TabHandlerDelegate, const GURL& url, const content::SSLStatus& ssl, bool show_history); + void ShowChromeToMobileBubble(); // Returns true if the Browser supports the specified feature. The value of // this varies during the lifetime of the browser. For example, if the window diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h index f359706..f31a537 100644 --- a/chrome/browser/ui/browser_window.h +++ b/chrome/browser/ui/browser_window.h @@ -214,6 +214,9 @@ class BrowserWindow : public BaseWindow { // |already_bookmarked| is true if the url is already bookmarked. virtual void ShowBookmarkBubble(const GURL& url, bool already_bookmarked) = 0; + // Shows the Chrome To Mobile bubble. + virtual void ShowChromeToMobileBubble() = 0; + // Whether or not the shelf view is visible. virtual bool IsDownloadShelfVisible() const = 0; diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.h b/chrome/browser/ui/cocoa/browser_window_cocoa.h index 01eb795..3c07a19 100644 --- a/chrome/browser/ui/cocoa/browser_window_cocoa.h +++ b/chrome/browser/ui/cocoa/browser_window_cocoa.h @@ -92,6 +92,7 @@ class BrowserWindowCocoa : public BrowserWindow, virtual void ShowBackgroundPages() OVERRIDE; virtual void ShowBookmarkBubble(const GURL& url, bool already_bookmarked) OVERRIDE; + virtual void ShowChromeToMobileBubble() OVERRIDE; virtual bool IsDownloadShelfVisible() const OVERRIDE; virtual DownloadShelf* GetDownloadShelf() OVERRIDE; virtual void ConfirmBrowserCloseWithPendingDownloads() OVERRIDE; diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.mm b/chrome/browser/ui/cocoa/browser_window_cocoa.mm index 11ac7e4..9de896f 100644 --- a/chrome/browser/ui/cocoa/browser_window_cocoa.mm +++ b/chrome/browser/ui/cocoa/browser_window_cocoa.mm @@ -441,6 +441,10 @@ void BrowserWindowCocoa::ShowBookmarkBubble(const GURL& url, alreadyBookmarked:(already_bookmarked ? YES : NO)]; } +void BrowserWindowCocoa::ShowChromeToMobileBubble() { + NOTIMPLEMENTED(); +} + bool BrowserWindowCocoa::IsDownloadShelfVisible() const { return [controller_ isDownloadShelfVisible] != NO; } diff --git a/chrome/browser/ui/cocoa/view_id_util_browsertest.mm b/chrome/browser/ui/cocoa/view_id_util_browsertest.mm index 271cd70..405c295 100644 --- a/chrome/browser/ui/cocoa/view_id_util_browsertest.mm +++ b/chrome/browser/ui/cocoa/view_id_util_browsertest.mm @@ -69,7 +69,8 @@ class ViewIDTest : public InProcessBrowserTest { i == VIEW_ID_AUTOCOMPLETE || i == VIEW_ID_CONTENTS_SPLIT || i == VIEW_ID_FEEDBACK_BUTTON || - i == VIEW_ID_OMNIBOX) { + i == VIEW_ID_OMNIBOX || + i == VIEW_ID_CHROME_TO_MOBILE_BUTTON) { continue; } diff --git a/chrome/browser/ui/gtk/browser_window_gtk.cc b/chrome/browser/ui/gtk/browser_window_gtk.cc index 13801e8..b389589 100644 --- a/chrome/browser/ui/gtk/browser_window_gtk.cc +++ b/chrome/browser/ui/gtk/browser_window_gtk.cc @@ -1069,6 +1069,10 @@ void BrowserWindowGtk::ShowBookmarkBubble(const GURL& url, toolbar_->GetLocationBarView()->ShowStarBubble(url, !already_bookmarked); } +void BrowserWindowGtk::ShowChromeToMobileBubble() { + NOTIMPLEMENTED(); +} + bool BrowserWindowGtk::IsDownloadShelfVisible() const { return download_shelf_.get() && download_shelf_->IsShowing(); } diff --git a/chrome/browser/ui/gtk/browser_window_gtk.h b/chrome/browser/ui/gtk/browser_window_gtk.h index 0b79dd2..4e49e4b 100644 --- a/chrome/browser/ui/gtk/browser_window_gtk.h +++ b/chrome/browser/ui/gtk/browser_window_gtk.h @@ -134,6 +134,7 @@ class BrowserWindowGtk : public BrowserWindow, virtual void ShowBackgroundPages() OVERRIDE; virtual void ShowBookmarkBubble(const GURL& url, bool already_bookmarked) OVERRIDE; + virtual void ShowChromeToMobileBubble() OVERRIDE; virtual bool IsDownloadShelfVisible() const OVERRIDE; virtual DownloadShelf* GetDownloadShelf() OVERRIDE; virtual void ConfirmBrowserCloseWithPendingDownloads() OVERRIDE; diff --git a/chrome/browser/ui/gtk/view_id_util_browsertest.cc b/chrome/browser/ui/gtk/view_id_util_browsertest.cc index d460cf2..a961043 100644 --- a/chrome/browser/ui/gtk/view_id_util_browsertest.cc +++ b/chrome/browser/ui/gtk/view_id_util_browsertest.cc @@ -43,7 +43,8 @@ IN_PROC_BROWSER_TEST_F(ViewIDTest, Basic) { i == VIEW_ID_BOOKMARK_BAR_ELEMENT || i == VIEW_ID_TAB || i == VIEW_ID_FEEDBACK_BUTTON || - i == VIEW_ID_OMNIBOX) { + i == VIEW_ID_OMNIBOX || + i == VIEW_ID_CHROME_TO_MOBILE_BUTTON) { continue; } diff --git a/chrome/browser/ui/panels/panel.cc b/chrome/browser/ui/panels/panel.cc index 475a07b..348b3fa 100644 --- a/chrome/browser/ui/panels/panel.cc +++ b/chrome/browser/ui/panels/panel.cc @@ -486,6 +486,10 @@ void Panel::ShowBookmarkBubble(const GURL& url, bool already_bookmarked) { NOTIMPLEMENTED(); } +void Panel::ShowChromeToMobileBubble() { + NOTIMPLEMENTED(); +} + bool Panel::IsDownloadShelfVisible() const { return false; } diff --git a/chrome/browser/ui/panels/panel.h b/chrome/browser/ui/panels/panel.h index 1fdad63f..13bccd1 100644 --- a/chrome/browser/ui/panels/panel.h +++ b/chrome/browser/ui/panels/panel.h @@ -136,6 +136,7 @@ class Panel : public BrowserWindow, virtual void ShowBackgroundPages() OVERRIDE; virtual void ShowBookmarkBubble( const GURL& url, bool already_bookmarked) OVERRIDE; + virtual void ShowChromeToMobileBubble() OVERRIDE; virtual bool IsDownloadShelfVisible() const OVERRIDE; virtual DownloadShelf* GetDownloadShelf() OVERRIDE; virtual void ConfirmBrowserCloseWithPendingDownloads() OVERRIDE; diff --git a/chrome/browser/ui/view_ids.h b/chrome/browser/ui/view_ids.h index 3ed1b21..005d2b4 100644 --- a/chrome/browser/ui/view_ids.h +++ b/chrome/browser/ui/view_ids.h @@ -46,6 +46,7 @@ enum ViewID { VIEW_ID_BROWSER_ACTION_TOOLBAR, VIEW_ID_FEEDBACK_BUTTON, VIEW_ID_OMNIBOX, + VIEW_ID_CHROME_TO_MOBILE_BUTTON, // The Bookmark Bar. VIEW_ID_BOOKMARK_BAR, diff --git a/chrome/browser/ui/views/browser_dialogs.h b/chrome/browser/ui/views/browser_dialogs.h index 976969d..7f68b98 100644 --- a/chrome/browser/ui/views/browser_dialogs.h +++ b/chrome/browser/ui/views/browser_dialogs.h @@ -50,6 +50,11 @@ void ShowBookmarkBubbleView(views::View* anchor_view, void HideBookmarkBubbleView(); bool IsBookmarkBubbleViewShowing(); +// Shows or hides the Chrome To Mobile bubble anchored to the supplied view. +void ShowChromeToMobileBubbleView(views::View* anchor_view, Profile* profile); +void HideChromeToMobileBubbleView(); +bool IsChromeToMobileBubbleViewShowing(); + // Shows the page info bubble anchored to the supplied view. void ShowPageInfoBubble(views::View* anchor_view, Profile* profile, diff --git a/chrome/browser/ui/views/chrome_to_mobile_bubble_view.cc b/chrome/browser/ui/views/chrome_to_mobile_bubble_view.cc new file mode 100755 index 0000000..68d57b7 --- /dev/null +++ b/chrome/browser/ui/views/chrome_to_mobile_bubble_view.cc @@ -0,0 +1,315 @@ +// Copyright (c) 2012 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/ui/views/chrome_to_mobile_bubble_view.h" + +#include "base/bind.h" +#include "base/file_util.h" +#include "base/string16.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/app/chrome_command_ids.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chrome_to_mobile_service.h" +#include "chrome/browser/chrome_to_mobile_service_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/views/window.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" +#include "ui/base/animation/throb_animation.h" +#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/base/text/bytes_formatting.h" +#include "ui/views/controls/button/checkbox.h" +#include "ui/views/controls/button/radio_button.h" +#include "ui/views/controls/button/text_button.h" +#include "ui/views/controls/label.h" +#include "ui/views/events/event.h" +#include "ui/views/layout/grid_layout.h" +#include "ui/views/layout/layout_constants.h" + +using views::GridLayout; + +namespace { + +// The millisecond duration of the "Sending..." progress throb animation. +const size_t kProgressThrobDurationMS = 2400; + +// The bubble's margin for the "Sending..." and "Sent" states. +const size_t kProgressMargin = 20; + +// The title label's color; matches the bookmark bubble's title. +const SkColor kTitleColor = 0xFF062D75; + +} // namespace + +// Declared in browser_dialogs.h so callers don't have to depend on our header. + +namespace browser { + +void ShowChromeToMobileBubbleView(views::View* anchor_view, Profile* profile) { + ChromeToMobileBubbleView::ShowBubble(anchor_view, profile); +} + +void HideChromeToMobileBubbleView() { + ChromeToMobileBubbleView::Hide(); +} + +bool IsChromeToMobileBubbleViewShowing() { + return ChromeToMobileBubbleView::IsShowing(); +} + +} // namespace browser + +// ChromeToMobileBubbleView ---------------------------------------------------- + +ChromeToMobileBubbleView* ChromeToMobileBubbleView::bubble_ = NULL; + +ChromeToMobileBubbleView::~ChromeToMobileBubbleView() {} + +// static +void ChromeToMobileBubbleView::ShowBubble(views::View* anchor_view, + Profile* profile) { + if (IsShowing()) + return; + + bubble_ = new ChromeToMobileBubbleView(anchor_view, profile); + browser::CreateViewsBubble(bubble_); + bubble_->Show(); +} + +// static +bool ChromeToMobileBubbleView::IsShowing() { + return bubble_ != NULL; +} + +void ChromeToMobileBubbleView::Hide() { + if (IsShowing()) + bubble_->GetWidget()->Close(); +} + +views::View* ChromeToMobileBubbleView::GetInitiallyFocusedView() { + return send_; +} + +gfx::Rect ChromeToMobileBubbleView::GetAnchorRect() { + // Compensate for some built-in padding in the arrow image. + gfx::Rect rect(BubbleDelegateView::GetAnchorRect()); + rect.Inset(0, anchor_view() ? 5 : 0); + return rect; +} + +void ChromeToMobileBubbleView::WindowClosing() { + // We have to reset |bubble_| here, not in our destructor, because we'll be + // destroyed asynchronously and the shown state will be checked before then. + DCHECK(bubble_ == this); + bubble_ = NULL; +} + +bool ChromeToMobileBubbleView::AcceleratorPressed( + const ui::Accelerator& accelerator) { + if (accelerator.key_code() == ui::VKEY_RETURN && + (send_->HasFocus() || cancel_->HasFocus())) { + HandleButtonPressed(send_->HasFocus() ? send_ : cancel_); + return true; + } + return BubbleDelegateView::AcceleratorPressed(accelerator); +} + +void ChromeToMobileBubbleView::AnimationProgressed( + const ui::Animation* animation) { + if (animation == progress_animation_.get()) { + double animation_value = animation->GetCurrentValue(); + int message = IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_3; + // Show each of four messages for 1/4 of the animation. + if (animation_value < 0.25) + message = IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_0; + else if (animation_value < 0.5) + message = IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_1; + else if (animation_value < 0.75) + message = IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_2; + progress_label_->SetText(l10n_util::GetStringUTF16(message)); + // Run Layout but do not resize the bubble for each progress message. + Layout(); + return; + } + views::BubbleDelegateView::AnimationProgressed(animation); +} + +void ChromeToMobileBubbleView::ButtonPressed(views::Button* sender, + const views::Event& event) { + HandleButtonPressed(sender); +} + +void ChromeToMobileBubbleView::SnapshotGenerated(const FilePath& path, + int64 bytes) { + if (bytes > 0) { + snapshot_path_ = path; + send_copy_->SetText(l10n_util::GetStringFUTF16( + IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY, ui::FormatBytes(bytes))); + send_copy_->SetEnabled(true); + } else { + send_copy_->SetText(l10n_util::GetStringUTF16( + IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY_FAILED)); + } + Layout(); +} + +void ChromeToMobileBubbleView::OnSendComplete(bool success) { + progress_animation_->Stop(); + progress_label_->SetText(l10n_util::GetStringUTF16(success ? + IDS_CHROME_TO_MOBILE_BUBBLE_SENT : IDS_CHROME_TO_MOBILE_BUBBLE_ERROR)); + SizeToContents(); +} + +void ChromeToMobileBubbleView::Init() { + GridLayout* layout = new GridLayout(this); + SetLayoutManager(layout); + + const size_t single_column_set_id = 0; + views::ColumnSet* cs = layout->AddColumnSet(single_column_set_id); + cs->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, + GridLayout::USE_PREF, 0, 0); + cs->AddPaddingColumn(1, 0); + + const size_t button_column_set_id = 1; + cs = layout->AddColumnSet(button_column_set_id); + cs->AddPaddingColumn(1, 0); + cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0, + GridLayout::USE_PREF, 0, 0); + // Subtract 2px for the natural button padding and to correspond with row + // separation height; like BookmarkBubbleView. + cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing - 2); + cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0, + GridLayout::USE_PREF, 0, 0); + + std::vector<DictionaryValue*> mobiles = + ChromeToMobileServiceFactory::GetForProfile(profile_)->mobiles(); + DCHECK_GT(mobiles.size(), 0U); + + layout->StartRow(0, single_column_set_id); + views::Label* title_label = new views::Label(); + title_label->SetFont( + ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::MediumFont)); + title_label->SetEnabledColor(kTitleColor); + layout->AddView(title_label); + + if (mobiles.size() == 1) { + selected_mobile_ = mobiles[0]; + string16 mobile_name; + mobiles[0]->GetString("name", &mobile_name); + title_label->SetText(l10n_util::GetStringFUTF16( + IDS_CHROME_TO_MOBILE_BUBBLE_SINGLE_TITLE, mobile_name)); + } else { + title_label->SetText(l10n_util::GetStringUTF16( + IDS_CHROME_TO_MOBILE_BUBBLE_MULTI_TITLE)); + + const size_t radio_column_set_id = 2; + cs = layout->AddColumnSet(radio_column_set_id); + cs->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); + cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0, + GridLayout::USE_PREF, 0, 0); + + views::RadioButton* radio; + layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing); + for (std::vector<DictionaryValue*>::const_iterator it = mobiles.begin(); + it != mobiles.end(); ++it) { + string16 name; + (*it)->GetString("name", &name); + radio = new views::RadioButton(name, 0); + radio->set_listener(this); + mobile_map_[radio] = *it; + layout->StartRow(0, radio_column_set_id); + layout->AddView(radio); + } + mobile_map_.begin()->first->SetChecked(true); + selected_mobile_ = mobile_map_.begin()->second; + } + + send_copy_ = new views::Checkbox( + l10n_util::GetStringFUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY, + l10n_util::GetStringUTF16( + IDS_CHROME_TO_MOBILE_BUBBLE_SEND_COPY_GENERATING))); + send_copy_->SetEnabled(false); + layout->StartRow(0, single_column_set_id); + layout->AddView(send_copy_); + + layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing); + send_ = new views::NativeTextButton( + this, l10n_util::GetStringUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_SEND)); + send_->SetIsDefault(true); + cancel_ = new views::NativeTextButton( + this, l10n_util::GetStringUTF16(IDS_CANCEL)); + layout->StartRow(0, button_column_set_id); + layout->AddView(send_); + layout->AddView(cancel_); + + AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, 0)); +} + +ChromeToMobileBubbleView::ChromeToMobileBubbleView(views::View* anchor_view, + Profile* profile) + : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT), + ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)), + profile_(profile), + selected_mobile_(NULL), + send_copy_(NULL), + send_(NULL), + cancel_(NULL), + progress_label_(NULL) { + // Generate the MHTML snapshot now to report its size in the bubble. + ChromeToMobileServiceFactory::GetForProfile(profile)-> + GenerateSnapshot(weak_ptr_factory_.GetWeakPtr()); +} + +void ChromeToMobileBubbleView::HandleButtonPressed(views::Button* sender) { + if (sender == send_) { + Send(); + } else if (sender == cancel_) { + GetWidget()->Close(); + } else { + // The sender is a mobile radio button + views::RadioButton* radio = static_cast<views::RadioButton*>(sender); + DCHECK(mobile_map_.find(radio) != mobile_map_.end()); + selected_mobile_ = mobile_map_.find(radio)->second; + } +} + +void ChromeToMobileBubbleView::Send() { + string16 mobile_id; + selected_mobile_->GetString("id", &mobile_id); + ChromeToMobileServiceFactory::GetForProfile(profile_)->SendToMobile( + mobile_id, send_copy_->checked() ? snapshot_path_ : FilePath(), + weak_ptr_factory_.GetWeakPtr()); + + // Re-initialize the view's contents to show progress sending the page. + RemoveAllChildViews(true); + send_copy_ = NULL; + send_ = NULL; + cancel_ = NULL; + + GridLayout* layout = new GridLayout(this); + SetLayoutManager(layout); + + const size_t single_column_set_id = 0; + views::ColumnSet* cs = layout->AddColumnSet(single_column_set_id); + cs->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, + GridLayout::USE_PREF, 0, 0); + set_margin(kProgressMargin); + + // Use the final (longest) progress label string to resize the bubble. + layout->StartRow(0, single_column_set_id); + progress_label_ = new views::Label( + l10n_util::GetStringUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_SENDING_3)); + progress_label_->SetFont( + ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::MediumFont)); + progress_label_->SetEnabledColor(kTitleColor); + layout->AddView(progress_label_); + SizeToContents(); + + progress_animation_.reset(new ui::ThrobAnimation(this)); + progress_animation_->SetDuration(kProgressThrobDurationMS); + progress_animation_->StartThrobbing(-1); +} diff --git a/chrome/browser/ui/views/chrome_to_mobile_bubble_view.h b/chrome/browser/ui/views/chrome_to_mobile_bubble_view.h new file mode 100755 index 0000000..cff5474 --- /dev/null +++ b/chrome/browser/ui/views/chrome_to_mobile_bubble_view.h @@ -0,0 +1,104 @@ +// Copyright (c) 2012 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. + +#ifndef CHROME_BROWSER_UI_VIEWS_CHROME_TO_MOBILE_BUBBLE_VIEW_H_ +#define CHROME_BROWSER_UI_VIEWS_CHROME_TO_MOBILE_BUBBLE_VIEW_H_ +#pragma once + +#include <map> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/chrome_to_mobile_service.h" +#include "ui/views/bubble/bubble_delegate.h" +#include "ui/views/controls/button/button.h" + +class Profile; + +namespace base { +class DictionaryValue; +} + +namespace ui { +class ThrobAnimation; +} + +namespace views { +class Checkbox; +class Label; +class RadioButton; +class TextButton; +} + +// ChromeToMobileBubbleView is a bubble view for the Chrome To Mobile service. +class ChromeToMobileBubbleView : public views::BubbleDelegateView, + public views::ButtonListener, + public ChromeToMobileService::Observer { + public: + virtual ~ChromeToMobileBubbleView(); + + static void ShowBubble(views::View* anchor_view, Profile* profile); + static bool IsShowing(); + static void Hide(); + + // views::BubbleDelegateView methods. + virtual views::View* GetInitiallyFocusedView() OVERRIDE; + virtual gfx::Rect GetAnchorRect() OVERRIDE; + virtual void WindowClosing() OVERRIDE; + virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE; + virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE; + + // views::ButtonListener method. + virtual void ButtonPressed(views::Button* sender, + const views::Event& event) OVERRIDE; + + // ChromeToMobileService::Observer methods. + virtual void SnapshotGenerated(const FilePath& path, int64 bytes) OVERRIDE; + virtual void OnSendComplete(bool success) OVERRIDE; + + protected: + // views::BubbleDelegateView method. + virtual void Init() OVERRIDE; + + private: + ChromeToMobileBubbleView(views::View* anchor_view, Profile* profile); + + // Handle the message when the user presses a button. + void HandleButtonPressed(views::Button* sender); + + // Send the page to the mobile device. + void Send(); + + // The Chrome To Mobile bubble, if we're showing one. + static ChromeToMobileBubbleView* bubble_; + + base::WeakPtrFactory<ChromeToMobileBubbleView> weak_ptr_factory_; + + Profile* profile_; + + // A map of radio buttons for each mobile device to the device's information. + typedef std::map<views::RadioButton*, base::DictionaryValue*> DeviceMap; + DeviceMap mobile_map_; + + // The currently selected (or solitary) mobile device's info. + base::DictionaryValue* selected_mobile_; + + // The file path for the MHTML page snapshot. + FilePath snapshot_path_; + + views::Checkbox* send_copy_; + views::TextButton* send_; + views::TextButton* cancel_; + + // The label indicating "Sending..."/"Sent" progress after invoking "Send". + views::Label* progress_label_; + + // An animation used to cycle through the "Sending..." status messages. + scoped_ptr<ui::ThrobAnimation> progress_animation_; + + DISALLOW_COPY_AND_ASSIGN(ChromeToMobileBubbleView); +}; + +#endif // CHROME_BROWSER_UI_VIEWS_CHROME_TO_MOBILE_BUBBLE_VIEW_H_ diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index 470e3f0..cb703dd2 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc @@ -1126,6 +1126,10 @@ void BrowserView::ShowBookmarkBubble(const GURL& url, bool already_bookmarked) { GetLocationBarView()->ShowStarBubble(url, !already_bookmarked); } +void BrowserView::ShowChromeToMobileBubble() { + GetLocationBarView()->ShowChromeToMobileBubble(); +} + void BrowserView::SetDownloadShelfVisible(bool visible) { // This can be called from the superclass destructor, when it destroys our // child views. At that point, browser_ is already gone. diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h index 60a3455..0f6468b 100644 --- a/chrome/browser/ui/views/frame/browser_view.h +++ b/chrome/browser/ui/views/frame/browser_view.h @@ -283,8 +283,9 @@ class BrowserView : public BrowserWindow, virtual void ShowUpdateChromeDialog() OVERRIDE; virtual void ShowTaskManager() OVERRIDE; virtual void ShowBackgroundPages() OVERRIDE; - virtual void ShowBookmarkBubble(const GURL& url, bool already_bookmarked) - OVERRIDE; + virtual void ShowBookmarkBubble(const GURL& url, + bool already_bookmarked) OVERRIDE; + virtual void ShowChromeToMobileBubble() OVERRIDE; // TODO(beng): Not an override, move somewhere else. void SetDownloadShelfVisible(bool visible); virtual bool IsDownloadShelfVisible() const OVERRIDE; diff --git a/chrome/browser/ui/views/location_bar/chrome_to_mobile_view.cc b/chrome/browser/ui/views/location_bar/chrome_to_mobile_view.cc new file mode 100644 index 0000000..fc54e85 --- /dev/null +++ b/chrome/browser/ui/views/location_bar/chrome_to_mobile_view.cc @@ -0,0 +1,76 @@ +// Copyright (c) 2012 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/ui/views/location_bar/chrome_to_mobile_view.h" + +#include "base/utf_string_conversions.h" +#include "chrome/app/chrome_command_ids.h" +#include "chrome/browser/ui/view_ids.h" +#include "chrome/browser/ui/views/browser_dialogs.h" +#include "chrome/browser/ui/views/location_bar/location_bar_view.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" +#include "grit/theme_resources_standard.h" +#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" + +ChromeToMobileView::ChromeToMobileView( + LocationBarView* location_bar_view, + CommandUpdater* command_updater) + : location_bar_view_(location_bar_view), + command_updater_(command_updater) { + set_id(VIEW_ID_CHROME_TO_MOBILE_BUTTON); + set_accessibility_focusable(true); + SetImage(ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_STAR)); + SetTooltipText( + l10n_util::GetStringUTF16(IDS_CHROME_TO_MOBILE_BUBBLE_TOOLTIP)); + SetVisible(command_updater_->IsCommandEnabled(IDC_CHROME_TO_MOBILE_PAGE)); + command_updater_->AddCommandObserver(IDC_CHROME_TO_MOBILE_PAGE, this); +} + +ChromeToMobileView::~ChromeToMobileView() { + command_updater_->RemoveCommandObserver(IDC_CHROME_TO_MOBILE_PAGE, this); +} + +void ChromeToMobileView::EnabledStateChangedForCommand(int id, bool enabled) { + DCHECK_EQ(id, IDC_CHROME_TO_MOBILE_PAGE); + if (enabled != visible()) { + SetVisible(enabled); + location_bar_view_->Update(NULL); + } +} + +void ChromeToMobileView::GetAccessibleState(ui::AccessibleViewState* state) { + state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_CHROME_TO_MOBILE); + state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON; +} + +bool ChromeToMobileView::GetTooltipText(const gfx::Point& p, + string16* tooltip) const { + // Don't show tooltip to distract user if ChromeToMobileBubbleView is showing. + if (browser::IsChromeToMobileBubbleViewShowing()) + return false; + + return ImageView::GetTooltipText(p, tooltip); +} + +bool ChromeToMobileView::OnMousePressed(const views::MouseEvent& event) { + // Show the bubble on mouse release; that is standard button behavior. + return true; +} + +void ChromeToMobileView::OnMouseReleased(const views::MouseEvent& event) { + if (event.IsOnlyLeftMouseButton() && HitTest(event.location())) + command_updater_->ExecuteCommand(IDC_CHROME_TO_MOBILE_PAGE); +} + +bool ChromeToMobileView::OnKeyPressed(const views::KeyEvent& event) { + if (event.key_code() == ui::VKEY_SPACE || + event.key_code() == ui::VKEY_RETURN) { + command_updater_->ExecuteCommand(IDC_CHROME_TO_MOBILE_PAGE); + return true; + } + return false; +} diff --git a/chrome/browser/ui/views/location_bar/chrome_to_mobile_view.h b/chrome/browser/ui/views/location_bar/chrome_to_mobile_view.h new file mode 100644 index 0000000..5670568 --- /dev/null +++ b/chrome/browser/ui/views/location_bar/chrome_to_mobile_view.h @@ -0,0 +1,48 @@ +// Copyright (c) 2012 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. + +#ifndef CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_CHROME_TO_MOBILE_VIEW_H_ +#define CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_CHROME_TO_MOBILE_VIEW_H_ +#pragma once + +#include "chrome/browser/command_updater.h" +#include "ui/views/controls/image_view.h" + +class LocationBarView; + +namespace views { +class KeyEvent; +class MouseEvent; +} + +// A Page Action image view for the Chrome To Mobile bubble. +class ChromeToMobileView : public views::ImageView, + public CommandUpdater::CommandObserver { + public: + ChromeToMobileView(LocationBarView* location_bar_view, + CommandUpdater* command_updater); + virtual ~ChromeToMobileView(); + + // CommandUpdater::CommandObserver overrides: + virtual void EnabledStateChangedForCommand(int id, bool enabled) OVERRIDE; + + private: + // views::ImageView overrides: + virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual bool GetTooltipText(const gfx::Point& p, + string16* tooltip) const OVERRIDE; + virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE; + virtual void OnMouseReleased(const views::MouseEvent& event) OVERRIDE; + virtual bool OnKeyPressed(const views::KeyEvent& event) OVERRIDE; + + // The LocationBarView hosting this view. + LocationBarView* location_bar_view_; + + // The CommandUpdater for the Browser object that owns the location bar. + CommandUpdater* command_updater_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(ChromeToMobileView); +}; + +#endif // CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_CHROME_TO_MOBILE_VIEW_H_ diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc index d1edcd7..2dcb8d0 100644 --- a/chrome/browser/ui/views/location_bar/location_bar_view.cc +++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc @@ -17,6 +17,8 @@ #include "chrome/app/chrome_command_ids.h" #include "chrome/browser/alternate_nav_url_fetcher.h" #include "chrome/browser/autocomplete/autocomplete_popup_model.h" +#include "chrome/browser/chrome_to_mobile_service.h" +#include "chrome/browser/chrome_to_mobile_service_factory.h" #include "chrome/browser/defaults.h" #include "chrome/browser/extensions/extension_browser_event_router.h" #include "chrome/browser/extensions/extension_service.h" @@ -30,6 +32,7 @@ #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" #include "chrome/browser/ui/view_ids.h" #include "chrome/browser/ui/views/browser_dialogs.h" +#include "chrome/browser/ui/views/location_bar/chrome_to_mobile_view.h" #include "chrome/browser/ui/views/location_bar/content_setting_image_view.h" #include "chrome/browser/ui/views/location_bar/ev_bubble_view.h" #include "chrome/browser/ui/views/location_bar/keyword_hint_view.h" @@ -135,6 +138,7 @@ LocationBarView::LocationBarView(Browser* browser, #endif keyword_hint_view_(NULL), star_view_(NULL), + chrome_to_mobile_view_(NULL), mode_(mode), show_focus_rect_(false), template_url_service_(NULL), @@ -223,11 +227,23 @@ void LocationBarView::Init() { content_blocked_view->SetVisible(false); } - // The star is not visible in popups and in the app launcher. + // Hide the star and Chrome To Mobile icons in popups and in the app launcher. if (browser_defaults::bookmarks_enabled && (mode_ == NORMAL)) { star_view_ = new StarView(browser_->command_updater()); AddChildView(star_view_); star_view_->SetVisible(true); + + // Also disable Chrome To Mobile for off-the-record and non-synced profiles. + Profile* profile = browser_->profile(); + if (!profile->IsOffTheRecord() && profile->IsSyncAccessible()) { + chrome_to_mobile_view_ = + new ChromeToMobileView(this, browser_->command_updater()); + AddChildView(chrome_to_mobile_view_); + ChromeToMobileService* service = + ChromeToMobileServiceFactory::GetForProfile(browser_->profile()); + service->RequestMobileListUpdate(); + chrome_to_mobile_view_->SetVisible(!service->mobiles().empty()); + } } // Initialize the location entry. We do this to avoid a black flash which is @@ -302,10 +318,18 @@ void LocationBarView::SetAnimationOffset(int offset) { void LocationBarView::Update(const WebContents* tab_for_state_restoring) { bool star_enabled = star_view_ && !model_->input_in_progress() && edit_bookmarks_enabled_.GetValue(); - browser_->command_updater()->UpdateCommandEnabled( - IDC_BOOKMARK_PAGE, star_enabled); + CommandUpdater* command_updater = browser_->command_updater(); + command_updater->UpdateCommandEnabled(IDC_BOOKMARK_PAGE, star_enabled); if (star_view_) star_view_->SetVisible(star_enabled); + + Profile* profile = browser_->profile(); + bool chrome_to_mobile_enabled = chrome_to_mobile_view_ && + !model_->input_in_progress() && profile->IsSyncAccessible() && + !ChromeToMobileServiceFactory::GetForProfile(profile)->mobiles().empty(); + command_updater->UpdateCommandEnabled(IDC_CHROME_TO_MOBILE_PAGE, + chrome_to_mobile_enabled); + RefreshContentSettingViews(); RefreshPageActionViews(); // Don't Update in app launcher mode so that the location entry does not show @@ -402,6 +426,13 @@ void LocationBarView::ShowStarBubble(const GURL& url, bool newly_bookmarked) { newly_bookmarked); } +void LocationBarView::ShowChromeToMobileBubble() { + ChromeToMobileServiceFactory::GetForProfile(browser_->profile())-> + RequestMobileListUpdate(); + browser::ShowChromeToMobileBubbleView(chrome_to_mobile_view_, + browser_->profile()); +} + gfx::Point LocationBarView::GetLocationEntryOrigin() const { gfx::Point origin(location_entry_view_->bounds().origin()); // If the UI layout is RTL, the coordinate system is not transformed and @@ -526,6 +557,9 @@ void LocationBarView::Layout() { if (star_view_ && star_view_->visible()) entry_width -= star_view_->GetPreferredSize().width() + kItemPadding; + if (chrome_to_mobile_view_ && chrome_to_mobile_view_->visible()) + entry_width -= chrome_to_mobile_view_->GetPreferredSize().width() + + kItemPadding; for (PageActionViews::const_iterator i(page_action_views_.begin()); i != page_action_views_.end(); ++i) { if ((*i)->visible()) @@ -539,7 +573,7 @@ void LocationBarView::Layout() { // The gap between the edit and whatever is to its right is shortened. entry_width += kEditInternalSpace; - // Size the EV bubble. We do this after taking the star/page actions/content + // Size the EV bubble after taking star/ChromeToMobile/page actions/content // settings out of |entry_width| so we won't take too much space. if (ev_bubble_width) { // Try to elide the bubble to be no larger than half the total available @@ -593,6 +627,14 @@ void LocationBarView::Layout() { offset -= kItemPadding; } + if (chrome_to_mobile_view_ && chrome_to_mobile_view_->visible()) { + int icon_width = chrome_to_mobile_view_->GetPreferredSize().width(); + offset -= icon_width; + chrome_to_mobile_view_->SetBounds(offset, location_y, + icon_width, location_height); + offset -= kItemPadding; + } + for (PageActionViews::const_iterator i(page_action_views_.begin()); i != page_action_views_.end(); ++i) { if ((*i)->visible()) { @@ -958,6 +1000,8 @@ void LocationBarView::RefreshPageActionViews() { DeletePageActionViews(); // Delete the old views (if any). page_action_views_.resize(page_actions.size()); + View* view = chrome_to_mobile_view_ ? chrome_to_mobile_view_ : + static_cast<View*>(star_view_); // Add the page actions in reverse order, so that the child views are // inserted in left-to-right order for accessibility. @@ -965,7 +1009,7 @@ void LocationBarView::RefreshPageActionViews() { page_action_views_[i] = new PageActionWithBadgeView( new PageActionImageView(this, page_actions[i])); page_action_views_[i]->SetVisible(false); - AddChildViewAt(page_action_views_[i], GetIndexOf(star_view_)); + AddChildViewAt(page_action_views_[i], GetIndexOf(view)); } } diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.h b/chrome/browser/ui/views/location_bar/location_bar_view.h index 94777a2..4781c21 100644 --- a/chrome/browser/ui/views/location_bar/location_bar_view.h +++ b/chrome/browser/ui/views/location_bar/location_bar_view.h @@ -33,6 +33,7 @@ #endif class Browser; +class ChromeToMobileView; class ContentSettingImageView; class EVBubbleView; class ExtensionAction; @@ -152,6 +153,9 @@ class LocationBarView : public LocationBar, // Shows the bookmark bubble. void ShowStarBubble(const GURL& url, bool newly_bookmarked); + // Shows the Chrome To Mobile bubble. + void ShowChromeToMobileBubble(); + // Returns the screen coordinates of the location entry (where the URL text // appears, not where the icons are shown). gfx::Point GetLocationEntryOrigin() const; @@ -409,6 +413,9 @@ class LocationBarView : public LocationBar, // The star. StarView* star_view_; + // The Chrome To Mobile page action icon view. + ChromeToMobileView* chrome_to_mobile_view_; + // The mode that dictates how the bar shows. Mode mode_; diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index c1eff92..64589a1 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -397,6 +397,10 @@ 'browser/chrome_plugin_service_filter.h', 'browser/chrome_quota_permission_context.cc', 'browser/chrome_quota_permission_context.h', + 'browser/chrome_to_mobile_service.cc', + 'browser/chrome_to_mobile_service.h', + 'browser/chrome_to_mobile_service_factory.cc', + 'browser/chrome_to_mobile_service_factory.h', 'browser/chromeos/accessibility/accessibility_util.cc', 'browser/chromeos/accessibility/accessibility_util.h', 'browser/chromeos/audio/audio_handler.cc', @@ -3239,6 +3243,8 @@ 'browser/ui/views/browser_actions_container.h', 'browser/ui/views/browser_dialogs.h', 'browser/ui/views/certificate_viewer_win.cc', + 'browser/ui/views/chrome_to_mobile_bubble_view.cc', + 'browser/ui/views/chrome_to_mobile_bubble_view.h', 'browser/ui/views/chrome_views_delegate.cc', 'browser/ui/views/chrome_views_delegate.h', 'browser/ui/views/collected_cookies_views.cc', @@ -3407,6 +3413,8 @@ 'browser/ui/views/local_storage_info_view.h', 'browser/ui/views/local_storage_set_item_info_view.cc', 'browser/ui/views/local_storage_set_item_info_view.h', + 'browser/ui/views/location_bar/chrome_to_mobile_view.cc', + 'browser/ui/views/location_bar/chrome_to_mobile_view.h', 'browser/ui/views/location_bar/click_handler.cc', 'browser/ui/views/location_bar/click_handler.h', 'browser/ui/views/location_bar/content_setting_image_view.cc', @@ -4776,6 +4784,8 @@ ['include', '^browser/ui/views/bookmarks/bookmark_menu_delegate.h'], ['include', '^browser/ui/views/browser_actions_container.cc'], ['include', '^browser/ui/views/browser_actions_container.h'], + ['include', '^browser/ui/views/chrome_to_mobile_bubble_view.cc'], + ['include', '^browser/ui/views/chrome_to_mobile_bubble_view.h'], ['include', '^browser/ui/views/chrome_views_delegate.cc'], ['include', '^browser/ui/views/constrained_html_delegate_gtk.cc'], ['include', '^browser/ui/views/constrained_window_views.cc'], @@ -4874,6 +4884,8 @@ ['include', '^browser/ui/views/keyboard_overlay_delegate.h'], ['include', '^browser/ui/views/keyboard_overlay_dialog_view.cc'], ['include', '^browser/ui/views/keyboard_overlay_dialog_view.h'], + ['include', '^browser/ui/views/location_bar/chrome_to_mobile_view.cc'], + ['include', '^browser/ui/views/location_bar/chrome_to_mobile_view.h'], ['include', '^browser/ui/views/location_bar/click_handler.cc'], ['include', '^browser/ui/views/location_bar/click_handler.h'], ['include', '^browser/ui/views/location_bar/content_setting_image_view.cc'], diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi index f399837..c68b8c1 100644 --- a/chrome/chrome_common.gypi +++ b/chrome/chrome_common.gypi @@ -89,6 +89,8 @@ 'common/chrome_version_info.h', 'common/cloud_print/cloud_print_class_mac.h', 'common/cloud_print/cloud_print_class_mac.mm', + 'common/cloud_print/cloud_print_helpers.cc', + 'common/cloud_print/cloud_print_helpers.h', 'common/cloud_print/cloud_print_proxy_info.cc', 'common/cloud_print/cloud_print_proxy_info.h', 'common/common_api.h', diff --git a/chrome/common/cloud_print/cloud_print_helpers.cc b/chrome/common/cloud_print/cloud_print_helpers.cc new file mode 100755 index 0000000..cebe4a3 --- /dev/null +++ b/chrome/common/cloud_print/cloud_print_helpers.cc @@ -0,0 +1,94 @@ +// Copyright (c) 2012 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/common/cloud_print/cloud_print_helpers.h" + +#include "base/json/json_reader.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/rand_util.h" +#include "base/stringprintf.h" +#include "base/values.h" +#include "chrome/common/guid.h" +#include "googleurl/src/gurl.h" + +namespace cloud_print { + +const char kPrinterListValue[] = "printers"; +const char kSuccessValue[] = "success"; + +// Certain cloud print requests require Chrome's X-CloudPrint-Proxy header. +const char kChromeCloudPrintProxyHeader[] = "X-CloudPrint-Proxy: Chrome"; + +std::string AppendPathToUrl(const GURL& url, const std::string& path) { + DCHECK_NE(path[0], '/'); + std::string ret = url.path(); + if (url.has_path() && (ret[ret.length() - 1] != '/')) + ret += '/'; + ret += path; + return ret; +} + +GURL GetUrlForSearch(const GURL& cloud_print_server_url) { + std::string path(AppendPathToUrl(cloud_print_server_url, "search")); + GURL::Replacements replacements; + replacements.SetPathStr(path); + return cloud_print_server_url.ReplaceComponents(replacements); +} + +GURL GetUrlForSubmit(const GURL& cloud_print_server_url) { + std::string path(AppendPathToUrl(cloud_print_server_url, "submit")); + GURL::Replacements replacements; + replacements.SetPathStr(path); + return cloud_print_server_url.ReplaceComponents(replacements); +} + +bool ParseResponseJSON(const std::string& response_data, + bool* succeeded, + DictionaryValue** response_dict) { + scoped_ptr<Value> message_value(base::JSONReader::Read(response_data, false)); + if (!message_value.get()) + return false; + + if (!message_value->IsType(Value::TYPE_DICTIONARY)) + return false; + + scoped_ptr<DictionaryValue> response_dict_local( + static_cast<DictionaryValue*>(message_value.release())); + if (succeeded && + !response_dict_local->GetBoolean(cloud_print::kSuccessValue, succeeded)) + *succeeded = false; + if (response_dict) + *response_dict = response_dict_local.release(); + return true; +} + +void AddMultipartValueForUpload(const std::string& value_name, + const std::string& value, + const std::string& mime_boundary, + const std::string& content_type, + std::string* post_data) { + DCHECK(post_data); + // First line is the boundary + post_data->append("--" + mime_boundary + "\r\n"); + // Next line is the Content-disposition + post_data->append(StringPrintf("Content-Disposition: form-data; " + "name=\"%s\"\r\n", value_name.c_str())); + if (!content_type.empty()) { + // If Content-type is specified, the next line is that + post_data->append(StringPrintf("Content-Type: %s\r\n", + content_type.c_str())); + } + // Leave an empty line and append the value. + post_data->append(StringPrintf("\r\n%s\r\n", value.c_str())); +} + +// Create a MIME boundary marker (27 '-' characters followed by 16 hex digits). +void CreateMimeBoundaryForUpload(std::string* out) { + int r1 = base::RandInt(0, kint32max); + int r2 = base::RandInt(0, kint32max); + base::SStringPrintf(out, "---------------------------%08X%08X", r1, r2); +} + +} // namespace cloud_print diff --git a/chrome/common/cloud_print/cloud_print_helpers.h b/chrome/common/cloud_print/cloud_print_helpers.h new file mode 100755 index 0000000..1b4a15e --- /dev/null +++ b/chrome/common/cloud_print/cloud_print_helpers.h @@ -0,0 +1,56 @@ +// Copyright (c) 2012 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. + +#ifndef CHROME_COMMON_CLOUD_PRINT_CLOUD_PRINT_HELPERS_H_ +#define CHROME_COMMON_CLOUD_PRINT_CLOUD_PRINT_HELPERS_H_ + +#include <string> + +class GURL; + +namespace base { +class DictionaryValue; +} + +// Helper consts and methods for both cloud print and chrome browser. +namespace cloud_print { + +// Values in the respone JSON from the cloud print server +extern const char kPrinterListValue[]; +extern const char kSuccessValue[]; + +extern const char kChromeCloudPrintProxyHeader[]; + +// Appends a relative path to the url making sure to append a '/' if the +// URL's path does not end with a slash. It is assumed that |path| does not +// begin with a '/'. +// NOTE: Since we ALWAYS want to append here, we simply append the path string +// instead of calling url_utils::ResolveRelative. The input |url| may or may not +// contain a '/' at the end. +std::string AppendPathToUrl(const GURL& url, const std::string& path); + +GURL GetUrlForSearch(const GURL& cloud_print_server_url); +GURL GetUrlForSubmit(const GURL& cloud_print_server_url); + +// Parses the response data for any cloud print server request. The method +// returns false if there was an error in parsing the JSON. The succeeded +// value returns the value of the "success" value in the response JSON. +// Returns the response as a dictionary value. +bool ParseResponseJSON(const std::string& response_data, + bool* succeeded, + base::DictionaryValue** response_dict); + +// Prepares one value as part of a multi-part upload request. +void AddMultipartValueForUpload(const std::string& value_name, + const std::string& value, + const std::string& mime_boundary, + const std::string& content_type, + std::string* post_data); + +// Create a MIME boundary marker (27 '-' characters followed by 16 hex digits). +void CreateMimeBoundaryForUpload(std::string *out); + +} // namespace cloud_print + +#endif // CHROME_COMMON_CLOUD_PRINT_CLOUD_PRINT_HELPERS_H_ diff --git a/chrome/service/cloud_print/cloud_print_connector.cc b/chrome/service/cloud_print/cloud_print_connector.cc index ec02d2a..244babf 100644 --- a/chrome/service/cloud_print/cloud_print_connector.cc +++ b/chrome/service/cloud_print/cloud_print_connector.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -13,6 +13,7 @@ #include "base/stringprintf.h" #include "base/utf_string_conversions.h" #include "base/values.h" +#include "chrome/common/cloud_print/cloud_print_helpers.h" #include "chrome/service/cloud_print/cloud_print_consts.h" #include "chrome/service/cloud_print/cloud_print_helpers.h" #include "grit/generated_resources.h" @@ -177,7 +178,8 @@ CloudPrintConnector::HandlePrinterListResponse( // Go through the list of the cloud printers and init print job handlers. ListValue* printer_list = NULL; // There may be no "printers" value in the JSON - if (json_data->GetList(kPrinterListValue, &printer_list) && printer_list) { + if (json_data->GetList(cloud_print::kPrinterListValue, &printer_list) + && printer_list) { for (size_t index = 0; index < printer_list->GetSize(); index++) { DictionaryValue* printer_data = NULL; if (printer_list->GetDictionary(index, &printer_data)) { @@ -233,7 +235,7 @@ CloudPrintConnector::HandleRegisterPrinterResponse( if (succeeded) { ListValue* printer_list = NULL; // There should be a "printers" value in the JSON - if (json_data->GetList(kPrinterListValue, &printer_list)) { + if (json_data->GetList(cloud_print::kPrinterListValue, &printer_list)) { DictionaryValue* printer_data = NULL; if (printer_list->GetDictionary(0, &printer_data)) InitJobHandlerForPrinter(printer_data); @@ -277,15 +279,12 @@ void CloudPrintConnector::ReportUserMessage(const std::string& message_id, // This is a fire and forget type of function. // Result of this request will be ignored. std::string mime_boundary; - CloudPrintHelpers::CreateMimeBoundaryForUpload(&mime_boundary); + cloud_print::CreateMimeBoundaryForUpload(&mime_boundary); GURL url = CloudPrintHelpers::GetUrlForUserMessage(cloud_print_server_url_, message_id); std::string post_data; - CloudPrintHelpers::AddMultipartValueForUpload(kMessageTextValue, - failure_msg, - mime_boundary, - std::string(), - &post_data); + cloud_print::AddMultipartValueForUpload(kMessageTextValue, failure_msg, + mime_boundary, std::string(), &post_data); // Terminate the request body post_data.append("--" + mime_boundary + "--\r\n"); std::string mime_type("multipart/form-data; boundary="); @@ -496,40 +495,32 @@ void CloudPrintConnector::OnReceivePrinterCaps( DCHECK(IsSamePrinter(info.printer_name, printer_name)); std::string mime_boundary; - CloudPrintHelpers::CreateMimeBoundaryForUpload(&mime_boundary); + cloud_print::CreateMimeBoundaryForUpload(&mime_boundary); std::string post_data; - CloudPrintHelpers::AddMultipartValueForUpload(kProxyIdValue, proxy_id_, - mime_boundary, - std::string(), &post_data); - CloudPrintHelpers::AddMultipartValueForUpload(kPrinterNameValue, - info.printer_name, - mime_boundary, - std::string(), &post_data); - CloudPrintHelpers::AddMultipartValueForUpload(kPrinterDescValue, - info.printer_description, - mime_boundary, - std::string() , &post_data); - CloudPrintHelpers::AddMultipartValueForUpload( - kPrinterStatusValue, base::StringPrintf("%d", info.printer_status), + cloud_print::AddMultipartValueForUpload(kProxyIdValue, proxy_id_, + mime_boundary, std::string(), &post_data); + cloud_print::AddMultipartValueForUpload(kPrinterNameValue, info.printer_name, + mime_boundary, std::string(), &post_data); + cloud_print::AddMultipartValueForUpload(kPrinterDescValue, + info.printer_description, mime_boundary, std::string() , &post_data); + cloud_print::AddMultipartValueForUpload(kPrinterStatusValue, + base::StringPrintf("%d", info.printer_status), mime_boundary, std::string(), &post_data); // Add printer options as tags. CloudPrintHelpers::GenerateMultipartPostDataForPrinterTags(info.options, mime_boundary, &post_data); - CloudPrintHelpers::AddMultipartValueForUpload( - kPrinterCapsValue, caps_and_defaults.printer_capabilities, - mime_boundary, caps_and_defaults.caps_mime_type, - &post_data); - CloudPrintHelpers::AddMultipartValueForUpload( - kPrinterDefaultsValue, caps_and_defaults.printer_defaults, - mime_boundary, caps_and_defaults.defaults_mime_type, - &post_data); + cloud_print::AddMultipartValueForUpload(kPrinterCapsValue, + caps_and_defaults.printer_capabilities, mime_boundary, + caps_and_defaults.caps_mime_type, &post_data); + cloud_print::AddMultipartValueForUpload(kPrinterDefaultsValue, + caps_and_defaults.printer_defaults, mime_boundary, + caps_and_defaults.defaults_mime_type, &post_data); // Send a hash of the printer capabilities to the server. We will use this // later to check if the capabilities have changed - CloudPrintHelpers::AddMultipartValueForUpload( - kPrinterCapsHashValue, + cloud_print::AddMultipartValueForUpload(kPrinterCapsHashValue, base::MD5String(caps_and_defaults.printer_capabilities), mime_boundary, std::string(), &post_data); diff --git a/chrome/service/cloud_print/cloud_print_consts.cc b/chrome/service/cloud_print/cloud_print_consts.cc index 39275b9..ee3f42b 100644 --- a/chrome/service/cloud_print/cloud_print_consts.cc +++ b/chrome/service/cloud_print/cloud_print_consts.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -17,8 +17,6 @@ const char kPrinterRemoveTagValue[] = "remove_tag"; const char kMessageTextValue[] = "message"; // Values in the respone JSON from the cloud print server -const char kPrinterListValue[] = "printers"; -const char kSuccessValue[] = "success"; const char kNameValue[] = "name"; const char kIdValue[] = "id"; const char kTicketUrlValue[] = "ticketUrl"; @@ -34,15 +32,11 @@ const char kProxyTagPrefix[] = "__cp__"; const char kTagsHashTagName[] = "__cp__tagshash"; const char kTagDryRunFlag[] = "__cp__dry_run"; - const char kDefaultCloudPrintServerUrl[] = "https://www.google.com/cloudprint"; const char kCloudPrintGaiaServiceId[] = "cloudprint"; const char kSyncGaiaServiceId[] = "chromiumsync"; const char kProxyAuthUserAgent[] = "ChromiumBrowser"; const char kCloudPrintPushNotificationsSource[] = "cloudprint.google.com"; -// The cloud print server expects the X-Google-CloudPrint-Proxy header for -// certain requests. -const char kChromeCloudPrintProxyHeader[] = "X-CloudPrint-Proxy: Chrome"; // The string to be appended to the user-agent for cloudprint requests. const char kCloudPrintUserAgent[] = "GoogleCloudPrintProxy"; @@ -65,4 +59,3 @@ const char kEnumPrintersFailedMessageId[] = "enumfail"; const char kDefaultCloudPrintOAuthClientId[] = "551556820943.apps.googleusercontent.com"; const char kDefaultCloudPrintOAuthClientSecret[] = "u3/mp8CgLFxh4uiX1855/MHe"; - diff --git a/chrome/service/cloud_print/cloud_print_consts.h b/chrome/service/cloud_print/cloud_print_consts.h index 319e2f4..c860448 100644 --- a/chrome/service/cloud_print/cloud_print_consts.h +++ b/chrome/service/cloud_print/cloud_print_consts.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -20,8 +20,6 @@ extern const char kPrinterRemoveTagValue[]; extern const char kMessageTextValue[]; // Values in the respone JSON from the cloud print server -extern const char kPrinterListValue[]; -extern const char kSuccessValue[]; extern const char kNameValue[]; extern const char kIdValue[]; extern const char kTicketUrlValue[]; @@ -41,7 +39,6 @@ extern const char kCloudPrintGaiaServiceId[]; extern const char kSyncGaiaServiceId[]; extern const char kProxyAuthUserAgent[]; extern const char kCloudPrintPushNotificationsSource[]; -extern const char kChromeCloudPrintProxyHeader[]; extern const char kCloudPrintUserAgent[]; extern const char kJobFetchReasonStartup[]; extern const char kJobFetchReasonPoll[]; @@ -53,7 +50,6 @@ extern const char kEnumPrintersFailedMessageId[]; extern const char kDefaultCloudPrintOAuthClientId[]; extern const char kDefaultCloudPrintOAuthClientSecret[]; - // Max retry count for job data fetch requests. const int kJobDataMaxRetryCount = 5; // Max retry count (infinity) for API fetch requests. diff --git a/chrome/service/cloud_print/cloud_print_helpers.cc b/chrome/service/cloud_print/cloud_print_helpers.cc index 631113a..3376f4c 100644 --- a/chrome/service/cloud_print/cloud_print_helpers.cc +++ b/chrome/service/cloud_print/cloud_print_helpers.cc @@ -6,12 +6,11 @@ #include "base/json/json_reader.h" #include "base/md5.h" -#include "base/memory/scoped_ptr.h" #include "base/rand_util.h" #include "base/string_util.h" #include "base/stringprintf.h" #include "base/utf_string_conversions.h" -#include "base/values.h" +#include "chrome/common/cloud_print/cloud_print_helpers.h" #include "chrome/service/cloud_print/cloud_print_consts.h" #include "chrome/service/cloud_print/cloud_print_token_store.h" #include "chrome/service/service_process.h" @@ -36,33 +35,20 @@ std::string StringFromJobStatus(cloud_print::PrintJobStatus status) { return ret; } -// Appends a relative path to the url making sure to append a '/' if the -// URL's path does not end with a slash. It is assumed that |path| does not -// begin with a '/'. -// NOTE: Since we ALWAYS want to append here, we simply append the path string -// instead of calling url_utils::ResolveRelative. The input |url| may or may not -// contain a '/' at the end. -std::string AppendPathToUrl(const GURL& url, const std::string& path) { - DCHECK(path[0] != '/'); - std::string ret = url.path(); - if (url.has_path() && (ret[ret.length() - 1] != '/')) { - ret += '/'; - } - ret += path; - return ret; -} - GURL CloudPrintHelpers::GetUrlForPrinterRegistration( const GURL& cloud_print_server_url) { - std::string path(AppendPathToUrl(cloud_print_server_url, "register")); + std::string path( + cloud_print::AppendPathToUrl(cloud_print_server_url, "register")); GURL::Replacements replacements; replacements.SetPathStr(path); return cloud_print_server_url.ReplaceComponents(replacements); } GURL CloudPrintHelpers::GetUrlForPrinterUpdate( - const GURL& cloud_print_server_url, const std::string& printer_id) { - std::string path(AppendPathToUrl(cloud_print_server_url, "update")); + const GURL& cloud_print_server_url, + const std::string& printer_id) { + std::string path( + cloud_print::AppendPathToUrl(cloud_print_server_url, "update")); GURL::Replacements replacements; replacements.SetPathStr(path); std::string query = StringPrintf("printerid=%s", printer_id.c_str()); @@ -71,8 +57,10 @@ GURL CloudPrintHelpers::GetUrlForPrinterUpdate( } GURL CloudPrintHelpers::GetUrlForPrinterDelete( - const GURL& cloud_print_server_url, const std::string& printer_id) { - std::string path(AppendPathToUrl(cloud_print_server_url, "delete")); + const GURL& cloud_print_server_url, + const std::string& printer_id) { + std::string path( + cloud_print::AppendPathToUrl(cloud_print_server_url, "delete")); GURL::Replacements replacements; replacements.SetPathStr(path); std::string query = StringPrintf("printerid=%s", printer_id.c_str()); @@ -82,7 +70,8 @@ GURL CloudPrintHelpers::GetUrlForPrinterDelete( GURL CloudPrintHelpers::GetUrlForPrinterList(const GURL& cloud_print_server_url, const std::string& proxy_id) { - std::string path(AppendPathToUrl(cloud_print_server_url, "list")); + std::string path( + cloud_print::AppendPathToUrl(cloud_print_server_url, "list")); GURL::Replacements replacements; replacements.SetPathStr(path); std::string query = StringPrintf("proxy=%s", proxy_id.c_str()); @@ -93,7 +82,8 @@ GURL CloudPrintHelpers::GetUrlForPrinterList(const GURL& cloud_print_server_url, GURL CloudPrintHelpers::GetUrlForJobFetch(const GURL& cloud_print_server_url, const std::string& printer_id, const std::string& reason) { - std::string path(AppendPathToUrl(cloud_print_server_url, "fetch")); + std::string path( + cloud_print::AppendPathToUrl(cloud_print_server_url, "fetch")); GURL::Replacements replacements; replacements.SetPathStr(path); std::string query = StringPrintf("printerid=%s&deb=%s", @@ -104,10 +94,12 @@ GURL CloudPrintHelpers::GetUrlForJobFetch(const GURL& cloud_print_server_url, } GURL CloudPrintHelpers::GetUrlForJobStatusUpdate( - const GURL& cloud_print_server_url, const std::string& job_id, + const GURL& cloud_print_server_url, + const std::string& job_id, cloud_print::PrintJobStatus status) { std::string status_string = StringFromJobStatus(status); - std::string path(AppendPathToUrl(cloud_print_server_url, "control")); + std::string path( + cloud_print::AppendPathToUrl(cloud_print_server_url, "control")); GURL::Replacements replacements; replacements.SetPathStr(path); std::string query = StringPrintf("jobid=%s&status=%s", @@ -117,10 +109,12 @@ GURL CloudPrintHelpers::GetUrlForJobStatusUpdate( } GURL CloudPrintHelpers::GetUrlForJobStatusUpdate( - const GURL& cloud_print_server_url, const std::string& job_id, + const GURL& cloud_print_server_url, + const std::string& job_id, const cloud_print::PrintJobDetails& details) { std::string status_string = StringFromJobStatus(details.status); - std::string path(AppendPathToUrl(cloud_print_server_url, "control")); + std::string path( + cloud_print::AppendPathToUrl(cloud_print_server_url, "control")); GURL::Replacements replacements; replacements.SetPathStr(path); std::string query = @@ -138,7 +132,8 @@ GURL CloudPrintHelpers::GetUrlForJobStatusUpdate( GURL CloudPrintHelpers::GetUrlForUserMessage(const GURL& cloud_print_server_url, const std::string& message_id) { - std::string path(AppendPathToUrl(cloud_print_server_url, "message")); + std::string path( + cloud_print::AppendPathToUrl(cloud_print_server_url, "message")); GURL::Replacements replacements; replacements.SetPathStr(path); std::string query = StringPrintf("code=%s", message_id.c_str()); @@ -151,7 +146,8 @@ GURL CloudPrintHelpers::GetUrlForGetAuthCode(const GURL& cloud_print_server_url, const std::string& proxy_id) { // We use the internal API "createrobot" instead of "getauthcode". This API // will add the robot as owner to all the existing printers for this user. - std::string path(AppendPathToUrl(cloud_print_server_url, "createrobot")); + std::string path( + cloud_print::AppendPathToUrl(cloud_print_server_url, "createrobot")); GURL::Replacements replacements; replacements.SetPathStr(path); std::string query = StringPrintf("oauth_client_id=%s&proxy=%s", @@ -161,54 +157,6 @@ GURL CloudPrintHelpers::GetUrlForGetAuthCode(const GURL& cloud_print_server_url, return cloud_print_server_url.ReplaceComponents(replacements); } - -bool CloudPrintHelpers::ParseResponseJSON( - const std::string& response_data, bool* succeeded, - DictionaryValue** response_dict) { - scoped_ptr<Value> message_value(base::JSONReader::Read(response_data, false)); - if (!message_value.get()) - return false; - - if (!message_value->IsType(Value::TYPE_DICTIONARY)) - return false; - - scoped_ptr<DictionaryValue> response_dict_local( - static_cast<DictionaryValue*>(message_value.release())); - if (succeeded) { - if (!response_dict_local->GetBoolean(kSuccessValue, succeeded)) - *succeeded = false; - } - if (response_dict) - *response_dict = response_dict_local.release(); - return true; -} - -void CloudPrintHelpers::AddMultipartValueForUpload( - const std::string& value_name, const std::string& value, - const std::string& mime_boundary, const std::string& content_type, - std::string* post_data) { - DCHECK(post_data); - // First line is the boundary - post_data->append("--" + mime_boundary + "\r\n"); - // Next line is the Content-disposition - post_data->append(StringPrintf("Content-Disposition: form-data; " - "name=\"%s\"\r\n", value_name.c_str())); - if (!content_type.empty()) { - // If Content-type is specified, the next line is that - post_data->append(StringPrintf("Content-Type: %s\r\n", - content_type.c_str())); - } - // Leave an empty line and append the value. - post_data->append(StringPrintf("\r\n%s\r\n", value.c_str())); -} - -// Create a MIME boundary marker (27 '-' characters followed by 16 hex digits). -void CloudPrintHelpers::CreateMimeBoundaryForUpload(std::string* out) { - int r1 = base::RandInt(0, kint32max); - int r2 = base::RandInt(0, kint32max); - base::SStringPrintf(out, "---------------------------%08X%08X", r1, r2); -} - std::string CloudPrintHelpers::GenerateHashOfStringMap( const std::map<std::string, std::string>& string_map) { std::string values_list; @@ -247,15 +195,15 @@ void CloudPrintHelpers::GenerateMultipartPostDataForPrinterTags( msg += it->first; msg += "="; msg += it->second; - AddMultipartValueForUpload(kPrinterTagValue, msg, mime_boundary, - std::string(), post_data); + cloud_print::AddMultipartValueForUpload(kPrinterTagValue, msg, + mime_boundary, std::string(), post_data); } std::string tags_hash = base::MD5String(tags_list); std::string tags_hash_msg(kTagsHashTagName); tags_hash_msg += "="; tags_hash_msg += tags_hash; - AddMultipartValueForUpload(kPrinterTagValue, tags_hash_msg, mime_boundary, - std::string(), post_data); + cloud_print::AddMultipartValueForUpload(kPrinterTagValue, tags_hash_msg, + mime_boundary, std::string(), post_data); } bool CloudPrintHelpers::IsDryRunJob(const std::vector<std::string>& tags) { @@ -282,4 +230,3 @@ std::string CloudPrintHelpers::GetCloudPrintAuthHeader() { } return header; } - diff --git a/chrome/service/cloud_print/cloud_print_helpers.h b/chrome/service/cloud_print/cloud_print_helpers.h index ee8ed49..33887e4 100644 --- a/chrome/service/cloud_print/cloud_print_helpers.h +++ b/chrome/service/cloud_print/cloud_print_helpers.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -34,7 +34,8 @@ class CloudPrintHelpers { const std::string& job_id, cloud_print::PrintJobStatus status); static GURL GetUrlForJobStatusUpdate( - const GURL& cloud_print_server_url, const std::string& job_id, + const GURL& cloud_print_server_url, + const std::string& job_id, const cloud_print::PrintJobDetails& details); static GURL GetUrlForUserMessage(const GURL& cloud_print_server_url, const std::string& message_id); @@ -42,22 +43,6 @@ class CloudPrintHelpers { const std::string& oauth_client_id, const std::string& proxy_id); - - // Parses the response data for any cloud print server request. The method - // returns false if there was an error in parsing the JSON. The succeeded - // value returns the value of the "success" value in the response JSON. - // Returns the response as a dictionary value. - static bool ParseResponseJSON(const std::string& response_data, - bool* succeeded, - base::DictionaryValue** response_dict); - - // Prepares one value as part of a multi-part upload request. - static void AddMultipartValueForUpload( - const std::string& value_name, const std::string& value, - const std::string& mime_boundary, const std::string& content_type, - std::string* post_data); -// Create a MIME boundary marker (27 '-' characters followed by 16 hex digits). - static void CreateMimeBoundaryForUpload(std::string *out); // Generates an MD5 hash of the contents of a string map. static std::string GenerateHashOfStringMap( const std::map<std::string, std::string>& string_map); diff --git a/chrome/service/cloud_print/cloud_print_url_fetcher.cc b/chrome/service/cloud_print/cloud_print_url_fetcher.cc index 137fa7e..4fc7e54 100644 --- a/chrome/service/cloud_print/cloud_print_url_fetcher.cc +++ b/chrome/service/cloud_print/cloud_print_url_fetcher.cc @@ -6,6 +6,7 @@ #include "base/stringprintf.h" #include "base/values.h" +#include "chrome/common/cloud_print/cloud_print_helpers.h" #include "chrome/service/cloud_print/cloud_print_consts.h" #include "chrome/service/cloud_print/cloud_print_helpers.h" #include "chrome/service/cloud_print/cloud_print_token_store.h" @@ -90,7 +91,7 @@ void CloudPrintURLFetcher::OnURLFetchComplete( // to a non-cloudprint-server URL eg. for authentication). bool succeeded = false; DictionaryValue* response_dict = NULL; - CloudPrintHelpers::ParseResponseJSON(data, &succeeded, &response_dict); + cloud_print::ParseResponseJSON(data, &succeeded, &response_dict); if (response_dict) action = delegate_->HandleJSONData(source, source->GetURL(), @@ -159,7 +160,7 @@ void CloudPrintURLFetcher::SetupRequestHeaders() { std::string headers = delegate_->GetAuthHeader(); if (!headers.empty()) headers += "\r\n"; - headers += kChromeCloudPrintProxyHeader; + headers += cloud_print::kChromeCloudPrintProxyHeader; if (!additional_headers_.empty()) { headers += "\r\n"; headers += additional_headers_; diff --git a/chrome/service/cloud_print/printer_job_handler.cc b/chrome/service/cloud_print/printer_job_handler.cc index a9edadf..60639ee 100644 --- a/chrome/service/cloud_print/printer_job_handler.cc +++ b/chrome/service/cloud_print/printer_job_handler.cc @@ -12,6 +12,7 @@ #include "base/stringprintf.h" #include "base/utf_string_conversions.h" #include "base/values.h" +#include "chrome/common/cloud_print/cloud_print_helpers.h" #include "chrome/service/cloud_print/cloud_print_consts.h" #include "chrome/service/cloud_print/cloud_print_helpers.h" #include "chrome/service/cloud_print/job_status_updater.h" @@ -185,7 +186,7 @@ void PrinterJobHandler::OnReceivePrinterCaps( std::string post_data; std::string mime_boundary; - CloudPrintHelpers::CreateMimeBoundaryForUpload(&mime_boundary); + cloud_print::CreateMimeBoundaryForUpload(&mime_boundary); if (succeeded) { std::string caps_hash = @@ -194,16 +195,14 @@ void PrinterJobHandler::OnReceivePrinterCaps( // Hashes don't match, we need to upload new capabilities (the defaults // go for free along with the capabilities) printer_info_cloud_.caps_hash = caps_hash; - CloudPrintHelpers::AddMultipartValueForUpload( - kPrinterCapsValue, caps_and_defaults.printer_capabilities, - mime_boundary, caps_and_defaults.caps_mime_type, &post_data); - CloudPrintHelpers::AddMultipartValueForUpload( - kPrinterDefaultsValue, caps_and_defaults.printer_defaults, - mime_boundary, caps_and_defaults.defaults_mime_type, - &post_data); - CloudPrintHelpers::AddMultipartValueForUpload( - kPrinterCapsHashValue, caps_hash, mime_boundary, std::string(), - &post_data); + cloud_print::AddMultipartValueForUpload(kPrinterCapsValue, + caps_and_defaults.printer_capabilities, mime_boundary, + caps_and_defaults.caps_mime_type, &post_data); + cloud_print::AddMultipartValueForUpload(kPrinterDefaultsValue, + caps_and_defaults.printer_defaults, mime_boundary, + caps_and_defaults.defaults_mime_type, &post_data); + cloud_print::AddMultipartValueForUpload(kPrinterCapsHashValue, + caps_hash, mime_boundary, std::string(), &post_data); } } else { LOG(ERROR) << "Failed to get printer caps and defaults for printer: " @@ -219,29 +218,23 @@ void PrinterJobHandler::OnReceivePrinterCaps( // Remove all the exising proxy tags. std::string cp_tag_wildcard(kProxyTagPrefix); cp_tag_wildcard += ".*"; - CloudPrintHelpers::AddMultipartValueForUpload( - kPrinterRemoveTagValue, cp_tag_wildcard, mime_boundary, std::string(), - &post_data); + cloud_print::AddMultipartValueForUpload(kPrinterRemoveTagValue, + cp_tag_wildcard, mime_boundary, std::string(), &post_data); } if (printer_info.printer_name != printer_info_.printer_name) { - CloudPrintHelpers::AddMultipartValueForUpload(kPrinterNameValue, - printer_info.printer_name, - mime_boundary, - std::string(), &post_data); + cloud_print::AddMultipartValueForUpload(kPrinterNameValue, + printer_info.printer_name, mime_boundary, std::string(), &post_data); } if (printer_info.printer_description != printer_info_.printer_description) { - CloudPrintHelpers::AddMultipartValueForUpload( - kPrinterDescValue, printer_info.printer_description, mime_boundary, - std::string() , &post_data); + cloud_print::AddMultipartValueForUpload(kPrinterDescValue, + printer_info.printer_description, mime_boundary, + std::string(), &post_data); } if (printer_info.printer_status != printer_info_.printer_status) { - CloudPrintHelpers::AddMultipartValueForUpload( - kPrinterStatusValue, - base::StringPrintf("%d", printer_info.printer_status), - mime_boundary, - std::string(), - &post_data); + cloud_print::AddMultipartValueForUpload(kPrinterStatusValue, + base::StringPrintf("%d", printer_info.printer_status), mime_boundary, + std::string(), &post_data); } printer_info_ = printer_info; if (!post_data.empty()) { diff --git a/chrome/test/base/test_browser_window.h b/chrome/test/base/test_browser_window.h index f024740..57f7f11 100644 --- a/chrome/test/base/test_browser_window.h +++ b/chrome/test/base/test_browser_window.h @@ -91,6 +91,7 @@ class TestBrowserWindow : public BrowserWindow { virtual void ShowBackgroundPages() OVERRIDE {} virtual void ShowBookmarkBubble(const GURL& url, bool already_bookmarked) OVERRIDE {} + virtual void ShowChromeToMobileBubble() OVERRIDE {} virtual bool IsDownloadShelfVisible() const OVERRIDE; virtual DownloadShelf* GetDownloadShelf() OVERRIDE; virtual void ConfirmBrowserCloseWithPendingDownloads() OVERRIDE {} |