Camino only: Bug 178607 - Keychain cannot save multiple passwords for the same host. Patch by Bryan Atwood <batwood.bugs@gmail.com>, r=smorgan, sr=mento.
git-svn-id: svn://10.0.0.236/trunk@243675 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
83
mozilla/camino/src/formfill/AutoCompleteUtils.h
Normal file
83
mozilla/camino/src/formfill/AutoCompleteUtils.h
Normal file
@@ -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
|
||||
87
mozilla/camino/src/formfill/AutoCompleteUtils.mm
Normal file
87
mozilla/camino/src/formfill/AutoCompleteUtils.mm
Normal file
@@ -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
|
||||
100
mozilla/camino/src/formfill/FormFillController.h
Normal file
100
mozilla/camino/src/formfill/FormFillController.h
Normal file
@@ -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
|
||||
748
mozilla/camino/src/formfill/FormFillController.mm
Normal file
748
mozilla/camino/src/formfill/FormFillController.mm
Normal file
@@ -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
|
||||
83
mozilla/camino/src/formfill/FormFillPopup.h
Normal file
83
mozilla/camino/src/formfill/FormFillPopup.h
Normal file
@@ -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
|
||||
230
mozilla/camino/src/formfill/FormFillPopup.mm
Normal file
230
mozilla/camino/src/formfill/FormFillPopup.mm
Normal file
@@ -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
|
||||
85
mozilla/camino/src/formfill/KeychainAutoCompleteSession.h
Normal file
85
mozilla/camino/src/formfill/KeychainAutoCompleteSession.h
Normal file
@@ -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
|
||||
369
mozilla/camino/src/formfill/KeychainAutoCompleteSession.mm
Normal file
369
mozilla/camino/src/formfill/KeychainAutoCompleteSession.mm
Normal file
@@ -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();
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[mUsernames release];
|
||||
[mDefaultUser release];
|
||||
|
||||
if (mDOMListener)
|
||||
delete 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()];
|
||||
}
|
||||
Reference in New Issue
Block a user