diff --git a/DEPS b/DEPS index 2e5aaa8..cdd076e 100644 --- a/DEPS +++ b/DEPS
@@ -39,11 +39,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': '3ca73360493ba71831c86c8c5f9c36187c0355ca', + 'skia_revision': '973d92cf91b21013361209e8a5c0c4685728847f', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': 'c0eabe60e9260ccca9fc2d119e723fdd91814846', + 'v8_revision': 'd6a1568f656bfeacd8e85bdd6cddf04fea906336', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling swarming_client # and whatever else without interference from each other.
diff --git a/base/android/linker/modern_linker_jni.cc b/base/android/linker/modern_linker_jni.cc index 1cd18db..53ae405c 100644 --- a/base/android/linker/modern_linker_jni.cc +++ b/base/android/linker/modern_linker_jni.cc
@@ -290,8 +290,10 @@ // Helper for LoadLibrary(). We reserve an address space larger than // needed. After library loading we want to trim that reservation to only -// what is needed. -bool ResizeReservedAddressSpace(void* addr, +// what is needed. Failure to trim should not occur, but if it does then +// everything will still run, so we treat it as a warning rather than +// an error. +void ResizeReservedAddressSpace(void* addr, size_t reserved_size, size_t load_size, size_t min_vaddr) { @@ -301,38 +303,35 @@ const uintptr_t uintptr_addr = reinterpret_cast<uintptr_t>(addr); - if (reserved_size < load_size) { + if (reserved_size > load_size) { + // Unmap the part of the reserved address space that is beyond the end of + // the loaded library data. + void* unmap = reinterpret_cast<void*>(uintptr_addr + load_size); + const size_t length = reserved_size - load_size; + if (munmap(unmap, length) == -1) { + LOG_ERROR("WARNING: unmap of %d bytes at %p failed: %s", + static_cast<int>(length), unmap, strerror(errno)); + } + } else { LOG_ERROR("WARNING: library reservation was too small"); - return true; - } - - // Unmap the part of the reserved address space that is beyond the end of - // the loaded library data. - void* unmap = reinterpret_cast<void*>(uintptr_addr + load_size); - size_t length = reserved_size - load_size; - if (munmap(unmap, length) == -1) { - LOG_ERROR("Failed to unmap %d at %p", static_cast<int>(length), unmap); - return false; } #if RESERVE_BREAKPAD_GUARD_REGION - if (min_vaddr > kBreakpadGuardRegionBytes) { + if (kBreakpadGuardRegionBytes > min_vaddr) { + // Unmap the part of the reserved address space that is ahead of where we + // actually need the guard region to start. Resizes the guard region to + // min_vaddr bytes. + void* unmap = + reinterpret_cast<void*>(uintptr_addr - kBreakpadGuardRegionBytes); + const size_t length = kBreakpadGuardRegionBytes - min_vaddr; + if (munmap(unmap, length) == -1) { + LOG_ERROR("WARNING: unmap of %d bytes at %p failed: %s", + static_cast<int>(length), unmap, strerror(errno)); + } + } else { LOG_ERROR("WARNING: breakpad guard region reservation was too small"); - return true; - } - - // Unmap the part of the reserved address space that is ahead of where we - // actually need the guard region to start. Resizes the guard region to - // min_vaddr bytes. - unmap = reinterpret_cast<void*>(uintptr_addr - kBreakpadGuardRegionBytes); - length = kBreakpadGuardRegionBytes - min_vaddr; - if (munmap(unmap, length) == -1) { - LOG_ERROR("Failed to unmap %d at %p", static_cast<int>(length), unmap); - return false; } #endif - - return true; } // Load a library with the chromium linker, using android_dlopen_ext(). @@ -420,17 +419,29 @@ return false; } - // After loading, trim the mapping to match the library's actual size. + // For https://crbug.com/568880. + // + // Release the scoped mapping. Now that the library has loaded we can no + // longer assume we have control of all of this area. libdl knows addr and + // has loaded the library into some portion of the reservation. It will + // not expect that portion of memory to be arbitrarily unmapped. + mapping.Release(); + + // After loading we can find the actual size of the library. It should + // be less than the space we reserved for it. size_t load_size = 0; size_t min_vaddr = 0; if (!GetLibraryLoadSize(addr, &load_size, &min_vaddr)) { LOG_ERROR("Unable to find size for load at %p", addr); return false; } - if (!ResizeReservedAddressSpace(addr, size, load_size, min_vaddr)) { - LOG_ERROR("Unable to resize reserved address mapping"); - return false; - } + + // Trim the reservation mapping to match the library's actual size. Failure + // to resize is not a fatal error. At worst we lose a portion of virtual + // address space that we might otherwise have recovered. Note that trimming + // the mapping here requires that we have already released the scoped + // mapping. + ResizeReservedAddressSpace(addr, size, load_size, min_vaddr); // Locate and if found then call the loaded library's JNI_OnLoad() function. using JNI_OnLoadFunctionPtr = int (*)(void* vm, void* reserved); @@ -445,9 +456,6 @@ } } - // Release mapping before returning so that we do not unmap reserved space. - mapping.Release(); - // Note the load address and load size in the supplied libinfo object. const size_t cast_addr = reinterpret_cast<size_t>(addr); s_lib_info_fields.SetLoadInfo(env, lib_info_obj, cast_addr, load_size); @@ -532,11 +540,31 @@ return false; } - // Unload the library from this address. The reserved space is - // automatically unmapped on exit from this function. + // For https://crbug.com/568880. + // + // Release the scoped mapping. See comment in LoadLibrary() above for more. + mapping.Release(); + + // For https://crbug.com/568880. + // + // Unload the library from this address. Calling dlclose() will unmap the + // part of the reservation occupied by the libary, but will leave the + // remainder of the reservation mapped, and we have no effective way of + // unmapping the leftover portions because we don't know where dlclose's + // unmap ended. + // + // For now we live with this. It is a loss of some virtual address space + // (but not actual memory), and because it occurs only once and only in + // the browser process, and never in renderer processes, it is not a + // significant issue. + // + // TODO(simonb): Between mapping.Release() and here, consider calling the + // functions that trim the reservation down to the size of the loaded + // library. This may help recover some or all of the virtual address space + // that is otherwise lost. dlclose(handle); - // Reopen the shared RELFO fd in read-only mode. This ensures that nothing + // Reopen the shared RELRO fd in read-only mode. This ensures that nothing // can write to it through the RELRO fd that we return in libinfo. close(relro_fd); relro_fd = open(filepath, O_RDONLY);
diff --git a/base/mac/sdk_forward_declarations.h b/base/mac/sdk_forward_declarations.h index 363ada1e..99abc909 100644 --- a/base/mac/sdk_forward_declarations.h +++ b/base/mac/sdk_forward_declarations.h
@@ -330,7 +330,7 @@ - (void)toggleFullScreen:(id)sender; - (void)setRestorable:(BOOL)flag; - (NSRect)convertRectFromScreen:(NSRect)aRect; -- (NSSize)convertRectToScreen:(NSRect)aRect; +- (NSRect)convertRectToScreen:(NSRect)aRect; @end @interface NSCursor (LionSDKDeclarations)
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 352225f..0ccee99 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -3902,6 +3902,12 @@ <message name="IDS_EXTENSION_PROMPT_WARNING_HOSTS_LIST_READ_ONLY" desc="Permission string for read-only access to data for an arbitrary list of websites."> Read your data on a number of websites </message> + <message name="IDS_EXTENSION_PROMPT_WARNING_HOST_AND_SUBDOMAIN" desc="Permission string requesting access to data on a website and its sub-domains."> + all <ph name="WEBSITE_1">$1<ex>google.com</ex></ph> sites + </message> + <message name="IDS_EXTENSION_PROMPT_WARNING_HOST_AND_SUBDOMAIN_LIST" desc="Single entry permission string requesting access to data on a website and its sub-domains."> + All <ph name="WEBSITE_1">$1<ex>google.com</ex></ph> sites + </message> <message name="IDS_EXTENSION_PROMPT_WARNING_INPUT" desc="Permission string for access to input."> Read and change anything you type </message>
diff --git a/chrome/browser/profiles/profile_impl.h b/chrome/browser/profiles/profile_impl.h index 008fae1..49c9726f9 100644 --- a/chrome/browser/profiles/profile_impl.h +++ b/chrome/browser/profiles/profile_impl.h
@@ -64,7 +64,7 @@ } namespace user_prefs { -class refRegistrySyncable; +class PrefRegistrySyncable; } // The default profile implementation.
diff --git a/chrome/browser/ui/passwords/account_chooser_prompt.h b/chrome/browser/ui/passwords/account_chooser_prompt.h index 9848073d2..d8d6d4c 100644 --- a/chrome/browser/ui/passwords/account_chooser_prompt.h +++ b/chrome/browser/ui/passwords/account_chooser_prompt.h
@@ -5,6 +5,12 @@ #ifndef CHROME_BROWSER_UI_PASSWORDS_ACCOUNT_CHOOSER_PROMPT_H_ #define CHROME_BROWSER_UI_PASSWORDS_ACCOUNT_CHOOSER_PROMPT_H_ +namespace content { +class WebContents; +} + +class PasswordDialogController; + // A platform-independent interface for the account chooser dialog. class AccountChooserPrompt { public: @@ -18,6 +24,8 @@ virtual ~AccountChooserPrompt() = default; }; - +// Factory function for AccountChooserPrompt on desktop platforms. +AccountChooserPrompt* CreateAccountChooserPromptView( + PasswordDialogController* controller, content::WebContents* web_contents); #endif // CHROME_BROWSER_UI_PASSWORDS_ACCOUNT_CHOOSER_PROMPT_H_
diff --git a/chrome/browser/ui/passwords/manage_passwords_test.cc b/chrome/browser/ui/passwords/manage_passwords_test.cc index b3dfb7b..973c8c6 100644 --- a/chrome/browser/ui/passwords/manage_passwords_test.cc +++ b/chrome/browser/ui/passwords/manage_passwords_test.cc
@@ -78,20 +78,6 @@ GetController()->OnAutomaticPasswordSave(std::move(test_form_manager)); } -void ManagePasswordsTest::SetupChooseCredentials( - ScopedVector<autofill::PasswordForm> local_credentials, - ScopedVector<autofill::PasswordForm> federated_credentials, - const GURL& origin) { - base::string16 kTestUsername = base::ASCIIToUTF16("test_username"); - autofill::PasswordFormMap map; - map.insert(std::make_pair( - kTestUsername, - make_scoped_ptr(new autofill::PasswordForm(*test_form())))); - GetController()->OnChooseCredentials( - std::move(local_credentials), std::move(federated_credentials), origin, - base::Bind(&ManagePasswordsTest::OnChooseCredential, this)); -} - void ManagePasswordsTest::SetupAutoSignin( ScopedVector<autofill::PasswordForm> local_credentials) { GetController()->OnAutoSignin(std::move(local_credentials));
diff --git a/chrome/browser/ui/passwords/manage_passwords_test.h b/chrome/browser/ui/passwords/manage_passwords_test.h index 60c3cd9..3eda4b1 100644 --- a/chrome/browser/ui/passwords/manage_passwords_test.h +++ b/chrome/browser/ui/passwords/manage_passwords_test.h
@@ -44,12 +44,6 @@ // Put the controller, icon, and bubble into a pending-password state. void SetupPendingPassword(); - // Put the controller, icon, and bubble into a choosing credential state. - void SetupChooseCredentials( - ScopedVector<autofill::PasswordForm> local_credentials, - ScopedVector<autofill::PasswordForm> federated_credentials, - const GURL& origin); - // Put the controller, icon, and bubble into an auto sign-in state. void SetupAutoSignin( ScopedVector<autofill::PasswordForm> local_credentials);
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc index ad06a11..a1394be 100644 --- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc +++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
@@ -17,7 +17,9 @@ #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/chrome_pages.h" #include "chrome/browser/ui/location_bar/location_bar.h" +#include "chrome/browser/ui/passwords/account_chooser_prompt.h" #include "chrome/browser/ui/passwords/manage_passwords_icon_view.h" +#include "chrome/browser/ui/passwords/password_dialog_controller_impl.h" #include "chrome/browser/ui/tab_dialogs.h" #include "chrome/common/url_constants.h" #include "chrome/grit/generated_resources.h" @@ -42,6 +44,14 @@ ServiceAccessType::EXPLICIT_ACCESS).get(); } +std::vector<scoped_ptr<autofill::PasswordForm>> CopyFormVector( + const ScopedVector<autofill::PasswordForm>& forms) { + std::vector<scoped_ptr<autofill::PasswordForm>> result(forms.size()); + for (size_t i = 0; i < forms.size(); ++i) + result[i].reset(new autofill::PasswordForm(*forms[i])); + return result; +} + } // namespace DEFINE_WEB_CONTENTS_USER_DATA_KEY(ManagePasswordsUIController); @@ -88,9 +98,24 @@ const GURL& origin, base::Callback<void(const password_manager::CredentialInfo&)> callback) { DCHECK(!local_credentials.empty() || !federated_credentials.empty()); + PasswordDialogController::FormsVector locals = + CopyFormVector(local_credentials); + PasswordDialogController::FormsVector federations = + CopyFormVector(federated_credentials); passwords_data_.OnRequestCredentials( std::move(local_credentials), std::move(federated_credentials), origin); +#if defined(OS_MACOSX) + // TODO(vasilii): remove once Mac supports the dialog. + // http://crbug.com/550922 base::AutoReset<bool> resetter(&should_pop_up_bubble_, true); +#else + dialog_controller_.reset(new PasswordDialogControllerImpl( + Profile::FromBrowserContext(web_contents()->GetBrowserContext()), + this)); + dialog_controller_->ShowAccountChooser( + CreateAccountChooser(dialog_controller_.get()), + std::move(locals), std::move(federations)); +#endif UpdateBubbleAndIconVisibility(); if (!should_pop_up_bubble_) { passwords_data_.set_credentials_callback(callback); @@ -142,12 +167,18 @@ void ManagePasswordsUIController::UpdateIconAndBubbleState( ManagePasswordsIconView* icon) { if (should_pop_up_bubble_) { + DCHECK(!dialog_controller_); // We must display the icon before showing the bubble, as the bubble would // be otherwise unanchored. icon->SetState(GetState()); ShowBubbleWithoutUserInteraction(); } else { - icon->SetState(GetState()); + password_manager::ui::State state = GetState(); + // The dialog should hide the icon. + if (dialog_controller_ && + state == password_manager::ui::CREDENTIAL_REQUEST_STATE) + state = password_manager::ui::INACTIVE_STATE; + icon->SetState(state); } } @@ -204,6 +235,7 @@ } void ManagePasswordsUIController::OnBubbleHidden() { + dialog_controller_.reset(); if (GetState() == password_manager::ui::CREDENTIAL_REQUEST_STATE || GetState() == password_manager::ui::CONFIRMATION_STATE || GetState() == password_manager::ui::AUTO_SIGNIN_STATE) { @@ -256,6 +288,11 @@ const autofill::PasswordForm& form, password_manager::CredentialType credential_type) { passwords_data_.ChooseCredential(form, credential_type); + if (dialog_controller_) { + dialog_controller_.reset(); + passwords_data_.TransitionToState(password_manager::ui::MANAGE_STATE); + UpdateBubbleAndIconVisibility(); + } } void ManagePasswordsUIController::NavigateToExternalPasswordManager() { @@ -324,6 +361,19 @@ location_bar->UpdateManagePasswordsIconAndBubble(); } +#if defined(OS_MACOSX) +// TODO(vasilii): remove once Mac supports the dialog. +AccountChooserPrompt* ManagePasswordsUIController::CreateAccountChooser( + PasswordDialogController* controller) { + return nullptr; +} +#else +AccountChooserPrompt* ManagePasswordsUIController::CreateAccountChooser( + PasswordDialogController* controller) { + return CreateAccountChooserPromptView(controller, web_contents()); +} +#endif + void ManagePasswordsUIController::DidNavigateMainFrame( const content::LoadCommittedDetails& details, const content::FrameNavigateParams& params) {
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.h b/chrome/browser/ui/passwords/manage_passwords_ui_controller.h index 6f31d1b..ecbf087 100644 --- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.h +++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
@@ -25,7 +25,10 @@ class PasswordFormManager; } +class AccountChooserPrompt; class ManagePasswordsIconView; +class PasswordDialogController; +class PasswordDialogControllerImpl; // Per-tab class to control the Omnibox password icon and bubble. class ManagePasswordsUIController @@ -105,6 +108,10 @@ // manage passwords icon and bubble. virtual void UpdateBubbleAndIconVisibility(); + // Called to create the account chooser dialog. Mocked in tests. + virtual AccountChooserPrompt* CreateAccountChooser( + PasswordDialogController* controller); + // Overwrites the client for |passwords_data_|. void set_client(password_manager::PasswordManagerClient* client) { passwords_data_.set_client(client); @@ -128,6 +135,9 @@ // The wrapper around current state and data. ManagePasswordsState passwords_data_; + // The controller for the blocking dialogs. + scoped_ptr<PasswordDialogControllerImpl> dialog_controller_; + // Contains true if the bubble is to be popped up in the next call to // UpdateBubbleAndIconVisibility(). bool should_pop_up_bubble_;
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc index 2c451dd..1204dba 100644 --- a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc +++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
@@ -12,9 +12,11 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" +#include "chrome/browser/ui/passwords/account_chooser_prompt.h" #include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h" #include "chrome/browser/ui/passwords/manage_passwords_icon_view.h" #include "chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.h" +#include "chrome/browser/ui/passwords/password_dialog_controller.h" #include "chrome/test/base/chrome_render_view_host_test_harness.h" #include "chrome/test/base/testing_profile.h" #include "components/autofill/core/common/password_form.h" @@ -33,14 +35,24 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +using ::testing::DoAll; using ::testing::ElementsAre; using ::testing::Pointee; +using ::testing::Return; +using ::testing::SaveArg; +using ::testing::_; namespace { // Number of dismissals that for sure supresses the bubble. const int kGreatDissmisalCount = 10; +class AccountChooserPromptMock : public AccountChooserPrompt { + public: + MOCK_METHOD0(Show, void()); + MOCK_METHOD0(ControllerGone, void()); +}; + class TestManagePasswordsIconView : public ManagePasswordsIconView { public: TestManagePasswordsIconView() {} @@ -67,6 +79,8 @@ bool opened_bubble() const { return opened_bubble_; } + MOCK_METHOD1(CreateAccountChooser, + AccountChooserPrompt*(PasswordDialogController*)); using ManagePasswordsUIController::DidNavigateMainFrame; private: @@ -129,46 +143,14 @@ ManagePasswordsUIControllerTest() : password_manager_(&client_), field_trial_list_(nullptr) {} - void SetUp() override { - ChromeRenderViewHostTestHarness::SetUp(); - - // Create the test UIController here so that it's bound to - // |test_web_contents_|, and will be retrieved correctly via - // ManagePasswordsUIController::FromWebContents in |controller()|. - new TestManagePasswordsUIController(web_contents(), &client_); - - test_local_form_.origin = GURL("http://example.com/login"); - test_local_form_.username_value = base::ASCIIToUTF16("username"); - test_local_form_.password_value = base::ASCIIToUTF16("12345"); - - test_federated_form_.origin = GURL("http://example.com/login"); - test_federated_form_.username_value = base::ASCIIToUTF16("username"); - test_federated_form_.federation_url = GURL("https://federation.test/"); - - // We need to be on a "webby" URL for most tests. - content::WebContentsTester::For(web_contents()) - ->NavigateAndCommit(GURL("http://example.com")); - } - - void ExpectIconStateIs(password_manager::ui::State state) { -// No op on Android, where there is no icon. -#if !defined(OS_ANDROID) - TestManagePasswordsIconView view; - controller()->UpdateIconAndBubbleState(&view); - EXPECT_EQ(state, view.state()); -#endif - } - - void ExpectIconAndControllerStateIs(password_manager::ui::State state) { - ExpectIconStateIs(state); - EXPECT_EQ(state, controller()->GetState()); - } + void SetUp() override; autofill::PasswordForm& test_local_form() { return test_local_form_; } autofill::PasswordForm& test_federated_form() { return test_federated_form_; } password_manager::CredentialInfo* credential_info() const { return credential_info_.get(); } + AccountChooserPromptMock& account_chooser() { return account_chooser_; } TestManagePasswordsUIController* controller() { return static_cast<TestManagePasswordsUIController*>( @@ -179,6 +161,9 @@ credential_info_.reset(new password_manager::CredentialInfo(info)); } + void ExpectIconStateIs(password_manager::ui::State state); + void ExpectIconAndControllerStateIs(password_manager::ui::State state); + scoped_ptr<password_manager::PasswordFormManager> CreateFormManagerWithBestMatches( const autofill::PasswordForm& observed_form, @@ -195,8 +180,43 @@ autofill::PasswordForm test_federated_form_; scoped_ptr<password_manager::CredentialInfo> credential_info_; base::FieldTrialList field_trial_list_; + AccountChooserPromptMock account_chooser_; }; +void ManagePasswordsUIControllerTest::SetUp() { + ChromeRenderViewHostTestHarness::SetUp(); + + // Create the test UIController here so that it's bound to + // |test_web_contents_|, and will be retrieved correctly via + // ManagePasswordsUIController::FromWebContents in |controller()|. + new TestManagePasswordsUIController(web_contents(), &client_); + + test_local_form_.origin = GURL("http://example.com/login"); + test_local_form_.username_value = base::ASCIIToUTF16("username"); + test_local_form_.password_value = base::ASCIIToUTF16("12345"); + + test_federated_form_.origin = GURL("http://example.com/login"); + test_federated_form_.username_value = base::ASCIIToUTF16("username"); + test_federated_form_.federation_url = GURL("https://federation.test/"); + + // We need to be on a "webby" URL for most tests. + content::WebContentsTester::For(web_contents()) + ->NavigateAndCommit(GURL("http://example.com")); +} + +void ManagePasswordsUIControllerTest::ExpectIconStateIs( + password_manager::ui::State state) { + TestManagePasswordsIconView view; + controller()->UpdateIconAndBubbleState(&view); + EXPECT_EQ(state, view.state()); +} + +void ManagePasswordsUIControllerTest::ExpectIconAndControllerStateIs( + password_manager::ui::State state) { + ExpectIconStateIs(state); + EXPECT_EQ(state, controller()->GetState()); +} + scoped_ptr<password_manager::PasswordFormManager> ManagePasswordsUIControllerTest::CreateFormManagerWithBestMatches( const autofill::PasswordForm& observed_form, @@ -433,6 +453,8 @@ ExpectIconStateIs(password_manager::ui::MANAGE_STATE); } +#if defined(OS_MACOSX) +// TODO(vasilii): remove once Mac supports the account chooser dialog. TEST_F(ManagePasswordsUIControllerTest, ChooseCredentialLocal) { ScopedVector<autofill::PasswordForm> local_credentials; local_credentials.push_back(new autofill::PasswordForm(test_local_form())); @@ -547,6 +569,138 @@ EXPECT_EQ(password_manager::CredentialType::CREDENTIAL_TYPE_EMPTY, credential_info()->type); } +#else // defined(OS_MACOSX) +TEST_F(ManagePasswordsUIControllerTest, ChooseCredentialLocal) { + ScopedVector<autofill::PasswordForm> local_credentials; + local_credentials.push_back(new autofill::PasswordForm(test_local_form())); + ScopedVector<autofill::PasswordForm> federated_credentials; + GURL origin("http://example.com"); + PasswordDialogController* dialog_controller = nullptr; + EXPECT_CALL(*controller(), CreateAccountChooser(_)).WillOnce( + DoAll(SaveArg<0>(&dialog_controller), Return(&account_chooser()))); + EXPECT_CALL(account_chooser(), Show()); + EXPECT_TRUE(controller()->OnChooseCredentials( + std::move(local_credentials), std::move(federated_credentials), origin, + base::Bind(&ManagePasswordsUIControllerTest::CredentialCallback, + base::Unretained(this)))); + EXPECT_EQ(password_manager::ui::CREDENTIAL_REQUEST_STATE, + controller()->GetState()); + EXPECT_EQ(origin, controller()->GetOrigin()); + EXPECT_THAT(controller()->GetCurrentForms(), + ElementsAre(Pointee(test_local_form()))); + + ExpectIconStateIs(password_manager::ui::INACTIVE_STATE); + + EXPECT_CALL(account_chooser(), ControllerGone()); + dialog_controller->OnChooseCredentials( + test_local_form(), + password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD); + EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState()); + ASSERT_TRUE(credential_info()); + EXPECT_EQ(test_local_form().username_value, credential_info()->id); + EXPECT_EQ(test_local_form().password_value, credential_info()->password); + EXPECT_TRUE(credential_info()->federation.is_empty()); + EXPECT_EQ(password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD, + credential_info()->type); +} + +TEST_F(ManagePasswordsUIControllerTest, ChooseCredentialLocalButFederated) { + ScopedVector<autofill::PasswordForm> local_credentials; + local_credentials.push_back( + new autofill::PasswordForm(test_federated_form())); + ScopedVector<autofill::PasswordForm> federated_credentials; + GURL origin("http://example.com"); + PasswordDialogController* dialog_controller = nullptr; + EXPECT_CALL(*controller(), CreateAccountChooser(_)).WillOnce( + DoAll(SaveArg<0>(&dialog_controller), Return(&account_chooser()))); + EXPECT_CALL(account_chooser(), Show()); + EXPECT_TRUE(controller()->OnChooseCredentials( + std::move(local_credentials), std::move(federated_credentials), origin, + base::Bind(&ManagePasswordsUIControllerTest::CredentialCallback, + base::Unretained(this)))); + EXPECT_EQ(password_manager::ui::CREDENTIAL_REQUEST_STATE, + controller()->GetState()); + EXPECT_EQ(origin, controller()->GetOrigin()); + EXPECT_THAT(controller()->GetCurrentForms(), + ElementsAre(Pointee(test_federated_form()))); + + ExpectIconStateIs(password_manager::ui::INACTIVE_STATE); + + EXPECT_CALL(account_chooser(), ControllerGone()); + dialog_controller->OnChooseCredentials( + test_federated_form(), + password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD); + EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState()); + ASSERT_TRUE(credential_info()); + EXPECT_EQ(test_federated_form().username_value, credential_info()->id); + EXPECT_EQ(test_federated_form().federation_url, + credential_info()->federation); + EXPECT_TRUE(credential_info()->password.empty()); + EXPECT_EQ(password_manager::CredentialType::CREDENTIAL_TYPE_FEDERATED, + credential_info()->type); +} + +TEST_F(ManagePasswordsUIControllerTest, ChooseCredentialFederated) { + ScopedVector<autofill::PasswordForm> local_credentials; + ScopedVector<autofill::PasswordForm> federated_credentials; + federated_credentials.push_back( + new autofill::PasswordForm(test_local_form())); + GURL origin("http://example.com"); + PasswordDialogController* dialog_controller = nullptr; + EXPECT_CALL(*controller(), CreateAccountChooser(_)).WillOnce( + DoAll(SaveArg<0>(&dialog_controller), Return(&account_chooser()))); + EXPECT_CALL(account_chooser(), Show()); + EXPECT_TRUE(controller()->OnChooseCredentials( + std::move(local_credentials), std::move(federated_credentials), origin, + base::Bind(&ManagePasswordsUIControllerTest::CredentialCallback, + base::Unretained(this)))); + EXPECT_EQ(password_manager::ui::CREDENTIAL_REQUEST_STATE, + controller()->GetState()); + EXPECT_EQ(0u, controller()->GetCurrentForms().size()); + EXPECT_EQ(origin, controller()->GetOrigin()); + + ExpectIconStateIs(password_manager::ui::INACTIVE_STATE); + + EXPECT_CALL(account_chooser(), ControllerGone()); + dialog_controller->OnChooseCredentials( + test_local_form(), + password_manager::CredentialType::CREDENTIAL_TYPE_FEDERATED); + controller()->OnBubbleHidden(); + EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState()); + ASSERT_TRUE(credential_info()); + EXPECT_EQ(test_local_form().username_value, credential_info()->id); + EXPECT_TRUE(credential_info()->password.empty()); + EXPECT_EQ(password_manager::CredentialType::CREDENTIAL_TYPE_FEDERATED, + credential_info()->type); +} + +TEST_F(ManagePasswordsUIControllerTest, ChooseCredentialCancel) { + ScopedVector<autofill::PasswordForm> local_credentials; + local_credentials.push_back(new autofill::PasswordForm(test_local_form())); + ScopedVector<autofill::PasswordForm> federated_credentials; + GURL origin("http://example.com"); + PasswordDialogController* dialog_controller = nullptr; + EXPECT_CALL(*controller(), CreateAccountChooser(_)).WillOnce( + DoAll(SaveArg<0>(&dialog_controller), Return(&account_chooser()))); + EXPECT_CALL(account_chooser(), Show()); + EXPECT_TRUE(controller()->OnChooseCredentials( + std::move(local_credentials), std::move(federated_credentials), origin, + base::Bind(&ManagePasswordsUIControllerTest::CredentialCallback, + base::Unretained(this)))); + EXPECT_EQ(password_manager::ui::CREDENTIAL_REQUEST_STATE, + controller()->GetState()); + EXPECT_EQ(origin, controller()->GetOrigin()); + + EXPECT_CALL(account_chooser(), ControllerGone()).Times(0); + dialog_controller->OnCloseAccountChooser(); + EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState()); + ASSERT_TRUE(credential_info()); + EXPECT_TRUE(credential_info()->federation.is_empty()); + EXPECT_TRUE(credential_info()->password.empty()); + EXPECT_EQ(password_manager::CredentialType::CREDENTIAL_TYPE_EMPTY, + credential_info()->type); +} +#endif // defined(OS_MACOSX) TEST_F(ManagePasswordsUIControllerTest, AutoSignin) { ScopedVector<autofill::PasswordForm> local_credentials;
diff --git a/chrome/browser/ui/passwords/password_dialog_controller.h b/chrome/browser/ui/passwords/password_dialog_controller.h index e6019b3..92dc15e 100644 --- a/chrome/browser/ui/passwords/password_dialog_controller.h +++ b/chrome/browser/ui/passwords/password_dialog_controller.h
@@ -41,6 +41,9 @@ virtual void OnChooseCredentials( const autofill::PasswordForm& password_form, password_manager::CredentialType credential_type) = 0; + + // Called when the account chooser dialog was closed. + virtual void OnCloseAccountChooser() = 0; protected: virtual ~PasswordDialogController() = default; };
diff --git a/chrome/browser/ui/passwords/password_dialog_controller_impl.cc b/chrome/browser/ui/passwords/password_dialog_controller_impl.cc index f70710f..bb4d6ab4c 100644 --- a/chrome/browser/ui/passwords/password_dialog_controller_impl.cc +++ b/chrome/browser/ui/passwords/password_dialog_controller_impl.cc
@@ -7,6 +7,7 @@ #include "chrome/browser/sync/profile_sync_service_factory.h" #include "chrome/browser/ui/passwords/account_chooser_prompt.h" #include "chrome/browser/ui/passwords/manage_passwords_view_utils.h" +#include "chrome/browser/ui/passwords/passwords_model_delegate.h" #include "components/autofill/core/common/password_form.h" #include "components/browser_sync/browser/profile_sync_service.h" #include "components/password_manager/core/browser/password_bubble_experiment.h" @@ -19,12 +20,17 @@ } } // namespace -PasswordDialogControllerImpl::PasswordDialogControllerImpl(Profile* profle) +PasswordDialogControllerImpl::PasswordDialogControllerImpl( + Profile* profle, + PasswordsModelDelegate* delegate) : profile_(profle), + delegate_(delegate), current_dialog_(nullptr) { } -PasswordDialogControllerImpl::~PasswordDialogControllerImpl() = default; +PasswordDialogControllerImpl::~PasswordDialogControllerImpl() { + ResetDialog(); +} void PasswordDialogControllerImpl::ShowAccountChooser( AccountChooserPrompt* dialog, @@ -58,14 +64,20 @@ } void PasswordDialogControllerImpl::OnSmartLockLinkClicked() { - // TODO(vasilii): notify the UI controller. + delegate_->NavigateToExternalPasswordManager(); } void PasswordDialogControllerImpl::OnChooseCredentials( const autofill::PasswordForm& password_form, password_manager::CredentialType credential_type) { ResetDialog(); - // TODO(vasilii): notify the UI controller. + delegate_->ChooseCredential(password_form, credential_type); +} + +void PasswordDialogControllerImpl::OnCloseAccountChooser() { + current_dialog_ = nullptr; + // The dialog isn't a bubble but ManagePasswordsUIController handles this. + delegate_->OnBubbleHidden(); } void PasswordDialogControllerImpl::ResetDialog() {
diff --git a/chrome/browser/ui/passwords/password_dialog_controller_impl.h b/chrome/browser/ui/passwords/password_dialog_controller_impl.h index bf36852..4ff7dcc 100644 --- a/chrome/browser/ui/passwords/password_dialog_controller_impl.h +++ b/chrome/browser/ui/passwords/password_dialog_controller_impl.h
@@ -12,12 +12,14 @@ #include "chrome/browser/ui/passwords/password_dialog_controller.h" class AccountChooserPrompt; +class PasswordsModelDelegate; class Profile; // A UI controller responsible for the account chooser dialog. class PasswordDialogControllerImpl : public PasswordDialogController { public: - explicit PasswordDialogControllerImpl(Profile* profle); + PasswordDialogControllerImpl(Profile* profle, + PasswordsModelDelegate* delegate); ~PasswordDialogControllerImpl() override; // Pop up the account chooser dialog. @@ -32,12 +34,14 @@ void OnChooseCredentials( const autofill::PasswordForm& password_form, password_manager::CredentialType credential_type) override; + void OnCloseAccountChooser() override; private: // Release |current_dialog_| and close the open dialog. void ResetDialog(); Profile* const profile_; + PasswordsModelDelegate* const delegate_; AccountChooserPrompt* current_dialog_; std::vector<scoped_ptr<autofill::PasswordForm>> local_credentials_; std::vector<scoped_ptr<autofill::PasswordForm>> federated_credentials_;
diff --git a/chrome/browser/ui/passwords/password_dialog_controller_impl_unittest.cc b/chrome/browser/ui/passwords/password_dialog_controller_impl_unittest.cc index 0e3e664..aed00f4 100644 --- a/chrome/browser/ui/passwords/password_dialog_controller_impl_unittest.cc +++ b/chrome/browser/ui/passwords/password_dialog_controller_impl_unittest.cc
@@ -10,9 +10,11 @@ #include "base/memory/scoped_ptr.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/ui/passwords/account_chooser_prompt.h" +#include "chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.h" #include "chrome/test/base/testing_profile.h" #include "components/autofill/core/common/password_form.h" #include "content/public/test/test_browser_thread_bundle.h" +#include "content/public/test/web_contents_tester.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -44,30 +46,67 @@ return form; } +class PasswordDialogControllerTest : public testing::Test { + public: + PasswordDialogControllerTest() + : test_web_contents_(content::WebContentsTester::CreateTestWebContents( + &profile_, nullptr)), + ui_controller_mock_(new StrictMock<ManagePasswordsUIControllerMock>( + test_web_contents_.get())), + controller_(&profile_, ui_controller_mock_) { + } -TEST(PasswordDialogControllerTest, Show) { - content::TestBrowserThreadBundle thread_bundle; - TestingProfile profile; - PasswordDialogControllerImpl controller(&profile); + ManagePasswordsUIControllerMock& ui_controller_mock() { + return *ui_controller_mock_; + } + + PasswordDialogControllerImpl& controller() { return controller_; } + + private: + content::TestBrowserThreadBundle thread_bundle_; + TestingProfile profile_; + scoped_ptr<content::WebContents> test_web_contents_; + // Owned by |test_web_contents_|. + ManagePasswordsUIControllerMock* ui_controller_mock_; + PasswordDialogControllerImpl controller_; +}; + +TEST_F(PasswordDialogControllerTest, ShowAccountChooser) { StrictMock<MockAccountChooserPrompt> prompt; autofill::PasswordForm local_form = GetLocalForm(); autofill::PasswordForm idp_form = GetFederationProviderForm(); std::vector<scoped_ptr<autofill::PasswordForm>> locals; locals.push_back(make_scoped_ptr(new autofill::PasswordForm(local_form))); + autofill::PasswordForm* local_form_ptr = locals[0].get(); std::vector<scoped_ptr<autofill::PasswordForm>> federations; federations.push_back(make_scoped_ptr(new autofill::PasswordForm(idp_form))); EXPECT_CALL(prompt, Show()); - controller.ShowAccountChooser(&prompt, - std::move(locals), std::move(federations)); - EXPECT_THAT(controller.GetLocalForms(), ElementsAre(Pointee(local_form))); - EXPECT_THAT(controller.GetFederationsForms(), ElementsAre(Pointee(idp_form))); + controller().ShowAccountChooser(&prompt, + std::move(locals), std::move(federations)); + EXPECT_THAT(controller().GetLocalForms(), ElementsAre(Pointee(local_form))); + EXPECT_THAT(controller().GetFederationsForms(), + ElementsAre(Pointee(idp_form))); // Close the dialog. EXPECT_CALL(prompt, ControllerGone()); - controller.OnChooseCredentials( - autofill::PasswordForm(), - password_manager::CredentialType::CREDENTIAL_TYPE_EMPTY); + EXPECT_CALL(ui_controller_mock(), ChooseCredential( + local_form, + password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD)); + controller().OnChooseCredentials( + *local_form_ptr, + password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD); +} + +TEST_F(PasswordDialogControllerTest, AccountChooserClosed) { + StrictMock<MockAccountChooserPrompt> prompt; + EXPECT_CALL(prompt, Show()); + controller().ShowAccountChooser(&prompt, + PasswordDialogController::FormsVector(), + PasswordDialogController::FormsVector()); + + EXPECT_CALL(ui_controller_mock(), OnBubbleHidden()); + controller().OnCloseAccountChooser(); } } // namespace
diff --git a/chrome/browser/ui/views/passwords/account_chooser_dialog_view.cc b/chrome/browser/ui/views/passwords/account_chooser_dialog_view.cc index 64054f7..56eca17 100644 --- a/chrome/browser/ui/views/passwords/account_chooser_dialog_view.cc +++ b/chrome/browser/ui/views/passwords/account_chooser_dialog_view.cc
@@ -106,6 +106,11 @@ return l10n_util::GetStringUTF16(IDS_APP_CANCEL); } +void AccountChooserDialogView::OnClosed() { + if (controller_) + controller_->OnCloseAccountChooser(); +} + gfx::Size AccountChooserDialogView::GetPreferredSize() const { return gfx::Size(kDesiredWidth, GetHeightForWidth(kDesiredWidth)); } @@ -168,3 +173,8 @@ // the buttons. layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); } + +AccountChooserPrompt* CreateAccountChooserPromptView( + PasswordDialogController* controller, content::WebContents* web_contents) { + return new AccountChooserDialogView(controller, web_contents); +}
diff --git a/chrome/browser/ui/views/passwords/account_chooser_dialog_view.h b/chrome/browser/ui/views/passwords/account_chooser_dialog_view.h index 0d87d1f..283252b5 100644 --- a/chrome/browser/ui/views/passwords/account_chooser_dialog_view.h +++ b/chrome/browser/ui/views/passwords/account_chooser_dialog_view.h
@@ -40,6 +40,7 @@ // DialogDelegate: int GetDialogButtons() const override; base::string16 GetDialogButtonLabel(ui::DialogButton button) const override; + void OnClosed() override; // views::View gfx::Size GetPreferredSize() const override;
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view_browsertest.cc b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view_interactive_uitest.cc similarity index 83% rename from chrome/browser/ui/views/passwords/manage_passwords_bubble_view_browsertest.cc rename to chrome/browser/ui/views/passwords/manage_passwords_bubble_view_interactive_uitest.cc index 988cd5b..6ded4c3 100644 --- a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view_browsertest.cc +++ b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view_interactive_uitest.cc
@@ -291,60 +291,6 @@ EXPECT_FALSE(IsBubbleShowing()); } -IN_PROC_BROWSER_TEST_F(ManagePasswordsBubbleViewTest, ChooseCredential) { - GURL origin("https://example.com"); - ScopedVector<autofill::PasswordForm> local_credentials; - test_form()->origin = origin; - test_form()->display_name = base::ASCIIToUTF16("Peter"); - test_form()->username_value = base::ASCIIToUTF16("pet12@gmail.com"); - test_form()->icon_url = GURL("broken url"); - local_credentials.push_back(new autofill::PasswordForm(*test_form())); - ScopedVector<autofill::PasswordForm> federated_credentials; - GURL icon_url("https://google.com/icon.png"); - test_form()->icon_url = icon_url; - test_form()->display_name = base::ASCIIToUTF16("Peter Pen"); - test_form()->federation_url = GURL("https://google.com/federation"); - federated_credentials.push_back(new autofill::PasswordForm(*test_form())); - - // Prepare to capture the network request. - TestURLFetcherCallback url_callback; - net::FakeURLFetcherFactory factory( - NULL, - base::Bind(&TestURLFetcherCallback::CreateURLFetcher, - base::Unretained(&url_callback))); - factory.SetFakeResponse(icon_url, std::string(), net::HTTP_OK, - net::URLRequestStatus::FAILED); - EXPECT_CALL(url_callback, OnRequestDone(icon_url)); - - SetupChooseCredentials(std::move(local_credentials), - std::move(federated_credentials), origin); - EXPECT_TRUE(IsBubbleShowing()); - EXPECT_CALL(*this, OnChooseCredential( - Field(&password_manager::CredentialInfo::type, - password_manager::CredentialType::CREDENTIAL_TYPE_EMPTY))); - ManagePasswordsBubbleView::CloseBubble(); -} - -IN_PROC_BROWSER_TEST_F(ManagePasswordsBubbleViewTest, ChooseCredentialNoFocus) { - GURL origin("https://example.com"); - ScopedVector<autofill::PasswordForm> local_credentials; - test_form()->origin = origin; - test_form()->display_name = base::ASCIIToUTF16("Peter"); - test_form()->username_value = base::ASCIIToUTF16("pet12@gmail.com"); - local_credentials.push_back(new autofill::PasswordForm(*test_form())); - ScopedVector<autofill::PasswordForm> federated_credentials; - - // Open another window with focus. - Browser* focused_window = CreateBrowser(browser()->profile()); - ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(focused_window)); - content::RunAllPendingInMessageLoop(); - - EXPECT_FALSE(browser()->window()->IsActive()); - SetupChooseCredentials(std::move(local_credentials), - std::move(federated_credentials), origin); - EXPECT_TRUE(IsBubbleShowing()); -} - IN_PROC_BROWSER_TEST_F(ManagePasswordsBubbleViewTest, AutoSignin) { // The switch enables the new UI. base::CommandLine::ForCurrentProcess()->AppendSwitch(
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_icon_view_browsertest.cc b/chrome/browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc similarity index 100% rename from chrome/browser/ui/views/passwords/manage_passwords_icon_view_browsertest.cc rename to chrome/browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc
diff --git a/chrome/browser/ui/views/passwords/password_dialog_view_browsertest.cc b/chrome/browser/ui/views/passwords/password_dialog_view_browsertest.cc new file mode 100644 index 0000000..208fd20 --- /dev/null +++ b/chrome/browser/ui/views/passwords/password_dialog_view_browsertest.cc
@@ -0,0 +1,163 @@ +// Copyright 2016 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. + +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/password_manager/chrome_password_manager_client.h" +#include "chrome/browser/ui/autofill/chrome_autofill_client.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/views/passwords/account_chooser_dialog_view.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "net/url_request/test_url_fetcher_factory.h" +#include "testing/gmock/include/gmock/gmock.h" + +using ::testing::Field; + +namespace { + +// A helper class that will create FakeURLFetcher and record the requested URLs. +class TestURLFetcherCallback { + public: + scoped_ptr<net::FakeURLFetcher> CreateURLFetcher( + const GURL& url, + net::URLFetcherDelegate* d, + const std::string& response_data, + net::HttpStatusCode response_code, + net::URLRequestStatus::Status status) { + OnRequestDone(url); + return scoped_ptr<net::FakeURLFetcher>(new net::FakeURLFetcher( + url, d, response_data, response_code, status)); + } + + MOCK_METHOD1(OnRequestDone, void(const GURL&)); +}; + +// ManagePasswordsUIController subclass to capture the dialog instance +class TestManagePasswordsUIController : public ManagePasswordsUIController { + public: + explicit TestManagePasswordsUIController(content::WebContents* web_contents); + + AccountChooserPrompt* CreateAccountChooser( + PasswordDialogController* controller) override; + + AccountChooserPrompt* current_dialog() const { return current_dialog_; } + + private: + AccountChooserPrompt* current_dialog_; + + DISALLOW_COPY_AND_ASSIGN(TestManagePasswordsUIController); +}; + +TestManagePasswordsUIController::TestManagePasswordsUIController( + content::WebContents* web_contents) + : ManagePasswordsUIController(web_contents), + current_dialog_(nullptr) { + // Attach TestManagePasswordsUIController to |web_contents| so the default + // ManagePasswordsUIController isn't created. + // Do not silently replace an existing ManagePasswordsUIController because it + // unregisters itself in WebContentsDestroyed(). + EXPECT_FALSE(web_contents->GetUserData(UserDataKey())); + web_contents->SetUserData(UserDataKey(), this); +} + +AccountChooserPrompt* TestManagePasswordsUIController::CreateAccountChooser( + PasswordDialogController* controller) { + current_dialog_ = + ManagePasswordsUIController::CreateAccountChooser(controller); + return current_dialog_; +} + +class PasswordDialogViewTest : public InProcessBrowserTest { + public: + // InProcessBrowserTest: + void SetUpOnMainThread() override; + + void SetupChooseCredentials( + ScopedVector<autofill::PasswordForm> local_credentials, + ScopedVector<autofill::PasswordForm> federated_credentials, + const GURL& origin); + + TestManagePasswordsUIController* controller() const { return controller_; } + + MOCK_METHOD1(OnChooseCredential, + void(const password_manager::CredentialInfo&)); + + private: + TestManagePasswordsUIController* controller_; +}; + +void PasswordDialogViewTest::SetUpOnMainThread() { + // Open a new tab with modified ManagePasswordsUIController. + content::WebContents* tab = + browser()->tab_strip_model()->GetActiveWebContents(); + content::WebContents* new_tab = content::WebContents::Create( + content::WebContents::CreateParams(tab->GetBrowserContext())); + ASSERT_TRUE(new_tab); + + // ManagePasswordsUIController needs ChromePasswordManagerClient for logging. + // ChromePasswordManagerClient needs ChromeAutofillClient on creation. + autofill::ChromeAutofillClient::CreateForWebContents(new_tab); + ChromePasswordManagerClient::CreateForWebContentsWithAutofillClient( + new_tab, + autofill::ChromeAutofillClient::FromWebContents(new_tab)); + ASSERT_TRUE(ChromePasswordManagerClient::FromWebContents(new_tab)); + controller_ = new TestManagePasswordsUIController(new_tab); + browser()->tab_strip_model()->AppendWebContents(new_tab, true); + + // Navigate to a Web URL. + ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL( + browser(), GURL("http://www.google.com"))); + ASSERT_EQ(controller_, ManagePasswordsUIController::FromWebContents(new_tab)); +} + +void PasswordDialogViewTest::SetupChooseCredentials( + ScopedVector<autofill::PasswordForm> local_credentials, + ScopedVector<autofill::PasswordForm> federated_credentials, + const GURL& origin) { + controller()->OnChooseCredentials( + std::move(local_credentials), std::move(federated_credentials), origin, + base::Bind(&PasswordDialogViewTest::OnChooseCredential, this)); + EXPECT_EQ(password_manager::ui::CREDENTIAL_REQUEST_STATE, + controller()->GetState()); +} + +IN_PROC_BROWSER_TEST_F(PasswordDialogViewTest, PopupAccountChooser) { + GURL origin("https://example.com"); + ScopedVector<autofill::PasswordForm> local_credentials; + autofill::PasswordForm form; + form.origin = origin; + form.display_name = base::ASCIIToUTF16("Peter"); + form.username_value = base::ASCIIToUTF16("peter@pan.test"); + form.icon_url = GURL("broken url"); + local_credentials.push_back(new autofill::PasswordForm(form)); + GURL icon_url("https://google.com/icon.png"); + form.icon_url = icon_url; + form.display_name = base::ASCIIToUTF16("Peter Pen"); + form.federation_url = GURL("https://google.com/federation"); + local_credentials.push_back(new autofill::PasswordForm(form)); + + // Prepare to capture the network request. + TestURLFetcherCallback url_callback; + net::FakeURLFetcherFactory factory( + NULL, + base::Bind(&TestURLFetcherCallback::CreateURLFetcher, + base::Unretained(&url_callback))); + factory.SetFakeResponse(icon_url, std::string(), net::HTTP_OK, + net::URLRequestStatus::FAILED); + EXPECT_CALL(url_callback, OnRequestDone(icon_url)); + + SetupChooseCredentials(std::move(local_credentials), + ScopedVector<autofill::PasswordForm>(), origin); + ASSERT_TRUE(controller()->current_dialog()); + AccountChooserDialogView* dialog = + static_cast<AccountChooserDialogView*>(controller()->current_dialog()); + EXPECT_CALL(*this, OnChooseCredential( + Field(&password_manager::CredentialInfo::type, + password_manager::CredentialType::CREDENTIAL_TYPE_EMPTY))); + EXPECT_TRUE(dialog->Close()); +} + +} // namespace
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 19309d9..35df53ed 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi
@@ -413,8 +413,8 @@ 'browser/ssl/cert_verifier_browser_test.h', 'browser/ssl/certificate_reporting_test_utils.cc', 'browser/ssl/certificate_reporting_test_utils.h', - 'browser/ssl/chrome_ssl_host_state_delegate_test.cc', 'browser/ssl/chrome_security_state_model_client_browser_tests.cc', + 'browser/ssl/chrome_ssl_host_state_delegate_test.cc', 'browser/ssl/ssl_browser_tests.cc', 'browser/ssl/ssl_client_certificate_selector_test.cc', 'browser/ssl/ssl_client_certificate_selector_test.h', @@ -660,6 +660,7 @@ 'browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc', 'browser/ui/views/media_router/media_router_ui_browsertest.cc', 'browser/ui/views/new_task_manager_view_browsertest.cc', + 'browser/ui/views/passwords/password_dialog_view_browsertest.cc', 'browser/ui/views/toolbar/browser_actions_container_browsertest.cc', 'browser/ui/views/toolbar/toolbar_view_browsertest.cc', 'browser/ui/views/translate/translate_bubble_view_browsertest.cc', @@ -855,10 +856,10 @@ 'browser/safe_browsing/safe_browsing_blocking_page_test.cc', 'browser/safe_browsing/safe_browsing_service_browsertest.cc', 'browser/safe_browsing/safe_browsing_test.cc', - 'renderer/safe_browsing/threat_dom_details_browsertest.cc', 'renderer/safe_browsing/phishing_classifier_browsertest.cc', 'renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc', 'renderer/safe_browsing/phishing_dom_feature_extractor_browsertest.cc', + 'renderer/safe_browsing/threat_dom_details_browsertest.cc', ], 'chrome_browser_tests_remoting_sources': [ 'test/remoting/auth_browsertest.cc', @@ -1151,8 +1152,8 @@ 'browser/ui/views/location_bar/page_action_image_view_interactive_uitest.cc', 'browser/ui/views/location_bar/star_view_browsertest.cc', 'browser/ui/views/omnibox/omnibox_view_views_browsertest.cc', - 'browser/ui/views/passwords/manage_passwords_bubble_view_browsertest.cc', - 'browser/ui/views/passwords/manage_passwords_icon_view_browsertest.cc', + 'browser/ui/views/passwords/manage_passwords_bubble_view_interactive_uitest.cc', + 'browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc', 'browser/ui/views/ssl_client_certificate_selector_browsertest.cc', 'browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc', 'browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h',
diff --git a/chrome/common/extensions/manifest_handlers/automation.cc b/chrome/common/extensions/manifest_handlers/automation.cc index dfdb72c..2e22cb3 100644 --- a/chrome/common/extensions/manifest_handlers/automation.cc +++ b/chrome/common/extensions/manifest_handlers/automation.cc
@@ -91,12 +91,11 @@ automation_info_->matches, ®ular_hosts, &permissions); std::set<std::string> hosts = permission_message_util::GetDistinctHosts(regular_hosts, true, true); - if (!hosts.empty()) { - permission_message_util::AddHostPermissions( - &permissions, hosts, automation_info_->interact - ? permission_message_util::kReadWrite - : permission_message_util::kReadOnly); - } + APIPermission::ID permission_id = automation_info_->interact + ? APIPermission::kHostReadWrite + : APIPermission::kHostReadOnly; + for (const auto& host : hosts) + permissions.insert(permission_id, base::UTF8ToUTF16(host)); } return permissions; }
diff --git a/chrome/common/extensions/permissions/chrome_permission_message_provider.cc b/chrome/common/extensions/permissions/chrome_permission_message_provider.cc index 4561243..cc2aae7 100644 --- a/chrome/common/extensions/permissions/chrome_permission_message_provider.cc +++ b/chrome/common/extensions/permissions/chrome_permission_message_provider.cc
@@ -10,6 +10,7 @@ #include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" #include "chrome/common/extensions/permissions/chrome_permission_message_rules.h" #include "chrome/grit/generated_resources.h" #include "extensions/common/extensions_client.h" @@ -167,9 +168,9 @@ std::set<std::string> hosts = permission_message_util::GetDistinctHosts(regular_hosts, true, true); - if (!hosts.empty()) { - permission_message_util::AddHostPermissions( - permission_ids, hosts, permission_message_util::kReadWrite); + for (const auto& host : hosts) { + permission_ids->insert(APIPermission::kHostReadWrite, + base::UTF8ToUTF16(host)); } } }
diff --git a/chrome/common/extensions/permissions/chrome_permission_message_rules.cc b/chrome/common/extensions/permissions/chrome_permission_message_rules.cc index b56691489..dffd650 100644 --- a/chrome/common/extensions/permissions/chrome_permission_message_rules.cc +++ b/chrome/common/extensions/permissions/chrome_permission_message_rules.cc
@@ -9,7 +9,6 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "chrome/grit/generated_resources.h" -#include "grit/extensions_strings.h" #include "ui/base/l10n/l10n_util.h" namespace extensions { @@ -119,40 +118,34 @@ DISALLOW_COPY_AND_ASSIGN(SpaceSeparatedListFormatter); }; -// Creates a comma-separated list of permissions with the given PermissionID. -// The list is inserted into the messages with the given IDs: one for each case -// of 1-3 permissions, and the other for the case where there are 4 or more -// permissions. In the case of 4 or more permissions, rather than insert the -// list into the message, the permissions are displayed as submessages in the -// resultant PermissionMessage. -class CommaSeparatedListFormatter : public ChromePermissionMessageFormatter { +// Creates a list of host permissions. If there are 1-3 hosts, they are inserted +// directly into a message with a given ID. If there are more than 3, they are +// displayed as list of submessages instead. +class HostListFormatter : public ChromePermissionMessageFormatter { public: - CommaSeparatedListFormatter(int message_id_for_one_host, - int message_id_for_two_hosts, - int message_id_for_three_hosts, - int message_id_for_many_hosts) + HostListFormatter(int message_id_for_one_host, + int message_id_for_two_hosts, + int message_id_for_three_hosts, + int message_id_for_many_hosts) : message_id_for_one_host_(message_id_for_one_host), message_id_for_two_hosts_(message_id_for_two_hosts), message_id_for_three_hosts_(message_id_for_three_hosts), message_id_for_many_hosts_(message_id_for_many_hosts) {} - ~CommaSeparatedListFormatter() override {} + ~HostListFormatter() override {} PermissionMessage GetPermissionMessage( const PermissionIDSet& permissions) const override { - DCHECK(permissions.size() > 0); + DCHECK(!permissions.empty()); std::vector<base::string16> hostnames = - permissions.GetAllPermissionParameters(); - PermissionMessages messages; - if (hostnames.size() <= 3) { + GetHostMessages(permissions.GetAllPermissionParameters()); + int message_id = message_id_for_hosts(hostnames.size()); + if (hostnames.size() <= kMaxHostsInMainMessage) { return PermissionMessage( - l10n_util::GetStringFUTF16(message_id_for_hosts(hostnames.size()), - hostnames, NULL), + l10n_util::GetStringFUTF16(message_id, hostnames, nullptr), permissions); } - - return PermissionMessage( - l10n_util::GetStringUTF16(message_id_for_many_hosts_), permissions, - hostnames); + return PermissionMessage(l10n_util::GetStringUTF16(message_id), permissions, + hostnames); } private: @@ -169,12 +162,29 @@ } } + std::vector<base::string16> GetHostMessages( + const std::vector<base::string16>& hosts) const { + int msg_id = hosts.size() <= kMaxHostsInMainMessage + ? IDS_EXTENSION_PROMPT_WARNING_HOST_AND_SUBDOMAIN + : IDS_EXTENSION_PROMPT_WARNING_HOST_AND_SUBDOMAIN_LIST; + std::vector<base::string16> messages; + for (const base::string16& host : hosts) { + messages.push_back( + host[0] == '*' && host[1] == '.' + ? l10n_util::GetStringFUTF16(msg_id, host.substr(2)) + : host); + } + return messages; + } + + static const int kMaxHostsInMainMessage = 3; + int message_id_for_one_host_; int message_id_for_two_hosts_; int message_id_for_three_hosts_; int message_id_for_many_hosts_; - DISALLOW_COPY_AND_ASSIGN(CommaSeparatedListFormatter); + DISALLOW_COPY_AND_ASSIGN(HostListFormatter); }; class USBDevicesFormatter : public ChromePermissionMessageFormatter { @@ -372,17 +382,16 @@ APIPermission::kProcesses, APIPermission::kTab, APIPermission::kTopSites, APIPermission::kWebNavigation}}, - {new CommaSeparatedListFormatter(IDS_EXTENSION_PROMPT_WARNING_1_HOST, - IDS_EXTENSION_PROMPT_WARNING_2_HOSTS, - IDS_EXTENSION_PROMPT_WARNING_3_HOSTS, - IDS_EXTENSION_PROMPT_WARNING_HOSTS_LIST), + {new HostListFormatter(IDS_EXTENSION_PROMPT_WARNING_1_HOST, + IDS_EXTENSION_PROMPT_WARNING_2_HOSTS, + IDS_EXTENSION_PROMPT_WARNING_3_HOSTS, + IDS_EXTENSION_PROMPT_WARNING_HOSTS_LIST), {APIPermission::kHostReadWrite}, {}}, - {new CommaSeparatedListFormatter( - IDS_EXTENSION_PROMPT_WARNING_1_HOST_READ_ONLY, - IDS_EXTENSION_PROMPT_WARNING_2_HOSTS_READ_ONLY, - IDS_EXTENSION_PROMPT_WARNING_3_HOSTS_READ_ONLY, - IDS_EXTENSION_PROMPT_WARNING_HOSTS_LIST_READ_ONLY), + {new HostListFormatter(IDS_EXTENSION_PROMPT_WARNING_1_HOST_READ_ONLY, + IDS_EXTENSION_PROMPT_WARNING_2_HOSTS_READ_ONLY, + IDS_EXTENSION_PROMPT_WARNING_3_HOSTS_READ_ONLY, + IDS_EXTENSION_PROMPT_WARNING_HOSTS_LIST_READ_ONLY), {APIPermission::kHostReadOnly}, {}},
diff --git a/chrome/renderer/external_extension.cc b/chrome/renderer/external_extension.cc index 99b03a8..f3a60cf 100644 --- a/chrome/renderer/external_extension.cc +++ b/chrome/renderer/external_extension.cc
@@ -118,7 +118,7 @@ return; GURL osdd_url = GURL(webframe->document().url()).Resolve(osdd_string); - if (!osdd_url.is_empty() && osdd_url.is_valid()) { + if (osdd_url.is_valid()) { webframe->didCallAddSearchProvider(); render_view->Send(new ChromeViewHostMsg_PageHasOSDD( render_view->GetRoutingID(), webframe->document().url(), osdd_url,
diff --git a/components/scheduler/renderer/renderer_scheduler_impl.cc b/components/scheduler/renderer/renderer_scheduler_impl.cc index 5402dbee..d739302 100644 --- a/components/scheduler/renderer/renderer_scheduler_impl.cc +++ b/components/scheduler/renderer/renderer_scheduler_impl.cc
@@ -618,10 +618,10 @@ } MainThreadOnly().touchstart_expected_soon = touchstart_expected_soon; - base::TimeDelta expected_idle_duration = - MainThreadOnly().idle_time_estimator.GetExpectedIdleDuration( - MainThreadOnly().compositor_frame_interval); - MainThreadOnly().expected_idle_duration = expected_idle_duration; + base::TimeDelta longest_jank_free_task_duration = + EstimateLongestJankFreeTaskDuration(); + MainThreadOnly().longest_jank_free_task_duration = + longest_jank_free_task_duration; bool loading_tasks_seem_expensive = false; bool timer_tasks_seem_expensive = false; @@ -630,10 +630,10 @@ if (!MainThreadOnly().begin_frame_not_expected_soon) { loading_tasks_seem_expensive = MainThreadOnly().loading_task_cost_estimator.expected_task_duration() > - expected_idle_duration; + longest_jank_free_task_duration; timer_tasks_seem_expensive = MainThreadOnly().timer_task_cost_estimator.expected_task_duration() > - expected_idle_duration; + longest_jank_free_task_duration; } MainThreadOnly().timer_tasks_seem_expensive = timer_tasks_seem_expensive; MainThreadOnly().loading_tasks_seem_expensive = loading_tasks_seem_expensive; @@ -856,6 +856,26 @@ return UseCase::NONE; } +base::TimeDelta RendererSchedulerImpl::EstimateLongestJankFreeTaskDuration() + const { + switch (MainThreadOnly().current_use_case) { + case UseCase::TOUCHSTART: + case UseCase::COMPOSITOR_GESTURE: + case UseCase::LOADING: + case UseCase::NONE: + return base::TimeDelta::FromMilliseconds(kRailsResponseTimeMillis); + + case UseCase::MAIN_THREAD_GESTURE: + case UseCase::SYNCHRONIZED_GESTURE: + return MainThreadOnly().idle_time_estimator.GetExpectedIdleDuration( + MainThreadOnly().compositor_frame_interval); + + default: + NOTREACHED(); + return base::TimeDelta::FromMilliseconds(kRailsResponseTimeMillis); + } +} + bool RendererSchedulerImpl::CanEnterLongIdlePeriod( base::TimeTicks now, base::TimeDelta* next_long_idle_period_delay_out) { @@ -983,8 +1003,9 @@ .timer_task_cost_estimator.expected_task_duration() .InMillisecondsF()); // TODO(skyostil): Can we somehow trace how accurate these estimates were? - state->SetDouble("expected_idle_duration", - MainThreadOnly().expected_idle_duration.InMillisecondsF()); + state->SetDouble( + "longest_jank_free_task_duration", + MainThreadOnly().longest_jank_free_task_duration.InMillisecondsF()); state->SetDouble( "compositor_frame_interval", MainThreadOnly().compositor_frame_interval.InMillisecondsF());
diff --git a/components/scheduler/renderer/renderer_scheduler_impl.h b/components/scheduler/renderer/renderer_scheduler_impl.h index 0dd4da3..52868c9 100644 --- a/components/scheduler/renderer/renderer_scheduler_impl.h +++ b/components/scheduler/renderer/renderer_scheduler_impl.h
@@ -250,6 +250,10 @@ // nagigation. This function does that. Must be called from the main thread. void ResetForNavigationLocked(); + // Estimates the maximum task length that won't cause a jank based on the + // current system state. Must be called from the main thread. + base::TimeDelta EstimateLongestJankFreeTaskDuration() const; + SchedulerHelper helper_; IdleHelper idle_helper_; ThrottlingHelper throttling_helper_; @@ -283,7 +287,7 @@ base::TimeTicks current_policy_expiration_time; base::TimeTicks estimated_next_frame_begin; base::TimeDelta compositor_frame_interval; - base::TimeDelta expected_idle_duration; + base::TimeDelta longest_jank_free_task_duration; int timer_queue_suspend_count; // TIMER_TASK_QUEUE suspended if non-zero. int navigation_task_expected_count; bool renderer_hidden;
diff --git a/components/scheduler/renderer/renderer_scheduler_impl_unittest.cc b/components/scheduler/renderer/renderer_scheduler_impl_unittest.cc index 31b24a8..87d5cc3 100644 --- a/components/scheduler/renderer/renderer_scheduler_impl_unittest.cc +++ b/components/scheduler/renderer/renderer_scheduler_impl_unittest.cc
@@ -179,6 +179,7 @@ public: using RendererSchedulerImpl::OnIdlePeriodEnded; using RendererSchedulerImpl::OnIdlePeriodStarted; + using RendererSchedulerImpl::EstimateLongestJankFreeTaskDuration; RendererSchedulerImplForTest( scoped_refptr<SchedulerTqmDelegate> main_task_runner) @@ -428,6 +429,12 @@ scheduler_->DidCommitFrameToCompositor(); } + void SimulateMainThreadCompositorTask( + base::TimeDelta begin_main_frame_duration) { + clock_->Advance(begin_main_frame_duration); + scheduler_->DidCommitFrameToCompositor(); + } + void SimulateTimerTask(base::TimeDelta duration) { clock_->Advance(duration); simulate_timer_task_ran_ = true; @@ -537,6 +544,11 @@ RendererSchedulerImpl::kSuspendTimersWhenBackgroundedDelayMillis); } + static base::TimeDelta rails_response_time() { + return base::TimeDelta::FromMilliseconds( + RendererSchedulerImpl::kRailsResponseTimeMillis); + } + template <typename E> static void CallForEachEnumValue(E first, E last, @@ -2465,6 +2477,45 @@ } } +TEST_F(RendererSchedulerImplTest, + FourtyMsTimer_NotBlocked_CompositorScrolling) { + EnableTaskBlocking(); + scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true); + RunUntilIdle(); + for (int i = 0; i < 20; i++) { + simulate_timer_task_ran_ = false; + + cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( + BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), + base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); + begin_frame_args.on_critical_path = false; + scheduler_->WillBeginFrame(begin_frame_args); + scheduler_->DidAnimateForInputOnCompositorThread(); + + compositor_task_runner_->PostTask( + FROM_HERE, + base::Bind(&RendererSchedulerImplTest::SimulateMainThreadCompositorTask, + base::Unretained(this), + base::TimeDelta::FromMilliseconds(8))); + timer_task_runner_->PostTask( + FROM_HERE, base::Bind(&RendererSchedulerImplTest::SimulateTimerTask, + base::Unretained(this), + base::TimeDelta::FromMilliseconds(40))); + + RunUntilIdle(); + EXPECT_TRUE(simulate_timer_task_ran_) << " i = " << i; + EXPECT_EQ(RendererScheduler::UseCase::COMPOSITOR_GESTURE, CurrentUseCase()) + << " i = " << i; + EXPECT_FALSE(LoadingTasksSeemExpensive()) << " i = " << i; + EXPECT_FALSE(TimerTasksSeemExpensive()) << " i = " << i; + + base::TimeDelta time_till_next_frame = + EstimatedNextFrameBegin() - clock_->NowTicks(); + if (time_till_next_frame > base::TimeDelta()) + clock_->Advance(time_till_next_frame); + } +} + TEST_F(RendererSchedulerImplTest, ExpensiveTimer_Blocked) { EnableTaskBlocking(); scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true); @@ -2509,4 +2560,74 @@ } } +TEST_F(RendererSchedulerImplTest, + EstimateLongestJankFreeTaskDuration_UseCase_NONE) { + EXPECT_EQ(UseCase::NONE, CurrentUseCase()); + EXPECT_EQ(rails_response_time(), + scheduler_->EstimateLongestJankFreeTaskDuration()); +} + +TEST_F(RendererSchedulerImplTest, + EstimateLongestJankFreeTaskDuration_UseCase_COMPOSITOR_GESTURE) { + SimulateCompositorGestureStart(TouchEventPolicy::DONT_SEND_TOUCH_START); + EXPECT_EQ(UseCase::COMPOSITOR_GESTURE, + ForceUpdatePolicyAndGetCurrentUseCase()); + EXPECT_EQ(rails_response_time(), + scheduler_->EstimateLongestJankFreeTaskDuration()); +} + +// TODO(alexclarke): Reenable once we've reinstaed the Loading UseCase. +TEST_F(RendererSchedulerImplTest, + DISABLED_EstimateLongestJankFreeTaskDuration_UseCase_) { + scheduler_->OnNavigationStarted(); + EXPECT_EQ(UseCase::LOADING, ForceUpdatePolicyAndGetCurrentUseCase()); + EXPECT_EQ(rails_response_time(), + scheduler_->EstimateLongestJankFreeTaskDuration()); +} + +TEST_F(RendererSchedulerImplTest, + EstimateLongestJankFreeTaskDuration_UseCase_MAIN_THREAD_GESTURE) { + cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( + BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), + base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); + begin_frame_args.on_critical_path = false; + scheduler_->WillBeginFrame(begin_frame_args); + + compositor_task_runner_->PostTask( + FROM_HERE, + base::Bind( + &RendererSchedulerImplTest::SimulateMainThreadGestureCompositorTask, + base::Unretained(this), base::TimeDelta::FromMilliseconds(5))); + + RunUntilIdle(); + EXPECT_EQ(UseCase::MAIN_THREAD_GESTURE, CurrentUseCase()); + + // 16ms frame - 5ms compositor work = 11ms for other stuff. + EXPECT_EQ(base::TimeDelta::FromMilliseconds(11), + scheduler_->EstimateLongestJankFreeTaskDuration()); +} + +TEST_F(RendererSchedulerImplTest, + EstimateLongestJankFreeTaskDuration_UseCase_SYNCHRONIZED_GESTURE) { + SimulateCompositorGestureStart(TouchEventPolicy::DONT_SEND_TOUCH_START); + + cc::BeginFrameArgs begin_frame_args = cc::BeginFrameArgs::Create( + BEGINFRAME_FROM_HERE, clock_->NowTicks(), base::TimeTicks(), + base::TimeDelta::FromMilliseconds(16), cc::BeginFrameArgs::NORMAL); + begin_frame_args.on_critical_path = true; + scheduler_->WillBeginFrame(begin_frame_args); + + compositor_task_runner_->PostTask( + FROM_HERE, + base::Bind(&RendererSchedulerImplTest::SimulateMainThreadCompositorTask, + base::Unretained(this), base::TimeDelta::FromMilliseconds(5))); + + RunUntilIdle(); + EXPECT_EQ(UseCase::SYNCHRONIZED_GESTURE, CurrentUseCase()); + + // 16ms frame - 5ms compositor work = 11ms for other stuff. + EXPECT_EQ(base::TimeDelta::FromMilliseconds(11), + scheduler_->EstimateLongestJankFreeTaskDuration()); +} + } // namespace scheduler
diff --git a/content/browser/screen_orientation/screen_orientation_browsertest.cc b/content/browser/screen_orientation/screen_orientation_browsertest.cc index 77675b99..ca895018 100644 --- a/content/browser/screen_orientation/screen_orientation_browsertest.cc +++ b/content/browser/screen_orientation/screen_orientation_browsertest.cc
@@ -227,18 +227,22 @@ // Check that when --disable-screen-orientation-lock is passed to the command // line, screen.orientation.lock() correctly reports to not be supported. -// Flaky: https://crbug.com/498236 -IN_PROC_BROWSER_TEST_F(ScreenOrientationLockDisabledBrowserTest, - DISABLED_NotSupported) { +IN_PROC_BROWSER_TEST_F(ScreenOrientationLockDisabledBrowserTest, NotSupported) { GURL test_url = GetTestUrl("screen_orientation", "screen_orientation_lock_disabled.html"); - TestNavigationObserver navigation_observer(shell()->web_contents(), 2); + TestNavigationObserver navigation_observer(shell()->web_contents(), 1); shell()->LoadURL(test_url); navigation_observer.Wait(); - EXPECT_EQ("NotSupportedError", - shell()->web_contents()->GetLastCommittedURL().ref()); + { + ASSERT_TRUE(ExecuteScript(shell()->web_contents(), "run();")); + + TestNavigationObserver navigation_observer(shell()->web_contents(), 1); + navigation_observer.Wait(); + EXPECT_EQ("NotSupportedError", + shell()->web_contents()->GetLastCommittedURL().ref()); + } } #endif // defined(OS_ANDROID)
diff --git a/content/test/data/screen_orientation/screen_orientation_lock_disabled.html b/content/test/data/screen_orientation/screen_orientation_lock_disabled.html index e7faecf..26aae23 100644 --- a/content/test/data/screen_orientation/screen_orientation_lock_disabled.html +++ b/content/test/data/screen_orientation/screen_orientation_lock_disabled.html
@@ -3,11 +3,13 @@ <head> </head> <script> +function run() { screen.orientation.lock('portrait-secondary').then(function() { document.location.hash = '#success'; }, function(e) { document.location.hash = '#' + e.name; }); +} </script> <body> <div>Starting...</div>
diff --git a/extensions/common/permissions/permission_message_util.cc b/extensions/common/permissions/permission_message_util.cc index b69e65b..0ee3abf 100644 --- a/extensions/common/permissions/permission_message_util.cc +++ b/extensions/common/permissions/permission_message_util.cc
@@ -7,14 +7,9 @@ #include <stddef.h> #include <vector> -#include "base/strings/string16.h" #include "base/strings/string_split.h" -#include "base/strings/utf_string_conversions.h" -#include "extensions/common/permissions/api_permission_set.h" #include "extensions/common/url_pattern_set.h" -#include "grit/extensions_strings.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" -#include "ui/base/l10n/l10n_util.h" #include "url/url_constants.h" using extensions::URLPatternSet; @@ -38,46 +33,6 @@ namespace permission_message_util { -// The number of host messages supported. The first N - 1 of these messages are -// specific for the number of hosts; the last one is a catch-all for N or more -// hosts. -static const int kNumMessages = 4; - -// Gets a list of hosts to display in a permission message from the given list -// of hosts from the manifest. -std::vector<base::string16> GetHostListFromHosts( - const std::set<std::string>& hosts) { - int host_msg_id = hosts.size() < kNumMessages - ? IDS_EXTENSION_PROMPT_WARNING_HOST_AND_SUBDOMAIN - : IDS_EXTENSION_PROMPT_WARNING_HOST_AND_SUBDOMAIN_LIST; - std::vector<base::string16> host_list; - for (const std::string& host : hosts) { - host_list.push_back( - host[0] == '*' && host[1] == '.' - ? l10n_util::GetStringFUTF16(host_msg_id, - base::UTF8ToUTF16(host.substr(2))) - : base::UTF8ToUTF16(host)); - } - DCHECK(host_list.size()); - return host_list; -} - -void AddHostPermissions(extensions::PermissionIDSet* permissions, - const std::set<std::string>& hosts, - PermissionMessageProperties properties) { - std::vector<base::string16> host_list = GetHostListFromHosts(hosts); - - // Create a separate permission for each host, and add it to the permissions - // list. - // TODO(sashab): Add coalescing rules for kHostReadOnly and kHostReadWrite - // to mimic the current behavior of GetHostListFromHosts() above. - extensions::APIPermission::ID permission_id = - properties == kReadOnly ? extensions::APIPermission::kHostReadOnly - : extensions::APIPermission::kHostReadWrite; - for (const auto& host : host_list) - permissions->insert(permission_id, host); -} - std::set<std::string> GetDistinctHosts(const URLPatternSet& host_patterns, bool include_rcd, bool exclude_file_scheme) {
diff --git a/extensions/common/permissions/permission_message_util.h b/extensions/common/permissions/permission_message_util.h index 5e33c07..66dd01d 100644 --- a/extensions/common/permissions/permission_message_util.h +++ b/extensions/common/permissions/permission_message_util.h
@@ -9,19 +9,11 @@ #include <string> namespace extensions { -class PermissionIDSet; class URLPatternSet; } namespace permission_message_util { -enum PermissionMessageProperties { kReadOnly, kReadWrite }; - -// Adds the appropriate permissions from the given |hosts| to |permissions|. -void AddHostPermissions(extensions::PermissionIDSet* permissions, - const std::set<std::string>& hosts, - PermissionMessageProperties properties); - std::set<std::string> GetDistinctHosts( const extensions::URLPatternSet& host_patterns, bool include_rcd,
diff --git a/extensions/extensions_strings.grd b/extensions/extensions_strings.grd index 0ccbcbdd..ecdc0fa 100644 --- a/extensions/extensions_strings.grd +++ b/extensions/extensions_strings.grd
@@ -189,14 +189,6 @@ extension <ph name="EXTENSION_NAME">$1<ex>Adblock</ex></ph> </message> - <!-- Host access permissions. Please keep alphabetized. --> - <message name="IDS_EXTENSION_PROMPT_WARNING_HOST_AND_SUBDOMAIN" desc="Permission string requesting access to data on a website and its sub-domains."> - all <ph name="WEBSITE_1">$1<ex>google.com</ex></ph> sites - </message> - <message name="IDS_EXTENSION_PROMPT_WARNING_HOST_AND_SUBDOMAIN_LIST" desc="Single entry permission string requesting access to data on a website and its sub-domains."> - All <ph name="WEBSITE_1">$1<ex>google.com</ex></ph> sites - </message> - <!-- Policy strings. Please keep alphabetized. --> <message name="IDS_EXTENSION_CANT_INSTALL_POLICY_BLOCKED" desc="Error message when a user tries to install an extension that is blocked by administrator policy."> <ph name="EXTENSION_NAME">$1<ex>Google Talk</ex></ph> (extension ID "<ph name="EXTENSION_ID">$2<ex>nckgahadagoaajjgafhacjanaoiihapd</ex></ph>") is blocked by the administrator.
diff --git a/third_party/WebKit/LayoutTests/http/tests/notifications/resources/serviceworker-notification-event.js b/third_party/WebKit/LayoutTests/http/tests/notifications/resources/serviceworker-notification-event.js index a46d320e..91ca1368 100644 --- a/third_party/WebKit/LayoutTests/http/tests/notifications/resources/serviceworker-notification-event.js +++ b/third_party/WebKit/LayoutTests/http/tests/notifications/resources/serviceworker-notification-event.js
@@ -1,30 +1,41 @@ importScripts('../../serviceworker/resources/worker-testharness.js'); -importScripts('/resources/testharness-helpers.js'); -test(function() { +let messagePort = null; +addEventListener('message', workerEvent => { + messagePort = workerEvent.data; + messagePort.postMessage('ready'); +}); + +addEventListener('notificationclick', e => runTest(e.notification)); + +// Test body for the serviceworker-notification-event.html layout test. +function runTest(notification) { assert_true('NotificationEvent' in self); - var event = new NotificationEvent('NotificationEvent'); + assert_throws(null, () => new NotificationEvent('NotificationEvent')); + assert_throws(null, () => new NotificationEvent('NotificationEvent', {})); + + const event = new NotificationEvent('NotificationEvent', { notification }); + assert_equals(event.type, 'NotificationEvent'); - assert_will_be_idl_attribute(event, 'notification'); - assert_will_be_idl_attribute(event, 'action'); + assert_idl_attribute(event, 'notification'); + assert_idl_attribute(event, 'action'); assert_equals(event.cancelable, false); assert_equals(event.bubbles, false); - assert_equals(event.notification, null); - assert_equals(event.action, ""); + assert_equals(event.notification, notification); + assert_equals(event.action, ''); assert_inherits(event, 'waitUntil'); - var eventWithInit = new NotificationEvent('NotificationEvent', - { cancelable: true, - bubbles: true - }); - assert_equals(eventWithInit.cancelable, true); - assert_equals(eventWithInit.bubbles, true); + const customEvent = new NotificationEvent('NotificationEvent', { + notification: notification, + bubbles: true, + cancelable: true }); -}, 'NotificationEvent is exposed, and has the expected interface.'); + assert_equals(customEvent.type, 'NotificationEvent'); + assert_equals(customEvent.cancelable, true); + assert_equals(customEvent.bubbles, true); + assert_equals(customEvent.notification, notification); -test(function() { - assert_will_be_idl_attribute(self, 'onnotificationclick', - 'The notificationclick event exists.'); - -}, 'The notificationclick event exists on the global scope.'); + // Signal to the document that the test has finished running. + messagePort.postMessage(true /* success */); +}
diff --git a/third_party/WebKit/LayoutTests/http/tests/notifications/serviceworker-notification-event.html b/third_party/WebKit/LayoutTests/http/tests/notifications/serviceworker-notification-event.html index d91d6508..51ed274 100644 --- a/third_party/WebKit/LayoutTests/http/tests/notifications/serviceworker-notification-event.html +++ b/third_party/WebKit/LayoutTests/http/tests/notifications/serviceworker-notification-event.html
@@ -5,15 +5,50 @@ <script src="../resources/testharness.js"></script> <script src="../resources/testharnessreport.js"></script> <script src="../serviceworker/resources/test-helpers.js"></script> + <script src="resources/test-helpers.js"></script> </head> <body> <script> // Tests that the NotificationEvent object is exposed in a ServiceWorker - // and exposes the "notification" and "waitUntil" members, as well as the - // "notificationclick" and "notificationerror" events. - service_worker_test( - 'resources/serviceworker-notification-event.js', - 'Exposure of the NotificationEvent object in a ServiceWorker.'); + // and exposes the "notification", "action" and "waitUntil" members. + // + // Because the NotificationEvent must be created with a notification, a + // notification is shown and clicked on prior to running the actual test + // in the Service Worker (serviceworker-notification-event.js). + async_test(function(test) { + var scope = 'resources/scope/' + location.pathname, + script = 'resources/serviceworker-notification-event.js'; + + testRunner.setPermission('notifications', 'granted', location.origin, location.origin); + + var workerInfo = null; + getActiveServiceWorkerWithMessagePort(test, script, scope).then(function(info) { + workerInfo = info; + + // (1) Display a Web Notification from the document. + assert_inherits(workerInfo.registration, 'showNotification', 'showNotification() must be exposed.'); + return workerInfo.registration.showNotification(scope, { + body: 'Hello, world!', + icon: '/icon.png' + }); + }).then(function() { + // (2) Simulate a click on the notification that has been displayed. + testRunner.simulateWebNotificationClick(scope); + + workerInfo.port.addEventListener('message', function(event) { + if (typeof event.data != 'boolean') { + assert_unreached('Received an invalid message from the Service Worker.'); + return; + } + + // (3) Verify that the tests were successful in the Service Worker. + assert_true(event.data); + + test.done(); + }); + }).catch(unreached_rejection(test)); + + }, 'The NotificationEvent exposes the expected semantics in a Service Worker.'); </script> </body> -</html> +</html> \ No newline at end of file
diff --git a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp index 203b3d8..506828c 100644 --- a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp +++ b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
@@ -295,6 +295,13 @@ return cssValuePool().createIdentifierValue(range.consumeIncludingWhitespace().id()); } +static PassRefPtrWillBeRawPtr<CSSPrimitiveValue> consumeIdentRange(CSSParserTokenRange& range, CSSValueID lower, CSSValueID upper) +{ + if (range.peek().id() < lower || range.peek().id() > upper) + return nullptr; + return consumeIdent(range); +} + static PassRefPtrWillBeRawPtr<CSSCustomIdentValue> consumeCustomIdent(CSSParserTokenRange& range) { if (range.peek().type() != IdentToken) @@ -1107,7 +1114,7 @@ static PassRefPtrWillBeRawPtr<CSSValue> consumeGenericFamily(CSSParserTokenRange& range) { - return consumeIdent<CSSValueSerif, CSSValueSansSerif, CSSValueCursive, CSSValueFantasy, CSSValueMonospace, CSSValueWebkitBody>(range); + return consumeIdentRange(range, CSSValueSerif, CSSValueWebkitBody); } static PassRefPtrWillBeRawPtr<CSSValueList> consumeFontFamily(CSSParserTokenRange& range) @@ -2191,7 +2198,7 @@ range.consumeIncludingWhitespace(); return cssValuePool().createValue(percent, CSSPrimitiveValue::UnitType::Percentage); } - return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Forbid); + return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll); } static PassRefPtrWillBeRawPtr<CSSValue> consumePositionX(CSSParserTokenRange& range, CSSParserMode cssParserMode) @@ -2316,7 +2323,7 @@ CSSValueID id = range.peek().id(); if (id == CSSValueBaseline || id == CSSValueSub || id == CSSValueSuper) return consumeIdent(range); - return consumeLengthOrPercent(range, SVGAttributeMode, ValueRangeAll, UnitlessQuirk::Forbid); + return consumeLengthOrPercent(range, SVGAttributeMode, ValueRangeAll); } static PassRefPtrWillBeRawPtr<CSSValue> consumeImageSet(CSSParserTokenRange& range, const CSSParserContext& context) @@ -2934,6 +2941,14 @@ return CSSValuePair::create(parsedValue1.release(), parsedValue2.release(), CSSValuePair::DropIdenticalValues); } +static PassRefPtrWillBeRawPtr<CSSPrimitiveValue> consumeVerticalAlign(CSSParserTokenRange& range, CSSParserMode cssParserMode) +{ + RefPtrWillBeRawPtr<CSSPrimitiveValue> parsedValue = consumeIdentRange(range, CSSValueBaseline, CSSValueWebkitBaselineMiddle); + if (!parsedValue) + parsedValue = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow); + return parsedValue.release(); +} + PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseSingleValue(CSSPropertyID unresolvedProperty) { CSSPropertyID property = resolveCSSPropertyID(unresolvedProperty); @@ -3154,6 +3169,8 @@ case CSSPropertyFillOpacity: case CSSPropertyStopOpacity: case CSSPropertyFloodOpacity: + case CSSPropertyOpacity: + case CSSPropertyWebkitBoxFlex: return consumeNumber(m_range, ValueRangeAll); case CSSPropertyBaselineShift: return consumeBaselineShift(m_range); @@ -3191,6 +3208,16 @@ case CSSPropertyBorderBottomLeftRadius: case CSSPropertyBorderBottomRightRadius: return consumeBorderRadiusCorner(m_range, m_context.mode()); + case CSSPropertyWebkitBoxFlexGroup: + return consumeInteger(m_range, 0); + case CSSPropertyOrder: + return consumeInteger(m_range); + case CSSPropertyTextUnderlinePosition: + // auto | [ under || [ left | right ] ], but we only support auto | under for now + ASSERT(RuntimeEnabledFeatures::css3TextDecorationsEnabled()); + return consumeIdent<CSSValueAuto, CSSValueUnder>(m_range); + case CSSPropertyVerticalAlign: + return consumeVerticalAlign(m_range, m_context.mode()); default: return nullptr; }
diff --git a/third_party/WebKit/Source/core/css/parser/LegacyCSSPropertyParser.cpp b/third_party/WebKit/Source/core/css/parser/LegacyCSSPropertyParser.cpp index 761c7c3..4287922 100644 --- a/third_party/WebKit/Source/core/css/parser/LegacyCSSPropertyParser.cpp +++ b/third_party/WebKit/Source/core/css/parser/LegacyCSSPropertyParser.cpp
@@ -403,16 +403,6 @@ validPrimitive = validUnit(value, FLength | FNonNeg | unitless); break; - case CSSPropertyVerticalAlign: - // baseline | sub | super | top | text-top | middle | bottom | text-bottom | - // <percentage> | <length> | inherit - - if (id >= CSSValueBaseline && id <= CSSValueWebkitBaselineMiddle) - validPrimitive = true; - else - validPrimitive = validUnit(value, FLength | FPercent | FUnitlessQuirk); - break; - case CSSPropertyBottom: // <length> | <percentage> | auto | inherit case CSSPropertyLeft: // <length> | <percentage> | auto | inherit case CSSPropertyRight: // <length> | <percentage> | auto | inherit @@ -423,12 +413,6 @@ validPrimitive = validUnit(value, FLength | FPercent | FUnitlessQuirk); break; - case CSSPropertyTextUnderlinePosition: - // auto | [ under || [ left | right ] ], but we only support auto | under for now - ASSERT(RuntimeEnabledFeatures::css3TextDecorationsEnabled()); - validPrimitive = (id == CSSValueAuto || id == CSSValueUnder); - break; - case CSSPropertySrc: case CSSPropertyUnicodeRange: /* @font-face only descriptors */ @@ -493,17 +477,6 @@ ASSERT(RuntimeEnabledFeatures::cssFontSizeAdjustEnabled()); validPrimitive = (id == CSSValueNone) ? true : validUnit(value, FNumber | FNonNeg); break; - case CSSPropertyOpacity: - case CSSPropertyWebkitBoxFlex: - validPrimitive = validUnit(value, FNumber); - break; - case CSSPropertyWebkitBoxFlexGroup: - validPrimitive = validUnit(value, FInteger | FNonNeg); - break; - case CSSPropertyOrder: - validPrimitive = validUnit(value, FInteger); - break; - case CSSPropertyJustifyContent: ASSERT(RuntimeEnabledFeatures::cssGridLayoutEnabled()); parsedValue = parseContentDistributionOverflowPosition(); @@ -821,6 +794,8 @@ case CSSPropertyFillOpacity: case CSSPropertyStopOpacity: case CSSPropertyFloodOpacity: + case CSSPropertyOpacity: + case CSSPropertyWebkitBoxFlex: case CSSPropertyBaselineShift: case CSSPropertyStrokeMiterlimit: case CSSPropertyStrokeWidth: @@ -849,6 +824,10 @@ case CSSPropertyBorderBottomRightRadius: case CSSPropertyBorderRadius: case CSSPropertyAliasWebkitBorderRadius: + case CSSPropertyWebkitBoxFlexGroup: + case CSSPropertyOrder: + case CSSPropertyTextUnderlinePosition: + case CSSPropertyVerticalAlign: validPrimitive = false; break;
diff --git a/third_party/WebKit/Source/modules/notifications/NotificationEvent.idl b/third_party/WebKit/Source/modules/notifications/NotificationEvent.idl index 2d4045d..42617777 100644 --- a/third_party/WebKit/Source/modules/notifications/NotificationEvent.idl +++ b/third_party/WebKit/Source/modules/notifications/NotificationEvent.idl
@@ -5,7 +5,7 @@ // https://notifications.spec.whatwg.org/#service-worker-api [ - Constructor(DOMString type, optional NotificationEventInit eventInitDict), + Constructor(DOMString type, NotificationEventInit eventInitDict), Exposed=ServiceWorker, RuntimeEnabled=Notifications, ] interface NotificationEvent : ExtendableEvent {
diff --git a/third_party/WebKit/Source/modules/notifications/NotificationEventInit.idl b/third_party/WebKit/Source/modules/notifications/NotificationEventInit.idl index 460ee7a6..79ab16b 100644 --- a/third_party/WebKit/Source/modules/notifications/NotificationEventInit.idl +++ b/third_party/WebKit/Source/modules/notifications/NotificationEventInit.idl
@@ -5,6 +5,6 @@ // https://notifications.spec.whatwg.org/#notificationevent dictionary NotificationEventInit : ExtendableEventInit { - Notification notification; + required Notification notification; DOMString action = ""; };
diff --git a/third_party/brotli/README.chromium b/third_party/brotli/README.chromium index 972cdb3..813e758 100644 --- a/third_party/brotli/README.chromium +++ b/third_party/brotli/README.chromium
@@ -1,6 +1,6 @@ Name: Brotli URL: https://github.com/google/brotli -Version: c90ec29f54e9165df815b0f1311cfddb1be4afad +Version: 66db08156eba94f1fb0f77b6af519e4adedea8bf License: Apache 2.0 License File: LICENSE Security Critical: yes @@ -11,8 +11,8 @@ to decode WOFF 2.0 fonts. Local Modifications: -- This only includes the dec/ directory and the README.md file, - removing unneeded direcories such as encoder, tests, and tools. +- This only includes the dec/ directory, the README.md and the LICENSE + files, removing unneeded direcories such as encoder, tests, and tools. - .gitignore: Added. - BUILD.gn: Added. - brotli.gyp: Added.
diff --git a/third_party/brotli/dec/bit_reader.h b/third_party/brotli/dec/bit_reader.h index f390348..468afe1 100644 --- a/third_party/brotli/dec/bit_reader.h +++ b/third_party/brotli/dec/bit_reader.h
@@ -18,6 +18,7 @@ #ifndef BROTLI_DEC_BIT_READER_H_ #define BROTLI_DEC_BIT_READER_H_ +#include <stdio.h> #include <string.h> #include "./port.h" #include "./types.h"
diff --git a/third_party/brotli/dec/decode.c b/third_party/brotli/dec/decode.c index bd0d2132..5d4af1d9 100644 --- a/third_party/brotli/dec/decode.c +++ b/third_party/brotli/dec/decode.c
@@ -73,6 +73,34 @@ #define NUM_DISTANCE_SHORT_CODES 16 +BrotliState* BrotliCreateState( + brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) { + BrotliState* state = 0; + if (!alloc_func && !free_func) { + state = (BrotliState*)malloc(sizeof(BrotliState)); + } else if (alloc_func && free_func) { + state = (BrotliState*)alloc_func(opaque, sizeof(BrotliState)); + } + if (state == 0) { + (void)BROTLI_FAILURE(); + return 0; + } + BrotliStateInitWithCustomAllocators(state, alloc_func, free_func, opaque); + return state; +} + +/* Deinitializes and frees BrotliState instance. */ +void BrotliDestroyState(BrotliState* state) { + if (!state) { + return; + } else { + brotli_free_func free_func = state->free_func; + void* opaque = state->memory_manager_opaque; + BrotliStateCleanup(state); + free_func(opaque, state); + } +} + /* Decodes a number in the range [9..24], by reading 1 - 7 bits. Precondition: bit-reader accumulator has at least 7 bits. */ static uint32_t DecodeWindowBits(BrotliBitReader* br) { @@ -907,7 +935,7 @@ s->context_index = 0; BROTLI_LOG_UINT(context_map_size); BROTLI_LOG_UINT(*num_htrees); - *context_map_arg = (uint8_t*)malloc((size_t)context_map_size); + *context_map_arg = (uint8_t*)BROTLI_ALLOC(s, (size_t)context_map_size); if (*context_map_arg == 0) { return BROTLI_FAILURE(); } @@ -1006,7 +1034,7 @@ /* Decodes a command or literal and updates block type ringbuffer. Reads 3..54 bits. */ -static int BROTLI_INLINE DecodeBlockTypeAndLength(int safe, +static BROTLI_INLINE int DecodeBlockTypeAndLength(int safe, BrotliState* s, int tree_type) { uint32_t max_block_type = s->num_block_types[tree_type]; int tree_offset = tree_type * BROTLI_HUFFMAN_MAX_TABLE_SIZE; @@ -1048,7 +1076,7 @@ /* Decodes the block type and updates the state for literal context. Reads 3..54 bits. */ -static int BROTLI_INLINE DecodeLiteralBlockSwitchInternal(int safe, +static BROTLI_INLINE int DecodeLiteralBlockSwitchInternal(int safe, BrotliState* s) { uint8_t context_mode; uint32_t context_offset; @@ -1075,7 +1103,7 @@ /* Block switch for insert/copy length. Reads 3..54 bits. */ -static int BROTLI_INLINE DecodeCommandBlockSwitchInternal(int safe, +static BROTLI_INLINE int DecodeCommandBlockSwitchInternal(int safe, BrotliState* s) { if (!DecodeBlockTypeAndLength(safe, s, 1)) { return 0; @@ -1093,7 +1121,7 @@ /* Block switch for distance codes. Reads 3..54 bits. */ -static int BROTLI_INLINE DecodeDistanceBlockSwitchInternal(int safe, +static BROTLI_INLINE int DecodeDistanceBlockSwitchInternal(int safe, BrotliState* s) { if (!DecodeBlockTypeAndLength(safe, s, 2)) { return 0; @@ -1255,7 +1283,7 @@ } s->ringbuffer_mask = s->ringbuffer_size - 1; - s->ringbuffer = (uint8_t*)malloc((size_t)(s->ringbuffer_size + + s->ringbuffer = (uint8_t*)BROTLI_ALLOC(s, (size_t)(s->ringbuffer_size + kRingBufferWriteAheadSlack + kBrotliMaxDictionaryWordLength)); if (s->ringbuffer == 0) { return 0; @@ -1289,7 +1317,7 @@ return BROTLI_RESULT_SUCCESS; } -static void BROTLI_INLINE TakeDistanceFromRingBuffer(BrotliState* s) { +static BROTLI_INLINE void TakeDistanceFromRingBuffer(BrotliState* s) { if (s->distance_code == 0) { --s->dist_rb_idx; s->distance_code = s->dist_rb[s->dist_rb_idx & 3]; @@ -1329,7 +1357,7 @@ } /* Precondition: s->distance_code < 0 */ -static int BROTLI_INLINE ReadDistanceInternal(int safe, +static BROTLI_INLINE int ReadDistanceInternal(int safe, BrotliState* s, BrotliBitReader* br) { int distval; BrotliBitReaderState memento; @@ -1386,15 +1414,15 @@ return 1; } -static void BROTLI_INLINE ReadDistance(BrotliState* s, BrotliBitReader* br) { +static BROTLI_INLINE void ReadDistance(BrotliState* s, BrotliBitReader* br) { ReadDistanceInternal(0, s, br); } -static int BROTLI_INLINE SafeReadDistance(BrotliState* s, BrotliBitReader* br) { +static BROTLI_INLINE int SafeReadDistance(BrotliState* s, BrotliBitReader* br) { return ReadDistanceInternal(1, s, br); } -static int BROTLI_INLINE ReadCommandInternal(int safe, +static BROTLI_INLINE int ReadCommandInternal(int safe, BrotliState* s, BrotliBitReader* br, int* insert_length) { uint32_t cmd_code; uint32_t insert_len_extra = 0; @@ -1432,12 +1460,12 @@ return 1; } -static void BROTLI_INLINE ReadCommand(BrotliState* s, BrotliBitReader* br, +static BROTLI_INLINE void ReadCommand(BrotliState* s, BrotliBitReader* br, int* insert_length) { ReadCommandInternal(0, s, br, insert_length); } -static int BROTLI_INLINE SafeReadCommand(BrotliState* s, BrotliBitReader* br, +static BROTLI_INLINE int SafeReadCommand(BrotliState* s, BrotliBitReader* br, int* insert_length) { return ReadCommandInternal(1, s, br, insert_length); } @@ -1833,10 +1861,10 @@ size_t total_out; if (s->legacy_input_buffer == 0) { - s->legacy_input_buffer = (uint8_t*)malloc(kBufferSize); + s->legacy_input_buffer = (uint8_t*)BROTLI_ALLOC(s, kBufferSize); } if (s->legacy_output_buffer == 0) { - s->legacy_output_buffer = (uint8_t*)malloc(kBufferSize); + s->legacy_output_buffer = (uint8_t*)BROTLI_ALLOC(s, kBufferSize); } if (s->legacy_input_buffer == 0 || s->legacy_output_buffer == 0) { return BROTLI_FAILURE(); @@ -2010,7 +2038,7 @@ s->max_backward_distance - s->custom_dict_size; /* Allocate memory for both block_type_trees and block_len_trees. */ - s->block_type_trees = (HuffmanCode*)malloc( + s->block_type_trees = (HuffmanCode*)BROTLI_ALLOC(s, 6 * BROTLI_HUFFMAN_MAX_TABLE_SIZE * sizeof(HuffmanCode)); if (s->block_type_trees == 0) { result = BROTLI_FAILURE(); @@ -2145,7 +2173,8 @@ BROTLI_LOG_UINT(s->num_direct_distance_codes); BROTLI_LOG_UINT(s->distance_postfix_bits); s->distance_postfix_mask = (int)BitMask(s->distance_postfix_bits); - s->context_modes = (uint8_t*)malloc((size_t)s->num_block_types[0]); + s->context_modes = + (uint8_t*)BROTLI_ALLOC(s, (size_t)s->num_block_types[0]); if (s->context_modes == 0) { result = BROTLI_FAILURE(); break; @@ -2188,13 +2217,13 @@ if (result != BROTLI_RESULT_SUCCESS) { break; } - BrotliHuffmanTreeGroupInit( - &s->literal_hgroup, kNumLiteralCodes, s->num_literal_htrees); - BrotliHuffmanTreeGroupInit( - &s->insert_copy_hgroup, kNumInsertAndCopyCodes, + BrotliHuffmanTreeGroupInit(s, &s->literal_hgroup, kNumLiteralCodes, + s->num_literal_htrees); + BrotliHuffmanTreeGroupInit(s, &s->insert_copy_hgroup, + kNumInsertAndCopyCodes, s->num_block_types[1]); - BrotliHuffmanTreeGroupInit( - &s->distance_hgroup, num_distance_codes, s->num_dist_htrees); + BrotliHuffmanTreeGroupInit(s, &s->distance_hgroup, num_distance_codes, + s->num_dist_htrees); if (s->literal_hgroup.codes == 0 || s->insert_copy_hgroup.codes == 0 || s->distance_hgroup.codes == 0) {
diff --git a/third_party/brotli/dec/decode.h b/third_party/brotli/dec/decode.h index 78a156e..57b68615 100644 --- a/third_party/brotli/dec/decode.h +++ b/third_party/brotli/dec/decode.h
@@ -51,6 +51,16 @@ } #endif +/* Creates the instance of BrotliState and initializes it. alloc_func and + free_func MUST be both zero or both non-zero. In the case they are both zero, + default memory allocators are used. opaque parameter is passed to alloc_func + and free_func when they are called. */ +BrotliState* BrotliCreateState( + brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque); + +/* Deinitializes and frees BrotliState instance. */ +void BrotliDestroyState(BrotliState* state); + /* Sets *decoded_size to the decompressed size of the given encoded stream. */ /* This function only works if the encoded buffer has a single meta block, */ /* or if it has two meta-blocks, where the first is uncompressed and the */
diff --git a/third_party/brotli/dec/huffman.c b/third_party/brotli/dec/huffman.c index 0628c36..a580aae 100644 --- a/third_party/brotli/dec/huffman.c +++ b/third_party/brotli/dec/huffman.c
@@ -362,24 +362,6 @@ return goal_size; } -void BrotliHuffmanTreeGroupInit(HuffmanTreeGroup* group, uint32_t alphabet_size, - uint32_t ntrees) { - /* Pack two mallocs into one */ - const size_t code_size = - sizeof(HuffmanCode) * (size_t)(ntrees * BROTLI_HUFFMAN_MAX_TABLE_SIZE); - const size_t htree_size = sizeof(HuffmanCode*) * (size_t)ntrees; - char *p = (char*)malloc(code_size + htree_size); - group->alphabet_size = (uint16_t)alphabet_size; - group->num_htrees = (uint16_t)ntrees; - group->codes = (HuffmanCode*)p; - group->htrees = (HuffmanCode**)(p + code_size); -} - -void BrotliHuffmanTreeGroupRelease(HuffmanTreeGroup* group) { - BROTLI_FREE(group->codes); - group->htrees = NULL; -} - #if defined(__cplusplus) || defined(c_plusplus) } /* extern "C" */ #endif
diff --git a/third_party/brotli/dec/huffman.h b/third_party/brotli/dec/huffman.h index bb67f4b..783cd7d 100644 --- a/third_party/brotli/dec/huffman.h +++ b/third_party/brotli/dec/huffman.h
@@ -70,10 +70,6 @@ uint16_t num_htrees; } HuffmanTreeGroup; -void BrotliHuffmanTreeGroupInit(HuffmanTreeGroup* group, - uint32_t alphabet_size, uint32_t ntrees); -void BrotliHuffmanTreeGroupRelease(HuffmanTreeGroup* group); - #if defined(__cplusplus) || defined(c_plusplus) } /* extern "C" */ #endif
diff --git a/third_party/brotli/dec/port.h b/third_party/brotli/dec/port.h index f8fc4a8..2f5b0ce 100644 --- a/third_party/brotli/dec/port.h +++ b/third_party/brotli/dec/port.h
@@ -237,8 +237,10 @@ #define BROTLI_HAS_UBFX 0 #endif -#define BROTLI_FREE(X) { \ - free(X); \ +#define BROTLI_ALLOC(S, L) S->alloc_func(S->memory_manager_opaque, L) + +#define BROTLI_FREE(S, X) { \ + S->free_func(S->memory_manager_opaque, X); \ X = NULL; \ }
diff --git a/third_party/brotli/dec/state.c b/third_party/brotli/dec/state.c index f4f239a..32c1eceb 100644 --- a/third_party/brotli/dec/state.c +++ b/third_party/brotli/dec/state.c
@@ -23,7 +23,32 @@ extern "C" { #endif +static void* DefaultAllocFunc(void* opaque, size_t size) { + BROTLI_UNUSED(opaque); + return malloc(size); +} + +static void DefaultFreeFunc(void* opaque, void* address) { + BROTLI_UNUSED(opaque); + free(address); +} + void BrotliStateInit(BrotliState* s) { + BrotliStateInitWithCustomAllocators(s, 0, 0, 0); +} + +void BrotliStateInitWithCustomAllocators(BrotliState* s, + brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) { + if (!alloc_func) { + s->alloc_func = DefaultAllocFunc; + s->free_func = DefaultFreeFunc; + s->memory_manager_opaque = 0; + } else { + s->alloc_func = alloc_func; + s->free_func = free_func; + s->memory_manager_opaque = opaque; + } + BrotliInitBitReader(&s->br); s->state = BROTLI_STATE_UNINITED; s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE; @@ -120,22 +145,22 @@ } void BrotliStateCleanupAfterMetablock(BrotliState* s) { - BROTLI_FREE(s->context_modes); - BROTLI_FREE(s->context_map); - BROTLI_FREE(s->dist_context_map); + BROTLI_FREE(s, s->context_modes); + BROTLI_FREE(s, s->context_map); + BROTLI_FREE(s, s->dist_context_map); - BrotliHuffmanTreeGroupRelease(&s->literal_hgroup); - BrotliHuffmanTreeGroupRelease(&s->insert_copy_hgroup); - BrotliHuffmanTreeGroupRelease(&s->distance_hgroup); + BrotliHuffmanTreeGroupRelease(s, &s->literal_hgroup); + BrotliHuffmanTreeGroupRelease(s, &s->insert_copy_hgroup); + BrotliHuffmanTreeGroupRelease(s, &s->distance_hgroup); } void BrotliStateCleanup(BrotliState* s) { BrotliStateCleanupAfterMetablock(s); - BROTLI_FREE(s->ringbuffer); - BROTLI_FREE(s->block_type_trees); - BROTLI_FREE(s->legacy_input_buffer); - BROTLI_FREE(s->legacy_output_buffer); + BROTLI_FREE(s, s->ringbuffer); + BROTLI_FREE(s, s->block_type_trees); + BROTLI_FREE(s, s->legacy_input_buffer); + BROTLI_FREE(s, s->legacy_output_buffer); } int BrotliStateIsStreamStart(const BrotliState* s) { @@ -147,6 +172,23 @@ return s->state == BROTLI_STATE_DONE; } +void BrotliHuffmanTreeGroupInit(BrotliState* s, HuffmanTreeGroup* group, + uint32_t alphabet_size, uint32_t ntrees) { + /* Pack two allocations into one */ + const size_t code_size = + sizeof(HuffmanCode) * (size_t)(ntrees * BROTLI_HUFFMAN_MAX_TABLE_SIZE); + const size_t htree_size = sizeof(HuffmanCode*) * (size_t)ntrees; + char *p = (char*)BROTLI_ALLOC(s, code_size + htree_size); + group->alphabet_size = (uint16_t)alphabet_size; + group->num_htrees = (uint16_t)ntrees; + group->codes = (HuffmanCode*)p; + group->htrees = (HuffmanCode**)(p + code_size); +} + +void BrotliHuffmanTreeGroupRelease(BrotliState* s, HuffmanTreeGroup* group) { + BROTLI_FREE(s, group->codes); + group->htrees = NULL; +} #if defined(__cplusplus) || defined(c_plusplus) } /* extern "C" */
diff --git a/third_party/brotli/dec/state.h b/third_party/brotli/dec/state.h index 0d9329b..2adf26c8 100644 --- a/third_party/brotli/dec/state.h +++ b/third_party/brotli/dec/state.h
@@ -106,6 +106,10 @@ BrotliRunningState state; BrotliBitReader br; + brotli_alloc_func alloc_func; + brotli_free_func free_func; + void* memory_manager_opaque; + /* Temporary storage for remaining input. */ union { uint64_t u64; @@ -234,10 +238,16 @@ typedef struct BrotliStateStruct BrotliState; void BrotliStateInit(BrotliState* s); +void BrotliStateInitWithCustomAllocators(BrotliState* s, + brotli_alloc_func alloc_func, + brotli_free_func free_func, + void* opaque); void BrotliStateCleanup(BrotliState* s); void BrotliStateMetablockBegin(BrotliState* s); void BrotliStateCleanupAfterMetablock(BrotliState* s); - +void BrotliHuffmanTreeGroupInit(BrotliState* s, HuffmanTreeGroup* group, + uint32_t alphabet_size, uint32_t ntrees); +void BrotliHuffmanTreeGroupRelease(BrotliState* s, HuffmanTreeGroup* group); /* Returns 1, if s is in a state where we have not read any input bytes yet, and 0 otherwise */
diff --git a/third_party/brotli/dec/types.h b/third_party/brotli/dec/types.h index 8a9cc4a8..096b5911 100644 --- a/third_party/brotli/dec/types.h +++ b/third_party/brotli/dec/types.h
@@ -33,4 +33,15 @@ #include <stdint.h> #endif /* defined(_MSC_VER) && (_MSC_VER < 1600) */ +/* Allocating function pointer. Function MUST return 0 in the case of failure. + Otherwise it MUST return a valid pointer to a memory region of at least + size length. Neither items nor size are allowed to be 0. + opaque argument is a pointer provided by client and could be used to bind + function to specific object (memory pool). */ +typedef void* (*brotli_alloc_func) (void* opaque, size_t size); + +/* Deallocating function pointer. Function SHOULD be no-op in the case the + address is 0. */ +typedef void (*brotli_free_func) (void* opaque, void* address); + #endif /* BROTLI_DEC_TYPES_H_ */
diff --git a/third_party/libjingle/BUILD.gn b/third_party/libjingle/BUILD.gn index 9a80a44..3843e44c 100644 --- a/third_party/libjingle/BUILD.gn +++ b/third_party/libjingle/BUILD.gn
@@ -296,7 +296,6 @@ # as is supported in the GYP build. It's not clear what this is used for. source_set("libjingle_webrtc_common") { sources = [ - "overrides/talk/media/webrtc/webrtcexport.h", "source/talk/app/webrtc/audiotrack.cc", "source/talk/app/webrtc/audiotrack.h", "source/talk/app/webrtc/datachannel.cc",
diff --git a/third_party/openh264/BUILD.gn b/third_party/openh264/BUILD.gn index 5d8e796..1b1ea41 100644 --- a/third_party/openh264/BUILD.gn +++ b/third_party/openh264/BUILD.gn
@@ -14,9 +14,8 @@ cflags = [] defines = [] - # Compiler warnings to ignore. - if (!is_win) { - # GCC/clang flags + # GCC and clang flags. MSVS (is_win && !is_clang) does not use cflags. + if (!is_win || is_clang) { cflags += [ "-Wno-format", "-Wno-header-hygiene",
diff --git a/third_party/openh264/openh264.gyp b/third_party/openh264/openh264.gyp index e461ee6..4d46bfc 100644 --- a/third_party/openh264/openh264.gyp +++ b/third_party/openh264/openh264.gyp
@@ -9,41 +9,52 @@ ], # Settings shared by all openh264 targets. 'target_defaults': { - 'variables': { - 'conditions': [ - ['OS!="win"', { - # GCC/clang flags + 'conditions': [ + ['OS!="win"', { + # GCC and clang flags. + 'variables': { 'openh264_cflags_add': [ '-Wno-format', - '-Wno-header-hygiene', '-Wno-unused-value', ], 'openh264_cflags_remove': [ '-Wall', '-Wheader-hygiene', ], - },{ - # Windows uses 'msvs_disabled_warnings' instead, for MSVC flags. - 'openh264_cflags_add': [], - 'openh264_cflags_remove': [], - }], - ], - }, - 'cflags': [ '<@(openh264_cflags_add)' ], - 'cflags!': [ '<@(openh264_cflags_remove)' ], - 'xcode_settings': { - 'WARNING_CFLAGS': [ '<@(openh264_cflags_add)' ], - 'WARNING_CFLAGS!': [ '<@(openh264_cflags_remove)' ], - }, - 'msvs_disabled_warnings': [ - 4324, # structure was padded - 4245, # signed/unsigned mismatch - 4701, # uninitialized variable used - 4702, # unreachable code - ], - - # Platform-specific defines. - 'conditions': [ + }, + 'cflags': [ '<@(openh264_cflags_add)' ], + 'cflags!': [ '<@(openh264_cflags_remove)' ], + 'xcode_settings': { + 'WARNING_CFLAGS': [ '<@(openh264_cflags_add)' ], + 'WARNING_CFLAGS!': [ '<@(openh264_cflags_remove)' ], + }, + }, { + # The land of special cases: Windows. + 'conditions': [ + ['clang==0', { + # MSVS compiler uses warning numbers instead of cflags. + 'msvs_disabled_warnings': [ + 4324, # structure was padded + 4245, # signed/unsigned mismatch + 4701, # uninitialized variable used + 4702, # unreachable code + ], + }, { + # For clang on windows, |cflags| is mysteriously ignored and we + # resort to using |AdditionalOptions| instead. + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions!': [ + '-Wheader-hygiene', + ], + 'AdditionalOptions': [ + '-Wno-unused-value', + ], + }, + }, + }], + ], + }], ['OS=="android"', { 'defines': [ # Android NDK is necessary for its cpufeatures and this define is
diff --git a/tools/android/loading/devtools_monitor.py b/tools/android/loading/devtools_monitor.py index d4847cd81..fc652d2 100644 --- a/tools/android/loading/devtools_monitor.py +++ b/tools/android/loading/devtools_monitor.py
@@ -173,3 +173,25 @@ def GetEvents(self): """Returns a list of collected events, finalizing the state if necessary.""" pass + + def ToJsonDict(self): + """Serializes to a dictionary, to be dumped as JSON. + + Returns: + A dict that can be dumped by the json module, and loaded by + FromJsonDict(). + """ + pass + + @classmethod + def FromJsonDict(cls, json_dict): + """Returns a Track instance constructed from data dumped by + Track.ToJsonDict(). + + Args: + json_data: (dict) Parsed from a JSON file using the json module. + + Returns: + a Track instance. + """ + pass
diff --git a/tools/android/loading/request_track.py b/tools/android/loading/request_track.py index 995c574..de3fd115 100644 --- a/tools/android/loading/request_track.py +++ b/tools/android/loading/request_track.py
@@ -94,16 +94,20 @@ request_time = self.timing.request_time return (timestamp - request_time) * 1000 - def ToDict(self): + def ToJsonDict(self): return copy.deepcopy(self.__dict__) @classmethod - def FromDict(cls, data_dict): + def FromJsonDict(cls, data_dict): result = Request() for (k, v) in data_dict.items(): setattr(result, k, v) return result + # For testing. + def __eq__(self, o): + return self.__dict__ == o.__dict__ + class RequestTrack(devtools_monitor.Track): """Aggregates request data.""" @@ -135,6 +139,20 @@ % len(self._requests_in_flight)) return self._requests + def ToJsonDict(self): + if self._requests_in_flight: + logging.warning('Requests in flight, will be ignored in the dump') + return {'events': [request.ToJsonDict() for request in self._requests]} + + @classmethod + def FromJsonDict(cls, json_dict): + assert 'events' in json_dict + result = RequestTrack(None) + requests = [Request.FromJsonDict(request) + for request in json_dict['events']] + result._requests = requests + return result + def _RequestWillBeSent(self, request_id, params): # Several "requestWillBeSent" events can be dispatched in a row in the case # of redirects. @@ -239,6 +257,9 @@ self._completed_requests_by_id[request_id] = request self._requests.append(request) + def __eq__(self, o): + return self._requests == o._requests + RequestTrack._METHOD_TO_HANDLER = { 'Network.requestWillBeSent': RequestTrack._RequestWillBeSent,
diff --git a/tools/android/loading/request_track_unittest.py b/tools/android/loading/request_track_unittest.py index 5a25b813..459e2de 100644 --- a/tools/android/loading/request_track_unittest.py +++ b/tools/android/loading/request_track_unittest.py
@@ -2,6 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import json import unittest from request_track import (Request, RequestTrack, _TimingFromDict) @@ -242,6 +243,17 @@ RequestTrackTestCase._DATA_RECEIVED_2['params']['encodedDataLength'], r.data_chunks[1][1]) + def testCanSerialize(self): + self._ValidSequence(self.request_track) + json_dict = self.request_track.ToJsonDict() + _ = json.dumps(json_dict) # Should not raise an exception. + + def testCanDeserialize(self): + self._ValidSequence(self.request_track) + json_dict = self.request_track.ToJsonDict() + request_track = RequestTrack.FromJsonDict(json_dict) + self.assertEquals(self.request_track, request_track) + @classmethod def _ValidSequence(cls, request_track): request_track.Handle(
diff --git a/tools/android/loading/trace_recorder.py b/tools/android/loading/trace_recorder.py index a3f289b..6e638c4 100755 --- a/tools/android/loading/trace_recorder.py +++ b/tools/android/loading/trace_recorder.py
@@ -28,8 +28,9 @@ self._connection = connection self._events = [] self._main_frame_id = None - self._connection.RegisterListener('Page.frameStartedLoading', self) - self._connection.RegisterListener('Page.frameStoppedLoading', self) + if self._connection: + self._connection.RegisterListener('Page.frameStartedLoading', self) + self._connection.RegisterListener('Page.frameStoppedLoading', self) def Handle(self, method, msg): params = msg['params'] @@ -47,6 +48,17 @@ def GetEvents(self): return self._events + def ToJsonDict(self): + return {'events': [event for event in self._events]} + + @classmethod + def FromJsonDict(cls, json_dict): + assert 'events' in json_dict + result = PageTrack(None) + events = [event for event in json_dict['events']] + result._events = events + return result + class AndroidTraceRecorder(object): """Records a loading trace."""
diff --git a/url/gurl_unittest.cc b/url/gurl_unittest.cc index 1320609..abc3f106 100644 --- a/url/gurl_unittest.cc +++ b/url/gurl_unittest.cc
@@ -67,7 +67,12 @@ // the parser is already tested and works, so we are mostly interested if the // object does the right thing with the results. TEST(GURLTest, Components) { + GURL empty_url(WStringToUTF16(L"")); + EXPECT_TRUE(empty_url.is_empty()); + EXPECT_FALSE(empty_url.is_valid()); + GURL url(WStringToUTF16(L"http://user:pass@google.com:99/foo;bar?q=a#ref")); + EXPECT_FALSE(url.is_empty()); EXPECT_TRUE(url.is_valid()); EXPECT_TRUE(url.SchemeIs("http")); EXPECT_FALSE(url.SchemeIsFile());