Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2012 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/strings/stringprintf.h"
      6 #include "base/strings/utf_string_conversions.h"
      7 #include "chrome/browser/background/background_contents_service.h"
      8 #include "chrome/browser/background/background_contents_service_factory.h"
      9 #include "chrome/browser/background/background_mode_manager.h"
     10 #include "chrome/browser/browser_process.h"
     11 #include "chrome/browser/chrome_notification_types.h"
     12 #include "chrome/browser/extensions/extension_apitest.h"
     13 #include "chrome/browser/extensions/extension_service.h"
     14 #include "chrome/browser/profiles/profile.h"
     15 #include "chrome/browser/ui/browser.h"
     16 #include "chrome/browser/ui/browser_window.h"
     17 #include "chrome/common/chrome_switches.h"
     18 #include "chrome/common/extensions/extension.h"
     19 #include "content/public/browser/notification_service.h"
     20 #include "content/public/test/test_notification_tracker.h"
     21 #include "content/public/test/test_utils.h"
     22 #include "net/dns/mock_host_resolver.h"
     23 #include "net/test/embedded_test_server/embedded_test_server.h"
     24 
     25 #if defined(OS_MACOSX)
     26 #include "base/mac/scoped_nsautorelease_pool.h"
     27 #endif
     28 
     29 using extensions::Extension;
     30 
     31 class AppBackgroundPageApiTest : public ExtensionApiTest {
     32  public:
     33   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
     34     ExtensionApiTest::SetUpCommandLine(command_line);
     35     command_line->AppendSwitch(switches::kDisablePopupBlocking);
     36     command_line->AppendSwitch(switches::kAllowHTTPBackgroundPage);
     37   }
     38 
     39   bool CreateApp(const std::string& app_manifest,
     40                  base::FilePath* app_dir) {
     41     if (!app_dir_.CreateUniqueTempDir()) {
     42       LOG(ERROR) << "Unable to create a temporary directory.";
     43       return false;
     44     }
     45     base::FilePath manifest_path = app_dir_.path().AppendASCII("manifest.json");
     46     int bytes_written = file_util::WriteFile(manifest_path,
     47                                              app_manifest.data(),
     48                                              app_manifest.size());
     49     if (bytes_written != static_cast<int>(app_manifest.size())) {
     50       LOG(ERROR) << "Unable to write complete manifest to file. Return code="
     51                  << bytes_written;
     52       return false;
     53     }
     54     *app_dir = app_dir_.path();
     55     return true;
     56   }
     57 
     58   bool WaitForBackgroundMode(bool expected_background_mode) {
     59 #if defined(OS_CHROMEOS)
     60     // BackgroundMode is not supported on chromeos, so we should test the
     61     // behavior of BackgroundContents, but not the background mode state itself.
     62     return true;
     63 #else
     64     BackgroundModeManager* manager =
     65         g_browser_process->background_mode_manager();
     66     // If background mode is disabled on this platform (e.g. cros), then skip
     67     // this check.
     68     if (!manager || !manager->IsBackgroundModePrefEnabled()) {
     69       DLOG(WARNING) << "Skipping check - background mode disabled";
     70       return true;
     71     }
     72     if (manager->IsBackgroundModeActiveForTest() == expected_background_mode)
     73       return true;
     74 
     75     // We are not currently in the expected state - wait for the state to
     76     // change.
     77     content::WindowedNotificationObserver watcher(
     78         chrome::NOTIFICATION_BACKGROUND_MODE_CHANGED,
     79         content::NotificationService::AllSources());
     80     watcher.Wait();
     81     return manager->IsBackgroundModeActiveForTest() == expected_background_mode;
     82 #endif
     83   }
     84 
     85   void CloseBrowser(Browser* browser) {
     86     content::WindowedNotificationObserver observer(
     87         chrome::NOTIFICATION_BROWSER_CLOSED,
     88         content::NotificationService::AllSources());
     89     browser->window()->Close();
     90 #if defined(OS_MACOSX)
     91     // BrowserWindowController depends on the auto release pool being recycled
     92     // in the message loop to delete itself, which frees the Browser object
     93     // which fires this event.
     94     AutoreleasePool()->Recycle();
     95 #endif
     96     observer.Wait();
     97   }
     98 
     99   void UnloadExtensionViaTask(const std::string& id) {
    100     base::MessageLoop::current()->PostTask(
    101         FROM_HERE,
    102         base::Bind(&AppBackgroundPageApiTest::UnloadExtension, this, id));
    103   }
    104 
    105  private:
    106   base::ScopedTempDir app_dir_;
    107 };
    108 
    109 // Disable on Mac only.  http://crbug.com/95139
    110 #if defined(OS_MACOSX)
    111 #define MAYBE_Basic DISABLED_Basic
    112 #else
    113 #define MAYBE_Basic Basic
    114 #endif
    115 
    116 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, MAYBE_Basic) {
    117   host_resolver()->AddRule("a.com", "127.0.0.1");
    118   ASSERT_TRUE(StartEmbeddedTestServer());
    119 
    120   std::string app_manifest = base::StringPrintf(
    121       "{"
    122       "  \"name\": \"App\","
    123       "  \"version\": \"0.1\","
    124       "  \"manifest_version\": 2,"
    125       "  \"app\": {"
    126       "    \"urls\": ["
    127       "      \"http://a.com/\""
    128       "    ],"
    129       "    \"launch\": {"
    130       "      \"web_url\": \"http://a.com:%d/\""
    131       "    }"
    132       "  },"
    133       "  \"permissions\": [\"background\"]"
    134       "}",
    135       embedded_test_server()->port());
    136 
    137   base::FilePath app_dir;
    138   ASSERT_TRUE(CreateApp(app_manifest, &app_dir));
    139   ASSERT_TRUE(LoadExtension(app_dir));
    140   // Background mode should not be active until a background page is created.
    141   ASSERT_TRUE(WaitForBackgroundMode(false));
    142   ASSERT_TRUE(RunExtensionTest("app_background_page/basic")) << message_;
    143   // The test closes the background contents, so we should fall back to no
    144   // background mode at the end.
    145   ASSERT_TRUE(WaitForBackgroundMode(false));
    146 }
    147 
    148 // Crashy, http://crbug.com/69215.
    149 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, DISABLED_LacksPermission) {
    150   host_resolver()->AddRule("a.com", "127.0.0.1");
    151   ASSERT_TRUE(StartEmbeddedTestServer());
    152 
    153   std::string app_manifest = base::StringPrintf(
    154       "{"
    155       "  \"name\": \"App\","
    156       "  \"version\": \"0.1\","
    157       "  \"manifest_version\": 2,"
    158       "  \"app\": {"
    159       "    \"urls\": ["
    160       "      \"http://a.com/\""
    161       "    ],"
    162       "    \"launch\": {"
    163       "      \"web_url\": \"http://a.com:%d/\""
    164       "    }"
    165       "  }"
    166       "}",
    167       embedded_test_server()->port());
    168 
    169   base::FilePath app_dir;
    170   ASSERT_TRUE(CreateApp(app_manifest, &app_dir));
    171   ASSERT_TRUE(LoadExtension(app_dir));
    172   ASSERT_TRUE(RunExtensionTest("app_background_page/lacks_permission"))
    173       << message_;
    174   ASSERT_TRUE(WaitForBackgroundMode(false));
    175 }
    176 
    177 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, ManifestBackgroundPage) {
    178   host_resolver()->AddRule("a.com", "127.0.0.1");
    179   ASSERT_TRUE(StartEmbeddedTestServer());
    180 
    181   std::string app_manifest = base::StringPrintf(
    182       "{"
    183       "  \"name\": \"App\","
    184       "  \"version\": \"0.1\","
    185       "  \"manifest_version\": 2,"
    186       "  \"app\": {"
    187       "    \"urls\": ["
    188       "      \"http://a.com/\""
    189       "    ],"
    190       "    \"launch\": {"
    191       "      \"web_url\": \"http://a.com:%d/\""
    192       "    }"
    193       "  },"
    194       "  \"permissions\": [\"background\"],"
    195       "  \"background\": {"
    196       "    \"page\": \"http://a.com:%d/test.html\""
    197       "  }"
    198       "}",
    199       embedded_test_server()->port(),
    200       embedded_test_server()->port());
    201 
    202   base::FilePath app_dir;
    203   ASSERT_TRUE(CreateApp(app_manifest, &app_dir));
    204   // Background mode should not be active now because no background app was
    205   // loaded.
    206   ASSERT_TRUE(LoadExtension(app_dir));
    207   // Background mode be active now because a background page was created when
    208   // the app was loaded.
    209   ASSERT_TRUE(WaitForBackgroundMode(true));
    210 
    211   const Extension* extension = GetSingleLoadedExtension();
    212   ASSERT_TRUE(
    213       BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
    214           GetAppBackgroundContents(ASCIIToUTF16(extension->id())));
    215   UnloadExtension(extension->id());
    216 }
    217 
    218 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, NoJsBackgroundPage) {
    219   // Make sure that no BackgroundContentses get deleted (a signal that repeated
    220   // window.open calls recreate instances, instead of being no-ops).
    221   content::TestNotificationTracker background_deleted_tracker;
    222   background_deleted_tracker.ListenFor(
    223       chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED,
    224       content::Source<Profile>(browser()->profile()));
    225 
    226   host_resolver()->AddRule("a.com", "127.0.0.1");
    227   ASSERT_TRUE(StartEmbeddedTestServer());
    228 
    229   std::string app_manifest = base::StringPrintf(
    230       "{"
    231       "  \"name\": \"App\","
    232       "  \"version\": \"0.1\","
    233       "  \"manifest_version\": 2,"
    234       "  \"app\": {"
    235       "    \"urls\": ["
    236       "      \"http://a.com/\""
    237       "    ],"
    238       "    \"launch\": {"
    239       "      \"web_url\": \"http://a.com:%d/test.html\""
    240       "    }"
    241       "  },"
    242       "  \"permissions\": [\"background\"],"
    243       "  \"background\": {"
    244       "    \"allow_js_access\": false"
    245       "  }"
    246       "}",
    247       embedded_test_server()->port());
    248 
    249   base::FilePath app_dir;
    250   ASSERT_TRUE(CreateApp(app_manifest, &app_dir));
    251   ASSERT_TRUE(LoadExtension(app_dir));
    252 
    253   // There isn't a background page loaded initially.
    254   const Extension* extension = GetSingleLoadedExtension();
    255   ASSERT_FALSE(
    256       BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
    257           GetAppBackgroundContents(ASCIIToUTF16(extension->id())));
    258   // The test makes sure that window.open returns null.
    259   ASSERT_TRUE(RunExtensionTest("app_background_page/no_js")) << message_;
    260   // And after it runs there should be a background page.
    261   ASSERT_TRUE(
    262       BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
    263           GetAppBackgroundContents(ASCIIToUTF16(extension->id())));
    264 
    265   EXPECT_EQ(0u, background_deleted_tracker.size());
    266   UnloadExtension(extension->id());
    267 }
    268 
    269 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, NoJsManifestBackgroundPage) {
    270   host_resolver()->AddRule("a.com", "127.0.0.1");
    271   ASSERT_TRUE(StartEmbeddedTestServer());
    272 
    273   std::string app_manifest = base::StringPrintf(
    274       "{"
    275       "  \"name\": \"App\","
    276       "  \"version\": \"0.1\","
    277       "  \"manifest_version\": 2,"
    278       "  \"app\": {"
    279       "    \"urls\": ["
    280       "      \"http://a.com/\""
    281       "    ],"
    282       "    \"launch\": {"
    283       "      \"web_url\": \"http://a.com:%d/\""
    284       "    }"
    285       "  },"
    286       "  \"permissions\": [\"background\"],"
    287       "  \"background\": {"
    288       "    \"page\": \"http://a.com:%d/bg.html\","
    289       "    \"allow_js_access\": false"
    290       "  }"
    291       "}",
    292       embedded_test_server()->port(),
    293       embedded_test_server()->port());
    294 
    295   base::FilePath app_dir;
    296   ASSERT_TRUE(CreateApp(app_manifest, &app_dir));
    297   ASSERT_TRUE(LoadExtension(app_dir));
    298 
    299   // The background page should load, but window.open should return null.
    300   const Extension* extension = GetSingleLoadedExtension();
    301   ASSERT_TRUE(
    302       BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
    303           GetAppBackgroundContents(ASCIIToUTF16(extension->id())));
    304   ASSERT_TRUE(RunExtensionTest("app_background_page/no_js_manifest")) <<
    305       message_;
    306   UnloadExtension(extension->id());
    307 }
    308 
    309 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, OpenTwoBackgroundPages) {
    310   host_resolver()->AddRule("a.com", "127.0.0.1");
    311   ASSERT_TRUE(StartEmbeddedTestServer());
    312 
    313   std::string app_manifest = base::StringPrintf(
    314       "{"
    315       "  \"name\": \"App\","
    316       "  \"version\": \"0.1\","
    317       "  \"manifest_version\": 2,"
    318       "  \"app\": {"
    319       "    \"urls\": ["
    320       "      \"http://a.com/\""
    321       "    ],"
    322       "    \"launch\": {"
    323       "      \"web_url\": \"http://a.com:%d/\""
    324       "    }"
    325       "  },"
    326       "  \"permissions\": [\"background\"]"
    327       "}",
    328       embedded_test_server()->port());
    329 
    330   base::FilePath app_dir;
    331   ASSERT_TRUE(CreateApp(app_manifest, &app_dir));
    332   ASSERT_TRUE(LoadExtension(app_dir));
    333   const Extension* extension = GetSingleLoadedExtension();
    334   ASSERT_TRUE(RunExtensionTest("app_background_page/two_pages")) << message_;
    335   UnloadExtension(extension->id());
    336 }
    337 
    338 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, OpenTwoPagesWithManifest) {
    339   host_resolver()->AddRule("a.com", "127.0.0.1");
    340   ASSERT_TRUE(StartEmbeddedTestServer());
    341 
    342   std::string app_manifest = base::StringPrintf(
    343       "{"
    344       "  \"name\": \"App\","
    345       "  \"version\": \"0.1\","
    346       "  \"manifest_version\": 2,"
    347       "  \"app\": {"
    348       "    \"urls\": ["
    349       "      \"http://a.com/\""
    350       "    ],"
    351       "    \"launch\": {"
    352       "      \"web_url\": \"http://a.com:%d/\""
    353       "    }"
    354       "  },"
    355       "  \"background\": {"
    356       "    \"page\": \"http://a.com:%d/bg.html\""
    357       "  },"
    358       "  \"permissions\": [\"background\"]"
    359       "}",
    360       embedded_test_server()->port(),
    361       embedded_test_server()->port());
    362 
    363   base::FilePath app_dir;
    364   ASSERT_TRUE(CreateApp(app_manifest, &app_dir));
    365   ASSERT_TRUE(LoadExtension(app_dir));
    366   const Extension* extension = GetSingleLoadedExtension();
    367   ASSERT_TRUE(RunExtensionTest("app_background_page/two_with_manifest")) <<
    368       message_;
    369   UnloadExtension(extension->id());
    370 }
    371 
    372 // Times out occasionally -- see crbug.com/108493
    373 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, DISABLED_OpenPopupFromBGPage) {
    374   host_resolver()->AddRule("a.com", "127.0.0.1");
    375   ASSERT_TRUE(StartEmbeddedTestServer());
    376 
    377   std::string app_manifest = base::StringPrintf(
    378       "{"
    379       "  \"name\": \"App\","
    380       "  \"version\": \"0.1\","
    381       "  \"manifest_version\": 2,"
    382       "  \"app\": {"
    383       "    \"urls\": ["
    384       "      \"http://a.com/\""
    385       "    ],"
    386       "    \"launch\": {"
    387       "      \"web_url\": \"http://a.com:%d/\""
    388       "    }"
    389       "  },"
    390       "  \"background\": { \"page\": \"http://a.com:%d/extensions/api_test/"
    391       "app_background_page/bg_open/bg_open_bg.html\" },"
    392       "  \"permissions\": [\"background\"]"
    393       "}",
    394       embedded_test_server()->port(),
    395       embedded_test_server()->port());
    396 
    397   base::FilePath app_dir;
    398   ASSERT_TRUE(CreateApp(app_manifest, &app_dir));
    399   ASSERT_TRUE(LoadExtension(app_dir));
    400   ASSERT_TRUE(RunExtensionTest("app_background_page/bg_open")) << message_;
    401 }
    402 
    403 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, DISABLED_OpenThenClose) {
    404   host_resolver()->AddRule("a.com", "127.0.0.1");
    405   ASSERT_TRUE(StartEmbeddedTestServer());
    406 
    407   std::string app_manifest = base::StringPrintf(
    408       "{"
    409       "  \"name\": \"App\","
    410       "  \"version\": \"0.1\","
    411       "  \"manifest_version\": 2,"
    412       "  \"app\": {"
    413       "    \"urls\": ["
    414       "      \"http://a.com/\""
    415       "    ],"
    416       "    \"launch\": {"
    417       "      \"web_url\": \"http://a.com:%d/\""
    418       "    }"
    419       "  },"
    420       "  \"permissions\": [\"background\"]"
    421       "}",
    422       embedded_test_server()->port());
    423 
    424   base::FilePath app_dir;
    425   ASSERT_TRUE(CreateApp(app_manifest, &app_dir));
    426   ASSERT_TRUE(LoadExtension(app_dir));
    427   // There isn't a background page loaded initially.
    428   const Extension* extension = GetSingleLoadedExtension();
    429   ASSERT_FALSE(
    430       BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
    431           GetAppBackgroundContents(ASCIIToUTF16(extension->id())));
    432   // Background mode should not be active until a background page is created.
    433   ASSERT_TRUE(WaitForBackgroundMode(false));
    434   ASSERT_TRUE(RunExtensionTest("app_background_page/basic_open")) << message_;
    435   // Background mode should be active now because a background page was created.
    436   ASSERT_TRUE(WaitForBackgroundMode(true));
    437   ASSERT_TRUE(
    438       BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
    439           GetAppBackgroundContents(ASCIIToUTF16(extension->id())));
    440   // Now close the BackgroundContents.
    441   ASSERT_TRUE(RunExtensionTest("app_background_page/basic_close")) << message_;
    442   // Background mode should no longer be active.
    443   ASSERT_TRUE(WaitForBackgroundMode(false));
    444   ASSERT_FALSE(
    445       BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
    446           GetAppBackgroundContents(ASCIIToUTF16(extension->id())));
    447 }
    448 
    449 IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, UnloadExtensionWhileHidden) {
    450   host_resolver()->AddRule("a.com", "127.0.0.1");
    451   ASSERT_TRUE(StartEmbeddedTestServer());
    452 
    453   std::string app_manifest = base::StringPrintf(
    454       "{"
    455       "  \"name\": \"App\","
    456       "  \"version\": \"0.1\","
    457       "  \"manifest_version\": 2,"
    458       "  \"app\": {"
    459       "    \"urls\": ["
    460       "      \"http://a.com/\""
    461       "    ],"
    462       "    \"launch\": {"
    463       "      \"web_url\": \"http://a.com:%d/\""
    464       "    }"
    465       "  },"
    466       "  \"permissions\": [\"background\"],"
    467       "  \"background\": {"
    468       "    \"page\": \"http://a.com:%d/test.html\""
    469       "  }"
    470       "}",
    471       embedded_test_server()->port(),
    472       embedded_test_server()->port());
    473 
    474   base::FilePath app_dir;
    475   ASSERT_TRUE(CreateApp(app_manifest, &app_dir));
    476   // Background mode should not be active now because no background app was
    477   // loaded.
    478   ASSERT_TRUE(LoadExtension(app_dir));
    479   // Background mode be active now because a background page was created when
    480   // the app was loaded.
    481   ASSERT_TRUE(WaitForBackgroundMode(true));
    482 
    483   const Extension* extension = GetSingleLoadedExtension();
    484   ASSERT_TRUE(
    485       BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
    486           GetAppBackgroundContents(ASCIIToUTF16(extension->id())));
    487 
    488   // Close all browsers - app should continue running.
    489   set_exit_when_last_browser_closes(false);
    490   CloseBrowser(browser());
    491 
    492   // Post a task to unload the extension - this should cause Chrome to exit
    493   // cleanly (not crash).
    494   UnloadExtensionViaTask(extension->id());
    495   content::RunAllPendingInMessageLoop();
    496   ASSERT_TRUE(WaitForBackgroundMode(false));
    497 }
    498