// Copyright (c) 2006-2008 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.

#include "chrome/browser/render_view_context_menu_controller.h"

#include "base/command_line.h"
#include "base/path_service.h"
#include "base/scoped_clipboard_writer.h"
#include "base/string_util.h"
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/browser/views/options/fonts_languages_window_view.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/pref_service.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/spellchecker.h"
#include "chrome/browser/download/download_manager.h"
#include "chrome/browser/download/save_package.h"
#include "chrome/browser/navigation_controller.h"
#include "chrome/browser/navigation_entry.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/template_url_model.h"
#include "chrome/browser/views/page_info_window.h"
#include "chrome/browser/web_contents.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/clipboard_service.h"
#include "chrome/common/l10n_util.h"
#include "chrome/common/win_util.h"
#include "net/base/escape.h"
#include "net/base/net_util.h"
#include "net/url_request/url_request.h"

#include "generated_resources.h"

RenderViewContextMenuController::RenderViewContextMenuController(
    WebContents* source_web_contents,
    const ViewHostMsg_ContextMenu_Params& params)
  : source_web_contents_(source_web_contents),
    params_(params) {
}

RenderViewContextMenuController::~RenderViewContextMenuController() {
}

///////////////////////////////////////////////////////////////////////////////
// Controller methods

void RenderViewContextMenuController::OpenURL(
    const GURL& url,
    WindowOpenDisposition disposition,
    PageTransition::Type transition) {
  source_web_contents_->OpenURL(url, GURL(), disposition, transition);
}

void RenderViewContextMenuController::CopyImageAt(int x, int y) {
  source_web_contents_->render_view_host()->CopyImageAt(x, y);
}

void RenderViewContextMenuController::Inspect(int x, int y) {
  source_web_contents_->render_view_host()->InspectElementAt(x, y);
}

void RenderViewContextMenuController::WriteTextToClipboard(
    const std::wstring& text) {
  ClipboardService* clipboard = g_browser_process->clipboard_service();

  if (!clipboard)
    return;

  ScopedClipboardWriter scw(clipboard);
  scw.WriteText(text);
}

void RenderViewContextMenuController::WriteURLToClipboard(const GURL& url) {
  if (url.SchemeIs("mailto"))
    WriteTextToClipboard(UTF8ToWide(url.path()));
  else
    WriteTextToClipboard(UTF8ToWide(url.spec()));
}

///////////////////////////////////////////////////////////////////////////////
// Menu::Delegate methods

std::wstring RenderViewContextMenuController::GetLabel(int id) const {
  switch (id) {
    case IDS_CONTENT_CONTEXT_SEARCHWEBFOR: {
      const TemplateURL* const default_provider = source_web_contents_->
          profile()->GetTemplateURLModel()->GetDefaultSearchProvider();
      DCHECK(default_provider);  // The context menu should not contain this
                                 // item when there is no provider.
      return l10n_util::GetStringF(id, default_provider->short_name(),
          l10n_util::TruncateString(params_.selection_text, 50));
    }

    case IDS_CONTENT_CONTEXT_COPYLINKLOCATION:
      if (params_.link_url.SchemeIs("mailto"))
        return l10n_util::GetString(IDS_CONTENT_CONTEXT_COPYEMAILADDRESS);

    default:
      return l10n_util::GetString(id);
  }
}

bool RenderViewContextMenuController::IsCommandEnabled(int id) const {
  // Allow Spell Check language items on sub menu for text area context menu.
  if ((id >= IDC_SPELLCHECK_LANGUAGES_FIRST) &&
      (id < IDC_SPELLCHECK_LANGUAGES_LAST)) {
    return true;
  }

  switch (id) {
    case IDS_CONTENT_CONTEXT_BACK:
      return source_web_contents_->controller()->CanGoBack();

    case IDS_CONTENT_CONTEXT_FORWARD:
      return source_web_contents_->controller()->CanGoForward();

    case IDS_CONTENT_CONTEXT_VIEWPAGESOURCE:
    case IDS_CONTENT_CONTEXT_VIEWFRAMESOURCE:
    case IDS_CONTENT_CONTEXT_INSPECTELEMENT:
      return IsDevCommandEnabled(id);

    case IDS_CONTENT_CONTEXT_OPENLINKNEWTAB:
    case IDS_CONTENT_CONTEXT_OPENLINKNEWWINDOW:
    case IDS_CONTENT_CONTEXT_COPYLINKLOCATION:
      return params_.link_url.is_valid();

    case IDS_CONTENT_CONTEXT_SAVELINKAS:
      return params_.link_url.is_valid() &&
             URLRequest::IsHandledURL(params_.link_url);

    case IDS_CONTENT_CONTEXT_SAVEIMAGEAS:
      return params_.image_url.is_valid() &&
             URLRequest::IsHandledURL(params_.image_url);

    case IDS_CONTENT_CONTEXT_OPENIMAGENEWTAB:
    case IDS_CONTENT_CONTEXT_COPYIMAGELOCATION:
      return params_.image_url.is_valid();

    case IDS_CONTENT_CONTEXT_SAVEPAGEAS:
      return SavePackage::IsSavableURL(source_web_contents_->GetURL());

    case IDS_CONTENT_CONTEXT_OPENFRAMENEWTAB:
    case IDS_CONTENT_CONTEXT_OPENFRAMENEWWINDOW:
      return params_.frame_url.is_valid();

    case IDS_CONTENT_CONTEXT_UNDO:
      return !!(params_.edit_flags & ContextNode::CAN_UNDO);

    case IDS_CONTENT_CONTEXT_REDO:
      return !!(params_.edit_flags & ContextNode::CAN_REDO);

    case IDS_CONTENT_CONTEXT_CUT:
      return !!(params_.edit_flags & ContextNode::CAN_CUT);

    case IDS_CONTENT_CONTEXT_COPY:
      return !!(params_.edit_flags & ContextNode::CAN_COPY);

    case IDS_CONTENT_CONTEXT_PASTE:
      return !!(params_.edit_flags & ContextNode::CAN_PASTE);

    case IDS_CONTENT_CONTEXT_DELETE:
      return !!(params_.edit_flags & ContextNode::CAN_DELETE);

    case IDS_CONTENT_CONTEXT_SELECTALL:
      return !!(params_.edit_flags & ContextNode::CAN_SELECT_ALL);

    case IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD:
      return !source_web_contents_->profile()->IsOffTheRecord() &&
             params_.link_url.is_valid();

    case IDS_CONTENT_CONTEXT_OPENFRAMEOFFTHERECORD:
      return !source_web_contents_->profile()->IsOffTheRecord() &&
             params_.frame_url.is_valid();

    case IDS_CONTENT_CONTEXT_ADD_TO_DICTIONARY:
      return !params_.misspelled_word.empty();

    case IDS_CONTENT_CONTEXT_VIEWPAGEINFO:
      return (source_web_contents_->controller()->GetActiveEntry() != NULL);

    case IDS_CONTENT_CONTEXT_RELOAD:
    case IDS_CONTENT_CONTEXT_COPYIMAGE:
    case IDS_CONTENT_CONTEXT_PRINT:
    case IDS_CONTENT_CONTEXT_SEARCHWEBFOR:
    case IDC_SPELLCHECK_SUGGESTION_0:
    case IDC_SPELLCHECK_SUGGESTION_1:
    case IDC_SPELLCHECK_SUGGESTION_2:
    case IDC_SPELLCHECK_SUGGESTION_3:
    case IDC_SPELLCHECK_SUGGESTION_4:
    case IDC_SPELLCHECK_MENU:
    case IDC_CHECK_SPELLING_OF_THIS_FIELD:
    case IDS_CONTENT_CONTEXT_LANGUAGE_SETTINGS:
    case IDS_CONTENT_CONTEXT_VIEWFRAMEINFO:
      return true;

    case IDS_CONTENT_CONTEXT_SAVEFRAMEAS:
    case IDS_CONTENT_CONTEXT_PRINTFRAME:
    case IDS_CONTENT_CONTEXT_ADDSEARCHENGINE:  // Not implemented.
    default:
      return false;
  }
}

bool RenderViewContextMenuController::IsItemChecked(int id) const {
  // Check box for 'Check the Spelling of this field'.
  if (id == IDC_CHECK_SPELLING_OF_THIS_FIELD)
    return params_.spellcheck_enabled;
  
  // Don't bother getting the display language vector if this isn't a spellcheck
  // language.
  if ((id < IDC_SPELLCHECK_LANGUAGES_FIRST) ||
      (id >= IDC_SPELLCHECK_LANGUAGES_LAST))
    return false;

  SpellChecker::Languages display_languages;
  return SpellChecker::GetSpellCheckLanguagesToDisplayInContextMenu(
      source_web_contents_->profile(), &display_languages) ==
      (id - IDC_SPELLCHECK_LANGUAGES_FIRST);
}

bool RenderViewContextMenuController::GetAcceleratorInfo(
    int id,
    views::Accelerator* accel) {
  // There are no formally defined accelerators we can query so we assume
  // that Ctrl+C, Ctrl+V, Ctrl+X, Ctrl-A, etc do what they normally do.
  switch (id) {
    case IDS_CONTENT_CONTEXT_UNDO:
      *accel = views::Accelerator(L'Z', false, true, false);
      return true;

    case IDS_CONTENT_CONTEXT_REDO:
      *accel = views::Accelerator(L'Z', true, true, false);
      return true;

    case IDS_CONTENT_CONTEXT_CUT:
      *accel = views::Accelerator(L'X', false, true, false);
      return true;

    case IDS_CONTENT_CONTEXT_COPY:
      *accel = views::Accelerator(L'C', false, true, false);
      return true;

    case IDS_CONTENT_CONTEXT_PASTE:
      *accel = views::Accelerator(L'V', false, true, false);
      return true;

    case IDS_CONTENT_CONTEXT_SELECTALL:
      *accel = views::Accelerator(L'A', false, true, false);

    default:
      return false;
  }
}

void RenderViewContextMenuController::ExecuteCommand(int id) {
  // Check to see if one of the spell check language ids have been clicked.
  if (id >= IDC_SPELLCHECK_LANGUAGES_FIRST &&
      id < IDC_SPELLCHECK_LANGUAGES_LAST) {
    const size_t language_number = id - IDC_SPELLCHECK_LANGUAGES_FIRST;
    SpellChecker::Languages display_languages; 
    SpellChecker::GetSpellCheckLanguagesToDisplayInContextMenu(
        source_web_contents_->profile(), &display_languages);
    if (language_number < display_languages.size()) {
      StringPrefMember dictionary_language;
      dictionary_language.Init(prefs::kSpellCheckDictionary,
          source_web_contents_->profile()->GetPrefs(), NULL);
      dictionary_language.SetValue(display_languages[language_number]);
    }
      
    return;
  }

  switch (id) {
    case IDS_CONTENT_CONTEXT_OPENLINKNEWTAB:
      OpenURL(params_.link_url, NEW_BACKGROUND_TAB, PageTransition::LINK);
      break;

    case IDS_CONTENT_CONTEXT_OPENLINKNEWWINDOW:
      OpenURL(params_.link_url, NEW_WINDOW, PageTransition::LINK);
      break;

    case IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD:
      OpenURL(params_.link_url, OFF_THE_RECORD, PageTransition::LINK);
      break;

    // TODO(paulg): Prompt the user for file name when saving links and images.
    case IDS_CONTENT_CONTEXT_SAVEIMAGEAS:
    case IDS_CONTENT_CONTEXT_SAVELINKAS: {
      const GURL& referrer =
          params_.frame_url.is_empty() ? params_.page_url : params_.frame_url;
      const GURL& url = id == IDS_CONTENT_CONTEXT_SAVELINKAS ? params_.link_url :
                                                               params_.image_url;
      DownloadManager* dlm =
          source_web_contents_->profile()->GetDownloadManager();
      dlm->DownloadUrl(url, referrer, source_web_contents_);
      break;
    }

    case IDS_CONTENT_CONTEXT_COPYLINKLOCATION:
      WriteURLToClipboard(params_.link_url);
      break;

    case IDS_CONTENT_CONTEXT_COPYIMAGELOCATION:
      WriteURLToClipboard(params_.image_url);
      break;

    case IDS_CONTENT_CONTEXT_COPYIMAGE:
      CopyImageAt(params_.x, params_.y);
      break;

    case IDS_CONTENT_CONTEXT_OPENIMAGENEWTAB:
      OpenURL(params_.image_url, NEW_BACKGROUND_TAB, PageTransition::LINK);
      break;

    case IDS_CONTENT_CONTEXT_BACK:
      source_web_contents_->controller()->GoBack();
      break;

    case IDS_CONTENT_CONTEXT_FORWARD:
      source_web_contents_->controller()->GoForward();
      break;

    case IDS_CONTENT_CONTEXT_SAVEPAGEAS:
      source_web_contents_->OnSavePage();
      break;

    case IDS_CONTENT_CONTEXT_RELOAD:
      source_web_contents_->controller()->Reload(true);
      break;

    case IDS_CONTENT_CONTEXT_PRINT:
      source_web_contents_->PrintPreview();
      break;

    case IDS_CONTENT_CONTEXT_VIEWPAGESOURCE:
      OpenURL(GURL("view-source:" + params_.page_url.spec()),
              NEW_FOREGROUND_TAB, PageTransition::GENERATED);
      break;

    case IDS_CONTENT_CONTEXT_INSPECTELEMENT:
      Inspect(params_.x, params_.y);
      break;

    case IDS_CONTENT_CONTEXT_VIEWPAGEINFO: {
      NavigationEntry* nav_entry =
          source_web_contents_->controller()->GetActiveEntry();
      PageInfoWindow::CreatePageInfo(source_web_contents_->profile(),
                                     nav_entry,
                                     source_web_contents_->GetContentHWND(),
                                     PageInfoWindow::SECURITY);      
      break;
    }

    case IDS_CONTENT_CONTEXT_OPENFRAMENEWTAB:
      OpenURL(params_.frame_url, NEW_BACKGROUND_TAB, PageTransition::LINK);
      break;

    case IDS_CONTENT_CONTEXT_OPENFRAMENEWWINDOW:
      OpenURL(params_.frame_url, NEW_WINDOW, PageTransition::LINK);
      break;

    case IDS_CONTENT_CONTEXT_OPENFRAMEOFFTHERECORD:
      OpenURL(params_.frame_url, OFF_THE_RECORD, PageTransition::LINK);
      break;

    case IDS_CONTENT_CONTEXT_SAVEFRAMEAS:
      win_util::MessageBox(NULL, L"Context Menu Action", L"Save Frame As",
                           MB_OK);
      break;

    case IDS_CONTENT_CONTEXT_PRINTFRAME:
      win_util::MessageBox(NULL, L"Context Menu Action", L"Print Frame",
                           MB_OK);
      break;

    case IDS_CONTENT_CONTEXT_VIEWFRAMESOURCE:
      OpenURL(GURL("view-source:" + params_.frame_url.spec()),
              NEW_FOREGROUND_TAB, PageTransition::GENERATED);
      break;

    case IDS_CONTENT_CONTEXT_VIEWFRAMEINFO: {
      // Deserialize the SSL info.
      NavigationEntry::SSLStatus ssl;
      if (!params_.security_info.empty()) {
        int cert_id, cert_status, security_bits;
        SSLManager::DeserializeSecurityInfo(params_.security_info,
                                            &cert_id,
                                            &cert_status,
                                            &security_bits);
        ssl.set_cert_id(cert_id);
        ssl.set_cert_status(cert_status);
        ssl.set_security_bits(security_bits);
      }
      PageInfoWindow::CreateFrameInfo(source_web_contents_->profile(),
                                      params_.frame_url,
                                      ssl,
                                      source_web_contents_->GetContentHWND(),
                                      PageInfoWindow::SECURITY);
      break;
    }

    case IDS_CONTENT_CONTEXT_UNDO:
      source_web_contents_->render_view_host()->Undo();
      break;

    case IDS_CONTENT_CONTEXT_REDO:
      source_web_contents_->render_view_host()->Redo();
      break;

    case IDS_CONTENT_CONTEXT_CUT:
      source_web_contents_->render_view_host()->Cut();
      break;

    case IDS_CONTENT_CONTEXT_COPY:
      source_web_contents_->render_view_host()->Copy();
      break;

    case IDS_CONTENT_CONTEXT_PASTE:
      source_web_contents_->render_view_host()->Paste();
      break;

    case IDS_CONTENT_CONTEXT_DELETE:
      source_web_contents_->render_view_host()->Delete();
      break;

    case IDS_CONTENT_CONTEXT_SELECTALL:
      source_web_contents_->render_view_host()->SelectAll();
      break;

    case IDS_CONTENT_CONTEXT_SEARCHWEBFOR: {
      const TemplateURL* const default_provider = source_web_contents_->
          profile()->GetTemplateURLModel()->GetDefaultSearchProvider();
      DCHECK(default_provider);  // The context menu should not contain this
                                 // item when there is no provider.
      const TemplateURLRef* const search_url = default_provider->url();
      DCHECK(search_url->SupportsReplacement());
      OpenURL(GURL(search_url->ReplaceSearchTerms(*default_provider,
          params_.selection_text, TemplateURLRef::NO_SUGGESTIONS_AVAILABLE,
          std::wstring())), NEW_FOREGROUND_TAB, PageTransition::GENERATED);
      break;
    }

    case IDC_SPELLCHECK_SUGGESTION_0:
    case IDC_SPELLCHECK_SUGGESTION_1:
    case IDC_SPELLCHECK_SUGGESTION_2:
    case IDC_SPELLCHECK_SUGGESTION_3:
    case IDC_SPELLCHECK_SUGGESTION_4:
      source_web_contents_->render_view_host()->Replace(
          params_.dictionary_suggestions[id - IDC_SPELLCHECK_SUGGESTION_0]);
      break;

    case IDC_CHECK_SPELLING_OF_THIS_FIELD:
      source_web_contents_->render_view_host()->ToggleSpellCheck();
      break;
    case IDS_CONTENT_CONTEXT_ADD_TO_DICTIONARY:
      source_web_contents_->render_view_host()->AddToDictionary(
          params_.misspelled_word);
      break;

    case IDS_CONTENT_CONTEXT_LANGUAGE_SETTINGS: {
      FontsLanguagesWindowView* window_ = new FontsLanguagesWindowView(
          source_web_contents_->profile());
      views::Window::CreateChromeWindow(source_web_contents_->GetContentHWND(),
                                        gfx::Rect(), window_)->Show();
      window_->SelectLanguagesTab();
      break;
    }

    case IDS_CONTENT_CONTEXT_ADDSEARCHENGINE:  // Not implemented.
    default:
      break;
  }
}

bool RenderViewContextMenuController::IsDevCommandEnabled(int id) const {
  CommandLine command_line;
  if (command_line.HasSwitch(switches::kAlwaysEnableDevTools))
    return true;

  NavigationEntry *active_entry =
      source_web_contents_->controller()->GetActiveEntry();
  if (!active_entry)
    return false;

  // Don't inspect HTML dialogs.
  if (source_web_contents_->type() == TAB_CONTENTS_HTML_DIALOG)
    return false;

  // Don't inspect view source.
  if (source_web_contents_->type() == TAB_CONTENTS_VIEW_SOURCE)
    return false;

  // Don't inspect inspector, new tab UI, etc.
  if (active_entry->url().SchemeIs("chrome"))
    return false;

  // Don't inspect about:network, about:memory, etc.
  // However, we do want to inspect about:blank, which is often
  // used by ordinary web pages.
  if (active_entry->display_url().SchemeIs("about") &&
      !LowerCaseEqualsASCII(active_entry->display_url().path(), "blank"))
    return false;

  // Don't enable the web inspector if JavaScript is disabled
  if (id == IDS_CONTENT_CONTEXT_INSPECTELEMENT) {
    PrefService* prefs = source_web_contents_->profile()->GetPrefs();
    if (!prefs->GetBoolean(prefs::kWebKitJavascriptEnabled) ||
        command_line.HasSwitch(switches::kDisableJavaScript))
      return false;
  }

  return true;
}