/****************************************************************************
** Dooble - The Secure Internet Web Browser
**
** Copyright (c) 2008, 2009, 2010 Ronald Landheer-Cieslak,
** Gunther van Dooble, and the Dooble Team.
** All rights reserved.
**
** License: GPL2 only:
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; version 2 of the License only.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
** or see here: http://www.gnu.org/licenses/gpl.html
**
** For the WebKit library, please see: http://webkit.org.
**
** THE CODE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY
** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
** ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
** GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
** IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
** ARISING IN ANY WAY OUT OF THE USE OF THIS APPLICATION, EVEN IF ADVISED
** OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Please report all praise, requests, bugs, and problems to the project
** team and administrators: http://sf.net/projects/dooble.
**
** You can find us listed at our project page. New team members are welcome.
** The name of the authors should not be used to endorse or promote products
** derived from Dooble without specific prior written permission.
** If you use this code for other projects, please let us know.
**
** Web sites:
**   http://sf.net/projects/dooble
**   http://dooble.sf.net
****************************************************************************/

#include "dooble.h"
#include "dwebpage.h"

#include <QUrl>
#include <QFile>
#include <QtDebug>
#include <QSettings>
#include <QWebFrame>
#include <QMessageBox>
#include <QInputDialog>

dwebpage::dwebpage(QObject *parent):QWebPage(parent),
				    never_show_error_page_(false),
				    never_emit_error_signal_(false)
{
  m_navigationSuggestedAction = DoobleNavigationType::NotSet;
  connect(this, SIGNAL(unsupportedContent(QNetworkReply *)),
	  this, SLOT(handleUnsupportedContent(QNetworkReply *)));
  setForwardUnsupportedContent(true);
}

void dwebpage::javaScriptAlert(QWebFrame *frame, const QString &msg)
{
  /*
  ** The default implementation of javaScriptAlert() causes
  ** segmentation faults when viewing certain Web pages.
  */

  QMessageBox *mb = new QMessageBox(frame->page()->view());

  mb->setIcon(QMessageBox::Information);
  mb->setWindowTitle(tr("JavaScript Alert"));
  mb->setStandardButtons(QMessageBox::Ok);
  mb->setText(msg);

  QSettings settings(dooble::s_settings.value("iconSet").toString(),
		     QSettings::IniFormat);

  for(int i = 0; i < mb->buttons().size(); i++)
    if(mb->buttonRole(mb->buttons().at(i)) == QMessageBox::AcceptRole)
      mb->buttons().at(i)->setIcon
	(QIcon(settings.value("okButtonIcon").toString()));

  mb->setWindowIcon
    (QIcon(settings.value("mainWindow/windowIcon").toString()));
  connect(mb,
	  SIGNAL(finished(int)),
	  mb,
	  SLOT(deleteLater(void)));
  mb->show();
}

bool dwebpage::javaScriptConfirm(QWebFrame *frame, const QString &msg)
{
  QMessageBox mb(frame->page()->view());

  mb.setIcon(QMessageBox::Question);
  mb.setWindowTitle(tr("JavaScript Confirm"));
  mb.setWindowModality(Qt::ApplicationModal);
  mb.setStandardButtons(QMessageBox::No | QMessageBox::Yes);
  mb.setText(msg);

  QSettings settings(dooble::s_settings.value("iconSet").toString(),
		     QSettings::IniFormat);

  for(int i = 0; i < mb.buttons().size(); i++)
    if(mb.buttonRole(mb.buttons().at(i)) == QMessageBox::YesRole)
      mb.buttons().at(i)->setIcon
	(QIcon(settings.value("okButtonIcon").toString()));
    else
      mb.buttons().at(i)->setIcon
	(QIcon(settings.value("cancelButtonIcon").toString()));

  mb.setWindowIcon
    (QIcon(settings.value("mainWindow/windowIcon").toString()));

  if(mb.exec() == QMessageBox::Yes)
    return true;
  else
    return false;
}

bool dwebpage::javaScriptPrompt(QWebFrame *frame, const QString &msg,
				const QString &defaultValue, QString *result)
{
  QSettings settings(dooble::s_settings.value("iconSet").toString(),
		     QSettings::IniFormat);
  QInputDialog id(frame->page()->view());

  id.setInputMode(QInputDialog::TextInput);
  id.setLabelText(msg);
  id.setWindowTitle(tr("JavaScript Prompt"));
  id.setWindowModality(Qt::ApplicationModal);
  id.setTextValue(defaultValue);
  id.setOkButtonText(tr("OK"));
  id.setCancelButtonText(tr("Cancel"));
  id.setWindowIcon
    (QIcon(settings.value("mainWindow/windowIcon").toString()));

  foreach(QPushButton *button, id.findChildren<QPushButton *> ())
    if(button->text() == tr("OK"))
      button->setIcon
	(QIcon(settings.value("okButtonIcon").toString()));
    else
      button->setIcon
	(QIcon(settings.value("cancelButtonIcon").toString()));

  if(id.exec() == QDialog::Accepted)
    {
      *result = id.textValue();
      return true;
    }
  else
    {
      *result = "";
      return false;
    }
}

void dwebpage::handleUnsupportedContent(QNetworkReply *reply)
{
  if(!reply) return; // Silent failure, if no reply.

  bool show_error_page(false);
  bool emit_error_signal(false);
  const char *title_string(0);
  const char *message_string(0);
  QUrl target_url(reply->url());
  QNetworkReply::NetworkError error(reply->error());

  switch (error)
    {
    case QNetworkReply::NoError: // No error condition.
      break;
    case QNetworkReply::ConnectionRefusedError: /*
						** The remote server
						** refused the connection
						** (the server is not
						** accepting requests).
						*/
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Connection Error";
      message_string = "The remote server refused the connection "
	"(the server is not accepting requests).";
      break;
    case QNetworkReply::RemoteHostClosedError: /*
					       ** The remote server
					       ** closed the connection
					       ** prematurely, before the
					       ** entire reply was received
					       ** and processed.
					       */
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Server Error";
      message_string = "The remote server closed the connection "
	"prematurely, before the entire reply was received and processed.";
      break;
    case QNetworkReply::HostNotFoundError: /*
					   ** The remote host name was
					   ** not found (invalid hostname).
					   */
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Connection Error";
      message_string = "The remote host name was not found "
	"(invalid hostname).";
      break;
    case QNetworkReply::TimeoutError: /*
				      ** The connection to the remote
				      ** server timed out.
				      */
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Connection Error";
      message_string = "The connection to the remote server timed out.";
      break;
    case QNetworkReply::OperationCanceledError: /*
						** The operation was
						** canceled via calls to
						** abort() or close()
						** before it was finished.
						*/
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Connection Error";
      message_string = "The operation was canceled via calls to abort() or "
	"close() before it was finished.";
      break;
    case QNetworkReply::SslHandshakeFailedError: /*
						 ** The SSL/TLS handshake
						 ** failed and the encrypted
						 ** channel could not be
						 ** established. The
						 ** sslErrors()
						 ** signal should have been
						 ** emitted.
						 */
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Connection Error";
      message_string = "The SSL/TLS handshake failed and the encrypted "
	"channel could not be established.";
      break;
    case QNetworkReply::ProxyConnectionRefusedError: /*
						     ** The connection to the
						     ** proxy server was
						     ** refused (the proxy
						     ** server is not accepting
						     ** requests).
						     */
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Connection Error";
      message_string = "The connection to the proxy server was refused "
	"(the proxy server is not accepting requests).";
      break;
    case QNetworkReply::ProxyConnectionClosedError: /*
						    ** The proxy server
						    ** closed the connection
						    ** prematurely, before the
						    ** entire reply was
						    ** received
						    ** and processed.
						    */
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Proxy Error";
      message_string = "The proxy server closed the connection prematurely, "
	"before the entire reply was received and processed.";
      break;
    case QNetworkReply::ProxyNotFoundError: /*
					    ** The proxy host name was not
					    ** found (invalid proxy hostname).
					    */
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Connection Error";
      message_string = "The proxy host name was not found "
	"(invalid proxy hostname).";
      break;
    case QNetworkReply::ProxyTimeoutError: /*
					   ** The connection to the proxy
					   ** timed out or the proxy did
					   ** not reply in time to the request
					   ** sent.
					   */
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Proxy Error";
      message_string = "The connection to the proxy timed out or the proxy "
	"did not reply in time to the request sent.";
      break;
    case QNetworkReply::ProxyAuthenticationRequiredError: /*
							  ** The proxy requires
							  ** authentication in
							  ** order to honor the
							  ** request but did
							  ** not accept any
							  ** credentials
							  ** offered
							  ** (if any).
							  */
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Connection Error";
      message_string = "The proxy requires authentication in order to honor "
	"the request but did not accept any credentials offered (if any).";
      break;
    case QNetworkReply::ContentAccessDenied: /*
					     ** The access to the remote
					     ** content was denied (similar
					     ** to HTTP error 401).
					     */
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Connection Error";
      message_string = "The access to the remote content was denied.";
      break;
    case QNetworkReply::ContentOperationNotPermittedError: /*
							   ** The operation
							   ** requested on the
							   ** remote content is
							   ** not permitted.
							   */
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Connection Error";
      message_string = "The operation requested on the remote content is not "
	"permitted.";
      break;
    case QNetworkReply::ContentNotFoundError: /*
					      ** The remote content was not
					      ** found at the server (similar
					      ** to HTTP error 404).
					      */
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Connection Error";
      message_string = "The remote content was not found at the server.";
      break;
    case QNetworkReply::AuthenticationRequiredError: /*
						     ** The remote server
						     ** requires authentication
						     ** to serve the content
						     ** but
						     ** the credentials
						     ** provided
						     ** were not accepted
						     ** (if any).
						     */
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Connection Error";
      message_string = "The remote server requires authentication to serve "
	"the content but the credentials provided were not accepted (if any).";
      break;
    case QNetworkReply::ProtocolUnknownError: /*
					      ** The Network Access API cannot
					      ** honor the request because the
					      ** protocol is not known.
					      */
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Protocol Unknown";
      message_string = "The Network Access API cannot honor the request "
	"because the protocol is not known.";
      break;
    case QNetworkReply::ProtocolInvalidOperationError: /*
						       ** The requested
						       ** operation
						       ** is invalid for this
						       ** protocol.
						       */
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Protocol Error";
      message_string = "The requested operation is invalid for this protocol.";
      break;
    case QNetworkReply::UnknownNetworkError: /*
					     ** An unknown network-related
					     ** error was detected.
					     */
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Unknown Network Error";
      message_string = "An unknown network-related error was detected.";
      break;
    case QNetworkReply::UnknownProxyError: /*
					   ** An unknown proxy-related error
					   ** was detected.
					   */
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Unknown Proxy Error";
      message_string = "An unknown proxy-related error was detected.";
      break;
    case QNetworkReply::UnknownContentError: /*
					     ** An unknown error related
					     ** to the remote content
					     ** was detected.
					     */
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Unknwon Content Error";
      message_string = "An unknown error related to the remote "
	"content was detected.";
      break;
    case QNetworkReply::ProtocolFailure: /*
					 ** A breakdown in protocol was
					 ** detected (parsing error,
					 ** invalid or unexpected
					 ** responses, etc.).
					 */
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Protocol Failure";
      message_string = "A breakdown in protocol was detected (parsing error, "
	"invalid or unexpected responses, etc.).";
      break;
    default: // Unknown error.
      show_error_page = true;
      emit_error_signal = true;
      title_string = "Unknown Error";
      message_string = "An unknown error occured.";
      break;
    }

  if(show_error_page && !never_show_error_page_)
    {
      bool shown = false;
      QString html("<html><title>Page Load Error</title>");
      QList<QWebFrame*> frames;

      html.append(QString("Unable to load %1.<br>").
		  arg(target_url.toString(QUrl::StripTrailingSlash)));
      html.append(QString("Error: %1").arg(message_string));
      html.append("</html>");
      frames.append(mainFrame());

      while(!frames.isEmpty() && !shown)
	{
	  QWebFrame *frame = frames.takeFirst();

	  if(frame->url().toString(QUrl::StripTrailingSlash) ==
	     reply->url().toString(QUrl::StripTrailingSlash))
	    {
	      frame->setHtml(html, reply->url());
	      shown = true;
	    }
	  else
	    {
	      QList<QWebFrame *> children = frame->childFrames();

	      foreach(QWebFrame *frame, children)
		frames.append(frame);
	    }
	}

      if(!shown)
	mainFrame()->setHtml(html, reply->url());
      else
	{
	  /*
	  ** Already shown, or don't show here.
	  */
	}
    }
  else
    {
      /*
      ** Either not an error or don't show it.
      */
    }

  if(emit_error_signal && !never_emit_error_signal_)
    emit networkError(error);
  else
    {
      /*
      ** Don't emit any signals.
      */
    }
}

QWebPage *dwebpage::createWindow(WebWindowType type)
{
  Q_UNUSED(type);
  QUrl url("about: blank");

  /*
  ** Read Include/dview.h for determining valid popups.
  */

  if(m_navigationSuggestedAction == DoobleNavigationType::NewTab)
    {
      m_navigationSuggestedAction = DoobleNavigationType::NotSet;

      dview *new_page = findDooble()->newTab(url);

      /*
      ** The new page must be set as the tab's current widget.
      */

      findDooble()->setCurrentPage(new_page);
      return new_page->page();
    }
  else if(m_navigationSuggestedAction == DoobleNavigationType::NewWindow)
    {
      m_navigationSuggestedAction = DoobleNavigationType::NotSet;

      dview *v = 0;
      dooble *dbl = new dooble(false);

      v = dbl->newTab(url);
      return v->page();
    }

  if(dooble::s_settings.value("settingsWindow/blockPopups", false).toBool())
    {
      bool allowPopup = false;
      dwebview *v = qobject_cast<dwebview *> (view());

      allowPopup = v->allowPopup();
      v->resetAllowPopup();

      if(!allowPopup)
	return 0;
    }

  if(dooble::s_settings.value("settingsWindow/javascriptEnabled",
			      true).toBool())
    {
      if(dooble::s_settings.value("settingsWindow/javascriptAllowNewWindows",
				  false).toBool())
	{
	  dview *v = 0;
	  dooble *dbl = new dooble(true);

	  v = dbl->newTab(url);
	  return v->page();
	}
      else
	return 0;
    }

  /*
  ** A new window will not be created.
  */

  return 0;
}

dooble *dwebpage::findDooble(void)
{
  dooble *dbl(0);
  QObject *prnt(this);

  do
    {
      prnt = prnt->parent();
      dbl = dynamic_cast<dooble *> (prnt);
    }
  while (prnt != 0 && dbl == 0);

  return dbl;
}

QString dwebpage::userAgentForUrl(const QUrl &url) const
{
  return QWebPage::userAgentForUrl(url);
}

dwebpage::~dwebpage()
{
}

bool dwebpage::acceptNavigationRequest(QWebFrame *frame,
				       const QNetworkRequest &request,
				       NavigationType type)
{
  dwebview *v = qobject_cast<dwebview *> (view());

  v->resetAllowPopup();

  if(type == QWebPage::NavigationTypeLinkClicked ||
     type == QWebPage::NavigationTypeFormSubmitted ||
     type == QWebPage::NavigationTypeFormResubmitted)
    {
      if(!frame)
	{
	  if(dooble::s_settings.value("settingsWindow/openInNewTab",
				      true).toBool())
	    m_navigationSuggestedAction = DoobleNavigationType::NewTab;
	  else
	    m_navigationSuggestedAction =
	      DoobleNavigationType::NewWindow;
	}
    }

  return QWebPage::acceptNavigationRequest(frame, request, type);
}
