After a great deal of work, I finally have the whole browser-save-password
code working on all browsers I can find (Chrome, IE, Firefox, Safari).  The
code graciously provided by Saumar Hajjar in this thread does work.  You
do, however, have to understand it in order to use it in your application.
 Given the state of HTML and browser technology, as well as the various
specific browsers, this code is incredibly fragile and sequence dependent.
 It took me many days of debugging and trial-and-error coding to get it all
working.  Now that it does work and I have some degree of understanding, I
thought I would share some of the important points I discovered.  I hope
this can be helpful to others.  I apologize in advance to those of you who
find my observations obvious.

Notice the line that has Window.Location.reload().  That critical line
makes the whole thing work differently than anything you would have
expected.  That line causes the whole page to reload from scratch losing
all state information (including JavaScript variables, GWT instance and
static variables, etc.) _except_ session data. You will notice that his
backend code utilizes session data in order to keep track of the fact that
he'd been there before (the user logged in).  This is the reason the first
thing he has to do is call a backend service - to find out which state he
is in (user logged in or not).  The system must re-load like this
(according to him) in order for all browsers to execute their save-password
operation.  Password saving happens at that reload point.

Interestingly, his note states that the reload call is only necessary for
Chrome.  Unfortunately, that one line dictates most of the architecture.
 In other words, if that line wasn't required, a far simpler approach could
be used.

As has been stated all over the place, the browser will only save the
password if the password stuff is in the original HTML file - not GWT added
controls.  This means that you will likely want to make the login HTML
disappear after they log-in, and re-appear when they log out.  I did that
by wrapping the whole HTML body in a div like this:

<div class="login-page" id="login-page" style="display: none;"> ....  </div>

I could then control its visibility with:

    private void hideLoginPage() {
        RootPanel.get("login-page").setVisible(false);
    }

    private void showLoginPage() {
        RootPanel.get("login-page").setVisible(true);
    }

    private void reloadLoginPage() {
        Window.Location.reload();
    }

In my case, I set style="display: none;" on the div to prevent it from
showing at first.  Something I needed.  If you want to show it right away,
just remove that.  Importantly, I discovered that showLoginPage() can only
be called _after_ all of the calls to the various wrap() methods have been
called.

Another thing I noticed, my call to reloadLoginPage() (in order to get the
browser to save the password) only worked in response to a backend call -
as part of the response handler as he does below.  During testing, if I
eliminated the backend call, the reloadLoginPage() would not cause the
browser to save the password.

This is all I can think of.  I hope it is helpful.

Blake McBride





On Mon, Jun 9, 2014 at 12:39 AM, Saumar Hajjar <[email protected]> wrote:

> Working example
> *PleaseSaveMyPassword.html:*
> <!doctype html>
> <html>
>   <head>
>     <meta http-equiv="content-type" content="text/html; charset=UTF-8">
>     <title>Please Save My Password</title>
>     <script type="text/javascript" language="javascript"
> src="pleasesavemypassword/pleasesavemypassword.nocache.js"></script>
>     <style>
> h1 {font-size: 2em; font-weight: bold; color: #777777; margin: 40px 0px
> 70px; text-align: center;}
> #gwt, table {width: 100%;}
>  .loginPanel {width: 300px; margin: 0 auto; border: 1px solid #ccc;
> border-radius: 5px;}
> input {width: 200px; float: right;}
>  button {width: 80px; float: right;}
> .loggedInPanel {width: 300px; margin: 0 auto; font-size: 1.5em;}
> .gwt-HTML {float: left;}
>  a {float: right;}
> </style>
>   </head>
>   <body>
>     <h1>Please Save My Password</h1>
>     <div id="gwt"></div>
> <div id="login" style="display: none;">
> <form id="login_form" action="javascript:;">
>  <input type="text" name="username" id="login_username" />
> <input type="password" name="password" id="login_password" />
>  <button type="submit" id="login_submit"></button>
> </form>
> </div>
>   </body>
> </html>
>
> *PleaseSaveMyPassword.java:*
> package com.sh.pleasesavemypassword.client;
>
> import com.google.gwt.core.client.EntryPoint;
> import com.google.gwt.core.client.GWT;
> import com.google.gwt.event.dom.client.ClickEvent;
> import com.google.gwt.event.dom.client.ClickHandler;
> import com.google.gwt.user.client.DOM;
> import com.google.gwt.user.client.Window;
> import com.google.gwt.user.client.rpc.AsyncCallback;
> import com.google.gwt.user.client.ui.Anchor;
> import com.google.gwt.user.client.ui.Button;
> import com.google.gwt.user.client.ui.FlexTable;
> import com.google.gwt.user.client.ui.FlowPanel;
> import com.google.gwt.user.client.ui.FormPanel;
> import com.google.gwt.user.client.ui.FormPanel.SubmitEvent;
> import com.google.gwt.user.client.ui.FormPanel.SubmitHandler;
> import com.google.gwt.user.client.ui.HTML;
> import com.google.gwt.user.client.ui.Label;
> import com.google.gwt.user.client.ui.PasswordTextBox;
> import com.google.gwt.user.client.ui.RootPanel;
> import com.google.gwt.user.client.ui.SubmitButton;
> import com.google.gwt.user.client.ui.TextBox;
>
> public class PleaseSaveMyPassword implements EntryPoint {
>  private static final BackendServiceAsync backend =
> GWT.create(BackendService.class);
>  private static class LoginForm extends FlowPanel {
>  private static LoginForm instance = null;
> private static final TextBox txtUser =
> TextBox.wrap(DOM.getElementById("login_username"));
>  private static final PasswordTextBox txtPassword =
> PasswordTextBox.wrap(DOM.getElementById("login_password"));
> private static final Button btnSubmit =
> SubmitButton.wrap(DOM.getElementById("login_submit"));
>  private static final FormPanel form =
> FormPanel.wrap(DOM.getElementById("login_form"));
>  public static LoginForm getInstance() {
> if (instance == null) {
> instance = new LoginForm();
>  }
> return instance;
> }
> private LoginForm() {
>  setStyleName("loginPanel");
>
> FlexTable table = new FlexTable();
> table.setWidget(0, 0, new Label("User:"));
>  table.setWidget(0, 1, txtUser);
> table.setWidget(1, 0, new Label("Password:"));
> table.setWidget(1, 1, txtPassword);
>  btnSubmit.setText("Login");
> table.setWidget(2, 1, btnSubmit);
> form.setWidget(table);
>  add(form);
>  form.addSubmitHandler(new SubmitHandler() {
>  @Override
> public void onSubmit(SubmitEvent event) {
> btnSubmit.setEnabled(false);
>  backend.login(txtUser.getText(), txtPassword.getText(), new
> AsyncCallback<String>() {
> @Override
>  public void onFailure(Throwable caught) {
> Window.alert(caught.getMessage());
> btnSubmit.setEnabled(true);
>  }
>
> @Override
> public void onSuccess(String result) {
>  if (result == null) {
> Window.alert("Invalid username or password");
> btnSubmit.setEnabled(true);
>  }
> else {
> // This is the only I've found that makes Chrome happy:
>  Window.Location.reload();
> //  IE and FF don't require this reload thing.
> // showLoggedInContents(result);
>  }
> }
> });
> }
>  });
> }
> }
> public void onModuleLoad() {
>  // check if the user is already logged in
> backend.getLoggedInUser(new AsyncCallback<String>() {
>  @Override
> public void onSuccess(String result) {
> if (result == null)
>  showLoginForm();
> else
> showLoggedInContents(result);
>  }
>  @Override
> public void onFailure(Throwable caught) {
>  Window.alert(caught.getMessage());
> }
> });
>  }
>  private void showLoginForm() {
>  RootPanel.get("gwt").clear();
> RootPanel.get("gwt").add(LoginForm.getInstance());
> }
>  public static void showLoggedInContents(String user) {
> FlowPanel loggedInPanel = new FlowPanel();
>  loggedInPanel.setStyleName("loggedInPanel");
>  HTML html = new HTML("Hello, <strong>" + user + "</strong>");
>  loggedInPanel.add(html);
>  Anchor a = new Anchor("Logout");
>  a.addClickHandler(new ClickHandler() {
> @Override
> public void onClick(ClickEvent event) {
>  backend.logout(new AsyncCallback<Void>() {
> @Override
> public void onFailure(Throwable caught) {
>  Window.alert(caught.getMessage());
> }
>
> @Override
>  public void onSuccess(Void result) {
> Window.Location.reload();
> }
>  });
> }
> });
> loggedInPanel.add(a);
>  RootPanel.get("gwt").add(loggedInPanel);
>  }
> }
> *BackendServiceImpl.java:*
> package com.sh.pleasesavemypassword.server;
>
> import com.google.gwt.user.server.rpc.RemoteServiceServlet;
> import com.sh.pleasesavemypassword.client.BackendService;
>
> @SuppressWarnings("serial")
> public class BackendServiceImpl extends RemoteServiceServlet implements
> BackendService {
>
> @Override
>  public String getLoggedInUser() {
> return (String) getThreadLocalRequest().getSession().getAttribute("user");
>  }
>
> @Override
> public String login(String user, String password) {
>  if (password.equals("ta7yasoorya")) {
> getThreadLocalRequest().getSession().setAttribute("user", user);
>  return user;
> }
> return null;
> }
>
> @Override
> public void logout() {
> getThreadLocalRequest().getSession().removeAttribute("user");
>  }
>
> }
>
>
> Em domingo, 8 de junho de 2014 23h17min43s UTC-3, Blake escreveu:
>
>> On Sat, Jun 7, 2014 at 5:29 PM, Jens <[email protected]> wrote:
>>
>>>  Other than that you should follow your original approach and let the
>>>> browser/password manager ask the user to store the password.
>>>>
>>>
>> That is clearly the best and my preferred approach.  The problem is that
>> no person and no web site has stated they know how to do it (in a way that
>> supports most modern browsers), and here is specifically how it is done (a
>> working example).  The web sites have many half solutions, and I have seen
>> many "try this" solutions.  I am weaker than most in this technology, and I
>> am a bit lost.  I think the only thing that is going to help me is a
>> working example.
>>
>> Thanks.
>>
>> Blake
>>
>>  --
> You received this message because you are subscribed to the Google Groups
> "Google Web Toolkit" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To post to this group, send email to [email protected].
> Visit this group at http://groups.google.com/group/google-web-toolkit.
> For more options, visit https://groups.google.com/d/optout.
>

-- 
You received this message because you are subscribed to the Google Groups 
"Google Web Toolkit" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/d/optout.

Reply via email to