summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authormsw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-13 06:08:46 +0000
committermsw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-13 06:08:46 +0000
commitb106ca53e5f9159acfd33498850a535dcf7cfbb7 (patch)
tree1b8bb9a3844cfebe8ef2737baf5673728763ebe6 /chrome
parent653007448cee9796264581fdd087d2353c3e03a6 (diff)
downloadchromium_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')
-rw-r--r--chrome/app/chrome_command_ids.h1
-rw-r--r--chrome/app/generated_resources.grd44
-rwxr-xr-xchrome/browser/chrome_to_mobile_service.cc336
-rwxr-xr-xchrome/browser/chrome_to_mobile_service.h133
-rwxr-xr-xchrome/browser/chrome_to_mobile_service_factory.cc32
-rwxr-xr-xchrome/browser/chrome_to_mobile_service_factory.h36
-rw-r--r--chrome/browser/ui/browser.cc10
-rw-r--r--chrome/browser/ui/browser.h1
-rw-r--r--chrome/browser/ui/browser_window.h3
-rw-r--r--chrome/browser/ui/cocoa/browser_window_cocoa.h1
-rw-r--r--chrome/browser/ui/cocoa/browser_window_cocoa.mm4
-rw-r--r--chrome/browser/ui/cocoa/view_id_util_browsertest.mm3
-rw-r--r--chrome/browser/ui/gtk/browser_window_gtk.cc4
-rw-r--r--chrome/browser/ui/gtk/browser_window_gtk.h1
-rw-r--r--chrome/browser/ui/gtk/view_id_util_browsertest.cc3
-rw-r--r--chrome/browser/ui/panels/panel.cc4
-rw-r--r--chrome/browser/ui/panels/panel.h1
-rw-r--r--chrome/browser/ui/view_ids.h1
-rw-r--r--chrome/browser/ui/views/browser_dialogs.h5
-rwxr-xr-xchrome/browser/ui/views/chrome_to_mobile_bubble_view.cc315
-rwxr-xr-xchrome/browser/ui/views/chrome_to_mobile_bubble_view.h104
-rw-r--r--chrome/browser/ui/views/frame/browser_view.cc4
-rw-r--r--chrome/browser/ui/views/frame/browser_view.h5
-rw-r--r--chrome/browser/ui/views/location_bar/chrome_to_mobile_view.cc76
-rw-r--r--chrome/browser/ui/views/location_bar/chrome_to_mobile_view.h48
-rw-r--r--chrome/browser/ui/views/location_bar/location_bar_view.cc54
-rw-r--r--chrome/browser/ui/views/location_bar/location_bar_view.h7
-rw-r--r--chrome/chrome_browser.gypi12
-rw-r--r--chrome/chrome_common.gypi2
-rwxr-xr-xchrome/common/cloud_print/cloud_print_helpers.cc94
-rwxr-xr-xchrome/common/cloud_print/cloud_print_helpers.h56
-rw-r--r--chrome/service/cloud_print/cloud_print_connector.cc57
-rw-r--r--chrome/service/cloud_print/cloud_print_consts.cc9
-rw-r--r--chrome/service/cloud_print/cloud_print_consts.h6
-rw-r--r--chrome/service/cloud_print/cloud_print_helpers.cc115
-rw-r--r--chrome/service/cloud_print/cloud_print_helpers.h21
-rw-r--r--chrome/service/cloud_print/cloud_print_url_fetcher.cc5
-rw-r--r--chrome/service/cloud_print/printer_job_handler.cc47
-rw-r--r--chrome/test/base/test_browser_window.h1
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 {}