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

Side by Side Diff: chrome/browser/ui/cocoa/web_drag_source.mm

Issue 6358008: [Mac] Organize some files into chrome/browser/ui/cocoa/tab_contents/.... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 9 years, 11 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) 2010 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 "chrome/browser/ui/cocoa/web_drag_source.h"
6
7 #include "app/mac/nsimage_cache.h"
8 #include "base/file_path.h"
9 #include "base/string_util.h"
10 #include "base/sys_string_conversions.h"
11 #include "base/task.h"
12 #include "base/threading/thread.h"
13 #include "base/utf_string_conversions.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/download/download_manager.h"
16 #include "chrome/browser/download/download_util.h"
17 #include "chrome/browser/download/drag_download_file.h"
18 #include "chrome/browser/download/drag_download_util.h"
19 #include "chrome/browser/renderer_host/render_view_host.h"
20 #include "chrome/browser/tab_contents/tab_contents.h"
21 #include "chrome/browser/tab_contents/tab_contents_view_mac.h"
22 #include "net/base/file_stream.h"
23 #include "net/base/net_util.h"
24 #import "third_party/mozilla/NSPasteboard+Utils.h"
25 #include "webkit/glue/webdropdata.h"
26
27 using base::SysNSStringToUTF8;
28 using base::SysUTF8ToNSString;
29 using base::SysUTF16ToNSString;
30 using net::FileStream;
31
32
33 namespace {
34
35 // An unofficial standard pasteboard title type to be provided alongside the
36 // |NSURLPboardType|.
37 NSString* const kNSURLTitlePboardType = @"public.url-name";
38
39 // Returns a filename appropriate for the drop data
40 // TODO(viettrungluu): Refactor to make it common across platforms,
41 // and move it somewhere sensible.
42 FilePath GetFileNameFromDragData(const WebDropData& drop_data) {
43 // Images without ALT text will only have a file extension so we need to
44 // synthesize one from the provided extension and URL.
45 FilePath file_name([SysUTF16ToNSString(drop_data.file_description_filename)
46 fileSystemRepresentation]);
47 file_name = file_name.BaseName().RemoveExtension();
48
49 if (file_name.empty()) {
50 // Retrieve the name from the URL.
51 file_name = net::GetSuggestedFilename(drop_data.url, "", "", FilePath());
52 }
53
54 file_name = file_name.ReplaceExtension([SysUTF16ToNSString(
55 drop_data.file_extension) fileSystemRepresentation]);
56
57 return file_name;
58 }
59
60 // This class's sole task is to write out data for a promised file; the caller
61 // is responsible for opening the file.
62 class PromiseWriterTask : public Task {
63 public:
64 // Assumes ownership of file_stream.
65 PromiseWriterTask(const WebDropData& drop_data,
66 FileStream* file_stream);
67 virtual ~PromiseWriterTask();
68 virtual void Run();
69
70 private:
71 WebDropData drop_data_;
72
73 // This class takes ownership of file_stream_ and will close and delete it.
74 scoped_ptr<FileStream> file_stream_;
75 };
76
77 // Takes the drop data and an open file stream (which it takes ownership of and
78 // will close and delete).
79 PromiseWriterTask::PromiseWriterTask(const WebDropData& drop_data,
80 FileStream* file_stream) :
81 drop_data_(drop_data) {
82 file_stream_.reset(file_stream);
83 DCHECK(file_stream_.get());
84 }
85
86 PromiseWriterTask::~PromiseWriterTask() {
87 DCHECK(file_stream_.get());
88 if (file_stream_.get())
89 file_stream_->Close();
90 }
91
92 void PromiseWriterTask::Run() {
93 CHECK(file_stream_.get());
94 file_stream_->Write(drop_data_.file_contents.data(),
95 drop_data_.file_contents.length(),
96 NULL);
97
98 // Let our destructor take care of business.
99 }
100
101 } // namespace
102
103
104 @interface WebDragSource(Private)
105
106 - (void)fillPasteboard;
107 - (NSImage*)dragImage;
108
109 @end // @interface WebDragSource(Private)
110
111
112 @implementation WebDragSource
113
114 - (id)initWithContentsView:(TabContentsViewCocoa*)contentsView
115 dropData:(const WebDropData*)dropData
116 image:(NSImage*)image
117 offset:(NSPoint)offset
118 pasteboard:(NSPasteboard*)pboard
119 dragOperationMask:(NSDragOperation)dragOperationMask {
120 if ((self = [super init])) {
121 contentsView_ = contentsView;
122 DCHECK(contentsView_);
123
124 dropData_.reset(new WebDropData(*dropData));
125 DCHECK(dropData_.get());
126
127 dragImage_.reset([image retain]);
128 imageOffset_ = offset;
129
130 pasteboard_.reset([pboard retain]);
131 DCHECK(pasteboard_.get());
132
133 dragOperationMask_ = dragOperationMask;
134
135 [self fillPasteboard];
136 }
137
138 return self;
139 }
140
141 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal {
142 return dragOperationMask_;
143 }
144
145 - (void)lazyWriteToPasteboard:(NSPasteboard*)pboard forType:(NSString*)type {
146 // NSHTMLPboardType requires the character set to be declared. Otherwise, it
147 // assumes US-ASCII. Awesome.
148 static const string16 kHtmlHeader =
149 ASCIIToUTF16("<meta http-equiv=\"Content-Type\" "
150 "content=\"text/html;charset=UTF-8\">");
151
152 // Be extra paranoid; avoid crashing.
153 if (!dropData_.get()) {
154 NOTREACHED() << "No drag-and-drop data available for lazy write.";
155 return;
156 }
157
158 // HTML.
159 if ([type isEqualToString:NSHTMLPboardType]) {
160 DCHECK(!dropData_->text_html.empty());
161 // See comment on |kHtmlHeader| above.
162 [pboard setString:SysUTF16ToNSString(kHtmlHeader + dropData_->text_html)
163 forType:NSHTMLPboardType];
164
165 // URL.
166 } else if ([type isEqualToString:NSURLPboardType]) {
167 DCHECK(dropData_->url.is_valid());
168 NSURL* url = [NSURL URLWithString:SysUTF8ToNSString(dropData_->url.spec())];
169 [url writeToPasteboard:pboard];
170
171 // URL title.
172 } else if ([type isEqualToString:kNSURLTitlePboardType]) {
173 [pboard setString:SysUTF16ToNSString(dropData_->url_title)
174 forType:kNSURLTitlePboardType];
175
176 // File contents.
177 } else if ([type isEqualToString:NSFileContentsPboardType] ||
178 [type isEqualToString:NSCreateFileContentsPboardType(
179 SysUTF16ToNSString(dropData_->file_extension))]) {
180 // TODO(viettrungluu: find something which is known to accept
181 // NSFileContentsPboardType to check that this actually works!
182 scoped_nsobject<NSFileWrapper> file_wrapper(
183 [[NSFileWrapper alloc] initRegularFileWithContents:[NSData
184 dataWithBytes:dropData_->file_contents.data()
185 length:dropData_->file_contents.length()]]);
186 [file_wrapper setPreferredFilename:SysUTF8ToNSString(
187 GetFileNameFromDragData(*dropData_).value())];
188 [pboard writeFileWrapper:file_wrapper];
189
190 // TIFF.
191 } else if ([type isEqualToString:NSTIFFPboardType]) {
192 // TODO(viettrungluu): This is a bit odd since we rely on Cocoa to render
193 // our image into a TIFF. This is also suboptimal since this is all done
194 // synchronously. I'm not sure there's much we can easily do about it.
195 scoped_nsobject<NSImage> image(
196 [[NSImage alloc] initWithData:[NSData
197 dataWithBytes:dropData_->file_contents.data()
198 length:dropData_->file_contents.length()]]);
199 [pboard setData:[image TIFFRepresentation] forType:NSTIFFPboardType];
200
201 // Plain text.
202 } else if ([type isEqualToString:NSStringPboardType]) {
203 DCHECK(!dropData_->plain_text.empty());
204 [pboard setString:SysUTF16ToNSString(dropData_->plain_text)
205 forType:NSStringPboardType];
206
207 // Oops!
208 } else {
209 NOTREACHED() << "Asked for a drag pasteboard type we didn't offer.";
210 }
211 }
212
213 - (NSPoint)convertScreenPoint:(NSPoint)screenPoint {
214 DCHECK([contentsView_ window]);
215 NSPoint basePoint = [[contentsView_ window] convertScreenToBase:screenPoint];
216 return [contentsView_ convertPoint:basePoint fromView:nil];
217 }
218
219 - (void)startDrag {
220 NSEvent* currentEvent = [NSApp currentEvent];
221
222 // Synthesize an event for dragging, since we can't be sure that
223 // [NSApp currentEvent] will return a valid dragging event.
224 NSWindow* window = [contentsView_ window];
225 NSPoint position = [window mouseLocationOutsideOfEventStream];
226 NSTimeInterval eventTime = [currentEvent timestamp];
227 NSEvent* dragEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
228 location:position
229 modifierFlags:NSLeftMouseDraggedMask
230 timestamp:eventTime
231 windowNumber:[window windowNumber]
232 context:nil
233 eventNumber:0
234 clickCount:1
235 pressure:1.0];
236
237 if (dragImage_) {
238 position.x -= imageOffset_.x;
239 // Deal with Cocoa's flipped coordinate system.
240 position.y -= [dragImage_.get() size].height - imageOffset_.y;
241 }
242 // Per kwebster, offset arg is ignored, see -_web_DragImageForElement: in
243 // third_party/WebKit/Source/WebKit/mac/Misc/WebNSViewExtras.m.
244 [window dragImage:[self dragImage]
245 at:position
246 offset:NSZeroSize
247 event:dragEvent
248 pasteboard:pasteboard_
249 source:contentsView_
250 slideBack:YES];
251 }
252
253 - (void)endDragAt:(NSPoint)screenPoint
254 operation:(NSDragOperation)operation {
255 RenderViewHost* rvh = [contentsView_ tabContents]->render_view_host();
256 if (rvh) {
257 rvh->DragSourceSystemDragEnded();
258
259 // Convert |screenPoint| to view coordinates and flip it.
260 NSPoint localPoint = NSMakePoint(0, 0);
261 if ([contentsView_ window])
262 localPoint = [self convertScreenPoint:screenPoint];
263 NSRect viewFrame = [contentsView_ frame];
264 localPoint.y = viewFrame.size.height - localPoint.y;
265 // Flip |screenPoint|.
266 NSRect screenFrame = [[[contentsView_ window] screen] frame];
267 screenPoint.y = screenFrame.size.height - screenPoint.y;
268
269 rvh->DragSourceEndedAt(localPoint.x, localPoint.y,
270 screenPoint.x, screenPoint.y,
271 static_cast<WebKit::WebDragOperation>(operation));
272 }
273
274 // Make sure the pasteboard owner isn't us.
275 [pasteboard_ declareTypes:[NSArray array] owner:nil];
276 }
277
278 - (void)moveDragTo:(NSPoint)screenPoint {
279 RenderViewHost* rvh = [contentsView_ tabContents]->render_view_host();
280 if (rvh) {
281 // Convert |screenPoint| to view coordinates and flip it.
282 NSPoint localPoint = NSMakePoint(0, 0);
283 if ([contentsView_ window])
284 localPoint = [self convertScreenPoint:screenPoint];
285 NSRect viewFrame = [contentsView_ frame];
286 localPoint.y = viewFrame.size.height - localPoint.y;
287 // Flip |screenPoint|.
288 NSRect screenFrame = [[[contentsView_ window] screen] frame];
289 screenPoint.y = screenFrame.size.height - screenPoint.y;
290
291 rvh->DragSourceMovedTo(localPoint.x, localPoint.y,
292 screenPoint.x, screenPoint.y);
293 }
294 }
295
296 - (NSString*)dragPromisedFileTo:(NSString*)path {
297 // Be extra paranoid; avoid crashing.
298 if (!dropData_.get()) {
299 NOTREACHED() << "No drag-and-drop data available for promised file.";
300 return nil;
301 }
302
303 FilePath fileName = downloadFileName_.empty() ?
304 GetFileNameFromDragData(*dropData_) : downloadFileName_;
305 FilePath filePath(SysNSStringToUTF8(path));
306 filePath = filePath.Append(fileName);
307 FileStream* fileStream =
308 drag_download_util::CreateFileStreamForDrop(&filePath);
309 if (!fileStream)
310 return nil;
311
312 if (downloadURL_.is_valid()) {
313 TabContents* tabContents = [contentsView_ tabContents];
314 scoped_refptr<DragDownloadFile> dragFileDownloader(new DragDownloadFile(
315 filePath,
316 linked_ptr<net::FileStream>(fileStream),
317 downloadURL_,
318 tabContents->GetURL(),
319 tabContents->encoding(),
320 tabContents));
321
322 // The finalizer will take care of closing and deletion.
323 dragFileDownloader->Start(
324 new drag_download_util::PromiseFileFinalizer(dragFileDownloader));
325 } else {
326 // The writer will take care of closing and deletion.
327 g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
328 new PromiseWriterTask(*dropData_, fileStream));
329 }
330
331 // Once we've created the file, we should return the file name.
332 return SysUTF8ToNSString(filePath.BaseName().value());
333 }
334
335 @end // @implementation WebDragSource
336
337
338 @implementation WebDragSource (Private)
339
340 - (void)fillPasteboard {
341 DCHECK(pasteboard_.get());
342
343 [pasteboard_ declareTypes:[NSArray array] owner:contentsView_];
344
345 // HTML.
346 if (!dropData_->text_html.empty())
347 [pasteboard_ addTypes:[NSArray arrayWithObject:NSHTMLPboardType]
348 owner:contentsView_];
349
350 // URL (and title).
351 if (dropData_->url.is_valid())
352 [pasteboard_ addTypes:[NSArray arrayWithObjects:NSURLPboardType,
353 kNSURLTitlePboardType, nil]
354 owner:contentsView_];
355
356 // File.
357 if (!dropData_->file_contents.empty() ||
358 !dropData_->download_metadata.empty()) {
359 NSString* fileExtension = 0;
360
361 if (dropData_->download_metadata.empty()) {
362 // |dropData_->file_extension| comes with the '.', which we must strip.
363 fileExtension = (dropData_->file_extension.length() > 0) ?
364 SysUTF16ToNSString(dropData_->file_extension.substr(1)) : @"";
365 } else {
366 string16 mimeType;
367 FilePath fileName;
368 if (drag_download_util::ParseDownloadMetadata(
369 dropData_->download_metadata,
370 &mimeType,
371 &fileName,
372 &downloadURL_)) {
373 std::string contentDisposition =
374 "attachment; filename=" + fileName.value();
375 download_util::GenerateFileName(downloadURL_,
376 contentDisposition,
377 std::string(),
378 UTF16ToUTF8(mimeType),
379 &downloadFileName_);
380 fileExtension = SysUTF8ToNSString(downloadFileName_.Extension());
381 }
382 }
383
384 if (fileExtension) {
385 // File contents (with and without specific type), file (HFS) promise,
386 // TIFF.
387 // TODO(viettrungluu): others?
388 [pasteboard_ addTypes:[NSArray arrayWithObjects:
389 NSFileContentsPboardType,
390 NSCreateFileContentsPboardType(fileExtension),
391 NSFilesPromisePboardType,
392 NSTIFFPboardType,
393 nil]
394 owner:contentsView_];
395
396 // For the file promise, we need to specify the extension.
397 [pasteboard_ setPropertyList:[NSArray arrayWithObject:fileExtension]
398 forType:NSFilesPromisePboardType];
399 }
400 }
401
402 // Plain text.
403 if (!dropData_->plain_text.empty())
404 [pasteboard_ addTypes:[NSArray arrayWithObject:NSStringPboardType]
405 owner:contentsView_];
406 }
407
408 - (NSImage*)dragImage {
409 if (dragImage_)
410 return dragImage_;
411
412 // Default to returning a generic image.
413 return app::mac::GetCachedImageWithName(@"nav.pdf");
414 }
415
416 @end // @implementation WebDragSource (Private)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698