Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / automation / automation_apitest.cc
1 // Copyright 2014 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 "base/files/file_path.h"
6 #include "base/path_service.h"
7 #include "base/strings/string_number_conversions.h"
8 #include "chrome/browser/extensions/api/automation_internal/automation_util.h"
9 #include "chrome/browser/extensions/chrome_extension_function.h"
10 #include "chrome/browser/extensions/extension_apitest.h"
11 #include "chrome/browser/ui/tabs/tab_strip_model.h"
12 #include "chrome/common/chrome_paths.h"
13 #include "chrome/common/chrome_switches.h"
14 #include "chrome/common/extensions/api/automation_internal.h"
15 #include "chrome/test/base/ui_test_utils.h"
16 #include "content/public/browser/ax_event_notification_details.h"
17 #include "content/public/browser/render_widget_host.h"
18 #include "content/public/browser/render_widget_host_view.h"
19 #include "content/public/browser/web_contents.h"
20 #include "extensions/test/extension_test_message_listener.h"
21 #include "net/dns/mock_host_resolver.h"
22 #include "net/test/embedded_test_server/embedded_test_server.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24 #include "ui/accessibility/ax_node.h"
25 #include "ui/accessibility/ax_serializable_tree.h"
26 #include "ui/accessibility/ax_tree.h"
27 #include "ui/accessibility/ax_tree_serializer.h"
28 #include "ui/accessibility/tree_generator.h"
29
30 namespace extensions {
31
32 namespace {
33 static const char kDomain[] = "a.com";
34 static const char kSitesDir[] = "automation/sites";
35 static const char kGotTree[] = "got_tree";
36 }  // anonymous namespace
37
38 class AutomationApiTest : public ExtensionApiTest {
39  protected:
40   GURL GetURLForPath(const std::string& host, const std::string& path) {
41     std::string port = base::IntToString(embedded_test_server()->port());
42     GURL::Replacements replacements;
43     replacements.SetHostStr(host);
44     replacements.SetPortStr(port);
45     GURL url =
46         embedded_test_server()->GetURL(path).ReplaceComponents(replacements);
47     return url;
48   }
49
50   void StartEmbeddedTestServer() {
51     base::FilePath test_data;
52     ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data));
53     embedded_test_server()->ServeFilesFromDirectory(
54         test_data.AppendASCII("extensions/api_test")
55         .AppendASCII(kSitesDir));
56     ASSERT_TRUE(ExtensionApiTest::StartEmbeddedTestServer());
57     host_resolver()->AddRule("*", embedded_test_server()->base_url().host());
58   }
59
60  public:
61   virtual void SetUpInProcessBrowserTestFixture() override {
62     ExtensionApiTest::SetUpInProcessBrowserTestFixture();
63   }
64 };
65
66 IN_PROC_BROWSER_TEST_F(AutomationApiTest, TestRendererAccessibilityEnabled) {
67   StartEmbeddedTestServer();
68   const GURL url = GetURLForPath(kDomain, "/index.html");
69   ui_test_utils::NavigateToURL(browser(), url);
70
71   ASSERT_EQ(1, browser()->tab_strip_model()->count());
72   content::WebContents* const tab =
73       browser()->tab_strip_model()->GetWebContentsAt(0);
74   ASSERT_FALSE(tab->IsFullAccessibilityModeForTesting());
75   ASSERT_FALSE(tab->IsTreeOnlyAccessibilityModeForTesting());
76
77   base::FilePath extension_path =
78       test_data_dir_.AppendASCII("automation/tests/basic");
79   ExtensionTestMessageListener got_tree(kGotTree, false /* no reply */);
80   LoadExtension(extension_path);
81   ASSERT_TRUE(got_tree.WaitUntilSatisfied());
82
83   ASSERT_FALSE(tab->IsFullAccessibilityModeForTesting());
84   ASSERT_TRUE(tab->IsTreeOnlyAccessibilityModeForTesting());
85 }
86
87 IN_PROC_BROWSER_TEST_F(AutomationApiTest, SanityCheck) {
88   StartEmbeddedTestServer();
89   ASSERT_TRUE(RunExtensionSubtest("automation/tests/tabs", "sanity_check.html"))
90       << message_;
91 }
92
93 IN_PROC_BROWSER_TEST_F(AutomationApiTest, Unit) {
94   ASSERT_TRUE(RunExtensionSubtest("automation/tests/unit", "unit.html"))
95       << message_;
96 }
97
98 IN_PROC_BROWSER_TEST_F(AutomationApiTest, GetTreeByTabId) {
99   StartEmbeddedTestServer();
100   ASSERT_TRUE(RunExtensionSubtest("automation/tests/tabs", "tab_id.html"))
101       << message_;
102 }
103
104 IN_PROC_BROWSER_TEST_F(AutomationApiTest, Events) {
105   StartEmbeddedTestServer();
106   ASSERT_TRUE(RunExtensionSubtest("automation/tests/tabs", "events.html"))
107       << message_;
108 }
109
110 IN_PROC_BROWSER_TEST_F(AutomationApiTest, Actions) {
111   StartEmbeddedTestServer();
112   ASSERT_TRUE(RunExtensionSubtest("automation/tests/tabs", "actions.html"))
113       << message_;
114 }
115
116 IN_PROC_BROWSER_TEST_F(AutomationApiTest, Location) {
117   StartEmbeddedTestServer();
118   ASSERT_TRUE(RunExtensionSubtest("automation/tests/tabs", "location.html"))
119       << message_;
120 }
121
122 IN_PROC_BROWSER_TEST_F(AutomationApiTest, TabsAutomationBooleanPermissions) {
123   StartEmbeddedTestServer();
124   ASSERT_TRUE(RunExtensionSubtest(
125           "automation/tests/tabs_automation_boolean", "permissions.html"))
126       << message_;
127 }
128
129 IN_PROC_BROWSER_TEST_F(AutomationApiTest, TabsAutomationBooleanActions) {
130   StartEmbeddedTestServer();
131   ASSERT_TRUE(RunExtensionSubtest(
132           "automation/tests/tabs_automation_boolean", "actions.html"))
133       << message_;
134 }
135
136 IN_PROC_BROWSER_TEST_F(AutomationApiTest, TabsAutomationHostsPermissions) {
137   StartEmbeddedTestServer();
138   ASSERT_TRUE(RunExtensionSubtest(
139           "automation/tests/tabs_automation_hosts", "permissions.html"))
140       << message_;
141 }
142
143 #if defined(OS_CHROMEOS)
144 IN_PROC_BROWSER_TEST_F(AutomationApiTest, Desktop) {
145   ASSERT_TRUE(RunExtensionSubtest("automation/tests/desktop", "desktop.html"))
146       << message_;
147 }
148
149 IN_PROC_BROWSER_TEST_F(AutomationApiTest, DesktopNotRequested) {
150   ASSERT_TRUE(RunExtensionSubtest("automation/tests/tabs",
151                                   "desktop_not_requested.html")) << message_;
152 }
153
154 IN_PROC_BROWSER_TEST_F(AutomationApiTest, DesktopActions) {
155   ASSERT_TRUE(RunExtensionSubtest("automation/tests/desktop", "actions.html"))
156       << message_;
157 }
158
159 IN_PROC_BROWSER_TEST_F(AutomationApiTest, DesktopLoadTabs) {
160   ASSERT_TRUE(RunExtensionSubtest("automation/tests/desktop", "load_tabs.html"))
161       << message_;
162 }
163 #else
164 IN_PROC_BROWSER_TEST_F(AutomationApiTest, DesktopNotSupported) {
165   ASSERT_TRUE(RunExtensionSubtest("automation/tests/desktop",
166                                   "desktop_not_supported.html")) << message_;
167 }
168 #endif
169
170 IN_PROC_BROWSER_TEST_F(AutomationApiTest, CloseTab) {
171   StartEmbeddedTestServer();
172   ASSERT_TRUE(RunExtensionSubtest("automation/tests/tabs", "close_tab.html"))
173       << message_;
174 }
175
176 IN_PROC_BROWSER_TEST_F(AutomationApiTest, QuerySelector) {
177   StartEmbeddedTestServer();
178   ASSERT_TRUE(
179       RunExtensionSubtest("automation/tests/tabs", "queryselector.html"))
180       << message_;
181 }
182
183 static const int kPid = 1;
184 static const int kTab0Rid = 1;
185 static const int kTab1Rid = 2;
186
187 using content::BrowserContext;
188
189 typedef ui::AXTreeSerializer<const ui::AXNode*> TreeSerializer;
190 typedef ui::AXTreeSource<const ui::AXNode*> TreeSource;
191
192 #define AX_EVENT_ASSERT_EQUAL ui::AX_EVENT_LOAD_COMPLETE
193 #define AX_EVENT_ASSERT_NOT_EQUAL ui::AX_EVENT_ACTIVEDESCENDANTCHANGED
194 #define AX_EVENT_IGNORE ui::AX_EVENT_CHILDREN_CHANGED
195 #define AX_EVENT_TEST_COMPLETE ui::AX_EVENT_BLUR
196
197 // This test is based on ui/accessibility/ax_generated_tree_unittest.cc
198 // However, because the tree updates need to be sent to the extension, we can't
199 // use a straightforward set of nested loops as that test does, so this class
200 // keeps track of where we're up to in our imaginary loops, while the extension
201 // function classes below do the work of actually incrementing the state when
202 // appropriate.
203 // The actual deserialization and comparison happens in the API bindings and the
204 // test extension respectively: see
205 // c/t/data/extensions/api_test/automation/tests/generated/generated_trees.js
206 class TreeSerializationState {
207  public:
208   TreeSerializationState()
209 #ifdef NDEBUG
210       : tree_size(3),
211 #else
212       : tree_size(2),
213 #endif
214         generator(tree_size),
215         num_trees(generator.UniqueTreeCount()),
216         tree0_version(0),
217         tree1_version(0) {
218   }
219
220   // Serializes tree and sends it as an accessibility event to the extension.
221   void SendDataForTree(const ui::AXTree* tree,
222                        TreeSerializer* serializer,
223                        int routing_id,
224                        BrowserContext* browser_context) {
225     ui::AXTreeUpdate update;
226     serializer->SerializeChanges(tree->GetRoot(), &update);
227     SendUpdate(update,
228                ui::AX_EVENT_LAYOUT_COMPLETE,
229                tree->GetRoot()->id(),
230                routing_id,
231                browser_context);
232   }
233
234   // Sends the given AXTreeUpdate to the extension as an accessibility event.
235   void SendUpdate(ui::AXTreeUpdate update,
236                   ui::AXEvent event,
237                   int node_id,
238                   int routing_id,
239                   BrowserContext* browser_context) {
240     content::AXEventNotificationDetails detail(update.node_id_to_clear,
241                                                update.nodes,
242                                                event,
243                                                node_id,
244                                                kPid,
245                                                routing_id);
246     std::vector<content::AXEventNotificationDetails> details;
247     details.push_back(detail);
248     automation_util::DispatchAccessibilityEventsToAutomation(
249         details, browser_context, gfx::Vector2d());
250   }
251
252   // Notify the extension bindings to destroy the tree for the given tab
253   // (identified by routing_id)
254   void SendTreeDestroyedEvent(int routing_id, BrowserContext* browser_context) {
255     automation_util::DispatchTreeDestroyedEventToAutomation(
256         kPid, routing_id, browser_context);
257   }
258
259   // Reset tree0 to a new generated tree based on tree0_version, reset
260   // tree0_source accordingly.
261   void ResetTree0() {
262     tree0.reset(new ui::AXSerializableTree);
263     tree0_source.reset(tree0->CreateTreeSource());
264     generator.BuildUniqueTree(tree0_version, tree0.get());
265     if (!serializer0.get())
266       serializer0.reset(new TreeSerializer(tree0_source.get()));
267   }
268
269   // Reset tree0, set up serializer0, send down the initial tree data to create
270   // the tree in the extension
271   void InitializeTree0(BrowserContext* browser_context) {
272     ResetTree0();
273     serializer0->ChangeTreeSourceForTesting(tree0_source.get());
274     serializer0->Reset();
275     SendDataForTree(tree0.get(), serializer0.get(), kTab0Rid, browser_context);
276   }
277
278   // Reset tree1 to a new generated tree based on tree1_version, reset
279   // tree1_source accordingly.
280   void ResetTree1() {
281     tree1.reset(new ui::AXSerializableTree);
282     tree1_source.reset(tree1->CreateTreeSource());
283     generator.BuildUniqueTree(tree1_version, tree1.get());
284     if (!serializer1.get())
285       serializer1.reset(new TreeSerializer(tree1_source.get()));
286   }
287
288   // Reset tree1, set up serializer1, send down the initial tree data to create
289   // the tree in the extension
290   void InitializeTree1(BrowserContext* browser_context) {
291     ResetTree1();
292     serializer1->ChangeTreeSourceForTesting(tree1_source.get());
293     serializer1->Reset();
294     SendDataForTree(tree1.get(), serializer1.get(), kTab1Rid, browser_context);
295   }
296
297   const int tree_size;
298   const ui::TreeGenerator generator;
299
300   // The loop variables: comments indicate which variables in
301   // ax_generated_tree_unittest they correspond to.
302   const int num_trees; // n
303   int tree0_version;   // i
304   int tree1_version;   // j
305   int starting_node;   // k
306
307   // Tree infrastructure; tree0 and tree1 need to be regenerated whenever
308   // tree0_version and tree1_version change, respectively; tree0_source and
309   // tree1_source need to be reset whenever that happens.
310   scoped_ptr<ui::AXSerializableTree> tree0, tree1;
311   scoped_ptr<TreeSource> tree0_source, tree1_source;
312   scoped_ptr<TreeSerializer> serializer0, serializer1;
313
314   // Whether tree0 needs to be destroyed after the extension has performed its
315   // checks
316   bool destroy_tree0;
317 };
318
319 static TreeSerializationState state;
320
321 // Override for chrome.automationInternal.enableTab
322 // This fakes out the process and routing IDs for two "tabs", which contain the
323 // source and target trees, respectively, and sends down the current tree for
324 // the requested tab - tab 1 always has tree1, and tab 0 starts with tree0
325 // and then has a series of updates intended to translate tree0 to tree1.
326 // Once all the updates have been sent, the extension asserts that both trees
327 // are equivalent, and then one or both of the trees are reset to a new version.
328 class FakeAutomationInternalEnableTabFunction
329     : public UIThreadExtensionFunction {
330  public:
331   FakeAutomationInternalEnableTabFunction() {}
332
333   ExtensionFunction::ResponseAction Run() override {
334     using api::automation_internal::EnableTab::Params;
335     scoped_ptr<Params> params(Params::Create(*args_));
336     EXTENSION_FUNCTION_VALIDATE(params.get());
337     if (!params->tab_id.get())
338       return RespondNow(Error("tab_id not specified"));
339     int tab_id = *params->tab_id;
340     if (tab_id == 0) {
341       // tab 0 <--> tree0
342       base::MessageLoop::current()->PostTask(
343           FROM_HERE,
344           base::Bind(&TreeSerializationState::InitializeTree0,
345                      base::Unretained(&state),
346                      base::Unretained(browser_context())));
347       // TODO(aboxhall): Need to rewrite this test in terms of tree ids.
348       return RespondNow(ArgumentList(
349           api::automation_internal::EnableTab::Results::Create(0)));
350     }
351     if (tab_id == 1) {
352       // tab 1 <--> tree1
353       base::MessageLoop::current()->PostTask(
354           FROM_HERE,
355           base::Bind(&TreeSerializationState::InitializeTree1,
356                      base::Unretained(&state),
357                      base::Unretained(browser_context())));
358       return RespondNow(ArgumentList(
359           api::automation_internal::EnableTab::Results::Create(0)));
360     }
361     return RespondNow(Error("Unrecognised tab_id"));
362   }
363 };
364
365 // Factory method for use in OverrideFunction()
366 ExtensionFunction* FakeAutomationInternalEnableTabFunctionFactory() {
367   return new FakeAutomationInternalEnableTabFunction();
368 }
369
370 // Helper method to serialize a series of updates via source_serializer to
371 // transform the tree which source_serializer was initialized from into
372 // target_tree, and then trigger the test code to assert the two tabs contain
373 // the same tree.
374 void TransformTree(TreeSerializer* source_serializer,
375                    ui::AXTree* target_tree,
376                    TreeSource* target_tree_source,
377                    content::BrowserContext* browser_context) {
378   source_serializer->ChangeTreeSourceForTesting(target_tree_source);
379   for (int node_delta = 0; node_delta < state.tree_size; ++node_delta) {
380     int id = 1 + (state.starting_node + node_delta) % state.tree_size;
381     ui::AXTreeUpdate update;
382     source_serializer->SerializeChanges(target_tree->GetFromId(id), &update);
383     bool is_last_update = node_delta == state.tree_size - 1;
384     ui::AXEvent event =
385         is_last_update ? AX_EVENT_ASSERT_EQUAL : AX_EVENT_IGNORE;
386     state.SendUpdate(
387         update, event, target_tree->GetRoot()->id(), kTab0Rid, browser_context);
388   }
389 }
390
391 // Helper method to send a no-op tree update to tab 0 with the given event.
392 void SendEvent(ui::AXEvent event, content::BrowserContext* browser_context) {
393   ui::AXTreeUpdate update;
394   ui::AXNode* root = state.tree0->GetRoot();
395   state.serializer0->SerializeChanges(root, &update);
396   state.SendUpdate(update, event, root->id(), kTab0Rid, browser_context);
397 }
398
399 // Override for chrome.automationInternal.performAction
400 // This is used as a synchronization mechanism; the general flow is:
401 // 1. The extension requests tree0 and tree1 (on tab 0 and tab 1 respectively)
402 // 2. FakeAutomationInternalEnableTabFunction sends down the trees
403 // 3. When the callback for getTree(0) fires, the extension calls doDefault() on
404 //    the root node of tree0, which calls into this class's Run() method.
405 // 4. In the normal case, we're in the "inner loop" (iterating over
406 //    starting_node). For each value of starting_node, we do the following:
407 //    a. Serialize a sequence of updates which should transform tree0 into
408 //       tree1. Each of these updates is sent as a childrenChanged event,
409 //       except for the last which is sent as a loadComplete event.
410 //    b. state.destroy_tree0 is set to true
411 //    c. state.starting_node gets incremented
412 //    d. The loadComplete event triggers an assertion in the extension.
413 //    e. The extension performs another doDefault() on the root node of the
414 //       tree.
415 //    f. This time, we send a destroy event to tab0, so that the tree can be
416 //       reset.
417 //    g. The extension is notified of the tree's destruction and requests the
418 //       tree for tab 0 again, returning to step 2.
419 // 5. When starting_node exceeds state.tree_size, we increment tree0_version if
420 //    it would not exceed state.num_trees, or increment tree1_version and reset
421 //    tree0_version to 0 otherwise, and reset starting_node to 0.
422 //    Then we reset one or both trees as appropriate, and send down destroyed
423 //    events similarly, causing the extension to re-request the tree and going
424 //    back to step 2 again.
425 // 6. When tree1_version has gone through all possible values, we send a blur
426 //    event, signaling the extension to call chrome.test.succeed() and finish
427 //    the test.
428 class FakeAutomationInternalPerformActionFunction
429     : public UIThreadExtensionFunction {
430  public:
431   FakeAutomationInternalPerformActionFunction() {}
432
433   ExtensionFunction::ResponseAction Run() override {
434     if (state.destroy_tree0) {
435       // Step 4.f: tell the extension to destroy the tree and re-request it.
436       state.SendTreeDestroyedEvent(kTab0Rid, browser_context());
437       state.destroy_tree0 = false;
438       return RespondNow(NoArguments());
439     }
440
441     TreeSerializer* serializer0 = state.serializer0.get();
442     if (state.starting_node < state.tree_size) {
443       // As a sanity check, if the trees are not equal, assert that they are not
444       // equal before serializing changes.
445       if (state.tree0_version != state.tree1_version)
446         SendEvent(AX_EVENT_ASSERT_NOT_EQUAL, browser_context());
447
448       // Step 4.a: pretend that tree0 turned into tree1, and serialize
449       // a sequence of updates to tab 0 to match.
450       TransformTree(serializer0,
451                     state.tree1.get(),
452                     state.tree1_source.get(),
453                     browser_context());
454
455       // Step 4.b: remember that we need to tell the extension to destroy and
456       // re-request the tree on the next action.
457       state.destroy_tree0 = true;
458
459       // Step 4.c: increment starting_node.
460       state.starting_node++;
461     } else if (state.tree0_version < state.num_trees - 1) {
462       // Step 5: Increment tree0_version and reset starting_node
463       state.tree0_version++;
464       state.starting_node = 0;
465
466       // Step 5: Reset tree0 and tell the extension to destroy and re-request it
467       state.SendTreeDestroyedEvent(kTab0Rid, browser_context());
468     } else if (state.tree1_version < state.num_trees - 1) {
469       // Step 5: Increment tree1_version and reset tree0_version and
470       // starting_node
471       state.tree1_version++;
472       state.tree0_version = 0;
473       state.starting_node = 0;
474
475       // Step 5: Reset tree0 and tell the extension to destroy and re-request it
476       state.SendTreeDestroyedEvent(kTab0Rid, browser_context());
477
478       // Step 5: Reset tree1 and tell the extension to destroy and re-request it
479       state.SendTreeDestroyedEvent(kTab1Rid, browser_context());
480     } else {
481       // Step 6: Send a TEST_COMPLETE (blur) event to signal the extension to
482       // call chrome.test.succeed().
483       SendEvent(AX_EVENT_TEST_COMPLETE, browser_context());
484     }
485
486     return RespondNow(NoArguments());
487   }
488 };
489
490 // Factory method for use in OverrideFunction()
491 ExtensionFunction* FakeAutomationInternalPerformActionFunctionFactory() {
492   return new FakeAutomationInternalPerformActionFunction();
493 }
494
495 // http://crbug.com/396353
496 IN_PROC_BROWSER_TEST_F(AutomationApiTest, DISABLED_GeneratedTree) {
497   ASSERT_TRUE(extensions::ExtensionFunctionDispatcher::OverrideFunction(
498       "automationInternal.enableTab",
499       FakeAutomationInternalEnableTabFunctionFactory));
500   ASSERT_TRUE(extensions::ExtensionFunctionDispatcher::OverrideFunction(
501       "automationInternal.performAction",
502       FakeAutomationInternalPerformActionFunctionFactory));
503   ASSERT_TRUE(RunExtensionSubtest("automation/tests/generated",
504                                   "generated_trees.html")) << message_;
505 }
506
507 }  // namespace extensions