// 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.

#include "chrome/browser/blocked_content_container.h"

#include "content/browser/tab_contents/tab_contents.h"
#include "ui/gfx/rect.h"

// static
const size_t BlockedContentContainer::kImpossibleNumberOfPopups = 30;

struct BlockedContentContainer::BlockedContent {
  BlockedContent(TabContents* tab_contents,
                 WindowOpenDisposition disposition,
                 const gfx::Rect& bounds,
                 bool user_gesture)
      : tab_contents(tab_contents),
        disposition(disposition),
        bounds(bounds),
        user_gesture(user_gesture) {
  }

  TabContents* tab_contents;
  WindowOpenDisposition disposition;
  gfx::Rect bounds;
  bool user_gesture;
};

BlockedContentContainer::BlockedContentContainer(TabContents* owner)
    : owner_(owner) {
}

BlockedContentContainer::~BlockedContentContainer() {}

void BlockedContentContainer::AddTabContents(TabContents* tab_contents,
                                             WindowOpenDisposition disposition,
                                             const gfx::Rect& bounds,
                                             bool user_gesture) {
  if (blocked_contents_.size() == (kImpossibleNumberOfPopups - 1)) {
    delete tab_contents;
    VLOG(1) << "Warning: Renderer is sending more popups to us than should be "
               "possible. Renderer compromised?";
    return;
  }

  blocked_contents_.push_back(
      BlockedContent(tab_contents, disposition, bounds, user_gesture));
  tab_contents->set_delegate(this);
  // Since the new tab_contents will not be showed, call WasHidden to change
  // its status on both RenderViewHost and RenderView.
  tab_contents->WasHidden();
  if (blocked_contents_.size() == 1)
    owner_->PopupNotificationVisibilityChanged(true);
}

void BlockedContentContainer::LaunchForContents(TabContents* tab_contents) {
  // Open the popup.
  for (BlockedContents::iterator i(blocked_contents_.begin());
       i != blocked_contents_.end(); ++i) {
    if (i->tab_contents == tab_contents) {
      // To support the owner blocking the content again we copy and erase
      // before attempting to add.
      BlockedContent content(*i);
      blocked_contents_.erase(i);
      i = blocked_contents_.end();
      tab_contents->set_delegate(NULL);
      // We needn't call WasRestored to change its status because the
      // TabContents::AddNewContents will do it.
      owner_->AddOrBlockNewContents(tab_contents,
                                    content.disposition,
                                    content.bounds,
                                    content.user_gesture);
      break;
    }
  }

  if (blocked_contents_.empty())
    Destroy();
}

size_t BlockedContentContainer::GetBlockedContentsCount() const {
  return blocked_contents_.size();
}

void BlockedContentContainer::GetBlockedContents(
    std::vector<TabContents*>* blocked_contents) const {
  DCHECK(blocked_contents);
  for (BlockedContents::const_iterator i(blocked_contents_.begin());
       i != blocked_contents_.end(); ++i)
    blocked_contents->push_back(i->tab_contents);
}

void BlockedContentContainer::Destroy() {
  for (BlockedContents::iterator i(blocked_contents_.begin());
       i != blocked_contents_.end(); ++i) {
    TabContents* tab_contents = i->tab_contents;
    tab_contents->set_delegate(NULL);
    delete tab_contents;
  }
  blocked_contents_.clear();
  owner_->WillCloseBlockedContentContainer(this);
  delete this;
}

// Overridden from TabContentsDelegate:
void BlockedContentContainer::OpenURLFromTab(TabContents* source,
                                             const GURL& url,
                                             const GURL& referrer,
                                             WindowOpenDisposition disposition,
                                             PageTransition::Type transition) {
  owner_->OpenURL(url, referrer, disposition, transition);
}

void BlockedContentContainer::AddNewContents(TabContents* source,
                                             TabContents* new_contents,
                                             WindowOpenDisposition disposition,
                                             const gfx::Rect& initial_position,
                                             bool user_gesture) {
  owner_->AddOrBlockNewContents(
      new_contents, disposition, initial_position, user_gesture);
}

void BlockedContentContainer::CloseContents(TabContents* source) {
  for (BlockedContents::iterator i(blocked_contents_.begin());
       i != blocked_contents_.end(); ++i) {
    TabContents* tab_contents = i->tab_contents;
    if (tab_contents == source) {
      tab_contents->set_delegate(NULL);
      blocked_contents_.erase(i);
      delete tab_contents;
      break;
    }
  }
}

void BlockedContentContainer::MoveContents(TabContents* source,
                                           const gfx::Rect& new_bounds) {
  for (BlockedContents::iterator i(blocked_contents_.begin());
       i != blocked_contents_.end(); ++i) {
    if (i->tab_contents == source) {
      i->bounds = new_bounds;
      break;
    }
  }
}

bool BlockedContentContainer::IsPopup(const TabContents* source) const {
  // Assume everything added is a popup. This may turn out to be wrong, but
  // callers don't cache this information so it should be fine if the value ends
  // up changing.
  return true;
}

TabContents* BlockedContentContainer::GetConstrainingContents(
    TabContents* source) {
  return owner_;
}