зеркало из https://github.com/mozilla/pjs.git
Relanding bug 178607 - Keychain cannot save multiple passwords for the same host. Patch by Bryan Atwood <batwood.bugs@gmail.com>, r=smorgan, sr=mento.
This commit is contained in:
Родитель
8f07d7f084
Коммит
0007b3b331
|
@ -1038,6 +1038,14 @@
|
|||
DE8C62000AB67CDA00078871 /* hidemanager.tiff in Resources */ = {isa = PBXBuildFile; fileRef = DE8C61FE0AB67CDA00078871 /* hidemanager.tiff */; };
|
||||
DE8EEA0C0D39A2A500BB96C1 /* dom_json.xpt in Copy Component XPTs */ = {isa = PBXBuildFile; fileRef = DE8EEA0B0D39A2A500BB96C1 /* dom_json.xpt */; };
|
||||
DE8EEA0D0D39A2A500BB96C1 /* dom_json.xpt in Copy Component XPTs */ = {isa = PBXBuildFile; fileRef = DE8EEA0B0D39A2A500BB96C1 /* dom_json.xpt */; };
|
||||
DE963D190D43EFCF007D44EE /* AutoCompleteUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = DE963D110D43EFCF007D44EE /* AutoCompleteUtils.mm */; };
|
||||
DE963D1A0D43EFCF007D44EE /* FormFillPopup.mm in Sources */ = {isa = PBXBuildFile; fileRef = DE963D120D43EFCF007D44EE /* FormFillPopup.mm */; };
|
||||
DE963D1B0D43EFCF007D44EE /* FormFillController.mm in Sources */ = {isa = PBXBuildFile; fileRef = DE963D130D43EFCF007D44EE /* FormFillController.mm */; };
|
||||
DE963D1C0D43EFCF007D44EE /* KeychainAutoCompleteSession.mm in Sources */ = {isa = PBXBuildFile; fileRef = DE963D150D43EFCF007D44EE /* KeychainAutoCompleteSession.mm */; settings = {COMPILER_FLAGS = "-DNO_NSPR_10_SUPPORT"; }; };
|
||||
DE963D1D0D43EFCF007D44EE /* AutoCompleteUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = DE963D110D43EFCF007D44EE /* AutoCompleteUtils.mm */; };
|
||||
DE963D1E0D43EFCF007D44EE /* FormFillPopup.mm in Sources */ = {isa = PBXBuildFile; fileRef = DE963D120D43EFCF007D44EE /* FormFillPopup.mm */; };
|
||||
DE963D1F0D43EFCF007D44EE /* FormFillController.mm in Sources */ = {isa = PBXBuildFile; fileRef = DE963D130D43EFCF007D44EE /* FormFillController.mm */; };
|
||||
DE963D200D43EFCF007D44EE /* KeychainAutoCompleteSession.mm in Sources */ = {isa = PBXBuildFile; fileRef = DE963D150D43EFCF007D44EE /* KeychainAutoCompleteSession.mm */; settings = {COMPILER_FLAGS = "-DNO_NSPR_10_SUPPORT"; }; };
|
||||
DEA5484A0A251FA900186C93 /* GeckoUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DEA548490A251FA900186C93 /* GeckoUtils.cpp */; };
|
||||
DEA5484B0A251FA900186C93 /* GeckoUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DEA548490A251FA900186C93 /* GeckoUtils.cpp */; };
|
||||
DEB608CA0CAF0964006C34F1 /* throbber-00.tiff in Resources */ = {isa = PBXBuildFile; fileRef = DEB608C90CAF0964006C34F1 /* throbber-00.tiff */; };
|
||||
|
@ -2472,6 +2480,14 @@
|
|||
DE8AD69F0ADB4E38009D44F6 /* bm_horizontal_separator.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = bm_horizontal_separator.tiff; path = resources/images/chrome/bm_horizontal_separator.tiff; sourceTree = SOURCE_ROOT; };
|
||||
DE8C61FE0AB67CDA00078871 /* hidemanager.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = hidemanager.tiff; path = resources/images/toolbar/hidemanager.tiff; sourceTree = SOURCE_ROOT; };
|
||||
DE8EEA0B0D39A2A500BB96C1 /* dom_json.xpt */ = {isa = PBXFileReference; lastKnownFileType = file; name = dom_json.xpt; path = ../dist/bin/components/dom_json.xpt; sourceTree = SOURCE_ROOT; };
|
||||
DE963D110D43EFCF007D44EE /* AutoCompleteUtils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AutoCompleteUtils.mm; path = src/formfill/AutoCompleteUtils.mm; sourceTree = SOURCE_ROOT; };
|
||||
DE963D120D43EFCF007D44EE /* FormFillPopup.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = FormFillPopup.mm; path = src/formfill/FormFillPopup.mm; sourceTree = SOURCE_ROOT; };
|
||||
DE963D130D43EFCF007D44EE /* FormFillController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = FormFillController.mm; path = src/formfill/FormFillController.mm; sourceTree = SOURCE_ROOT; };
|
||||
DE963D140D43EFCF007D44EE /* KeychainAutoCompleteSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = KeychainAutoCompleteSession.h; path = src/formfill/KeychainAutoCompleteSession.h; sourceTree = SOURCE_ROOT; };
|
||||
DE963D150D43EFCF007D44EE /* KeychainAutoCompleteSession.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = KeychainAutoCompleteSession.mm; path = src/formfill/KeychainAutoCompleteSession.mm; sourceTree = SOURCE_ROOT; };
|
||||
DE963D160D43EFCF007D44EE /* FormFillPopup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FormFillPopup.h; path = src/formfill/FormFillPopup.h; sourceTree = SOURCE_ROOT; };
|
||||
DE963D170D43EFCF007D44EE /* FormFillController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FormFillController.h; path = src/formfill/FormFillController.h; sourceTree = SOURCE_ROOT; };
|
||||
DE963D180D43EFCF007D44EE /* AutoCompleteUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AutoCompleteUtils.h; path = src/formfill/AutoCompleteUtils.h; sourceTree = SOURCE_ROOT; };
|
||||
DEA548490A251FA900186C93 /* GeckoUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = GeckoUtils.cpp; path = src/extensions/GeckoUtils.cpp; sourceTree = SOURCE_ROOT; };
|
||||
DEB608C90CAF0964006C34F1 /* throbber-00.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = "throbber-00.tiff"; path = "resources/images/throbber/throbber-00.tiff"; sourceTree = SOURCE_ROOT; };
|
||||
DEB968140B0D8DF70023F8B1 /* KeychainItem.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = KeychainItem.m; path = src/formfill/KeychainItem.m; sourceTree = "<group>"; };
|
||||
|
@ -3525,6 +3541,14 @@
|
|||
335639BD0C84E8B900DC4D06 /* Keychain */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DE963D180D43EFCF007D44EE /* AutoCompleteUtils.h */,
|
||||
DE963D110D43EFCF007D44EE /* AutoCompleteUtils.mm */,
|
||||
DE963D170D43EFCF007D44EE /* FormFillController.h */,
|
||||
DE963D130D43EFCF007D44EE /* FormFillController.mm */,
|
||||
DE963D160D43EFCF007D44EE /* FormFillPopup.h */,
|
||||
DE963D120D43EFCF007D44EE /* FormFillPopup.mm */,
|
||||
DE963D140D43EFCF007D44EE /* KeychainAutoCompleteSession.h */,
|
||||
DE963D150D43EFCF007D44EE /* KeychainAutoCompleteSession.mm */,
|
||||
7BB8FC190D2D589100CC63B0 /* KeychainDenyList.h */,
|
||||
7BB8FC1A0D2D589100CC63B0 /* KeychainDenyList.mm */,
|
||||
DEB968170B0D8E0B0023F8B1 /* KeychainItem.h */,
|
||||
|
@ -5295,6 +5319,10 @@
|
|||
C79573880D35314D0028A773 /* XMLSearchPluginParser.mm in Sources */,
|
||||
C795738A0D35314D0028A773 /* OpenSearchParser.mm in Sources */,
|
||||
33E1EA100D34550A00910BBD /* AddSearchProviderHandler.mm in Sources */,
|
||||
DE963D190D43EFCF007D44EE /* AutoCompleteUtils.mm in Sources */,
|
||||
DE963D1A0D43EFCF007D44EE /* FormFillPopup.mm in Sources */,
|
||||
DE963D1B0D43EFCF007D44EE /* FormFillController.mm in Sources */,
|
||||
DE963D1C0D43EFCF007D44EE /* KeychainAutoCompleteSession.mm in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -5468,6 +5496,10 @@
|
|||
C795738C0D35314D0028A773 /* XMLSearchPluginParser.mm in Sources */,
|
||||
C795738E0D35314D0028A773 /* OpenSearchParser.mm in Sources */,
|
||||
33E1EA120D34550A00910BBD /* AddSearchProviderHandler.mm in Sources */,
|
||||
DE963D1D0D43EFCF007D44EE /* AutoCompleteUtils.mm in Sources */,
|
||||
DE963D1E0D43EFCF007D44EE /* FormFillPopup.mm in Sources */,
|
||||
DE963D1F0D43EFCF007D44EE /* FormFillController.mm in Sources */,
|
||||
DE963D200D43EFCF007D44EE /* KeychainAutoCompleteSession.mm in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
|
||||
@class BrowserWindowController;
|
||||
@class ToolTip;
|
||||
@class FormFillController;
|
||||
@class AutoCompleteTextField;
|
||||
@class RolloverImageButton;
|
||||
|
||||
|
@ -149,6 +150,7 @@ class nsIArray;
|
|||
|
||||
CHBrowserView* mBrowserView; // retained
|
||||
ToolTip* mToolTip;
|
||||
FormFillController* mFormFillController; // strong
|
||||
NSMutableArray* mStatusStrings; // current status bar messages, STRONG
|
||||
NSMutableSet* mLoadingResources; // page resources currently loading, STRONG
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#import "BrowserTabView.h"
|
||||
#import "BrowserTabViewItem.h"
|
||||
#import "ToolTip.h"
|
||||
#import "FormFillController.h"
|
||||
#import "PageProxyIcon.h"
|
||||
#import "KeychainService.h"
|
||||
#import "AutoCompleteTextField.h"
|
||||
|
@ -166,6 +167,9 @@ enum StatusPriority {
|
|||
|
||||
mToolTip = [[ToolTip alloc] init];
|
||||
|
||||
mFormFillController = [[FormFillController alloc] init];
|
||||
[mFormFillController attachToBrowser:mBrowserView];
|
||||
|
||||
//[self setSiteIconImage:[NSImage imageNamed:@"globe_ico"]];
|
||||
//[self setSiteIconURI: [NSString string]];
|
||||
|
||||
|
@ -199,6 +203,7 @@ enum StatusPriority {
|
|||
|
||||
[mToolTip release];
|
||||
[mDisplayTitle release];
|
||||
[mFormFillController release];
|
||||
[mPendingURI release];
|
||||
|
||||
NS_IF_RELEASE(mBlockedPopups);
|
||||
|
|
|
@ -63,7 +63,11 @@
|
|||
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIDocShellTreeItem.h"
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsIFrame.h"
|
||||
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIDOMWindowInternal.h"
|
||||
#include "nsIDOMNSHTMLElement.h"
|
||||
|
@ -352,3 +356,45 @@ void GeckoUtils::GetIntrisicSize(nsIDOMWindow* aWindow, PRInt32* outWidth, PRIn
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
PRBool GeckoUtils::GetFrameInScreenCoordinates(nsIDOMElement* aElement, nsIntRect* aRect)
|
||||
{
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
|
||||
if (!content)
|
||||
return PR_FALSE;
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = content->GetDocument();
|
||||
if (!doc)
|
||||
return PR_FALSE;
|
||||
|
||||
nsCOMPtr<nsIPresShell> presShell = doc->GetPrimaryShell();
|
||||
if (!presShell)
|
||||
return PR_FALSE;
|
||||
|
||||
nsIFrame* frame = presShell->GetPrimaryFrameFor(content);
|
||||
if (!frame)
|
||||
return PR_FALSE;
|
||||
|
||||
*aRect = frame->GetScreenRectExternal();
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
void GeckoUtils::ScrollElementIntoView(nsIDOMElement* aElement)
|
||||
{
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
|
||||
if (!content)
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = content->GetDocument();
|
||||
if (!doc)
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIPresShell> presShell = doc->GetPrimaryShell();
|
||||
if (!presShell)
|
||||
return;
|
||||
|
||||
presShell->ScrollContentIntoView(content, NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE,
|
||||
NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE);
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "nsString.h"
|
||||
#include "jsapi.h"
|
||||
#include "nsIJSContextStack.h"
|
||||
#include "nsRect.h"
|
||||
|
||||
class nsIDOMWindow;
|
||||
class nsIDOMNode;
|
||||
|
@ -81,6 +82,13 @@ class GeckoUtils
|
|||
|
||||
/* Finds the preferred size (ie the minimum size where scrollbars are not needed) of the content window. */
|
||||
static void GetIntrisicSize(nsIDOMWindow* aWindow, PRInt32* outWidth, PRInt32* outHeight);
|
||||
|
||||
// Finds the screen location (nsIntRect) in screen coordinates of a DOM Element.
|
||||
// Returns PR_FALSE if the function fails.
|
||||
static PRBool GetFrameInScreenCoordinates(nsIDOMElement* aElement, nsIntRect* aRect);
|
||||
|
||||
// Given a DOM Element, scroll the view so that the element is shown
|
||||
static void ScrollElementIntoView(nsIDOMElement* aElement);
|
||||
};
|
||||
|
||||
/* Stack-based utility that will push a null JSContext onto the JS stack during the
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Camino code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Bryan Atwood
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Bryan Atwood <bryan.h.atwood@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
// AutoCompleteResults
|
||||
//
|
||||
// Container object for generic auto complete.
|
||||
// Holds the search string, array of matched objects and the default item.
|
||||
//
|
||||
@interface AutoCompleteResults : NSObject
|
||||
{
|
||||
NSString* mSearchString; // strong
|
||||
NSArray* mMatches; // strong
|
||||
int mDefaultIndex;
|
||||
}
|
||||
- (NSString*)searchString;
|
||||
- (void)setSearchString:(NSString*)string;
|
||||
|
||||
- (NSArray*)matches;
|
||||
- (void)setMatches:(NSArray*)matches;
|
||||
|
||||
- (int)defaultIndex;
|
||||
- (void)setDefaultIndex:(int)defaultIndex;
|
||||
|
||||
@end
|
||||
|
||||
// AutoCompleteListener
|
||||
//
|
||||
// This defines the protocol methods for the object that listens for auto complete
|
||||
// results. |onAutoComplete| is called by the object that searches the data and
|
||||
// the results are returned to the originating caller as AutoCompleteResults.
|
||||
//
|
||||
@protocol AutoCompleteListener
|
||||
- (void)autoCompleteFoundResults:(AutoCompleteResults*)results;
|
||||
@end
|
||||
|
||||
// AutoCompleteSession
|
||||
//
|
||||
// An AutoCompleteSession object listens for search requests and searches a set of data
|
||||
// |startAutoCompleteWithSearch| initiates the process. Previous results are passed in
|
||||
// as well as the listener object for when the search is complete.
|
||||
//
|
||||
@protocol AutoCompleteSession
|
||||
- (void)startAutoCompleteWithSearch:(NSString*)searchString
|
||||
previousResults:(AutoCompleteResults*)previousSearchResults
|
||||
listener:(id<AutoCompleteListener>)listener;
|
||||
@end
|
|
@ -0,0 +1,87 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Camino code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Bryan Atwood
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Bryan Atwood <bryan.h.atwood@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#import "AutoCompleteUtils.h"
|
||||
|
||||
@implementation AutoCompleteResults
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[mSearchString release];
|
||||
[mMatches release];
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSString*)searchString
|
||||
{
|
||||
return mSearchString;
|
||||
}
|
||||
|
||||
- (void)setSearchString:(NSString*)string
|
||||
{
|
||||
[mSearchString release];
|
||||
|
||||
// The caller might change the string, so keep a copy.
|
||||
mSearchString = [string copy];
|
||||
}
|
||||
|
||||
- (NSArray*)matches
|
||||
{
|
||||
return mMatches;
|
||||
}
|
||||
|
||||
- (void)setMatches:(NSArray*)matches
|
||||
{
|
||||
if (mMatches != matches) {
|
||||
[mMatches release];
|
||||
mMatches = [matches retain];
|
||||
}
|
||||
}
|
||||
|
||||
- (int)defaultIndex
|
||||
{
|
||||
return mDefaultIndex;
|
||||
}
|
||||
|
||||
- (void)setDefaultIndex:(int)index
|
||||
{
|
||||
mDefaultIndex = index;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,100 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Camino code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Bryan Atwood
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Bryan Atwood <bryan.h.atwood@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "AutoCompleteUtils.h"
|
||||
|
||||
#include "nsIDOMEventListener.h"
|
||||
|
||||
extern const int kFormFillMaxRows;
|
||||
|
||||
@class KeychainAutoCompleteSession;
|
||||
@class CHBrowserView;
|
||||
@class FormFillPopup;
|
||||
@class FormFillController;
|
||||
|
||||
class nsIDOMHTMLInputElement;
|
||||
|
||||
// FormFillListener
|
||||
//
|
||||
// The FormFillListener object listens for DOM events and the corresponding
|
||||
// methods in the FormFillController are called for handling.
|
||||
//
|
||||
class FormFillListener : public nsIDOMEventListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
|
||||
FormFillListener(FormFillController* aController);
|
||||
|
||||
protected:
|
||||
FormFillController* mController; // weak
|
||||
};
|
||||
|
||||
// FormFillController
|
||||
//
|
||||
// Manages the FormFillPopup windows that contain search results
|
||||
// as well as sending search requests to the KeychainAutoCompleteSession
|
||||
// and listening for search results. This can be extended to send
|
||||
// search requests to a form history session as well.
|
||||
@interface FormFillController : NSObject <AutoCompleteListener>
|
||||
{
|
||||
KeychainAutoCompleteSession* mKeychainSession; // strong
|
||||
AutoCompleteResults* mResults; // strong
|
||||
FormFillListener* mListener; // strong
|
||||
FormFillPopup* mPopupWindow; // strong
|
||||
|
||||
CHBrowserView* mBrowserView; // weak
|
||||
nsIDOMHTMLInputElement* mFocusedInputElement; // weak
|
||||
|
||||
// mCompleteResult determines if the current search should complete the default
|
||||
// result when ready. This prevents backspace/delete from autocompleting.
|
||||
BOOL mCompleteResult;
|
||||
|
||||
// mUsernameFillEnabled determines whether we send searches to the Keychain
|
||||
// Service. Form fill history value can be added here as well.
|
||||
BOOL mUsernameFillEnabled;
|
||||
}
|
||||
|
||||
- (void)attachToBrowser:(CHBrowserView*)browser;
|
||||
|
||||
// Callback function for when a row in the focused popup window is clicked.
|
||||
- (void)popupSelected;
|
||||
|
||||
@end
|
|
@ -0,0 +1,748 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Camino code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Bryan Atwood
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Bryan Atwood <bryan.h.atwood@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#import "FormFillController.h"
|
||||
#import "CHBrowserView.h"
|
||||
#import "FormFillPopup.h"
|
||||
#import "KeychainAutoCompleteSession.h"
|
||||
#import "PreferenceManager.h"
|
||||
|
||||
#import "NSString+Gecko.h"
|
||||
#import "NSArray+Utils.h"
|
||||
|
||||
#include "GeckoUtils.h"
|
||||
#include "nsString.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsIDOMDocumentEvent.h"
|
||||
#include "nsIPrivateDOMEvent.h"
|
||||
#include "nsIDOMEventTarget.h"
|
||||
#include "nsIDOMEvent.h"
|
||||
#include "nsIDOMHTMLFormElement.h"
|
||||
#include "nsIDOMHTMLInputElement.h"
|
||||
#include "nsIDOMNSHTMLInputElement.h"
|
||||
#include "nsIDOMKeyEvent.h"
|
||||
|
||||
const int kFormFillMaxRows = 10;
|
||||
|
||||
@interface FormFillController(Private)
|
||||
|
||||
// Listener and autocomplete initialization and cleanup
|
||||
- (void)browserResized:(NSNotification*)notification;
|
||||
- (void)addResizeObserver:(NSWindow*)browserWindow;
|
||||
- (void)removeResizeObserver:(NSWindow*)browserWindow;
|
||||
- (void)addWindowListeners:(nsIDOMWindow*)aWindow;
|
||||
- (void)removeWindowListeners:(nsIDOMWindow*)aWindow;
|
||||
- (void)startControllingInputElement:(nsIDOMHTMLInputElement*)aInputElement;
|
||||
- (void)stopControllingInputElement;
|
||||
|
||||
// Popup window management
|
||||
- (BOOL)isPopupOpen;
|
||||
- (void)openPopup;
|
||||
- (void)closePopup;
|
||||
- (void)shiftRowSelectionBy:(int)aRows;
|
||||
|
||||
// Autocomplete methods
|
||||
- (void)startSearch:(NSString*)searchString;
|
||||
- (void)dataReady;
|
||||
- (void)autoCompleteFieldText;
|
||||
- (void)filledAutoCompleteFieldText;
|
||||
|
||||
// Event handlers
|
||||
- (void)focus:(nsIDOMEvent*)aEvent;
|
||||
- (void)blur:(nsIDOMEvent*)aEvent;
|
||||
- (void)unload:(nsIDOMEvent*)aEvent;
|
||||
- (void)submit:(nsIDOMEvent*)aEvent;
|
||||
- (void)input:(nsIDOMEvent*)aEvent;
|
||||
- (void)keyPress:(nsIDOMEvent*)aEvent;
|
||||
- (BOOL)handleKeyNavigation:(int)aKey;
|
||||
|
||||
// Utility methods
|
||||
- (BOOL)IsCaretAtEndOfLine;
|
||||
|
||||
@end
|
||||
|
||||
NS_IMPL_ISUPPORTS1(FormFillListener, nsIDOMEventListener)
|
||||
|
||||
FormFillListener::FormFillListener(FormFillController* aController)
|
||||
: mController(aController)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP FormFillListener::HandleEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
nsAutoString type;
|
||||
aEvent->GetType(type);
|
||||
|
||||
if (type.Equals(NS_LITERAL_STRING("focus")))
|
||||
[mController focus:aEvent];
|
||||
else if (type.Equals(NS_LITERAL_STRING("blur")))
|
||||
[mController blur:aEvent];
|
||||
else if (type.Equals(NS_LITERAL_STRING("unload")))
|
||||
[mController unload:aEvent];
|
||||
else if (type.Equals(NS_LITERAL_STRING("submit")))
|
||||
[mController submit:aEvent];
|
||||
else if (type.Equals(NS_LITERAL_STRING("input")))
|
||||
[mController input:aEvent];
|
||||
else if (type.Equals(NS_LITERAL_STRING("keypress")))
|
||||
[mController keyPress:aEvent];
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@implementation FormFillController
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
// mListener captures DOM and auto complete events.
|
||||
mListener = new FormFillListener(self);
|
||||
NS_ADDREF(mListener);
|
||||
|
||||
// Initialize the password fill session.
|
||||
// History form fill session can also be added when someone writes it.
|
||||
mKeychainSession = [[KeychainAutoCompleteSession alloc] init];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
// Remove ourselves as a focus listener from cached view.
|
||||
nsCOMPtr<nsIDOMWindow> domWindow = [mBrowserView contentWindow];
|
||||
if (domWindow)
|
||||
[self removeWindowListeners:domWindow];
|
||||
|
||||
[self removeResizeObserver:[mBrowserView nativeWindow]];
|
||||
|
||||
[mResults release];
|
||||
[mKeychainSession release];
|
||||
[mPopupWindow release];
|
||||
NS_IF_RELEASE(mListener);
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)attachToBrowser:(CHBrowserView*)browser
|
||||
{
|
||||
if (!browser)
|
||||
return;
|
||||
|
||||
mBrowserView = browser;
|
||||
|
||||
// Listen for focus events on the domWindow of the browser view.
|
||||
nsCOMPtr<nsIDOMWindow> domWindow = [mBrowserView contentWindow];
|
||||
if (domWindow)
|
||||
[self addWindowListeners:domWindow];
|
||||
}
|
||||
|
||||
- (void)browserResized:(NSNotification*)notification
|
||||
{
|
||||
[self closePopup];
|
||||
}
|
||||
|
||||
- (void)addResizeObserver:(NSWindow*)browserWindow
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(browserResized:)
|
||||
name:NSWindowDidResizeNotification
|
||||
object:browserWindow];
|
||||
}
|
||||
|
||||
- (void)removeResizeObserver:(NSWindow*)browserWindow
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self
|
||||
name:NSWindowDidResizeNotification
|
||||
object:browserWindow];
|
||||
}
|
||||
|
||||
- (void)addWindowListeners:(nsIDOMWindow*)aWindow
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> privateDOMWindow = do_QueryInterface(aWindow);
|
||||
if (!privateDOMWindow)
|
||||
return;
|
||||
|
||||
nsPIDOMEventTarget* chromeEventHandler = privateDOMWindow->GetChromeEventHandler();
|
||||
|
||||
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(chromeEventHandler);
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
target->AddEventListener(NS_LITERAL_STRING("focus"),
|
||||
static_cast<nsIDOMEventListener*>(mListener),
|
||||
PR_TRUE);
|
||||
|
||||
target->AddEventListener(NS_LITERAL_STRING("blur"),
|
||||
static_cast<nsIDOMEventListener*>(mListener),
|
||||
PR_TRUE);
|
||||
|
||||
target->AddEventListener(NS_LITERAL_STRING("unload"),
|
||||
static_cast<nsIDOMEventListener*>(mListener),
|
||||
PR_TRUE);
|
||||
|
||||
target->AddEventListener(NS_LITERAL_STRING("submit"),
|
||||
static_cast<nsIDOMEventListener*>(mListener),
|
||||
PR_TRUE);
|
||||
|
||||
target->AddEventListener(NS_LITERAL_STRING("input"),
|
||||
static_cast<nsIDOMEventListener*>(mListener),
|
||||
PR_TRUE);
|
||||
|
||||
target->AddEventListener(NS_LITERAL_STRING("keypress"),
|
||||
static_cast<nsIDOMEventListener*>(mListener),
|
||||
PR_TRUE);
|
||||
}
|
||||
|
||||
- (void)removeWindowListeners:(nsIDOMWindow*)aWindow
|
||||
{
|
||||
[self stopControllingInputElement];
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> privateDOMWindow = do_QueryInterface(aWindow);
|
||||
if (!privateDOMWindow)
|
||||
return;
|
||||
|
||||
nsPIDOMEventTarget* chromeEventHandler = privateDOMWindow->GetChromeEventHandler();
|
||||
|
||||
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(chromeEventHandler);
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
target->RemoveEventListener(NS_LITERAL_STRING("focus"),
|
||||
static_cast<nsIDOMEventListener*>(mListener),
|
||||
PR_TRUE);
|
||||
|
||||
target->RemoveEventListener(NS_LITERAL_STRING("blur"),
|
||||
static_cast<nsIDOMEventListener*>(mListener),
|
||||
PR_TRUE);
|
||||
|
||||
target->RemoveEventListener(NS_LITERAL_STRING("unload"),
|
||||
static_cast<nsIDOMEventListener*>(mListener),
|
||||
PR_TRUE);
|
||||
|
||||
target->RemoveEventListener(NS_LITERAL_STRING("submit"),
|
||||
static_cast<nsIDOMEventListener*>(mListener),
|
||||
PR_TRUE);
|
||||
|
||||
target->RemoveEventListener(NS_LITERAL_STRING("input"),
|
||||
static_cast<nsIDOMEventListener*>(mListener),
|
||||
PR_TRUE);
|
||||
|
||||
target->RemoveEventListener(NS_LITERAL_STRING("keypress"),
|
||||
static_cast<nsIDOMEventListener*>(mListener),
|
||||
PR_TRUE);
|
||||
}
|
||||
|
||||
- (void)startControllingInputElement:(nsIDOMHTMLInputElement*)aInputElement
|
||||
{
|
||||
// Make sure we're not still attached to an input element.
|
||||
[self stopControllingInputElement];
|
||||
|
||||
// Set the autocomplete session for this input. Currently, only password autocomplete
|
||||
// but other sessions like form fill history can be added here.
|
||||
mUsernameFillEnabled = [mKeychainSession attachToInput:aInputElement];
|
||||
|
||||
// If this is not a valid auto-complete input, we can return now.
|
||||
if (!mUsernameFillEnabled)
|
||||
return;
|
||||
|
||||
// Cache the input element.
|
||||
mFocusedInputElement = aInputElement;
|
||||
|
||||
// Create the popup window if it doesn't exist.
|
||||
if (!mPopupWindow) {
|
||||
mPopupWindow = [[FormFillPopup alloc] init];
|
||||
[mPopupWindow attachToController:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stopControllingInputElement
|
||||
{
|
||||
if (mPopupWindow) {
|
||||
[self closePopup];
|
||||
[mPopupWindow setItems:nil];
|
||||
}
|
||||
|
||||
mFocusedInputElement = nsnull;
|
||||
|
||||
// Stop sending search requests to auto-form fill.
|
||||
mUsernameFillEnabled = NO;
|
||||
|
||||
[mResults release];
|
||||
mResults = nil;
|
||||
}
|
||||
|
||||
-(void)autoCompleteFoundResults:(AutoCompleteResults*)results
|
||||
{
|
||||
[mResults release];
|
||||
mResults = nil;
|
||||
|
||||
if ([[results matches] count] > 0) {
|
||||
mResults = [results retain];
|
||||
[self dataReady];
|
||||
}
|
||||
else {
|
||||
[mPopupWindow setItems:nil];
|
||||
[self closePopup];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isPopupOpen
|
||||
{
|
||||
return mPopupWindow ? [mPopupWindow isPopupOpen] : NO;
|
||||
}
|
||||
|
||||
- (void)openPopup
|
||||
{
|
||||
// Only open popup if it's not already open.
|
||||
if ([self isPopupOpen])
|
||||
return;
|
||||
|
||||
// Make sure input field is visible before showing popup.
|
||||
GeckoUtils::ScrollElementIntoView(mFocusedInputElement);
|
||||
|
||||
nsIntRect inputIntRect;
|
||||
if (!(GeckoUtils::GetFrameInScreenCoordinates(mFocusedInputElement, &inputIntRect)))
|
||||
return;
|
||||
|
||||
NSRect inputElementFrame = NSMakeRect(inputIntRect.x, inputIntRect.y, inputIntRect.width, inputIntRect.height);
|
||||
|
||||
NSScreen* mainScreen = [[NSScreen screens] firstObject]; // NSArray category method
|
||||
if (!mainScreen)
|
||||
return;
|
||||
|
||||
NSPoint origin = inputElementFrame.origin;
|
||||
float width = NSWidth(inputElementFrame);
|
||||
|
||||
// y-flip and subtract the control height to convert to cocoa coords
|
||||
origin.y = NSMaxY([mainScreen frame]) - inputElementFrame.origin.y - inputElementFrame.size.height;
|
||||
|
||||
// To account for the text box border, shift rectangle position to the right by 2 pixels
|
||||
// and reduce the width by 3 pixels.
|
||||
// TODO: check shift here, still not aligned sometimes.
|
||||
origin.x += 2.0;
|
||||
width -= 3.0;
|
||||
|
||||
[mPopupWindow openPopup:[mBrowserView nativeWindow] withOrigin:origin width:width];
|
||||
|
||||
// Listen for resize events to close the popup.
|
||||
[self addResizeObserver:[mBrowserView nativeWindow]];
|
||||
}
|
||||
|
||||
- (void)closePopup
|
||||
{
|
||||
if (mPopupWindow) {
|
||||
// Deselecting the row prevents a flash when popup is opened and default is selected.
|
||||
[mPopupWindow selectRow:-1];
|
||||
[mPopupWindow closePopup];
|
||||
[self removeResizeObserver:[mBrowserView nativeWindow]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)shiftRowSelectionBy:(int)aRows
|
||||
{
|
||||
int row = [mPopupWindow selectedRow] + aRows;
|
||||
|
||||
// pin result at top row
|
||||
if (row < 0)
|
||||
row = 0;
|
||||
|
||||
// pin result at bottom row
|
||||
int numRows = [mPopupWindow rowCount];
|
||||
if (row >= numRows)
|
||||
row = numRows - 1;
|
||||
|
||||
[mPopupWindow selectRow:row];
|
||||
}
|
||||
|
||||
//
|
||||
// popupSelected
|
||||
//
|
||||
// Called when a new item in the popup window is selected, either by mouse click
|
||||
// or keyboard movement. The form field is set to the value of the selected item
|
||||
// and an autocomplete event is sent for any listeners
|
||||
- (void)popupSelected
|
||||
{
|
||||
if (![self isPopupOpen] || !mResults)
|
||||
return;
|
||||
|
||||
int row = [mPopupWindow selectedRow];
|
||||
if (row < 0)
|
||||
return;
|
||||
|
||||
nsAutoString value;
|
||||
[[mPopupWindow resultForRow:row] assignTo_nsAString:value];
|
||||
mFocusedInputElement->SetValue(value);
|
||||
|
||||
// Auto-fill the input so that untyped letters are selected.
|
||||
nsCOMPtr<nsIDOMNSHTMLInputElement> nsInput = do_QueryInterface(mFocusedInputElement);
|
||||
if (nsInput) {
|
||||
int searchLength = [[mResults searchString] length];
|
||||
nsInput->SetSelectionStart((PRInt32)searchLength);
|
||||
}
|
||||
|
||||
// Send an autocomplete DOM event any time auto fill is done.
|
||||
[self filledAutoCompleteFieldText];
|
||||
}
|
||||
|
||||
- (void)startSearch:(NSString*)searchString;
|
||||
{
|
||||
// Check if password autocomplete is enabled.
|
||||
// Form history autocomplete can be added here.
|
||||
if (mUsernameFillEnabled) {
|
||||
[mKeychainSession startAutoCompleteWithSearch:searchString
|
||||
previousResults:mResults
|
||||
listener:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dataReady
|
||||
{
|
||||
[mPopupWindow setItems:[mResults matches]];
|
||||
|
||||
// Open the popup if more than one result is returned.
|
||||
// Also require mCompleteResult since it prevents a backspace from opening popup
|
||||
// even though we want to search on a backspace to get the larger result set.
|
||||
if ([mPopupWindow rowCount] > 1 && mCompleteResult)
|
||||
[self openPopup];
|
||||
else
|
||||
[self closePopup];
|
||||
|
||||
// Prevents backspace from auto-completing the result we just erased.
|
||||
if (mCompleteResult)
|
||||
[self autoCompleteFieldText];
|
||||
}
|
||||
|
||||
//
|
||||
// autoCompleteFieldText
|
||||
//
|
||||
// Called when FormFillController should fill the text field with the default
|
||||
// autocomplete result.
|
||||
- (void)autoCompleteFieldText
|
||||
{
|
||||
if (!mResults)
|
||||
return;
|
||||
|
||||
NSArray* matches = [mResults matches];
|
||||
|
||||
// Select the default if it's available.
|
||||
// Otherwise, select the first username in the list.
|
||||
int defaultIndex = [mResults defaultIndex];
|
||||
|
||||
nsAutoString value;
|
||||
[[matches objectAtIndex:defaultIndex] assignTo_nsAString:value];
|
||||
mFocusedInputElement->SetValue(value);
|
||||
|
||||
// Auto-fill the input such that untyped letters are selected.
|
||||
nsCOMPtr<nsIDOMNSHTMLInputElement> nsInput = do_QueryInterface(mFocusedInputElement);
|
||||
if (nsInput) {
|
||||
int searchLength = [[mResults searchString] length];
|
||||
nsInput->SetSelectionStart((PRInt32)searchLength);
|
||||
}
|
||||
|
||||
// Select the default entry in the popup.
|
||||
if ([self isPopupOpen])
|
||||
[mPopupWindow selectRow:defaultIndex];
|
||||
|
||||
// Send a DOM event any time auto fill is done.
|
||||
[self filledAutoCompleteFieldText];
|
||||
}
|
||||
|
||||
//
|
||||
// filledAutoCompleteFieldText
|
||||
//
|
||||
// Called whenever the form element is filled. It sends a DOM event
|
||||
// "DOMAutoComplete" that can be listened for, e.g., to fill a password
|
||||
// when a username element is completed.
|
||||
//
|
||||
- (void)filledAutoCompleteFieldText
|
||||
{
|
||||
if (!mFocusedInputElement)
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIDOMDocument> domDoc;
|
||||
mFocusedInputElement->GetOwnerDocument(getter_AddRefs(domDoc));
|
||||
|
||||
nsCOMPtr<nsIDOMDocumentEvent> doc = do_QueryInterface(domDoc);
|
||||
if (!doc)
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIDOMEvent> event;
|
||||
doc->CreateEvent(NS_LITERAL_STRING("Events"), getter_AddRefs(event));
|
||||
nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
|
||||
if (!privateEvent)
|
||||
return;
|
||||
|
||||
event->InitEvent(NS_LITERAL_STRING("DOMAutoComplete"), PR_TRUE, PR_TRUE);
|
||||
|
||||
privateEvent->SetTrusted(PR_TRUE);
|
||||
|
||||
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(mFocusedInputElement);
|
||||
|
||||
PRBool defaultActionEnabled;
|
||||
target->DispatchEvent(event, &defaultActionEnabled);
|
||||
}
|
||||
|
||||
//
|
||||
// focus
|
||||
//
|
||||
// When a new element is selected, check whether we allow autocomplete.
|
||||
// If autocomplete is allowed, start controlling the input to that element.
|
||||
//
|
||||
- (void)focus:(nsIDOMEvent*)aEvent
|
||||
{
|
||||
nsCOMPtr<nsIDOMEventTarget> target;
|
||||
aEvent->GetTarget(getter_AddRefs(target));
|
||||
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> inputElement = do_QueryInterface(target);
|
||||
if (!inputElement)
|
||||
return;
|
||||
|
||||
nsAutoString type;
|
||||
inputElement->GetType(type);
|
||||
if (!type.LowerCaseEqualsLiteral("text"))
|
||||
return;
|
||||
|
||||
PRBool isReadOnly = PR_FALSE;
|
||||
if (NS_FAILED(inputElement->GetReadOnly(&isReadOnly)) || isReadOnly)
|
||||
return;
|
||||
|
||||
PRBool autoCompleteOverride = [[PreferenceManager sharedInstance] getBooleanPref:"wallet.crypto.autocompleteoverride" withSuccess:NULL];
|
||||
|
||||
if (!autoCompleteOverride) {
|
||||
nsAutoString autocomplete;
|
||||
inputElement->GetAttribute(NS_LITERAL_STRING("autocomplete"), autocomplete);
|
||||
|
||||
if (autocomplete.EqualsIgnoreCase("off"))
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIDOMHTMLFormElement> form;
|
||||
inputElement->GetForm(getter_AddRefs(form));
|
||||
if (form) {
|
||||
form->GetAttribute(NS_LITERAL_STRING("autocomplete"), autocomplete);
|
||||
if (autocomplete.EqualsIgnoreCase("off"))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[self startControllingInputElement:inputElement];
|
||||
}
|
||||
|
||||
//
|
||||
// blur
|
||||
//
|
||||
// Anytime focus moves from an element, stop autocompleting it until next
|
||||
// input element is focused.
|
||||
//
|
||||
- (void)blur:(nsIDOMEvent*)aEvent
|
||||
{
|
||||
if (mFocusedInputElement)
|
||||
[self stopControllingInputElement];
|
||||
}
|
||||
|
||||
//
|
||||
// unload
|
||||
//
|
||||
// Stop autocompleting on a page if it is unloaded.
|
||||
//
|
||||
- (void)unload:(nsIDOMEvent*)aEvent
|
||||
{
|
||||
if (mFocusedInputElement) {
|
||||
nsCOMPtr<nsIDOMEventTarget> target;
|
||||
aEvent->GetTarget(getter_AddRefs(target));
|
||||
|
||||
nsCOMPtr<nsIDOMDocument> eventDoc = do_QueryInterface(target);
|
||||
nsCOMPtr<nsIDOMDocument> inputDoc;
|
||||
mFocusedInputElement->GetOwnerDocument(getter_AddRefs(inputDoc));
|
||||
|
||||
if (eventDoc == inputDoc)
|
||||
[self stopControllingInputElement];
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// submit
|
||||
//
|
||||
// If a form is submitted, stop autocompleting.
|
||||
//
|
||||
- (void)submit:(nsIDOMEvent*)aEvent
|
||||
{
|
||||
if (mFocusedInputElement)
|
||||
[self stopControllingInputElement];
|
||||
}
|
||||
|
||||
//
|
||||
// input
|
||||
//
|
||||
// If an HTML input box is being controlled, do a search when input occurs.
|
||||
//
|
||||
- (void)input:(nsIDOMEvent*)aEvent
|
||||
{
|
||||
nsCOMPtr<nsIDOMEventTarget> target;
|
||||
aEvent->GetTarget(getter_AddRefs(target));
|
||||
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(target);
|
||||
|
||||
if (input && mFocusedInputElement == input) {
|
||||
nsAutoString value;
|
||||
mFocusedInputElement->GetValue(value);
|
||||
[self startSearch:[NSString stringWith_nsAString:value]];
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// keyPress
|
||||
//
|
||||
// This is triggered when a key is pressed but before an 'input' is triggered.
|
||||
// It also handles non-input keys like arrow keys.
|
||||
//
|
||||
- (void)keyPress:(nsIDOMEvent*)aEvent
|
||||
{
|
||||
if (!mFocusedInputElement)
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
|
||||
if (!keyEvent)
|
||||
return;
|
||||
|
||||
// By default, allow keystroke to continue to the next listener.
|
||||
BOOL cancel = NO;
|
||||
|
||||
// By default, autocomplete on keystrokes that later trigger 'input' events.
|
||||
mCompleteResult = YES;
|
||||
|
||||
PRUint32 k;
|
||||
keyEvent->GetKeyCode(&k);
|
||||
switch (k) {
|
||||
case nsIDOMKeyEvent::DOM_VK_UP:
|
||||
case nsIDOMKeyEvent::DOM_VK_DOWN:
|
||||
case nsIDOMKeyEvent::DOM_VK_PAGE_UP:
|
||||
case nsIDOMKeyEvent::DOM_VK_PAGE_DOWN:
|
||||
cancel = [self handleKeyNavigation:k];
|
||||
break;
|
||||
case nsIDOMKeyEvent::DOM_VK_ESCAPE:
|
||||
case nsIDOMKeyEvent::DOM_VK_RETURN:
|
||||
cancel = [self isPopupOpen];
|
||||
[self closePopup];
|
||||
break;
|
||||
case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
|
||||
case nsIDOMKeyEvent::DOM_VK_DELETE:
|
||||
// Don't allow autocomplete of 'input' event if it's due to a back space or
|
||||
// delete.
|
||||
mCompleteResult = NO;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cancel) {
|
||||
aEvent->StopPropagation();
|
||||
aEvent->PreventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// handleKeyNavigation
|
||||
//
|
||||
// Handles key events that are for navigation of the popup window.
|
||||
// Should return YES if the event should be cancelled and not propagated.
|
||||
//
|
||||
- (BOOL)handleKeyNavigation:(int)aKey
|
||||
{
|
||||
switch (aKey) {
|
||||
case nsIDOMKeyEvent::DOM_VK_UP:
|
||||
if ([self isPopupOpen]) {
|
||||
[self shiftRowSelectionBy:-1];
|
||||
[self popupSelected];
|
||||
return YES;
|
||||
}
|
||||
break;
|
||||
case nsIDOMKeyEvent::DOM_VK_DOWN:
|
||||
if ([self isPopupOpen]) {
|
||||
[self shiftRowSelectionBy:1];
|
||||
[self popupSelected];
|
||||
return YES;
|
||||
}
|
||||
else if ([self IsCaretAtEndOfLine]) {
|
||||
nsAutoString value;
|
||||
mFocusedInputElement->GetValue(value);
|
||||
[self startSearch:[NSString stringWith_nsAString:value]];
|
||||
return YES;
|
||||
}
|
||||
break;
|
||||
case nsIDOMKeyEvent::DOM_VK_PAGE_UP:
|
||||
if ([self isPopupOpen]) {
|
||||
[self shiftRowSelectionBy:-kFormFillMaxRows];
|
||||
[self popupSelected];
|
||||
return YES;
|
||||
}
|
||||
break;
|
||||
case nsIDOMKeyEvent::DOM_VK_PAGE_DOWN:
|
||||
if ([self isPopupOpen]) {
|
||||
[self shiftRowSelectionBy:kFormFillMaxRows];
|
||||
[self popupSelected];
|
||||
return YES;
|
||||
}
|
||||
else {
|
||||
nsAutoString value;
|
||||
mFocusedInputElement->GetValue(value);
|
||||
[self startSearch:[NSString stringWith_nsAString:value]];
|
||||
return YES;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)IsCaretAtEndOfLine
|
||||
{
|
||||
if (!mFocusedInputElement)
|
||||
return NO;
|
||||
|
||||
nsCOMPtr<nsIDOMNSHTMLInputElement> nsInput = do_QueryInterface(mFocusedInputElement);
|
||||
if (!nsInput)
|
||||
return NO;
|
||||
|
||||
PRInt32 selectStart;
|
||||
nsInput->GetSelectionStart(&selectStart);
|
||||
|
||||
PRInt32 textLength;
|
||||
nsInput->GetTextLength(&textLength);
|
||||
|
||||
return (selectStart == textLength);
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,83 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Camino code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Bryan Atwood
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Bryan Atwood <bryan.h.atwood@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@class FormFillController;
|
||||
|
||||
//
|
||||
// FormFillPopupWindow
|
||||
//
|
||||
// The popup window needs to look like a "key" (activated) window even thought it's
|
||||
// a child window. This subclass overrides |isKeyWindow| to return YES so that it is
|
||||
// able to be a key window (and have activated scrollbars, etc) but not steal focus.
|
||||
//
|
||||
@interface FormFillPopupWindow : NSPanel
|
||||
@end
|
||||
|
||||
// FormFillPopup
|
||||
//
|
||||
// Manages the display of the popup window and receives data from the FormFillController.
|
||||
//
|
||||
@interface FormFillPopup : NSObject
|
||||
{
|
||||
FormFillPopupWindow* mPopupWin; // strong
|
||||
NSArray* mItems; // strong
|
||||
|
||||
NSTableView* mTableView; // weak
|
||||
FormFillController* mController; // weak
|
||||
}
|
||||
|
||||
- (void)attachToController:(FormFillController*)controller;
|
||||
|
||||
// openPopup expects an origin point in Cocoa coordinates and a width that
|
||||
// should be equal to the text box.
|
||||
- (void)openPopup:(NSWindow*)browserWindow withOrigin:(NSPoint)origin width:(float)width;
|
||||
- (void)resizePopup;
|
||||
- (void)closePopup;
|
||||
- (BOOL)isPopupOpen;
|
||||
|
||||
- (int)visibleRows;
|
||||
- (int)rowCount;
|
||||
- (void)selectRow:(int)index;
|
||||
- (int)selectedRow;
|
||||
|
||||
- (void)setItems:(NSArray*)items;
|
||||
- (NSString*)resultForRow:(int)index;
|
||||
|
||||
@end
|
|
@ -0,0 +1,230 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Camino code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Bryan Atwood
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Bryan Atwood <bryan.h.atwood@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#import "FormFillPopup.h"
|
||||
#import "FormFillController.h"
|
||||
|
||||
@implementation FormFillPopupWindow
|
||||
|
||||
- (BOOL)isKeyWindow
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface FormFillPopup(Private)
|
||||
|
||||
- (void)onRowClicked:(NSNotification *)aNote;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FormFillPopup
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
// Construct and configure the popup window.
|
||||
mPopupWin = [[FormFillPopupWindow alloc] initWithContentRect:NSMakeRect(0,0,0,0)
|
||||
styleMask:NSNonactivatingPanelMask
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:NO];
|
||||
|
||||
[mPopupWin setReleasedWhenClosed:NO];
|
||||
[mPopupWin setHasShadow:YES];
|
||||
[mPopupWin setAlphaValue:0.9];
|
||||
|
||||
// Construct and configure the view.
|
||||
mTableView = [[[NSTableView alloc] initWithFrame:NSMakeRect(0,0,0,0)] autorelease];
|
||||
[mTableView setIntercellSpacing:NSMakeSize(1, 2)];
|
||||
[mTableView setTarget:self];
|
||||
[mTableView setAction:@selector(onRowClicked:)];
|
||||
|
||||
// Create the text column (only one).
|
||||
NSTableColumn* column = [[[NSTableColumn alloc] initWithIdentifier:@"usernames"] autorelease];
|
||||
[mTableView addTableColumn:column];
|
||||
|
||||
// Hide the table header.
|
||||
[mTableView setHeaderView:nil];
|
||||
|
||||
[mTableView setDataSource:self];
|
||||
|
||||
NSScrollView *scrollView = [[[NSScrollView alloc] initWithFrame:NSMakeRect(0,0,0,0)] autorelease];
|
||||
[scrollView setHasVerticalScroller:YES];
|
||||
[scrollView setAutohidesScrollers:YES];
|
||||
[[scrollView verticalScroller] setControlSize:NSSmallControlSize];
|
||||
|
||||
[scrollView setDocumentView:mTableView];
|
||||
|
||||
[mPopupWin setContentView:scrollView];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[mPopupWin release];
|
||||
[mItems release];
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)attachToController:(FormFillController*)controller
|
||||
{
|
||||
mController = controller;
|
||||
}
|
||||
|
||||
- (BOOL)isPopupOpen
|
||||
{
|
||||
return [mPopupWin isVisible];
|
||||
}
|
||||
|
||||
- (void)openPopup:(NSWindow*)browserWindow withOrigin:(NSPoint)origin width:(float)width
|
||||
{
|
||||
// Set the size of the popup window.
|
||||
NSRect tableViewFrame = [mTableView frame];
|
||||
tableViewFrame.size.width = width;
|
||||
[mTableView setFrame:tableViewFrame];
|
||||
|
||||
// Size the panel correctly.
|
||||
tableViewFrame.size.height = (int)([mTableView rowHeight] + [mTableView intercellSpacing].height) * [self visibleRows];
|
||||
[mPopupWin setContentSize:tableViewFrame.size];
|
||||
[mPopupWin setFrameTopLeftPoint:origin];
|
||||
|
||||
// Show the popup.
|
||||
if (![mPopupWin isVisible]) {
|
||||
[browserWindow addChildWindow:mPopupWin ordered:NSWindowAbove];
|
||||
[mPopupWin orderFront:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resizePopup
|
||||
{
|
||||
// Don't resize if popup isn't visible.
|
||||
if (![mPopupWin isVisible])
|
||||
return;
|
||||
|
||||
if ([self visibleRows] == 0) {
|
||||
[self closePopup];
|
||||
return;
|
||||
}
|
||||
|
||||
NSRect popupWinFrame = [mPopupWin frame];
|
||||
int tableHeight = (int)([mTableView rowHeight] + [mTableView intercellSpacing].height) * [self visibleRows];
|
||||
|
||||
popupWinFrame.origin.y += NSHeight(popupWinFrame) - tableHeight;
|
||||
popupWinFrame.size.height = tableHeight;
|
||||
|
||||
[mPopupWin setFrame:popupWinFrame display:YES];
|
||||
}
|
||||
|
||||
- (void)closePopup
|
||||
{
|
||||
// We can get -closePopup even if we didn't show it.
|
||||
if ([mPopupWin isVisible]) {
|
||||
[[mPopupWin parentWindow] removeChildWindow:mPopupWin];
|
||||
[mPopupWin orderOut:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onRowClicked:(NSNotification *)aNote
|
||||
{
|
||||
[mController popupSelected];
|
||||
[self closePopup];
|
||||
}
|
||||
|
||||
- (int)visibleRows
|
||||
{
|
||||
int minRows = [self rowCount];
|
||||
return (minRows < kFormFillMaxRows) ? minRows : kFormFillMaxRows;
|
||||
}
|
||||
|
||||
- (int)rowCount
|
||||
{
|
||||
if (!mItems)
|
||||
return 0;
|
||||
|
||||
return [mItems count];
|
||||
}
|
||||
|
||||
- (void)selectRow:(int)index
|
||||
{
|
||||
if (index == -1)
|
||||
[mTableView deselectAll:self];
|
||||
else {
|
||||
[mTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:index] byExtendingSelection:NO];
|
||||
[mTableView scrollRowToVisible:index];
|
||||
}
|
||||
}
|
||||
|
||||
- (int)selectedRow
|
||||
{
|
||||
return [mTableView selectedRow];
|
||||
}
|
||||
|
||||
- (NSString*)resultForRow:(int)index
|
||||
{
|
||||
return [mItems objectAtIndex:index];
|
||||
}
|
||||
|
||||
- (void)setItems:(NSArray*)items
|
||||
{
|
||||
if (items != mItems) {
|
||||
[mItems release];
|
||||
mItems = [items retain];
|
||||
}
|
||||
|
||||
// Update the view any time we get new data
|
||||
[mTableView noteNumberOfRowsChanged];
|
||||
[self resizePopup];
|
||||
}
|
||||
|
||||
// methods for table view interaction
|
||||
-(int)numberOfRowsInTableView:(NSTableView*)aTableView
|
||||
{
|
||||
return [self rowCount];
|
||||
}
|
||||
|
||||
-(id)tableView:(NSTableView*)aTableView objectValueForTableColumn:(NSTableColumn*)aTableColumn row:(int)aRowIndex
|
||||
{
|
||||
return [self resultForRow:aRowIndex];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,85 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Camino code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Bryan Atwood
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Bryan Atwood <bryan.h.atwood@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "AutoCompleteUtils.h"
|
||||
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
class nsIDOMHTMLInputElement;
|
||||
|
||||
//
|
||||
// KeychainAutoCompleteDOMListener
|
||||
//
|
||||
// Listens for password fill requests from the FormFillController when a
|
||||
// username is entered.
|
||||
class KeychainAutoCompleteDOMListener : public nsIDOMEventListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
|
||||
void SetElements(nsIDOMHTMLInputElement* usernameElement,
|
||||
nsIDOMHTMLInputElement* passwordElement);
|
||||
|
||||
protected:
|
||||
// Fills the password with the keychain data from the cached username input
|
||||
// element.
|
||||
void FillPassword();
|
||||
|
||||
// Pointers to the login input elements so that the form doesn't need to
|
||||
// be searched when the same input element is attached again.
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> mUsernameElement; // strong
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> mPasswordElement; // strong
|
||||
};
|
||||
|
||||
@interface KeychainAutoCompleteSession : NSObject<AutoCompleteSession>
|
||||
{
|
||||
KeychainAutoCompleteDOMListener* mDOMListener; // strong
|
||||
NSMutableArray* mUsernames; // strong
|
||||
NSString* mDefaultUser; // strong
|
||||
|
||||
// Cache the username input element so that we don't reread the keychain for
|
||||
// the same element.
|
||||
nsIDOMHTMLInputElement* mUsernameElement; // weak
|
||||
}
|
||||
|
||||
- (BOOL)attachToInput:(nsIDOMHTMLInputElement*)usernameElement;
|
||||
|
||||
@end
|
|
@ -0,0 +1,369 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Camino code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Bryan Atwood
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Bryan Atwood <bryan.h.atwood@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
#import "NSString+Gecko.h"
|
||||
|
||||
#import "KeychainAutoCompleteSession.h"
|
||||
#import "KeychainItem.h"
|
||||
#import "KeychainService.h"
|
||||
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIDOMEvent.h"
|
||||
#include "nsIDOMEventTarget.h";
|
||||
#include "nsIDOMHTMLInputElement.h";
|
||||
|
||||
static BOOL FindPasswordField(nsIDOMHTMLInputElement* inUsername, nsIDOMHTMLInputElement** outPassword);
|
||||
|
||||
static void GetFormInfoForInput(nsIDOMHTMLInputElement* aElement,
|
||||
NSString** host,
|
||||
NSString** asciiHost,
|
||||
PRInt32* port,
|
||||
NSString** scheme);
|
||||
|
||||
NS_IMPL_ISUPPORTS1(KeychainAutoCompleteDOMListener, nsIDOMEventListener)
|
||||
|
||||
NS_IMETHODIMP KeychainAutoCompleteDOMListener::HandleEvent(nsIDOMEvent *aEvent)
|
||||
{
|
||||
nsAutoString type;
|
||||
aEvent->GetType(type);
|
||||
if (type.EqualsLiteral("DOMAutoComplete")) {
|
||||
// Don't fill password if autofilling has been disabled.
|
||||
if(![[KeychainService instance] formPasswordFillIsEnabled])
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIDOMEventTarget> target;
|
||||
if (NS_FAILED(aEvent->GetTarget(getter_AddRefs(target))))
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> userElement = do_QueryInterface(target);
|
||||
if (userElement && userElement == mUsernameElement)
|
||||
FillPassword();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void KeychainAutoCompleteDOMListener::SetElements(nsIDOMHTMLInputElement* usernameElement,
|
||||
nsIDOMHTMLInputElement* passwordElement)
|
||||
{
|
||||
mUsernameElement = usernameElement;
|
||||
mPasswordElement = passwordElement;
|
||||
}
|
||||
|
||||
void KeychainAutoCompleteDOMListener::FillPassword()
|
||||
{
|
||||
if (!mUsernameElement || !mPasswordElement)
|
||||
return;
|
||||
|
||||
// Get the entered username.
|
||||
nsAutoString nsUsername;
|
||||
mUsernameElement->GetValue(nsUsername);
|
||||
NSString* username = [NSString stringWith_nsAString:nsUsername];
|
||||
|
||||
NSString* host;
|
||||
NSString* asciiHost;
|
||||
PRInt32 port;
|
||||
NSString* scheme;
|
||||
|
||||
GetFormInfoForInput(mUsernameElement, &host, &asciiHost, &port, &scheme);
|
||||
|
||||
KeychainService* keychain = [KeychainService instance];
|
||||
KeychainItem* keychainEntry = [keychain findWebFormKeychainEntryForUsername:username
|
||||
forHost:host
|
||||
port:port
|
||||
scheme:scheme];
|
||||
if (!keychainEntry && ![asciiHost isEqualToString:host]) {
|
||||
keychainEntry = [keychain findWebFormKeychainEntryForUsername:username
|
||||
forHost:asciiHost
|
||||
port:port
|
||||
scheme:scheme];
|
||||
}
|
||||
if (!keychainEntry)
|
||||
return;
|
||||
|
||||
nsAutoString password;
|
||||
[[keychainEntry password] assignTo_nsAString:password];
|
||||
|
||||
if (password.IsEmpty())
|
||||
return;
|
||||
|
||||
mPasswordElement->SetValue(password);
|
||||
|
||||
// Now that we have actually filled the password, cache the keychain entry.
|
||||
nsCOMPtr<nsIDOMDocument> domDoc;
|
||||
nsresult rv = mUsernameElement->GetOwnerDocument(getter_AddRefs(domDoc));
|
||||
if (NS_FAILED(rv) || !domDoc)
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIDocument> doc (do_QueryInterface(domDoc));
|
||||
if (!doc)
|
||||
return;
|
||||
|
||||
nsIURI* docURL = doc->GetDocumentURI();
|
||||
if (!docURL)
|
||||
return;
|
||||
|
||||
nsCAutoString uriCAString;
|
||||
rv = docURL->GetSpec(uriCAString);
|
||||
if (NS_FAILED(rv))
|
||||
return;
|
||||
|
||||
NSString* uri = [NSString stringWithCString:uriCAString.get()];
|
||||
if (uri)
|
||||
[keychain cacheKeychainEntry:keychainEntry forKey:uri];
|
||||
}
|
||||
|
||||
@implementation KeychainAutoCompleteSession
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
mUsernames = [[NSMutableArray alloc] init];
|
||||
mDOMListener = new KeychainAutoCompleteDOMListener();
|
||||
NS_IF_ADDREF(mDOMListener);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[mUsernames release];
|
||||
[mDefaultUser release];
|
||||
|
||||
NS_IF_RELEASE(mDOMListener);
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
//
|
||||
// attachToInput
|
||||
//
|
||||
// Sets the session to cache the usernames for a username element
|
||||
// Returns NO if this element is not a valid username element and should
|
||||
// be remembered by the calling function so that autocomplete requests are not sent.
|
||||
- (BOOL)attachToInput:(nsIDOMHTMLInputElement*)usernameElement
|
||||
{
|
||||
// Don't listen if element is empty or password fill is disabled.
|
||||
KeychainService* keychain = [KeychainService instance];
|
||||
if (!usernameElement || ![keychain formPasswordFillIsEnabled])
|
||||
return NO;
|
||||
|
||||
if (mUsernameElement == usernameElement)
|
||||
return YES;
|
||||
|
||||
// If there is no corresponding password, this element isn't listened to.
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> passwordElement;
|
||||
if (!FindPasswordField(usernameElement, getter_AddRefs(passwordElement)))
|
||||
return NO;
|
||||
|
||||
// Get the host information so we can get the keychain entries below.
|
||||
NSString* host;
|
||||
NSString* asciiHost;
|
||||
PRInt32 port;
|
||||
NSString* scheme;
|
||||
|
||||
GetFormInfoForInput(usernameElement, &host, &asciiHost, &port, &scheme);
|
||||
|
||||
// If session is not cached, default to empty session.
|
||||
[mUsernames removeAllObjects];
|
||||
[mDefaultUser release];
|
||||
mDefaultUser = nil;
|
||||
mUsernameElement = usernameElement;
|
||||
|
||||
// Cache all of the usernames in the object so that the keychain
|
||||
// doesn't have to be read again. This should be a faster way to search and sort.
|
||||
NSMutableArray* keychainEntries =
|
||||
[NSMutableArray arrayWithArray:[keychain allWebFormKeychainItemsForHost:host port:port scheme:scheme]];
|
||||
|
||||
// And add the keychain items for the punycode host.
|
||||
if (![asciiHost isEqualToString:host])
|
||||
[keychainEntries addObjectsFromArray:[keychain allWebFormKeychainItemsForHost:asciiHost port:port scheme:scheme]];
|
||||
|
||||
NSEnumerator* keychainEnumerator = [keychainEntries objectEnumerator];
|
||||
KeychainItem* item;
|
||||
while ((item = [keychainEnumerator nextObject])) {
|
||||
// Only add a username once since there may be duplicates.
|
||||
NSString* username = [item username];
|
||||
if (![mUsernames containsObject:username])
|
||||
[mUsernames addObject:username];
|
||||
}
|
||||
|
||||
// Get the default keychain and cache the username.
|
||||
item = [keychain defaultFromKeychainItems:keychainEntries];
|
||||
if (item)
|
||||
mDefaultUser = [[item username] copy];
|
||||
|
||||
// Sort usernames alphabetically.
|
||||
[mUsernames sortUsingSelector:@selector(caseInsensitiveCompare:)];
|
||||
|
||||
// Listen for DOM form fill events so that the password can
|
||||
// be autofilled when a username is entered.
|
||||
mDOMListener->SetElements(usernameElement, passwordElement);
|
||||
nsCOMPtr<nsIDOMEventTarget> targ = do_QueryInterface(usernameElement);
|
||||
targ->AddEventListener(NS_LITERAL_STRING("DOMAutoComplete"), mDOMListener, PR_FALSE);
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
//
|
||||
// startAutoCompleteWithSearch
|
||||
//
|
||||
// Function that takes a search string and returns all usernames that match it.
|
||||
// Assume that if this function is called that formfilling is enabled. If it's
|
||||
// not enabled, don't create the session in the first place.
|
||||
//
|
||||
-(void)startAutoCompleteWithSearch:(NSString*)searchString
|
||||
previousResults:(AutoCompleteResults*)previousSearchResults
|
||||
listener:(id<AutoCompleteListener>)listener
|
||||
{
|
||||
AutoCompleteResults* results = [[AutoCompleteResults alloc] init];
|
||||
[results setSearchString:searchString];
|
||||
|
||||
// determine if we can skip searching the whole list of usernames
|
||||
// and only search through the previous search results.
|
||||
bool searchPrevious = (previousSearchResults) ? [searchString hasPrefix:[previousSearchResults searchString]] : NO;
|
||||
|
||||
NSEnumerator* usernameEnumerator;
|
||||
if (searchPrevious)
|
||||
usernameEnumerator = [[previousSearchResults matches] objectEnumerator];
|
||||
else
|
||||
usernameEnumerator = [mUsernames objectEnumerator];
|
||||
|
||||
NSMutableArray* resultMatches = [[NSMutableArray alloc] init];
|
||||
|
||||
NSString* username;
|
||||
bool searchStringIsEmpty = (searchString && [searchString length] > 0) ? NO : YES;
|
||||
while ((username = [usernameEnumerator nextObject])) {
|
||||
if (searchStringIsEmpty || [username hasPrefix:searchString]) {
|
||||
[resultMatches addObject:username];
|
||||
|
||||
// Check for the default.
|
||||
if ([username isEqualToString:mDefaultUser])
|
||||
[results setDefaultIndex:([resultMatches count]-1)];
|
||||
}
|
||||
}
|
||||
|
||||
[results setMatches:resultMatches];
|
||||
[resultMatches release];
|
||||
|
||||
[listener autoCompleteFoundResults:results];
|
||||
[results release];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
//
|
||||
// FindPasswordField
|
||||
//
|
||||
// Given an HTML input element for username entry, return the corresponding password field
|
||||
// or set the password input element to nsnull if there isn't one. Return YES on success.
|
||||
//
|
||||
BOOL FindPasswordField(nsIDOMHTMLInputElement* inUsername, nsIDOMHTMLInputElement** outPassword)
|
||||
{
|
||||
if (!inUsername)
|
||||
return NO;
|
||||
|
||||
// Get the form node for scanning username/password pair.
|
||||
nsCOMPtr<nsIDOMHTMLFormElement> formElement;
|
||||
nsresult rv = inUsername->GetForm(getter_AddRefs(formElement));
|
||||
if (NS_FAILED(rv) || !formElement)
|
||||
return NO;
|
||||
|
||||
// Check if the input element is a username field with a password field.
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> usernameElement;
|
||||
rv = FindUsernamePasswordFields(formElement,
|
||||
getter_AddRefs(usernameElement),
|
||||
outPassword,
|
||||
PR_TRUE);
|
||||
// Return a null password if the password was not found or if the username
|
||||
// field in the form is not the same as the input.
|
||||
if (NS_FAILED(rv) || usernameElement != inUsername)
|
||||
return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
static void GetFormInfoForInput(nsIDOMHTMLInputElement* aElement,
|
||||
NSString** host,
|
||||
NSString** asciiHost,
|
||||
PRInt32* port,
|
||||
NSString** scheme)
|
||||
{
|
||||
if (!aElement)
|
||||
return;
|
||||
*host = nil;
|
||||
*asciiHost = nil;
|
||||
*port = -1;
|
||||
*scheme = nil;
|
||||
|
||||
nsCOMPtr<nsIDOMDocument> domDoc;
|
||||
nsresult rv = aElement->GetOwnerDocument(getter_AddRefs(domDoc));
|
||||
if (NS_FAILED(rv))
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
|
||||
if (!doc)
|
||||
return;
|
||||
|
||||
nsIURI* docURL = doc->GetDocumentURI();
|
||||
if (!docURL)
|
||||
return;
|
||||
|
||||
nsCAutoString hostCAString;
|
||||
rv = docURL->GetHost(hostCAString);
|
||||
if (NS_FAILED(rv))
|
||||
return;
|
||||
|
||||
*host = [NSString stringWithCString:hostCAString.get()];
|
||||
|
||||
// Get the host in punycode for keychain use.
|
||||
nsCAutoString asciiHostCAString;
|
||||
rv = docURL->GetAsciiHost(asciiHostCAString);
|
||||
*asciiHost = NS_SUCCEEDED(rv) ? [NSString stringWithCString:asciiHostCAString.get()]
|
||||
: *host;
|
||||
|
||||
docURL->GetPort(port);
|
||||
|
||||
nsCAutoString schemeCAString;
|
||||
rv = docURL->GetScheme(schemeCAString);
|
||||
if (NS_FAILED(rv))
|
||||
return;
|
||||
|
||||
*scheme = [NSString stringWithCString:schemeCAString.get()];
|
||||
}
|
|
@ -58,6 +58,7 @@
|
|||
|
||||
// Returns the first keychain item matching the given criteria.
|
||||
+ (KeychainItem*)keychainItemForHost:(NSString*)host
|
||||
username:(NSString*)username
|
||||
port:(UInt16)port
|
||||
protocol:(SecProtocolType)protocol
|
||||
authenticationType:(SecAuthenticationType)authType;
|
||||
|
|
|
@ -49,14 +49,18 @@
|
|||
@implementation KeychainItem
|
||||
|
||||
+ (KeychainItem*)keychainItemForHost:(NSString*)host
|
||||
username:(NSString*)username
|
||||
port:(UInt16)port
|
||||
protocol:(SecProtocolType)protocol
|
||||
authenticationType:(SecAuthenticationType)authType
|
||||
{
|
||||
SecKeychainItemRef itemRef;
|
||||
const char* serverName = host ? [host UTF8String] : NULL;
|
||||
const char* serverName = [host UTF8String];
|
||||
UInt32 serverNameLength = serverName ? strlen(serverName) : 0;
|
||||
OSStatus result = SecKeychainFindInternetPassword(NULL, serverNameLength, serverName, 0, NULL, 0, NULL,
|
||||
const char* accountName = [username UTF8String];
|
||||
UInt32 accountNameLength = accountName ? strlen(accountName) : 0;
|
||||
OSStatus result = SecKeychainFindInternetPassword(NULL, serverNameLength, serverName,
|
||||
0, NULL, accountNameLength, accountName,
|
||||
0, NULL, port, protocol, authType,
|
||||
NULL, NULL, &itemRef);
|
||||
if (result != noErr)
|
||||
|
|
|
@ -48,6 +48,8 @@
|
|||
#include "nsIAuthPromptWrapper.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIFormSubmitObserver.h"
|
||||
#include "nsIDOMHTMLFormElement.h"
|
||||
#include "nsIDOMHTMLInputElement.h"
|
||||
|
||||
class nsIPrefBranch;
|
||||
|
||||
|
@ -56,6 +58,11 @@ enum KeychainPromptResult { kSave, kDontRemember, kNeverRemember } ;
|
|||
@class CHBrowserView;
|
||||
@class KeychainItem;
|
||||
|
||||
nsresult FindUsernamePasswordFields(nsIDOMHTMLFormElement* inFormElement,
|
||||
nsIDOMHTMLInputElement** outUsername,
|
||||
nsIDOMHTMLInputElement** outPassword,
|
||||
PRBool inStopWhenFound);
|
||||
|
||||
@interface KeychainService : NSObject
|
||||
{
|
||||
IBOutlet id mConfirmFillPasswordPanel;
|
||||
|
@ -84,21 +91,35 @@ enum KeychainPromptResult { kSave, kDontRemember, kNeverRemember } ;
|
|||
inWindow:(NSWindow*)window;
|
||||
- (BOOL)confirmFillPassword:(NSWindow*)parent;
|
||||
|
||||
- (KeychainItem*)findKeychainEntryForHost:(NSString*)host
|
||||
port:(PRInt32)port
|
||||
scheme:(NSString*)scheme
|
||||
securityDomain:(NSString*)securityDomain
|
||||
isForm:(BOOL)isForm;
|
||||
- (void)storeUsername:(NSString*)username
|
||||
password:(NSString*)password
|
||||
forHost:(NSString*)host
|
||||
securityDomain:(NSString*)host
|
||||
port:(PRInt32)port
|
||||
scheme:(NSString*)scheme
|
||||
isForm:(BOOL)isForm;
|
||||
- (KeychainItem*)updateKeychainEntry:(KeychainItem*)keychainItem
|
||||
withUsername:(NSString*)username
|
||||
password:(NSString*)password;
|
||||
- (KeychainItem*)findWebFormKeychainEntryForUsername:(NSString*)username
|
||||
forHost:(NSString*)host
|
||||
port:(PRInt32)port
|
||||
scheme:(NSString*)scheme;
|
||||
- (KeychainItem*)findDefaultWebFormKeychainEntryForHost:(NSString*)host
|
||||
port:(PRInt32)port
|
||||
scheme:(NSString*)scheme;
|
||||
- (void)setDefaultWebFormKeychainEntry:(KeychainItem*)keychainItem;
|
||||
|
||||
- (NSArray*)allWebFormKeychainItemsForHost:(NSString*)host
|
||||
port:(PRInt32)port
|
||||
scheme:(NSString*)scheme;
|
||||
- (KeychainItem*)defaultFromKeychainItems:(NSArray*)items;
|
||||
|
||||
- (KeychainItem*)findAuthKeychainEntryForHost:(NSString*)host
|
||||
port:(PRInt32)port
|
||||
scheme:(NSString*)scheme
|
||||
securityDomain:(NSString*)securityDomain;
|
||||
- (KeychainItem*)updateAuthKeychainEntry:(KeychainItem*)keychainItem
|
||||
withUsername:(NSString*)username
|
||||
password:(NSString*)password;
|
||||
- (KeychainItem*)storeUsername:(NSString*)username
|
||||
password:(NSString*)password
|
||||
forHost:(NSString*)host
|
||||
securityDomain:(NSString*)securityDomain
|
||||
port:(PRInt32)port
|
||||
scheme:(NSString*)scheme
|
||||
isForm:(BOOL)isForm;
|
||||
|
||||
- (void)removeAllUsernamesAndPasswords;
|
||||
|
||||
- (void)addListenerToView:(CHBrowserView*)view;
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#import "KeychainDenyList.h"
|
||||
#import "CHBrowserService.h"
|
||||
#import "PreferenceManager.h"
|
||||
#import "AutoCompleteUtils.h"
|
||||
#import "KeyEquivView.h"
|
||||
#import "nsAlertController.h"
|
||||
|
||||
|
@ -64,10 +65,12 @@
|
|||
#include "nsIDocument.h"
|
||||
#include "nsIDOMHTMLDocument.h"
|
||||
#include "nsIDOMHTMLCollection.h"
|
||||
#include "nsIDOMHTMLFormElement.h"
|
||||
#include "nsIDOMHTMLInputElement.h"
|
||||
#include "nsIDOMNSHTMLInputElement.h"
|
||||
#include "nsIDOMHTMLSelectElement.h"
|
||||
#include "nsIDOMHTMLOptionElement.h"
|
||||
#include "nsIDOMEvent.h"
|
||||
#include "nsIDOMEventTarget.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsIURL.h"
|
||||
#include "nsIDOMWindowCollection.h"
|
||||
#include "nsIContent.h"
|
||||
|
@ -101,13 +104,7 @@ static const int kCacheTimeout = 120;
|
|||
// from CHBrowserService.h
|
||||
extern NSString* const XPCOMShutDownNotificationName;
|
||||
|
||||
|
||||
nsresult
|
||||
FindUsernamePasswordFields(nsIDOMHTMLFormElement* inFormElement, nsIDOMHTMLInputElement** outUsername,
|
||||
nsIDOMHTMLInputElement** outPassword, PRBool inStopWhenFound);
|
||||
|
||||
NSWindow*
|
||||
GetNSWindow(nsIDOMWindow* inWindow);
|
||||
static NSWindow* GetNSWindow(nsIDOMWindow* inWindow);
|
||||
|
||||
@interface KeychainService(Private)
|
||||
- (KeychainItem*)findLegacyKeychainEntryForHost:(NSString*)host port:(PRInt32)port;
|
||||
|
@ -225,21 +222,167 @@ int KeychainPrefChangedCallback(const char* inPref, void* unused)
|
|||
return mFormPasswordFillIsEnabled;
|
||||
}
|
||||
|
||||
- (KeychainItem*)findKeychainEntryForHost:(NSString*)host
|
||||
port:(PRInt32)port
|
||||
scheme:(NSString*)scheme
|
||||
securityDomain:(NSString*)securityDomain
|
||||
isForm:(BOOL)isForm;
|
||||
- (KeychainItem*)findWebFormKeychainEntryForUsername:(NSString*)username
|
||||
forHost:(NSString*)host
|
||||
port:(PRInt32)port
|
||||
scheme:(NSString*)scheme
|
||||
{
|
||||
if (port == -1)
|
||||
port = kAnyPort;
|
||||
SecProtocolType protocol = [scheme isEqualToString:@"https"] ? kSecProtocolTypeHTTPS : kSecProtocolTypeHTTP;
|
||||
|
||||
KeychainItem* item = [KeychainItem keychainItemForHost:host
|
||||
username:username
|
||||
port:port
|
||||
protocol:protocol
|
||||
authenticationType:kSecAuthenticationTypeHTMLForm];
|
||||
|
||||
if (item && [item password] && ![[item password] isEqualToString:@" "])
|
||||
return item;
|
||||
|
||||
item = [self findLegacyKeychainEntryForHost:host port:port];
|
||||
if (item && [item password] && [[item username] isEqualToString:username])
|
||||
return item;
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (KeychainItem*)findDefaultWebFormKeychainEntryForHost:(NSString*)host
|
||||
port:(PRInt32)port
|
||||
scheme:(NSString*)scheme
|
||||
{
|
||||
NSArray* keychainItems = [self allWebFormKeychainItemsForHost:host port:port scheme:scheme];
|
||||
|
||||
return [self defaultFromKeychainItems:keychainItems];
|
||||
}
|
||||
|
||||
//
|
||||
// setDefaultWebFormKeychainEntry
|
||||
//
|
||||
// Discussion of how the default keychain entry is chosen in Camino and Safari:
|
||||
// Safari sets its default entry by entering 'default' in the keychain entry
|
||||
// comment field. We designed Camino so that it can use another default
|
||||
// by setting 'camino_default' in the comment field. Camino first checks for
|
||||
// 'camino_default', then looks for 'default' if there isn't one. Safari will
|
||||
// overwrite the comment field when setting the default. If Safari changes the
|
||||
// 'camino_default' entry to 'default', Camino will still treat it as the
|
||||
// default. Camino won't overwrite a comment field, meaning that the Safari
|
||||
// default will be preserved if the user selects the same item in Camino.
|
||||
//
|
||||
// This method sets the default Camino keychain entry. The default Camino
|
||||
// entry will have a comment value set to "camino_default". It first clears
|
||||
// any existing default Camino entry before setting the passed in keychain to
|
||||
// the new default. It doesn't modify any other comment values, meaning that
|
||||
// the Safari default will preserved.
|
||||
- (void)setDefaultWebFormKeychainEntry:(KeychainItem*)keychainItem
|
||||
{
|
||||
NSArray* keychainArray = [KeychainItem allKeychainItemsForHost:[keychainItem host]
|
||||
port:[keychainItem port]
|
||||
protocol:[keychainItem protocol]
|
||||
authenticationType:[keychainItem authenticationType]
|
||||
creator:0];
|
||||
|
||||
NSEnumerator* keychainEnumerator = [keychainArray objectEnumerator];
|
||||
KeychainItem* keychainTemp;
|
||||
while ((keychainTemp = [keychainEnumerator nextObject])) {
|
||||
if ([[keychainTemp comment] isEqualToString:@"camino_default"])
|
||||
[keychainTemp setComment:@""];
|
||||
}
|
||||
|
||||
if ([[keychainItem comment] isEqualToString:@""])
|
||||
[keychainItem setComment:@"camino_default"];
|
||||
}
|
||||
|
||||
- (NSArray*)allWebFormKeychainItemsForHost:(NSString*)host port:(PRInt32)port scheme:(NSString*)scheme
|
||||
{
|
||||
if (port == -1)
|
||||
port = kAnyPort;
|
||||
SecProtocolType protocol = [scheme isEqualToString:@"https"] ? kSecProtocolTypeHTTPS : kSecProtocolTypeHTTP;
|
||||
|
||||
// Since there are old-style (pre-1.1) authenticationType values, retrieve them all then filter.
|
||||
NSArray* keychainItems = [KeychainItem allKeychainItemsForHost:host
|
||||
port:(UInt16)port
|
||||
protocol:protocol
|
||||
authenticationType:0
|
||||
creator:0];
|
||||
|
||||
NSMutableArray* returnItems = [[[NSMutableArray alloc] init] autorelease];
|
||||
KeychainItem* item;
|
||||
NSEnumerator* keychainEnumerator = [keychainItems objectEnumerator];
|
||||
while ((item = [keychainEnumerator nextObject])) {
|
||||
if ([item authenticationType] == kSecAuthenticationTypeHTMLForm ||
|
||||
[item authenticationType] == kSecAuthenticationTypeHTTPDigest) {
|
||||
[returnItems addObject:item];
|
||||
}
|
||||
else if ([item authenticationType] == kSecAuthenticationTypeHTTPDigestReversed) {
|
||||
[item setAuthenticationType:kSecAuthenticationTypeHTTPDigest];
|
||||
[returnItems addObject:item];
|
||||
}
|
||||
}
|
||||
|
||||
return returnItems;
|
||||
}
|
||||
|
||||
//
|
||||
// defaultFromKeychainItems
|
||||
//
|
||||
// Method that takes an array of web form keychain items and returns the default.
|
||||
//
|
||||
- (KeychainItem*)defaultFromKeychainItems:(NSArray*)items
|
||||
{
|
||||
// First, check for the default Camino entry.
|
||||
KeychainItem* item;
|
||||
NSEnumerator* keychainEnumerator = [items objectEnumerator];
|
||||
while ((item = [keychainEnumerator nextObject])) {
|
||||
if ([[item comment] isEqualToString:@"camino_default"] && [item password])
|
||||
return item;
|
||||
}
|
||||
|
||||
// Next, check for Safari's default.
|
||||
keychainEnumerator = [items objectEnumerator];
|
||||
while ((item = [keychainEnumerator nextObject])) {
|
||||
if ([[item comment] isEqualToString:@"default"]) {
|
||||
// Safari doesn't bother to set kSecNegativeItemAttr on "Passwords not saved"
|
||||
// items; that's just the way they roll. This fragile method is the best we can do.
|
||||
if ([[item password] isEqualToString:@" "])
|
||||
continue;
|
||||
if ([item password])
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
// Then check for the first new-style Camino keychain entry.
|
||||
keychainEnumerator = [items objectEnumerator];
|
||||
while ((item = [keychainEnumerator nextObject])) {
|
||||
if (([item creator] == kCaminoKeychainCreatorCode) && [item password])
|
||||
return item;
|
||||
}
|
||||
|
||||
// Finally, just arbitrarily pick the first one.
|
||||
keychainEnumerator = [items objectEnumerator];
|
||||
while ((item = [keychainEnumerator nextObject])) {
|
||||
if ([[item password] isEqualToString:@" "])
|
||||
continue;
|
||||
if ([item password])
|
||||
return item;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (KeychainItem*)findAuthKeychainEntryForHost:(NSString*)host
|
||||
port:(PRInt32)port
|
||||
scheme:(NSString*)scheme
|
||||
securityDomain:(NSString*)securityDomain
|
||||
{
|
||||
if (port == -1)
|
||||
port = kAnyPort;
|
||||
SecProtocolType protocol = [scheme isEqualToString:@"https"] ? kSecProtocolTypeHTTPS : kSecProtocolTypeHTTP;
|
||||
SecAuthenticationType authType = isForm ? kSecAuthenticationTypeHTMLForm : kSecAuthenticationTypeDefault;
|
||||
|
||||
NSArray* newKeychainItems = [KeychainItem allKeychainItemsForHost:host
|
||||
port:(UInt16)port
|
||||
protocol:protocol
|
||||
authenticationType:authType
|
||||
authenticationType:kSecAuthenticationTypeDefault
|
||||
creator:0];
|
||||
|
||||
// If we have a security domain, discard mismatched domains and re-sort the array
|
||||
|
@ -281,22 +424,7 @@ int KeychainPrefChangedCallback(const char* inPref, void* unused)
|
|||
if (item && [item password])
|
||||
return item;
|
||||
|
||||
// Finally, check for a new style entry created by something other than Camino.
|
||||
// Since we don't yet have any UI for multiple accounts, we use Safari's default
|
||||
// if we can find it, otherwise we just arbitrarily pick the first one that
|
||||
// looks plausible.
|
||||
keychainEnumerator = [newKeychainItems objectEnumerator];
|
||||
while ((item = [keychainEnumerator nextObject])) {
|
||||
NSString* comment = [item comment];
|
||||
if (comment && ([comment rangeOfString:@"default"].location != NSNotFound)) {
|
||||
// Safari doesn't bother to set kSecNegativeItemAttr on "Passwords not saved"
|
||||
// items; that's just the way they roll. This fragile method is the best we can do.
|
||||
if ([[item password] isEqualToString:@" "])
|
||||
continue;
|
||||
if ([item password])
|
||||
return item;
|
||||
}
|
||||
}
|
||||
// Finally, just arbitrarily pick the first one that looks plausible.
|
||||
keychainEnumerator = [newKeychainItems objectEnumerator];
|
||||
while ((item = [keychainEnumerator nextObject])) {
|
||||
if ([[item password] isEqualToString:@" "])
|
||||
|
@ -315,12 +443,14 @@ int KeychainPrefChangedCallback(const char* inPref, void* unused)
|
|||
- (KeychainItem*)findLegacyKeychainEntryForHost:(NSString*)host port:(PRInt32)port
|
||||
{
|
||||
KeychainItem* item = [KeychainItem keychainItemForHost:host
|
||||
username:nil
|
||||
port:(UInt16)port
|
||||
protocol:kSecProtocolTypeHTTP
|
||||
authenticationType:kSecAuthenticationTypeHTTPDigest];
|
||||
if (!item) {
|
||||
// check for the reverse auth type
|
||||
item = [KeychainItem keychainItemForHost:host
|
||||
username:nil
|
||||
port:(UInt16)port
|
||||
protocol:kSecProtocolTypeHTTP
|
||||
authenticationType:kSecAuthenticationTypeHTTPDigestReversed];
|
||||
|
@ -330,13 +460,13 @@ int KeychainPrefChangedCallback(const char* inPref, void* unused)
|
|||
return item;
|
||||
}
|
||||
|
||||
- (void)storeUsername:(NSString*)username
|
||||
password:(NSString*)password
|
||||
forHost:(NSString*)host
|
||||
securityDomain:(NSString*)securityDomain
|
||||
port:(PRInt32)port
|
||||
scheme:(NSString*)scheme
|
||||
isForm:(BOOL)isForm
|
||||
- (KeychainItem*)storeUsername:(NSString*)username
|
||||
password:(NSString*)password
|
||||
forHost:(NSString*)host
|
||||
securityDomain:(NSString*)securityDomain
|
||||
port:(PRInt32)port
|
||||
scheme:(NSString*)scheme
|
||||
isForm:(BOOL)isForm
|
||||
{
|
||||
if (port == -1)
|
||||
port = kAnyPort;
|
||||
|
@ -356,6 +486,8 @@ int KeychainPrefChangedCallback(const char* inPref, void* unused)
|
|||
else
|
||||
[newItem setSecurityDomain:securityDomain];
|
||||
}
|
||||
|
||||
return newItem;
|
||||
}
|
||||
|
||||
// Stores changes to a site's stored account. Note that this may return a different item than
|
||||
|
@ -363,9 +495,9 @@ int KeychainPrefChangedCallback(const char* inPref, void* unused)
|
|||
// store an acccount in Camino that isn't the one we pick up from Safari. For password updates
|
||||
// we update the existing item, but for username updates we make a new item if the one we were
|
||||
// using wasn't Camino-created.
|
||||
- (KeychainItem*)updateKeychainEntry:(KeychainItem*)keychainItem
|
||||
withUsername:(NSString*)username
|
||||
password:(NSString*)password
|
||||
- (KeychainItem*)updateAuthKeychainEntry:(KeychainItem*)keychainItem
|
||||
withUsername:(NSString*)username
|
||||
password:(NSString*)password
|
||||
{
|
||||
if ([username isEqualToString:[keychainItem username]] || [keychainItem creator] == kCaminoKeychainCreatorCode) {
|
||||
[keychainItem setUsername:username password:password];
|
||||
|
@ -496,13 +628,16 @@ int KeychainPrefChangedCallback(const char* inPref, void* unused)
|
|||
NSDictionary* loginInfo = (NSDictionary*)contextInfo;
|
||||
switch (returnCode) {
|
||||
case NSAlertFirstButtonReturn: // Save
|
||||
[self storeUsername:[loginInfo objectForKey:kLoginUsernameKey]
|
||||
password:[loginInfo objectForKey:kLoginPasswordKey]
|
||||
forHost:[loginInfo objectForKey:kLoginHostKey]
|
||||
securityDomain:[loginInfo objectForKey:kLoginSecurityDomainKey]
|
||||
port:[[loginInfo objectForKey:kLoginPortKey] intValue]
|
||||
scheme:[loginInfo objectForKey:kLoginSchemeKey]
|
||||
isForm:YES];
|
||||
KeychainItem* keychainEntry =
|
||||
[self storeUsername:[loginInfo objectForKey:kLoginUsernameKey]
|
||||
password:[loginInfo objectForKey:kLoginPasswordKey]
|
||||
forHost:[loginInfo objectForKey:kLoginHostKey]
|
||||
securityDomain:[loginInfo objectForKey:kLoginSecurityDomainKey]
|
||||
port:[[loginInfo objectForKey:kLoginPortKey] intValue]
|
||||
scheme:[loginInfo objectForKey:kLoginSchemeKey]
|
||||
isForm:YES];
|
||||
[self setDefaultWebFormKeychainEntry:keychainEntry];
|
||||
|
||||
break;
|
||||
|
||||
case NSAlertSecondButtonReturn: // Don't remember
|
||||
|
@ -557,8 +692,8 @@ int KeychainPrefChangedCallback(const char* inPref, void* unused)
|
|||
{
|
||||
NSDictionary* loginInfo = (NSDictionary*)contextInfo;
|
||||
if (returnCode == NSAlertFirstButtonReturn) {
|
||||
[self updateKeychainEntry:[loginInfo objectForKey:kLoginKeychainEntry]
|
||||
withUsername:[loginInfo objectForKey:kLoginUsernameKey]
|
||||
KeychainItem* keychainItem = [loginInfo objectForKey:kLoginKeychainEntry];
|
||||
[keychainItem setUsername:[loginInfo objectForKey:kLoginUsernameKey]
|
||||
password:[loginInfo objectForKey:kLoginPasswordKey]];
|
||||
}
|
||||
[loginInfo release]; // balance the retain in the alert creation
|
||||
|
@ -746,11 +881,10 @@ KeychainPrompt::PreFill(const PRUnichar *realmBlob, PRUnichar **user, PRUnichar
|
|||
NSString* scheme = (port == kDefaultHTTPSPort) ? @"https" : @"http";
|
||||
|
||||
KeychainService* keychain = [KeychainService instance];
|
||||
KeychainItem* entry = [keychain findKeychainEntryForHost:host
|
||||
port:port
|
||||
scheme:scheme
|
||||
securityDomain:realm
|
||||
isForm:NO];
|
||||
KeychainItem* entry = [keychain findAuthKeychainEntryForHost:host
|
||||
port:port
|
||||
scheme:scheme
|
||||
securityDomain:realm];
|
||||
if (entry) {
|
||||
[keychain cacheKeychainEntry:entry forKey:realmBlobString];
|
||||
if (user)
|
||||
|
@ -780,11 +914,10 @@ KeychainPrompt::ProcessPrompt(const PRUnichar* realmBlob, bool checked, PRUnicha
|
|||
KeychainService* keychain = [KeychainService instance];
|
||||
KeychainItem* keychainEntry = [keychain cachedKeychainEntryForKey:realmBlobString];
|
||||
if (!keychainEntry) {
|
||||
keychainEntry = [keychain findKeychainEntryForHost:host
|
||||
port:port
|
||||
scheme:scheme
|
||||
securityDomain:realm
|
||||
isForm:NO];
|
||||
keychainEntry = [keychain findAuthKeychainEntryForHost:host
|
||||
port:port
|
||||
scheme:scheme
|
||||
securityDomain:realm];
|
||||
}
|
||||
|
||||
// Update, store or remove the user/password depending on the user
|
||||
|
@ -808,9 +941,9 @@ KeychainPrompt::ProcessPrompt(const PRUnichar* realmBlob, bool checked, PRUnicha
|
|||
if (![[keychainEntry username] isEqualToString:username] ||
|
||||
![[keychainEntry password] isEqualToString:password])
|
||||
{
|
||||
keychainEntry = [keychain updateKeychainEntry:keychainEntry
|
||||
withUsername:username
|
||||
password:password];
|
||||
keychainEntry = [keychain updateAuthKeychainEntry:keychainEntry
|
||||
withUsername:username
|
||||
password:password];
|
||||
}
|
||||
// TODO: this is just to upgrade pre-1.1 HTTPAuth items; at some point in
|
||||
// the future remove from here...
|
||||
|
@ -995,29 +1128,28 @@ KeychainFormSubmitObserver::Notify(nsIDOMHTMLFormElement* formNode, nsIDOMWindow
|
|||
if (!actionHost)
|
||||
actionHost = host;
|
||||
|
||||
// Search for an existing keychain entry with the same username.
|
||||
// Check the cache first, then fall back to a search in case a cached
|
||||
// entry expired before the user submitted.
|
||||
KeychainItem* keychainEntry = [keychain cachedKeychainEntryForKey:uri];
|
||||
// We weren't using punycode for keychain entries up through 1.5, so try
|
||||
// non-punycode first to favor Camino entries.
|
||||
if (!keychainEntry) {
|
||||
keychainEntry = [keychain findKeychainEntryForHost:host
|
||||
port:port
|
||||
scheme:scheme
|
||||
securityDomain:nil
|
||||
isForm:YES];
|
||||
if (!keychainEntry || ![[keychainEntry username] isEqualToString:username]) {
|
||||
keychainEntry = [keychain findWebFormKeychainEntryForUsername:username
|
||||
forHost:host
|
||||
port:port
|
||||
scheme:scheme];
|
||||
}
|
||||
if (!keychainEntry && ![asciiHost isEqualToString:host]) {
|
||||
keychainEntry = [keychain findKeychainEntryForHost:asciiHost
|
||||
port:port
|
||||
scheme:scheme
|
||||
securityDomain:nil
|
||||
isForm:YES];
|
||||
keychainEntry = [keychain findWebFormKeychainEntryForUsername:username
|
||||
forHost:asciiHost
|
||||
port:port
|
||||
scheme:scheme];
|
||||
}
|
||||
// If there's already an entry in the keychain, check if the username
|
||||
// and password match. If not, ask the user what they want to do and replace
|
||||
// it as necessary. If there's no entry, ask if they want to remember it
|
||||
// and then put it into the keychain
|
||||
// If there's already an entry in the keychain for the username, check if the
|
||||
// password matches. If not, ask the user what they want to do and replace
|
||||
// it as necessary. If there's no entry for this username, ask if they want to
|
||||
// remember it and then put it into the keychain
|
||||
if (keychainEntry) {
|
||||
// If it's an old-style entry, upgrade it now that we know what it goes with.
|
||||
if ([keychainEntry authenticationType] == kSecAuthenticationTypeHTTPDigest)
|
||||
|
@ -1033,17 +1165,15 @@ KeychainFormSubmitObserver::Notify(nsIDOMHTMLFormElement* formNode, nsIDOMWindow
|
|||
}
|
||||
// ... to here.
|
||||
|
||||
if (![[keychainEntry username] isEqualToString:username] ||
|
||||
![[keychainEntry password] isEqualToString:password])
|
||||
{
|
||||
// Note: this can create a new entry rather than updating the entry
|
||||
// passed in, so |keychainEntry| shouldn't be used after this point.
|
||||
if (![[keychainEntry password] isEqualToString:password]) {
|
||||
[keychain promptToUpdateKeychainItem:keychainEntry
|
||||
withUsername:username
|
||||
password:password
|
||||
inWindow:GetNSWindow(window)];
|
||||
}
|
||||
|
||||
[keychain setDefaultWebFormKeychainEntry:keychainEntry];
|
||||
|
||||
if ([[keychain allowedActionHostsForHost:host] count] == 0)
|
||||
[keychain setAllowedActionHosts:[NSArray arrayWithObject:actionHost] forHost:host];
|
||||
// We don't need to look up the entry again, so remove it from the cache.
|
||||
|
@ -1163,20 +1293,16 @@ KeychainFormSubmitObserver::Notify(nsIDOMHTMLFormElement* formNode, nsIDOMWindow
|
|||
|
||||
// We weren't using punycode for keychain entries up through 1.5, so try
|
||||
// non-punycode first to favor Camino entries.
|
||||
keychainEntry = [keychain findKeychainEntryForHost:host
|
||||
port:port
|
||||
scheme:scheme
|
||||
securityDomain:nil
|
||||
isForm:YES];
|
||||
keychainEntry = [keychain findDefaultWebFormKeychainEntryForHost:host
|
||||
port:port
|
||||
scheme:scheme];
|
||||
// TODO: once a released version checks for punycode, anything found with
|
||||
// the above search (and with a Camino creator code) should be fixed
|
||||
// immediately to have the right host.
|
||||
if (!keychainEntry && ![asciiHost isEqualToString:host]) {
|
||||
keychainEntry = [keychain findKeychainEntryForHost:asciiHost
|
||||
port:port
|
||||
scheme:scheme
|
||||
securityDomain:nil
|
||||
isForm:YES];
|
||||
keychainEntry = [keychain findDefaultWebFormKeychainEntryForHost:asciiHost
|
||||
port:port
|
||||
scheme:scheme];
|
||||
}
|
||||
}
|
||||
// If we don't have a password for the page, don't bother looking for
|
||||
|
@ -1220,6 +1346,11 @@ KeychainFormSubmitObserver::Notify(nsIDOMHTMLFormElement* formNode, nsIDOMWindow
|
|||
if (!userValue.Length() || userValue.Equals(user, nsCaseInsensitiveStringComparator())) {
|
||||
rv = usernameElement->SetValue(user);
|
||||
rv = passwordElement->SetValue(pwd);
|
||||
|
||||
// Highlight the username.
|
||||
nsCOMPtr<nsIDOMNSHTMLInputElement> nsElement = do_QueryInterface(usernameElement);
|
||||
if (nsElement)
|
||||
nsElement->SetSelectionStart(0);
|
||||
}
|
||||
|
||||
// Now that we have actually filled the password, cache the keychain entry.
|
||||
|
@ -1327,8 +1458,7 @@ KeychainFormSubmitObserver::Notify(nsIDOMHTMLFormElement* formNode, nsIDOMWindow
|
|||
//
|
||||
// Finds the native window for the given DOM window
|
||||
//
|
||||
NSWindow*
|
||||
GetNSWindow(nsIDOMWindow* inWindow)
|
||||
NSWindow* GetNSWindow(nsIDOMWindow* inWindow)
|
||||
{
|
||||
CHBrowserView* browserView = [CHBrowserView browserViewFromDOMWindow:inWindow];
|
||||
return [browserView nativeWindow];
|
||||
|
@ -1345,9 +1475,10 @@ GetNSWindow(nsIDOMWindow* inWindow)
|
|||
// "change your password" form). If we find one, null out
|
||||
// |outPassword| since we probably don't want to prefill this form.
|
||||
//
|
||||
nsresult
|
||||
FindUsernamePasswordFields(nsIDOMHTMLFormElement* inFormElement, nsIDOMHTMLInputElement** outUsername,
|
||||
nsIDOMHTMLInputElement** outPassword, PRBool inStopWhenFound)
|
||||
nsresult FindUsernamePasswordFields(nsIDOMHTMLFormElement* inFormElement,
|
||||
nsIDOMHTMLInputElement** outUsername,
|
||||
nsIDOMHTMLInputElement** outPassword,
|
||||
PRBool inStopWhenFound)
|
||||
{
|
||||
if (!outUsername || !outPassword)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
|
Загрузка…
Ссылка в новой задаче