<commit_before>// 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/tab_contents/web_drag_source.h"

#include <sys/param.h>

#include "base/file_path.h"
#include "base/mac/crash_logging.h"
#include "base/string_util.h"
#include "base/sys_string_conversions.h"
#include "base/task.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h"
#include "base/utf_string_conversions.h"
#import "chrome/app/breakpad_mac.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/download_util.h"
#include "chrome/browser/tab_contents/tab_contents_view_mac.h"
#include "content/browser/download/download_manager.h"
#include "content/browser/download/drag_download_file.h"
#include "content/browser/download/drag_download_util.h"
#include "content/browser/renderer_host/render_view_host.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "content/common/url_constants.h"
#include "net/base/file_stream.h"
#include "net/base/net_util.h"
#import "third_party/mozilla/NSPasteboard+Utils.h"
#include "ui/gfx/mac/nsimage_cache.h"
#include "webkit/glue/webdropdata.h"

using base::SysNSStringToUTF8;
using base::SysUTF8ToNSString;
using base::SysUTF16ToNSString;
using net::FileStream;


namespace {

// An unofficial standard pasteboard title type to be provided alongside the
// |NSURLPboardType|.
NSString* const kNSURLTitlePboardType = @"public.url-name";

// Converts a string16 into a FilePath. Use this method instead of
// -[NSString fileSystemRepresentation] to prevent exceptions from being thrown.
// See http://crbug.com/78782 for more info.
FilePath FilePathFromFilename(const string16& filename) {
  NSString* str = SysUTF16ToNSString(filename);
  char buf[MAXPATHLEN];
  if (![str getFileSystemRepresentation:buf maxLength:sizeof(buf)])
    return FilePath();
  return FilePath(buf);
}

// Returns a filename appropriate for the drop data
// TODO(viettrungluu): Refactor to make it common across platforms,
// and move it somewhere sensible.
FilePath GetFileNameFromDragData(const WebDropData& drop_data) {
  // Set a breakpad key for the scope of this function to help debug
  // http://crbug.com/78782
  static NSString* const kUrlKey = @"drop_data_url";
  NSString* value = SysUTF8ToNSString(drop_data.url.spec());
  base::mac::ScopedCrashKey key(kUrlKey, value);

  // Images without ALT text will only have a file extension so we need to
  // synthesize one from the provided extension and URL.
  FilePath file_name(FilePathFromFilename(drop_data.file_description_filename));
  file_name = file_name.BaseName().RemoveExtension();

  if (file_name.empty()) {
    // Retrieve the name from the URL.
    string16 suggested_filename =
        net::GetSuggestedFilename(drop_data.url, "", "", "", "", string16());
    file_name = FilePathFromFilename(suggested_filename);
  }

  file_name = file_name.ReplaceExtension(UTF16ToUTF8(drop_data.file_extension));

  return file_name;
}

// This class's sole task is to write out data for a promised file; the caller
// is responsible for opening the file.
class PromiseWriterTask : public Task {
 public:
  // Assumes ownership of file_stream.
  PromiseWriterTask(const WebDropData& drop_data,
                    FileStream* file_stream);
  virtual ~PromiseWriterTask();
  virtual void Run();

 private:
  WebDropData drop_data_;

  // This class takes ownership of file_stream_ and will close and delete it.
  scoped_ptr<FileStream> file_stream_;
};

// Takes the drop data and an open file stream (which it takes ownership of and
// will close and delete).
PromiseWriterTask::PromiseWriterTask(const WebDropData& drop_data,
                                     FileStream* file_stream) :
    drop_data_(drop_data) {
  file_stream_.reset(file_stream);
  DCHECK(file_stream_.get());
}

PromiseWriterTask::~PromiseWriterTask() {
  DCHECK(file_stream_.get());
  if (file_stream_.get())
    file_stream_->Close();
}

void PromiseWriterTask::Run() {
  CHECK(file_stream_.get());
  file_stream_->Write(drop_data_.file_contents.data(),
                      drop_data_.file_contents.length(),
                      NULL);

  // Let our destructor take care of business.
}

}  // namespace


@interface WebDragSource(Private)

- (void)fillPasteboard;
- (NSImage*)dragImage;

@end  // @interface WebDragSource(Private)


@implementation WebDragSource

- (id)initWithContentsView:(TabContentsViewCocoa*)contentsView
                  dropData:(const WebDropData*)dropData
                     image:(NSImage*)image
                    offset:(NSPoint)offset
                pasteboard:(NSPasteboard*)pboard
         dragOperationMask:(NSDragOperation)dragOperationMask {
  if ((self = [super init])) {
    contentsView_ = contentsView;
    DCHECK(contentsView_);

    dropData_.reset(new WebDropData(*dropData));
    DCHECK(dropData_.get());

    dragImage_.reset([image retain]);
    imageOffset_ = offset;

    pasteboard_.reset([pboard retain]);
    DCHECK(pasteboard_.get());

    dragOperationMask_ = dragOperationMask;

    [self fillPasteboard];
  }

  return self;
}

- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal {
  return dragOperationMask_;
}

- (void)lazyWriteToPasteboard:(NSPasteboard*)pboard forType:(NSString*)type {
  // NSHTMLPboardType requires the character set to be declared. Otherwise, it
  // assumes US-ASCII. Awesome.
  static const string16 kHtmlHeader =
      ASCIIToUTF16("<meta http-equiv=\"Content-Type\" "
                   "content=\"text/html;charset=UTF-8\">");

  // Be extra paranoid; avoid crashing.
  if (!dropData_.get()) {
    NOTREACHED() << "No drag-and-drop data available for lazy write.";
    return;
  }

  // HTML.
  if ([type isEqualToString:NSHTMLPboardType]) {
    DCHECK(!dropData_->text_html.empty());
    // See comment on |kHtmlHeader| above.
    [pboard setString:SysUTF16ToNSString(kHtmlHeader + dropData_->text_html)
              forType:NSHTMLPboardType];

  // URL.
  } else if ([type isEqualToString:NSURLPboardType]) {
    DCHECK(dropData_->url.is_valid());
    NSString* urlStr = SysUTF8ToNSString(dropData_->url.spec());
    NSURL* url = [NSURL URLWithString:urlStr];
    // If NSURL creation failed, check for a badly-escaped javascript URL.
    if (!url && urlStr && dropData_->url.SchemeIs(chrome::kJavaScriptScheme)) {
      NSString *escapedStr =
        [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
      url = [NSURL URLWithString:escapedStr];
    }
    [url writeToPasteboard:pboard];
  // URL title.
  } else if ([type isEqualToString:kNSURLTitlePboardType]) {
    [pboard setString:SysUTF16ToNSString(dropData_->url_title)
              forType:kNSURLTitlePboardType];

  // File contents.
  } else if ([type isEqualToString:NSFileContentsPboardType] ||
      [type isEqualToString:NSCreateFileContentsPboardType(
              SysUTF16ToNSString(dropData_->file_extension))]) {
    // TODO(viettrungluu: find something which is known to accept
    // NSFileContentsPboardType to check that this actually works!
    scoped_nsobject<NSFileWrapper> file_wrapper(
        [[NSFileWrapper alloc] initRegularFileWithContents:[NSData
                dataWithBytes:dropData_->file_contents.data()
                       length:dropData_->file_contents.length()]]);
    [file_wrapper setPreferredFilename:SysUTF8ToNSString(
            GetFileNameFromDragData(*dropData_).value())];
    [pboard writeFileWrapper:file_wrapper];

  // TIFF.
  } else if ([type isEqualToString:NSTIFFPboardType]) {
    // TODO(viettrungluu): This is a bit odd since we rely on Cocoa to render
    // our image into a TIFF. This is also suboptimal since this is all done
    // synchronously. I'm not sure there's much we can easily do about it.
    scoped_nsobject<NSImage> image(
        [[NSImage alloc] initWithData:[NSData
                dataWithBytes:dropData_->file_contents.data()<commit_msg>Encoding issue dragging bookmarklets to Bookmarks Bar<commit_after>// 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/tab_contents/web_drag_source.h"

#include <sys/param.h>

#include "base/file_path.h"
#include "base/mac/crash_logging.h"
#include "base/string_util.h"
#include "base/sys_string_conversions.h"
#include "base/task.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h"
#include "base/utf_string_conversions.h"
#import "chrome/app/breakpad_mac.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/download_util.h"
#include "chrome/browser/tab_contents/tab_contents_view_mac.h"
#include "content/browser/download/download_manager.h"
#include "content/browser/download/drag_download_file.h"
#include "content/browser/download/drag_download_util.h"
#include "content/browser/renderer_host/render_view_host.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "content/common/url_constants.h"
#include "net/base/file_stream.h"
#include "net/base/net_util.h"
#import "third_party/mozilla/NSPasteboard+Utils.h"
#include "ui/gfx/mac/nsimage_cache.h"
#include "webkit/glue/webdropdata.h"

using base::SysNSStringToUTF8;
using base::SysUTF8ToNSString;
using base::SysUTF16ToNSString;
using net::FileStream;


namespace {

// An unofficial standard pasteboard title type to be provided alongside the
// |NSURLPboardType|.
NSString* const kNSURLTitlePboardType = @"public.url-name";

// Converts a string16 into a FilePath. Use this method instead of
// -[NSString fileSystemRepresentation] to prevent exceptions from being thrown.
// See http://crbug.com/78782 for more info.
FilePath FilePathFromFilename(const string16& filename) {
  NSString* str = SysUTF16ToNSString(filename);
  char buf[MAXPATHLEN];
  if (![str getFileSystemRepresentation:buf maxLength:sizeof(buf)])
    return FilePath();
  return FilePath(buf);
}

// Returns a filename appropriate for the drop data
// TODO(viettrungluu): Refactor to make it common across platforms,
// and move it somewhere sensible.
FilePath GetFileNameFromDragData(const WebDropData& drop_data) {
  // Set a breakpad key for the scope of this function to help debug
  // http://crbug.com/78782
  static NSString* const kUrlKey = @"drop_data_url";
  NSString* value = SysUTF8ToNSString(drop_data.url.spec());
  base::mac::ScopedCrashKey key(kUrlKey, value);

  // Images without ALT text will only have a file extension so we need to
  // synthesize one from the provided extension and URL.
  FilePath file_name(FilePathFromFilename(drop_data.file_description_filename));
  file_name = file_name.BaseName().RemoveExtension();

  if (file_name.empty()) {
    // Retrieve the name from the URL.
    string16 suggested_filename =
        net::GetSuggestedFilename(drop_data.url, "", "", "", "", string16());
    file_name = FilePathFromFilename(suggested_filename);
  }

  file_name = file_name.ReplaceExtension(UTF16ToUTF8(drop_data.file_extension));

  return file_name;
}

// This class's sole task is to write out data for a promised file; the caller
// is responsible for opening the file.
class PromiseWriterTask : public Task {
 public:
  // Assumes ownership of file_stream.
  PromiseWriterTask(const WebDropData& drop_data,
                    FileStream* file_stream);
  virtual ~PromiseWriterTask();
  virtual void Run();

 private:
  WebDropData drop_data_;

  // This class takes ownership of file_stream_ and will close and delete it.
  scoped_ptr<FileStream> file_stream_;
};

// Takes the drop data and an open file stream (which it takes ownership of and
// will close and delete).
PromiseWriterTask::PromiseWriterTask(const WebDropData& drop_data,
                                     FileStream* file_stream) :
    drop_data_(drop_data) {
  file_stream_.reset(file_stream);
  DCHECK(file_stream_.get());
}

PromiseWriterTask::~PromiseWriterTask() {
  DCHECK(file_stream_.get());
  if (file_stream_.get())
    file_stream_->Close();
}

void PromiseWriterTask::Run() {
  CHECK(file_stream_.get());
  file_stream_->Write(drop_data_.file_contents.data(),
                      drop_data_.file_contents.length(),
                      NULL);

  // Let our destructor take care of business.
}

}  // namespace


@interface WebDragSource(Private)

- (void)fillPasteboard;
- (NSImage*)dragImage;

@end  // @interface WebDragSource(Private)


@implementation WebDragSource

- (id)initWithContentsView:(TabContentsViewCocoa*)contentsView
                  dropData:(const WebDropData*)dropData
                     image:(NSImage*)image
                    offset:(NSPoint)offset
                pasteboard:(NSPasteboard*)pboard
         dragOperationMask:(NSDragOperation)dragOperationMask {
  if ((self = [super init])) {
    contentsView_ = contentsView;
    DCHECK(contentsView_);

    dropData_.reset(new WebDropData(*dropData));
    DCHECK(dropData_.get());

    dragImage_.reset([image retain]);
    imageOffset_ = offset;

    pasteboard_.reset([pboard retain]);
    DCHECK(pasteboard_.get());

    dragOperationMask_ = dragOperationMask;

    [self fillPasteboard];
  }

  return self;
}

- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal {
  return dragOperationMask_;
}

- (void)lazyWriteToPasteboard:(NSPasteboard*)pboard forType:(NSString*)type {
  // NSHTMLPboardType requires the character set to be declared. Otherwise, it
  // assumes US-ASCII. Awesome.
  static const string16 kHtmlHeader =
      ASCIIToUTF16("<meta http-equiv=\"Content-Type\" "
                   "content=\"text/html;charset=UTF-8\">");

  // Be extra paranoid; avoid crashing.
  if (!dropData_.get()) {
    NOTREACHED() << "No drag-and-drop data available for lazy write.";
    return;
  }

  // HTML.
  if ([type isEqualToString:NSHTMLPboardType]) {
    DCHECK(!dropData_->text_html.empty());
    // See comment on |kHtmlHeader| above.
    [pboard setString:SysUTF16ToNSString(kHtmlHeader + dropData_->text_html)
              forType:NSHTMLPboardType];

  // URL.
  } else if ([type isEqualToString:NSURLPboardType]) {
    DCHECK(dropData_->url.is_valid());
    NSString* urlStr = SysUTF8ToNSString(dropData_->url.spec());
    NSURL* url = [NSURL URLWithString:urlStr];
    // If NSURL creation failed, check for a badly-escaped JavaScript URL.
    // Strip out any existing escapes and then re-escape uniformly.
    if (!url && urlStr && dropData_->url.SchemeIs(chrome::kJavaScriptScheme)) {
      NSString* unEscapedStr = [urlStr
          stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
      NSString* escapedStr = [unEscapedStr
          stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
      url = [NSURL URLWithString:escapedStr];
    }
    [url writeToPasteboard:pboard];
  // URL title.
  } else if ([type isEqualToString:kNSURLTitlePboardType]) {
    [pboard setString:SysUTF16ToNSString(dropData_->url_title)
              forType:kNSURLTitlePboardType];

  // File contents.
  } else if ([type isEqualToString:NSFileContentsPboardType] ||
      [type isEqualToString:NSCreateFileContentsPboardType(
              SysUTF16ToNSString(dropData_->file_extension))]) {
    // TODO(viettrungluu: find something which is known to accept
    // NSFileContentsPboardType to check that this actually works!
    scoped_nsobject<NSFileWrapper> file_wrapper(
        [[NSFileWrapper alloc] initRegularFileWithContents:[NSData
                dataWithBytes:dropData_->file_contents.data()
                       length:dropData_->file_contents.length()]]);
    [file_wrapper setPreferredFilename:SysUTF8ToNSString(
            GetFileNameFromDragData(*dropData_).value())];
    [pboard writeFileWrapper:file_wrapper];

  // TIFF.
  } else if ([type isEqualToString:NSTIFFPboardType]) {
    // TODO(viettrungluu): This is a bit odd since we rely on Cocoa to render
    // our image into a TIFF. This is also suboptimal since this is all done
    // synchronously. I'm not sure there's much we can easily do about it.
    scoped_nsobject<NSImage> image(
        [[NSImage alloc] initWithData:[NSData
                dataWithBytes:dropData_->file_contents.data()
{'fraction_non_alphanumeric': 0.10871028952684092, 'fraction_numerical': 0.0056925996204933585, 'mean_word_length': 4.011656441717792, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 20, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2492025', 'n_tokens_mistral': 5110, 'n_tokens_neox': 4871, 'n_words': 1152}