blob: 6a2122ee1ed73ad6319d7e473e8207a39ba505da [file] [log] [blame]
// Copyright (c) 2011 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.
#import "chrome/browser/ui/cocoa/drag_util.h"
#include <cmath>
#include "base/files/file_path.h"
#include "base/i18n/rtl.h"
#include "base/mac/scoped_nsobject.h"
#include "base/strings/sys_string_conversions.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/cocoa/l10n_util.h"
#include "content/public/browser/plugin_service.h"
#include "content/public/common/webplugininfo.h"
#include "ipc/ipc_message.h"
#include "net/base/filename_util.h"
#include "net/base/mime_util.h"
#include "third_party/WebKit/common/mime_util/mime_util.h"
#import "third_party/mozilla/NSPasteboard+Utils.h"
#import "ui/base/dragdrop/cocoa_dnd_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
#include "ui/resources/grit/ui_resources.h"
#include "url/gurl.h"
#include "url/origin.h"
#include "url/url_constants.h"
using content::PluginService;
namespace drag_util {
namespace {
BOOL IsSupportedFileURL(Profile* profile, const GURL& url) {
base::FilePath full_path;
net::FileURLToFilePath(url, &full_path);
std::string mime_type;
net::GetMimeTypeFromFile(full_path, &mime_type);
// This logic mirrors |BufferedResourceHandler::ShouldDownload()|.
// TODO(asvitkine): Refactor this out to a common location instead of
// duplicating code.
if (blink::IsSupportedMimeType(mime_type))
return YES;
// Check whether there is a plugin that supports the mime type. (e.g. PDF)
// TODO(bauerb): This possibly uses stale information, but it's guaranteed not
// to do disk access.
bool allow_wildcard = false;
content::WebPluginInfo plugin;
return PluginService::GetInstance()->GetPluginInfo(
-1, // process ID
MSG_ROUTING_NONE, // routing ID
profile->GetResourceContext(), url, url::Origin(), mime_type,
allow_wildcard, NULL, &plugin, NULL);
}
// Draws string |title| within box |frame|, positioning it at the origin.
// Truncates text with fading if it is too long to fit horizontally.
// Based on code from GradientButtonCell but simplified where possible.
void DrawTruncatedTitle(NSAttributedString* title,
NSRect frame,
bool is_title_rtl) {
NSSize size = [title size];
if (std::floor(size.width) <= NSWidth(frame)) {
[title drawAtPoint:frame.origin];
return;
}
// The gradient is about twice the line height long.
NSRectEdge gradient_edge;
if (is_title_rtl)
gradient_edge = NSMinXEdge;
else
gradient_edge = NSMaxXEdge;
CGFloat gradient_width = std::min(size.height * 2, NSWidth(frame) / 4);
NSRect solid_part, gradient_part;
NSDivideRect(frame, &gradient_part, &solid_part, gradient_width,
gradient_edge);
CGContextRef context = static_cast<CGContextRef>(
[[NSGraphicsContext currentContext] graphicsPort]);
CGContextBeginTransparencyLayerWithRect(context, NSRectToCGRect(frame), 0);
{ // Draw text clipped to frame.
gfx::ScopedNSGraphicsContextSaveGState scoped_state;
[NSBezierPath clipRect:frame];
[title drawInRect:frame];
}
NSColor* color = [NSColor blackColor];
NSColor* alpha_color = [color colorWithAlphaComponent:0.0];
base::scoped_nsobject<NSGradient> mask(
[[NSGradient alloc] initWithStartingColor:color endingColor:alpha_color]);
// Draw the gradient mask.
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGFloat gradient_x_start, gradient_x_end;
if (is_title_rtl) {
gradient_x_start = NSMaxX(gradient_part);
gradient_x_end = NSMinX(gradient_part);
} else {
gradient_x_start = NSMinX(gradient_part);
gradient_x_end = NSMaxX(gradient_part);
}
[mask drawFromPoint:NSMakePoint(gradient_x_start, NSMinY(frame))
toPoint:NSMakePoint(gradient_x_end, NSMinY(frame))
options:NSGradientDrawsBeforeStartingLocation];
CGContextEndTransparencyLayer(context);
}
} // namespace
GURL GetFileURLFromDropData(id<NSDraggingInfo> info) {
if ([[info draggingPasteboard] containsURLDataConvertingTextToURL:YES]) {
GURL url;
ui::PopulateURLAndTitleFromPasteboard(&url,
NULL,
[info draggingPasteboard],
YES);
if (url.SchemeIs(url::kFileScheme))
return url;
}
return GURL();
}
BOOL IsUnsupportedDropData(Profile* profile, id<NSDraggingInfo> info) {
GURL url = GetFileURLFromDropData(info);
if (!url.is_empty()) {
// If dragging a file, only allow dropping supported file types (that the
// web view can display).
return !IsSupportedFileURL(profile, url);
}
return NO;
}
NSImage* DragImageForBookmark(NSImage* favicon,
const base::string16& title,
CGFloat drag_image_width) {
// If no favicon, use a default.
if (!favicon) {
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
favicon = rb.GetNativeImageNamed(IDR_DEFAULT_FAVICON).ToNSImage();
}
// If no title, just use icon.
if (title.empty())
return favicon;
NSString* ns_title = base::SysUTF16ToNSString(title);
// Set the look of the title.
base::scoped_nsobject<NSMutableParagraphStyle> paragraph_style(
[[NSMutableParagraphStyle alloc] init]);
[paragraph_style setLineBreakMode:NSLineBreakByClipping];
NSDictionary* attrs = @{
NSFontAttributeName :
[NSFont systemFontOfSize:[NSFont smallSystemFontSize]],
NSParagraphStyleAttributeName : paragraph_style
};
base::scoped_nsobject<NSAttributedString> rich_title(
[[NSAttributedString alloc] initWithString:ns_title attributes:attrs]);
// Set up sizes and locations for rendering.
const CGFloat kIconPadding = 2.0; // Gap between icon and text.
NSRect favicon_rect = {NSZeroPoint, [favicon size]};
CGFloat icon_plus_padding_width = NSWidth(favicon_rect) + kIconPadding;
CGFloat full_text_width = [rich_title size].width;
CGFloat allowed_text_width = drag_image_width - icon_plus_padding_width;
CGFloat used_text_width = std::min(full_text_width, allowed_text_width);
NSRect full_drag_image_rect = NSMakeRect(
0, 0, icon_plus_padding_width + used_text_width, NSHeight(favicon_rect));
NSRectEdge icon_edge;
if (cocoa_l10n_util::ShouldDoExperimentalRTLLayout())
icon_edge = NSMaxXEdge;
else
icon_edge = NSMinXEdge;
NSRect icon_rect;
NSRect text_plus_padding_rect;
NSRect padding_rect;
NSRect text_rect;
// Slice off the icon.
NSDivideRect(full_drag_image_rect, &icon_rect, &text_plus_padding_rect,
NSWidth(favicon_rect), icon_edge);
// Slice off the padding.
NSDivideRect(text_plus_padding_rect, &padding_rect, &text_rect, kIconPadding,
icon_edge);
// Render the drag image.
NSImage* drag_image =
[[[NSImage alloc] initWithSize:full_drag_image_rect.size] autorelease];
[drag_image lockFocus];
[favicon drawAtPoint:icon_rect.origin
fromRect:NSZeroRect
operation:NSCompositeSourceOver
fraction:0.7];
bool is_title_rtl = base::i18n::GetFirstStrongCharacterDirection(title) ==
base::i18n::RIGHT_TO_LEFT;
DrawTruncatedTitle(rich_title, text_rect, is_title_rtl);
[drag_image unlockFocus];
return drag_image;
}
} // namespace drag_util