diff --git a/DEPS b/DEPS index f8f432e..a16d2bc2 100644 --- a/DEPS +++ b/DEPS
@@ -78,7 +78,7 @@ # 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': '79f728aa4918895beec52edf0ee12dc5fe51eec2', + 'v8_revision': '9a1d0f9307ce99a8831f97da87cf7bbd98576731', # 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/android_webview/common/crash_reporter/crash_keys.cc b/android_webview/common/crash_reporter/crash_keys.cc index f62d444..e8ee59497 100644 --- a/android_webview/common/crash_reporter/crash_keys.cc +++ b/android_webview/common/crash_reporter/crash_keys.cc
@@ -55,13 +55,6 @@ // sandbox/: {"seccomp-sigsys", kMediumSize}, - // Site isolation. These keys help debug renderer kills such as - // https://crbug.com/773140. - {"requested_site_url", kSmallSize}, - {"requested_origin", kSmallSize}, - {"killed_process_origin_lock", kSmallSize}, - {"site_isolation_mode", kSmallSize}, - // Temporary for https://crbug.com/685996. {"user-cloud-policy-manager-connect-trace", kMediumSize},
diff --git a/chrome/app/chrome_crash_reporter_client_win.cc b/chrome/app/chrome_crash_reporter_client_win.cc index c2b7b90..e6159d7 100644 --- a/chrome/app/chrome_crash_reporter_client_win.cc +++ b/chrome/app/chrome_crash_reporter_client_win.cc
@@ -105,13 +105,6 @@ // media/: {kZeroEncodeDetails, kSmallSize}, - // Site isolation. These keys help debug renderer kills such as - // https://crbug.com/773140. - {"requested_site_url", kSmallSize}, - {"requested_origin", kSmallSize}, - {"killed_process_origin_lock", kSmallSize}, - {"site_isolation_mode", kSmallSize}, - // Temporary for https://crbug.com/685996. {"user-cloud-policy-manager-connect-trace", kMediumSize},
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp index fb64a066..9161fa5 100644 --- a/chrome/app/chromeos_strings.grdp +++ b/chrome/app/chromeos_strings.grdp
@@ -4232,6 +4232,18 @@ <message name="IDS_AD_PASSWORD_CHANGE_PASSWORDS_MISMATCH_ERROR" desc="Error message in case new password fields does not match on the Active Directory password change dialog."> Passwords do not match. </message> + <message name="IDS_AD_MORE_OPTIONS_BUTTON" desc="Text on the 'More options' button on the Active Directory domain join screen."> + More options + </message> + <message name="IDS_AD_ORG_UNIT_HINT" desc="Hint for the Active Directory Organizational units input on the 'More options' dialog."> + Computer OU (e.g. OU=Chromebooks,DC=example,DC=com) + </message> + <message name="IDS_AD_CONFIRM_BUTTON" desc="Text on the 'Confirm' button on the 'More options' dialog."> + Confirm + </message> + <message name="IDS_AD_CANCEL_BUTTON" desc="Text on the 'Cancel' button on the 'More options' dialog."> + Cancel + </message> <message name="IDS_AD_DOMAIN_JOIN_UNKNOWN_ERROR" desc="Default error text on the Active Directory join screen"> Oops! Something went wrong when trying to join the domain. Please try again. </message> @@ -4256,6 +4268,18 @@ <message name="IDS_AD_USER_HIT_JOIN_QUOTA" desc="Alert message that user can't add more machines to the domain"> Failed to join the machine to the domain. This might be due to exceeding the maximum number of allowed machine joins for your account on the server. </message> + <message name="IDS_AD_OU_DOES_NOT_EXIST" desc="Alert message that user joins the machine to non-existing organizational unit."> + Failed to join the machine to the domain. Organizational unit does not exist. + </message> + <message name="IDS_AD_OU_INVALID" desc="Alert message that user joins the machine to invalid organizational unit."> + Failed to join the machine to the domain. Organizational unit is invalid. + </message> + <message name="IDS_AD_OU_ACCESS_DENIED" desc="Alert message that user does not have rights to join the machine to specified organizational unit."> + Failed to join the machine to the domain. This might be due to insufficient privileges for your account for the organizational unit. + </message> + <message name="IDS_AD_OU_SETTING_FAILED" desc="Alert message that user could not join the machine to specified organizational unit due to unknown reason."> + Failed to join the machine to the domain. This might be due to issues with the organizational unit. + </message> <message name="IDS_AD_BOARD_NOT_SUPPORTED" desc="Alert message that the Chromebook model is not supported for Active Directory"> Chrome <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> integration is only supported on x86_64 platforms. Chromebooks built on top of an ARM or x86 platform do not support this functionality. </message>
diff --git a/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc index 429f0bf1..113b5c2 100644 --- a/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc +++ b/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
@@ -526,6 +526,7 @@ if (utterance == "Click me") break; } + speech_monitor_.GetNextUtterance(); EXPECT_EQ("Button", speech_monitor_.GetNextUtterance()); // Press Search+/ to enter ChromeVox's "find in page".
diff --git a/chrome/browser/chromeos/login/active_directory_login_browsertest.cc b/chrome/browser/chromeos/login/active_directory_login_browsertest.cc index 6a4975a..6e13bba2 100644 --- a/chrome/browser/chromeos/login/active_directory_login_browsertest.cc +++ b/chrome/browser/chromeos/login/active_directory_login_browsertest.cc
@@ -50,6 +50,7 @@ constexpr char kAdMachineName[] = "machine_name"; constexpr char kTestActiveDirectoryUser[] = "test-user"; constexpr char kAdMachineInput[] = "machineNameInput"; +constexpr char kAdMoreOptionsButton[] = "moreOptionsBtn"; constexpr char kAdUserInput[] = "userInput"; constexpr char kAdPasswordInput[] = "passwordInput"; constexpr char kAdButton[] = "button"; @@ -66,18 +67,6 @@ constexpr char kCloseButtonId[] = "closeButton"; -// Used for the callback from FakeAuthPolicy::JoinAdDomain. -void OnJoinedDomain(base::OnceClosure closure, authpolicy::ErrorType error) { - EXPECT_EQ(authpolicy::ERROR_NONE, error); - std::move(closure).Run(); -} - -// Used for the callback from FakeAuthPolicy::RefreshDevicePolicy. -void OnRefreshedPolicy(base::OnceClosure closure, authpolicy::ErrorType error) { - EXPECT_EQ(authpolicy::ERROR_NONE, error); - std::move(closure).Run(); -} - class TestAuthPolicyClient : public FakeAuthPolicyClient { public: TestAuthPolicyClient() { FakeAuthPolicyClient::set_started(true); } @@ -149,18 +138,29 @@ AuthPolicyLoginHelper helper; { base::RunLoop loop; - helper.JoinAdDomain(kAdMachineName, - kTestActiveDirectoryUser + ("@" + test_realm_), - "" /* password */, - base::BindOnce(&OnJoinedDomain, loop.QuitClosure())); + helper.JoinAdDomain( + kAdMachineName, "" /* distinguished_name */, + kTestActiveDirectoryUser + ("@" + test_realm_), "" /* password */, + base::BindOnce( + [](base::OnceClosure closure, const std::string& expected_domain, + authpolicy::ErrorType error, const std::string& domain) { + EXPECT_EQ(authpolicy::ERROR_NONE, error); + EXPECT_EQ(expected_domain, domain); + std::move(closure).Run(); + }, + loop.QuitClosure(), test_realm_)); loop.Run(); } ASSERT_TRUE(AuthPolicyLoginHelper::LockDeviceActiveDirectoryForTesting( test_realm_)); { base::RunLoop loop; - fake_auth_policy_client()->RefreshDevicePolicy( - base::BindOnce(&OnRefreshedPolicy, loop.QuitClosure())); + fake_auth_policy_client()->RefreshDevicePolicy(base::BindOnce( + [](base::OnceClosure closure, authpolicy::ErrorType error) { + EXPECT_EQ(authpolicy::ERROR_NONE, error); + std::move(closure).Run(); + }, + loop.QuitClosure())); loop.Run(); } } @@ -198,6 +198,7 @@ // Checks if Active Directory signin is visible. JSExpect("!document.querySelector('#offline-ad-auth').hidden"); JSExpect(JSElement(kAdOfflineAuthId, kAdMachineInput) + ".hidden"); + JSExpect(JSElement(kAdOfflineAuthId, kAdMoreOptionsButton) + ".hidden"); JSExpect("!" + JSElement(kAdOfflineAuthId, kAdUserInput) + ".hidden"); JSExpect("!" + JSElement(kAdOfflineAuthId, kAdPasswordInput) + ".hidden");
diff --git a/chrome/browser/chromeos/login/enrollment/enrollment_screen.h b/chrome/browser/chromeos/login/enrollment/enrollment_screen.h index 2470117..de64f640 100644 --- a/chrome/browser/chromeos/login/enrollment/enrollment_screen.h +++ b/chrome/browser/chromeos/login/enrollment/enrollment_screen.h
@@ -104,6 +104,8 @@ FRIEND_TEST_ALL_PREFIXES(EnterpriseEnrollmentTest, TestActiveDirectoryEnrollment_Success); FRIEND_TEST_ALL_PREFIXES(EnterpriseEnrollmentTest, + TestActiveDirectoryEnrollment_DistinguishedName); + FRIEND_TEST_ALL_PREFIXES(EnterpriseEnrollmentTest, TestActiveDirectoryEnrollment_UIErrors); FRIEND_TEST_ALL_PREFIXES(HandsOffNetworkScreenTest, RequiresNoInput); FRIEND_TEST_ALL_PREFIXES(HandsOffNetworkScreenTest, ContinueClickedOnlyOnce);
diff --git a/chrome/browser/chromeos/login/enterprise_enrollment_browsertest.cc b/chrome/browser/chromeos/login/enterprise_enrollment_browsertest.cc index 36102408..18c5955 100644 --- a/chrome/browser/chromeos/login/enterprise_enrollment_browsertest.cc +++ b/chrome/browser/chromeos/login/enterprise_enrollment_browsertest.cc
@@ -28,15 +28,47 @@ namespace { -const char kAdMachineNameInput[] = +constexpr char kAdMachineNameInput[] = "document.querySelector('#oauth-enroll-ad-join-ui /deep/ " "#machineNameInput')"; -const char kAdUsernameInput[] = +constexpr char kAdMachineOrgUnitInput[] = + "document.querySelector('#oauth-enroll-ad-join-ui /deep/ " + "#orgUnitInput')"; +constexpr char kAdUsernameInput[] = "document.querySelector('#oauth-enroll-ad-join-ui /deep/ #userInput')"; -const char kAdPasswordInput[] = +constexpr char kAdPasswordInput[] = "document.querySelector('#oauth-enroll-ad-join-ui /deep/ #passwordInput')"; -const char kAdTestRealm[] = "test_realm.com"; -const char kAdTestUser[] = "test_user@test_realm.com"; +constexpr char kAdUserDomain[] = "user.domain.com"; +constexpr char kAdMachineDomain[] = "machine.domain.com"; +constexpr char kAdMachineDomainDN[] = + "OU=leaf,OU=root,DC=machine,DC=domain,DC=com"; +constexpr const char* kAdOrganizationlUnit[] = {"leaf", "root"}; +constexpr char kAdTestUser[] = "test_user@user.domain.com"; + +class MockAuthPolicyClient : public FakeAuthPolicyClient { + public: + MockAuthPolicyClient() = default; + ~MockAuthPolicyClient() override = default; + void JoinAdDomain(const authpolicy::JoinDomainRequest& request, + int password_fd, + JoinCallback callback) override { + if (expected_request_) { + ASSERT_EQ(expected_request_->SerializeAsString(), + request.SerializeAsString()); + expected_request_.reset(); + } + FakeAuthPolicyClient::JoinAdDomain(request, password_fd, + std::move(callback)); + } + + void set_expected_request( + std::unique_ptr<authpolicy::JoinDomainRequest> expected_request) { + expected_request_ = std::move(expected_request); + } + + private: + std::unique_ptr<authpolicy::JoinDomainRequest> expected_request_; +}; } // namespace @@ -99,6 +131,7 @@ // Submits Active Directory domain join credentials. void SubmitActiveDirectoryCredentials(const std::string& machine_name, + const std::string& machine_ou, const std::string& username, const std::string& password) { EXPECT_TRUE(IsStepDisplayed("ad-join")); @@ -111,15 +144,34 @@ std::string(kAdUsernameInput) + ".value = '" + username + "'"; const std::string set_password = std::string(kAdPasswordInput) + ".value = '" + password + "'"; + const std::string set_machine_ou = + std::string(kAdMachineOrgUnitInput) + ".value = '" + machine_ou + "'"; js_checker().ExecuteAsync(set_machine_name); js_checker().ExecuteAsync(set_username); js_checker().ExecuteAsync(set_password); + js_checker().ExecuteAsync(set_machine_ou); js_checker().ExecuteAsync( "document.querySelector('#oauth-enroll-ad-join-ui /deep/ " "#button').fire('tap')"); ExecutePendingJavaScript(); } + void SetExpectedJoinRequest(const std::string& machine_name, + const std::string& machine_domain, + std::vector<std::string> organizational_unit, + const std::string& username) { + auto request = std::make_unique<authpolicy::JoinDomainRequest>(); + if (!machine_name.empty()) + request->set_machine_name(machine_name); + if (!machine_domain.empty()) + request->set_machine_domain(machine_domain); + for (std::string& it : organizational_unit) + request->add_machine_ou()->swap(it); + if (!username.empty()) + request->set_user_principal_name(username); + mock_auth_policy_client()->set_expected_request(std::move(request)); + } + void DisableAttributePromptUpdate() { AddEnrollmentSetupFunction( [](EnterpriseEnrollmentHelperMock* enrollment_helper) { @@ -149,22 +201,34 @@ } // Forces the Active Directory domain join flow during enterprise enrollment. - void SetupActiveDirectoryJoin() { + void SetupActiveDirectoryJoin(const std::string& expected_domain) { AddEnrollmentSetupFunction( - [this](EnterpriseEnrollmentHelperMock* enrollment_helper) { + [this, + expected_domain](EnterpriseEnrollmentHelperMock* enrollment_helper) { // Causes the attribute-prompt flow to activate. EXPECT_CALL(*enrollment_helper, EnrollUsingAuthCode("test_auth_code", _)) - .WillOnce(InvokeWithoutArgs([this]() { - this->enrollment_screen()->JoinDomain( - base::BindOnce([](const std::string& realm) { - EXPECT_EQ(kAdTestRealm, realm); - })); + .WillOnce(InvokeWithoutArgs([this, expected_domain]() { + this->enrollment_screen()->JoinDomain(base::BindOnce( + [](const std::string& expected_domain, + const std::string& domain) { + ASSERT_EQ(expected_domain, domain); + }, + expected_domain)); })); }); - static_cast<FakeAuthPolicyClient*>( - DBusThreadManager::Get()->GetAuthPolicyClient()) - ->DisableOperationDelayForTesting(); + } + + void SetUp() override { + DBusThreadManager::GetSetterForTesting()->SetAuthPolicyClient( + std::make_unique<MockAuthPolicyClient>()); + mock_auth_policy_client()->DisableOperationDelayForTesting(); + LoginManagerTest::SetUp(); + } + + MockAuthPolicyClient* mock_auth_policy_client() { + return static_cast<MockAuthPolicyClient*>( + DBusThreadManager::Get()->GetAuthPolicyClient()); } void SetupActiveDirectoryJSNotifications() { @@ -328,7 +392,7 @@ TestActiveDirectoryEnrollment_Success) { ShowEnrollmentScreen(); DisableAttributePromptUpdate(); - SetupActiveDirectoryJoin(); + SetupActiveDirectoryJoin(kAdUserDomain); SubmitEnrollmentCredentials(); chromeos::DBusThreadManager::Get() @@ -337,7 +401,43 @@ content::DOMMessageQueue message_queue; SetupActiveDirectoryJSNotifications(); - SubmitActiveDirectoryCredentials("machine_name", kAdTestUser, "password"); + SetExpectedJoinRequest("machine_name", "", {}, kAdTestUser); + SubmitActiveDirectoryCredentials("machine_name", "", kAdTestUser, "password"); + WaitForMessage(&message_queue, "\"ShowSpinnerScreen\""); + EXPECT_FALSE(IsStepDisplayed("ad-join")); + + CompleteEnrollment(); + // Verify that the success page is displayed. + EXPECT_TRUE(IsStepDisplayed("success")); + EXPECT_FALSE(IsStepDisplayed("error")); + + // We have to remove the enrollment_helper before the dtor gets called. + enrollment_screen()->enrollment_helper_.reset(); +} + +// Verifies that the distinguished name specified on the Active Directory join +// domain screen correctly parsed and passed into AuthPolicyClient. +IN_PROC_BROWSER_TEST_F(EnterpriseEnrollmentTest, + TestActiveDirectoryEnrollment_DistinguishedName) { + ShowEnrollmentScreen(); + DisableAttributePromptUpdate(); + SetupActiveDirectoryJoin(kAdMachineDomain); + SubmitEnrollmentCredentials(); + + chromeos::DBusThreadManager::Get() + ->GetUpstartClient() + ->StartAuthPolicyService(); + + content::DOMMessageQueue message_queue; + SetupActiveDirectoryJSNotifications(); + SetExpectedJoinRequest( + "machine_name", kAdMachineDomain, + std::vector<std::string>( + kAdOrganizationlUnit, + kAdOrganizationlUnit + arraysize(kAdOrganizationlUnit)), + kAdTestUser); + SubmitActiveDirectoryCredentials("machine_name", kAdMachineDomainDN, + kAdTestUser, "password"); WaitForMessage(&message_queue, "\"ShowSpinnerScreen\""); EXPECT_FALSE(IsStepDisplayed("ad-join")); @@ -357,7 +457,7 @@ IN_PROC_BROWSER_TEST_F(EnterpriseEnrollmentTest, TestActiveDirectoryEnrollment_UIErrors) { ShowEnrollmentScreen(); - SetupActiveDirectoryJoin(); + SetupActiveDirectoryJoin(kAdUserDomain); SubmitEnrollmentCredentials(); chromeos::DBusThreadManager::Get() @@ -369,14 +469,15 @@ // being checked in the UI. Machine name length is checked after that in the // authpolicyd. SetupActiveDirectoryJSNotifications(); - SubmitActiveDirectoryCredentials("too_long_machine_name", kAdTestUser, ""); + SubmitActiveDirectoryCredentials("too_long_machine_name", "", kAdTestUser, + ""); EXPECT_TRUE(IsStepDisplayed("ad-join")); js_checker().ExpectFalse(std::string(kAdMachineNameInput) + ".isInvalid"); js_checker().ExpectFalse(std::string(kAdUsernameInput) + ".isInvalid"); js_checker().ExpectTrue(std::string(kAdPasswordInput) + ".isInvalid"); // Checking error in case of too long machine name. - SubmitActiveDirectoryCredentials("too_long_machine_name", kAdTestUser, + SubmitActiveDirectoryCredentials("too_long_machine_name", "", kAdTestUser, "password"); WaitForMessage(&message_queue, "\"ShowJoinDomainError\""); EXPECT_TRUE(IsStepDisplayed("ad-join")); @@ -385,7 +486,7 @@ js_checker().ExpectFalse(std::string(kAdPasswordInput) + ".isInvalid"); // Checking error in case of bad username (without realm). - SubmitActiveDirectoryCredentials("machine_name", "test_user", "password"); + SubmitActiveDirectoryCredentials("machine_name", "", "test_user", "password"); WaitForMessage(&message_queue, "\"ShowJoinDomainError\""); EXPECT_TRUE(IsStepDisplayed("ad-join")); js_checker().ExpectFalse(std::string(kAdMachineNameInput) + ".isInvalid");
diff --git a/chrome/browser/resources/chromeos/chromevox/common/editable_text_base.js b/chrome/browser/resources/chromeos/chromevox/common/editable_text_base.js index f148885..a8b44c8f 100644 --- a/chrome/browser/resources/chromeos/chromevox/common/editable_text_base.js +++ b/chrome/browser/resources/chromeos/chromevox/common/editable_text_base.js
@@ -36,7 +36,16 @@ * @param {boolean} triggeredByUser . */ cvox.TextChangeEvent = function(newValue, newStart, newEnd, triggeredByUser) { + Object.defineProperty(this, 'value', { + get: function() { + return this.value_; + }.bind(this), + set: function(val) { + this.value_ = val.replace('\u00a0', ' '); + }.bind(this) + }); this.value = newValue; + this.start = newStart; this.end = newEnd; this.triggeredByUser = triggeredByUser; @@ -102,8 +111,17 @@ /** * Current value of the text field. * @type {string} - * @protected + * @private */ + this.value_ = ''; + Object.defineProperty(this, 'value', { + get: function() { + return this.value_; + }.bind(this), + set: function(val) { + this.value_ = val.replace('\u00a0', ' '); + }.bind(this) + }); this.value = value; /** @@ -317,8 +335,6 @@ * @param {cvox.TextChangeEvent} evt The text change event. */ cvox.ChromeVoxEditableTextBase.prototype.changed = function(evt) { - // Normalize space characters. - evt.value = evt.value.replace('\u00a0', ' '); if (!this.shouldDescribeChange(evt)) { this.lastChangeDescribed = false; return;
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util.js index 5a01fca..903c414 100644 --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util.js +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util.js
@@ -263,7 +263,6 @@ * @return {AutomationNode} */ AutomationUtil.hitTest = function(node, point) { - var loc = node.location; var child = node.firstChild; while (child) { var hit = AutomationUtil.hitTest(child, point); @@ -272,6 +271,12 @@ child = child.nextSibling; } + var loc = node.unclippedLocation; + + // When |node| is partially or fully offscreen, try to find a better match. + if (loc.left < 0 || loc.top < 0) + return null; + if (point.x <= (loc.left + loc.width) && point.x >= loc.left && point.y <= (loc.top + loc.height) && point.y >= loc.top) return node;
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/desktop_automation_handler.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/desktop_automation_handler.js index 0a646ef..b31c9d0 100644 --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/desktop_automation_handler.js +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/desktop_automation_handler.js
@@ -321,22 +321,6 @@ if (!node.root) return; - var root = AutomationUtil.getTopLevelRoot(node.root); - // If we're crossing out of a root, save it in case it needs recovering. - var prevRange = ChromeVoxState.instance.currentRange; - var prevNode = prevRange ? prevRange.start.node : null; - if (prevNode) { - var prevRoot = AutomationUtil.getTopLevelRoot(prevNode); - if (prevRoot && prevRoot !== root) - ChromeVoxState.instance.focusRecoveryMap.set(prevRoot, prevRange); - } - // If a previous node was saved for this focus, restore it. - var savedRange = ChromeVoxState.instance.focusRecoveryMap.get(root); - ChromeVoxState.instance.focusRecoveryMap.delete(root); - if (savedRange) { - ChromeVoxState.instance.navigateToRange(savedRange, false); - return; - } var event = new CustomAutomationEvent(EventType.FOCUS, node, evt.eventFrom); this.onEventDefault(event); @@ -349,6 +333,7 @@ * @param {!AutomationEvent} evt */ onLoadComplete: function(evt) { + this.lastRootUrl_ = ''; chrome.automation.getFocus(function(focus) { if (!focus || !AutomationUtil.isDescendantOf(focus, evt.target)) return; @@ -606,7 +591,7 @@ // starts tabbing before load complete), then don't move ChromeVox's // position on the page. if (curRoot && focusedRoot == curRoot && - this.lastRootUrl_ == focusedRoot.docUrl && focus != focusedRoot) + this.lastRootUrl_ == focusedRoot.docUrl) return; this.lastRootUrl_ = focusedRoot.docUrl || ''; @@ -625,10 +610,6 @@ o.format('$name', focusedRoot); } - if (ChromeVoxState.instance.currentRange && - focus == ChromeVoxState.instance.currentRange.start.node) - return; - ChromeVoxState.instance.setCurrentRange(cursors.Range.fromNode(focus)); if (!this.shouldOutput_(evt)) return;
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/find_handler.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/find_handler.js index 80ee94d..b604b41 100644 --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/find_handler.js +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/find_handler.js
@@ -30,7 +30,7 @@ */ FindHandler.init_ = function() { chrome.automation.addTreeChangeObserver( - TreeChangeObserverFilter.NO_TREE_CHANGES, FindHandler.onTextMatch_); + TreeChangeObserverFilter.TEXT_MARKER_CHANGES, FindHandler.onTextMatch_); }; /**
diff --git a/chrome/browser/resources/chromeos/login/offline_ad_login.html b/chrome/browser/resources/chromeos/login/offline_ad_login.html index a05bbe3..4e75585 100644 --- a/chrome/browser/resources/chromeos/login/offline_ad_login.html +++ b/chrome/browser/resources/chromeos/login/offline_ad_login.html
@@ -24,6 +24,8 @@ 'authCompleted' - Fired when user enters login and password. Fires with an argument |credentials| which contains: |credentials| = { 'machineName': <machine name input>, + 'distinguished_name': <orgUnitInput>, + (e.g. "OU=Computers,DC=example,DC=com") 'username': <username> (UPN), 'password': <typed password> } Methods: @@ -57,8 +59,32 @@ i18n-values="error:adLoginInvalidPassword; label:adLoginPassword"> </gaia-input> + <gaia-button id="moreOptionsBtn" type="link" + on-tap="onMoreOptionsClicked_" hidden="[[!showMachineInput]]"> + $i18n{adJoinMoreOptions} + </gaia-button> </gaia-input-form> </div> </gaia-card> + + <dialog is="cr-dialog" id="moreOptionsDlg" on-close="onMoreOptionsClosed_"> + <div slot="title"> + $i18n{adJoinMoreOptions} + </div> + <div slot="body"> + <gaia-input id="orgUnitInput" label="$i18n{adJoinOrgUnit}" required> + </gaia-input> + </div> + <div slot="button-container"> + <paper-button autofocus on-tap="onMoreOptionsCancelTap_" + class="action-button"> + $i18n{adJoinCancel} + </paper-button> + <paper-button autofocus on-tap="onMoreOptionsConfirmTap_" + class="action-button"> + $i18n{adJoinConfirm} + </paper-button> + </div> + </dialog> </template> </dom-module>
diff --git a/chrome/browser/resources/chromeos/login/offline_ad_login.js b/chrome/browser/resources/chromeos/login/offline_ad_login.js index b394531..8f5d0973 100644 --- a/chrome/browser/resources/chromeos/login/offline_ad_login.js +++ b/chrome/browser/resources/chromeos/login/offline_ad_login.js
@@ -50,6 +50,9 @@ machineNameError: String, }, + /** @private Used for 'More options' dialog. */ + storedOrgUnit: String, + /** @private */ realmChanged_: function() { this.adWelcomeMessage = @@ -127,10 +130,40 @@ user += this.userRealm; var msg = { 'machinename': this.$.machineNameInput.value, + 'distinguished_name': this.$.orgUnitInput.value, 'username': user, 'password': this.$.passwordInput.value }; this.$.passwordInput.value = ''; this.fire('authCompleted', msg); }, + + /** @private */ + onMoreOptionsClicked_: function() { + this.disabled = true; + this.fire('dialogShown'); + this.storedOrgUnit = this.$.orgUnitInput.value; + this.$$('#moreOptionsDlg').showModal(); + this.$$('#gaiaCard').classList.add('full-disabled'); + }, + + /** @private */ + onMoreOptionsConfirmTap_: function() { + this.storedOrgUnit = null; + this.$$('#moreOptionsDlg').close(); + }, + + /** @private */ + onMoreOptionsCancelTap_: function() { + this.$$('#moreOptionsDlg').close(); + }, + + /** @private */ + onMoreOptionsClosed_: function() { + if (this.storedOrgUnit) + this.$.orgUnitInput.value = this.storedOrgUnit; + this.fire('dialogHidden'); + this.disabled = false; + this.$$('#gaiaCard').classList.remove('full-disabled'); + }, });
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.js b/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.js index f57f6906..4d2db03 100644 --- a/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.js +++ b/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.js
@@ -150,9 +150,10 @@ this.offlineAdUi_.disabled = true; this.activeDirectoryMachine_ = e.detail.machinename; this.activeDirectoryUsername_ = e.detail.username; - chrome.send( - 'oauthEnrollAdCompleteLogin', - [e.detail.machinename, e.detail.username, e.detail.password]); + chrome.send('oauthEnrollAdCompleteLogin', [ + e.detail.machinename, e.detail.distinguished_name, e.detail.username, + e.detail.password + ]); }.bind(this)); this.authenticator_.addEventListener(
diff --git a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc index e8b95db..492913c 100644 --- a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc
@@ -422,6 +422,10 @@ builder->Add("adLoginInvalidPassword", IDS_AD_INVALID_PASSWORD); builder->Add("adJoinErrorMachineNameInvalid", IDS_AD_MACHINENAME_INVALID); builder->Add("adJoinErrorMachineNameTooLong", IDS_AD_MACHINENAME_TOO_LONG); + builder->Add("adJoinMoreOptions", IDS_AD_MORE_OPTIONS_BUTTON); + builder->Add("adJoinOrgUnit", IDS_AD_ORG_UNIT_HINT); + builder->Add("adJoinCancel", IDS_AD_CANCEL_BUTTON); + builder->Add("adJoinConfirm", IDS_AD_CONFIRM_BUTTON); builder->Add("licenseSelectionCardTitle", IDS_ENTERPRISE_ENROLLMENT_LICENSE_SELECTION); builder->Add("licenseSelectionCardExplanation", @@ -569,13 +573,14 @@ void EnrollmentScreenHandler::HandleAdCompleteLogin( const std::string& machine_name, + const std::string& distinguished_name, const std::string& user_name, const std::string& password) { observe_network_failure_ = false; DCHECK(controller_); DCHECK(authpolicy_login_helper_); authpolicy_login_helper_->JoinAdDomain( - machine_name, user_name, password, + machine_name, distinguished_name, user_name, password, base::BindOnce(&EnrollmentScreenHandler::HandleAdDomainJoin, weak_ptr_factory_.GetWeakPtr(), machine_name, user_name)); } @@ -583,12 +588,14 @@ void EnrollmentScreenHandler::HandleAdDomainJoin( const std::string& machine_name, const std::string& user_name, - authpolicy::ErrorType code) { + authpolicy::ErrorType code, + const std::string& machine_domain) { switch (code) { - case authpolicy::ERROR_NONE: + case authpolicy::ERROR_NONE: { ShowEnrollmentSpinnerScreen(); - controller_->OnAdJoined(gaia::ExtractDomainName(user_name)); + controller_->OnAdJoined(machine_domain); return; + } case authpolicy::ERROR_NETWORK_PROBLEM: // Could be a network problem, but could also be a misspelled domain name. ShowError(IDS_AD_AUTH_NETWORK_ERROR, true); @@ -620,6 +627,18 @@ case authpolicy::ERROR_USER_HIT_JOIN_QUOTA: ShowError(IDS_AD_USER_HIT_JOIN_QUOTA, true); return; + case authpolicy::ERROR_OU_DOES_NOT_EXIST: + ShowError(IDS_AD_OU_DOES_NOT_EXIST, true); + return; + case authpolicy::ERROR_INVALID_OU: + ShowError(IDS_AD_OU_INVALID, true); + return; + case authpolicy::ERROR_OU_ACCESS_DENIED: + ShowError(IDS_AD_OU_ACCESS_DENIED, true); + return; + case authpolicy::ERROR_SETTING_OU_FAILED: + ShowError(IDS_AD_OU_SETTING_FAILED, true); + return; #if !defined(ARCH_CPU_X86_64) // Currently, the Active Directory integration is only supported on x86_64 // systems. (see https://crbug.com/676602)
diff --git a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h index 28de6f39..23b7d798 100644 --- a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h +++ b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h
@@ -83,6 +83,7 @@ void HandleCompleteLogin(const std::string& user, const std::string& auth_code); void HandleAdCompleteLogin(const std::string& machine_name, + const std::string& distinguished_name, const std::string& user_name, const std::string& password); void HandleRetry(); @@ -127,7 +128,8 @@ // Handler callback from AuthPolicyClient. void HandleAdDomainJoin(const std::string& machine_name, const std::string& user_name, - authpolicy::ErrorType code); + authpolicy::ErrorType code, + const std::string& machine_domain); // Keeps the controller for this view. Controller* controller_ = nullptr;
diff --git a/chrome/common/crash_keys.cc b/chrome/common/crash_keys.cc index 1fe7e412..9d5d559 100644 --- a/chrome/common/crash_keys.cc +++ b/chrome/common/crash_keys.cc
@@ -91,13 +91,6 @@ {"seccomp-sigsys", kMediumSize}, #endif - // Site isolation. These keys help debug renderer kills such as - // https://crbug.com/773140. - {"requested_site_url", kSmallSize}, - {"requested_origin", kSmallSize}, - {"killed_process_origin_lock", kSmallSize}, - {"site_isolation_mode", kSmallSize}, - // Temporary for https://crbug.com/685996. {kUserCloudPolicyManagerConnectTrace, kMediumSize},
diff --git a/chromecast/crash/cast_crash_keys.cc b/chromecast/crash/cast_crash_keys.cc index 2a5a42b8..4e9d922 100644 --- a/chromecast/crash/cast_crash_keys.cc +++ b/chromecast/crash/cast_crash_keys.cc
@@ -42,13 +42,6 @@ // media/: {"zero-encode-details", ::crash_keys::kSmallSize}, - // Site isolation. These keys help debug renderer kills such as - // https://crbug.com/773140. - {"requested_site_url", ::crash_keys::kSmallSize}, - {"requested_origin", ::crash_keys::kSmallSize}, - {"killed_process_origin_lock", ::crash_keys::kSmallSize}, - {"site_isolation_mode", ::crash_keys::kSmallSize}, - // Temporary for https://crbug.com/685996. {"user-cloud-policy-manager-connect-trace", ::crash_keys::kMediumSize},
diff --git a/chromeos/dbus/auth_policy_client.cc b/chromeos/dbus/auth_policy_client.cc index c243d8d..0084fbdf 100644 --- a/chromeos/dbus/auth_policy_client.cc +++ b/chromeos/dbus/auth_policy_client.cc
@@ -69,7 +69,7 @@ authpolicy::kJoinADDomainMethod); dbus::MessageWriter writer(&method_call); if (!writer.AppendProtoAsArrayOfBytes(request)) { - std::move(callback).Run(authpolicy::ERROR_DBUS_FAILURE); + std::move(callback).Run(authpolicy::ERROR_DBUS_FAILURE, std::string()); return; } writer.AppendFileDescriptor(password_fd); @@ -178,12 +178,18 @@ void HandleJoinCallback(JoinCallback callback, dbus::Response* response) { if (!response) { DLOG(ERROR) << "Join: Couldn't call to authpolicy"; - std::move(callback).Run(authpolicy::ERROR_DBUS_FAILURE); + std::move(callback).Run(authpolicy::ERROR_DBUS_FAILURE, std::string()); return; } dbus::MessageReader reader(response); - std::move(callback).Run(GetErrorFromReader(&reader)); + authpolicy::ErrorType error = GetErrorFromReader(&reader); + std::string machine_domain; + if (error == authpolicy::ERROR_NONE) { + if (!reader.PopString(&machine_domain)) + error = authpolicy::ERROR_DBUS_FAILURE; + } + std::move(callback).Run(error, machine_domain); } template <class T>
diff --git a/chromeos/dbus/auth_policy_client.h b/chromeos/dbus/auth_policy_client.h index d7932399..e8e0164 100644 --- a/chromeos/dbus/auth_policy_client.h +++ b/chromeos/dbus/auth_policy_client.h
@@ -32,7 +32,9 @@ using GetUserKerberosFilesCallback = base::OnceCallback<void(authpolicy::ErrorType error, const authpolicy::KerberosFiles& kerberos_files)>; - using JoinCallback = base::OnceCallback<void(authpolicy::ErrorType error)>; + using JoinCallback = + base::OnceCallback<void(authpolicy::ErrorType error, + const std::string& machine_domain)>; using RefreshPolicyCallback = base::OnceCallback<void(authpolicy::ErrorType error)>;
diff --git a/chromeos/dbus/fake_auth_policy_client.cc b/chromeos/dbus/fake_auth_policy_client.cc index 5010932..80c9e81 100644 --- a/chromeos/dbus/fake_auth_policy_client.cc +++ b/chromeos/dbus/fake_auth_policy_client.cc
@@ -95,7 +95,9 @@ const authpolicy::JoinDomainRequest& request, int password_fd, JoinCallback callback) { + DCHECK(!AuthPolicyLoginHelper::IsAdLocked()); authpolicy::ErrorType error = authpolicy::ERROR_NONE; + std::string machine_domain; if (!started_) { LOG(ERROR) << "authpolicyd not started"; error = authpolicy::ERROR_DBUS_FAILURE; @@ -111,11 +113,18 @@ base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); if (parts.size() != 2 || parts[0].empty() || parts[1].empty()) { error = authpolicy::ERROR_PARSE_UPN_FAILED; + } else { + machine_domain = parts[1]; } } + if (error == authpolicy::ERROR_NONE) machine_name_ = request.machine_name(); - PostDelayedClosure(base::BindOnce(std::move(callback), error), + if (error != authpolicy::ERROR_NONE) + machine_domain.clear(); + else if (request.has_machine_domain() && !request.machine_domain().empty()) + machine_domain = request.machine_domain(); + PostDelayedClosure(base::BindOnce(std::move(callback), error, machine_domain), dbus_operation_delay_); } @@ -123,6 +132,7 @@ const authpolicy::AuthenticateUserRequest& request, int password_fd, AuthCallback callback) { + DCHECK(AuthPolicyLoginHelper::IsAdLocked()); authpolicy::ErrorType error = authpolicy::ERROR_NONE; authpolicy::ActiveDirectoryAccountInfo account_info; if (!started_) {
diff --git a/chromeos/dbus/fake_auth_policy_client_unittest.cc b/chromeos/dbus/fake_auth_policy_client_unittest.cc index 41c8e850..74e2be3 100644 --- a/chromeos/dbus/fake_auth_policy_client_unittest.cc +++ b/chromeos/dbus/fake_auth_policy_client_unittest.cc
@@ -17,9 +17,11 @@ namespace chromeos { namespace { -const char kCorrectMachineName[] = "machine_name"; -const char kCorrectUserName[] = "user@realm.com"; -const char kAccountId[] = "user-account-id"; +constexpr char kCorrectMachineName[] = "machine_name"; +constexpr char kCorrectUserName[] = "user@domain.com"; +constexpr char kCorrectUserDomain[] = "domain.com"; +constexpr char kAccountId[] = "user-account-id"; +constexpr char kMachineDomain[] = "machine.domain"; } // namespace @@ -28,7 +30,7 @@ FakeAuthPolicyClientTest() = default; protected: - FakeAuthPolicyClient* authpolicy_client() { return &client_; } + FakeAuthPolicyClient* authpolicy_client() { return auth_policy_client_ptr_; } void SetUp() override { ::testing::Test::SetUp(); @@ -36,6 +38,11 @@ std::make_unique<FakeSessionManagerClient>()); DBusThreadManager::GetSetterForTesting()->SetCryptohomeClient( std::make_unique<FakeCryptohomeClient>()); + auto auth_policy_client = std::make_unique<FakeAuthPolicyClient>(); + auth_policy_client_ptr_ = auth_policy_client.get(); + DBusThreadManager::GetSetterForTesting()->SetAuthPolicyClient( + std::move(auth_policy_client)); + authpolicy_client()->DisableOperationDelayForTesting(); } void JoinAdDomain(const std::string& machine_name, @@ -48,6 +55,18 @@ std::move(callback)); } + void JoinAdDomainWithMachineDomain(const std::string& machine_name, + const std::string& machine_domain, + const std::string& username, + AuthPolicyClient::JoinCallback callback) { + authpolicy::JoinDomainRequest request; + request.set_machine_name(machine_name); + request.set_user_principal_name(username); + request.set_machine_domain(machine_domain); + authpolicy_client()->JoinAdDomain(request, /* password_fd */ -1, + std::move(callback)); + } + void AuthenticateUser(const std::string& username, const std::string& account_id, AuthPolicyClient::AuthCallback callback) { @@ -64,7 +83,7 @@ } private: - FakeAuthPolicyClient client_; + FakeAuthPolicyClient* auth_policy_client_ptr_; // not owned. base::MessageLoop loop_; DISALLOW_COPY_AND_ASSIGN(FakeAuthPolicyClientTest); @@ -73,61 +92,111 @@ // Tests parsing machine name. TEST_F(FakeAuthPolicyClientTest, JoinAdDomain_ParseMachineName) { authpolicy_client()->set_started(true); - LockDeviceActiveDirectory(); JoinAdDomain("correct_length1", kCorrectUserName, - base::BindOnce([](authpolicy::ErrorType error) { - EXPECT_EQ(authpolicy::ERROR_NONE, error); - })); + base::BindOnce( + [](authpolicy::ErrorType error, const std::string& domain) { + EXPECT_EQ(authpolicy::ERROR_NONE, error); + EXPECT_EQ(kCorrectUserDomain, domain); + })); JoinAdDomain("", kCorrectUserName, - base::BindOnce([](authpolicy::ErrorType error) { - EXPECT_EQ(authpolicy::ERROR_INVALID_MACHINE_NAME, error); - })); + base::BindOnce( + [](authpolicy::ErrorType error, const std::string& domain) { + EXPECT_EQ(authpolicy::ERROR_INVALID_MACHINE_NAME, error); + EXPECT_TRUE(domain.empty()); + })); JoinAdDomain("too_long_machine_name", kCorrectUserName, - base::BindOnce([](authpolicy::ErrorType error) { - EXPECT_EQ(authpolicy::ERROR_MACHINE_NAME_TOO_LONG, error); - })); + base::BindOnce( + [](authpolicy::ErrorType error, const std::string& domain) { + EXPECT_EQ(authpolicy::ERROR_MACHINE_NAME_TOO_LONG, error); + EXPECT_TRUE(domain.empty()); + })); JoinAdDomain("invalid:name", kCorrectUserName, - base::BindOnce([](authpolicy::ErrorType error) { - EXPECT_EQ(authpolicy::ERROR_INVALID_MACHINE_NAME, error); - })); + base::BindOnce( + [](authpolicy::ErrorType error, const std::string& domain) { + EXPECT_EQ(authpolicy::ERROR_INVALID_MACHINE_NAME, error); + EXPECT_TRUE(domain.empty()); + })); + base::RunLoop loop; JoinAdDomain(">nvalidname", kCorrectUserName, - base::BindOnce([](authpolicy::ErrorType error) { - EXPECT_EQ(authpolicy::ERROR_INVALID_MACHINE_NAME, error); - })); + base::BindOnce( + [](base::OnceClosure closure, authpolicy::ErrorType error, + const std::string& domain) { + EXPECT_EQ(authpolicy::ERROR_INVALID_MACHINE_NAME, error); + EXPECT_TRUE(domain.empty()); + std::move(closure).Run(); + }, + loop.QuitClosure())); + loop.Run(); +} - base::RunLoop().RunUntilIdle(); +// Tests join to a different machine domain. +TEST_F(FakeAuthPolicyClientTest, JoinAdDomain_MachineDomain) { + authpolicy_client()->set_started(true); + JoinAdDomainWithMachineDomain(kCorrectMachineName, kMachineDomain, + kCorrectUserName, + base::BindOnce([](authpolicy::ErrorType error, + const std::string& domain) { + EXPECT_EQ(authpolicy::ERROR_NONE, error); + EXPECT_EQ(kMachineDomain, domain); + })); + base::RunLoop loop; + JoinAdDomainWithMachineDomain( + kCorrectMachineName, "", kCorrectUserName, + base::BindOnce( + [](base::OnceClosure closure, authpolicy::ErrorType error, + const std::string& domain) { + EXPECT_EQ(authpolicy::ERROR_NONE, error); + EXPECT_EQ(kCorrectUserDomain, domain); + std::move(closure).Run(); + }, + loop.QuitClosure())); + loop.Run(); } // Tests parsing user name. TEST_F(FakeAuthPolicyClientTest, JoinAdDomain_ParseUPN) { authpolicy_client()->set_started(true); - LockDeviceActiveDirectory(); - JoinAdDomain(kCorrectMachineName, "user@realm.com", - base::BindOnce([](authpolicy::ErrorType error) { - EXPECT_EQ(authpolicy::ERROR_NONE, error); - })); + JoinAdDomain(kCorrectMachineName, kCorrectUserName, + base::BindOnce( + [](authpolicy::ErrorType error, const std::string& domain) { + EXPECT_EQ(authpolicy::ERROR_NONE, error); + EXPECT_EQ(kCorrectUserDomain, domain); + })); JoinAdDomain(kCorrectMachineName, "user", - base::BindOnce([](authpolicy::ErrorType error) { - EXPECT_EQ(authpolicy::ERROR_PARSE_UPN_FAILED, error); - })); + base::BindOnce( + [](authpolicy::ErrorType error, const std::string& domain) { + EXPECT_EQ(authpolicy::ERROR_PARSE_UPN_FAILED, error); + EXPECT_TRUE(domain.empty()); + })); JoinAdDomain(kCorrectMachineName, "", - base::BindOnce([](authpolicy::ErrorType error) { - EXPECT_EQ(authpolicy::ERROR_PARSE_UPN_FAILED, error); - })); + base::BindOnce( + [](authpolicy::ErrorType error, const std::string& domain) { + EXPECT_EQ(authpolicy::ERROR_PARSE_UPN_FAILED, error); + EXPECT_TRUE(domain.empty()); + })); JoinAdDomain(kCorrectMachineName, "user@", - base::BindOnce([](authpolicy::ErrorType error) { - EXPECT_EQ(authpolicy::ERROR_PARSE_UPN_FAILED, error); - })); + base::BindOnce( + [](authpolicy::ErrorType error, const std::string& domain) { + EXPECT_EQ(authpolicy::ERROR_PARSE_UPN_FAILED, error); + EXPECT_TRUE(domain.empty()); + })); JoinAdDomain(kCorrectMachineName, "@realm", - base::BindOnce([](authpolicy::ErrorType error) { - EXPECT_EQ(authpolicy::ERROR_PARSE_UPN_FAILED, error); - })); + base::BindOnce( + [](authpolicy::ErrorType error, const std::string& domain) { + EXPECT_EQ(authpolicy::ERROR_PARSE_UPN_FAILED, error); + EXPECT_TRUE(domain.empty()); + })); + base::RunLoop loop; JoinAdDomain(kCorrectMachineName, "user@realm@com", - base::BindOnce([](authpolicy::ErrorType error) { - EXPECT_EQ(authpolicy::ERROR_PARSE_UPN_FAILED, error); - })); - - base::RunLoop().RunUntilIdle(); + base::BindOnce( + [](base::OnceClosure closure, authpolicy::ErrorType error, + const std::string& domain) { + EXPECT_EQ(authpolicy::ERROR_PARSE_UPN_FAILED, error); + EXPECT_TRUE(domain.empty()); + std::move(closure).Run(); + }, + loop.QuitClosure())); + loop.Run(); } // Test AuthenticateUser. @@ -147,11 +216,13 @@ // Tests calls to not started authpolicyd fails. TEST_F(FakeAuthPolicyClientTest, NotStartedAuthPolicyService) { - LockDeviceActiveDirectory(); JoinAdDomain(kCorrectMachineName, kCorrectUserName, - base::BindOnce([](authpolicy::ErrorType error) { - EXPECT_EQ(authpolicy::ERROR_DBUS_FAILURE, error); - })); + base::BindOnce( + [](authpolicy::ErrorType error, const std::string& domain) { + EXPECT_EQ(authpolicy::ERROR_DBUS_FAILURE, error); + EXPECT_TRUE(domain.empty()); + })); + LockDeviceActiveDirectory(); AuthenticateUser( kCorrectUserName, std::string() /* account_id */, base::BindOnce([](authpolicy::ErrorType error, @@ -162,13 +233,16 @@ base::BindOnce([](authpolicy::ErrorType error) { EXPECT_EQ(authpolicy::ERROR_DBUS_FAILURE, error); })); + base::RunLoop loop; authpolicy_client()->RefreshUserPolicy( AccountId::FromUserEmail(kCorrectUserName), - base::BindOnce([](authpolicy::ErrorType error) { - EXPECT_EQ(authpolicy::ERROR_DBUS_FAILURE, error); - })); - - base::RunLoop().RunUntilIdle(); + base::BindOnce( + [](base::OnceClosure closure, authpolicy::ErrorType error) { + EXPECT_EQ(authpolicy::ERROR_DBUS_FAILURE, error); + std::move(closure).Run(); + }, + loop.QuitClosure())); + loop.Run(); } // Tests RefreshDevicePolicy. On a not locked device it should cache policy. On @@ -180,12 +254,14 @@ EXPECT_EQ(authpolicy::ERROR_DEVICE_POLICY_CACHED_BUT_NOT_SENT, error); })); LockDeviceActiveDirectory(); - authpolicy_client()->RefreshDevicePolicy( - base::BindOnce([](authpolicy::ErrorType error) { + base::RunLoop loop; + authpolicy_client()->RefreshDevicePolicy(base::BindOnce( + [](base::OnceClosure closure, authpolicy::ErrorType error) { EXPECT_EQ(authpolicy::ERROR_NONE, error); - })); - - base::RunLoop().RunUntilIdle(); + std::move(closure).Run(); + }, + loop.QuitClosure())); + loop.Run(); } } // namespace chromeos
diff --git a/chromeos/login/auth/authpolicy_login_helper.cc b/chromeos/login/auth/authpolicy_login_helper.cc index e51fd2ed..cd17676 100644 --- a/chromeos/login/auth/authpolicy_login_helper.cc +++ b/chromeos/login/auth/authpolicy_login_helper.cc
@@ -5,6 +5,8 @@ #include "chromeos/login/auth/authpolicy_login_helper.h" #include "base/files/file_util.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" #include "base/task_scheduler/post_task.h" #include "chromeos/cryptohome/cryptohome_util.h" #include "chromeos/dbus/auth_policy_client.h" @@ -19,6 +21,8 @@ constexpr char kAttrMode[] = "enterprise.mode"; constexpr char kDeviceModeEnterpriseAD[] = "enterprise_ad"; +constexpr char kDCPrefix[] = "DC="; +constexpr char kOUPrefix[] = "OU="; base::ScopedFD GetDataReadPipe(const std::string& data) { int pipe_fds[2]; @@ -43,6 +47,30 @@ // Do nothing. } +bool ParseDomainAndOU(const std::string& distinguished_name, + authpolicy::JoinDomainRequest* request) { + std::string machine_domain; + std::vector<std::string> split_dn = + base::SplitString(distinguished_name, ",", base::TRIM_WHITESPACE, + base::SPLIT_WANT_NONEMPTY); + for (const std::string& str : split_dn) { + if (base::StartsWith(str, kOUPrefix, + base::CompareCase::INSENSITIVE_ASCII)) { + *request->add_machine_ou() = str.substr(strlen(kOUPrefix)); + } else if (base::StartsWith(str, kDCPrefix, + base::CompareCase::INSENSITIVE_ASCII)) { + if (!machine_domain.empty()) + machine_domain.append("."); + machine_domain.append(str.substr(strlen(kDCPrefix))); + } else { + return false; + } + } + if (!machine_domain.empty()) + request->set_machine_domain(machine_domain); + return true; +} + } // namespace AuthPolicyLoginHelper::AuthPolicyLoginHelper() : weak_factory_(this) {} @@ -76,24 +104,32 @@ bool AuthPolicyLoginHelper::LockDeviceActiveDirectoryForTesting( const std::string& realm) { return cu::InstallAttributesSet("enterprise.owned", "true") && - cu::InstallAttributesSet("enterprise.mode", "enterprise_ad") && + cu::InstallAttributesSet(kAttrMode, kDeviceModeEnterpriseAD) && cu::InstallAttributesSet("enterprise.realm", realm) && cu::InstallAttributesFinalize(); } void AuthPolicyLoginHelper::JoinAdDomain(const std::string& machine_name, + const std::string& distinguished_name, const std::string& username, const std::string& password, JoinCallback callback) { DCHECK(!IsAdLocked()); DCHECK(!weak_factory_.HasWeakPtrs()) << "Another operation is in progress"; authpolicy::JoinDomainRequest request; - request.set_machine_name(machine_name); - request.set_user_principal_name(username); + if (!ParseDomainAndOU(distinguished_name, &request)) { + DLOG(ERROR) << "Failed to parse computer distinguished name"; + std::move(callback).Run(authpolicy::ERROR_INVALID_OU, std::string()); + return; + } + if (!machine_name.empty()) + request.set_machine_name(machine_name); + if (!username.empty()) + request.set_user_principal_name(username); chromeos::DBusThreadManager::Get()->GetAuthPolicyClient()->JoinAdDomain( request, GetDataReadPipe(password).get(), base::BindOnce(&AuthPolicyLoginHelper::OnJoinCallback, - weak_factory_.GetWeakPtr(), base::Passed(&callback))); + weak_factory_.GetWeakPtr(), std::move(callback))); } void AuthPolicyLoginHelper::AuthenticateUser(const std::string& username, @@ -116,21 +152,23 @@ } void AuthPolicyLoginHelper::OnJoinCallback(JoinCallback callback, - authpolicy::ErrorType error) { + authpolicy::ErrorType error, + const std::string& machine_domain) { DCHECK(!IsAdLocked()); if (error != authpolicy::ERROR_NONE) { - std::move(callback).Run(error); + std::move(callback).Run(error, machine_domain); return; } chromeos::DBusThreadManager::Get() ->GetAuthPolicyClient() - ->RefreshDevicePolicy( - base::BindOnce(&AuthPolicyLoginHelper::OnFirstPolicyRefreshCallback, - weak_factory_.GetWeakPtr(), base::Passed(&callback))); + ->RefreshDevicePolicy(base::BindOnce( + &AuthPolicyLoginHelper::OnFirstPolicyRefreshCallback, + weak_factory_.GetWeakPtr(), base::Passed(&callback), machine_domain)); } void AuthPolicyLoginHelper::OnFirstPolicyRefreshCallback( JoinCallback callback, + const std::string& machine_domain, authpolicy::ErrorType error) { DCHECK(!IsAdLocked()); // First policy refresh happens before device is locked. So policy store @@ -139,7 +177,7 @@ DCHECK(error != authpolicy::ERROR_NONE); if (error == authpolicy::ERROR_DEVICE_POLICY_CACHED_BUT_NOT_SENT) error = authpolicy::ERROR_NONE; - std::move(callback).Run(error); + std::move(callback).Run(error, machine_domain); } void AuthPolicyLoginHelper::OnAuthCallback(
diff --git a/chromeos/login/auth/authpolicy_login_helper.h b/chromeos/login/auth/authpolicy_login_helper.h index 9b59d722..591b566f 100644 --- a/chromeos/login/auth/authpolicy_login_helper.h +++ b/chromeos/login/auth/authpolicy_login_helper.h
@@ -47,10 +47,14 @@ // Packs arguments and calls AuthPolicyClient::JoinAdDomain. Joins machine to // Active directory domain. Then it calls RefreshDevicePolicy to cache the // policy on the authpolicyd side. |machine_name| is a name for a local - // machine. |username|, |password| are credentials of the Active directory - // account which has right to join the machine to the domain. |callback| is - // called after getting (or failing to get) D-BUS response. + // machine. If |distinguished_name| is not empty |machine| would be put into + // that domain or/and organizational unit structure. Otherwise |machine| would + // be joined to domain of the |username|. |username|, |password| are + // credentials of the Active directory account which has right to join the + // machine to the domain. |callback| is called after getting (or failing to + // get) D-BUS response. void JoinAdDomain(const std::string& machine_name, + const std::string& distinguished_name, const std::string& username, const std::string& password, JoinCallback callback); @@ -71,11 +75,14 @@ private: // Called from AuthPolicyClient::JoinAdDomain. - void OnJoinCallback(JoinCallback callback, authpolicy::ErrorType error); + void OnJoinCallback(JoinCallback callback, + authpolicy::ErrorType error, + const std::string& machine_domain); // Called from AuthPolicyClient::RefreshDevicePolicy. This is used only once // during device enrollment with the first device policy refresh. void OnFirstPolicyRefreshCallback(JoinCallback callback, + const std::string& machine_domain, authpolicy::ErrorType error); // Called from AuthPolicyClient::AuthenticateUser.
diff --git a/chromeos/login/auth/authpolicy_login_helper_unittest.cc b/chromeos/login/auth/authpolicy_login_helper_unittest.cc index 0a585ce..6769daa1 100644 --- a/chromeos/login/auth/authpolicy_login_helper_unittest.cc +++ b/chromeos/login/auth/authpolicy_login_helper_unittest.cc
@@ -24,7 +24,7 @@ EXPECT_FALSE(join_ad_domain_called_); EXPECT_FALSE(refresh_device_policy_called_); join_ad_domain_called_ = true; - std::move(callback).Run(authpolicy::ERROR_NONE); + std::move(callback).Run(authpolicy::ERROR_NONE, std::string()); } void RefreshDevicePolicy(RefreshPolicyCallback callback) override { @@ -60,8 +60,11 @@ std::make_unique<FakeCryptohomeClient>()); AuthPolicyLoginHelper helper; helper.JoinAdDomain(std::string(), std::string(), std::string(), - base::BindOnce([](authpolicy::ErrorType error) { + std::string(), + base::BindOnce([](authpolicy::ErrorType error, + const std::string& domain) { EXPECT_EQ(authpolicy::ERROR_NONE, error); + EXPECT_TRUE(domain.empty()); })); mock_client_ptr->CheckExpectations(); }
diff --git a/components/exo/wm_helper.cc b/components/exo/wm_helper.cc index 9a086bda..3a42f7248 100644 --- a/components/exo/wm_helper.cc +++ b/components/exo/wm_helper.cc
@@ -72,8 +72,7 @@ } void WMHelper::AddCursorObserver(aura::client::CursorClientObserver* observer) { - // TODO(crbug.com/631103): Mushrome doesn't have a cursor manager yet. - if (ash::Shell::GetAshConfig() == ash::Config::CLASSIC) + if (ash::Shell::GetAshConfig() != ash::Config::MASH) ash::Shell::Get()->cursor_manager()->AddObserver(observer); } @@ -183,15 +182,13 @@ } ui::CursorSize WMHelper::GetCursorSize() const { - // TODO(crbug.com/631103): Mushrome doesn't have a cursor manager yet. - if (ash::Shell::GetAshConfig() == ash::Config::MUS) + if (ash::Shell::GetAshConfig() == ash::Config::MASH) return ui::CursorSize::kNormal; return ash::Shell::Get()->cursor_manager()->GetCursorSize(); } const display::Display& WMHelper::GetCursorDisplay() const { - // TODO(crbug.com/631103): Mushrome doesn't have a cursor manager yet. - if (ash::Shell::GetAshConfig() == ash::Config::MUS) { + if (ash::Shell::GetAshConfig() == ash::Config::MASH) { static const display::Display display; return display; }
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index 76191f4..be749242 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -1653,6 +1653,9 @@ "web_contents/web_drag_source_mac.mm", "web_contents/web_drag_utils_win.cc", "web_contents/web_drag_utils_win.h", + "webauth/cbor/cbor_binary.h", + "webauth/cbor/cbor_reader.cc", + "webauth/cbor/cbor_reader.h", "webauth/cbor/cbor_values.cc", "webauth/cbor/cbor_values.h", "webauth/cbor/cbor_writer.cc",
diff --git a/content/browser/bad_message.cc b/content/browser/bad_message.cc index 94b6c46..9918cc87 100644 --- a/content/browser/bad_message.cc +++ b/content/browser/bad_message.cc
@@ -73,5 +73,17 @@ return crash_key; } +base::debug::CrashKeyString* GetKilledProcessOriginLockKey() { + static auto* crash_key = base::debug::AllocateCrashKeyString( + "killed_process_origin_lock", base::debug::CrashKeySize::Size64); + return crash_key; +} + +base::debug::CrashKeyString* GetRequestedSiteURLKey() { + static auto* crash_key = base::debug::AllocateCrashKeyString( + "requested_site_url", base::debug::CrashKeySize::Size64); + return crash_key; +} + } // namespace bad_message } // namespace content
diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h index c0fbefd..d50dc9a 100644 --- a/content/browser/bad_message.h +++ b/content/browser/bad_message.h
@@ -240,6 +240,13 @@ // messages. base::debug::CrashKeyString* GetMojoErrorCrashKey(); +// Site isolation. These keys help debug renderer kills such as +// https://crbug.com/773140. +// Returns a key named "killed_process_origin_lock". +base::debug::CrashKeyString* GetKilledProcessOriginLockKey(); +// Retuns a key named "requested_site_url". +base::debug::CrashKeyString* GetRequestedSiteURLKey(); + } // namespace bad_message } // namespace content
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc index b759c9e..37139a47 100644 --- a/content/browser/child_process_security_policy_impl.cc +++ b/content/browser/child_process_security_policy_impl.cc
@@ -18,6 +18,7 @@ #include "base/stl_util.h" #include "base/strings/string_util.h" #include "build/build_config.h" +#include "content/browser/bad_message.h" #include "content/browser/isolated_origin_util.h" #include "content/browser/site_instance_impl.h" #include "content/common/site_isolation_policy.h" @@ -1059,10 +1060,15 @@ if (!can_access) { // Returning false here will result in a renderer kill. Set some crash // keys that will help understand the circumstances of that kill. - base::debug::SetCrashKeyValue("requested_site_url", site_url.spec()); - base::debug::SetCrashKeyValue("requested_origin", url.GetOrigin().spec()); - base::debug::SetCrashKeyValue("killed_process_origin_lock", - state->second->origin_lock().spec()); + base::debug::SetCrashKeyString(bad_message::GetRequestedSiteURLKey(), + site_url.spec()); + base::debug::SetCrashKeyString(bad_message::GetKilledProcessOriginLockKey(), + state->second->origin_lock().spec()); + + static auto* requested_origin_key = base::debug::AllocateCrashKeyString( + "requested_origin", base::debug::CrashKeySize::Size64); + base::debug::SetCrashKeyString(requested_origin_key, + url.GetOrigin().spec()); } return can_access; }
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc index bb9d0265..a37bc56d 100644 --- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc +++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -892,6 +892,46 @@ PlaceAndCaptureBox(kFrameSize, gfx::Size(100, 200), 1.0, 0.); } +// Verifies that setDefaultBackgroundColorOverride changes the background color +// of a page that does not specify one. +IN_PROC_BROWSER_TEST_F(CaptureScreenshotTest, + SetDefaultBackgroundColorOverride) { + if (base::SysInfo::IsLowEndDevice()) + return; + + shell()->LoadURL(GURL("about:blank")); + WaitForLoadStop(shell()->web_contents()); + Attach(); + + // Override background to blue. + std::unique_ptr<base::DictionaryValue> color(new base::DictionaryValue()); + color->SetInteger("r", 0x00); + color->SetInteger("g", 0x00); + color->SetInteger("b", 0xff); + color->SetDouble("a", 1.0); + std::unique_ptr<base::DictionaryValue> params(new base::DictionaryValue()); + params->Set("color", std::move(color)); + SendCommand("Emulation.setDefaultBackgroundColorOverride", std::move(params)); + + SkBitmap expected_bitmap; + // We compare against the actual physical backing size rather than the + // view size, because the view size is stored adjusted for DPI and only in + // integer precision. + gfx::Size view_size = static_cast<RenderWidgetHostViewBase*>( + shell()->web_contents()->GetRenderWidgetHostView()) + ->GetPhysicalBackingSize(); + expected_bitmap.allocN32Pixels(view_size.width(), view_size.height()); + expected_bitmap.eraseColor(SkColorSetRGB(0x00, 0x00, 0xff)); + CaptureScreenshotAndCompareTo(expected_bitmap, ENCODING_PNG, true); + + // Tests that resetting Emulation.setDefaultBackgroundColorOverride + // clears the background color override. + SendCommand("Emulation.setDefaultBackgroundColorOverride", + std::make_unique<base::DictionaryValue>()); + expected_bitmap.eraseColor(SK_ColorWHITE); + CaptureScreenshotAndCompareTo(expected_bitmap, ENCODING_PNG, true); +} + // Verifies that setDefaultBackgroundColor and captureScreenshot support a // transparent background, and that setDeviceMetricsOverride doesn't affect it.
diff --git a/content/browser/loader/resource_loader.cc b/content/browser/loader/resource_loader.cc index 58ba7dd..5e4d137 100644 --- a/content/browser/loader/resource_loader.cc +++ b/content/browser/loader/resource_loader.cc
@@ -102,15 +102,13 @@ request->GetLoadTimingInfo(&response->head.load_timing); if (request->ssl_info().cert.get()) { - response->head.has_major_certificate_errors = - net::IsCertStatusError(request->ssl_info().cert_status) && - !net::IsCertStatusMinorError(request->ssl_info().cert_status); + response->head.cert_status = request->ssl_info().cert_status; response->head.is_legacy_symantec_cert = - !response->head.has_major_certificate_errors && + (!net::IsCertStatusError(response->head.cert_status) || + net::IsCertStatusMinorError(response->head.cert_status)) && net::IsLegacySymantecCert(request->ssl_info().public_key_hashes); response->head.cert_validity_start = request->ssl_info().cert->valid_start(); - response->head.cert_status = request->ssl_info().cert_status; if (info->ShouldReportRawHeaders()) { // Only pass these members when the network panel of the DevTools is open, // i.e. ShouldReportRawHeaders() is set. These data are used to populate
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index 3d38968..12c1074 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -2218,8 +2218,9 @@ if (crash_report_mode == CrashReportMode::GENERATE_CRASH_DUMP) { // Set crash keys to understand renderer kills related to site isolation. auto* policy = ChildProcessSecurityPolicyImpl::GetInstance(); - base::debug::SetCrashKeyValue("killed_process_origin_lock", - policy->GetOriginLock(GetID()).spec()); + base::debug::SetCrashKeyString(bad_message::GetKilledProcessOriginLockKey(), + policy->GetOriginLock(GetID()).spec()); + std::string site_isolation_mode; if (SiteIsolationPolicy::UseDedicatedProcessesForAllSites()) site_isolation_mode += "spp "; @@ -2227,7 +2228,10 @@ site_isolation_mode += "tdi "; if (SiteIsolationPolicy::AreIsolatedOriginsEnabled()) site_isolation_mode += "io "; - base::debug::SetCrashKeyValue("site_isolation_mode", site_isolation_mode); + + static auto* isolation_mode_key = base::debug::AllocateCrashKeyString( + "site_isolation_mode", base::debug::CrashKeySize::Size32); + base::debug::SetCrashKeyString(isolation_mode_key, site_isolation_mode); // Report a crash, since none will be generated by the killed renderer. base::debug::DumpWithoutCrashing(); @@ -3574,9 +3578,10 @@ browser_context, site_url)) { ChildProcessSecurityPolicyImpl* policy = ChildProcessSecurityPolicyImpl::GetInstance(); - base::debug::SetCrashKeyValue("requested_site_url", site_url.spec()); - base::debug::SetCrashKeyValue( - "killed_process_origin_lock", + base::debug::SetCrashKeyString(bad_message::GetRequestedSiteURLKey(), + site_url.spec()); + base::debug::SetCrashKeyString( + bad_message::GetKilledProcessOriginLockKey(), policy->GetOriginLock(render_process_host->GetID()).spec()); CHECK(false) << "Unsuitable process reused for site " << site_url; }
diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc index 827e674c..61b61fc5 100644 --- a/content/browser/site_instance_impl.cc +++ b/content/browser/site_instance_impl.cc
@@ -566,9 +566,10 @@ HAS_WRONG_LOCK: // We should never attempt to reassign a different origin lock to a // process. - base::debug::SetCrashKeyValue("requested_site_url", site_.spec()); - base::debug::SetCrashKeyValue( - "killed_process_origin_lock", + base::debug::SetCrashKeyString(bad_message::GetRequestedSiteURLKey(), + site_.spec()); + base::debug::SetCrashKeyString( + bad_message::GetKilledProcessOriginLockKey(), policy->GetOriginLock(process_->GetID()).spec()); CHECK(false) << "Trying to lock a process to " << site_ << " but the process is already locked to " @@ -586,9 +587,10 @@ // If the site that we've just committed doesn't require a dedicated // process, make sure we aren't putting it in a process for a site that // does. - base::debug::SetCrashKeyValue("requested_site_url", site_.spec()); - base::debug::SetCrashKeyValue( - "killed_process_origin_lock", + base::debug::SetCrashKeyString(bad_message::GetRequestedSiteURLKey(), + site_.spec()); + base::debug::SetCrashKeyString( + bad_message::GetKilledProcessOriginLockKey(), policy->GetOriginLock(process_->GetID()).spec()); CHECK_EQ(lock_state, ChildProcessSecurityPolicyImpl::CheckOriginLockResult::NO_LOCK)
diff --git a/content/browser/webauth/cbor/cbor_binary.h b/content/browser/webauth/cbor/cbor_binary.h new file mode 100644 index 0000000..14a182d --- /dev/null +++ b/content/browser/webauth/cbor/cbor_binary.h
@@ -0,0 +1,35 @@ +// Copyright 2017 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 <stdint.h> + +#ifndef CONTENT_BROWSER_WEBAUTH_CBOR_CBOR_BINARY_H_ +#define CONTENT_BROWSER_WEBAUTH_CBOR_CBOR_BINARY_H_ + +namespace content { +namespace impl { + +// Mask selecting the low-order 5 bits of the "initial byte", which is where +// the additional information is encoded. +static constexpr uint8_t kAdditionalInformationMask = 0x1F; +// Mask selecting the high-order 3 bits of the "initial byte", which indicates +// the major type of the encoded value. +static constexpr uint8_t kMajorTypeMask = 0xE0; +// Indicates the number of bits the "initial byte" needs to be shifted to the +// right after applying |kMajorTypeMask| to produce the major type in the +// lowermost bits. +static constexpr uint8_t kMajorTypeBitShift = 5u; +// Indicates the integer is in the following byte. +static constexpr uint8_t kAdditionalInformation1Byte = 24u; +// Indicates the integer is in the next 2 bytes. +static constexpr uint8_t kAdditionalInformation2Bytes = 25u; +// Indicates the integer is in the next 4 bytes. +static constexpr uint8_t kAdditionalInformation4Bytes = 26u; +// Indicates the integer is in the next 8 bytes. +static constexpr uint8_t kAdditionalInformation8Bytes = 27u; + +} // namespace impl +} // namespace content + +#endif // CONTENT_BROWSER_WEBAUTH_CBOR_CBOR_BINARY_H_
diff --git a/content/browser/webauth/cbor/cbor_reader.cc b/content/browser/webauth/cbor/cbor_reader.cc new file mode 100644 index 0000000..654f249 --- /dev/null +++ b/content/browser/webauth/cbor/cbor_reader.cc
@@ -0,0 +1,286 @@ +// Copyright 2017 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 "content/browser/webauth/cbor/cbor_reader.h" + +#include <math.h> +#include "base/numerics/safe_conversions.h" +#include "base/stl_util.h" +#include "base/strings/string_util.h" +#include "content/browser/webauth/cbor/cbor_binary.h" + +namespace content { + +namespace { + +CBORValue::Type GetMajorType(uint8_t initial_data_byte) { + return static_cast<CBORValue::Type>( + (initial_data_byte & impl::kMajorTypeMask) >> impl::kMajorTypeBitShift); +} + +uint8_t GetAdditionalInfo(uint8_t initial_data_byte) { + return initial_data_byte & impl::kAdditionalInformationMask; +} + +// Error messages that correspond to each of the error codes. +const char kNoError[] = "Successfully deserialized to a CBOR value."; +const char kUnsupportedMajorType[] = "Unsupported major type."; +const char kUnknownAdditionalInfo[] = + "Unknown additional info format in the first byte."; +const char kIncompleteCBORData[] = + "Prematurely terminated CBOR data byte array."; +const char kIncorrectMapKeyType[] = + "Map keys other than utf-8 encoded strings are not allowed."; +const char kTooMuchNesting[] = "Too much nesting."; +const char kInvalidUTF8[] = "String encoding other than utf8 are not allowed."; +const char kExtraneousData[] = "Trailing data bytes are not allowed."; +const char kDuplicateKey[] = "Duplicate map keys are not allowed."; +const char kMapKeyOutOfOrder[] = + "Map keys must be sorted by byte length and then by byte-wise lexical " + "order."; +const char kNonMinimalCBOREncoding[] = + "Unsigned integers must be encoded with minimum number of bytes."; + +} // namespace + +CBORReader::CBORReader(Bytes::const_iterator it, Bytes::const_iterator end) + : it_(it), end_(end), error_code_(DecoderError::NO_ERROR) {} +CBORReader::~CBORReader() {} + +// static +base::Optional<CBORValue> CBORReader::Read(const Bytes& data, + DecoderError* error_code_out, + int max_nesting_level) { + CBORReader reader(data.begin(), data.end()); + base::Optional<CBORValue> decoded_cbor = reader.DecodeCBOR(max_nesting_level); + + if (decoded_cbor.has_value()) + reader.CheckExtraneousData(); + if (error_code_out) + *error_code_out = reader.GetErrorCode(); + + if (reader.GetErrorCode() != DecoderError::NO_ERROR) + return base::nullopt; + return decoded_cbor; +} + +base::Optional<CBORValue> CBORReader::DecodeCBOR(int max_nesting_level) { + if (max_nesting_level < 0 || max_nesting_level > kCBORMaxDepth) { + error_code_ = DecoderError::TOO_MUCH_NESTING; + return base::nullopt; + } + + if (!CanConsume(1)) { + error_code_ = DecoderError::INCOMPLETE_CBOR_DATA; + return base::nullopt; + } + + const uint8_t initial_byte = *it_++; + const auto major_type = GetMajorType(initial_byte); + const uint8_t additional_info = GetAdditionalInfo(initial_byte); + + uint64_t length; + if (!ReadUnsignedInt(additional_info, &length)) + return base::nullopt; + + switch (major_type) { + case CBORValue::Type::UNSIGNED: + return CBORValue(length); + case CBORValue::Type::BYTE_STRING: + return ReadBytes(length); + case CBORValue::Type::STRING: + return ReadString(length); + case CBORValue::Type::ARRAY: + return ReadCBORArray(length, max_nesting_level); + case CBORValue::Type::MAP: + return ReadCBORMap(length, max_nesting_level); + case CBORValue::Type::NONE: + break; + } + + error_code_ = DecoderError::UNSUPPORTED_MAJOR_TYPE; + return base::nullopt; +} + +bool CBORReader::ReadUnsignedInt(int additional_info, uint64_t* length) { + uint8_t additional_bytes = 0; + if (additional_info < 24) { + *length = additional_info; + return true; + } else if (additional_info == 24) { + additional_bytes = 1; + } else if (additional_info == 25) { + additional_bytes = 2; + } else if (additional_info == 26) { + additional_bytes = 4; + } else if (additional_info == 27) { + additional_bytes = 8; + } else { + error_code_ = DecoderError::UNKNOWN_ADDITIONAL_INFO; + return false; + } + + if (!CanConsume(additional_bytes)) { + error_code_ = DecoderError::INCOMPLETE_CBOR_DATA; + return false; + } + + uint64_t int_data = 0; + for (uint8_t i = 0; i < additional_bytes; ++i) { + int_data <<= 8; + int_data |= *it_++; + } + + *length = int_data; + return CheckUintEncodedByteLength(additional_bytes, int_data); +} + +base::Optional<CBORValue> CBORReader::ReadString(uint64_t num_bytes) { + if (!CanConsume(num_bytes)) { + error_code_ = DecoderError::INCOMPLETE_CBOR_DATA; + return base::nullopt; + } + + std::string cbor_string(it_, it_ + num_bytes); + it_ += num_bytes; + + return HasValidUTF8Format(cbor_string) + ? base::make_optional<CBORValue>(CBORValue(std::move(cbor_string))) + : base::nullopt; +} + +base::Optional<CBORValue> CBORReader::ReadBytes(uint64_t num_bytes) { + if (!CanConsume(num_bytes)) { + error_code_ = DecoderError::INCOMPLETE_CBOR_DATA; + return base::nullopt; + } + + Bytes cbor_byte_string(it_, it_ + num_bytes); + it_ += num_bytes; + + return CBORValue(std::move(cbor_byte_string)); +} + +base::Optional<CBORValue> CBORReader::ReadCBORArray(uint64_t length, + int max_nesting_level) { + CBORValue::ArrayValue cbor_array; + while (length-- > 0) { + base::Optional<CBORValue> cbor_element = DecodeCBOR(max_nesting_level - 1); + if (!cbor_element.has_value()) + return base::nullopt; + cbor_array.push_back(std::move(cbor_element.value())); + } + return CBORValue(std::move(cbor_array)); +} + +base::Optional<CBORValue> CBORReader::ReadCBORMap(uint64_t length, + int max_nesting_level) { + CBORValue::MapValue cbor_map; + while (length-- > 0) { + base::Optional<CBORValue> key = DecodeCBOR(max_nesting_level - 1); + base::Optional<CBORValue> value = DecodeCBOR(max_nesting_level - 1); + if (!key.has_value() || !value.has_value()) + return base::nullopt; + if (key.value().type() != CBORValue::Type::STRING) { + error_code_ = DecoderError::INCORRECT_MAP_KEY_TYPE; + return base::nullopt; + } + + if (!CheckDuplicateKey(key.value().GetString(), &cbor_map) || + !CheckOutOfOrderKey(key.value().GetString(), &cbor_map)) { + return base::nullopt; + } + + cbor_map.insert_or_assign(key.value().GetString(), + std::move(value.value())); + } + return CBORValue(std::move(cbor_map)); +} + +bool CBORReader::CanConsume(uint64_t bytes) { + if (base::checked_cast<uint64_t>(std::distance(it_, end_)) >= bytes) { + return true; + } + error_code_ = DecoderError::INCOMPLETE_CBOR_DATA; + return false; +} + +bool CBORReader::CheckUintEncodedByteLength(uint8_t additional_bytes, + uint64_t uint_data) { + if ((additional_bytes == 1 && uint_data < 24) || + uint_data <= (1ULL << 8 * (additional_bytes >> 1)) - 1) { + error_code_ = DecoderError::NON_MINIMAL_CBOR_ENCODING; + return false; + } + return true; +} + +void CBORReader::CheckExtraneousData() { + if (it_ != end_) + error_code_ = DecoderError::EXTRANEOUS_DATA; +} + +bool CBORReader::CheckDuplicateKey(const std::string& new_key, + CBORValue::MapValue* map) { + if (base::ContainsKey(*map, new_key)) { + error_code_ = DecoderError::DUPLICATE_KEY; + return false; + } + return true; +} + +bool CBORReader::HasValidUTF8Format(const std::string& string_data) { + if (!base::IsStringUTF8(string_data.data())) { + error_code_ = DecoderError::INVALID_UTF8; + return false; + } + return true; +} + +bool CBORReader::CheckOutOfOrderKey(const std::string& new_key, + CBORValue::MapValue* map) { + auto comparator = map->key_comp(); + if (!map->empty() && comparator(new_key, map->rbegin()->first)) { + error_code_ = DecoderError::OUT_OF_ORDER_KEY; + return false; + } + return true; +} + +CBORReader::DecoderError CBORReader::GetErrorCode() { + return error_code_; +} + +// static +const char* CBORReader::ErrorCodeToString(DecoderError error) { + switch (error) { + case DecoderError::NO_ERROR: + return kNoError; + case DecoderError::UNSUPPORTED_MAJOR_TYPE: + return kUnsupportedMajorType; + case DecoderError::UNKNOWN_ADDITIONAL_INFO: + return kUnknownAdditionalInfo; + case DecoderError::INCOMPLETE_CBOR_DATA: + return kIncompleteCBORData; + case DecoderError::INCORRECT_MAP_KEY_TYPE: + return kIncorrectMapKeyType; + case DecoderError::TOO_MUCH_NESTING: + return kTooMuchNesting; + case DecoderError::INVALID_UTF8: + return kInvalidUTF8; + case DecoderError::EXTRANEOUS_DATA: + return kExtraneousData; + case DecoderError::DUPLICATE_KEY: + return kDuplicateKey; + case DecoderError::OUT_OF_ORDER_KEY: + return kMapKeyOutOfOrder; + case DecoderError::NON_MINIMAL_CBOR_ENCODING: + return kNonMinimalCBOREncoding; + default: + NOTREACHED(); + return "Unknown error code."; + } +} + +} // namespace content
diff --git a/content/browser/webauth/cbor/cbor_reader.h b/content/browser/webauth/cbor/cbor_reader.h new file mode 100644 index 0000000..46552097 --- /dev/null +++ b/content/browser/webauth/cbor/cbor_reader.h
@@ -0,0 +1,107 @@ +// Copyright 2017 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. + +#ifndef CONTENT_BROWSER_WEBAUTH_CBOR_CBOR_READER_H_ +#define CONTENT_BROWSER_WEBAUTH_CBOR_CBOR_READER_H_ + +#include <stddef.h> +#include <string> +#include <vector> + +#include "base/optional.h" +#include "content/browser/webauth/cbor/cbor_values.h" +#include "content/common/content_export.h" + +// Concise Binary Object Representation (CBOR) decoder as defined by +// https://tools.ietf.org/html/rfc7049. This decoder only accepts canonical +// CBOR as defined by section 3.9. +// Supported: +// * Major types: +// * 0: Unsigned integers, up to 64-bit. +// * 2: Byte strings. +// * 3: UTF-8 strings. +// * 4: Definite-length arrays. +// * 5: Definite-length maps. +// +// Requirements for canonical CBOR representation: +// - Duplicate keys for map are not allowed. +// - Keys for map must be sorted first by length and then by byte-wise +// lexical order. +// +// Known limitations and interpretations of the RFC: +// - Does not support negative integers, floating point numbers, indefinite +// data streams and tagging. +// - Non-character codepoint are not supported for Major type 3. +// - Incomplete CBOR data items are treated as syntax errors. +// - Trailing data bytes are treated as errors. +// - Unknown additional information formats are treated as syntax errors. +// - Callers can decode CBOR values with at most 16 nested depth layer. More +// strict restrictions on nesting layer size of CBOR values can be enforced +// by setting |max_nesting_level|. + +namespace content { + +class CONTENT_EXPORT CBORReader { + public: + using Bytes = std::vector<uint8_t>; + + enum class DecoderError { + NO_ERROR = 0, + UNSUPPORTED_MAJOR_TYPE, + UNKNOWN_ADDITIONAL_INFO, + INCOMPLETE_CBOR_DATA, + INCORRECT_MAP_KEY_TYPE, + TOO_MUCH_NESTING, + INVALID_UTF8, + EXTRANEOUS_DATA, + DUPLICATE_KEY, + OUT_OF_ORDER_KEY, + NON_MINIMAL_CBOR_ENCODING, + }; + + // CBOR nested depth sufficient for most use cases. + static const int kCBORMaxDepth = 16; + + ~CBORReader(); + + // Reads and parses |input_data| into a CBORValue. If any one of the syntax + // formats is violated -including unknown additional info and incomplete + // CBOR data- then an empty optional is returned. Optional |error_code_out| + // can be provided by the caller to obtain additional information about + // decoding failures. + static base::Optional<CBORValue> Read(const Bytes& input_data, + DecoderError* error_code_out = nullptr, + int max_nesting_level = kCBORMaxDepth); + + // Translates errors to human-readable error messages. + static const char* ErrorCodeToString(DecoderError error_code); + + private: + CBORReader(Bytes::const_iterator it, const Bytes::const_iterator end); + base::Optional<CBORValue> DecodeCBOR(int max_nesting_level); + bool ReadUnsignedInt(int additional_info, uint64_t* length); + base::Optional<CBORValue> ReadBytes(uint64_t num_bytes); + base::Optional<CBORValue> ReadString(uint64_t num_bytes); + base::Optional<CBORValue> ReadCBORArray(uint64_t length, + int max_nesting_level); + base::Optional<CBORValue> ReadCBORMap(uint64_t length, int max_nesting_level); + bool CanConsume(uint64_t bytes); + void CheckExtraneousData(); + bool CheckDuplicateKey(const std::string& new_key, CBORValue::MapValue* map); + bool HasValidUTF8Format(const std::string& string_data); + bool CheckOutOfOrderKey(const std::string& new_key, CBORValue::MapValue* map); + bool CheckUintEncodedByteLength(uint8_t additional_bytes, uint64_t uint_data); + + DecoderError GetErrorCode(); + + Bytes::const_iterator it_; + const Bytes::const_iterator end_; + DecoderError error_code_; + + DISALLOW_COPY_AND_ASSIGN(CBORReader); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_WEBAUTH_CBOR_CBOR_READER_H_
diff --git a/content/browser/webauth/cbor/cbor_reader_unittest.cc b/content/browser/webauth/cbor/cbor_reader_unittest.cc new file mode 100644 index 0000000..947137e --- /dev/null +++ b/content/browser/webauth/cbor/cbor_reader_unittest.cc
@@ -0,0 +1,530 @@ +// Copyright 2017 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 "content/browser/webauth/cbor/cbor_reader.h" +#include "base/strings/stringprintf.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +/* Leveraging RFC 7049 examples from + https://github.com/cbor/test-vectors/blob/master/appendix_a.json. */ +namespace content { + +TEST(CBORReaderTest, TestReadUint) { + typedef struct { + const uint64_t value; + const std::vector<uint8_t> cbor_data; + } UintTestCase; + + static const UintTestCase kUintTestCases[] = { + {0, {0x00}}, + {1, {0x01}}, + {10, {0x0a}}, + {23, {0x17}}, + {24, {0x18, 0x18}}, + {25, {0x18, 0x19}}, + {100, {0x18, 0x64}}, + {1000, {0x19, 0x03, 0xe8}}, + {1000000, {0x1a, 0x00, 0x0f, 0x42, 0x40}}, + {0xFFFFFFFF, {0x1a, 0xff, 0xff, 0xff, 0xff}}, + }; + + int test_case_index = 0; + for (const UintTestCase& test_case : kUintTestCases) { + testing::Message scope_message; + scope_message << "testing uint at index : " << test_case_index++; + SCOPED_TRACE(scope_message); + + base::Optional<CBORValue> cbor = CBORReader::Read(test_case.cbor_data); + ASSERT_TRUE(cbor.has_value()); + ASSERT_EQ(cbor.value().type(), CBORValue::Type::UNSIGNED); + EXPECT_EQ(cbor.value().GetUnsigned(), test_case.value); + } +} + +TEST(CBORReaderTest, TestUintEncodedWithNonMinimumByteLength) { + static const std::vector<std::vector<uint8_t>> non_minimal_uint_encodings = { + // Uint 23 encoded with 1 byte. + {0x18, 0x17}, + // Uint 255 encoded with 2 bytes. + {0x19, 0x00, 0xff}, + // Uint 65535 encoded with 4 byte. + {0x1a, 0x00, 0x00, 0xff, 0xff}, + // Uint 4294967295 encoded with 8 byte. + {0x1b, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff}}; + + int test_case_index = 0; + CBORReader::DecoderError error_code; + for (const auto non_minimal_uint : non_minimal_uint_encodings) { + testing::Message scope_message; + scope_message << "testing element at index " << test_case_index++; + SCOPED_TRACE(scope_message); + + base::Optional<CBORValue> cbor = + CBORReader::Read(non_minimal_uint, &error_code); + EXPECT_FALSE(cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::NON_MINIMAL_CBOR_ENCODING); + } +} + +TEST(CBORReaderTest, TestReadBytes) { + typedef struct { + const std::vector<uint8_t> value; + const std::vector<uint8_t> cbor_data; + } ByteTestCase; + + static const ByteTestCase kByteStringTestCases[] = { + // clang-format off + {{}, {0x40}}, + {{0x01, 0x02, 0x03, 0x04},{0x44, 0x01, 0x02, 0x03, 0x04}}, + // clang-format on + }; + + int element_index = 0; + for (const ByteTestCase& test_case : kByteStringTestCases) { + testing::Message scope_message; + scope_message << "testing string test case at : " << element_index++; + SCOPED_TRACE(scope_message); + + base::Optional<CBORValue> cbor = CBORReader::Read(test_case.cbor_data); + ASSERT_TRUE(cbor.has_value()); + ASSERT_EQ(cbor.value().type(), CBORValue::Type::BYTE_STRING); + EXPECT_EQ(cbor.value().GetBytestring(), test_case.value); + } +} + +TEST(CBORReaderTest, TestReadString) { + typedef struct { + const std::string value; + const std::vector<uint8_t> cbor_data; + } StringTestCase; + + static const StringTestCase kStringTestCases[] = { + {"", {0x60}}, + {"a", {0x61, 0x61}}, + {"IETF", {0x64, 0x49, 0x45, 0x54, 0x46}}, + {"\"\\", {0x62, 0x22, 0x5c}}, + {"\xc3\xbc", {0x62, 0xc3, 0xbc}}, + {"\xe6\xb0\xb4", {0x63, 0xe6, 0xb0, 0xb4}}, + {"\xf0\x90\x85\x91", {0x64, 0xf0, 0x90, 0x85, 0x91}}, + }; + + for (const StringTestCase& test_case : kStringTestCases) { + testing::Message scope_message; + scope_message << "testing string value : " + << base::StringPrintf("%s", test_case.value.data()); + SCOPED_TRACE(scope_message); + + base::Optional<CBORValue> cbor = CBORReader::Read(test_case.cbor_data); + ASSERT_TRUE(cbor.has_value()); + ASSERT_EQ(cbor.value().type(), CBORValue::Type::STRING); + EXPECT_EQ(cbor.value().GetString(), test_case.value); + } +} + +TEST(CBORReaderTest, TestReadArray) { + static const std::vector<uint8_t> kArrayTestCaseCbor = { + // clang-format off + 0x98, 0x19, // array of 25 elements + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x18, 0x18, 0x19, + // clang-format on + }; + + base::Optional<CBORValue> cbor = CBORReader::Read(kArrayTestCaseCbor); + ASSERT_TRUE(cbor.has_value()); + CBORValue cbor_array = std::move(cbor.value()); + ASSERT_EQ(cbor_array.type(), CBORValue::Type::ARRAY); + ASSERT_THAT(cbor_array.GetArray(), testing::SizeIs(25)); + + std::vector<CBORValue> array; + for (int i = 0; i < 25; i++) { + testing::Message scope_message; + scope_message << "testing array element at index " << i; + SCOPED_TRACE(scope_message); + + ASSERT_EQ(cbor_array.GetArray()[i].type(), CBORValue::Type::UNSIGNED); + EXPECT_EQ(cbor_array.GetArray()[i].GetUnsigned(), + static_cast<uint64_t>(i + 1)); + } +} + +TEST(CBORReaderTest, TestReadMapWithMapValue) { + static const std::vector<uint8_t> kMapTestCaseCbor = { + // clang-format off + 0xa3, // map of 3 pairs: + 0x60, // "" + 0x61, 0x2e, // "." + + 0x61, 0x62, // "b" + 0x61, 0x42, // "B" + + 0x62, 0x61, 0x61, // "aa" + 0x62, 0x41, 0x41, // "AA" + // clang-format on + }; + + base::Optional<CBORValue> cbor = CBORReader::Read(kMapTestCaseCbor); + ASSERT_TRUE(cbor.has_value()); + CBORValue cbor_val = std::move(cbor.value()); + ASSERT_EQ(cbor_val.type(), CBORValue::Type::MAP); + ASSERT_EQ(cbor_val.GetMap().size(), 3u); + + ASSERT_EQ(cbor_val.GetMap().count(""), 1u); + ASSERT_EQ(cbor_val.GetMap().find("")->second.type(), CBORValue::Type::STRING); + EXPECT_EQ(cbor_val.GetMap().find("")->second.GetString(), "."); + + ASSERT_EQ(cbor_val.GetMap().count("b"), 1u); + ASSERT_EQ(cbor_val.GetMap().find("b")->second.type(), + CBORValue::Type::STRING); + EXPECT_EQ(cbor_val.GetMap().find("b")->second.GetString(), "B"); + + ASSERT_EQ(cbor_val.GetMap().count("aa"), 1u); + ASSERT_EQ(cbor_val.GetMap().find("aa")->second.type(), + CBORValue::Type::STRING); + EXPECT_EQ(cbor_val.GetMap().find("aa")->second.GetString(), "AA"); +} + +TEST(CBORReaderTest, TestReadMapWithArray) { + static const std::vector<uint8_t> kMapArrayTestCaseCbor = { + // clang-format off + 0xa2, // map of 2 pairs + 0x61, 0x61, // "a" + 0x01, + + 0x61, 0x62, // "b" + 0x82, // array with 2 elements + 0x02, + 0x03, + // clang-format on + }; + + base::Optional<CBORValue> cbor = CBORReader::Read(kMapArrayTestCaseCbor); + ASSERT_TRUE(cbor.has_value()); + CBORValue cbor_val = std::move(cbor.value()); + ASSERT_EQ(cbor_val.type(), CBORValue::Type::MAP); + ASSERT_EQ(cbor_val.GetMap().size(), 2u); + + ASSERT_EQ(cbor_val.GetMap().count("a"), 1u); + ASSERT_EQ(cbor_val.GetMap().find("a")->second.type(), + CBORValue::Type::UNSIGNED); + EXPECT_EQ(cbor_val.GetMap().find("a")->second.GetUnsigned(), 1u); + + ASSERT_EQ(cbor_val.GetMap().count("b"), 1u); + ASSERT_EQ(cbor_val.GetMap().find("b")->second.type(), CBORValue::Type::ARRAY); + + CBORValue nested_array = cbor_val.GetMap().find("b")->second.Clone(); + ASSERT_EQ(nested_array.GetArray().size(), 2u); + for (int i = 0; i < 2; i++) { + ASSERT_THAT(nested_array.GetArray()[i].type(), CBORValue::Type::UNSIGNED); + EXPECT_EQ(nested_array.GetArray()[i].GetUnsigned(), + static_cast<uint64_t>(i + 2)); + } +} + +TEST(CBORReaderTest, TestReadNestedMap) { + static const std::vector<uint8_t> kNestedMapTestCase = { + // clang-format off + 0xa2, // map of 2 pairs + 0x61, 0x61, // "a" + 0x01, + + 0x61, 0x62, // "b" + 0xa2, // map of 2 pairs + 0x61, 0x63, // "c" + 0x02, + + 0x61, 0x64, // "d" + 0x03, + // clang-format on + }; + + base::Optional<CBORValue> cbor = CBORReader::Read(kNestedMapTestCase); + ASSERT_TRUE(cbor.has_value()); + CBORValue cbor_val = std::move(cbor.value()); + ASSERT_EQ(cbor_val.type(), CBORValue::Type::MAP); + ASSERT_EQ(cbor_val.GetMap().size(), 2u); + ASSERT_EQ(cbor_val.GetMap().count("a"), 1u); + ASSERT_EQ(cbor_val.GetMap().find("a")->second.type(), + CBORValue::Type::UNSIGNED); + EXPECT_EQ(cbor_val.GetMap().find("a")->second.GetUnsigned(), 1u); + + ASSERT_EQ(cbor_val.GetMap().count("b"), 1u); + CBORValue nested_map = cbor_val.GetMap().find("b")->second.Clone(); + ASSERT_EQ(nested_map.type(), CBORValue::Type::MAP); + ASSERT_EQ(nested_map.GetMap().size(), 2u); + + ASSERT_EQ(nested_map.GetMap().count("c"), 1u); + ASSERT_EQ(nested_map.GetMap().find("c")->second.type(), + CBORValue::Type::UNSIGNED); + EXPECT_EQ(nested_map.GetMap().find("c")->second.GetUnsigned(), 2u); + + ASSERT_EQ(nested_map.GetMap().count("d"), 1u); + ASSERT_EQ(nested_map.GetMap().find("d")->second.type(), + CBORValue::Type::UNSIGNED); + EXPECT_EQ(nested_map.GetMap().find("d")->second.GetUnsigned(), 3u); +} + +TEST(CBORReaderTest, TestIncompleteCBORDataError) { + static const std::vector<std::vector<uint8_t>> incomplete_cbor_list = { + // Additional info byte corresponds to unsigned int that corresponds + // to 2 additional bytes. But actual data encoded in one byte. + {0x19, 0x03}, + // CBOR bytestring of length 3 encoded with additional info of length 4. + {0x44, 0x01, 0x02, 0x03}, + // CBOR string data "IETF" of length 4 encoded with additional info of + // length 5. + {0x65, 0x49, 0x45, 0x54, 0x46}, + // CBOR array of length 1 encoded with additional info of length 2. + {0x82, 0x02}, + // CBOR map with single key value pair encoded with additional info of + // length 2. + {0xa2, 0x61, 0x61, 0x01}, + }; + + int test_element_index = 0; + for (auto incomplete_data : incomplete_cbor_list) { + testing::Message scope_message; + scope_message << "testing incomplete data at index : " + << test_element_index++; + SCOPED_TRACE(scope_message); + + CBORReader::DecoderError error_code; + base::Optional<CBORValue> cbor = + CBORReader::Read(incomplete_data, &error_code); + EXPECT_FALSE(cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::INCOMPLETE_CBOR_DATA); + } +} + +// While RFC 7049 allows CBOR map keys with all types, current decoder only +// supports string type for CBOR map keys. Checks for error when decoding CBOR +// map with key other than UTF-8 string. +TEST(CBORReaderTest, TestUnsupportedMapKeyFormatError) { + static const std::vector<uint8_t> kMapWithUintKey = { + // clang-format off + 0xa2, // map of 2 pairs + 0x01, // key : 1 + 0x02, // value : 2 + + 0x61, 0x64, // key : "d" + 0x03, // value : 3 + // clang-format on + }; + + CBORReader::DecoderError error_code; + base::Optional<CBORValue> cbor = + CBORReader::Read(kMapWithUintKey, &error_code); + EXPECT_FALSE(cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::INCORRECT_MAP_KEY_TYPE); +} + +TEST(CBORReaderTest, TestUnknownAdditionalInfoError) { + static const std::vector<std::vector<uint8_t>> kUnknownAdditionalInfoList = { + // "IETF" encoded with major type 3 and additional info of 28. + {0x7C, 0x49, 0x45, 0x54, 0x46}, + // "\"\\" encoded with major type 3 and additional info of 29. + {0x7D, 0x22, 0x5c}, + // "\xc3\xbc" encoded with major type 3 and additional info of 30. + {0x7E, 0xc3, 0xbc}, + // "\xe6\xb0\xb4" encoded with major type 3 and additional info of 31. + {0x7F, 0xe6, 0xb0, 0xb4}}; + + int test_element_index = 0; + for (auto incorrect_cbor : kUnknownAdditionalInfoList) { + testing::Message scope_message; + scope_message << "testing data : " << test_element_index++; + SCOPED_TRACE(scope_message); + + CBORReader::DecoderError error_code; + base::Optional<CBORValue> cbor = + CBORReader::Read(incorrect_cbor, &error_code); + EXPECT_FALSE(cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::UNKNOWN_ADDITIONAL_INFO); + } +} + +TEST(CBORReaderTest, TestTooMuchNestingError) { + static const std::vector<std::vector<uint8_t>> kZeroDepthCBORList = { + // Unsigned int with value 100. + {0x18, 0x64}, + // CBOR bytestring of length 4. + {0x44, 0x01, 0x02, 0x03, 0x04}, + // CBOR string of corresponding to "IETF. + {0x64, 0x49, 0x45, 0x54, 0x46}, + // Empty CBOR array. + {0x80}, + // Empty CBOR Map + {0xa0}, + }; + + int test_element_index = 0; + for (auto zero_depth_data : kZeroDepthCBORList) { + testing::Message scope_message; + scope_message << "testing zero nested data : " << test_element_index++; + SCOPED_TRACE(scope_message); + + CBORReader::DecoderError error_code; + base::Optional<CBORValue> cbor = + CBORReader::Read(zero_depth_data, &error_code, 0); + EXPECT_TRUE(cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::NO_ERROR); + } + + // Corresponds to a CBOR structure with a nesting depth of 2: + // {"a": 1, + // "b": [2, 3]} + static const std::vector<uint8_t> kNestedCBORData = { + // clang-format off + 0xa2, // map of 2 pairs + 0x61, 0x61, // "a" + 0x01, + + 0x61, 0x62, // "b" + 0x82, // array with 2 elements + 0x02, + 0x03, + // clang-format on + }; + + CBORReader::DecoderError error_code; + base::Optional<CBORValue> cbor_single_layer_max = + CBORReader::Read(kNestedCBORData, &error_code, 1); + EXPECT_FALSE(cbor_single_layer_max.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::TOO_MUCH_NESTING); + + base::Optional<CBORValue> cbor_double_layer_max = + CBORReader::Read(kNestedCBORData, &error_code, 2); + EXPECT_TRUE(cbor_double_layer_max.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::NO_ERROR); +} + +TEST(CBORReaderTest, TestOutOfOrderKeyError) { + static const std::vector<uint8_t> kMapWithUnsortedKey = { + // clang-format off + 0xa6, // map of 6 pairs: + 0x60, // "" + 0x61, 0x2e, // "." + + 0x61, 0x62, // "b" + 0x61, 0x42, // "B" + + 0x61, 0x63, // "c" + 0x61, 0x43, // "C" + + 0x61, 0x64, // "d" + 0x61, 0x44, // "D" + + 0x61, 0x61, // "a" (Out of order key) + 0x61, 0x45, // "E" + + 0x62, 0x61, 0x61, // "aa" + 0x62, 0x41, 0x41, // "AA" + // clang-format on + }; + + CBORReader::DecoderError error_code; + + base::Optional<CBORValue> cbor = + CBORReader::Read(kMapWithUnsortedKey, &error_code); + EXPECT_FALSE(cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::OUT_OF_ORDER_KEY); +} + +TEST(CBORReaderTest, TestDuplicateKeyError) { + static const std::vector<uint8_t> kMapWithDuplicateKey = { + // clang-format off + 0xa6, // map of 6 pairs: + 0x60, // "" + 0x61, 0x2e, // "." + + 0x61, 0x62, // "b" + 0x61, 0x42, // "B" + + 0x61, 0x62, // "b" (Duplicate key) + 0x61, 0x43, // "C" + + 0x61, 0x64, // "d" + 0x61, 0x44, // "D" + + 0x61, 0x65, // "e" + 0x61, 0x44, // "D" + + 0x62, 0x61, 0x61, // "aa" + 0x62, 0x41, 0x41, // "AA" + // clang-format on + }; + + CBORReader::DecoderError error_code; + + base::Optional<CBORValue> cbor = + CBORReader::Read(kMapWithDuplicateKey, &error_code); + EXPECT_FALSE(cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::DUPLICATE_KEY); +} + +// Leveraging Markus Kuhn’s UTF-8 decoder stress test. See +// http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt for details. +TEST(CBORReaderTest, TestIncorrectStringEncodingError) { + static const std::vector<std::vector<uint8_t>> utf8_character_encodings = { + // Corresponds to utf8 encoding of "" (section 2.3.1 of stress test). + {0x63, 0xED, 0x9F, 0xBF}, + // Corresponds to utf8 encoding of "" (section 2.3.2 of stress test). + {0x63, 0xEE, 0x80, 0x80}, + // Corresponds to utf8 encoding of "�" (section 2.3.3 of stress test). + {0x63, 0xEF, 0xBF, 0xBD}, + }; + + int test_element_index = 0; + CBORReader::DecoderError error_code; + for (auto cbor_byte : utf8_character_encodings) { + testing::Message scope_message; + scope_message << "testing cbor data utf8 encoding : " + << test_element_index++; + SCOPED_TRACE(scope_message); + + base::Optional<CBORValue> correctly_encoded_cbor = + CBORReader::Read(cbor_byte, &error_code); + EXPECT_TRUE(correctly_encoded_cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::NO_ERROR); + } + + // Incorrect UTF8 encoding referenced by section 3.5.3 of the stress test. + std::vector<uint8_t> impossible_utf_byte{0x64, 0xfe, 0xfe, 0xff, 0xff}; + base::Optional<CBORValue> incorrectly_encoded_cbor = + CBORReader::Read(impossible_utf_byte, &error_code); + EXPECT_FALSE(incorrectly_encoded_cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::INVALID_UTF8); +} + +TEST(CBORReaderTest, TestExtraneousCBORDataError) { + static const std::vector<std::vector<uint8_t>> zero_padded_cbor_list = { + // 1 extra byte after a 2-byte unsigned int. + {0x19, 0x03, 0x05, 0x00}, + // 1 extra byte after a 4-byte cbor byte array. + {0x44, 0x01, 0x02, 0x03, 0x04, 0x00}, + // 1 extra byte after a 4-byte string. + {0x64, 0x49, 0x45, 0x54, 0x46, 0x00}, + // 1 extra byte after CBOR array of length 2. + {0x82, 0x01, 0x02, 0x00}, + // 1 extra key value pair after CBOR map of size 2. + {0xa1, 0x61, 0x63, 0x02, 0x61, 0x64, 0x03}, + }; + + int test_element_index = 0; + for (auto extraneous_cbor_data : zero_padded_cbor_list) { + testing::Message scope_message; + scope_message << "testing cbor extraneous data : " << test_element_index++; + SCOPED_TRACE(scope_message); + + CBORReader::DecoderError error_code; + base::Optional<CBORValue> cbor = + CBORReader::Read(extraneous_cbor_data, &error_code); + EXPECT_FALSE(cbor.has_value()); + EXPECT_EQ(error_code, CBORReader::DecoderError::EXTRANEOUS_DATA); + } +} + +} // namespace content
diff --git a/content/browser/webauth/cbor/cbor_values.cc b/content/browser/webauth/cbor/cbor_values.cc index 1b3494c9..790a23f 100644 --- a/content/browser/webauth/cbor/cbor_values.cc +++ b/content/browser/webauth/cbor/cbor_values.cc
@@ -20,12 +20,10 @@ CBORValue::CBORValue(Type type) : type_(type) { // Initialize with the default value. switch (type_) { - case Type::NONE: - return; case Type::UNSIGNED: unsigned_value_ = 0; return; - case Type::BYTESTRING: + case Type::BYTE_STRING: new (&bytestring_value_) BinaryValue(); return; case Type::STRING: @@ -37,17 +35,20 @@ case Type::MAP: new (&map_value_) MapValue(); return; + case Type::NONE: + return; } + NOTREACHED(); } CBORValue::CBORValue(uint64_t in_unsigned) : type_(Type::UNSIGNED), unsigned_value_(in_unsigned) {} CBORValue::CBORValue(const BinaryValue& in_bytes) - : type_(Type::BYTESTRING), bytestring_value_(in_bytes) {} + : type_(Type::BYTE_STRING), bytestring_value_(in_bytes) {} CBORValue::CBORValue(BinaryValue&& in_bytes) noexcept - : type_(Type::BYTESTRING), bytestring_value_(std::move(in_bytes)) {} + : type_(Type::BYTE_STRING), bytestring_value_(std::move(in_bytes)) {} CBORValue::CBORValue(const char* in_string) : CBORValue(std::string(in_string)) {} @@ -96,7 +97,7 @@ return CBORValue(); case Type::UNSIGNED: return CBORValue(unsigned_value_); - case Type::BYTESTRING: + case Type::BYTE_STRING: return CBORValue(bytestring_value_); case Type::STRING: return CBORValue(string_value_); @@ -139,12 +140,10 @@ type_ = that.type_; switch (type_) { - case Type::NONE: - return; case Type::UNSIGNED: unsigned_value_ = that.unsigned_value_; return; - case Type::BYTESTRING: + case Type::BYTE_STRING: new (&bytestring_value_) BinaryValue(std::move(that.bytestring_value_)); return; case Type::STRING: @@ -156,17 +155,15 @@ case Type::MAP: new (&map_value_) MapValue(std::move(that.map_value_)); return; + case Type::NONE: + return; } + NOTREACHED(); } void CBORValue::InternalCleanup() { switch (type_) { - case Type::NONE: - case Type::UNSIGNED: - // Nothing to do - break; - ; - case Type::BYTESTRING: + case Type::BYTE_STRING: bytestring_value_.~BinaryValue(); break; case Type::STRING: @@ -178,6 +175,9 @@ case Type::MAP: map_value_.~MapValue(); break; + case Type::NONE: + case Type::UNSIGNED: + break; } type_ = Type::NONE; }
diff --git a/content/browser/webauth/cbor/cbor_values.h b/content/browser/webauth/cbor/cbor_values.h index 6fe9e14..3e993d90 100644 --- a/content/browser/webauth/cbor/cbor_values.h +++ b/content/browser/webauth/cbor/cbor_values.h
@@ -5,7 +5,6 @@ #ifndef CONTENT_BROWSER_WEBAUTH_CBOR_CBOR_VALUES_H_ #define CONTENT_BROWSER_WEBAUTH_CBOR_CBOR_VALUES_H_ -#include <stdint.h> #include <string> #include <tuple> #include <vector> @@ -60,12 +59,12 @@ using MapValue = base::flat_map<std::string, CBORValue, CTAPLess>; enum class Type { - NONE, - UNSIGNED, - BYTESTRING, - STRING, - ARRAY, - MAP, + UNSIGNED = 0, + BYTE_STRING = 2, + STRING = 3, + ARRAY = 4, + MAP = 5, + NONE = -1, }; CBORValue(CBORValue&& that) noexcept; @@ -102,7 +101,7 @@ bool is_type(Type type) const { return type == type_; } bool is_none() const { return type() == Type::NONE; } bool is_unsigned() const { return type() == Type::UNSIGNED; } - bool is_bytestring() const { return type() == Type::BYTESTRING; } + bool is_bytestring() const { return type() == Type::BYTE_STRING; } bool is_string() const { return type() == Type::STRING; } bool is_array() const { return type() == Type::ARRAY; } bool is_map() const { return type() == Type::MAP; }
diff --git a/content/browser/webauth/cbor/cbor_values_unittest.cc b/content/browser/webauth/cbor/cbor_values_unittest.cc index a0df69ad..7500850 100644 --- a/content/browser/webauth/cbor/cbor_values_unittest.cc +++ b/content/browser/webauth/cbor/cbor_values_unittest.cc
@@ -59,7 +59,7 @@ TEST(CBORValuesTest, ConstructBytestring) { CBORValue value(CBORValue::BinaryValue({0xF, 0x0, 0x0, 0xB, 0xA, 0x2})); - EXPECT_EQ(CBORValue::Type::BYTESTRING, value.type()); + EXPECT_EQ(CBORValue::Type::BYTE_STRING, value.type()); EXPECT_EQ(CBORValue::BinaryValue({0xF, 0x0, 0x0, 0xB, 0xA, 0x2}), value.GetBytestring()); } @@ -215,13 +215,13 @@ const CBORValue::BinaryValue bytes({0xF, 0x0, 0x0, 0xB, 0xA, 0x2}); CBORValue value(bytes); CBORValue moved_value(std::move(value)); - EXPECT_EQ(CBORValue::Type::BYTESTRING, moved_value.type()); + EXPECT_EQ(CBORValue::Type::BYTE_STRING, moved_value.type()); EXPECT_EQ(bytes, moved_value.GetBytestring()); CBORValue blank; blank = CBORValue(bytes); - EXPECT_EQ(CBORValue::Type::BYTESTRING, blank.type()); + EXPECT_EQ(CBORValue::Type::BYTE_STRING, blank.type()); EXPECT_EQ(bytes, blank.GetBytestring()); } @@ -267,4 +267,4 @@ EXPECT_TRUE(test.GetUnsigned() == 1); } -} // namespace content \ No newline at end of file +} // namespace content
diff --git a/content/browser/webauth/cbor/cbor_writer.cc b/content/browser/webauth/cbor/cbor_writer.cc index 9ee55b21..514c38c 100644 --- a/content/browser/webauth/cbor/cbor_writer.cc +++ b/content/browser/webauth/cbor/cbor_writer.cc
@@ -8,25 +8,10 @@ #include "base/numerics/safe_conversions.h" #include "base/strings/string_piece.h" +#include "content/browser/webauth/cbor/cbor_binary.h" namespace content { -namespace { - -// Mask selecting the last 5 bits of the "initial byte" where -// 'additional information is encoded. -constexpr uint8_t kAdditionalInformationDataMask = 0x1F; -// Indicates the integer is in the following byte. -constexpr uint8_t kAdditionalInformation1Byte = 24u; -// Indicates the integer is in the next 2 bytes. -constexpr uint8_t kAdditionalInformation2Bytes = 25u; -// Indicates the integer is in the next 4 bytes. -constexpr uint8_t kAdditionalInformation4Bytes = 26u; -// Indicates the integer is in the next 8 bytes. -constexpr uint8_t kAdditionalInformation8Bytes = 27u; - -} // namespace - CBORWriter::~CBORWriter() {} // static @@ -48,21 +33,21 @@ switch (node.type()) { case CBORValue::Type::NONE: { - StartItem(CborMajorType::kByteString, 0); + StartItem(CBORValue::Type::BYTE_STRING, 0); return true; } // Represents unsigned integers. case CBORValue::Type::UNSIGNED: { uint64_t value = node.GetUnsigned(); - StartItem(CborMajorType::kUnsigned, value); + StartItem(CBORValue::Type::UNSIGNED, value); return true; } // Represents a byte string. - case CBORValue::Type::BYTESTRING: { + case CBORValue::Type::BYTE_STRING: { const CBORValue::BinaryValue& bytes = node.GetBytestring(); - StartItem(CborMajorType::kByteString, + StartItem(CBORValue::Type::BYTE_STRING, base::strict_cast<uint64_t>(bytes.size())); // Add the bytes. encoded_cbor_->insert(encoded_cbor_->end(), bytes.begin(), bytes.end()); @@ -71,7 +56,7 @@ case CBORValue::Type::STRING: { base::StringPiece string = node.GetString(); - StartItem(CborMajorType::kString, + StartItem(CBORValue::Type::STRING, base::strict_cast<uint64_t>(string.size())); // Add the characters. @@ -82,7 +67,7 @@ // Represents an array. case CBORValue::Type::ARRAY: { const CBORValue::ArrayValue& array = node.GetArray(); - StartItem(CborMajorType::kArray, array.size()); + StartItem(CBORValue::Type::ARRAY, array.size()); for (const auto& value : array) { if (!EncodeCBOR(value, max_nesting_level - 1)) return false; @@ -93,7 +78,7 @@ // Represents a map. case CBORValue::Type::MAP: { const CBORValue::MapValue& map = node.GetMap(); - StartItem(CborMajorType::kMap, map.size()); + StartItem(CBORValue::Type::MAP, map.size()); for (const auto& value : map) { if (!EncodeCBOR(CBORValue(value.first), max_nesting_level - 1)) @@ -103,23 +88,25 @@ } return true; } + default: + break; } NOTREACHED(); - return true; + return false; } -void CBORWriter::StartItem(CborMajorType type, uint64_t size) { - encoded_cbor_->push_back( - base::checked_cast<uint8_t>(static_cast<unsigned>(type) << 5)); +void CBORWriter::StartItem(CBORValue::Type type, uint64_t size) { + encoded_cbor_->push_back(base::checked_cast<uint8_t>( + static_cast<unsigned>(type) << impl::kMajorTypeBitShift)); SetUint(size); } void CBORWriter::SetAdditionalInformation(uint8_t additional_information) { DCHECK(!encoded_cbor_->empty()); - DCHECK_EQ(additional_information & kAdditionalInformationDataMask, + DCHECK_EQ(additional_information & impl::kAdditionalInformationMask, additional_information); encoded_cbor_->back() |= - (additional_information & kAdditionalInformationDataMask); + (additional_information & impl::kAdditionalInformationMask); } void CBORWriter::SetUint(uint64_t value) { @@ -133,19 +120,19 @@ SetAdditionalInformation(base::checked_cast<uint8_t>(value)); break; case 1: - SetAdditionalInformation(kAdditionalInformation1Byte); + SetAdditionalInformation(impl::kAdditionalInformation1Byte); shift = 0; break; case 2: - SetAdditionalInformation(kAdditionalInformation2Bytes); + SetAdditionalInformation(impl::kAdditionalInformation2Bytes); shift = 1; break; case 4: - SetAdditionalInformation(kAdditionalInformation4Bytes); + SetAdditionalInformation(impl::kAdditionalInformation4Bytes); shift = 3; break; case 8: - SetAdditionalInformation(kAdditionalInformation8Bytes); + SetAdditionalInformation(impl::kAdditionalInformation8Bytes); shift = 7; break; default:
diff --git a/content/browser/webauth/cbor/cbor_writer.h b/content/browser/webauth/cbor/cbor_writer.h index b8f40b3..8ea9651b 100644 --- a/content/browser/webauth/cbor/cbor_writer.h +++ b/content/browser/webauth/cbor/cbor_writer.h
@@ -5,12 +5,12 @@ #ifndef CONTENT_BROWSER_WEBAUTH_CBOR_CBOR_WRITER_H_ #define CONTENT_BROWSER_WEBAUTH_CBOR_CBOR_WRITER_H_ -#include "content/browser/webauth/cbor/cbor_values.h" - +#include <stddef.h> #include <stdint.h> #include <vector> #include "base/optional.h" +#include "content/browser/webauth/cbor/cbor_values.h" #include "content/common/content_export.h" // A basic Concise Binary Object Representation (CBOR) encoder as defined by @@ -48,23 +48,13 @@ // Current implementation of CBORWriter encoder meets all the requirements of // canonical CBOR. -enum class CborMajorType { - kUnsigned = 0, // Unsigned integer. - kNegative = 1, // Negative integer. Unsupported by this implementation. - kByteString = 2, // Byte string. - kString = 3, // String. - kArray = 4, // Array. - kMap = 5, // Map. -}; - namespace content { -namespace { -// Default that should be sufficiently large for most use cases. -constexpr size_t kDefaultMaxNestingDepth = 16; -} // namespace class CONTENT_EXPORT CBORWriter { public: + // Default that should be sufficiently large for most use cases. + static constexpr size_t kDefaultMaxNestingDepth = 16; + ~CBORWriter(); // Returns the CBOR byte string representation of |node|, unless its nesting @@ -85,7 +75,7 @@ bool EncodeCBOR(const CBORValue& node, int max_nesting_level); // Encodes the type and size of the data being added. - void StartItem(CborMajorType type, uint64_t size); + void StartItem(CBORValue::Type type, uint64_t size); // Encodes the additional information for the data. void SetAdditionalInformation(uint8_t additional_information);
diff --git a/content/common/resource_messages.h b/content/common/resource_messages.h index d8a3bf4..920ffb8 100644 --- a/content/common/resource_messages.h +++ b/content/common/resource_messages.h
@@ -140,7 +140,6 @@ IPC_STRUCT_TRAITS_MEMBER(headers) IPC_STRUCT_TRAITS_MEMBER(mime_type) IPC_STRUCT_TRAITS_MEMBER(charset) - IPC_STRUCT_TRAITS_MEMBER(has_major_certificate_errors) IPC_STRUCT_TRAITS_MEMBER(is_legacy_symantec_cert) IPC_STRUCT_TRAITS_MEMBER(cert_validity_start) IPC_STRUCT_TRAITS_MEMBER(content_length)
diff --git a/content/common/site_isolation_policy.cc b/content/common/site_isolation_policy.cc index 0d277b0..773000b 100644 --- a/content/common/site_isolation_policy.cc +++ b/content/common/site_isolation_policy.cc
@@ -9,6 +9,7 @@ #include "base/command_line.h" #include "base/feature_list.h" #include "base/metrics/field_trial_params.h" +#include "base/metrics/histogram_macros.h" #include "base/strings/string_split.h" #include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" @@ -44,8 +45,13 @@ std::string cmdline_arg = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kIsolateOrigins); - if (!cmdline_arg.empty()) - return ParseIsolatedOrigins(cmdline_arg); + if (!cmdline_arg.empty()) { + std::vector<url::Origin> cmdline_origins = + ParseIsolatedOrigins(cmdline_arg); + UMA_HISTOGRAM_COUNTS_1000("SiteIsolation.IsolateOrigins.Size", + cmdline_origins.size()); + return cmdline_origins; + } if (base::FeatureList::IsEnabled(features::kIsolateOrigins)) { std::string field_trial_arg = base::GetFieldTrialParamValueByFeature(
diff --git a/content/network/url_loader.cc b/content/network/url_loader.cc index 536cec49..57bc54d 100644 --- a/content/network/url_loader.cc +++ b/content/network/url_loader.cc
@@ -60,11 +60,9 @@ request->GetLoadTimingInfo(&response->head.load_timing); if (request->ssl_info().cert.get()) { - response->head.has_major_certificate_errors = - net::IsCertStatusError(request->ssl_info().cert_status) && - !net::IsCertStatusMinorError(request->ssl_info().cert_status); response->head.is_legacy_symantec_cert = - !response->head.has_major_certificate_errors && + (!net::IsCertStatusError(response->head.cert_status) || + net::IsCertStatusMinorError(response->head.cert_status)) && net::IsLegacySymantecCert(request->ssl_info().public_key_hashes); response->head.cert_validity_start = request->ssl_info().cert->valid_start();
diff --git a/content/public/common/resource_response.cc b/content/public/common/resource_response.cc index e3e2a664..a26e5f59 100644 --- a/content/public/common/resource_response.cc +++ b/content/public/common/resource_response.cc
@@ -18,8 +18,6 @@ } new_response->head.mime_type = head.mime_type; new_response->head.charset = head.charset; - new_response->head.has_major_certificate_errors = - head.has_major_certificate_errors; new_response->head.is_legacy_symantec_cert = head.is_legacy_symantec_cert; new_response->head.cert_validity_start = head.cert_validity_start; new_response->head.content_length = head.content_length;
diff --git a/content/public/common/resource_response_info.cc b/content/public/common/resource_response_info.cc index 8de8efad..0f01d5df 100644 --- a/content/public/common/resource_response_info.cc +++ b/content/public/common/resource_response_info.cc
@@ -11,8 +11,7 @@ namespace content { ResourceResponseInfo::ResourceResponseInfo() - : has_major_certificate_errors(false), - is_legacy_symantec_cert(false), + : is_legacy_symantec_cert(false), content_length(-1), encoded_data_length(-1), encoded_body_length(-1),
diff --git a/content/public/common/resource_response_info.h b/content/public/common/resource_response_info.h index 3740425..16f5a8a5 100644 --- a/content/public/common/resource_response_info.h +++ b/content/public/common/resource_response_info.h
@@ -51,9 +51,6 @@ // response's mime type. This may be a derived value. std::string charset; - // True if the resource was loaded in spite of certificate errors. - bool has_major_certificate_errors; - // True if the resource was loaded with an otherwise-valid legacy Symantec // certificate which will be distrusted in future. bool is_legacy_symantec_cert;
diff --git a/content/renderer/loader/web_url_loader_impl.cc b/content/renderer/loader/web_url_loader_impl.cc index 7801b9a..32a6c7c 100644 --- a/content/renderer/loader/web_url_loader_impl.cc +++ b/content/renderer/loader/web_url_loader_impl.cc
@@ -1118,7 +1118,9 @@ response->SetMIMEType(WebString::FromUTF8(info.mime_type)); response->SetTextEncodingName(WebString::FromUTF8(info.charset)); response->SetExpectedContentLength(info.content_length); - response->SetHasMajorCertificateErrors(info.has_major_certificate_errors); + response->SetHasMajorCertificateErrors( + net::IsCertStatusError(info.cert_status) && + !net::IsCertStatusMinorError(info.cert_status)); response->SetIsLegacySymantecCert(info.is_legacy_symantec_cert); response->SetCertValidityStart(info.cert_validity_start); response->SetAppCacheID(info.appcache_id);
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index c1b60819..fa85893 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -1452,6 +1452,7 @@ "../browser/web_contents/web_contents_view_mac_unittest.mm", "../browser/web_contents/web_drag_dest_mac_unittest.mm", "../browser/web_contents/web_drag_source_mac_unittest.mm", + "../browser/webauth/cbor/cbor_reader_unittest.cc", "../browser/webauth/cbor/cbor_values_unittest.cc", "../browser/webauth/cbor/cbor_writer_unittest.cc", "../browser/websockets/websocket_manager_unittest.cc",
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor0 b/content/test/data/fuzzer_corpus/cbor_data/cbor0 new file mode 100644 index 0000000..f76dd23 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor0 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor1 b/content/test/data/fuzzer_corpus/cbor_data/cbor1 new file mode 100644 index 0000000..6b2aaa76 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor1
@@ -0,0 +1 @@ + \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor10 b/content/test/data/fuzzer_corpus/cbor_data/cbor10 new file mode 100644 index 0000000..fed1e13 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor10
@@ -0,0 +1 @@ +ÿÿÿÿÿÿÿÿ \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor11 b/content/test/data/fuzzer_corpus/cbor_data/cbor11 new file mode 100644 index 0000000..00a3f0c --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor11 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor12 b/content/test/data/fuzzer_corpus/cbor_data/cbor12 new file mode 100644 index 0000000..077457a --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor12
@@ -0,0 +1 @@ +;ÿÿÿÿÿÿÿÿ \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor13 b/content/test/data/fuzzer_corpus/cbor_data/cbor13 new file mode 100644 index 0000000..b4818a3 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor13 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor14 b/content/test/data/fuzzer_corpus/cbor_data/cbor14 new file mode 100644 index 0000000..0519ecba --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor14
@@ -0,0 +1 @@ + \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor15 b/content/test/data/fuzzer_corpus/cbor_data/cbor15 new file mode 100644 index 0000000..e8a0f876 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor15
@@ -0,0 +1 @@ +) \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor16 b/content/test/data/fuzzer_corpus/cbor_data/cbor16 new file mode 100644 index 0000000..bfefdad18 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor16
@@ -0,0 +1 @@ +8c \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor17 b/content/test/data/fuzzer_corpus/cbor_data/cbor17 new file mode 100644 index 0000000..5de2555 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor17
@@ -0,0 +1 @@ +9ç \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor18 b/content/test/data/fuzzer_corpus/cbor_data/cbor18 new file mode 100644 index 0000000..e43ae5af --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor18 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor19 b/content/test/data/fuzzer_corpus/cbor_data/cbor19 new file mode 100644 index 0000000..6935671d --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor19 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor2 b/content/test/data/fuzzer_corpus/cbor_data/cbor2 new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor2
@@ -0,0 +1 @@ +
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor20 b/content/test/data/fuzzer_corpus/cbor_data/cbor20 new file mode 100644 index 0000000..ed788bb --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor20 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor21 b/content/test/data/fuzzer_corpus/cbor_data/cbor21 new file mode 100644 index 0000000..fc9ea00 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor21
@@ -0,0 +1 @@ +û?ñ \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor22 b/content/test/data/fuzzer_corpus/cbor_data/cbor22 new file mode 100644 index 0000000..5ac3929d --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor22 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor23 b/content/test/data/fuzzer_corpus/cbor_data/cbor23 new file mode 100644 index 0000000..735f467 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor23
@@ -0,0 +1 @@ +ù{ÿ \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor24 b/content/test/data/fuzzer_corpus/cbor_data/cbor24 new file mode 100644 index 0000000..f36a086e --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor24 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor25 b/content/test/data/fuzzer_corpus/cbor_data/cbor25 new file mode 100644 index 0000000..16f33457 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor25
@@ -0,0 +1 @@ +úÿÿ \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor26 b/content/test/data/fuzzer_corpus/cbor_data/cbor26 new file mode 100644 index 0000000..8e79bf5 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor26 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor27 b/content/test/data/fuzzer_corpus/cbor_data/cbor27 new file mode 100644 index 0000000..8e144ff --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor27 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor28 b/content/test/data/fuzzer_corpus/cbor_data/cbor28 new file mode 100644 index 0000000..16b0d434 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor28 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor29 b/content/test/data/fuzzer_corpus/cbor_data/cbor29 new file mode 100644 index 0000000..e5b8b4d --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor29 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor3 b/content/test/data/fuzzer_corpus/cbor_data/cbor3 new file mode 100644 index 0000000..c96ab3cc --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor3
@@ -0,0 +1 @@ + \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor30 b/content/test/data/fuzzer_corpus/cbor_data/cbor30 new file mode 100644 index 0000000..815adc3 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor30
@@ -0,0 +1 @@ +ûÀffffff \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor31 b/content/test/data/fuzzer_corpus/cbor_data/cbor31 new file mode 100644 index 0000000..2b119eb --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor31 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor32 b/content/test/data/fuzzer_corpus/cbor_data/cbor32 new file mode 100644 index 0000000..d73f58e3 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor32 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor33 b/content/test/data/fuzzer_corpus/cbor_data/cbor33 new file mode 100644 index 0000000..7c2eb64 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor33 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor34 b/content/test/data/fuzzer_corpus/cbor_data/cbor34 new file mode 100644 index 0000000..82965f3 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor34 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor35 b/content/test/data/fuzzer_corpus/cbor_data/cbor35 new file mode 100644 index 0000000..2a9ccaec --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor35 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor36 b/content/test/data/fuzzer_corpus/cbor_data/cbor36 new file mode 100644 index 0000000..a9e0eeb --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor36 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor37 b/content/test/data/fuzzer_corpus/cbor_data/cbor37 new file mode 100644 index 0000000..c90d682d --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor37 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor38 b/content/test/data/fuzzer_corpus/cbor_data/cbor38 new file mode 100644 index 0000000..802efe1 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor38 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor39 b/content/test/data/fuzzer_corpus/cbor_data/cbor39 new file mode 100644 index 0000000..7a1f61c --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor39 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor4 b/content/test/data/fuzzer_corpus/cbor_data/cbor4 new file mode 100644 index 0000000..a1910b3f6 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor4
@@ -0,0 +1 @@ + \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor40 b/content/test/data/fuzzer_corpus/cbor_data/cbor40 new file mode 100644 index 0000000..3a6e607 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor40
@@ -0,0 +1 @@ +ô \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor41 b/content/test/data/fuzzer_corpus/cbor_data/cbor41 new file mode 100644 index 0000000..bb7d13c --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor41
@@ -0,0 +1 @@ +õ \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor42 b/content/test/data/fuzzer_corpus/cbor_data/cbor42 new file mode 100644 index 0000000..f7a8cad --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor42
@@ -0,0 +1 @@ +ö \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor43 b/content/test/data/fuzzer_corpus/cbor_data/cbor43 new file mode 100644 index 0000000..009080e8 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor43
@@ -0,0 +1 @@ +÷ \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor44 b/content/test/data/fuzzer_corpus/cbor_data/cbor44 new file mode 100644 index 0000000..04f7b5b --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor44
@@ -0,0 +1 @@ +ð \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor45 b/content/test/data/fuzzer_corpus/cbor_data/cbor45 new file mode 100644 index 0000000..33c4e29 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor45
@@ -0,0 +1 @@ +ø \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor46 b/content/test/data/fuzzer_corpus/cbor_data/cbor46 new file mode 100644 index 0000000..a61a462 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor46
@@ -0,0 +1 @@ +øÿ \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor47 b/content/test/data/fuzzer_corpus/cbor_data/cbor47 new file mode 100644 index 0000000..d2fbff8 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor47
@@ -0,0 +1 @@ +Àt2013-03-21T20:04:00Z \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor48 b/content/test/data/fuzzer_corpus/cbor_data/cbor48 new file mode 100644 index 0000000..4ecf9468 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor48
@@ -0,0 +1 @@ +ÁQKg° \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor49 b/content/test/data/fuzzer_corpus/cbor_data/cbor49 new file mode 100644 index 0000000..c5c79cf --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor49 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor5 b/content/test/data/fuzzer_corpus/cbor_data/cbor5 new file mode 100644 index 0000000..d4e634a --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor5
@@ -0,0 +1 @@ + \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor50 b/content/test/data/fuzzer_corpus/cbor_data/cbor50 new file mode 100644 index 0000000..7d0120d --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor50
@@ -0,0 +1 @@ +×D \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor51 b/content/test/data/fuzzer_corpus/cbor_data/cbor51 new file mode 100644 index 0000000..2784fd8 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor51
@@ -0,0 +1 @@ +ØEdIETF \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor52 b/content/test/data/fuzzer_corpus/cbor_data/cbor52 new file mode 100644 index 0000000..b7d2601a --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor52
@@ -0,0 +1 @@ +Ø vhttp://www.example.com \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor53 b/content/test/data/fuzzer_corpus/cbor_data/cbor53 new file mode 100644 index 0000000..b516b2c --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor53
@@ -0,0 +1 @@ +@ \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor54 b/content/test/data/fuzzer_corpus/cbor_data/cbor54 new file mode 100644 index 0000000..352e89a --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor54
@@ -0,0 +1 @@ +D \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor55 b/content/test/data/fuzzer_corpus/cbor_data/cbor55 new file mode 100644 index 0000000..64845fb7 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor55
@@ -0,0 +1 @@ +` \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor56 b/content/test/data/fuzzer_corpus/cbor_data/cbor56 new file mode 100644 index 0000000..7ec9a4b --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor56
@@ -0,0 +1 @@ +aa \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor57 b/content/test/data/fuzzer_corpus/cbor_data/cbor57 new file mode 100644 index 0000000..743669b --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor57
@@ -0,0 +1 @@ +dIETF \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor58 b/content/test/data/fuzzer_corpus/cbor_data/cbor58 new file mode 100644 index 0000000..cce6615 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor58
@@ -0,0 +1 @@ +b"\ \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor59 b/content/test/data/fuzzer_corpus/cbor_data/cbor59 new file mode 100644 index 0000000..9055a92 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor59
@@ -0,0 +1 @@ +bü \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor6 b/content/test/data/fuzzer_corpus/cbor_data/cbor6 new file mode 100644 index 0000000..3b5c907 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor6
@@ -0,0 +1 @@ +d \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor60 b/content/test/data/fuzzer_corpus/cbor_data/cbor60 new file mode 100644 index 0000000..0868c9a --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor60
@@ -0,0 +1 @@ +c水 \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor61 b/content/test/data/fuzzer_corpus/cbor_data/cbor61 new file mode 100644 index 0000000..8ea48a0 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor61
@@ -0,0 +1 @@ +d𐅑 \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor62 b/content/test/data/fuzzer_corpus/cbor_data/cbor62 new file mode 100644 index 0000000..5416677 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor62
@@ -0,0 +1 @@ + \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor63 b/content/test/data/fuzzer_corpus/cbor_data/cbor63 new file mode 100644 index 0000000..4cdf6ce --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor63
@@ -0,0 +1 @@ + \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor64 b/content/test/data/fuzzer_corpus/cbor_data/cbor64 new file mode 100644 index 0000000..80fd3949 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor64
@@ -0,0 +1 @@ + \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor65 b/content/test/data/fuzzer_corpus/cbor_data/cbor65 new file mode 100644 index 0000000..b449584 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor65 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor66 b/content/test/data/fuzzer_corpus/cbor_data/cbor66 new file mode 100644 index 0000000..eea1bf0 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor66
@@ -0,0 +1 @@ + \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor67 b/content/test/data/fuzzer_corpus/cbor_data/cbor67 new file mode 100644 index 0000000..abc169a8 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor67
@@ -0,0 +1 @@ +¢ \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor68 b/content/test/data/fuzzer_corpus/cbor_data/cbor68 new file mode 100644 index 0000000..81e38473 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor68
@@ -0,0 +1 @@ +¢aaab \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor69 b/content/test/data/fuzzer_corpus/cbor_data/cbor69 new file mode 100644 index 0000000..9b1d08c --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor69
@@ -0,0 +1 @@ +aa¡abac \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor7 b/content/test/data/fuzzer_corpus/cbor_data/cbor7 new file mode 100644 index 0000000..fda74fd --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor7
@@ -0,0 +1 @@ +è \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor70 b/content/test/data/fuzzer_corpus/cbor_data/cbor70 new file mode 100644 index 0000000..05bb07fc --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor70
@@ -0,0 +1 @@ +¥aaaAabaBacaCadaDaeaE \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor71 b/content/test/data/fuzzer_corpus/cbor_data/cbor71 new file mode 100644 index 0000000..2681a857 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor71
@@ -0,0 +1 @@ +_BCÿ \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor72 b/content/test/data/fuzzer_corpus/cbor_data/cbor72 new file mode 100644 index 0000000..d0daf28 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor72
@@ -0,0 +1 @@ +estreadmingÿ \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor73 b/content/test/data/fuzzer_corpus/cbor_data/cbor73 new file mode 100644 index 0000000..422ce24 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor73
@@ -0,0 +1 @@ +ÿ \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor74 b/content/test/data/fuzzer_corpus/cbor_data/cbor74 new file mode 100644 index 0000000..f67cd57 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor74
@@ -0,0 +1 @@ +ÿÿ \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor75 b/content/test/data/fuzzer_corpus/cbor_data/cbor75 new file mode 100644 index 0000000..78343ed --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor75
@@ -0,0 +1 @@ +ÿ \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor76 b/content/test/data/fuzzer_corpus/cbor_data/cbor76 new file mode 100644 index 0000000..a0f1ea2 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor76
@@ -0,0 +1 @@ +ÿ \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor77 b/content/test/data/fuzzer_corpus/cbor_data/cbor77 new file mode 100644 index 0000000..dbbf8e5 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor77
@@ -0,0 +1 @@ +ÿ \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor78 b/content/test/data/fuzzer_corpus/cbor_data/cbor78 new file mode 100644 index 0000000..81c9910 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor78 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor79 b/content/test/data/fuzzer_corpus/cbor_data/cbor79 new file mode 100644 index 0000000..018602c --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor79
@@ -0,0 +1 @@ +¿aaabÿÿ \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor8 b/content/test/data/fuzzer_corpus/cbor_data/cbor8 new file mode 100644 index 0000000..91628195 --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor8 Binary files differ
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor80 b/content/test/data/fuzzer_corpus/cbor_data/cbor80 new file mode 100644 index 0000000..151c13e --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor80
@@ -0,0 +1 @@ +aa¿abacÿ \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor81 b/content/test/data/fuzzer_corpus/cbor_data/cbor81 new file mode 100644 index 0000000..c0f4fd5e --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor81
@@ -0,0 +1 @@ +¿cFunõcAmt!ÿ \ No newline at end of file
diff --git a/content/test/data/fuzzer_corpus/cbor_data/cbor9 b/content/test/data/fuzzer_corpus/cbor_data/cbor9 new file mode 100644 index 0000000..4cdcd7c --- /dev/null +++ b/content/test/data/fuzzer_corpus/cbor_data/cbor9 Binary files differ
diff --git a/content/test/fuzzer/BUILD.gn b/content/test/fuzzer/BUILD.gn index 3907fe2..3df1822 100644 --- a/content/test/fuzzer/BUILD.gn +++ b/content/test/fuzzer/BUILD.gn
@@ -99,3 +99,15 @@ ] testonly = true } + +fuzzer_test("cbor_reader_fuzzer") { + sources = [ + "cbor_reader_fuzzer.cc", + ] + deps = [ + ":fuzzer_support", + "//content/browser:for_content_tests", + ] + seed_corpus = "../data/fuzzer_corpus/cbor_data/" + libfuzzer_options = [ "max_len=65535" ] +}
diff --git a/content/test/fuzzer/cbor_reader_fuzzer.cc b/content/test/fuzzer/cbor_reader_fuzzer.cc new file mode 100644 index 0000000..f457236 --- /dev/null +++ b/content/test/fuzzer/cbor_reader_fuzzer.cc
@@ -0,0 +1,32 @@ +// Copyright 2017 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 <stdint.h> +#include <algorithm> + +#include "content/browser/webauth/cbor/cbor_reader.h" // nogncheck +#include "content/browser/webauth/cbor/cbor_writer.h" // nogncheck +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + std::vector<uint8_t> input(data, data + size); + base::Optional<CBORValue> cbor = CBORReader::Read(input); + + if (cbor.has_value()) { + base::Optional<std::vector<uint8_t>> serialized_cbor = + CBORWriter::Write(cbor.value()); + CHECK(serialized_cbor.has_value()); + if (serialized_cbor.has_value()) { + CHECK(serialized_cbor.value().size() == input.size()); + CHECK(memcmp(serialized_cbor.value().data(), input.data(), + input.size()) == 0); + } + } + return 0; +} + +} // namespace content
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json index 179ee65..36acacc 100644 --- a/gpu/config/gpu_driver_bug_list.json +++ b/gpu/config/gpu_driver_bug_list.json
@@ -2747,6 +2747,18 @@ "features": [ "depth_stencil_renderbuffer_resize_emulation" ] + }, + { + "id": 251, + "description": "Vivante GPUs shader cache breaks rendering", + "cr_bugs": [780498], + "os": { + "type": "android" + }, + "gl_extensions": ".*GL_VIV_shader_binary.*", + "features": [ + "disable_program_cache" + ] } ] }
diff --git a/ios/chrome/browser/feature_engagement/BUILD.gn b/ios/chrome/browser/feature_engagement/BUILD.gn index f2c59f8..74abe1f 100644 --- a/ios/chrome/browser/feature_engagement/BUILD.gn +++ b/ios/chrome/browser/feature_engagement/BUILD.gn
@@ -37,6 +37,7 @@ "//base", "//base/test:test_support", "//components/feature_engagement/public", + "//components/feature_engagement/test:test_support", "//ios/chrome/browser/browser_state", "//ios/chrome/test/app:test_support", "//ios/chrome/test/earl_grey:test_support",
diff --git a/ios/chrome/browser/feature_engagement/feature_engagement_egtest.mm b/ios/chrome/browser/feature_engagement/feature_engagement_egtest.mm index d85f578..1f1e8f4 100644 --- a/ios/chrome/browser/feature_engagement/feature_engagement_egtest.mm +++ b/ios/chrome/browser/feature_engagement/feature_engagement_egtest.mm
@@ -10,9 +10,9 @@ #include "components/feature_engagement/public/event_constants.h" #include "components/feature_engagement/public/feature_constants.h" #include "components/feature_engagement/public/tracker.h" +#include "components/feature_engagement/test/test_tracker.h" #include "ios/chrome/browser/browser_state/chrome_browser_state.h" #include "ios/chrome/browser/feature_engagement/tracker_factory.h" -#include "ios/chrome/browser/feature_engagement/tracker_factory_util.h" #import "ios/chrome/test/app/chrome_test_util.h" #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h" #import "ios/chrome/test/earl_grey/chrome_matchers.h" @@ -29,14 +29,17 @@ // Help feature to be shown. const int kMinChromeOpensRequired = 5; -// The timeout for the load of the feature engagement tracker. -const NSTimeInterval kWaitForTrackerLoadTimeout = 10.0; - // Matcher for the Reading List Text Badge. id<GREYMatcher> ReadingListTextBadge() { return grey_accessibilityID(@"kReadingListTextBadgeAccessibilityIdentifier"); } +// Create a test FeatureEngagementTracker. +std::unique_ptr<KeyedService> CreateTestFeatureEngagementTracker( + web::BrowserState* context) { + return feature_engagement::CreateTestTracker(); +} + // Simulate a Chrome Opened event for the Feature Engagement Tracker. void SimulateChromeOpenedEvent() { feature_engagement::TrackerFactory::GetForBrowserState( @@ -50,16 +53,7 @@ chrome_test_util::GetOriginalBrowserState(); feature_engagement::TrackerFactory::GetInstance()->SetTestingFactory( - browserState, feature_engagement::CreateFeatureEngagementTracker); - - feature_engagement::Tracker* tracker = - feature_engagement::TrackerFactory::GetForBrowserState(browserState); - GREYAssert( - testing::WaitUntilConditionOrTimeout(kWaitForTrackerLoadTimeout, - ^{ - return tracker->IsInitialized(); - }), - @"Engagement Tracker did not load before timeout."); + browserState, CreateTestFeatureEngagementTracker); } // Enables the Badged Reading List help to be triggered for |feature_list|.
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/device-mode/default-background-color-cleared-expected.html b/third_party/WebKit/LayoutTests/http/tests/devtools/device-mode/default-background-color-cleared-expected.html deleted file mode 100644 index b38f0f9..0000000 --- a/third_party/WebKit/LayoutTests/http/tests/devtools/device-mode/default-background-color-cleared-expected.html +++ /dev/null
@@ -1,16 +0,0 @@ -<html> -<head> - -<style> -html { - margin: 0; -} -</style> - -</head> -<body> -<p> -Tests that resetting Emulation.setDefaultBackgroundColorOverride clears the background color override. -</p> -</body> -</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/device-mode/default-background-color-cleared.html b/third_party/WebKit/LayoutTests/http/tests/devtools/device-mode/default-background-color-cleared.html deleted file mode 100644 index 83c61c76..0000000 --- a/third_party/WebKit/LayoutTests/http/tests/devtools/device-mode/default-background-color-cleared.html +++ /dev/null
@@ -1,42 +0,0 @@ -<html> -<head> - -<style> -html { - margin: 0; -} -</style> - -<script src="../../inspector/inspector-test.js"></script> - -<script> -function test() { - TestRunner.evaluateInPage('testRunner.dumpAsTextWithPixelResults();', () => { - TestRunner.EmulationAgent.invoke_setDefaultBackgroundColorOverride({color: {r: 0x12, g: 0x34, b: 0x56, a: 1.0}}) - .then(backgroundColorSet); - }); - - function backgroundColorSet(messageObject) { - if (messageObject[Protocol.Error]) - InspectorTest.log('FAIL: unexpected exception: ' + messageObject[Protocol.Error]); - TestRunner.EmulationAgent.invoke_setDefaultBackgroundColorOverride({}).then(backgroundColorCleared); - } - - function backgroundColorCleared(messageObject) { - if (messageObject[Protocol.Error]) - InspectorTest.log('FAIL: unexpected exception: ' + messageObject[Protocol.Error]); - - // Complete the test without closing the inspector, so that the override stays active for the picture. - TestRunner.flushResults(); - TestRunner.evaluateInPage('testRunner.notifyDone();'); - } -} -</script> - -</head> -<body onload="runTest()"> -<p> -Tests that resetting Emulation.setDefaultBackgroundColorOverride clears the background color override. -</p> -</body> -</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/device-mode/default-background-color-expected.html b/third_party/WebKit/LayoutTests/http/tests/devtools/device-mode/default-background-color-expected.html deleted file mode 100644 index 378f6f99..0000000 --- a/third_party/WebKit/LayoutTests/http/tests/devtools/device-mode/default-background-color-expected.html +++ /dev/null
@@ -1,17 +0,0 @@ -<html> -<head> - -<style> -html { - margin: 0; - background-color: #123456; -} -</style> - -</head> -<body> -<p> -Tests that Emulation.setDefaultBackgroundColorOverride changes the background color of a page that does not specify one. -</p> -</body> -</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/device-mode/default-background-color.html b/third_party/WebKit/LayoutTests/http/tests/devtools/device-mode/default-background-color.html deleted file mode 100644 index 49b8349..0000000 --- a/third_party/WebKit/LayoutTests/http/tests/devtools/device-mode/default-background-color.html +++ /dev/null
@@ -1,36 +0,0 @@ -<html> -<head> - -<style> -html { - margin: 0; -} -</style> - -<script src="../../inspector/inspector-test.js"></script> - -<script> -function test() { - TestRunner.evaluateInPage('testRunner.dumpAsTextWithPixelResults();', () => { - TestRunner.EmulationAgent.invoke_setDefaultBackgroundColorOverride({color: {r: 0x12, g: 0x34, b: 0x56, a: 1.0}}) - .then(backgroundColorSet); - }); - - function backgroundColorSet(messageObject) { - if (messageObject[Protocol.Error]) - InspectorTest.log('FAIL: unexpected exception: ' + messageObject[Protocol.Error]); - - // Complete the test without closing the inspector, so that the override stays active for the picture. - TestRunner.flushResults(); - TestRunner.evaluateInPage('testRunner.notifyDone();'); - } -} -</script> - -</head> -<body onload="runTest()"> -<p> -Tests that Emulation.setDefaultBackgroundColorOverride changes the background color of a page that does not specify one. -</p> -</body> -</html>
diff --git a/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js b/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js index c161f3c..9dd3c97 100644 --- a/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js +++ b/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js
@@ -36,8 +36,8 @@ function bluetooth_test(func, name, properties) { Promise.resolve() .then(() => { - // Chromium Testing API - if (window.chrome !== undefined) return loadChromiumResources(); + // Load Chromium specific resources when Mojo bindings are detected. + if (Mojo) return loadChromiumResources(); }) .then(() => promise_test(func, name, properties)); }
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index d2401d3..e6f4983e 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -81669,6 +81669,15 @@ </summary> </histogram> +<histogram name="SiteIsolation.IsolateOrigins.Size" units="origins"> + <owner>alexmos@chromium.org</owner> + <summary> + The number of currently enabled isolated origins. This includes origins + specified via the --isolate-origins command-line flag as well as those + configured via enterprise policy. Recorded on browser startup. + </summary> +</histogram> + <histogram name="SiteIsolation.OutOfProcessIframes"> <owner>nasko@chromium.org</owner> <summary>
diff --git a/ui/app_list/views/app_list_view_unittest.cc b/ui/app_list/views/app_list_view_unittest.cc index 5511614..a53a169 100644 --- a/ui/app_list/views/app_list_view_unittest.cc +++ b/ui/app_list/views/app_list_view_unittest.cc
@@ -538,7 +538,7 @@ // Type something in search box to transition to HALF state and populate // fake search results. - search_box_view()->search_box()->InsertText(base::UTF8ToUTF16("test")); + search_box_view()->search_box()->InsertText(base::ASCIIToUTF16("test")); EXPECT_EQ(app_list_view()->app_list_state(), AppListViewState::HALF); constexpr int kTileResults = 3; constexpr int kListResults = 2; @@ -695,7 +695,7 @@ // Type something in search box to transition to HALF state and populate // fake search results. - search_box_view()->search_box()->InsertText(base::UTF8ToUTF16("test")); + search_box_view()->search_box()->InsertText(base::ASCIIToUTF16("test")); EXPECT_EQ(app_list_view()->app_list_state(), AppListViewState::HALF); constexpr int kTileResults = 3; constexpr int kListResults = 2; @@ -778,7 +778,7 @@ // Type something in search box to transition to HALF state and populate // fake search results. - search_box_view()->search_box()->InsertText(base::UTF8ToUTF16("test")); + search_box_view()->search_box()->InsertText(base::ASCIIToUTF16("test")); const int kTileResults = 3; const int kListResults = 2; SetUpSearchResults(kTileResults, kListResults, true); @@ -865,7 +865,7 @@ // SearchBoxTextfield. TEST_F(AppListViewFocusTest, SearchBoxSelectionCoversWholeQueryOnFocus) { Show(); - search_box_view()->search_box()->InsertText(base::UTF8ToUTF16("test")); + search_box_view()->search_box()->InsertText(base::ASCIIToUTF16("test")); EXPECT_EQ(app_list_view()->app_list_state(), AppListViewState::HALF); constexpr int kListResults = 1; SetUpSearchResults(0, kListResults, false); @@ -913,7 +913,7 @@ // not focused. TEST_F(AppListViewFocusTest, CtrlASelectsAllTextInSearchbox) { Show(); - search_box_view()->search_box()->InsertText(base::UTF8ToUTF16("test")); + search_box_view()->search_box()->InsertText(base::ASCIIToUTF16("test")); EXPECT_EQ(app_list_view()->app_list_state(), AppListViewState::HALF); constexpr int kTileResults = 3; constexpr int kListResults = 2; @@ -944,14 +944,14 @@ search_box_view()->search_box()->GetSelectedRange()); } -// Tests that the first search result's view is always selected after search -// results are updated, but the focus is always on search box. +// Tests that the first search result's view is selected after search results +// are updated when the focus is on search box. TEST_F(AppListViewFocusTest, FirstResultSelectedAfterSearchResultsUpdated) { Show(); // Type something in search box to transition to HALF state and populate // fake list results. - search_box_view()->search_box()->InsertText(base::UTF8ToUTF16("test")); + search_box_view()->search_box()->InsertText(base::ASCIIToUTF16("test")); const int kListResults = 2; SetUpSearchResults(0, kListResults, false); const views::View* results_container = @@ -988,6 +988,56 @@ contents_view()->search_results_page_view()->first_result_view()); } +// Tests that the first search result's view is not selected after search +// results are updated when the focus is on one of the search results (This +// happens when the user quickly hits Tab key after typing query and before +// search results are updated for the new query). +TEST_F(AppListViewFocusTest, FirstResultNotSelectedAfterQuicklyHittingTab) { + Show(); + + // Type something in search box to transition to HALF state and populate + // fake list results. + search_box_view()->search_box()->InsertText(base::ASCIIToUTF16("test1")); + const int kListResults = 2; + SetUpSearchResults(0, kListResults, false); + const views::View* results_container = + contents_view() + ->search_result_list_view_for_test() + ->results_container_for_test(); + views::View* first_result_view = + contents_view()->search_results_page_view()->first_result_view(); + EXPECT_EQ(search_box_view()->search_box(), focused_view()); + EXPECT_EQ(results_container->child_at(0), first_result_view); + EXPECT_TRUE(static_cast<SearchResultView*>(first_result_view)->selected()); + + // Type something else. + search_box_view()->search_box()->InsertText(base::ASCIIToUTF16("test2")); + EXPECT_EQ(search_box_view()->search_box(), focused_view()); + + // Simulate hitting Tab key to move focus to the close button, then to the + // first result before search results are updated. + SimulateKeyPress(ui::VKEY_TAB, false); + EXPECT_EQ(search_box_view()->close_button(), focused_view()); + SimulateKeyPress(ui::VKEY_TAB, false); + EXPECT_EQ(results_container->child_at(0), focused_view()); + EXPECT_TRUE(static_cast<SearchResultView*>(first_result_view)->selected()); + + // Update search results, both list and tile results are populated. + const int kTileResults = 3; + SetUpSearchResults(kTileResults, kListResults, false); + const std::vector<SearchResultTileItemView*>& tile_views = + contents_view() + ->search_result_tile_item_list_view_for_test() + ->tile_views_for_test(); + first_result_view = + contents_view()->search_results_page_view()->first_result_view(); + EXPECT_EQ(results_container->child_at(0), focused_view()); + EXPECT_EQ(tile_views[0], first_result_view); + EXPECT_FALSE( + static_cast<SearchResultTileItemView*>(first_result_view)->selected()); + EXPECT_TRUE(static_cast<SearchResultView*>(focused_view())->selected()); +} + // Tests hitting Enter key when focus is on search box. // There are two behaviors: // 1. Activate the search box when it is inactive.
diff --git a/ui/app_list/views/search_result_answer_card_view.cc b/ui/app_list/views/search_result_answer_card_view.cc index f4c7958d..bd38c32 100644 --- a/ui/app_list/views/search_result_answer_card_view.cc +++ b/ui/app_list/views/search_result_answer_card_view.cc
@@ -223,11 +223,14 @@ : nullptr; } -views::View* SearchResultAnswerCardView::SetFirstResultSelected(bool selected) { +views::View* SearchResultAnswerCardView::GetFirstResultView() { + return num_results() <= 0 ? nullptr : search_answer_container_view_; +} + +void SearchResultAnswerCardView::SetFirstResultSelected(bool selected) { if (num_results() <= 0) - return nullptr; + return; search_answer_container_view_->SetSelected(selected); - return search_answer_container_view_; } views::View* SearchResultAnswerCardView::GetSearchAnswerContainerViewForTest()
diff --git a/ui/app_list/views/search_result_answer_card_view.h b/ui/app_list/views/search_result_answer_card_view.h index 4efca808..cf6b883 100644 --- a/ui/app_list/views/search_result_answer_card_view.h +++ b/ui/app_list/views/search_result_answer_card_view.h
@@ -30,7 +30,8 @@ void UpdateSelectedIndex(int old_selected, int new_selected) override; bool OnKeyPressed(const ui::KeyEvent& event) override; views::View* GetSelectedView() const override; - views::View* SetFirstResultSelected(bool selected) override; + views::View* GetFirstResultView() override; + void SetFirstResultSelected(bool selected) override; views::View* GetSearchAnswerContainerViewForTest() const;
diff --git a/ui/app_list/views/search_result_container_view.h b/ui/app_list/views/search_result_container_view.h index e1eedf68..e5bb4258 100644 --- a/ui/app_list/views/search_result_container_view.h +++ b/ui/app_list/views/search_result_container_view.h
@@ -89,9 +89,12 @@ // Returns selected view in this container view. virtual views::View* GetSelectedView() const = 0; - // Sets the first result in this container view selected/unselected. Returns - // the result's view. - virtual views::View* SetFirstResultSelected(bool selected) = 0; + // Returns the first result in the container view. Returns NULL if it does not + // exist. + virtual views::View* GetFirstResultView() = 0; + + // Sets the first result in this container view selected/unselected. + virtual void SetFirstResultSelected(bool selected) = 0; private: // Schedules an Update call using |update_factory_|. Do nothing if there is a
diff --git a/ui/app_list/views/search_result_list_view.cc b/ui/app_list/views/search_result_list_view.cc index 3c20d10c..598a290 100644 --- a/ui/app_list/views/search_result_list_view.cc +++ b/ui/app_list/views/search_result_list_view.cc
@@ -171,14 +171,18 @@ : nullptr; } -views::View* SearchResultListView::SetFirstResultSelected(bool selected) { +views::View* SearchResultListView::GetFirstResultView() { + DCHECK(results_container_->has_children()); + return num_results() <= 0 ? nullptr : results_container_->child_at(0); +} + +void SearchResultListView::SetFirstResultSelected(bool selected) { DCHECK(results_container_->has_children()); if (num_results() <= 0) - return nullptr; + return; SearchResultView* search_result_view = static_cast<SearchResultView*>(results_container_->child_at(0)); search_result_view->SetSelected(selected); - return search_result_view; } int SearchResultListView::DoUpdate() {
diff --git a/ui/app_list/views/search_result_list_view.h b/ui/app_list/views/search_result_list_view.h index 2211b0c..378ee38 100644 --- a/ui/app_list/views/search_result_list_view.h +++ b/ui/app_list/views/search_result_list_view.h
@@ -60,7 +60,8 @@ void NotifyFirstResultYIndex(int y_index) override; int GetYSize() override; views::View* GetSelectedView() const override; - views::View* SetFirstResultSelected(bool selected) override; + views::View* GetFirstResultView() override; + void SetFirstResultSelected(bool selected) override; views::View* results_container_for_test() const { return results_container_; }
diff --git a/ui/app_list/views/search_result_page_view.cc b/ui/app_list/views/search_result_page_view.cc index cf0074a..2ef7192 100644 --- a/ui/app_list/views/search_result_page_view.cc +++ b/ui/app_list/views/search_result_page_view.cc
@@ -367,9 +367,20 @@ SearchResultContainerView* old_first_container_view = result_container_views_[0]; ReorderSearchResultContainers(); - old_first_container_view->SetFirstResultSelected(false); - first_result_view_ = - result_container_views_[0]->SetFirstResultSelected(true); + + views::View* focused_view = GetFocusManager()->GetFocusedView(); + if (first_result_view_ != focused_view) { + // If the old first result is focused, do not clear the selection. (This + // happens when the user moved the focus before search results are + // updated.) + old_first_container_view->SetFirstResultSelected(false); + } + first_result_view_ = result_container_views_[0]->GetFirstResultView(); + if (!Contains(focused_view)) { + // If one of the search result is focused, do not set the first result + // selected. + result_container_views_[0]->SetFirstResultSelected(true); + } return; }
diff --git a/ui/app_list/views/search_result_tile_item_list_view.cc b/ui/app_list/views/search_result_tile_item_list_view.cc index b5bb56a..e770df4 100644 --- a/ui/app_list/views/search_result_tile_item_list_view.cc +++ b/ui/app_list/views/search_result_tile_item_list_view.cc
@@ -102,13 +102,16 @@ : nullptr; } -views::View* SearchResultTileItemListView::SetFirstResultSelected( - bool selected) { +views::View* SearchResultTileItemListView::GetFirstResultView() { + DCHECK(!tile_views_.empty()); + return num_results() <= 0 ? nullptr : tile_views_[0]; +} + +void SearchResultTileItemListView::SetFirstResultSelected(bool selected) { DCHECK(!tile_views_.empty()); if (num_results() <= 0) - return nullptr; + return; tile_views_[0]->SetSelected(selected); - return tile_views_[0]; } int SearchResultTileItemListView::DoUpdate() {
diff --git a/ui/app_list/views/search_result_tile_item_list_view.h b/ui/app_list/views/search_result_tile_item_list_view.h index c58014a7e..87ef0f0 100644 --- a/ui/app_list/views/search_result_tile_item_list_view.h +++ b/ui/app_list/views/search_result_tile_item_list_view.h
@@ -36,7 +36,8 @@ void NotifyFirstResultYIndex(int y_index) override; int GetYSize() override; views::View* GetSelectedView() const override; - views::View* SetFirstResultSelected(bool selected) override; + views::View* GetFirstResultView() override; + void SetFirstResultSelected(bool selected) override; // Overridden from views::View: bool OnKeyPressed(const ui::KeyEvent& event) override;
diff --git a/ui/app_list/views/suggestions_container_view.cc b/ui/app_list/views/suggestions_container_view.cc index 552b5f68..d624752 100644 --- a/ui/app_list/views/suggestions_container_view.cc +++ b/ui/app_list/views/suggestions_container_view.cc
@@ -105,10 +105,12 @@ : nullptr; } -views::View* SuggestionsContainerView::SetFirstResultSelected(bool selected) { +views::View* SuggestionsContainerView::GetFirstResultView() { return nullptr; } +void SuggestionsContainerView::SetFirstResultSelected(bool selected) {} + void SuggestionsContainerView::CreateAppsGrid(int apps_num) { DCHECK(search_result_tile_views_.empty()); views::GridLayout* tiles_layout_manager =
diff --git a/ui/app_list/views/suggestions_container_view.h b/ui/app_list/views/suggestions_container_view.h index b7af549..00afaf61 100644 --- a/ui/app_list/views/suggestions_container_view.h +++ b/ui/app_list/views/suggestions_container_view.h
@@ -40,7 +40,8 @@ void NotifyFirstResultYIndex(int y_index) override; int GetYSize() override; views::View* GetSelectedView() const override; - views::View* SetFirstResultSelected(bool selected) override; + views::View* GetFirstResultView() override; + void SetFirstResultSelected(bool selected) override; private: void CreateAppsGrid(int apps_num);