// 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/jsmessage_box_handler.h"

#include "chrome/browser/app_modal_dialog_queue.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/tab_contents/web_contents.h"
#include "chrome/common/gfx/text_elider.h"
#include "chrome/common/l10n_util.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_types.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/pref_service.h"
#include "chrome/views/message_box_view.h"
#include "chrome/views/window.h"

#include "generated_resources.h"

///////////////////////////////////////////////////////////////////////////////
// JavascriptMessageBoxHandler, public:

// static
void JavascriptMessageBoxHandler::RunJavascriptMessageBox(
    WebContents* web_contents,
    int dialog_flags,
    const std::wstring& message_text,
    const std::wstring& default_prompt_text,
    bool display_suppress_checkbox,
    IPC::Message* reply_msg) {
  JavascriptMessageBoxHandler* handler =
      new JavascriptMessageBoxHandler(web_contents, dialog_flags,
                                      message_text, default_prompt_text,
                                      display_suppress_checkbox, reply_msg);
  AppModalDialogQueue::AddDialog(handler);
}

JavascriptMessageBoxHandler::~JavascriptMessageBoxHandler() {
}

//////////////////////////////////////////////////////////////////////////////
// JavascriptMessageBoxHandler, views::DialogDelegate implementation:

int JavascriptMessageBoxHandler::GetDialogButtons() const {
  int dialog_buttons = 0;
  if (dialog_flags_ & MessageBoxView::kFlagHasOKButton)
    dialog_buttons = DIALOGBUTTON_OK;

  if (dialog_flags_ & MessageBoxView::kFlagHasCancelButton)
    dialog_buttons |= DIALOGBUTTON_CANCEL;

  return dialog_buttons;
}

std::wstring JavascriptMessageBoxHandler::GetWindowTitle() const {
  if (!web_contents_)
    return std::wstring();

  GURL url = web_contents_->GetURL();
  if (!url.has_host())
    return l10n_util::GetString(IDS_JAVASCRIPT_MESSAGEBOX_DEFAULT_TITLE);

  // We really only want the scheme, hostname, and port.
  GURL::Replacements replacements;
  replacements.ClearUsername();
  replacements.ClearPassword();
  replacements.ClearPath();
  replacements.ClearQuery();
  replacements.ClearRef();
  GURL clean_url = url.ReplaceComponents(replacements);

  // TODO(brettw) it should be easier than this to do the correct language
  // handling without getting the accept language from the profile.
  std::wstring base_address = gfx::ElideUrl(clean_url, ChromeFont(), 0,
      web_contents_->profile()->GetPrefs()->GetString(prefs::kAcceptLanguages));
  // Force URL to have LTR directionality.
  if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT)
    l10n_util::WrapStringWithLTRFormatting(&base_address);
  return l10n_util::GetStringF(IDS_JAVASCRIPT_MESSAGEBOX_TITLE, base_address);
}

void JavascriptMessageBoxHandler::WindowClosing() {
  dialog_ = NULL;

  if (message_box_view_->IsCheckBoxSelected() && web_contents_)
    web_contents_->set_suppress_javascript_messages(true);

  delete this;
}

bool JavascriptMessageBoxHandler::Cancel() {
  // We need to do this before WM_DESTROY (WindowClosing()) as any parent frame
  // will receive it's activation messages before this dialog receives
  // WM_DESTROY. The parent frame would then try to activate any modal dialogs
  // that were still open in the ModalDialogQueue, which would send activation
  // back to this one. The framework should be improved to handle this, so this
  // is a temporary workaround.
  AppModalDialogQueue::ShowNextDialog();

  if (web_contents_) {
    web_contents_->OnJavaScriptMessageBoxClosed(reply_msg_, false,
                                                EmptyWString());
  }
  return true;
}

bool JavascriptMessageBoxHandler::Accept() {
  AppModalDialogQueue::ShowNextDialog();

  if (web_contents_) {
    web_contents_->OnJavaScriptMessageBoxClosed(
        reply_msg_, true, message_box_view_->GetInputText());
  }
  return true;
}

//////////////////////////////////////////////////////////////////////////////
// JavascriptMessageBoxHandler, views::AppModalDialogDelegate
// implementation:

void JavascriptMessageBoxHandler::ShowModalDialog() {
  // If the WebContents that created this dialog navigated away before this
  // dialog became visible, simply show the next dialog if any.
  if (!web_contents_) {
    AppModalDialogQueue::ShowNextDialog();
    delete this;
    return;
  }

  web_contents_->Activate();
  HWND root_hwnd = GetAncestor(web_contents_->GetContainerHWND(), GA_ROOT);
  dialog_ = views::Window::CreateChromeWindow(root_hwnd, gfx::Rect(), this);
  dialog_->Show();
}

void JavascriptMessageBoxHandler::ActivateModalDialog() {
  // Ensure that the dialog is visible and at the top of the z-order. These
  // conditions may not be true if the dialog was opened on a different virtual
  // desktop to the one the browser window is on.
  dialog_->Show();
  dialog_->Activate();
}

///////////////////////////////////////////////////////////////////////////////
// JavascriptMessageBoxHandler, views::WindowDelegate implementation:

views::View* JavascriptMessageBoxHandler::GetContentsView() {
  return message_box_view_;
}

views::View* JavascriptMessageBoxHandler::GetInitiallyFocusedView() const {
  if (message_box_view_->text_box())
    return message_box_view_->text_box();
  return views::AppModalDialogDelegate::GetInitiallyFocusedView();
}

///////////////////////////////////////////////////////////////////////////////
// JavascriptMessageBoxHandler, private:

void JavascriptMessageBoxHandler::Observe(NotificationType type,
                                          const NotificationSource& source,
                                          const NotificationDetails& details) {
  bool web_contents_gone = false;
  if (!web_contents_)
    return;

  if (type == NOTIFY_NAV_ENTRY_COMMITTED &&
      Source<NavigationController>(source).ptr() == web_contents_->controller())
    web_contents_gone = true;

  if (type == NOTIFY_TAB_CONTENTS_DESTROYED &&
      Source<TabContents>(source).ptr() ==
      static_cast<TabContents*>(web_contents_))
    web_contents_gone = true;

  if (web_contents_gone) {
    web_contents_ = NULL;

    // If the dialog is visible close it.
    if (dialog_)
      dialog_->Close();
  }
}

JavascriptMessageBoxHandler::JavascriptMessageBoxHandler(
    WebContents* web_contents,
    int dialog_flags,
    const std::wstring& message_text,
    const std::wstring& default_prompt_text,
    bool display_suppress_checkbox,
    IPC::Message* reply_msg)
      : web_contents_(web_contents),
        reply_msg_(reply_msg),
        dialog_flags_(dialog_flags),
        dialog_(NULL),
        message_box_view_(new MessageBoxView(dialog_flags, message_text,
                                             default_prompt_text)) {
  DCHECK(message_box_view_);
  DCHECK(reply_msg_);

  if (display_suppress_checkbox) {
    message_box_view_->SetCheckBoxLabel(
        l10n_util::GetString(IDS_JAVASCRIPT_MESSAGEBOX_SUPPRESS_OPTION));
  }

  // Make sure we get navigation notifications so we know when our parent
  // contents will disappear or navigate to a different page.
  registrar_.Add(this, NOTIFY_NAV_ENTRY_COMMITTED,
                 NotificationService::AllSources());
  registrar_.Add(this, NOTIFY_TAB_CONTENTS_DESTROYED,
                 NotificationService::AllSources());
}