зеркало из https://github.com/mozilla/gecko-dev.git
Bug 700527 - Master password support for native fennec. r=mfinkle
This commit is contained in:
Родитель
0b3d8de319
Коммит
37e18a6598
|
@ -41,16 +41,24 @@ package org.mozilla.gecko;
|
|||
import java.lang.CharSequence;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.text.Editable;
|
||||
import android.app.AlertDialog;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.content.res.Resources;
|
||||
import android.content.Context;
|
||||
import android.preference.*;
|
||||
import android.preference.Preference.*;
|
||||
import android.text.InputType;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.text.TextWatcher;
|
||||
import android.content.DialogInterface;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
@ -128,9 +136,17 @@ public class GeckoPreferences
|
|||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
final private int DIALOG_CREATE_MASTER_PASSWORD = 0;
|
||||
final private int DIALOG_REMOVE_MASTER_PASSWORD = 1;
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
String prefName = preference.getKey();
|
||||
if (prefName.equals("privacy.masterpassword.enabled")) {
|
||||
showDialog((Boolean)newValue ? DIALOG_CREATE_MASTER_PASSWORD : DIALOG_REMOVE_MASTER_PASSWORD);
|
||||
return false;
|
||||
}
|
||||
|
||||
setPreference(prefName, newValue);
|
||||
if (preference instanceof ListPreference) {
|
||||
// We need to find the entry for the new value
|
||||
|
@ -143,6 +159,123 @@ public class GeckoPreferences
|
|||
return true;
|
||||
}
|
||||
|
||||
private EditText getTextBox(int aHintText) {
|
||||
EditText input = new EditText(GeckoApp.mAppContext);
|
||||
int inputtype = InputType.TYPE_CLASS_TEXT;
|
||||
inputtype |= InputType.TYPE_TEXT_VARIATION_PASSWORD | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
|
||||
input.setInputType(inputtype);
|
||||
|
||||
String hint = getResources().getString(aHintText);
|
||||
input.setHint(aHintText);
|
||||
return input;
|
||||
}
|
||||
|
||||
private AlertDialog mDialog = null;
|
||||
|
||||
private class PasswordTextWatcher implements TextWatcher {
|
||||
EditText input1 = null;
|
||||
EditText input2 = null;
|
||||
|
||||
PasswordTextWatcher(EditText aInput1, EditText aInput2) {
|
||||
input1 = aInput1;
|
||||
input2 = aInput2;
|
||||
}
|
||||
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (mDialog == null)
|
||||
return;
|
||||
|
||||
String text1 = input1.getText().toString();
|
||||
String text2 = input2.getText().toString();
|
||||
mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(text1.equals(text2));
|
||||
}
|
||||
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) { }
|
||||
}
|
||||
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
LinearLayout linearLayout = new LinearLayout(this);
|
||||
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
||||
switch(id) {
|
||||
case DIALOG_CREATE_MASTER_PASSWORD:
|
||||
final EditText input1 = getTextBox(R.string.masterpassword_password);
|
||||
final EditText input2 = getTextBox(R.string.masterpassword_confirm);
|
||||
PasswordTextWatcher watcher = new PasswordTextWatcher(input1, input2);
|
||||
input1.addTextChangedListener((TextWatcher)watcher);
|
||||
input2.addTextChangedListener((TextWatcher)watcher);
|
||||
linearLayout.addView(input1);
|
||||
linearLayout.addView(input2);
|
||||
|
||||
builder.setTitle(R.string.masterpassword_create_title)
|
||||
.setView((View)linearLayout)
|
||||
.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
JSONObject jsonPref = new JSONObject();
|
||||
try {
|
||||
jsonPref.put("name", "privacy.masterpassword.enabled");
|
||||
jsonPref.put("type", "string");
|
||||
jsonPref.put("value", input1.getText().toString());
|
||||
|
||||
GeckoEvent event = new GeckoEvent("Preferences:Set", jsonPref.toString());
|
||||
GeckoAppShell.sendEventToGecko(event);
|
||||
} catch(Exception ex) {
|
||||
Log.e(LOGTAG, "Error setting masterpassword", ex);
|
||||
}
|
||||
mDialog = null;
|
||||
input1.setText("");
|
||||
input2.setText("");
|
||||
return;
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.button_cancel, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
mDialog = null;
|
||||
input1.setText("");
|
||||
input2.setText("");
|
||||
return;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case DIALOG_REMOVE_MASTER_PASSWORD:
|
||||
final EditText input = getTextBox(R.string.masterpassword_password);
|
||||
linearLayout.addView(input);
|
||||
|
||||
builder.setTitle(R.string.masterpassword_remove_title)
|
||||
.setView((View)linearLayout)
|
||||
.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
try {
|
||||
JSONObject jsonPref = new JSONObject();
|
||||
jsonPref.put("name", "privacy.masterpassword.enabled");
|
||||
jsonPref.put("type", "string");
|
||||
jsonPref.put("value", input.getText().toString());
|
||||
|
||||
GeckoEvent event = new GeckoEvent("Preferences:Set", jsonPref.toString());
|
||||
GeckoAppShell.sendEventToGecko(event);
|
||||
} catch(Exception ex) {
|
||||
Log.e(LOGTAG, "Error setting masterpassword", ex);
|
||||
}
|
||||
input.setText("");
|
||||
mDialog = null;
|
||||
return;
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.button_cancel, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
mDialog = null;
|
||||
input.setText("");
|
||||
return;
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
return mDialog = builder.create();
|
||||
}
|
||||
|
||||
private void refresh(JSONArray jsonPrefs) {
|
||||
// enable all preferences once we have them from gecko
|
||||
GeckoAppShell.getMainHandler().post(new Runnable() {
|
||||
|
|
|
@ -62,15 +62,16 @@
|
|||
<!ENTITY pref_clear_history_confirm "Browsing history will be deleted">
|
||||
<!ENTITY pref_clear_private_data "Clear private data">
|
||||
<!ENTITY pref_clear_private_data_confirm "Browsing settings, including passwords and cookies, will be deleted">
|
||||
<!ENTITY pref_enable_plugins "Enable Plugins">
|
||||
<!ENTITY pref_enable_plugins "Enable plugins">
|
||||
<!ENTITY pref_enable_plugins_yes "Yes">
|
||||
<!ENTITY pref_enable_plugins_tap_to_play "Tap To Play">
|
||||
<!ENTITY pref_enable_plugins_tap_to_play "Tap to play">
|
||||
<!ENTITY pref_enable_plugins_no "No">
|
||||
<!ENTITY pref_font_size "Font size">
|
||||
<!ENTITY pref_font_size_small "Small">
|
||||
<!ENTITY pref_font_size_medium "Medium">
|
||||
<!ENTITY pref_font_size_large "Large">
|
||||
<!ENTITY pref_font_size_xlarge "Extra Large">
|
||||
<!ENTITY pref_use_master_password "Use master password">
|
||||
|
||||
<!ENTITY quit "Quit">
|
||||
|
||||
|
@ -89,3 +90,11 @@
|
|||
<!ENTITY site_settings_cancel "Cancel">
|
||||
<!ENTITY site_settings_clear "Clear">
|
||||
<!ENTITY site_settings_no_settings "There are no settings to clear.">
|
||||
|
||||
<!ENTITY masterpassword_create_title "Create Master Password">
|
||||
<!ENTITY masterpassword_remove_title "Remove Master Password">
|
||||
<!ENTITY masterpassword_password "Password">
|
||||
<!ENTITY masterpassword_confirm "Confirm password">
|
||||
|
||||
<!ENTITY button_ok "OK">
|
||||
<!ENTITY button_cancel "Cancel">
|
||||
|
|
|
@ -65,6 +65,11 @@
|
|||
android:title="@string/pref_telemetry"
|
||||
android:persistent="false" />
|
||||
|
||||
<CheckBoxPreference android:key="privacy.masterpassword.enabled"
|
||||
android:title="@string/pref_use_master_password"
|
||||
android:defaultValue="false"
|
||||
android:persistent="false" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
|
|
@ -93,4 +93,13 @@
|
|||
<string name="contextmenu_open_new_tab">&contextmenu_open_new_tab;</string>
|
||||
<string name="contextmenu_add_to_launcher">&contextmenu_add_to_launcher;</string>
|
||||
<string name="contextmenu_share">&contextmenu_share;</string>
|
||||
|
||||
<string name="pref_use_master_password">&pref_use_master_password;</string>
|
||||
<string name="masterpassword_create_title">&masterpassword_create_title;</string>
|
||||
<string name="masterpassword_remove_title">&masterpassword_remove_title;</string>
|
||||
<string name="masterpassword_password">&masterpassword_password;</string>
|
||||
<string name="masterpassword_confirm">&masterpassword_confirm;</string>
|
||||
|
||||
<string name="button_ok">&button_ok;</string>
|
||||
<string name="button_cancel">&button_cancel;</string>
|
||||
</resources>
|
||||
|
|
|
@ -490,6 +490,12 @@ var BrowserApp = {
|
|||
pref.value = PluginHelper.getPluginPreference();
|
||||
prefs.push(pref);
|
||||
continue;
|
||||
} else if (prefName == MasterPassword.pref) {
|
||||
// Master password is not a "real" pref
|
||||
pref.type = "bool";
|
||||
pref.value = MasterPassword.enabled;
|
||||
prefs.push(pref);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -552,6 +558,11 @@ var BrowserApp = {
|
|||
if (json.name == "plugin.enable") {
|
||||
PluginHelper.setPluginPreference(json.value);
|
||||
return;
|
||||
} else if(json.name == MasterPassword.pref) {
|
||||
if (MasterPassword.enabled)
|
||||
MasterPassword.removePassword(json.value);
|
||||
else
|
||||
MasterPassword.setPassword(json.value);
|
||||
}
|
||||
|
||||
// when sending to java, we normalized special preferences that use
|
||||
|
@ -3483,3 +3494,81 @@ var PermissionsHelper = {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
var MasterPassword = {
|
||||
pref: "privacy.masterpassword.enabled",
|
||||
get _secModuleDB() {
|
||||
delete this._secModuleDB;
|
||||
return this._secModuleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(Ci.nsIPKCS11ModuleDB);
|
||||
},
|
||||
|
||||
get _pk11DB() {
|
||||
delete this._pk11DB;
|
||||
return this._pk11DB = Cc["@mozilla.org/security/pk11tokendb;1"].getService(Ci.nsIPK11TokenDB);
|
||||
},
|
||||
|
||||
get enabled() {
|
||||
let slot = this._secModuleDB.findSlotByName(this._tokenName);
|
||||
if (slot) {
|
||||
let status = slot.status;
|
||||
return status != Ci.nsIPKCS11Slot.SLOT_UNINITIALIZED && status != Ci.nsIPKCS11Slot.SLOT_READY;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
setPassword: function setPassword(aPassword) {
|
||||
try {
|
||||
let status;
|
||||
let slot = this._secModuleDB.findSlotByName(this._tokenName);
|
||||
if (slot)
|
||||
status = slot.status;
|
||||
else
|
||||
return false;
|
||||
|
||||
let token = this._pk11DB.findTokenByName(this._tokenName);
|
||||
|
||||
if (status == Ci.nsIPKCS11Slot.SLOT_UNINITIALIZED)
|
||||
token.initPassword(aPassword);
|
||||
else if (status == Ci.nsIPKCS11Slot.SLOT_READY)
|
||||
token.changePassword("", aPassword);
|
||||
|
||||
this.updatePref();
|
||||
return true;
|
||||
} catch(e) {
|
||||
dump("MasterPassword.setPassword: " + e);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
removePassword: function removePassword(aOldPassword) {
|
||||
try {
|
||||
let token = this._pk11DB.getInternalKeyToken();
|
||||
if (token.checkPassword(aOldPassword)) {
|
||||
token.changePassword(aOldPassword, "");
|
||||
this.updatePref();
|
||||
return true;
|
||||
}
|
||||
} catch(e) {
|
||||
dump("MasterPassword.removePassword: " + e + "\n");
|
||||
}
|
||||
NativeWindow.toast.show(Strings.browser.GetStringFromName("masterPassword.incorrect"), "short");
|
||||
return false;
|
||||
},
|
||||
|
||||
updatePref: function() {
|
||||
var prefs = [];
|
||||
let pref = {
|
||||
name: this.pref,
|
||||
type: "bool",
|
||||
value: this.enabled
|
||||
};
|
||||
prefs.push(pref);
|
||||
|
||||
sendMessageToJava({
|
||||
gecko: {
|
||||
type: "Preferences:Data",
|
||||
preferences: prefs
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -328,7 +328,7 @@ Prompt.prototype = {
|
|||
|
||||
nsIPrompt_promptPassword: function nsIPrompt_promptPassword(
|
||||
aTitle, aText, aPassword, aCheckMsg, aCheckState) {
|
||||
let inputs = [{ type: "password", hint: "Password", value: aPassword.value }];
|
||||
let inputs = [{ type: "password", hint: "Password", value: aPassword.value || "" }];
|
||||
let data = this.commonPrompt(aTitle, aText, null, aCheckMsg, aCheckState, inputs);
|
||||
|
||||
if (aCheckMsg)
|
||||
|
|
|
@ -214,3 +214,5 @@ clickToPlayPlugins.no=No
|
|||
# dislay a list of current permissions settings for a site.
|
||||
# Example: "Store Offline Data: Allow"
|
||||
siteSettings.labelToValue=%S: %S
|
||||
|
||||
masterPassword.incorrect=Incorrect password
|
||||
|
|
Загрузка…
Ссылка в новой задаче