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

Side by Side Diff: chrome/browser/ui/metro_pin_tab_helper_win.cc

Issue 11198025: Site specific secondary tiles for Windows 8. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Feedback Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/metro_pin_tab_helper_win.h"
6
7 #include "base/base_paths.h"
8 #include "base/bind.h"
9 #include "base/file_path.h"
10 #include "base/file_util.h"
11 #include "base/logging.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/memory/ref_counted_memory.h"
14 #include "base/path_service.h"
15 #include "base/string_number_conversions.h"
16 #include "base/utf_string_conversions.h"
17 #include "base/win/metro.h"
18 #include "chrome/browser/favicon/favicon_tab_helper.h"
19 #include "chrome/browser/ui/tab_contents/tab_contents.h"
20 #include "chrome/common/chrome_paths.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/web_contents.h"
23 #include "crypto/sha2.h"
24 #include "ui/gfx/canvas.h"
25 #include "ui/gfx/codec/png_codec.h"
26 #include "ui/gfx/color_analysis.h"
27 #include "ui/gfx/color_utils.h"
28 #include "ui/gfx/image/image.h"
29 #include "ui/gfx/rect.h"
30 #include "ui/gfx/size.h"
31
32 DEFINE_WEB_CONTENTS_USER_DATA_KEY(MetroPinTabHelper)
33
34 namespace {
35
36 // Generate an ID for the tile based on |url_str|. The ID is simply a hash of
37 // the URL.
38 string16 GenerateTileId(const string16& url_str) {
39 uint8 hash[crypto::kSHA256Length];
40 crypto::SHA256HashString(UTF16ToUTF8(url_str), hash, sizeof(hash));
41 std::string hash_str = base::HexEncode(hash, sizeof(hash));
42 return UTF8ToUTF16(hash_str);
43 }
44
45 // Get the path of the directory to store the tile logos in.
46 FilePath GetTileImagesDir() {
47 FilePath time_images_dir;
48 DCHECK(PathService::Get(chrome::DIR_USER_DATA, &time_images_dir));
49 time_images_dir = time_images_dir.Append(L"TileImages");
50 if (!file_util::DirectoryExists(time_images_dir) &&
51 !file_util::CreateDirectory(time_images_dir))
52 return FilePath();
53
54 return time_images_dir;
55 }
56
57 // For the given |image| and |tile_id|, try to create a site specific logo in
58 // |logo_dir|. The path of any created logo is returned in |logo_path|. Return
59 // value indicates whether a site specific logo was created.
60 bool CreateSiteSpecificLogo(const gfx::ImageSkia& image,
61 const string16& tile_id,
62 const FilePath& logo_dir,
63 FilePath* logo_path) {
64 const int kLogoWidth = 120;
65 const int kLogoHeight = 120;
66 const int kBoxWidth = 40;
67 const int kBoxHeight = 40;
68 const int kCaptionHeight = 20;
69 const double kBoxFade = 0.75;
70 const int kColorMeanDarknessLimit = 100;
71 const int kColorMeanLightnessLimit = 100;
72
73 if (image.isNull())
74 return false;
75
76 *logo_path = logo_dir.Append(tile_id).ReplaceExtension(L".png");
77
78 // Use a canvas to paint the tile logo.
79 gfx::Canvas canvas(gfx::Size(kLogoWidth, kLogoHeight), ui::SCALE_FACTOR_100P,
80 true);
81
82 // Fill the tile logo with the average color from bitmap. To do this we need
83 // to work out the 'average color' which is calculated using PNG encoded data
84 // of the bitmap.
85 SkPaint paint;
86 std::vector<unsigned char> icon_png;
87 if (!gfx::PNGCodec::EncodeBGRASkBitmap(*image.bitmap(), true, &icon_png))
88 return false;
89
90 scoped_refptr<base::RefCountedStaticMemory> icon_mem(
91 new base::RefCountedStaticMemory(&icon_png.front(), icon_png.size()));
92 color_utils::GridSampler sampler;
93 SkColor mean_color = color_utils::CalculateKMeanColorOfPNG(
94 icon_mem, kColorMeanDarknessLimit, kColorMeanLightnessLimit, sampler);
95 paint.setColor(mean_color);
96 canvas.DrawRect(gfx::Rect(0, 0, kLogoWidth, kLogoHeight), paint);
97
98 // Now paint a faded square for the favicon to go in.
99 color_utils::HSL shift = {-1, -1, kBoxFade};
100 paint.setColor(color_utils::HSLShift(mean_color, shift));
101 int box_left = (kLogoWidth - kBoxWidth) / 2;
102 int box_top = (kLogoHeight - kCaptionHeight - kBoxHeight) / 2;
103 canvas.DrawRect(gfx::Rect(box_left, box_top, kBoxWidth, kBoxHeight), paint);
104
105 // Now paint the favicon into the tile, leaving some room at the bottom for
106 // the caption.
107 int left = (kLogoWidth - image.width()) / 2;
108 int top = (kLogoHeight - kCaptionHeight - image.height()) / 2;
109 canvas.DrawImageInt(image, left, top);
110
111 SkBitmap logo_bitmap = canvas.ExtractImageRep().sk_bitmap();
112 std::vector<unsigned char> logo_png;
113 if (!gfx::PNGCodec::EncodeBGRASkBitmap(logo_bitmap, true, &logo_png))
114 return false;
115
116 return file_util::WriteFile(*logo_path,
117 reinterpret_cast<char*>(&logo_png[0]),
118 logo_png.size()) > 0;
119 }
120
121 // Get the path to the backup logo. If the backup logo already exists in
122 // |logo_dir|, it will be used, otherwise it will be copied out of the install
123 // folder. (The version in the install folder is not used as it may disappear
124 // after an upgrade, causing tiles to lose their images if Windows rebuilds
125 // its tile image cache.)
126 // The path to the logo is returned in |logo_path|, with the return value
127 // indicating success.
128 bool GetPathToBackupLogo(const FilePath& logo_dir,
129 FilePath* logo_path) {
130 const wchar_t kDefaultLogoFileName[] = L"SecondaryTile.png";
131 *logo_path = logo_dir.Append(kDefaultLogoFileName);
132 if (file_util::PathExists(*logo_path))
133 return true;
134
135 FilePath default_logo_path;
136 DCHECK(PathService::Get(base::DIR_MODULE, &default_logo_path));
137 default_logo_path = default_logo_path.Append(kDefaultLogoFileName);
138 return file_util::CopyFile(default_logo_path, *logo_path);
139 }
140
141 } // namespace
142
143 class MetroPinTabHelper::TaskRunner
144 : public base::RefCountedThreadSafe<TaskRunner> {
145 public:
146 TaskRunner() {}
147
148 void PinPageToStartScreen(const string16& title,
149 const string16& url,
150 const gfx::ImageSkia& image);
151
152 private:
153 ~TaskRunner() {}
154
155 friend class base::RefCountedThreadSafe<TaskRunner>;
156 DISALLOW_COPY_AND_ASSIGN(TaskRunner);
157 };
158
159 void MetroPinTabHelper::TaskRunner::PinPageToStartScreen(
160 const string16& title,
161 const string16& url,
162 const gfx::ImageSkia& image) {
163 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
164
165 string16 tile_id = GenerateTileId(url);
166 FilePath logo_dir = GetTileImagesDir();
167 if (logo_dir.empty()) {
168 LOG(ERROR) << "Could not create directory to store tile image.";
169 return;
170 }
171
172 FilePath logo_path;
173 if (!CreateSiteSpecificLogo(image, tile_id, logo_dir, &logo_path) &&
174 !GetPathToBackupLogo(logo_dir, &logo_path)) {
175 LOG(ERROR) << "Count not get path to logo tile.";
176 return;
177 }
178
179 HMODULE metro_module = base::win::GetMetroModule();
180 if (!metro_module)
181 return;
182
183 typedef void (*MetroPinToStartScreen)(const string16&, const string16&,
184 const string16&, const FilePath&);
185 MetroPinToStartScreen metro_pin_to_start_screen =
186 reinterpret_cast<MetroPinToStartScreen>(
187 ::GetProcAddress(metro_module, "MetroPinToStartScreen"));
188 if (!metro_pin_to_start_screen) {
189 NOTREACHED();
190 return;
191 }
192
193 VLOG(1) << __FUNCTION__ << " calling pin with title: " << title
194 << " and url: " << url;
195 metro_pin_to_start_screen(tile_id, title, url, logo_path);
196 }
197
198 MetroPinTabHelper::MetroPinTabHelper(content::WebContents* web_contents)
199 : content::WebContentsObserver(web_contents),
200 is_pinned_(false),
201 task_runner_(new TaskRunner) {}
202
203 MetroPinTabHelper::~MetroPinTabHelper() {}
204
205 void MetroPinTabHelper::TogglePinnedToStartScreen() {
206 UpdatePinnedStateForCurrentURL();
207 bool was_pinned = is_pinned_;
208
209 // TODO(benwells): This will update the state incorrectly if the user
210 // cancels. To fix this some sort of callback needs to be introduced as
211 // the pinning happens on another thread.
212 is_pinned_ = !is_pinned_;
213
214 if (was_pinned) {
215 UnPinPageFromStartScreen();
216 return;
217 }
218
219 // TODO(benwells): Handle downloading a larger favicon if there is one.
220 GURL url = web_contents()->GetURL();
221 string16 url_str = UTF8ToUTF16(url.spec());
222 string16 title = web_contents()->GetTitle();
223 TabContents* tab_contents = TabContents::FromWebContents(web_contents());
224 DCHECK(tab_contents);
225 FaviconTabHelper* favicon_tab_helper = FaviconTabHelper::FromWebContents(
226 tab_contents->web_contents());
227 if (favicon_tab_helper->FaviconIsValid()) {
228 gfx::Image favicon = favicon_tab_helper->GetFavicon();
229 gfx::ImageSkia favicon_skia = favicon.AsImageSkia().DeepCopy();
230 content::BrowserThread::PostTask(
231 content::BrowserThread::FILE,
232 FROM_HERE,
233 base::Bind(&TaskRunner::PinPageToStartScreen,
234 task_runner_,
235 title,
236 url_str,
237 favicon_skia));
238 return;
239 }
240
241 content::BrowserThread::PostTask(
242 content::BrowserThread::FILE,
243 FROM_HERE,
244 base::Bind(&TaskRunner::PinPageToStartScreen,
245 task_runner_,
246 title,
247 url_str,
248 gfx::ImageSkia()));
249 }
250
251 void MetroPinTabHelper::DidNavigateMainFrame(
252 const content::LoadCommittedDetails& /*details*/,
253 const content::FrameNavigateParams& /*params*/) {
254 UpdatePinnedStateForCurrentURL();
255 }
256
257 void MetroPinTabHelper::UpdatePinnedStateForCurrentURL() {
258 HMODULE metro_module = base::win::GetMetroModule();
259 if (!metro_module)
260 return;
261
262 typedef BOOL (*MetroIsPinnedToStartScreen)(const string16&);
263 MetroIsPinnedToStartScreen metro_is_pinned_to_start_screen =
264 reinterpret_cast<MetroIsPinnedToStartScreen>(
265 ::GetProcAddress(metro_module, "MetroIsPinnedToStartScreen"));
266 if (!metro_is_pinned_to_start_screen) {
267 NOTREACHED();
268 return;
269 }
270
271 GURL url = web_contents()->GetURL();
272 string16 tile_id = GenerateTileId(UTF8ToUTF16(url.spec()));
273 is_pinned_ = metro_is_pinned_to_start_screen(tile_id) != 0;
274 VLOG(1) << __FUNCTION__ << " with url " << UTF8ToUTF16(url.spec())
275 << " result: " << is_pinned_;
276 }
277
278 void MetroPinTabHelper::UnPinPageFromStartScreen() {
279 HMODULE metro_module = base::win::GetMetroModule();
280 if (!metro_module)
281 return;
282
283 typedef void (*MetroUnPinFromStartScreen)(const string16&);
284 MetroUnPinFromStartScreen metro_un_pin_from_start_screen =
285 reinterpret_cast<MetroUnPinFromStartScreen>(
286 ::GetProcAddress(metro_module, "MetroUnPinFromStartScreen"));
287 if (!metro_un_pin_from_start_screen) {
288 NOTREACHED();
289 return;
290 }
291
292 GURL url = web_contents()->GetURL();
293 VLOG(1) << __FUNCTION__ << " calling unpin with url: "
294 << UTF8ToUTF16(url.spec());
295 string16 tile_id = GenerateTileId(UTF8ToUTF16(url.spec()));
296 metro_un_pin_from_start_screen(tile_id);
297 }
OLDNEW
« no previous file with comments | « chrome/browser/ui/metro_pin_tab_helper_win.h ('k') | chrome/browser/ui/tab_contents/tab_contents.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698