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

/*
 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved.
 *     (http://www.torchmobile.com/)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
 *     its contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS CONTRIBUTORS 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 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef CONTENT_RENDERER_HISTORY_CONTROLLER_H_
#define CONTENT_RENDERER_HISTORY_CONTROLLER_H_

#include <utility>

#include "base/containers/hash_tables.h"
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "content/common/content_export.h"
#include "content/renderer/history_entry.h"
#include "third_party/WebKit/public/platform/WebURLRequest.h"
#include "third_party/WebKit/public/web/WebHistoryCommitType.h"
#include "third_party/WebKit/public/web/WebHistoryItem.h"

namespace blink {
class WebFrame;
class WebLocalFrame;
}

namespace content {
class RenderFrameImpl;
class RenderViewImpl;
struct NavigationParams;

// A guide to history state in the renderer:
//
// HistoryController: Owned by RenderView, is the entry point for interacting
//     with history. Handles most of the operations to modify history state,
//     navigate to an existing back/forward entry, etc.
//
// HistoryEntry: Represents a single entry in the back/forward list,
//     encapsulating all frames in the page it represents. It provides access
//     to each frame's state via lookups by frame id or frame name.
// HistoryNode: Represents a single frame in a HistoryEntry. Owned by a
//     HistoryEntry. HistoryNodes form a tree that mirrors the frame tree in
//     the corresponding page.
// HistoryNodes represent the structure of the page, but don't hold any
// per-frame state except a list of child frames.
// WebHistoryItem (lives in blink): The state for a given frame. Can persist
//     across navigations. WebHistoryItem is reference counted, and each
//     HistoryNode holds a reference to its single corresponding
//     WebHistoryItem. Can be referenced by multiple HistoryNodes and can
//     therefore exist in multiple HistoryEntry instances.
//
// Suppose we have the following page, foo.com, which embeds foo.com/a in an
// iframe:
//
// HistoryEntry 0:
//     HistoryNode 0_0 (WebHistoryItem A (url: foo.com))
//         HistoryNode 0_1: (WebHistoryItem B (url: foo.com/a))
//
// Now we navigate the top frame to bar.com, which embeds bar.com/b and
// bar.com/c in iframes, and bar.com/b in turn embeds bar.com/d. We will
// create a new HistoryEntry with a tree containing 4 new HistoryNodes. The
// state will be:
//
// HistoryEntry 1:
//     HistoryNode 1_0 (WebHistoryItem C (url: bar.com))
//         HistoryNode 1_1: (WebHistoryItem D (url: bar.com/b))
//             HistoryNode 1_3: (WebHistoryItem F (url: bar.com/d))
//         HistoryNode 1_2: (WebHistoryItem E (url: bar.com/c))
//
//
// Finally, we navigate the first subframe from bar.com/b to bar.com/e, which
// embeds bar.com/f. We will create a new HistoryEntry and new HistoryNode for
// each frame. Any frame that navigates (bar.com/e and its child, bar.com/f)
// will receive a new WebHistoryItem. However, 2 frames were not navigated
// (bar.com and bar.com/c), so those two frames will reuse the existing
// WebHistoryItem:
//
// HistoryEntry 2:
//     HistoryNode 2_0 (WebHistoryItem C (url: bar.com))  *REUSED*
//         HistoryNode 2_1: (WebHistoryItem G (url: bar.com/e))
//            HistoryNode 2_3: (WebHistoryItem H (url: bar.com/f))
//         HistoryNode 2_2: (WebHistoryItem E (url: bar.com/c)) *REUSED*
//
class CONTENT_EXPORT HistoryController {
 public:
  explicit HistoryController(RenderViewImpl* render_view);
  ~HistoryController();

  void set_provisional_entry(scoped_ptr<HistoryEntry> entry) {
    provisional_entry_ = std::move(entry);
  }

  void GoToEntry(blink::WebLocalFrame* main_frame,
                 scoped_ptr<HistoryEntry> entry,
                 scoped_ptr<NavigationParams> navigation_params,
                 blink::WebURLRequest::CachePolicy cache_policy);

  void UpdateForCommit(RenderFrameImpl* frame,
                       const blink::WebHistoryItem& item,
                       blink::WebHistoryCommitType commit_type,
                       bool navigation_within_page);

  HistoryEntry* GetCurrentEntry();
  blink::WebHistoryItem GetItemForNewChildFrame(RenderFrameImpl* frame) const;
  void RemoveChildrenForRedirect(RenderFrameImpl* frame);

 private:
  typedef std::vector<std::pair<blink::WebFrame*, blink::WebHistoryItem> >
      HistoryFrameLoadVector;
  void RecursiveGoToEntry(blink::WebFrame* frame,
                          HistoryFrameLoadVector& sameDocumentLoads,
                          HistoryFrameLoadVector& differentDocumentLoads);

  void UpdateForInitialLoadInChildFrame(RenderFrameImpl* frame,
                                        const blink::WebHistoryItem& item);
  void CreateNewBackForwardItem(RenderFrameImpl* frame,
                                const blink::WebHistoryItem& item,
                                bool clone_children_of_target);

  RenderViewImpl* render_view_;

  // A HistoryEntry representing the currently-loaded page.
  scoped_ptr<HistoryEntry> current_entry_;
  // A HistoryEntry representing the page that is being loaded, or an empty
  // scoped_ptr if no page is being loaded.
  scoped_ptr<HistoryEntry> provisional_entry_;

  // The NavigationParams corresponding to the last back/forward load that was
  // initiated by |GoToEntry|. This is kept around so that it can be passed into
  // existing frames affected by a history navigation in GoToEntry(), and can be
  // passed into frames created after the commit that resulted from the
  // navigation in GetItemForNewChildFrame().
  //
  // This is reset in UpdateForCommit if we see a commit from a different
  // navigation, to avoid using stale parameters.
  scoped_ptr<NavigationParams> navigation_params_;

  DISALLOW_COPY_AND_ASSIGN(HistoryController);
};

}  // namespace content

#endif  // CONTENT_RENDERER_HISTORY_CONTROLLER_H_
