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