Home | History | Annotate | Download | only in frame_host
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/browser/frame_host/frame_tree.h"
      6 
      7 #include "base/run_loop.h"
      8 #include "base/strings/string_number_conversions.h"
      9 #include "content/browser/frame_host/navigator_impl.h"
     10 #include "content/browser/frame_host/render_frame_host_factory.h"
     11 #include "content/browser/frame_host/render_frame_host_impl.h"
     12 #include "content/browser/renderer_host/render_view_host_impl.h"
     13 #include "content/browser/web_contents/web_contents_impl.h"
     14 #include "content/common/view_messages.h"
     15 #include "content/public/browser/web_contents_observer.h"
     16 #include "content/public/test/mock_render_process_host.h"
     17 #include "content/public/test/test_browser_context.h"
     18 #include "content/public/test/test_browser_thread_bundle.h"
     19 #include "content/test/test_render_view_host.h"
     20 #include "content/test/test_web_contents.h"
     21 #include "testing/gtest/include/gtest/gtest.h"
     22 
     23 namespace content {
     24 namespace {
     25 
     26 // Appends a description of the structure of the frame tree to |result|.
     27 void AppendTreeNodeState(FrameTreeNode* node, std::string* result) {
     28   result->append(
     29       base::Int64ToString(node->current_frame_host()->GetRoutingID()));
     30   if (!node->frame_name().empty()) {
     31     result->append(" '");
     32     result->append(node->frame_name());
     33     result->append("'");
     34   }
     35   result->append(": [");
     36   const char* separator = "";
     37   for (size_t i = 0; i < node->child_count(); i++) {
     38     result->append(separator);
     39     AppendTreeNodeState(node->child_at(i), result);
     40     separator = ", ";
     41   }
     42   result->append("]");
     43 }
     44 
     45 // Logs calls to WebContentsObserver along with the state of the frame tree,
     46 // for later use in EXPECT_EQ().
     47 class TreeWalkingWebContentsLogger : public WebContentsObserver {
     48  public:
     49   explicit TreeWalkingWebContentsLogger(WebContents* web_contents)
     50       : WebContentsObserver(web_contents) {}
     51 
     52   virtual ~TreeWalkingWebContentsLogger() {
     53     EXPECT_EQ("", log_) << "Activity logged that was not expected";
     54   }
     55 
     56   // Gets and resets the log, which is a string of what happened.
     57   std::string GetLog() {
     58     std::string result = log_;
     59     log_.clear();
     60     return result;
     61   }
     62 
     63   // content::WebContentsObserver implementation.
     64   virtual void RenderFrameCreated(RenderFrameHost* render_frame_host) OVERRIDE {
     65     LogWhatHappened("RenderFrameCreated", render_frame_host);
     66   }
     67 
     68   virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE {
     69     LogWhatHappened("RenderFrameDeleted", render_frame_host);
     70   }
     71 
     72   virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE {
     73     LogWhatHappened("RenderProcessGone");
     74   }
     75 
     76  private:
     77   void LogWhatHappened(const std::string& event_name) {
     78     if (!log_.empty()) {
     79       log_.append("\n");
     80     }
     81     log_.append(event_name + " -> ");
     82     AppendTreeNodeState(
     83         static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root(),
     84         &log_);
     85   }
     86 
     87   void LogWhatHappened(const std::string& event_name, RenderFrameHost* rfh) {
     88     LogWhatHappened(
     89         base::StringPrintf("%s(%d)", event_name.c_str(), rfh->GetRoutingID()));
     90   }
     91 
     92   std::string log_;
     93 
     94   DISALLOW_COPY_AND_ASSIGN(TreeWalkingWebContentsLogger);
     95 };
     96 
     97 class FrameTreeTest : public RenderViewHostImplTestHarness {
     98  protected:
     99   // Prints a FrameTree, for easy assertions of the tree hierarchy.
    100   std::string GetTreeState(FrameTree* frame_tree) {
    101     std::string result;
    102     AppendTreeNodeState(frame_tree->root(), &result);
    103     return result;
    104   }
    105 };
    106 
    107 // Exercise tree manipulation routines.
    108 //  - Add a series of nodes and verify tree structure.
    109 //  - Remove a series of nodes and verify tree structure.
    110 TEST_F(FrameTreeTest, Shape) {
    111   // Use the FrameTree of the WebContents so that it has all the delegates it
    112   // needs.  We may want to consider a test version of this.
    113   FrameTree* frame_tree = contents()->GetFrameTree();
    114   FrameTreeNode* root = frame_tree->root();
    115 
    116   std::string no_children_node("no children node");
    117   std::string deep_subtree("node with deep subtree");
    118 
    119   ASSERT_EQ("1: []", GetTreeState(frame_tree));
    120 
    121   // Simulate attaching a series of frames to build the frame tree.
    122   frame_tree->AddFrame(root, 14, std::string());
    123   frame_tree->AddFrame(root, 15, std::string());
    124   frame_tree->AddFrame(root, 16, std::string());
    125 
    126   frame_tree->AddFrame(root->child_at(0), 244, std::string());
    127   frame_tree->AddFrame(root->child_at(1), 255, no_children_node);
    128   frame_tree->AddFrame(root->child_at(0), 245, std::string());
    129 
    130   ASSERT_EQ("1: [14: [244: [], 245: []], "
    131                 "15: [255 'no children node': []], "
    132                 "16: []]",
    133             GetTreeState(frame_tree));
    134 
    135   FrameTreeNode* child_16 = root->child_at(2);
    136   frame_tree->AddFrame(child_16, 264, std::string());
    137   frame_tree->AddFrame(child_16, 265, std::string());
    138   frame_tree->AddFrame(child_16, 266, std::string());
    139   frame_tree->AddFrame(child_16, 267, deep_subtree);
    140   frame_tree->AddFrame(child_16, 268, std::string());
    141 
    142   FrameTreeNode* child_267 = child_16->child_at(3);
    143   frame_tree->AddFrame(child_267, 365, std::string());
    144   frame_tree->AddFrame(child_267->child_at(0), 455, std::string());
    145   frame_tree->AddFrame(child_267->child_at(0)->child_at(0), 555, std::string());
    146   frame_tree->AddFrame(child_267->child_at(0)->child_at(0)->child_at(0), 655,
    147                        std::string());
    148 
    149   // Now that's it's fully built, verify the tree structure is as expected.
    150   ASSERT_EQ("1: [14: [244: [], 245: []], "
    151                 "15: [255 'no children node': []], "
    152                 "16: [264: [], 265: [], 266: [], "
    153                      "267 'node with deep subtree': "
    154                          "[365: [455: [555: [655: []]]]], 268: []]]",
    155             GetTreeState(frame_tree));
    156 
    157   FrameTreeNode* child_555 = child_267->child_at(0)->child_at(0)->child_at(0);
    158   frame_tree->RemoveFrame(child_555);
    159   ASSERT_EQ("1: [14: [244: [], 245: []], "
    160                 "15: [255 'no children node': []], "
    161                 "16: [264: [], 265: [], 266: [], "
    162                      "267 'node with deep subtree': "
    163                          "[365: [455: []]], 268: []]]",
    164             GetTreeState(frame_tree));
    165 
    166   frame_tree->RemoveFrame(child_16->child_at(1));
    167   ASSERT_EQ("1: [14: [244: [], 245: []], "
    168                 "15: [255 'no children node': []], "
    169                 "16: [264: [], 266: [], "
    170                      "267 'node with deep subtree': "
    171                          "[365: [455: []]], 268: []]]",
    172             GetTreeState(frame_tree));
    173 
    174   frame_tree->RemoveFrame(root->child_at(1));
    175   ASSERT_EQ("1: [14: [244: [], 245: []], "
    176                 "16: [264: [], 266: [], "
    177                      "267 'node with deep subtree': "
    178                          "[365: [455: []]], 268: []]]",
    179             GetTreeState(frame_tree));
    180 }
    181 
    182 // Do some simple manipulations of the frame tree, making sure that
    183 // WebContentsObservers see a consistent view of the tree as we go.
    184 TEST_F(FrameTreeTest, ObserverWalksTreeDuringFrameCreation) {
    185   TreeWalkingWebContentsLogger activity(contents());
    186   FrameTree* frame_tree = contents()->GetFrameTree();
    187   FrameTreeNode* root = frame_tree->root();
    188 
    189   // Simulate attaching a series of frames to build the frame tree.
    190   main_test_rfh()->OnCreateChildFrame(14, std::string());
    191   EXPECT_EQ("RenderFrameCreated(14) -> 1: [14: []]", activity.GetLog());
    192   main_test_rfh()->OnCreateChildFrame(18, std::string());
    193   EXPECT_EQ("RenderFrameCreated(18) -> 1: [14: [], 18: []]", activity.GetLog());
    194   frame_tree->RemoveFrame(root->child_at(0));
    195   EXPECT_EQ("RenderFrameDeleted(14) -> 1: [18: []]", activity.GetLog());
    196   frame_tree->RemoveFrame(root->child_at(0));
    197   EXPECT_EQ("RenderFrameDeleted(18) -> 1: []", activity.GetLog());
    198 }
    199 
    200 // Make sure that WebContentsObservers see a consistent view of the tree after
    201 // recovery from a render process crash.
    202 TEST_F(FrameTreeTest, ObserverWalksTreeAfterCrash) {
    203   TreeWalkingWebContentsLogger activity(contents());
    204 
    205   main_test_rfh()->OnCreateChildFrame(22, std::string());
    206   EXPECT_EQ("RenderFrameCreated(22) -> 1: [22: []]", activity.GetLog());
    207   main_test_rfh()->OnCreateChildFrame(23, std::string());
    208   EXPECT_EQ("RenderFrameCreated(23) -> 1: [22: [], 23: []]", activity.GetLog());
    209 
    210   // Crash the renderer
    211   test_rvh()->OnMessageReceived(ViewHostMsg_RenderProcessGone(
    212       0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
    213   EXPECT_EQ(
    214       "RenderFrameDeleted(22) -> 1: []\n"
    215       "RenderFrameDeleted(23) -> 1: []\n"
    216       "RenderProcessGone -> 1: []",
    217       activity.GetLog());
    218 }
    219 
    220 }  // namespace
    221 }  // namespace content
    222