2018-01-12 20:44:44 +01:00
! ( function ( ) {
2016-09-16 05:24:45 +02:00
/ *
1 Password Extension
Lovingly handcrafted by Dave Teare , Michael Fey , Rad Azzouz , and Roustem Karimov .
Copyright ( c ) 2014 AgileBits . All rights reserved .
=== === === === === === === === === === === === === === === === === === === === === === === === === === ==
Copyright ( c ) 2014 AgileBits Inc .
Permission is hereby granted , free of charge , to any person obtaining a copy
of this software and associated documentation files ( the "Software" ) , to deal
in the Software without restriction , including without limitation the rights
to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
copies of the Software , and to permit persons to whom the Software is
furnished to do so , subject to the following conditions :
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software .
THE SOFTWARE IS PROVIDED "AS IS" , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE .
* /
2017-01-20 01:16:13 +01:00
/ *
MODIFICATIONS FROM ORIGINAL
1. Populate isFirefox
2. Remove isChrome and isSafari since they are not used .
3. Unminify and format to meet Mozilla review requirements .
2017-10-11 15:01:59 +02:00
4. Remove unnecessary input types from getFormElements query selector and limit number of elements returned .
2017-10-26 05:07:00 +02:00
5. Remove fakeTested prop .
2018-04-14 04:42:32 +02:00
6. Rename com . agilebits . * stuff to com . bitwarden . *
2018-09-12 19:24:30 +02:00
7. Remove "some useful globals" on window
2017-01-20 01:16:13 +01:00
* /
2016-11-12 04:40:09 +01:00
2017-10-02 16:29:46 +02:00
function collect ( document , undefined ) {
2017-10-26 05:07:00 +02:00
// START MODIFICATION
2016-11-27 06:31:45 +01:00
var isFirefox = navigator . userAgent . indexOf ( 'Firefox' ) !== - 1 || navigator . userAgent . indexOf ( 'Gecko/' ) !== - 1 ;
2017-10-26 05:07:00 +02:00
// END MODIFICATION
2016-11-27 06:31:45 +01:00
2017-01-20 01:16:13 +01:00
document . elementsByOPID = { } ;
2018-04-14 04:42:32 +02:00
document . addEventListener ( 'input' , function ( inputevent ) {
inputevent . a !== false &&
inputevent . target . tagName . toLowerCase ( ) === 'input' &&
( inputevent . target . dataset [ 'com.bitwarden.browser.userEdited' ] = 'yes' ) ;
2017-01-20 01:16:13 +01:00
} , true ) ;
2017-01-20 04:43:45 +01:00
function getPageDetails ( theDoc , oneShotId ) {
// start helpers
// get the value of a dom element's attribute
function getElementAttrValue ( el , attrName ) {
var attrVal = el [ attrName ] ;
if ( 'string' == typeof attrVal ) {
return attrVal ;
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
attrVal = el . getAttribute ( attrName ) ;
return 'string' == typeof attrVal ? attrVal : null ;
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
// has the element been fake tested?
function checkIfFakeTested ( field , el ) {
if ( - 1 === [ 'text' , 'password' ] . indexOf ( el . type . toLowerCase ( ) ) ||
2017-10-03 22:11:11 +02:00
! ( passwordRegEx . test ( field . value ) ||
2017-01-20 04:43:45 +01:00
passwordRegEx . test ( field . htmlID ) || passwordRegEx . test ( field . htmlName ) ||
passwordRegEx . test ( field . placeholder ) || passwordRegEx . test ( field [ 'label-tag' ] ) ||
passwordRegEx . test ( field [ 'label-data' ] ) || passwordRegEx . test ( field [ 'label-aria' ] ) ) ) {
2017-01-20 01:16:13 +01:00
return false ;
}
2017-01-20 04:43:45 +01:00
if ( ! field . visible ) {
2017-01-20 01:16:13 +01:00
return true ;
}
2017-01-20 04:43:45 +01:00
if ( 'password' == el . type . toLowerCase ( ) ) {
2017-01-20 01:16:13 +01:00
return false ;
}
2017-01-20 04:43:45 +01:00
var elType = el . type ;
focusElement ( el , true ) ;
return elType !== el . type ;
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
// get the value of a dom element
function getElementValue ( el ) {
switch ( toLowerString ( el . type ) ) {
2017-01-20 01:16:13 +01:00
case 'checkbox' :
2017-01-20 04:43:45 +01:00
return el . checked ? '✓' : '' ;
2017-01-20 01:16:13 +01:00
case 'hidden' :
2017-01-20 04:43:45 +01:00
el = el . value ;
if ( ! el || 'number' != typeof el . length ) {
2017-01-20 01:16:13 +01:00
return '' ;
}
2017-01-20 04:43:45 +01:00
254 < el . length && ( el = el . substr ( 0 , 254 ) + '...SNIPPED' ) ;
return el ;
2017-01-20 01:16:13 +01:00
default :
2017-01-20 04:43:45 +01:00
return el . value ;
2017-01-20 01:16:13 +01:00
}
}
2017-01-20 04:43:45 +01:00
// get all the options for a "select" element
function getSelectElementOptions ( el ) {
if ( ! el . options ) {
return null ;
}
var options = Array . prototype . slice . call ( el . options ) . map ( function ( option ) {
var optionText = option . text ?
2017-10-03 22:11:11 +02:00
toLowerString ( option . text ) . replace ( /\\s/gm , '' ) . replace ( /[~`!@$%^&*()\\-_+=:;'\"\\[\\]|\\\\,<.>\\?]/gm , '' ) :
null ;
2017-01-20 04:43:45 +01:00
return [ optionText ? optionText : null , option . value ] ;
} )
return {
options : options
} ;
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
// get the top label
function getLabelTop ( el ) {
var parent ;
2017-10-03 22:11:11 +02:00
for ( el = el . parentElement || el . parentNode ; el && 'td' != toLowerString ( el . tagName ) ; ) {
2017-01-20 04:43:45 +01:00
el = el . parentElement || el . parentNode ;
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
if ( ! el || void 0 === el ) {
2017-01-20 01:16:13 +01:00
return null ;
}
2017-01-20 04:43:45 +01:00
parent = el . parentElement || el . parentNode ;
if ( 'tr' != parent . tagName . toLowerCase ( ) ) {
2017-01-20 01:16:13 +01:00
return null ;
}
2017-01-20 04:43:45 +01:00
parent = parent . previousElementSibling ;
if ( ! parent || 'tr' != ( parent . tagName + '' ) . toLowerCase ( ) ||
parent . cells && el . cellIndex >= parent . cells . length ) {
2017-01-20 01:16:13 +01:00
return null ;
}
2017-01-20 04:43:45 +01:00
el = parent . cells [ el . cellIndex ] ;
var elText = el . textContent || el . innerText ;
return elText = cleanText ( elText ) ;
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
// get all the tags for a given label
function getLabelTag ( el ) {
var docLabel ,
theLabels = [ ] ;
if ( el . labels && el . labels . length && 0 < el . labels . length ) {
theLabels = Array . prototype . slice . call ( el . labels ) ;
2017-01-20 01:16:13 +01:00
} else {
2017-01-20 04:43:45 +01:00
if ( el . id ) {
theLabels = theLabels . concat ( Array . prototype . slice . call (
queryDoc ( theDoc , 'label[for=' + JSON . stringify ( el . id ) + ']' ) ) ) ;
}
if ( el . name ) {
docLabel = queryDoc ( theDoc , 'label[for=' + JSON . stringify ( el . name ) + ']' ) ;
for ( var labelIndex = 0 ; labelIndex < docLabel . length ; labelIndex ++ ) {
if ( - 1 === theLabels . indexOf ( docLabel [ labelIndex ] ) ) {
theLabels . push ( docLabel [ labelIndex ] )
}
2017-01-20 01:16:13 +01:00
}
}
2017-01-20 04:43:45 +01:00
for ( var theEl = el ; theEl && theEl != theDoc ; theEl = theEl . parentNode ) {
if ( 'label' === toLowerString ( theEl . tagName ) && - 1 === theLabels . indexOf ( theEl ) ) {
theLabels . push ( theEl ) ;
}
}
}
if ( 0 === theLabels . length ) {
theEl = el . parentNode ;
if ( 'dd' === theEl . tagName . toLowerCase ( ) && null !== theEl . previousElementSibling
&& 'dt' === theEl . previousElementSibling . tagName . toLowerCase ( ) ) {
theLabels . push ( theEl . previousElementSibling ) ;
2017-01-20 01:16:13 +01:00
}
}
2017-01-20 04:43:45 +01:00
if ( 0 > theLabels . length ) {
return null ;
}
return theLabels . map ( function ( l ) {
return ( l . textContent || l . innerText )
. replace ( /^\\s+/ , '' ) . replace ( /\\s+$/ , '' ) . replace ( '\\n' , '' ) . replace ( /\\s{2,}/ , ' ' ) ;
} ) . join ( '' ) ;
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
// add property and value to the object if there is a value
function addProp ( obj , prop , val , d ) {
if ( 0 !== d && d === val || null === val || void 0 === val ) {
return ;
}
obj [ prop ] = val ;
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
// lowercase helper
2017-01-20 01:16:13 +01:00
function toLowerString ( s ) {
return 'string' === typeof s ? s . toLowerCase ( ) : ( '' + s ) . toLowerCase ( ) ;
}
2017-01-20 04:43:45 +01:00
// query the document helper
2017-01-20 01:16:13 +01:00
function queryDoc ( doc , query ) {
var els = [ ] ;
try {
els = doc . querySelectorAll ( query ) ;
} catch ( e ) { }
return els ;
}
2017-01-20 04:43:45 +01:00
// end helpers
2017-01-20 01:16:13 +01:00
var theView = theDoc . defaultView ? theDoc . defaultView : window ,
passwordRegEx = RegExp ( '((\\\\b|_|-)pin(\\\\b|_|-)|password|passwort|kennwort|(\\\\b|_|-)passe(\\\\b|_|-)|contraseña|senha|密码|adgangskode|hasło|wachtwoord)' , 'i' ) ;
2017-01-20 04:43:45 +01:00
// get all the docs
var theForms = Array . prototype . slice . call ( queryDoc ( theDoc , 'form' ) ) . map ( function ( formEl , elIndex ) {
2017-01-20 01:16:13 +01:00
var op = { } ,
formOpId = '__form__' + elIndex ;
formEl . opid = formOpId ;
op . opid = formOpId ;
2017-01-20 04:43:45 +01:00
addProp ( op , 'htmlName' , getElementAttrValue ( formEl , 'name' ) ) ;
addProp ( op , 'htmlID' , getElementAttrValue ( formEl , 'id' ) ) ;
formOpId = getElementAttrValue ( formEl , 'action' ) ;
2017-01-20 01:16:13 +01:00
formOpId = new URL ( formOpId , window . location . href ) ;
2017-01-20 04:43:45 +01:00
addProp ( op , 'htmlAction' , formOpId ? formOpId . href : null ) ;
addProp ( op , 'htmlMethod' , getElementAttrValue ( formEl , 'method' ) ) ;
2017-01-20 01:16:13 +01:00
return op ;
} ) ;
2017-01-20 04:43:45 +01:00
// get all the form fields
2017-10-11 15:01:59 +02:00
var theFields = Array . prototype . slice . call ( getFormElements ( theDoc , 50 ) ) . map ( function ( el , elIndex ) {
2017-01-20 04:43:45 +01:00
var field = { } ,
opId = '__' + elIndex ,
elMaxLen = - 1 == el . maxLength ? 999 : el . maxLength ;
if ( ! elMaxLen || 'number' === typeof elMaxLen && isNaN ( elMaxLen ) ) {
elMaxLen = 999 ;
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
theDoc . elementsByOPID [ opId ] = el ;
el . opid = opId ;
field . opid = opId ;
field . elementNumber = elIndex ;
addProp ( field , 'maxLength' , Math . min ( elMaxLen , 999 ) , 999 ) ;
field . visible = isElementVisible ( el ) ;
field . viewable = isElementViewable ( el ) ;
addProp ( field , 'htmlID' , getElementAttrValue ( el , 'id' ) ) ;
addProp ( field , 'htmlName' , getElementAttrValue ( el , 'name' ) ) ;
addProp ( field , 'htmlClass' , getElementAttrValue ( el , 'class' ) ) ;
addProp ( field , 'tabindex' , getElementAttrValue ( el , 'tabindex' ) ) ;
addProp ( field , 'title' , getElementAttrValue ( el , 'title' ) ) ;
2018-04-14 04:42:32 +02:00
// START MODIFICATION
addProp ( field , 'userEdited' , ! ! el . dataset [ 'com.browser.browser.userEdited' ] ) ;
// END MODIFICATION
2017-01-20 04:43:45 +01:00
if ( 'hidden' != toLowerString ( el . type ) ) {
addProp ( field , 'label-tag' , getLabelTag ( el ) ) ;
addProp ( field , 'label-data' , getElementAttrValue ( el , 'data-label' ) ) ;
addProp ( field , 'label-aria' , getElementAttrValue ( el , 'aria-label' ) ) ;
addProp ( field , 'label-top' , getLabelTop ( el ) ) ;
var labelArr = [ ] ;
for ( var sib = el ; sib && sib . nextSibling ; ) {
sib = sib . nextSibling ;
if ( isKnownTag ( sib ) ) {
2017-01-20 01:16:13 +01:00
break ;
}
2017-01-20 04:43:45 +01:00
checkNodeType ( labelArr , sib ) ;
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
addProp ( field , 'label-right' , labelArr . join ( '' ) ) ;
labelArr = [ ] ;
shiftForLeftLabel ( el , labelArr ) ;
labelArr = labelArr . reverse ( ) . join ( '' ) ;
addProp ( field , 'label-left' , labelArr ) ;
addProp ( field , 'placeholder' , getElementAttrValue ( el , 'placeholder' ) ) ;
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
addProp ( field , 'rel' , getElementAttrValue ( el , 'rel' ) ) ;
addProp ( field , 'type' , toLowerString ( getElementAttrValue ( el , 'type' ) ) ) ;
addProp ( field , 'value' , getElementValue ( el ) ) ;
addProp ( field , 'checked' , el . checked , false ) ;
addProp ( field , 'autoCompleteType' , el . getAttribute ( 'x-autocompletetype' ) || el . getAttribute ( 'autocompletetype' ) || el . getAttribute ( 'autocomplete' ) , 'off' ) ;
addProp ( field , 'disabled' , el . disabled ) ;
addProp ( field , 'readonly' , el . b || el . readOnly ) ;
addProp ( field , 'selectInfo' , getSelectElementOptions ( el ) ) ;
addProp ( field , 'aria-hidden' , 'true' == el . getAttribute ( 'aria-hidden' ) , false ) ;
addProp ( field , 'aria-disabled' , 'true' == el . getAttribute ( 'aria-disabled' ) , false ) ;
addProp ( field , 'aria-haspopup' , 'true' == el . getAttribute ( 'aria-haspopup' ) , false ) ;
addProp ( field , 'data-unmasked' , el . dataset . unmasked ) ;
addProp ( field , 'data-stripe' , getElementAttrValue ( el , 'data-stripe' ) ) ;
addProp ( field , 'onepasswordFieldType' , el . dataset . onepasswordFieldType || el . type ) ;
addProp ( field , 'onepasswordDesignation' , el . dataset . onepasswordDesignation ) ;
addProp ( field , 'onepasswordSignInUrl' , el . dataset . onepasswordSignInUrl ) ;
addProp ( field , 'onepasswordSectionTitle' , el . dataset . onepasswordSectionTitle ) ;
addProp ( field , 'onepasswordSectionFieldKind' , el . dataset . onepasswordSectionFieldKind ) ;
addProp ( field , 'onepasswordSectionFieldTitle' , el . dataset . onepasswordSectionFieldTitle ) ;
addProp ( field , 'onepasswordSectionFieldValue' , el . dataset . onepasswordSectionFieldValue ) ;
if ( el . form ) {
field . form = getElementAttrValue ( el . form , 'opid' ) ;
}
2017-10-26 05:07:00 +02:00
// START MODIFICATION
//addProp(field, 'fakeTested', checkIfFakeTested(field, el), false);
// END MODIFICATION
2017-01-20 04:43:45 +01:00
return field ;
2017-01-20 01:16:13 +01:00
} ) ;
2017-01-20 04:43:45 +01:00
// test form fields
theFields . filter ( function ( f ) {
return f . fakeTested ;
} ) . forEach ( function ( f ) {
var el = theDoc . elementsByOPID [ f . opid ] ;
el . getBoundingClientRect ( ) ;
var originalValue = el . value ;
// click it
! el || el && 'function' !== typeof el . click || el . click ( ) ;
focusElement ( el , false ) ;
el . dispatchEvent ( doEventOnElement ( el , 'keydown' ) ) ;
el . dispatchEvent ( doEventOnElement ( el , 'keypress' ) ) ;
el . dispatchEvent ( doEventOnElement ( el , 'keyup' ) ) ;
el . value !== originalValue && ( el . value = originalValue ) ;
el . click && el . click ( ) ;
f . postFakeTestVisible = isElementVisible ( el ) ;
f . postFakeTestViewable = isElementViewable ( el ) ;
f . postFakeTestType = el . type ;
var elValue = el . value ;
var event1 = el . ownerDocument . createEvent ( 'HTMLEvents' ) ,
event2 = el . ownerDocument . createEvent ( 'HTMLEvents' ) ;
el . dispatchEvent ( doEventOnElement ( el , 'keydown' ) ) ;
el . dispatchEvent ( doEventOnElement ( el , 'keypress' ) ) ;
el . dispatchEvent ( doEventOnElement ( el , 'keyup' ) ) ;
event2 . initEvent ( 'input' , true , true ) ;
el . dispatchEvent ( event2 ) ;
2017-01-21 18:38:52 +01:00
event1 . initEvent ( 'change' , true , true ) ;
el . dispatchEvent ( event1 ) ;
2017-01-20 04:43:45 +01:00
el . blur ( ) ;
el . value !== elValue && ( el . value = elValue ) ;
2017-01-20 01:16:13 +01:00
} ) ;
2017-01-20 04:43:45 +01:00
// build out the page details object. this is the final result
2017-01-20 01:16:13 +01:00
var pageDetails = {
documentUUID : oneShotId ,
title : theDoc . title ,
url : theView . location . href ,
documentUrl : theDoc . location . href ,
tabUrl : theView . location . href ,
2017-01-20 04:43:45 +01:00
forms : function ( forms ) {
var formObj = { } ;
forms . forEach ( function ( f ) {
formObj [ f . opid ] = f ;
2017-01-20 01:16:13 +01:00
} ) ;
2017-01-20 04:43:45 +01:00
return formObj ;
} ( theForms ) ,
2017-01-20 01:16:13 +01:00
fields : theFields ,
collectedTimestamp : new Date ( ) . getTime ( )
} ;
// get proper page title. maybe they are using the special meta tag?
2017-01-20 04:43:45 +01:00
var theTitle = document . querySelector ( '[data-onepassword-title]' )
2017-01-20 01:16:13 +01:00
if ( theTitle && theTitle . dataset [ DISPLAY _TITLE _ATTRIBUE ] ) {
pageDetails . displayTitle = theTitle . dataset . onepasswordTitle ;
}
return pageDetails ;
}
2017-01-20 04:43:45 +01:00
document . elementForOPID = getElementForOPID ;
2017-01-20 01:16:13 +01:00
2017-01-20 04:43:45 +01:00
function doEventOnElement ( kedol , fonor ) {
2017-01-20 01:16:13 +01:00
var quebo ;
isFirefox ? ( quebo = document . createEvent ( 'KeyboardEvent' ) , quebo . initKeyEvent ( fonor , true , false , null , false , false , false , false , 0 , 0 ) ) : ( quebo = kedol . ownerDocument . createEvent ( 'Events' ) ,
2017-10-03 22:11:11 +02:00
quebo . initEvent ( fonor , true , false ) , quebo . charCode = 0 , quebo . keyCode = 0 , quebo . which = 0 ,
quebo . srcElement = kedol , quebo . target = kedol ) ;
2017-01-20 01:16:13 +01:00
return quebo ;
}
2017-01-20 04:43:45 +01:00
// clean up the text
function cleanText ( s ) {
var sVal = null ;
s && ( sVal = s . replace ( /^\\s+|\\s+$|\\r?\\n.*$/gm , '' ) , sVal = 0 < sVal . length ? sVal : null ) ;
return sVal ;
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
// check the node type and adjust the array accordingly
function checkNodeType ( arr , el ) {
var theText = '' ;
3 === el . nodeType ? theText = el . nodeValue : 1 === el . nodeType && ( theText = el . textContent || el . innerText ) ;
( theText = cleanText ( theText ) ) && arr . push ( theText ) ;
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
function isKnownTag ( el ) {
if ( el && void 0 !== el ) {
var tags = 'select option input form textarea button table iframe body head script' . split ( ' ' ) ;
if ( el ) {
var elTag = el ? ( el . tagName || '' ) . toLowerCase ( ) : '' ;
return tags . constructor == Array ? 0 <= tags . indexOf ( elTag ) : elTag === tags ;
}
else {
return false ;
}
}
else {
return true ;
}
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
function shiftForLeftLabel ( el , arr , steps ) {
var sib ;
2017-10-03 22:11:11 +02:00
for ( steps || ( steps = 0 ) ; el && el . previousSibling ; ) {
2017-01-20 04:43:45 +01:00
el = el . previousSibling ;
if ( isKnownTag ( el ) ) {
2017-01-20 01:16:13 +01:00
return ;
}
2017-01-20 04:43:45 +01:00
checkNodeType ( arr , el ) ;
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
if ( el && 0 === arr . length ) {
for ( sib = null ; ! sib ; ) {
el = el . parentElement || el . parentNode ;
if ( ! el ) {
2017-01-20 01:16:13 +01:00
return ;
}
2017-01-20 04:43:45 +01:00
for ( sib = el . previousSibling ; sib && ! isKnownTag ( sib ) && sib . lastChild ; ) {
sib = sib . lastChild ;
2017-01-20 01:16:13 +01:00
}
}
2017-01-20 04:43:45 +01:00
// base case and recurse
isKnownTag ( sib ) || ( checkNodeType ( arr , sib ) , 0 === arr . length && shiftForLeftLabel ( sib , arr , steps + 1 ) ) ;
2017-01-20 01:16:13 +01:00
}
}
2017-01-20 04:43:45 +01:00
// is a dom element visible on screen?
function isElementVisible ( el ) {
var theEl = el ;
el = ( el = el . ownerDocument ) ? el . defaultView : { } ;
// walk the dom tree
for ( var elStyle ; theEl && theEl !== document ; ) {
elStyle = el . getComputedStyle ? el . getComputedStyle ( theEl , null ) : theEl . style ;
if ( ! elStyle ) {
2017-01-20 01:16:13 +01:00
return true ;
}
2017-01-20 04:43:45 +01:00
if ( 'none' === elStyle . display || 'hidden' == elStyle . visibility ) {
2017-01-20 01:16:13 +01:00
return false ;
}
2017-01-20 04:43:45 +01:00
// walk up
theEl = theEl . parentNode ;
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
return theEl === document ;
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
// is a dom element "viewable" on screen?
function isElementViewable ( el ) {
var theDoc = el . ownerDocument . documentElement ,
rect = el . getBoundingClientRect ( ) ,
docScrollWidth = theDoc . scrollWidth ,
2019-04-23 15:34:05 +02:00
docScrollHeight = theDoc . scrollHeight ,
2017-01-20 04:43:45 +01:00
leftOffset = rect . left - theDoc . clientLeft ,
topOffset = rect . top - theDoc . clientTop ,
theRect ;
if ( ! isElementVisible ( el ) || ! el . offsetParent || 10 > el . clientWidth || 10 > el . clientHeight ) {
2017-01-20 01:16:13 +01:00
return false ;
}
2017-01-20 04:43:45 +01:00
var rects = el . getClientRects ( ) ;
if ( 0 === rects . length ) {
2017-01-20 01:16:13 +01:00
return false ;
}
2017-01-20 04:43:45 +01:00
for ( var i = 0 ; i < rects . length ; i ++ ) {
if ( theRect = rects [ i ] , theRect . left > docScrollWidth || 0 > theRect . right ) {
2017-01-20 01:16:13 +01:00
return false ;
}
}
2017-01-20 04:43:45 +01:00
2019-04-23 15:34:05 +02:00
if ( 0 > leftOffset || leftOffset > docScrollWidth || 0 > topOffset || topOffset > docScrollHeight ) {
2017-01-20 01:16:13 +01:00
return false ;
}
2017-01-20 04:43:45 +01:00
// walk the tree
2017-10-03 22:11:11 +02:00
for ( var pointEl = el . ownerDocument . elementFromPoint ( leftOffset + ( rect . right > window . innerWidth ? ( window . innerWidth - leftOffset ) / 2 : rect . width / 2 ) , topOffset + ( rect . bottom > window . innerHeight ? ( window . innerHeight - topOffset ) / 2 : rect . height / 2 ) ) ; pointEl && pointEl !== el && pointEl !== document ; ) {
2017-01-20 04:43:45 +01:00
if ( pointEl . tagName && 'string' === typeof pointEl . tagName && 'label' === pointEl . tagName . toLowerCase ( )
&& el . labels && 0 < el . labels . length ) {
return 0 <= Array . prototype . slice . call ( el . labels ) . indexOf ( pointEl ) ;
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
// walk up
pointEl = pointEl . parentNode ;
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
return pointEl === el ;
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
function getElementForOPID ( opId ) {
var theEl ;
if ( void 0 === opId || null === opId ) {
2017-01-20 01:16:13 +01:00
return null ;
}
2017-01-20 04:43:45 +01:00
2017-01-20 01:16:13 +01:00
try {
2017-01-20 04:43:45 +01:00
var formEls = Array . prototype . slice . call ( getFormElements ( document ) ) ;
var filteredFormEls = formEls . filter ( function ( el ) {
return el . opid == opId ;
2017-01-20 01:16:13 +01:00
} ) ;
2017-01-20 04:43:45 +01:00
if ( 0 < filteredFormEls . length ) {
theEl = filteredFormEls [ 0 ] , 1 < filteredFormEls . length && console . warn ( 'More than one element found with opid ' + opId ) ;
2017-01-20 01:16:13 +01:00
} else {
2017-01-20 04:43:45 +01:00
var theIndex = parseInt ( opId . split ( '__' ) [ 1 ] , 10 ) ;
isNaN ( theIndex ) || ( theEl = formEls [ theIndex ] ) ;
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
} catch ( e ) {
console . error ( 'An unexpected error occurred: ' + e ) ;
2017-01-20 01:16:13 +01:00
} finally {
2017-01-20 04:43:45 +01:00
return theEl ;
2017-01-20 01:16:13 +01:00
}
}
// get all the form elements that we care about
2017-10-11 15:01:59 +02:00
function getFormElements ( theDoc , limit ) {
2017-10-26 05:07:00 +02:00
// START MODIFICATION
2017-01-20 01:16:13 +01:00
var els = [ ] ;
try {
2017-10-12 04:11:34 +02:00
var elsList = theDoc . querySelectorAll ( 'input:not([type="hidden"]):not([type="submit"]):not([type="reset"])' +
2017-10-03 22:11:11 +02:00
':not([type="button"]):not([type="image"]):not([type="file"]), select' ) ;
2017-10-12 04:11:34 +02:00
els = Array . prototype . slice . call ( elsList ) ;
2017-01-20 01:16:13 +01:00
} catch ( e ) { }
2017-10-11 15:01:59 +02:00
2017-10-26 15:19:18 +02:00
if ( ! limit || els . length <= limit ) {
return els ;
}
2017-11-04 15:21:23 +01:00
// non-checkboxes/radios have higher priority
var returnEls = [ ] ;
var unimportantEls = [ ] ;
for ( var i = 0 ; i < els . length ; i ++ ) {
if ( returnEls . length >= limit ) {
break ;
}
2017-10-26 15:19:18 +02:00
2017-11-04 15:21:23 +01:00
var el = els [ i ] ;
var type = el . type ? el . type . toLowerCase ( ) : el . type ;
if ( type === 'checkbox' || type === 'radio' ) {
unimportantEls . push ( el ) ;
2017-10-26 15:19:18 +02:00
}
2017-11-04 15:21:23 +01:00
else {
returnEls . push ( el ) ;
2017-10-26 15:19:18 +02:00
}
2017-11-04 15:21:23 +01:00
}
var unimportantElsToAdd = limit - returnEls . length ;
if ( unimportantElsToAdd > 0 ) {
returnEls = returnEls . concat ( unimportantEls . slice ( 0 , unimportantElsToAdd ) ) ;
}
2017-10-26 15:19:18 +02:00
2017-11-04 15:21:23 +01:00
return returnEls ;
2017-10-26 05:07:00 +02:00
// END MODIFICATION
2017-01-20 01:16:13 +01:00
}
2017-01-20 04:43:45 +01:00
// focus the element and optionally restore its original value
function focusElement ( el , setVal ) {
if ( setVal ) {
var initialValue = el . value ;
el . focus ( ) ;
if ( el . value !== initialValue ) {
el . value = initialValue ;
}
2017-01-20 01:16:13 +01:00
} else {
2017-01-20 04:43:45 +01:00
el . focus ( ) ;
2017-01-20 01:16:13 +01:00
}
}
2017-01-20 04:43:45 +01:00
return JSON . stringify ( getPageDetails ( document , 'oneshotUUID' ) ) ;
2016-09-16 05:24:45 +02:00
}
function fill ( document , fillScript , undefined ) {
2016-11-27 06:31:45 +01:00
var isFirefox = navigator . userAgent . indexOf ( 'Firefox' ) !== - 1 || navigator . userAgent . indexOf ( 'Gecko/' ) !== - 1 ;
2017-01-20 01:16:13 +01:00
var markTheFilling = true ,
animateTheFilling = true ;
// Check if URL is not secure when the original saved one was
function urlNotSecure ( savedURL ) {
var passwordInputs = null ;
if ( ! savedURL ) {
return false ;
}
return 0 === savedURL . indexOf ( 'https://' ) && 'http:' === document . location . protocol && ( passwordInputs = document . querySelectorAll ( 'input[type=password]' ) ,
2017-10-03 22:11:11 +02:00
0 < passwordInputs . length && ( confirmResult = confirm ( 'Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page.\\n\\nDo you still wish to fill this login?' ) ,
0 == confirmResult ) ) ? true : false ;
2017-01-20 01:16:13 +01:00
}
function doFill ( fillScript ) {
var fillScriptOps ,
theOpIds = [ ] ,
fillScriptProperties = fillScript . properties ,
operationDelayMs = 1 ,
doOperation ,
operationsToDo = [ ] ;
fillScriptProperties &&
2017-10-03 22:11:11 +02:00
fillScriptProperties . delay _between _operations &&
( operationDelayMs = fillScriptProperties . delay _between _operations ) ;
2017-01-20 01:16:13 +01:00
if ( urlNotSecure ( fillScript . savedURL ) ) {
return ;
}
doOperation = function ( ops , theOperation ) {
var op = ops [ 0 ] ;
if ( void 0 === op ) {
theOperation ( ) ;
} else {
// should we delay?
if ( 'delay' === op . operation || 'delay' === op [ 0 ] ) {
operationDelayMs = op . parameters ? op . parameters [ 0 ] : op [ 1 ] ;
} else {
if ( op = normalizeOp ( op ) ) {
for ( var opIndex = 0 ; opIndex < op . length ; opIndex ++ ) {
- 1 === operationsToDo . indexOf ( op [ opIndex ] ) && operationsToDo . push ( op [ opIndex ] ) ;
}
}
theOpIds = theOpIds . concat ( operationsToDo . map ( function ( operationToDo ) {
return operationToDo && operationToDo . hasOwnProperty ( 'opid' ) ? operationToDo . opid : null ;
} ) ) ;
}
setTimeout ( function ( ) {
doOperation ( ops . slice ( 1 ) , theOperation ) ;
} , operationDelayMs ) ;
}
} ;
if ( fillScriptOps = fillScript . options ) {
fillScriptOps . hasOwnProperty ( 'animate' ) && ( animateTheFilling = fillScriptOps . animate ) ,
2017-10-03 22:11:11 +02:00
fillScriptOps . hasOwnProperty ( 'markFilling' ) && ( markTheFilling = fillScriptOps . markFilling ) ;
2017-01-20 01:16:13 +01:00
}
// don't mark a password filling
fillScript . itemType && 'fillPassword' === fillScript . itemType && ( markTheFilling = false ) ;
if ( ! fillScript . hasOwnProperty ( 'script' ) ) {
return ;
}
// custom fill script
fillScriptOps = fillScript . script ;
doOperation ( fillScriptOps , function ( ) {
// Done now
// Do we have anything to autosubmit?
if ( fillScript . hasOwnProperty ( 'autosubmit' ) && 'function' == typeof autosubmit ) {
fillScript . itemType && 'fillLogin' !== fillScript . itemType || ( 0 < operationsToDo . length ? setTimeout ( function ( ) {
autosubmit ( fillScript . autosubmit , fillScriptProperties . allow _clicky _autosubmit , operationsToDo ) ;
} , AUTOSUBMIT _DELAY ) : DEBUG _AUTOSUBMIT && console . log ( '[AUTOSUBMIT] Not attempting to submit since no fields were filled: ' , operationsToDo ) )
}
// handle protectedGlobalPage
if ( 'object' == typeof protectedGlobalPage ) {
protectedGlobalPage . b ( 'fillItemResults' , {
documentUUID : documentUUID ,
fillContextIdentifier : fillScript . fillContextIdentifier ,
usedOpids : theOpIds
} , function ( ) {
fillingItemType = null ;
} )
}
} ) ;
}
// fill for reference
var thisFill = {
fill _by _opid : doFillByOpId ,
fill _by _query : doFillByQuery ,
click _on _opid : doClickByOpId ,
click _on _query : doClickByQuery ,
touch _all _fields : touchAllFields ,
simple _set _value _by _query : doSimpleSetByQuery ,
focus _by _opid : doFocusByOpId ,
delay : null
} ;
// normalize the op versus the reference
function normalizeOp ( op ) {
var thisOperation ;
if ( op . hasOwnProperty ( 'operation' ) && op . hasOwnProperty ( 'parameters' ) ) {
thisOperation = op . operation , op = op . parameters ;
} else {
if ( '[object Array]' === Object . prototype . toString . call ( op ) ) {
thisOperation = op [ 0 ] ,
2017-10-03 22:11:11 +02:00
op = op . splice ( 1 ) ;
2017-01-20 01:16:13 +01:00
} else {
return null ;
}
}
return thisFill . hasOwnProperty ( thisOperation ) ? thisFill [ thisOperation ] . apply ( this , op ) : null ;
}
// do a fill by opid operation
function doFillByOpId ( opId , op ) {
var el = getElementByOpId ( opId ) ;
return el ? ( fillTheElement ( el , op ) , [ el ] ) : null ;
}
// do a fill by query operation
function doFillByQuery ( query , op ) {
var elements = selectAllFromDoc ( query ) ;
return Array . prototype . map . call ( Array . prototype . slice . call ( elements ) , function ( el ) {
fillTheElement ( el , op ) ;
return el ;
} , this ) ;
}
// do a simple set value by query
function doSimpleSetByQuery ( query , valueToSet ) {
var elements = selectAllFromDoc ( query ) ,
arr = [ ] ;
Array . prototype . forEach . call ( Array . prototype . slice . call ( elements ) , function ( el ) {
el . disabled || el . a || el . readOnly || void 0 === el . value || ( el . value = valueToSet , arr . push ( el ) ) ;
} ) ;
return arr ;
}
// focus by opid
function doFocusByOpId ( opId ) {
var el = getElementByOpId ( opId )
if ( el ) {
'function' === typeof el . click && el . click ( ) ,
2017-10-03 22:11:11 +02:00
'function' === typeof el . focus && doFocusElement ( el , true ) ;
2017-01-20 01:16:13 +01:00
}
return null ;
}
// do a click by opid operation
function doClickByOpId ( opId ) {
var el = getElementByOpId ( opId ) ;
return el ? clickElement ( el ) ? [ el ] : null : null ;
}
// do a click by query operation
function doClickByQuery ( query ) {
query = selectAllFromDoc ( query ) ;
return Array . prototype . map . call ( Array . prototype . slice . call ( query ) , function ( el ) {
clickElement ( el ) ;
'function' === typeof el . click && el . click ( ) ;
'function' === typeof el . focus && doFocusElement ( el , true ) ;
return [ el ] ;
} , this ) ;
}
var checkRadioTrueOps = {
'true' : true ,
y : true ,
1 : true ,
yes : true ,
'✓' : true
} ,
2017-10-03 22:11:11 +02:00
styleTimeout = 200 ;
2017-01-20 01:16:13 +01:00
// fill an element
function fillTheElement ( el , op ) {
var shouldCheck ;
if ( el && null !== op && void 0 !== op && ! ( el . disabled || el . a || el . readOnly ) ) {
switch ( markTheFilling && el . form && ! el . form . opfilled && ( el . form . opfilled = true ) ,
el . type ? el . type . toLowerCase ( ) : null ) {
case 'checkbox' :
shouldCheck = op && 1 <= op . length && checkRadioTrueOps . hasOwnProperty ( op . toLowerCase ( ) ) && true === checkRadioTrueOps [ op . toLowerCase ( ) ] ;
el . checked === shouldCheck || doAllFillOperations ( el , function ( theEl ) {
theEl . checked = shouldCheck ;
} ) ;
break ;
case 'radio' :
true === checkRadioTrueOps [ op . toLowerCase ( ) ] && el . click ( ) ;
break ;
default :
el . value == op || doAllFillOperations ( el , function ( theEl ) {
theEl . value = op ;
} ) ;
}
}
}
// do all the full operations needed
function doAllFillOperations ( el , afterValSetFunc ) {
setValueForElement ( el ) ;
afterValSetFunc ( el ) ;
setValueForElementByEvent ( el ) ;
2018-04-14 04:42:32 +02:00
canSeeElementToStyle ( el ) && ( el . className += ' com-bitwarden-browser-animated-fill' ,
2017-10-03 22:11:11 +02:00
setTimeout ( function ( ) {
2018-04-14 04:42:32 +02:00
// START MODIFICATION
el && el . className && ( el . className = el . className . replace ( /(\\s)?com-bitwarden-browser-animated-fill/ , '' ) ) ;
// END MODIFICATION
2017-10-03 22:11:11 +02:00
} , styleTimeout ) ) ;
2017-01-20 01:16:13 +01:00
}
document . elementForOPID = getElementByOpId ;
// normalize the event since firefox handles events differently than others
function normalizeEvent ( el , eventName ) {
var ev ;
if ( isFirefox ) {
ev = document . createEvent ( 'KeyboardEvent' ) ;
ev . initKeyEvent ( eventName , true , false , null , false , false , false , false , 0 , 0 ) ;
}
else {
ev = el . ownerDocument . createEvent ( 'Events' ) ;
ev . initEvent ( eventName , true , false ) ;
ev . charCode = 0 ;
ev . keyCode = 0 ;
ev . which = 0 ;
ev . srcElement = el ;
ev . target = el ;
}
return ev ;
}
// set value of the given element
function setValueForElement ( el ) {
var valueToSet = el . value ;
clickElement ( el ) ;
doFocusElement ( el , false ) ;
el . dispatchEvent ( normalizeEvent ( el , 'keydown' ) ) ;
el . dispatchEvent ( normalizeEvent ( el , 'keypress' ) ) ;
el . dispatchEvent ( normalizeEvent ( el , 'keyup' ) ) ;
el . value !== valueToSet && ( el . value = valueToSet ) ;
}
// set value of the given element by using events
function setValueForElementByEvent ( el ) {
var valueToSet = el . value ,
ev1 = el . ownerDocument . createEvent ( 'HTMLEvents' ) ,
ev2 = el . ownerDocument . createEvent ( 'HTMLEvents' ) ;
el . dispatchEvent ( normalizeEvent ( el , 'keydown' ) ) ;
el . dispatchEvent ( normalizeEvent ( el , 'keypress' ) ) ;
el . dispatchEvent ( normalizeEvent ( el , 'keyup' ) ) ;
ev2 . initEvent ( 'input' , true , true ) ;
el . dispatchEvent ( ev2 ) ;
ev1 . initEvent ( 'change' , true , true ) ;
el . dispatchEvent ( ev1 ) ;
el . blur ( ) ;
el . value !== valueToSet && ( el . value = valueToSet ) ;
}
// click on an element
function clickElement ( el ) {
if ( ! el || el && 'function' !== typeof el . click ) {
return false ;
}
el . click ( ) ;
return true ;
}
// get all fields we care about
function getAllFields ( ) {
var r = RegExp ( '((\\\\b|_|-)pin(\\\\b|_|-)|password|passwort|kennwort|passe|contraseña|senha|密码|adgangskode|hasło|wachtwoord)' , 'i' ) ;
return Array . prototype . slice . call ( selectAllFromDoc ( "input[type='text']" ) ) . filter ( function ( el ) {
return el . value && r . test ( el . value ) ;
} , this ) ;
}
// touch all the fields
function touchAllFields ( ) {
getAllFields ( ) . forEach ( function ( el ) {
setValueForElement ( el ) ;
el . click && el . click ( ) ;
setValueForElementByEvent ( el ) ;
} ) ;
}
// can we see the element to apply some styling?
function canSeeElementToStyle ( el ) {
var currentEl ;
if ( currentEl = animateTheFilling ) {
a : {
currentEl = el ;
for ( var owner = el . ownerDocument , owner = owner ? owner . defaultView : { } , theStyle ; currentEl && currentEl !== document ; ) {
theStyle = owner . getComputedStyle ? owner . getComputedStyle ( currentEl , null ) : currentEl . style ;
if ( ! theStyle ) {
currentEl = true ;
break a ;
}
if ( 'none' === theStyle . display || 'hidden' == theStyle . visibility ) {
currentEl = false ;
break a ;
}
currentEl = currentEl . parentNode ;
}
currentEl = currentEl === document ;
}
}
return currentEl ? - 1 !== 'email text password number tel url' . split ( ' ' ) . indexOf ( el . type || '' ) : false ;
}
// find the element for this operation
function getElementByOpId ( theOpId ) {
var theElement ;
if ( void 0 === theOpId || null === theOpId ) {
return null ;
}
try {
var elements = Array . prototype . slice . call ( selectAllFromDoc ( 'input, select, button' ) ) ;
var filteredElements = elements . filter ( function ( o ) {
return o . opid == theOpId ;
} ) ;
if ( 0 < filteredElements . length ) {
theElement = filteredElements [ 0 ] ,
2017-10-03 22:11:11 +02:00
1 < filteredElements . length && console . warn ( 'More than one element found with opid ' + theOpId ) ;
2017-01-20 01:16:13 +01:00
} else {
var elIndex = parseInt ( theOpId . split ( '__' ) [ 1 ] , 10 ) ;
isNaN ( elIndex ) || ( theElement = elements [ elIndex ] ) ;
}
} catch ( e ) {
console . error ( 'An unexpected error occurred: ' + e ) ;
} finally {
return theElement ;
}
}
// helper for doc.querySelectorAll
function selectAllFromDoc ( theSelector ) {
var d = document , elements = [ ] ;
try {
elements = d . querySelectorAll ( theSelector ) ;
} catch ( e ) { }
return elements ;
}
// focus an element and optionally re-set its value after focusing
function doFocusElement ( el , setValue ) {
if ( setValue ) {
var existingValue = el . value ;
el . focus ( ) ;
el . value !== existingValue && ( el . value = existingValue ) ;
} else {
el . focus ( ) ;
}
}
doFill ( fillScript ) ;
return JSON . stringify ( {
success : true
} ) ;
2016-09-16 05:24:45 +02:00
}
/ *
End 1 Password Extension
* /
2018-01-16 03:40:42 +01:00
if ( ( typeof safari !== 'undefined' ) && navigator . userAgent . indexOf ( ' Safari/' ) !== - 1 &&
navigator . userAgent . indexOf ( 'Chrome' ) === - 1 ) {
2018-09-27 16:51:32 +02:00
if ( window . _ _bitwardenFrameId == null ) {
window . _ _bitwardenFrameId = Math . floor ( Math . random ( ) * Math . floor ( 99999999 ) ) ;
}
2018-01-12 21:20:19 +01:00
safari . self . addEventListener ( 'message' , function ( msgEvent ) {
2018-01-12 20:44:44 +01:00
var msg = msgEvent . message ;
2018-09-27 17:07:17 +02:00
if ( msg . bitwardenFrameId != null && window . _ _bitwardenFrameId !== msg . bitwardenFrameId ) {
2018-09-27 16:51:32 +02:00
return ;
}
2018-01-12 20:44:44 +01:00
if ( msg . command === 'collectPageDetails' ) {
var pageDetails = collect ( document ) ;
var pageDetailsObj = JSON . parse ( pageDetails ) ;
2019-08-16 02:31:23 +02:00
safari . extension . dispatchMessage ( 'bitwarden' , {
2018-01-12 20:44:44 +01:00
command : 'collectPageDetailsResponse' ,
tab : msg . tab ,
details : pageDetailsObj ,
2018-09-27 16:51:32 +02:00
sender : msg . sender ,
bitwardenFrameId : window . _ _bitwardenFrameId
2018-01-12 20:44:44 +01:00
} ) ;
}
else if ( msg . command === 'fillForm' ) {
fill ( document , msg . fillScript ) ;
}
} , false ) ;
return ;
}
2016-09-16 05:24:45 +02:00
chrome . runtime . onMessage . addListener ( function ( msg , sender , sendResponse ) {
if ( msg . command === 'collectPageDetails' ) {
2017-10-02 16:29:46 +02:00
var pageDetails = collect ( document ) ;
2016-11-26 04:01:46 +01:00
var pageDetailsObj = JSON . parse ( pageDetails ) ;
chrome . runtime . sendMessage ( {
command : 'collectPageDetailsResponse' ,
2017-08-28 19:00:46 +02:00
tab : msg . tab ,
2016-12-30 08:09:54 +01:00
details : pageDetailsObj ,
2017-08-28 19:00:46 +02:00
sender : msg . sender
2016-11-26 04:01:46 +01:00
} ) ;
sendResponse ( ) ;
2016-09-16 05:24:45 +02:00
return true ;
}
else if ( msg . command === 'fillForm' ) {
fill ( document , msg . fillScript ) ;
sendResponse ( ) ;
return true ;
}
} ) ;
} ) ( ) ;