Elim cr-checkbox
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / location_bar / autocomplete_text_field_cell.mm
blobfc6fe0e81304c1c1ef2e318b5d60266b1333f0ec
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.
5 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_cell.h"
7 #include "base/logging.h"
8 #include "base/mac/foundation_util.h"
9 #include "base/mac/mac_logging.h"
10 #include "chrome/browser/search/search.h"
11 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.h"
12 #import "chrome/browser/ui/cocoa/location_bar/location_bar_decoration.h"
13 #import "extensions/common/feature_switch.h"
14 #include "grit/theme_resources.h"
15 #import "third_party/mozilla/NSPasteboard+Utils.h"
16 #import "ui/base/cocoa/appkit_utils.h"
17 #import "ui/base/cocoa/nsview_additions.h"
18 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
20 using extensions::FeatureSwitch;
22 namespace {
24 // Matches the clipping radius of |GradientButtonCell|.
25 const CGFloat kCornerRadius = 3.0;
27 // How far to inset the left- and right-hand decorations from the field's
28 // bounds.
29 const CGFloat kLeftDecorationXOffset = 5.0;
30 const CGFloat kRightDecorationXOffset = 5.0;
32 // The amount of padding on either side reserved for drawing
33 // decorations.  [Views has |kItemPadding| == 3.]
34 const CGFloat kDecorationHorizontalPad = 3.0;
36 const ui::NinePartImageIds kPopupBorderImageIds =
37     IMAGE_GRID(IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW);
39 const ui::NinePartImageIds kNormalBorderImageIds = IMAGE_GRID(IDR_TEXTFIELD);
41 // How long to wait for mouse-up on the location icon before assuming
42 // that the user wants to drag.
43 const NSTimeInterval kLocationIconDragTimeout = 0.25;
45 // Calculate the positions for a set of decorations.  |frame| is the
46 // overall frame to do layout in, |remaining_frame| will get the
47 // left-over space.  |all_decorations| is the set of decorations to
48 // lay out, |decorations| will be set to the decorations which are
49 // visible and which fit, in the same order as |all_decorations|,
50 // while |decoration_frames| will be the corresponding frames.
51 // |x_edge| describes the edge to layout the decorations against
52 // (|NSMinXEdge| or |NSMaxXEdge|).  |regular_padding| is the padding
53 // from the edge of |cell_frame| to use when the first visible decoration
54 // is a regular decoration.
55 void CalculatePositionsHelper(
56     NSRect frame,
57     const std::vector<LocationBarDecoration*>& all_decorations,
58     NSRectEdge x_edge,
59     CGFloat regular_padding,
60     std::vector<LocationBarDecoration*>* decorations,
61     std::vector<NSRect>* decoration_frames,
62     NSRect* remaining_frame) {
63   DCHECK(x_edge == NSMinXEdge || x_edge == NSMaxXEdge);
64   DCHECK_EQ(decorations->size(), decoration_frames->size());
66   // The initial padding depends on whether the first visible decoration is
67   // a button or not.
68   bool is_first_visible_decoration = true;
70   for (size_t i = 0; i < all_decorations.size(); ++i) {
71     if (all_decorations[i]->IsVisible()) {
72       CGFloat padding = kDecorationHorizontalPad;
73       if (is_first_visible_decoration) {
74         padding = regular_padding;
75         is_first_visible_decoration = false;
76       }
78       NSRect padding_rect, available;
80       // Peel off the outside padding.
81       NSDivideRect(frame, &padding_rect, &available, padding, x_edge);
83       // Find out how large the decoration will be in the remaining
84       // space.
85       const CGFloat used_width =
86           all_decorations[i]->GetWidthForSpace(NSWidth(available));
88       if (used_width != LocationBarDecoration::kOmittedWidth) {
89         DCHECK_GT(used_width, 0.0);
90         NSRect decoration_frame;
92         // Peel off the desired width, leaving the remainder in
93         // |frame|.
94         NSDivideRect(available, &decoration_frame, &frame,
95                      used_width, x_edge);
97         decorations->push_back(all_decorations[i]);
98         decoration_frames->push_back(decoration_frame);
99         DCHECK_EQ(decorations->size(), decoration_frames->size());
101         // Adjust padding for between decorations.
102         padding = kDecorationHorizontalPad;
103       }
104     }
105   }
107   DCHECK_EQ(decorations->size(), decoration_frames->size());
108   *remaining_frame = frame;
111 // Helper function for calculating placement of decorations w/in the cell.
112 // |frame| is the cell's boundary rectangle, |remaining_frame| will get any
113 // space left after decorations are laid out (for text).  |left_decorations| is
114 // a set of decorations for the left-hand side of the cell, |right_decorations|
115 // for the right-hand side.
116 // |decorations| will contain the resulting visible decorations, and
117 // |decoration_frames| will contain their frames in the same coordinates as
118 // |frame|.  Decorations will be ordered left to right. As a convenience returns
119 // the index of the first right-hand decoration.
120 size_t CalculatePositionsInFrame(
121     NSRect frame,
122     const std::vector<LocationBarDecoration*>& left_decorations,
123     const std::vector<LocationBarDecoration*>& right_decorations,
124     std::vector<LocationBarDecoration*>* decorations,
125     std::vector<NSRect>* decoration_frames,
126     NSRect* remaining_frame) {
127   decorations->clear();
128   decoration_frames->clear();
130   // Layout |left_decorations| against the LHS.
131   CalculatePositionsHelper(frame, left_decorations, NSMinXEdge,
132                            kLeftDecorationXOffset, decorations,
133                            decoration_frames, &frame);
134   DCHECK_EQ(decorations->size(), decoration_frames->size());
136   // Capture the number of visible left-hand decorations.
137   const size_t left_count = decorations->size();
139   // Layout |right_decorations| against the RHS.
140   CalculatePositionsHelper(frame, right_decorations, NSMaxXEdge,
141                            kRightDecorationXOffset, decorations,
142                            decoration_frames, &frame);
143   DCHECK_EQ(decorations->size(), decoration_frames->size());
145   // Reverse the right-hand decorations so that overall everything is
146   // sorted left to right.
147   std::reverse(decorations->begin() + left_count, decorations->end());
148   std::reverse(decoration_frames->begin() + left_count,
149                decoration_frames->end());
151   *remaining_frame = frame;
152   return left_count;
155 }  // namespace
157 @implementation AutocompleteTextFieldCell
159 @synthesize isPopupMode = isPopupMode_;
161 - (CGFloat)topTextFrameOffset {
162   return 3.0;
165 - (CGFloat)bottomTextFrameOffset {
166   return 3.0;
169 - (CGFloat)cornerRadius {
170   return kCornerRadius;
173 - (BOOL)shouldDrawBezel {
174   return YES;
177 - (CGFloat)lineHeight {
178   return 17;
181 - (void)clearDecorations {
182   leftDecorations_.clear();
183   rightDecorations_.clear();
186 - (void)addLeftDecoration:(LocationBarDecoration*)decoration {
187   leftDecorations_.push_back(decoration);
190 - (void)addRightDecoration:(LocationBarDecoration*)decoration {
191   rightDecorations_.push_back(decoration);
194 - (CGFloat)availableWidthInFrame:(const NSRect)frame {
195   std::vector<LocationBarDecoration*> decorations;
196   std::vector<NSRect> decorationFrames;
197   NSRect textFrame;
198   CalculatePositionsInFrame(frame, leftDecorations_, rightDecorations_,
199                             &decorations, &decorationFrames, &textFrame);
201   return NSWidth(textFrame);
204 - (NSRect)frameForDecoration:(const LocationBarDecoration*)aDecoration
205                      inFrame:(NSRect)cellFrame {
206   // Short-circuit if the decoration is known to be not visible.
207   if (aDecoration && !aDecoration->IsVisible())
208     return NSZeroRect;
210   // Layout the decorations.
211   std::vector<LocationBarDecoration*> decorations;
212   std::vector<NSRect> decorationFrames;
213   NSRect textFrame;
214   CalculatePositionsInFrame(cellFrame, leftDecorations_, rightDecorations_,
215                             &decorations, &decorationFrames, &textFrame);
217   // Find our decoration and return the corresponding frame.
218   std::vector<LocationBarDecoration*>::const_iterator iter =
219       std::find(decorations.begin(), decorations.end(), aDecoration);
220   if (iter != decorations.end()) {
221     const size_t index = iter - decorations.begin();
222     return decorationFrames[index];
223   }
225   // Decorations which are not visible should have been filtered out
226   // at the top, but return |NSZeroRect| rather than a 0-width rect
227   // for consistency.
228   NOTREACHED();
229   return NSZeroRect;
232 // Overriden to account for the decorations.
233 - (NSRect)textFrameForFrame:(NSRect)cellFrame {
234   // Get the frame adjusted for decorations.
235   std::vector<LocationBarDecoration*> decorations;
236   std::vector<NSRect> decorationFrames;
237   NSRect textFrame = [super textFrameForFrame:cellFrame];
238   CalculatePositionsInFrame(textFrame, leftDecorations_, rightDecorations_,
239                             &decorations, &decorationFrames, &textFrame);
241   // NOTE: This function must closely match the logic in
242   // |-drawInteriorWithFrame:inView:|.
244   return textFrame;
247 - (NSRect)textCursorFrameForFrame:(NSRect)cellFrame {
248   std::vector<LocationBarDecoration*> decorations;
249   std::vector<NSRect> decorationFrames;
250   NSRect textFrame;
251   size_t left_count =
252       CalculatePositionsInFrame(cellFrame, leftDecorations_, rightDecorations_,
253                                 &decorations, &decorationFrames, &textFrame);
255   // Determine the left-most extent for the i-beam cursor.
256   CGFloat minX = NSMinX(textFrame);
257   for (size_t index = left_count; index--; ) {
258     if (decorations[index]->AcceptsMousePress())
259       break;
261     // If at leftmost decoration, expand to edge of cell.
262     if (!index) {
263       minX = NSMinX(cellFrame);
264     } else {
265       minX = NSMinX(decorationFrames[index]) - kDecorationHorizontalPad;
266     }
267   }
269   // Determine the right-most extent for the i-beam cursor.
270   CGFloat maxX = NSMaxX(textFrame);
271   for (size_t index = left_count; index < decorations.size(); ++index) {
272     if (decorations[index]->AcceptsMousePress())
273       break;
275     // If at rightmost decoration, expand to edge of cell.
276     if (index == decorations.size() - 1) {
277       maxX = NSMaxX(cellFrame);
278     } else {
279       maxX = NSMaxX(decorationFrames[index]) + kDecorationHorizontalPad;
280     }
281   }
283   // I-beam cursor covers left-most to right-most.
284   return NSMakeRect(minX, NSMinY(textFrame), maxX - minX, NSHeight(textFrame));
287 - (void)drawWithFrame:(NSRect)frame inView:(NSView*)controlView {
288   // Background color.
289   const CGFloat lineWidth = [controlView cr_lineWidth];
290   if (isPopupMode_) {
291     [[self backgroundColor] set];
292     NSRectFillUsingOperation(NSInsetRect(frame, 1, 1), NSCompositeSourceOver);
293   } else {
294     CGFloat insetSize = lineWidth == 0.5 ? 1.5 : 2.0;
295     NSRect fillRect = NSInsetRect(frame, insetSize, insetSize);
296     [[self backgroundColor] set];
297     [[NSBezierPath bezierPathWithRoundedRect:fillRect
298                                      xRadius:kCornerRadius
299                                      yRadius:kCornerRadius] fill];
300   }
302   // Border.
303   ui::DrawNinePartImage(frame,
304                         isPopupMode_ ? kPopupBorderImageIds
305                                      : kNormalBorderImageIds,
306                         NSCompositeSourceOver,
307                         1.0,
308                         true);
310   // Interior contents. Drawn after the border as some of the interior controls
311   // draw over the border.
312   [self drawInteriorWithFrame:frame inView:controlView];
314   // Focus ring.
315   if ([self showsFirstResponder]) {
316     NSRect focusRingRect = NSInsetRect(frame, lineWidth, lineWidth);
317     [[[NSColor keyboardFocusIndicatorColor]
318         colorWithAlphaComponent:0.5 / lineWidth] set];
319     NSBezierPath* path = [NSBezierPath bezierPathWithRoundedRect:focusRingRect
320                                                          xRadius:kCornerRadius
321                                                          yRadius:kCornerRadius];
322     [path setLineWidth:lineWidth * 2.0];
323     [path stroke];
324   }
327 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
328   std::vector<LocationBarDecoration*> decorations;
329   std::vector<NSRect> decorationFrames;
330   NSRect workingFrame;
332   CalculatePositionsInFrame(cellFrame, leftDecorations_, rightDecorations_,
333                             &decorations, &decorationFrames, &workingFrame);
335   // Draw the decorations.
336   for (size_t i = 0; i < decorations.size(); ++i) {
337     if (decorations[i]) {
338       NSRect background_frame = NSInsetRect(
339           decorationFrames[i], -(kDecorationHorizontalPad + 1) / 2, 2);
340       decorations[i]->DrawWithBackgroundInFrame(
341           background_frame, decorationFrames[i], controlView);
342     }
343   }
345   // NOTE: This function must closely match the logic in
346   // |-textFrameForFrame:|.
348   // Superclass draws text portion WRT original |cellFrame|.
349   // Even though -isOpaque is NO due to rounded corners, we know that the text
350   // will be drawn on top of an opaque area, therefore it is safe to enable
351   // font smoothing.
352   {
353     gfx::ScopedNSGraphicsContextSaveGState scopedGState;
354     NSGraphicsContext* context = [NSGraphicsContext currentContext];
355     CGContextRef cgContext = static_cast<CGContextRef>([context graphicsPort]);
356     CGContextSetShouldSmoothFonts(cgContext, true);
357     [super drawInteriorWithFrame:cellFrame inView:controlView];
358   }
361 - (LocationBarDecoration*)decorationForEvent:(NSEvent*)theEvent
362                                       inRect:(NSRect)cellFrame
363                                       ofView:(AutocompleteTextField*)controlView
365   const BOOL flipped = [controlView isFlipped];
366   const NSPoint location =
367       [controlView convertPoint:[theEvent locationInWindow] fromView:nil];
369   std::vector<LocationBarDecoration*> decorations;
370   std::vector<NSRect> decorationFrames;
371   NSRect textFrame;
372   CalculatePositionsInFrame(cellFrame, leftDecorations_, rightDecorations_,
373                             &decorations, &decorationFrames, &textFrame);
375   for (size_t i = 0; i < decorations.size(); ++i) {
376     if (NSMouseInRect(location, decorationFrames[i], flipped))
377       return decorations[i];
378   }
380   return NULL;
383 - (NSMenu*)decorationMenuForEvent:(NSEvent*)theEvent
384                            inRect:(NSRect)cellFrame
385                            ofView:(AutocompleteTextField*)controlView {
386   LocationBarDecoration* decoration =
387       [self decorationForEvent:theEvent inRect:cellFrame ofView:controlView];
388   if (decoration)
389     return decoration->GetMenu();
390   return nil;
393 - (BOOL)mouseDown:(NSEvent*)theEvent
394            inRect:(NSRect)cellFrame
395            ofView:(AutocompleteTextField*)controlView {
396   // TODO(groby): Factor this into three pieces - find target for event, handle
397   // delayed focus (for any and all events), execute mouseDown for target.
399   // Check if this mouseDown was the reason the control became firstResponder.
400   // If not, discard focus event.
401   base::scoped_nsobject<NSEvent> focusEvent(focusEvent_.release());
402   if (![theEvent isEqual:focusEvent])
403     focusEvent.reset();
405   LocationBarDecoration* decoration =
406       [self decorationForEvent:theEvent inRect:cellFrame ofView:controlView];
407   if (!decoration || !decoration->AcceptsMousePress())
408     return NO;
410   NSRect decorationRect =
411       [self frameForDecoration:decoration inFrame:cellFrame];
413   // If the decoration is draggable, then initiate a drag if the user
414   // drags or holds the mouse down for awhile.
415   if (decoration->IsDraggable()) {
416     NSDate* timeout =
417         [NSDate dateWithTimeIntervalSinceNow:kLocationIconDragTimeout];
418     NSEvent* event = [NSApp nextEventMatchingMask:(NSLeftMouseDraggedMask |
419                                                    NSLeftMouseUpMask)
420                                         untilDate:timeout
421                                            inMode:NSEventTrackingRunLoopMode
422                                           dequeue:YES];
423     if (!event || [event type] == NSLeftMouseDragged) {
424       NSPasteboard* pboard = decoration->GetDragPasteboard();
425       DCHECK(pboard);
427       NSImage* image = decoration->GetDragImage();
428       DCHECK(image);
430       NSRect dragImageRect = decoration->GetDragImageFrame(decorationRect);
432       // Center under mouse horizontally, with cursor below so the image
433       // can be seen.
434       const NSPoint mousePoint =
435           [controlView convertPoint:[theEvent locationInWindow] fromView:nil];
436       dragImageRect.origin =
437           NSMakePoint(mousePoint.x - NSWidth(dragImageRect) / 2.0,
438                       mousePoint.y - NSHeight(dragImageRect));
440       // -[NSView dragImage:at:*] wants the images lower-left point,
441       // regardless of -isFlipped.  Converting the rect to window base
442       // coordinates doesn't require any special-casing.  Note that
443       // -[NSView dragFile:fromRect:*] takes a rect rather than a
444       // point, likely for this exact reason.
445       const NSPoint dragPoint =
446           [controlView convertRect:dragImageRect toView:nil].origin;
447       [[controlView window] dragImage:image
448                                    at:dragPoint
449                                offset:NSZeroSize
450                                 event:theEvent
451                            pasteboard:pboard
452                                source:self
453                             slideBack:YES];
455       return YES;
456     }
458     // On mouse-up fall through to mouse-pressed case.
459     DCHECK_EQ([event type], NSLeftMouseUp);
460   }
462   const NSPoint mouseLocation = [theEvent locationInWindow];
463   const NSPoint point = [controlView convertPoint:mouseLocation fromView:nil];
464   return decoration->OnMousePressed(
465       decorationRect, NSMakePoint(point.x - decorationRect.origin.x,
466                                   point.y - decorationRect.origin.y));
469 // Given a newly created .webloc plist url file, also give it a resource
470 // fork and insert 'TEXT and 'url ' resources holding further copies of the
471 // url data. This is required for apps such as Terminal and Safari to accept it
472 // as a real webloc file when dragged in.
473 // It's expected that the resource fork requirement will go away at some
474 // point and this code can then be deleted.
475 OSErr WriteURLToNewWebLocFileResourceFork(NSURL* file, NSString* urlStr) {
476   ResFileRefNum refNum = kResFileNotOpened;
477   ResFileRefNum prevResRef = CurResFile();
478   FSRef fsRef;
479   OSErr err = noErr;
480   HFSUniStr255 resourceForkName;
481   FSGetResourceForkName(&resourceForkName);
483   if (![[NSFileManager defaultManager] fileExistsAtPath:[file path]])
484     return fnfErr;
486   if (!CFURLGetFSRef((CFURLRef)file, &fsRef))
487     return fnfErr;
489   err = FSCreateResourceFork(&fsRef,
490                              resourceForkName.length,
491                              resourceForkName.unicode,
492                              0);
493   if (err)
494     return err;
495   err = FSOpenResourceFile(&fsRef,
496                            resourceForkName.length,
497                            resourceForkName.unicode,
498                            fsRdWrPerm, &refNum);
499   if (err)
500     return err;
502   const char* utf8URL = [urlStr UTF8String];
503   int urlChars = strlen(utf8URL);
505   Handle urlHandle = NewHandle(urlChars);
506   memcpy(*urlHandle, utf8URL, urlChars);
508   Handle textHandle = NewHandle(urlChars);
509   memcpy(*textHandle, utf8URL, urlChars);
511   // Data for the 'drag' resource.
512   // This comes from derezzing webloc files made by the Finder.
513   // It's bigendian data, so it's represented here as chars to preserve
514   // byte order.
515   char dragData[] = {
516     0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, // Header.
517     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
518     0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x01, 0x00, // 'TEXT', 0, 256
519     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
520     0x75, 0x72, 0x6C, 0x20, 0x00, 0x00, 0x01, 0x00, // 'url ', 0, 256
521     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
522   };
523   Handle dragHandle = NewHandleClear(sizeof(dragData));
524   memcpy(*dragHandle, &dragData[0], sizeof(dragData));
526   // Save the resources to the file.
527   ConstStr255Param noName = {0};
528   AddResource(dragHandle, 'drag', 128, noName);
529   AddResource(textHandle, 'TEXT', 256, noName);
530   AddResource(urlHandle, 'url ', 256, noName);
532   CloseResFile(refNum);
533   UseResFile(prevResRef);
534   return noErr;
537 // Returns the file path for file |name| if saved at NSURL |base|.
538 static NSString* PathWithBaseURLAndName(NSURL* base, NSString* name) {
539   NSString* filteredName =
540       [name stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
541   return [[NSURL URLWithString:filteredName relativeToURL:base] path];
544 // Returns if there is already a file |name| at dir NSURL |base|.
545 static BOOL FileAlreadyExists(NSURL* base, NSString* name) {
546   NSString* path = PathWithBaseURLAndName(base, name);
547   DCHECK([path hasSuffix:name]);
548   return [[NSFileManager defaultManager] fileExistsAtPath:path];
551 // Takes a destination URL, a suggested file name, & an extension (eg .webloc).
552 // Returns the complete file name with extension you should use.
553 // The name returned will not contain /, : or ?, will not start with a dot,
554 // will not be longer than kMaxNameLength + length of extension, and will not
555 // be a file name that already exists in that directory. If necessary it will
556 // try appending a space and a number to the name (but before the extension)
557 // trying numbers up to and including kMaxIndex.
558 // If the function gives up it returns nil.
559 static NSString* UnusedLegalNameForNewDropFile(NSURL* saveLocation,
560                                                NSString *fileName,
561                                                NSString *extension) {
562   int number = 1;
563   const int kMaxIndex = 20;
564   const unsigned kMaxNameLength = 64; // Arbitrary.
566   NSString* filteredName = [fileName stringByReplacingOccurrencesOfString:@"/"
567                                                                withString:@"-"];
568   filteredName = [filteredName stringByReplacingOccurrencesOfString:@":"
569                                                          withString:@"-"];
570   filteredName = [filteredName stringByReplacingOccurrencesOfString:@"?"
571                                                          withString:@"-"];
572   filteredName =
573       [filteredName stringByReplacingOccurrencesOfString:@"."
574                                               withString:@"-"
575                                                  options:0
576                                                    range:NSMakeRange(0,1)];
578   if ([filteredName length] > kMaxNameLength)
579     filteredName = [filteredName substringToIndex:kMaxNameLength];
581   NSString* candidateName = [filteredName stringByAppendingString:extension];
583   while (FileAlreadyExists(saveLocation, candidateName)) {
584     if (number > kMaxIndex)
585       return nil;
586     else
587       candidateName = [filteredName stringByAppendingFormat:@" %d%@",
588                        number++, extension];
589   }
591   return candidateName;
594 - (NSArray*)namesOfPromisedFilesDroppedAtDestination:(NSURL*)dropDestination {
595   NSPasteboard* pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
596   NSFileManager* fileManager = [NSFileManager defaultManager];
598   if (![pboard containsURLData])
599     return NULL;
601   NSArray *urls = NULL;
602   NSArray* titles = NULL;
603   [pboard getURLs:&urls andTitles:&titles convertingFilenames:YES];
605   NSString* urlStr = [urls objectAtIndex:0];
606   NSString* nameStr = [titles objectAtIndex:0];
608   NSString* nameWithExtensionStr =
609       UnusedLegalNameForNewDropFile(dropDestination, nameStr, @".webloc");
610   if (!nameWithExtensionStr)
611     return NULL;
613   NSDictionary* urlDict = [NSDictionary dictionaryWithObject:urlStr
614                                                       forKey:@"URL"];
615   NSURL* outputURL =
616       [NSURL fileURLWithPath:PathWithBaseURLAndName(dropDestination,
617                                                     nameWithExtensionStr)];
618   [urlDict writeToURL:outputURL
619            atomically:NO];
621   if (![fileManager fileExistsAtPath:[outputURL path]])
622     return NULL;
624   NSDictionary* attr = [NSDictionary dictionaryWithObjectsAndKeys:
625       [NSNumber numberWithBool:YES], NSFileExtensionHidden,
626       [NSNumber numberWithUnsignedLong:'ilht'], NSFileHFSTypeCode,
627       [NSNumber numberWithUnsignedLong:'MACS'], NSFileHFSCreatorCode,
628       nil];
629   [fileManager setAttributes:attr
630                 ofItemAtPath:[outputURL path]
631                        error:nil];
632   // Add resource data.
633   OSErr resStatus = WriteURLToNewWebLocFileResourceFork(outputURL, urlStr);
634   OSSTATUS_DCHECK(resStatus == noErr, resStatus);
636   return [NSArray arrayWithObject:nameWithExtensionStr];
639 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal {
640   return NSDragOperationCopy;
643 - (void)updateToolTipsInRect:(NSRect)cellFrame
644                       ofView:(AutocompleteTextField*)controlView {
645   std::vector<LocationBarDecoration*> decorations;
646   std::vector<NSRect> decorationFrames;
647   NSRect textFrame;
648   CalculatePositionsInFrame(cellFrame, leftDecorations_, rightDecorations_,
649                             &decorations, &decorationFrames, &textFrame);
651   for (size_t i = 0; i < decorations.size(); ++i) {
652     NSString* tooltip = decorations[i]->GetToolTip();
653     if ([tooltip length] > 0)
654       [controlView addToolTip:tooltip forRect:decorationFrames[i]];
655   }
658 - (BOOL)hideFocusState {
659   return hideFocusState_;
662 - (void)setHideFocusState:(BOOL)hideFocusState
663                    ofView:(AutocompleteTextField*)controlView {
664   if (hideFocusState_ == hideFocusState)
665     return;
666   hideFocusState_ = hideFocusState;
667   [controlView setNeedsDisplay:YES];
668   NSTextView* fieldEditor =
669       base::mac::ObjCCastStrict<NSTextView>([controlView currentEditor]);
670   [fieldEditor updateInsertionPointStateAndRestartTimer:YES];
673 - (BOOL)showsFirstResponder {
674   return [super showsFirstResponder] && !hideFocusState_;
677 - (void)handleFocusEvent:(NSEvent*)event
678                   ofView:(AutocompleteTextField*)controlView {
679   if ([controlView observer]) {
680     const bool controlDown = ([event modifierFlags] & NSControlKeyMask) != 0;
681     [controlView observer]->OnSetFocus(controlDown);
682   }
685 @end