Commit b9c25717 authored by gcasto@chromium.org's avatar gcasto@chromium.org

Use shadow DOM API to show icon for password generation.

The key icon in this CL is rough draft and will be improved before launch.

BUG=114092
TEST=Ran PasswordGenerationManagerTest

Review URL: https://chromiumcodereview.appspot.com/10449099

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@140091 0039d316-1c4b-4281-b951-d872f2087c98
parent ea5c5f05
......@@ -6,11 +6,14 @@
#include "base/logging.h"
#include "chrome/common/autofill_messages.h"
#include "content/public/renderer/render_view.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFormElement.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebCString.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebRect.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebVector.h"
#include "ui/gfx/rect.h"
......@@ -20,7 +23,9 @@ namespace autofill {
PasswordGenerationManager::PasswordGenerationManager(
content::RenderView* render_view)
: content::RenderViewObserver(render_view),
enabled_(false) {}
enabled_(false) {
render_view->GetWebView()->addTextFieldDecoratorClient(this);
}
PasswordGenerationManager::~PasswordGenerationManager() {}
void PasswordGenerationManager::DidFinishDocumentLoad(WebKit::WebFrame* frame) {
......@@ -29,14 +34,14 @@ void PasswordGenerationManager::DidFinishDocumentLoad(WebKit::WebFrame* frame) {
if (!enabled_)
return;
if (!ShouldAnalyzeFrame(*frame))
if (!ShouldAnalyzeDocument(frame->document()))
return;
WebKit::WebVector<WebKit::WebFormElement> forms;
frame->document().forms(forms);
for (size_t i = 0; i < forms.size(); ++i) {
const WebKit::WebFormElement& web_form = forms[i];
if (!web_form.autoComplete())
if (web_form.isNull() || !web_form.autoComplete())
continue;
// Grab all of the passwords for each form.
......@@ -50,21 +55,26 @@ void PasswordGenerationManager::DidFinishDocumentLoad(WebKit::WebFrame* frame) {
if (input_element && input_element->isPasswordField())
passwords.push_back(*input_element);
}
// For now, just assume that if there are two password fields in the
// form that this is meant for account creation.
// form that this is meant for account creation. Also, we assume that there
// is only one account creation field per URL.
// TODO(gcasto): Determine better heauristics for this.
if (passwords.size() == 2) {
account_creation_elements_ = make_pair(passwords[0], passwords);
break;
passwords_ = passwords;
// Make the decoration visible for this element.
passwords[0].decorationElementFor(this).setAttribute("style",
"display:block");
return;
}
}
}
bool PasswordGenerationManager::ShouldAnalyzeFrame(
const WebKit::WebFrame& frame) const {
bool PasswordGenerationManager::ShouldAnalyzeDocument(
const WebKit::WebDocument& document) const {
// Make sure that this security origin is allowed to use password manager.
// Generating a password that can't be saved is a bad idea.
WebKit::WebSecurityOrigin origin = frame.document().securityOrigin();
WebKit::WebSecurityOrigin origin = document.securityOrigin();
if (!origin.canAccessPasswordManager()) {
DVLOG(1) << "No PasswordManager access";
return false;
......@@ -73,25 +83,44 @@ bool PasswordGenerationManager::ShouldAnalyzeFrame(
return true;
}
void PasswordGenerationManager::FocusedNodeChanged(
const WebKit::WebNode& node) {
WebKit::WebInputElement input_element =
node.toConst<WebKit::WebInputElement>();
if (!input_element.isNull() &&
account_creation_elements_.first == input_element) {
gfx::Rect rect(input_element.boundsInViewportSpace());
webkit::forms::PasswordForm* password_form(
webkit::forms::PasswordFormDomManager::CreatePasswordForm(
input_element.form()));
if (password_form) {
Send(new AutofillHostMsg_ShowPasswordGenerationPopup(routing_id(),
rect,
*password_form));
}
bool PasswordGenerationManager::shouldAddDecorationTo(
const WebKit::WebInputElement& element) {
return element.isPasswordField();
}
bool PasswordGenerationManager::visibleByDefault() {
return false;
}
WebKit::WebCString PasswordGenerationManager::imageNameForNormalState() {
return WebKit::WebCString("generatePassword");
}
WebKit::WebCString PasswordGenerationManager::imageNameForDisabledState() {
return imageNameForNormalState();
}
WebKit::WebCString PasswordGenerationManager::imageNameForReadOnlyState() {
return imageNameForNormalState();
}
void PasswordGenerationManager::handleClick(WebKit::WebInputElement& element) {
gfx::Rect rect(element.decorationElementFor(this).boundsInViewportSpace());
webkit::forms::PasswordForm* password_form(
webkit::forms::PasswordFormDomManager::CreatePasswordForm(
element.form()));
if (password_form) {
Send(new AutofillHostMsg_ShowPasswordGenerationPopup(routing_id(),
rect,
*password_form));
}
}
void PasswordGenerationManager::willDetach(
const WebKit::WebInputElement& element) {
// No implementation
}
bool PasswordGenerationManager::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(PasswordGenerationManager, message)
......@@ -105,9 +134,8 @@ bool PasswordGenerationManager::OnMessageReceived(const IPC::Message& message) {
}
void PasswordGenerationManager::OnPasswordAccepted(const string16& password) {
for (std::vector<WebKit::WebInputElement>::iterator it =
account_creation_elements_.second.begin();
it != account_creation_elements_.second.end(); ++it) {
for (std::vector<WebKit::WebInputElement>::iterator it = passwords_.begin();
it != passwords_.end(); ++it) {
it->setValue(password);
it->setAutofilled(true);
}
......
......@@ -12,23 +12,28 @@
#include "content/public/renderer/render_view_observer.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebTextFieldDecoratorClient.h"
namespace WebKit {
class WebCString;
class WebDocument;
}
namespace autofill {
// This class is responsible for controlling communication for password
// generation between the browser (which shows the popup and generates
// passwords) and WebKit (determines which fields are for account signup and
// fills in the generated passwords). Currently the WebKit part is not
// implemented.
class PasswordGenerationManager : public content::RenderViewObserver {
// passwords) and WebKit (shows the generation icon in the password field).
class PasswordGenerationManager : public content::RenderViewObserver,
public WebKit::WebTextFieldDecoratorClient {
public:
explicit PasswordGenerationManager(content::RenderView* render_view);
virtual ~PasswordGenerationManager();
protected:
// Returns true if this frame is one that we should consider analyzing.
// Returns true if this document is one that we should consider analyzing.
// Virtual so that it can be overriden during testing.
virtual bool ShouldAnalyzeFrame(const WebKit::WebFrame& frame) const;
virtual bool ShouldAnalyzeDocument(const WebKit::WebDocument& document) const;
// RenderViewObserver:
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
......@@ -36,7 +41,16 @@ class PasswordGenerationManager : public content::RenderViewObserver {
private:
// RenderViewObserver:
virtual void DidFinishDocumentLoad(WebKit::WebFrame* frame) OVERRIDE;
virtual void FocusedNodeChanged(const WebKit::WebNode& node) OVERRIDE;
// WebTextFieldDecoratorClient:
virtual bool shouldAddDecorationTo(
const WebKit::WebInputElement& element) OVERRIDE;
virtual bool visibleByDefault() OVERRIDE;
virtual WebKit::WebCString imageNameForNormalState() OVERRIDE;
virtual WebKit::WebCString imageNameForDisabledState() OVERRIDE;
virtual WebKit::WebCString imageNameForReadOnlyState() OVERRIDE;
virtual void handleClick(WebKit::WebInputElement& element) OVERRIDE;
virtual void willDetach(const WebKit::WebInputElement&) OVERRIDE;
// Message handlers.
void OnPasswordAccepted(const string16& password);
......@@ -46,8 +60,7 @@ class PasswordGenerationManager : public content::RenderViewObserver {
// with this renderer.
bool enabled_;
std::pair<WebKit::WebInputElement,
std::vector<WebKit::WebInputElement> > account_creation_elements_;
std::vector<WebKit::WebInputElement> passwords_;
DISALLOW_COPY_AND_ASSIGN(PasswordGenerationManager);
};
......
......@@ -37,7 +37,7 @@ class TestPasswordGenerationManager : public PasswordGenerationManager {
}
protected:
virtual bool ShouldAnalyzeFrame(const WebKit::WebFrame& frame) const
virtual bool ShouldAnalyzeDocument(const WebKit::WebDocument& document) const
OVERRIDE {
return true;
}
......@@ -65,6 +65,18 @@ class PasswordGenerationManagerTest : public ChromeRenderViewTest {
generation_manager_.reset(new TestPasswordGenerationManager(view_));
}
void SimulateClickOnDecoration(WebKit::WebInputElement* input_element) {
WebKit::WebElement decoration =
input_element->decorationElementFor(generation_manager_.get());
decoration.simulateClick();
}
bool DecorationIsVisible(WebKit::WebInputElement* input_element) {
WebKit::WebElement decoration =
input_element->decorationElementFor(generation_manager_.get());
return decoration.hasNonEmptyBoundingBox();
}
protected:
scoped_ptr<TestPasswordGenerationManager> generation_manager_;
......@@ -95,39 +107,36 @@ TEST_F(PasswordGenerationManagerTest, DetectionTest) {
document.getElementById(WebString::fromUTF8("password"));
ASSERT_FALSE(element.isNull());
WebInputElement password_element = element.to<WebInputElement>();
EXPECT_EQ(0u, generation_manager_->messages().size());
EXPECT_FALSE(DecorationIsVisible(&password_element));
LoadHTML(kAccountCreationFormHTML);
// We don't do anything yet because the feature is disabled.
// We don't show the decoration yet because the feature isn't enabled.
document = GetMainFrame()->document();
element = document.getElementById(WebString::fromUTF8("first_password"));
ASSERT_FALSE(element.isNull());
WebInputElement first_password_element = element.to<WebInputElement>();
EXPECT_EQ(0u, generation_manager_->messages().size());
element = document.getElementById(WebString::fromUTF8("second_password"));
ASSERT_FALSE(element.isNull());
WebInputElement second_password_element = element.to<WebInputElement>();
EXPECT_EQ(0u, generation_manager_->messages().size());
SetFocused(first_password_element.to<WebNode>());
EXPECT_EQ(0u, generation_manager_->messages().size());
EXPECT_FALSE(DecorationIsVisible(&first_password_element));
// Pretend like password generation was enabled.
AutofillMsg_PasswordGenerationEnabled msg(0, true);
generation_manager_->OnMessageReceived(msg);
// Now we will send a message once the first password feld is focused.
// Now check that the decoration is visible on the first password field and
// that we send a message after the decoration is clicked.
LoadHTML(kAccountCreationFormHTML);
document = GetMainFrame()->document();
element = document.getElementById(WebString::fromUTF8("first_password"));
ASSERT_FALSE(element.isNull());
first_password_element = element.to<WebInputElement>();
EXPECT_EQ(0u, generation_manager_->messages().size());
element = document.getElementById(WebString::fromUTF8("second_password"));
ASSERT_FALSE(element.isNull());
second_password_element = element.to<WebInputElement>();
EXPECT_EQ(0u, generation_manager_->messages().size());
SetFocused(first_password_element.to<WebNode>());
EXPECT_TRUE(DecorationIsVisible(&first_password_element));
SimulateClickOnDecoration(&first_password_element);
EXPECT_EQ(1u, generation_manager_->messages().size());
EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID,
generation_manager_->messages()[0]->type());
......
......@@ -135,6 +135,7 @@
<include name="IDR_PDF_PROGRESS_BAR_BACKGROUND" file="resources\pdf_progress_background.png" type="BINDATA" />
<include name="IDR_PDF_PAGE_INDICATOR_BACKGROUND" file="resources\pdf_page_indicator_background.png" type="BINDATA" />
<include name="IDR_PDF_PAGE_DROPSHADOW" file="resources\pdf_dropshadow.png" type="BINDATA" />
<include name="IDR_PASSWORD_GENERATION_ICON" file="resources\password_generation.png" type="BINDATA" />
</includes>
</release>
</grit>
......@@ -486,6 +486,7 @@ const DataResource kDataResources[] = {
{ "masterCardCC", IDR_AUTOFILL_CC_MASTERCARD, ui::SCALE_FACTOR_100P },
{ "soloCC", IDR_AUTOFILL_CC_SOLO, ui::SCALE_FACTOR_100P },
{ "visaCC", IDR_AUTOFILL_CC_VISA, ui::SCALE_FACTOR_100P },
{ "generatePassword", IDR_PASSWORD_GENERATION_ICON, ui::SCALE_FACTOR_100P },
};
} // namespace
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment