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

Side by Side Diff: content/browser/tab_contents/web_drag_source_mac.mm

Issue 10014024: TabContents -> WebContentsImpl, part 2. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View 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 #import "content/browser/tab_contents/web_drag_source_mac.h"
6
7 #include <sys/param.h>
8
9 #include "base/bind.h"
10 #include "base/file_path.h"
11 #include "base/pickle.h"
12 #include "base/string_util.h"
13 #include "base/sys_string_conversions.h"
14 #include "base/threading/thread.h"
15 #include "base/threading/thread_restrictions.h"
16 #include "base/utf_string_conversions.h"
17 #include "content/browser/browser_thread_impl.h"
18 #include "content/browser/download/drag_download_file.h"
19 #include "content/browser/download/drag_download_util.h"
20 #include "content/browser/renderer_host/render_view_host_impl.h"
21 #include "content/browser/tab_contents/tab_contents.h"
22 #include "content/public/browser/content_browser_client.h"
23 #include "content/public/common/content_client.h"
24 #include "content/public/common/url_constants.h"
25 #include "net/base/file_stream.h"
26 #include "net/base/net_util.h"
27 #include "ui/base/clipboard/custom_data_helper.h"
28 #include "ui/gfx/mac/nsimage_cache.h"
29 #include "webkit/glue/webdropdata.h"
30
31 using base::SysNSStringToUTF8;
32 using base::SysUTF8ToNSString;
33 using base::SysUTF16ToNSString;
34 using content::BrowserThread;
35 using content::RenderViewHostImpl;
36 using net::FileStream;
37
38 namespace {
39
40 // An unofficial standard pasteboard title type to be provided alongside the
41 // |NSURLPboardType|.
42 NSString* const kNSURLTitlePboardType = @"public.url-name";
43
44 // Converts a string16 into a FilePath. Use this method instead of
45 // -[NSString fileSystemRepresentation] to prevent exceptions from being thrown.
46 // See http://crbug.com/78782 for more info.
47 FilePath FilePathFromFilename(const string16& filename) {
48 NSString* str = SysUTF16ToNSString(filename);
49 char buf[MAXPATHLEN];
50 if (![str getFileSystemRepresentation:buf maxLength:sizeof(buf)])
51 return FilePath();
52 return FilePath(buf);
53 }
54
55 // Returns a filename appropriate for the drop data
56 // TODO(viettrungluu): Refactor to make it common across platforms,
57 // and move it somewhere sensible.
58 FilePath GetFileNameFromDragData(const WebDropData& drop_data) {
59 FilePath file_name(FilePathFromFilename(drop_data.file_description_filename));
60 std::string extension = file_name.Extension();
61 file_name = file_name.BaseName().RemoveExtension();
62
63 // Images without ALT text will only have a file extension so we need to
64 // synthesize one from the provided extension and URL.
65 if (file_name.empty()) {
66 // Retrieve the name from the URL.
67 string16 suggested_filename =
68 net::GetSuggestedFilename(drop_data.url, "", "", "", "", "");
69 file_name = FilePathFromFilename(suggested_filename);
70 }
71
72 return file_name.ReplaceExtension(extension);
73 }
74
75 // This helper's sole task is to write out data for a promised file; the caller
76 // is responsible for opening the file. It takes the drop data and an open file
77 // stream.
78 void PromiseWriterHelper(const WebDropData& drop_data,
79 FileStream* file_stream) {
80 DCHECK(file_stream);
81 file_stream->WriteSync(drop_data.file_contents.data(),
82 drop_data.file_contents.length());
83
84 if (file_stream)
85 file_stream->CloseSync();
86 }
87
88 } // namespace
89
90
91 @interface WebDragSource(Private)
92
93 - (void)fillPasteboard;
94 - (NSImage*)dragImage;
95
96 @end // @interface WebDragSource(Private)
97
98
99 @implementation WebDragSource
100
101 - (id)initWithContents:(TabContents*)contents
102 view:(NSView*)contentsView
103 dropData:(const WebDropData*)dropData
104 image:(NSImage*)image
105 offset:(NSPoint)offset
106 pasteboard:(NSPasteboard*)pboard
107 dragOperationMask:(NSDragOperation)dragOperationMask {
108 if ((self = [super init])) {
109 contents_ = contents;
110 DCHECK(contents_);
111
112 contentsView_ = contentsView;
113 DCHECK(contentsView_);
114
115 dropData_.reset(new WebDropData(*dropData));
116 DCHECK(dropData_.get());
117
118 dragImage_.reset([image retain]);
119 imageOffset_ = offset;
120
121 pasteboard_.reset([pboard retain]);
122 DCHECK(pasteboard_.get());
123
124 dragOperationMask_ = dragOperationMask;
125
126 fileExtension_ = nil;
127
128 [self fillPasteboard];
129 }
130
131 return self;
132 }
133
134 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal {
135 return dragOperationMask_;
136 }
137
138 - (void)lazyWriteToPasteboard:(NSPasteboard*)pboard forType:(NSString*)type {
139 // NSHTMLPboardType requires the character set to be declared. Otherwise, it
140 // assumes US-ASCII. Awesome.
141 const string16 kHtmlHeader = ASCIIToUTF16(
142 "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">");
143
144 // Be extra paranoid; avoid crashing.
145 if (!dropData_.get()) {
146 NOTREACHED() << "No drag-and-drop data available for lazy write.";
147 return;
148 }
149
150 // HTML.
151 if ([type isEqualToString:NSHTMLPboardType]) {
152 DCHECK(!dropData_->text_html.empty());
153 // See comment on |kHtmlHeader| above.
154 [pboard setString:SysUTF16ToNSString(kHtmlHeader + dropData_->text_html)
155 forType:NSHTMLPboardType];
156
157 // URL.
158 } else if ([type isEqualToString:NSURLPboardType]) {
159 DCHECK(dropData_->url.is_valid());
160 NSString* urlStr = SysUTF8ToNSString(dropData_->url.spec());
161 NSURL* url = [NSURL URLWithString:urlStr];
162 // If NSURL creation failed, check for a badly-escaped JavaScript URL.
163 // Strip out any existing escapes and then re-escape uniformly.
164 if (!url && urlStr && dropData_->url.SchemeIs(chrome::kJavaScriptScheme)) {
165 NSString* unEscapedStr = [urlStr
166 stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
167 NSString* escapedStr = [unEscapedStr
168 stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
169 url = [NSURL URLWithString:escapedStr];
170 }
171 [url writeToPasteboard:pboard];
172 // URL title.
173 } else if ([type isEqualToString:kNSURLTitlePboardType]) {
174 [pboard setString:SysUTF16ToNSString(dropData_->url_title)
175 forType:kNSURLTitlePboardType];
176
177 // File contents.
178 } else if ([type isEqualToString:NSFileContentsPboardType] ||
179 (fileExtension_ &&
180 [type isEqualToString:NSCreateFileContentsPboardType(fileExtension_)])) {
181 // TODO(viettrungluu: find something which is known to accept
182 // NSFileContentsPboardType to check that this actually works!
183 scoped_nsobject<NSFileWrapper> file_wrapper(
184 [[NSFileWrapper alloc] initRegularFileWithContents:[NSData
185 dataWithBytes:dropData_->file_contents.data()
186 length:dropData_->file_contents.length()]]);
187 [file_wrapper setPreferredFilename:SysUTF8ToNSString(
188 GetFileNameFromDragData(*dropData_).value())];
189 [pboard writeFileWrapper:file_wrapper];
190
191 // TIFF.
192 } else if ([type isEqualToString:NSTIFFPboardType]) {
193 // TODO(viettrungluu): This is a bit odd since we rely on Cocoa to render
194 // our image into a TIFF. This is also suboptimal since this is all done
195 // synchronously. I'm not sure there's much we can easily do about it.
196 scoped_nsobject<NSImage> image(
197 [[NSImage alloc] initWithData:[NSData
198 dataWithBytes:dropData_->file_contents.data()
199 length:dropData_->file_contents.length()]]);
200 [pboard setData:[image TIFFRepresentation] forType:NSTIFFPboardType];
201
202 // Plain text.
203 } else if ([type isEqualToString:NSStringPboardType]) {
204 DCHECK(!dropData_->plain_text.empty());
205 [pboard setString:SysUTF16ToNSString(dropData_->plain_text)
206 forType:NSStringPboardType];
207
208 // Custom MIME data.
209 } else if ([type isEqualToString:ui::kWebCustomDataPboardType]) {
210 Pickle pickle;
211 ui::WriteCustomDataToPickle(dropData_->custom_data, &pickle);
212 [pboard setData:[NSData dataWithBytes:pickle.data() length:pickle.size()]
213 forType:ui::kWebCustomDataPboardType];
214
215 // Oops!
216 } else {
217 NOTREACHED() << "Asked for a drag pasteboard type we didn't offer.";
218 }
219 }
220
221 - (NSPoint)convertScreenPoint:(NSPoint)screenPoint {
222 DCHECK([contentsView_ window]);
223 NSPoint basePoint = [[contentsView_ window] convertScreenToBase:screenPoint];
224 return [contentsView_ convertPoint:basePoint fromView:nil];
225 }
226
227 - (void)startDrag {
228 NSEvent* currentEvent = [NSApp currentEvent];
229
230 // Synthesize an event for dragging, since we can't be sure that
231 // [NSApp currentEvent] will return a valid dragging event.
232 NSWindow* window = [contentsView_ window];
233 NSPoint position = [window mouseLocationOutsideOfEventStream];
234 NSTimeInterval eventTime = [currentEvent timestamp];
235 NSEvent* dragEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
236 location:position
237 modifierFlags:NSLeftMouseDraggedMask
238 timestamp:eventTime
239 windowNumber:[window windowNumber]
240 context:nil
241 eventNumber:0
242 clickCount:1
243 pressure:1.0];
244
245 if (dragImage_) {
246 position.x -= imageOffset_.x;
247 // Deal with Cocoa's flipped coordinate system.
248 position.y -= [dragImage_.get() size].height - imageOffset_.y;
249 }
250 // Per kwebster, offset arg is ignored, see -_web_DragImageForElement: in
251 // third_party/WebKit/Source/WebKit/mac/Misc/WebNSViewExtras.m.
252 [window dragImage:[self dragImage]
253 at:position
254 offset:NSZeroSize
255 event:dragEvent
256 pasteboard:pasteboard_
257 source:contentsView_
258 slideBack:YES];
259 }
260
261 - (void)endDragAt:(NSPoint)screenPoint
262 operation:(NSDragOperation)operation {
263 contents_->SystemDragEnded();
264
265 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
266 contents_->GetRenderViewHost());
267 if (rvh) {
268 // Convert |screenPoint| to view coordinates and flip it.
269 NSPoint localPoint = NSMakePoint(0, 0);
270 if ([contentsView_ window])
271 localPoint = [self convertScreenPoint:screenPoint];
272 NSRect viewFrame = [contentsView_ frame];
273 localPoint.y = viewFrame.size.height - localPoint.y;
274 // Flip |screenPoint|.
275 NSRect screenFrame = [[[contentsView_ window] screen] frame];
276 screenPoint.y = screenFrame.size.height - screenPoint.y;
277
278 // If AppKit returns a copy and move operation, mask off the move bit
279 // because WebCore does not understand what it means to do both, which
280 // results in an assertion failure/renderer crash.
281 if (operation == (NSDragOperationMove | NSDragOperationCopy))
282 operation &= ~NSDragOperationMove;
283
284 rvh->DragSourceEndedAt(localPoint.x, localPoint.y,
285 screenPoint.x, screenPoint.y,
286 static_cast<WebKit::WebDragOperation>(operation));
287 }
288
289 // Make sure the pasteboard owner isn't us.
290 [pasteboard_ declareTypes:[NSArray array] owner:nil];
291 }
292
293 - (void)moveDragTo:(NSPoint)screenPoint {
294 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
295 contents_->GetRenderViewHost());
296 if (rvh) {
297 // Convert |screenPoint| to view coordinates and flip it.
298 NSPoint localPoint = NSMakePoint(0, 0);
299 if ([contentsView_ window])
300 localPoint = [self convertScreenPoint:screenPoint];
301 NSRect viewFrame = [contentsView_ frame];
302 localPoint.y = viewFrame.size.height - localPoint.y;
303 // Flip |screenPoint|.
304 NSRect screenFrame = [[[contentsView_ window] screen] frame];
305 screenPoint.y = screenFrame.size.height - screenPoint.y;
306
307 rvh->DragSourceMovedTo(localPoint.x, localPoint.y,
308 screenPoint.x, screenPoint.y);
309 }
310 }
311
312 - (NSString*)dragPromisedFileTo:(NSString*)path {
313 // Be extra paranoid; avoid crashing.
314 if (!dropData_.get()) {
315 NOTREACHED() << "No drag-and-drop data available for promised file.";
316 return nil;
317 }
318
319 FilePath fileName = downloadFileName_.empty() ?
320 GetFileNameFromDragData(*dropData_) : downloadFileName_;
321 FilePath filePath(SysNSStringToUTF8(path));
322 filePath = filePath.Append(fileName);
323
324 // CreateFileStreamForDrop() will call file_util::PathExists(),
325 // which is blocking. Since this operation is already blocking the
326 // UI thread on OSX, it should be reasonable to let it happen.
327 base::ThreadRestrictions::ScopedAllowIO allowIO;
328 FileStream* fileStream =
329 drag_download_util::CreateFileStreamForDrop(
330 &filePath, content::GetContentClient()->browser()->GetNetLog());
331 if (!fileStream)
332 return nil;
333
334 if (downloadURL_.is_valid()) {
335 scoped_refptr<DragDownloadFile> dragFileDownloader(new DragDownloadFile(
336 filePath,
337 linked_ptr<net::FileStream>(fileStream),
338 downloadURL_,
339 contents_->GetURL(),
340 contents_->GetEncoding(),
341 contents_));
342
343 // The finalizer will take care of closing and deletion.
344 dragFileDownloader->Start(
345 new drag_download_util::PromiseFileFinalizer(dragFileDownloader));
346 } else {
347 // The writer will take care of closing and deletion.
348 BrowserThread::PostTask(BrowserThread::FILE,
349 FROM_HERE,
350 base::Bind(&PromiseWriterHelper,
351 *dropData_,
352 base::Owned(fileStream)));
353 }
354
355 // Once we've created the file, we should return the file name.
356 return SysUTF8ToNSString(filePath.BaseName().value());
357 }
358
359 @end // @implementation WebDragSource
360
361
362 @implementation WebDragSource (Private)
363
364 - (void)fillPasteboard {
365 DCHECK(pasteboard_.get());
366
367 [pasteboard_ declareTypes:[NSArray array] owner:contentsView_];
368
369 // HTML.
370 if (!dropData_->text_html.empty())
371 [pasteboard_ addTypes:[NSArray arrayWithObject:NSHTMLPboardType]
372 owner:contentsView_];
373
374 // URL (and title).
375 if (dropData_->url.is_valid())
376 [pasteboard_ addTypes:[NSArray arrayWithObjects:NSURLPboardType,
377 kNSURLTitlePboardType, nil]
378 owner:contentsView_];
379
380 std::string fileExtension;
381
382 // File.
383 if (!dropData_->file_contents.empty() ||
384 !dropData_->download_metadata.empty()) {
385 if (dropData_->download_metadata.empty()) {
386 fileExtension = GetFileNameFromDragData(*dropData_).Extension();
387 } else {
388 string16 mimeType;
389 FilePath fileName;
390 if (drag_download_util::ParseDownloadMetadata(
391 dropData_->download_metadata,
392 &mimeType,
393 &fileName,
394 &downloadURL_)) {
395 // Generate the file name based on both mime type and proposed file
396 // name.
397 std::string defaultName =
398 content::GetContentClient()->browser()->GetDefaultDownloadName();
399 downloadFileName_ =
400 net::GenerateFileName(downloadURL_,
401 std::string(),
402 std::string(),
403 fileName.value(),
404 UTF16ToUTF8(mimeType),
405 defaultName);
406 fileExtension = downloadFileName_.Extension();
407 }
408 }
409
410 if (!fileExtension.empty()) {
411 // Strip the leading dot.
412 fileExtension_ = SysUTF8ToNSString(fileExtension.substr(1));
413 // File contents (with and without specific type), and file (HFS) promise.
414 // TODO(viettrungluu): others?
415 NSArray* types = [NSArray arrayWithObjects:
416 NSFileContentsPboardType,
417 NSCreateFileContentsPboardType(fileExtension_),
418 NSFilesPromisePboardType,
419 nil];
420 [pasteboard_ addTypes:types owner:contentsView_];
421
422 if (!dropData_->file_contents.empty()) {
423 [pasteboard_ addTypes:[NSArray arrayWithObject:NSTIFFPboardType]
424 owner:contentsView_];
425 }
426
427 // For the file promise, we need to specify the extension.
428 [pasteboard_ setPropertyList:[NSArray arrayWithObject:fileExtension_]
429 forType:NSFilesPromisePboardType];
430 }
431 }
432
433 // Plain text.
434 if (!dropData_->plain_text.empty())
435 [pasteboard_ addTypes:[NSArray arrayWithObject:NSStringPboardType]
436 owner:contentsView_];
437
438 if (!dropData_->custom_data.empty()) {
439 [pasteboard_
440 addTypes:[NSArray arrayWithObject:ui::kWebCustomDataPboardType]
441 owner:contentsView_];
442 }
443 }
444
445 - (NSImage*)dragImage {
446 if (dragImage_)
447 return dragImage_;
448
449 // Default to returning a generic image.
450 return gfx::GetCachedImageWithName(@"nav.pdf");
451 }
452
453 @end // @implementation WebDragSource (Private)
OLDNEW
« no previous file with comments | « content/browser/tab_contents/web_drag_source_mac.h ('k') | content/browser/tab_contents/web_drag_source_win.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698