1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-12-24 16:49:26 +01:00

separated BrowserUtils from generic Utils

This commit is contained in:
Kyle Spearrin 2018-01-04 12:32:10 -05:00
parent ac0126b210
commit 0dd711471b
42 changed files with 474 additions and 451 deletions

View File

@ -14,6 +14,7 @@ import WindowsBackground from './windows.background';
import ApiService from '../services/api.service'; import ApiService from '../services/api.service';
import AppIdService from '../services/appId.service'; import AppIdService from '../services/appId.service';
import AutofillService from '../services/autofill.service'; import AutofillService from '../services/autofill.service';
import BrowserUtilsService from '../services/browserUtils.service';
import ChromeStorageService from '../services/chromeStorage.service'; import ChromeStorageService from '../services/chromeStorage.service';
import CipherService from '../services/cipher.service'; import CipherService from '../services/cipher.service';
import CollectionService from '../services/collection.service'; import CollectionService from '../services/collection.service';
@ -36,6 +37,7 @@ import { StorageService } from '../services/abstractions/storage.service';
export default class MainBackground { export default class MainBackground {
storageService: StorageService; storageService: StorageService;
i18nService: any; i18nService: any;
browserUtilsService: BrowserUtilsService;
utilsService: UtilsService; utilsService: UtilsService;
constantsService: ConstantsService; constantsService: ConstantsService;
cryptoService: CryptoService; cryptoService: CryptoService;
@ -75,12 +77,13 @@ export default class MainBackground {
// Services // Services
this.storageService = new ChromeStorageService(); this.storageService = new ChromeStorageService();
this.utilsService = new UtilsService(); this.utilsService = new UtilsService();
this.i18nService = i18nService(this.utilsService); this.browserUtilsService = new BrowserUtilsService();
this.constantsService = new ConstantsService(this.i18nService, this.utilsService); this.i18nService = i18nService(this.browserUtilsService);
this.constantsService = new ConstantsService(this.i18nService, this.browserUtilsService);
this.cryptoService = new CryptoService(this.storageService, this.storageService); this.cryptoService = new CryptoService(this.storageService, this.storageService);
this.tokenService = new TokenService(this.storageService); this.tokenService = new TokenService(this.storageService);
this.appIdService = new AppIdService(this.storageService); this.appIdService = new AppIdService(this.storageService);
this.apiService = new ApiService(this.tokenService, this.utilsService, this.apiService = new ApiService(this.tokenService, this.browserUtilsService,
(expired: boolean) => this.logout(expired)); (expired: boolean) => this.logout(expired));
this.environmentService = new EnvironmentService(this.apiService, this.storageService); this.environmentService = new EnvironmentService(this.apiService, this.storageService);
this.userService = new UserService(this.tokenService, this.storageService); this.userService = new UserService(this.tokenService, this.storageService);
@ -91,7 +94,7 @@ export default class MainBackground {
this.apiService, this.storageService); this.apiService, this.storageService);
this.collectionService = new CollectionService(this.cryptoService, this.userService, this.storageService); this.collectionService = new CollectionService(this.cryptoService, this.userService, this.storageService);
this.lockService = new LockService(this.cipherService, this.folderService, this.collectionService, this.lockService = new LockService(this.cipherService, this.folderService, this.collectionService,
this.cryptoService, this.utilsService, this.storageService, this.cryptoService, this.browserUtilsService, this.storageService,
() => this.setIcon(), () => this.refreshBadgeAndMenu()); () => this.setIcon(), () => this.refreshBadgeAndMenu());
this.syncService = new SyncService(this.userService, this.apiService, this.settingsService, this.syncService = new SyncService(this.userService, this.apiService, this.settingsService,
this.folderService, this.cipherService, this.cryptoService, this.collectionService, this.folderService, this.cipherService, this.cryptoService, this.collectionService,
@ -99,7 +102,7 @@ export default class MainBackground {
this.passwordGenerationService = new PasswordGenerationService(this.cryptoService, this.storageService); this.passwordGenerationService = new PasswordGenerationService(this.cryptoService, this.storageService);
this.totpService = new TotpService(this.storageService); this.totpService = new TotpService(this.storageService);
this.autofillService = new AutofillService(this.cipherService, this.tokenService, this.autofillService = new AutofillService(this.cipherService, this.tokenService,
this.totpService, this.utilsService); this.totpService, this.utilsService, this.browserUtilsService);
// Other fields // Other fields
this.sidebarAction = (typeof opr !== 'undefined') && opr.sidebarAction ? this.sidebarAction = (typeof opr !== 'undefined') && opr.sidebarAction ?
@ -111,7 +114,7 @@ export default class MainBackground {
this.passwordGenerationService); this.passwordGenerationService);
this.runtimeBackground = new RuntimeBackground(this, this.autofillService, this.cipherService); this.runtimeBackground = new RuntimeBackground(this, this.autofillService, this.cipherService);
this.tabsBackground = new TabsBackground(this); this.tabsBackground = new TabsBackground(this);
this.webRequestBackground = new WebRequestBackground(this.utilsService, this.cipherService); this.webRequestBackground = new WebRequestBackground(this.browserUtilsService, this.cipherService);
this.windowsBackground = new WindowsBackground(this); this.windowsBackground = new WindowsBackground(this);
} }
@ -251,7 +254,7 @@ export default class MainBackground {
}); });
// Firefox & Edge do not support writing to the clipboard from background // Firefox & Edge do not support writing to the clipboard from background
if (!this.utilsService.isFirefox() && !this.utilsService.isEdge()) { if (!this.browserUtilsService.isFirefox() && !this.browserUtilsService.isEdge()) {
await this.contextMenusCreate({ await this.contextMenusCreate({
type: 'normal', type: 'normal',
id: 'copy-username', id: 'copy-username',
@ -295,7 +298,7 @@ export default class MainBackground {
return; return;
} }
const tabDomain = UtilsService.getDomain(url); const tabDomain = BrowserUtilsService.getDomain(url);
if (tabDomain == null) { if (tabDomain == null) {
return; return;
} }
@ -370,7 +373,7 @@ export default class MainBackground {
}); });
} }
if (this.utilsService.isFirefox()) { if (this.browserUtilsService.isFirefox()) {
// Firefox does not support writing to the clipboard from background // Firefox does not support writing to the clipboard from background
return; return;
} }
@ -411,7 +414,7 @@ export default class MainBackground {
return; return;
} }
const tabDomain = UtilsService.getDomain(tab.url); const tabDomain = BrowserUtilsService.getDomain(tab.url);
if (tabDomain == null) { if (tabDomain == null) {
return; return;
} }
@ -489,7 +492,7 @@ export default class MainBackground {
}, },
}; };
if (this.utilsService.isFirefox()) { if (this.browserUtilsService.isFirefox()) {
await theAction.setIcon(options); await theAction.setIcon(options);
} else { } else {
return new Promise((resolve) => { return new Promise((resolve) => {

View File

@ -5,6 +5,7 @@ import BrowserApi from '../browser/browserApi';
import MainBackground from './main.background'; import MainBackground from './main.background';
import AutofillService from '../services/autofill.service'; import AutofillService from '../services/autofill.service';
import BrowserUtilsService from '../services/browserUtils.service';
import CipherService from '../services/cipher.service'; import CipherService from '../services/cipher.service';
import UtilsService from '../services/utils.service'; import UtilsService from '../services/utils.service';
@ -134,7 +135,7 @@ export default class RuntimeBackground {
} }
const loginInfo = this.main.loginsToAdd[i]; const loginInfo = this.main.loginsToAdd[i];
const tabDomain = UtilsService.getDomain(tab.url); const tabDomain = BrowserUtilsService.getDomain(tab.url);
if (tabDomain != null && tabDomain !== loginInfo.domain) { if (tabDomain != null && tabDomain !== loginInfo.domain) {
continue; continue;
} }
@ -172,7 +173,7 @@ export default class RuntimeBackground {
} }
const loginInfo = this.main.loginsToAdd[i]; const loginInfo = this.main.loginsToAdd[i];
const tabDomain = UtilsService.getDomain(tab.url); const tabDomain = BrowserUtilsService.getDomain(tab.url);
if (tabDomain != null && tabDomain !== loginInfo.domain) { if (tabDomain != null && tabDomain !== loginInfo.domain) {
continue; continue;
} }
@ -185,7 +186,7 @@ export default class RuntimeBackground {
} }
private async addLogin(loginInfo: any, tab: any) { private async addLogin(loginInfo: any, tab: any) {
const loginDomain = UtilsService.getDomain(loginInfo.url); const loginDomain = BrowserUtilsService.getDomain(loginInfo.url);
if (loginDomain == null) { if (loginDomain == null) {
return; return;
} }

View File

@ -1,14 +1,14 @@
import BrowserUtilsService from '../services/browserUtils.service';
import CipherService from '../services/cipher.service'; import CipherService from '../services/cipher.service';
import UtilsService from '../services/utils.service';
export default class WebRequestBackground { export default class WebRequestBackground {
private pendingAuthRequests: any[] = []; private pendingAuthRequests: any[] = [];
private webRequest: any; private webRequest: any;
private isFirefox: boolean; private isFirefox: boolean;
constructor(utilsService: UtilsService, private cipherService: CipherService) { constructor(browserUtilsService: BrowserUtilsService, private cipherService: CipherService) {
this.webRequest = (window as any).chrome.webRequest; this.webRequest = (window as any).chrome.webRequest;
this.isFirefox = utilsService.isFirefox(); this.isFirefox = browserUtilsService.isFirefox();
} }
async init() { async init() {
@ -24,7 +24,7 @@ export default class WebRequestBackground {
return; return;
} }
const domain = UtilsService.getDomain(details.url); const domain = BrowserUtilsService.getDomain(details.url);
if (domain == null) { if (domain == null) {
if (callback) { if (callback) {
callback(); callback();

View File

@ -11,7 +11,7 @@ import { Identity } from './identity';
import { Login } from './login'; import { Login } from './login';
import { SecureNote } from './secureNote'; import { SecureNote } from './secureNote';
import { UtilsService } from '../../services/abstractions/utils.service'; import { BrowserUtilsService } from '../../services/abstractions/browserUtils.service';
class Cipher extends Domain { class Cipher extends Domain {
id: string; id: string;
@ -32,7 +32,7 @@ class Cipher extends Domain {
fields: Field[]; fields: Field[];
collectionIds: string[]; collectionIds: string[];
private utilsService: UtilsService; private browserUtilsService: BrowserUtilsService;
constructor(obj?: CipherData, alreadyEncrypted: boolean = false, localData: any = null) { constructor(obj?: CipherData, alreadyEncrypted: boolean = false, localData: any = null) {
super(); super();
@ -119,12 +119,12 @@ class Cipher extends Domain {
model.login = await this.login.decrypt(this.organizationId); model.login = await this.login.decrypt(this.organizationId);
model.subTitle = model.login.username; model.subTitle = model.login.username;
if (model.login.uri) { if (model.login.uri) {
if (this.utilsService == null) { if (this.browserUtilsService == null) {
this.utilsService = chrome.extension.getBackgroundPage() this.browserUtilsService = chrome.extension.getBackgroundPage()
.bitwardenMain.utilsService as UtilsService; .bitwardenMain.browserUtilsService as BrowserUtilsService;
} }
model.login.domain = this.utilsService.getDomain(model.login.uri); model.login.domain = this.browserUtilsService.getDomain(model.login.uri);
} }
break; break;
case CipherType.SecureNote: case CipherType.SecureNote:

View File

@ -1,5 +1,5 @@
import { BrowserType } from '../../enums/browserType.enum'; import { BrowserType } from '../../enums/browserType.enum';
import { UtilsService } from '../../services/abstractions/utils.service'; import { BrowserUtilsService } from '../../services/abstractions/browserUtils.service';
class DeviceRequest { class DeviceRequest {
type: BrowserType; type: BrowserType;
@ -7,9 +7,9 @@ class DeviceRequest {
identifier: string; identifier: string;
pushToken?: string; pushToken?: string;
constructor(appId: string, utilsService: UtilsService) { constructor(appId: string, browserUtilsService: BrowserUtilsService) {
this.type = utilsService.getBrowser(); this.type = browserUtilsService.getBrowser();
this.name = utilsService.getBrowserString(); this.name = browserUtilsService.getBrowserString();
this.identifier = appId; this.identifier = appId;
this.pushToken = null; this.pushToken = null;
} }

View File

@ -1,10 +1,10 @@
angular angular
.module('bit.accounts') .module('bit.accounts')
.controller('accountsHintController', function ($scope, $state, apiService, toastr, $q, utilsService, .controller('accountsHintController', function ($scope, $state, apiService, toastr, $q, browserUtilsService,
$analytics, i18nService, $timeout) { $analytics, i18nService, $timeout) {
$timeout(function () { $timeout(function () {
utilsService.initListSectionItemListeners(document, angular); browserUtilsService.initListSectionItemListeners(document, angular);
document.getElementById('email').focus(); document.getElementById('email').focus();
}, 500); }, 500);

View File

@ -2,9 +2,9 @@ angular
.module('bit.accounts') .module('bit.accounts')
.controller('accountsLoginController', function ($scope, $state, $stateParams, authService, userService, toastr, .controller('accountsLoginController', function ($scope, $state, $stateParams, authService, userService, toastr,
utilsService, $analytics, i18nService, $timeout) { browserUtilsService, $analytics, i18nService, $timeout) {
$timeout(function () { $timeout(function () {
utilsService.initListSectionItemListeners(document, angular); browserUtilsService.initListSectionItemListeners(document, angular);
if ($stateParams.email) { if ($stateParams.email) {
document.getElementById('master-password').focus(); document.getElementById('master-password').focus();
} }

View File

@ -1,11 +1,11 @@
angular angular
.module('bit.accounts') .module('bit.accounts')
.controller('accountsLoginTwoFactorController', function ($scope, $state, authService, toastr, utilsService, SweetAlert, .controller('accountsLoginTwoFactorController', function ($scope, $state, authService, toastr, browserUtilsService, SweetAlert,
$analytics, i18nService, $stateParams, $filter, constantsService, $timeout, $window, cryptoService, apiService, $analytics, i18nService, $stateParams, $filter, constantsService, $timeout, $window, cryptoService, apiService,
environmentService) { environmentService) {
$timeout(function () { $timeout(function () {
utilsService.initListSectionItemListeners(document, angular); browserUtilsService.initListSectionItemListeners(document, angular);
}, 500); }, 500);
$scope.i18n = i18nService; $scope.i18n = i18nService;
@ -130,7 +130,7 @@ angular
var provider = $filter('filter')(constants.twoFactorProviderInfo, { type: keys[i], active: true }); var provider = $filter('filter')(constants.twoFactorProviderInfo, { type: keys[i], active: true });
if (provider.length && provider[0].priority > providerPriority) { if (provider.length && provider[0].priority > providerPriority) {
if (provider[0].type == constants.twoFactorProvider.u2f && (typeof $window.u2f === 'undefined') && if (provider[0].type == constants.twoFactorProvider.u2f && (typeof $window.u2f === 'undefined') &&
!utilsService.isChrome() && !utilsService.isOpera()) { !browserUtilsService.isChrome() && !browserUtilsService.isOpera()) {
continue; continue;
} }
@ -183,8 +183,8 @@ angular
params = providers[constants.twoFactorProvider.email]; params = providers[constants.twoFactorProvider.email];
$scope.twoFactorEmail = params.Email; $scope.twoFactorEmail = params.Email;
if (chrome.extension.getViews({ type: 'popup' }).length > 0 && !utilsService.inSidebar($window) && if (chrome.extension.getViews({ type: 'popup' }).length > 0 && !browserUtilsService.inSidebar($window) &&
!utilsService.inTab($window) && !utilsService.inPopout($window)) { !browserUtilsService.inTab($window) && !browserUtilsService.inPopout($window)) {
SweetAlert.swal({ SweetAlert.swal({
title: i18nService.twoStepLogin, title: i18nService.twoStepLogin,
text: i18nService.popup2faCloseMessage, text: i18nService.popup2faCloseMessage,

View File

@ -3,9 +3,9 @@ angular
.controller( .controller(
'accountsRegisterController', 'accountsRegisterController',
function ($scope, $state, cryptoService, toastr, $q, apiService, utilsService, $analytics, i18nService, $timeout) { function ($scope, $state, cryptoService, toastr, $q, apiService, browserUtilsService, $analytics, i18nService, $timeout) {
$timeout(function () { $timeout(function () {
utilsService.initListSectionItemListeners(document, angular); browserUtilsService.initListSectionItemListeners(document, angular);
document.getElementById('email').focus(); document.getElementById('email').focus();
}, 500); }, 500);

View File

@ -1,4 +1,4 @@
angular angular
.module('bit.accounts') .module('bit.accounts')
.controller('accountsTwoFactorMethodsController', function ($scope, $state, $stateParams, constantsService, .controller('accountsTwoFactorMethodsController', function ($scope, $state, $stateParams, constantsService,
@ -25,7 +25,8 @@
if (providers.hasOwnProperty(constants.twoFactorProvider.duo)) { if (providers.hasOwnProperty(constants.twoFactorProvider.duo)) {
add(constants.twoFactorProvider.duo); add(constants.twoFactorProvider.duo);
} }
if (providers.hasOwnProperty(constants.twoFactorProvider.u2f) && (utilsService.isChrome() || utilsService.isOpera())) { if (providers.hasOwnProperty(constants.twoFactorProvider.u2f) &&
(browserUtilsService.isChrome() || browserUtilsService.isOpera())) {
add(constants.twoFactorProvider.u2f); add(constants.twoFactorProvider.u2f);
} }

View File

@ -1,6 +1,6 @@
import * as template from './action-buttons.component.html'; import * as template from './action-buttons.component.html';
import { UtilsService } from '../../../services/abstractions/utils.service'; import { BrowserUtilsService } from '../../../services/abstractions/browserUtils.service';
export class ActionButtonsController implements ng.IController { export class ActionButtonsController implements ng.IController {
onView: Function; onView: Function;
@ -11,7 +11,7 @@ export class ActionButtonsController implements ng.IController {
constants: any; constants: any;
constructor(private i18nService: any, private $analytics: any, private constantsService: any, private toastr: any, constructor(private i18nService: any, private $analytics: any, private constantsService: any, private toastr: any,
private $timeout: any, private $window: any, private utilsService: UtilsService) { private $timeout: any, private $window: any, private browserUtilsService: BrowserUtilsService) {
this.i18n = i18nService; this.i18n = i18nService;
this.constants = constantsService; this.constants = constantsService;
} }
@ -22,7 +22,7 @@ export class ActionButtonsController implements ng.IController {
if (self.cipher.login.uri.startsWith('http://') || self.cipher.login.uri.startsWith('https://')) { if (self.cipher.login.uri.startsWith('http://') || self.cipher.login.uri.startsWith('https://')) {
self.$analytics.eventTrack('Launched Website From Listing'); self.$analytics.eventTrack('Launched Website From Listing');
chrome.tabs.create({ url: self.cipher.login.uri }); chrome.tabs.create({ url: self.cipher.login.uri });
if (self.utilsService.inPopup(self.$window)) { if (self.browserUtilsService.inPopup(self.$window)) {
self.$window.close(); self.$window.close();
} }
} }

View File

@ -1,11 +1,11 @@
import * as template from './pop-out.component.html'; import * as template from './pop-out.component.html';
import { UtilsService } from '../../../services/abstractions/utils.service'; import { BrowserUtilsService } from '../../../services/abstractions/browserUtils.service';
export class PopOutController implements ng.IController { export class PopOutController implements ng.IController {
i18n: any; i18n: any;
constructor(private $analytics: any, private $window: any, private utilsService: UtilsService, constructor(private $analytics: any, private $window: any, private browserUtilsService: BrowserUtilsService,
private i18nService: any) { private i18nService: any) {
this.i18n = i18nService; this.i18n = i18nService;
} }
@ -14,7 +14,7 @@ export class PopOutController implements ng.IController {
this.$analytics.eventTrack('Expand Vault'); this.$analytics.eventTrack('Expand Vault');
let href = this.$window.location.href; let href = this.$window.location.href;
if (this.utilsService.isEdge()) { if (this.browserUtilsService.isEdge()) {
const popupIndex = href.indexOf('/popup/'); const popupIndex = href.indexOf('/popup/');
if (popupIndex > -1) { if (popupIndex > -1) {
href = href.substring(popupIndex); href = href.substring(popupIndex);
@ -39,7 +39,7 @@ export class PopOutController implements ng.IController {
height: bodyRect.height, height: bodyRect.height,
}); });
if (this.utilsService.inPopup(this.$window)) { if (this.browserUtilsService.inPopup(this.$window)) {
this.$window.close(); this.$window.close();
} }
} else { } else {

View File

@ -1,5 +1,6 @@
import { CipherType } from '../../../enums/cipherType.enum'; import { CipherType } from '../../../enums/cipherType.enum';
import { BrowserUtilsService } from '../../../services/abstractions/browserUtils.service';
import { UtilsService } from '../../../services/abstractions/utils.service'; import { UtilsService } from '../../../services/abstractions/utils.service';
import * as template from './current.component.html'; import * as template from './current.component.html';
@ -17,12 +18,13 @@ export class CurrentController {
inSidebar: boolean = false; inSidebar: boolean = false;
disableSearch: boolean = false; disableSearch: boolean = false;
constructor($scope: any, private cipherService: any, private utilsService: UtilsService, private toastr: any, constructor($scope: any, private cipherService: any, private browserUtilsService: BrowserUtilsService,
private $window: any, private $state: any, private $timeout: any, private autofillService: any, private utilsService: UtilsService, private toastr: any, private $window: any, private $state: any,
private $analytics: any, private i18nService: any, private $filter: any) { private $timeout: any, private autofillService: any, private $analytics: any, private i18nService: any,
private $filter: any) {
this.i18n = i18nService; this.i18n = i18nService;
this.inSidebar = utilsService.inSidebar($window); this.inSidebar = browserUtilsService.inSidebar($window);
this.disableSearch = utilsService.isEdge(); this.disableSearch = browserUtilsService.isEdge();
$scope.$on('syncCompleted', (event: any, successfully: boolean) => { $scope.$on('syncCompleted', (event: any, successfully: boolean) => {
if (this.loaded) { if (this.loaded) {
@ -76,10 +78,10 @@ export class CurrentController {
fromBackground: false, fromBackground: false,
}).then((totpCode: string) => { }).then((totpCode: string) => {
this.$analytics.eventTrack('Autofilled'); this.$analytics.eventTrack('Autofilled');
if (totpCode && this.utilsService.isFirefox()) { if (totpCode && this.browserUtilsService.isFirefox()) {
this.utilsService.copyToClipboard(totpCode, document); this.utilsService.copyToClipboard(totpCode, document);
} }
if (this.utilsService.inPopup(this.$window)) { if (this.browserUtilsService.inPopup(this.$window)) {
this.$window.close(); this.$window.close();
} }
}).catch(() => { }).catch(() => {
@ -105,7 +107,7 @@ export class CurrentController {
return; return;
} }
this.domain = this.utilsService.getDomain(this.url); this.domain = this.browserUtilsService.getDomain(this.url);
chrome.tabs.sendMessage(tabs[0].id, { chrome.tabs.sendMessage(tabs[0].id, {
command: 'collectPageDetails', command: 'collectPageDetails',

View File

@ -1,22 +1,22 @@
import * as angular from 'angular'; import * as angular from 'angular';
import * as template from './lock.component.html'; import * as template from './lock.component.html';
import { BrowserUtilsService } from '../../../services/abstractions/browserUtils.service';
import { CryptoService } from '../../../services/abstractions/crypto.service'; import { CryptoService } from '../../../services/abstractions/crypto.service';
import { UtilsService } from '../../../services/abstractions/utils.service';
export class LockController { export class LockController {
i18n: any; i18n: any;
masterPassword: string; masterPassword: string;
constructor(public $state: any, public i18nService: any, private $timeout: any, private utilsService: UtilsService, constructor(public $state: any, public i18nService: any, private $timeout: any,
public cryptoService: CryptoService, public toastr: any, public userService: any, private browserUtilsService: BrowserUtilsService, public cryptoService: CryptoService, public toastr: any,
public SweetAlert: any) { public userService: any, public SweetAlert: any) {
this.i18n = i18nService; this.i18n = i18nService;
} }
$onInit() { $onInit() {
this.$timeout(() => { this.$timeout(() => {
this.utilsService.initListSectionItemListeners(document, angular); this.browserUtilsService.initListSectionItemListeners(document, angular);
document.getElementById('master-password').focus(); document.getElementById('master-password').focus();
}, 500); }, 500);
} }

View File

@ -1,13 +1,13 @@
import { DeviceRequest } from '../../../models/request/deviceRequest'; import { DeviceRequest } from '../../../models/request/deviceRequest';
import { TokenRequest } from '../../../models/request/tokenRequest'; import { TokenRequest } from '../../../models/request/tokenRequest';
import { BrowserUtilsService } from '../../../services/abstractions/browserUtils.service';
import { CryptoService } from '../../../services/abstractions/crypto.service'; import { CryptoService } from '../../../services/abstractions/crypto.service';
import { UtilsService } from '../../../services/abstractions/utils.service';
class AuthService { class AuthService {
constructor(public cryptoService: CryptoService, public apiService: any, public userService: any, constructor(public cryptoService: CryptoService, public apiService: any, public userService: any,
public tokenService: any, public $rootScope: any, public appIdService: any, public utilsService: UtilsService, public tokenService: any, public $rootScope: any, public appIdService: any,
public constantsService: any) { public browserUtilsService: BrowserUtilsService, public constantsService: any) {
} }
async logIn(email: string, masterPassword: string, twoFactorProvider?: number, async logIn(email: string, masterPassword: string, twoFactorProvider?: number,
@ -19,7 +19,7 @@ class AuthService {
const storedTwoFactorToken = await this.tokenService.getTwoFactorToken(email); const storedTwoFactorToken = await this.tokenService.getTwoFactorToken(email);
const hashedPassword = await this.cryptoService.hashPassword(masterPassword, key); const hashedPassword = await this.cryptoService.hashPassword(masterPassword, key);
const deviceRequest = new DeviceRequest(appId, this.utilsService); const deviceRequest = new DeviceRequest(appId, this.browserUtilsService);
let request: TokenRequest; let request: TokenRequest;

View File

@ -1,3 +1,4 @@
import { BrowserUtilsService } from '../../../services/abstractions/browserUtils.service';
import { CryptoService } from '../../../services/abstractions/crypto.service'; import { CryptoService } from '../../../services/abstractions/crypto.service';
import { StorageService } from '../../../services/abstractions/storage.service'; import { StorageService } from '../../../services/abstractions/storage.service';
import { UtilsService } from '../../../services/abstractions/utils.service'; import { UtilsService } from '../../../services/abstractions/utils.service';
@ -19,6 +20,7 @@ export const cipherService = getBackgroundService<CryptoService>('cipherService'
export const syncService = getBackgroundService<any>('syncService'); export const syncService = getBackgroundService<any>('syncService');
export const autofillService = getBackgroundService<any>('autofillService'); export const autofillService = getBackgroundService<any>('autofillService');
export const passwordGenerationService = getBackgroundService<any>('passwordGenerationService'); export const passwordGenerationService = getBackgroundService<any>('passwordGenerationService');
export const browserUtilsService = getBackgroundService<BrowserUtilsService>('browserUtilsService');
export const utilsService = getBackgroundService<UtilsService>('utilsService'); export const utilsService = getBackgroundService<UtilsService>('utilsService');
export const appIdService = getBackgroundService<any>('appIdService'); export const appIdService = getBackgroundService<any>('appIdService');
export const i18nService = getBackgroundService<any>('i18nService'); export const i18nService = getBackgroundService<any>('i18nService');

View File

@ -20,6 +20,7 @@ export default angular
.factory('syncService', backgroundServices.syncService) .factory('syncService', backgroundServices.syncService)
.factory('autofillService', backgroundServices.autofillService) .factory('autofillService', backgroundServices.autofillService)
.factory('passwordGenerationService', backgroundServices.passwordGenerationService) .factory('passwordGenerationService', backgroundServices.passwordGenerationService)
.factory('browserUtilsService', backgroundServices.browserUtilsService)
.factory('utilsService', backgroundServices.utilsService) .factory('utilsService', backgroundServices.utilsService)
.factory('appIdService', backgroundServices.appIdService) .factory('appIdService', backgroundServices.appIdService)
.factory('i18nService', backgroundServices.i18nService) .factory('i18nService', backgroundServices.i18nService)

View File

@ -1,5 +1,5 @@
import * as angular from 'angular'; import * as angular from 'angular';
import UtilsService from '../../../services/utils.service'; import BrowserUtilsService from '../../../services/browserUtils.service';
import * as template from './environment.component.html'; import * as template from './environment.component.html';
export class EnvironmentController { export class EnvironmentController {
@ -10,12 +10,12 @@ export class EnvironmentController {
baseUrl: string; baseUrl: string;
i18n: any; i18n: any;
constructor(private i18nService: any, private $analytics: any, utilsService: UtilsService, constructor(private i18nService: any, private $analytics: any, browserUtilsService: BrowserUtilsService,
private environmentService: any, private toastr: any, private $timeout: ng.ITimeoutService) { private environmentService: any, private toastr: any, private $timeout: ng.ITimeoutService) {
this.i18n = i18nService; this.i18n = i18nService;
$timeout(() => { $timeout(() => {
utilsService.initListSectionItemListeners(document, angular); browserUtilsService.initListSectionItemListeners(document, angular);
}, 500); }, 500);
this.baseUrl = environmentService.baseUrl || ''; this.baseUrl = environmentService.baseUrl || '';

View File

@ -1,17 +1,18 @@
import * as angular from 'angular'; import * as angular from 'angular';
import { Folder } from '../../../../models/domain/folder'; import { Folder } from '../../../../models/domain/folder';
import { UtilsService } from '../../../../services/abstractions/utils.service';
import * as template from './add-folder.component.html'; import * as template from './add-folder.component.html';
import { BrowserUtilsService } from '../../../../services/abstractions/browserUtils.service';
export class AddFolderController { export class AddFolderController {
savePromise: any; savePromise: any;
folder: {}; folder: {};
i18n: any; i18n: any;
constructor(private folderService: any, private $state: any, private toastr: any, utilsService: UtilsService, constructor(private folderService: any, private $state: any, private toastr: any,
private $analytics: any, private i18nService: any, $timeout: any) { browserUtilsService: BrowserUtilsService, private $analytics: any, private i18nService: any, $timeout: any) {
$timeout(() => { $timeout(() => {
utilsService.initListSectionItemListeners(document, angular); browserUtilsService.initListSectionItemListeners(document, angular);
document.getElementById('name').focus(); document.getElementById('name').focus();
}, 500); }, 500);

View File

@ -1,8 +1,9 @@
import * as angular from 'angular'; import * as angular from 'angular';
import { Folder } from '../../../../models/domain/folder'; import { Folder } from '../../../../models/domain/folder';
import UtilsService from '../../../../services/utils.service';
import * as template from './edit-folder.component.html'; import * as template from './edit-folder.component.html';
import { BrowserUtilsService } from '../../../../services/abstractions/browserUtils.service';
export class EditFolderController { export class EditFolderController {
$transition$: any; $transition$: any;
folderId: any; folderId: any;
@ -11,12 +12,12 @@ export class EditFolderController {
folder: Folder; folder: Folder;
constructor($scope: any, $stateParams: any, private folderService: any, private toastr: any, private $state: any, constructor($scope: any, $stateParams: any, private folderService: any, private toastr: any, private $state: any,
private SweetAlert: any, utilsService: UtilsService, private $analytics: any, private i18nService: any, private SweetAlert: any, browserUtilsService: BrowserUtilsService, private $analytics: any,
$timeout: any) { private i18nService: any, $timeout: any) {
this.i18n = i18nService; this.i18n = i18nService;
$timeout(() => { $timeout(() => {
utilsService.initListSectionItemListeners(document, angular); browserUtilsService.initListSectionItemListeners(document, angular);
document.getElementById('name').focus(); document.getElementById('name').focus();
}, 500); }, 500);

View File

@ -1,6 +1,6 @@
import * as angular from 'angular'; import * as angular from 'angular';
import { BrowserUtilsService } from '../../../services/abstractions/browserUtils.service';
import { StorageService } from '../../../services/abstractions/storage.service'; import { StorageService } from '../../../services/abstractions/storage.service';
import { UtilsService } from '../../../services/abstractions/utils.service';
import StateService from '../services/state.service'; import StateService from '../services/state.service';
import * as template from './options.component.html'; import * as template from './options.component.html';
@ -14,12 +14,12 @@ export class OptionsController {
i18n: any; i18n: any;
constructor(private i18nService: any, private $analytics: any, private constantsService: any, constructor(private i18nService: any, private $analytics: any, private constantsService: any,
private utilsService: UtilsService, private totpService: any, private stateService: StateService, private browserUtilsService: BrowserUtilsService, private totpService: any, private stateService: StateService,
private storageService: StorageService, private $timeout: ng.ITimeoutService) { private storageService: StorageService, private $timeout: ng.ITimeoutService) {
this.i18n = i18nService; this.i18n = i18nService;
$timeout(() => { $timeout(() => {
utilsService.initListSectionItemListeners(document, angular); browserUtilsService.initListSectionItemListeners(document, angular);
}, 500); }, 500);
this.loadSettings(); this.loadSettings();
@ -31,7 +31,7 @@ export class OptionsController {
const disableGa = await this.storageService.get<boolean>( const disableGa = await this.storageService.get<boolean>(
this.constantsService.disableGaKey); this.constantsService.disableGaKey);
this.disableGa = disableGa || (this.utilsService.isFirefox() && disableGa === undefined); this.disableGa = disableGa || (this.browserUtilsService.isFirefox() && disableGa === undefined);
this.disableAddLoginNotification = await this.storageService.get<boolean>( this.disableAddLoginNotification = await this.storageService.get<boolean>(
this.constantsService.disableAddLoginNotificationKey); this.constantsService.disableAddLoginNotificationKey);

View File

@ -1,8 +1,8 @@
import * as angular from 'angular'; import * as angular from 'angular';
import { BrowserType } from '../../../enums/browserType.enum'; import { BrowserType } from '../../../enums/browserType.enum';
import { BrowserUtilsService } from '../../../services/abstractions/browserUtils.service';
import { CryptoService } from '../../../services/abstractions/crypto.service'; import { CryptoService } from '../../../services/abstractions/crypto.service';
import { StorageService } from '../../../services/abstractions/storage.service'; import { StorageService } from '../../../services/abstractions/storage.service';
import { UtilsService } from '../../../services/abstractions/utils.service';
import ConstantsService from '../../../services/constants.service'; import ConstantsService from '../../../services/constants.service';
import * as template from './settings.component.html'; import * as template from './settings.component.html';
@ -27,17 +27,17 @@ export class SettingsController {
i18n: any; i18n: any;
showOnLocked: boolean; showOnLocked: boolean;
constructor(private $state: any, private SweetAlert: any, private utilsService: UtilsService, constructor(private $state: any, private SweetAlert: any, private browserUtilsService: BrowserUtilsService,
private $analytics: any, private i18nService: any, private constantsService: ConstantsService, private $analytics: any, private i18nService: any, private constantsService: ConstantsService,
private cryptoService: CryptoService, private lockService: any, private storageService: StorageService, private cryptoService: CryptoService, private lockService: any, private storageService: StorageService,
private $timeout: ng.ITimeoutService) { private $timeout: ng.ITimeoutService) {
this.i18n = i18nService; this.i18n = i18nService;
$timeout(() => { $timeout(() => {
utilsService.initListSectionItemListeners(document, angular); browserUtilsService.initListSectionItemListeners(document, angular);
}, 500); }, 500);
this.showOnLocked = !utilsService.isFirefox() && !utilsService.isEdge(); this.showOnLocked = !browserUtilsService.isFirefox() && !browserUtilsService.isEdge();
this.storageService.get(constantsService.lockOptionKey).then((lockOption: number) => { this.storageService.get(constantsService.lockOptionKey).then((lockOption: number) => {
if (lockOption != null) { if (lockOption != null) {
let option = lockOption.toString(); let option = lockOption.toString();
@ -146,7 +146,7 @@ export class SettingsController {
rate() { rate() {
this.$analytics.eventTrack('Rate Extension'); this.$analytics.eventTrack('Rate Extension');
chrome.tabs.create({ chrome.tabs.create({
url: RateUrls[this.utilsService.getBrowser()], url: RateUrls[this.browserUtilsService.getBrowser()],
}); });
} }
} }

View File

@ -1,7 +1,7 @@
import * as angular from 'angular'; import * as angular from 'angular';
import * as template from './password-generator.component.html'; import * as template from './password-generator.component.html';
import { UtilsService } from '../../../services/abstractions/utils.service'; import { BrowserUtilsService } from '../../../services/abstractions/browserUtils.service';
export class PasswordGeneratorController { export class PasswordGeneratorController {
$transition$: any; $transition$: any;
@ -13,7 +13,7 @@ export class PasswordGeneratorController {
i18n: any; i18n: any;
constructor(private $state: any, private passwordGenerationService: any, constructor(private $state: any, private passwordGenerationService: any,
private toastr: any, private utilsService: UtilsService, private $analytics: any, private toastr: any, private browserUtilsService: BrowserUtilsService, private $analytics: any,
private i18nService: any, private $timeout: any) { private i18nService: any, private $timeout: any) {
this.i18n = i18nService; this.i18n = i18nService;
@ -41,9 +41,8 @@ export class PasswordGeneratorController {
this.showSelect = this.addState || this.editState; this.showSelect = this.addState || this.editState;
const self = this;
this.$timeout(() => { this.$timeout(() => {
self.utilsService.initListSectionItemListeners(document, angular); this.browserUtilsService.initListSectionItemListeners(document, angular);
}, 500); }, 500);
} }

View File

@ -1,6 +1,6 @@
import * as template from './tools.component.html'; import * as template from './tools.component.html';
import { UtilsService } from '../../../services/abstractions/utils.service'; import { BrowserUtilsService } from '../../../services/abstractions/browserUtils.service';
export class ToolsController { export class ToolsController {
showExport: boolean; showExport: boolean;
@ -8,10 +8,10 @@ export class ToolsController {
private webVaultBaseUrl: string = 'https://vault.bitwarden.com'; private webVaultBaseUrl: string = 'https://vault.bitwarden.com';
constructor(private SweetAlert: any, private i18nService: any, constructor(private SweetAlert: any, private i18nService: any,
private $analytics: any, private utilsService: UtilsService, private $analytics: any, private browserUtilsService: BrowserUtilsService,
private environmentService: any) { private environmentService: any) {
this.i18n = i18nService; this.i18n = i18nService;
this.showExport = !utilsService.isEdge(); this.showExport = !browserUtilsService.isEdge();
if (environmentService.baseUrl) { if (environmentService.baseUrl) {
this.webVaultBaseUrl = environmentService.baseUrl; this.webVaultBaseUrl = environmentService.baseUrl;
} else if (environmentService.webVaultUrl) { } else if (environmentService.webVaultUrl) {

View File

@ -2,7 +2,7 @@ angular
.module('bit.vault') .module('bit.vault')
.controller('vaultAddCipherController', function ($scope, $state, $stateParams, cipherService, folderService, .controller('vaultAddCipherController', function ($scope, $state, $stateParams, cipherService, folderService,
cryptoService, toastr, utilsService, $analytics, i18nService, constantsService, $timeout) { cryptoService, toastr, browserUtilsService, $analytics, i18nService, constantsService, $timeout) {
$scope.i18n = i18nService; $scope.i18n = i18nService;
$scope.constants = constantsService; $scope.constants = constantsService;
$scope.addFieldType = constantsService.fieldType.text.toString(); $scope.addFieldType = constantsService.fieldType.text.toString();
@ -31,7 +31,7 @@ angular
} }
$timeout(function () { $timeout(function () {
utilsService.initListSectionItemListeners(document, angular); browserUtilsService.initListSectionItemListeners(document, angular);
if (!$stateParams.cipher && $scope.cipher.name && $scope.cipher.login && $scope.cipher.login.uri) { if (!$stateParams.cipher && $scope.cipher.name && $scope.cipher.login && $scope.cipher.login.uri) {
document.getElementById('loginUsername').focus(); document.getElementById('loginUsername').focus();
@ -49,7 +49,7 @@ angular
$scope.cipher.type = parseInt($scope.selectedType); $scope.cipher.type = parseInt($scope.selectedType);
$timeout(function () { $timeout(function () {
utilsService.initListSectionItemListeners(document, angular); browserUtilsService.initListSectionItemListeners(document, angular);
}, 500); }, 500);
}; };
@ -106,7 +106,7 @@ angular
}); });
$timeout(function () { $timeout(function () {
utilsService.initListSectionItemListeners(document, angular); browserUtilsService.initListSectionItemListeners(document, angular);
}, 500); }, 500);
}; };

View File

@ -2,9 +2,9 @@ angular
.module('bit.vault') .module('bit.vault')
.controller('vaultAttachmentsController', function ($scope, $state, $stateParams, cipherService, toastr, .controller('vaultAttachmentsController', function ($scope, $state, $stateParams, cipherService, toastr,
SweetAlert, utilsService, $analytics, i18nService, cryptoService, tokenService, $timeout) { SweetAlert, browserUtilsService, $analytics, i18nService, cryptoService, tokenService, $timeout) {
$timeout(function () { $timeout(function () {
utilsService.initListSectionItemListeners(document, angular); browserUtilsService.initListSectionItemListeners(document, angular);
}, 500); }, 500);
$scope.i18n = i18nService; $scope.i18n = i18nService;

View File

@ -2,14 +2,14 @@ angular
.module('bit.vault') .module('bit.vault')
.controller('vaultController', function ($scope, $rootScope, cipherService, folderService, $q, $state, $stateParams, toastr, .controller('vaultController', function ($scope, $rootScope, cipherService, folderService, $q, $state, $stateParams, toastr,
syncService, utilsService, $analytics, i18nService, stateService, $timeout, $window, collectionService, $filter) { syncService, browserUtilsService, $analytics, i18nService, stateService, $timeout, $window, collectionService, $filter) {
var stateKey = 'vault', var stateKey = 'vault',
state = stateService.getState(stateKey) || {}; state = stateService.getState(stateKey) || {};
stateService.removeState('viewGrouping'); stateService.removeState('viewGrouping');
$scope.i18n = i18nService; $scope.i18n = i18nService;
$scope.showGroupingCounts = !utilsService.isEdge(); $scope.showGroupingCounts = !browserUtilsService.isEdge();
$scope.disableSearch = utilsService.isEdge(); $scope.disableSearch = browserUtilsService.isEdge();
document.getElementById('search').focus(); document.getElementById('search').focus();
var syncOnLoad = $stateParams.syncOnLoad; var syncOnLoad = $stateParams.syncOnLoad;

View File

@ -2,15 +2,15 @@ angular
.module('bit.vault') .module('bit.vault')
.controller('vaultEditCipherController', function ($scope, $state, $stateParams, cipherService, folderService, .controller('vaultEditCipherController', function ($scope, $state, $stateParams, cipherService, folderService,
cryptoService, toastr, SweetAlert, utilsService, $analytics, i18nService, constantsService, $timeout) { cryptoService, toastr, SweetAlert, browserUtilsService, $analytics, i18nService, constantsService, $timeout) {
$timeout(function () { $timeout(function () {
utilsService.initListSectionItemListeners(document, angular); browserUtilsService.initListSectionItemListeners(document, angular);
document.getElementById('name').focus(); document.getElementById('name').focus();
}, 500); }, 500);
$scope.i18n = i18nService; $scope.i18n = i18nService;
$scope.constants = constantsService; $scope.constants = constantsService;
$scope.showAttachments = !utilsService.isEdge(); $scope.showAttachments = !browserUtilsService.isEdge();
$scope.addFieldType = constantsService.fieldType.text.toString(); $scope.addFieldType = constantsService.fieldType.text.toString();
$scope.selectedType = constantsService.cipherType.login.toString(); $scope.selectedType = constantsService.cipherType.login.toString();
var cipherId = $stateParams.cipherId; var cipherId = $stateParams.cipherId;
@ -40,7 +40,7 @@ angular
$scope.cipher.type = parseInt($scope.selectedType); $scope.cipher.type = parseInt($scope.selectedType);
$timeout(function () { $timeout(function () {
utilsService.initListSectionItemListeners(document, angular); browserUtilsService.initListSectionItemListeners(document, angular);
}, 500); }, 500);
}; };
@ -123,7 +123,7 @@ angular
}); });
$timeout(function () { $timeout(function () {
utilsService.initListSectionItemListeners(document, angular); browserUtilsService.initListSectionItemListeners(document, angular);
}, 500); }, 500);
}; };

View File

@ -2,11 +2,11 @@ angular
.module('bit.vault') .module('bit.vault')
.controller('vaultViewCipherController', function ($scope, $state, $stateParams, cipherService, toastr, .controller('vaultViewCipherController', function ($scope, $state, $stateParams, cipherService, toastr,
$analytics, i18nService, utilsService, totpService, $timeout, tokenService, $window, cryptoService, SweetAlert, $analytics, i18nService, browserUtilsService, totpService, $timeout, tokenService, $window, cryptoService, SweetAlert,
constantsService) { constantsService) {
$scope.constants = constantsService; $scope.constants = constantsService;
$scope.i18n = i18nService; $scope.i18n = i18nService;
$scope.showAttachments = !utilsService.isEdge(); $scope.showAttachments = !browserUtilsService.isEdge();
var from = $stateParams.from, var from = $stateParams.from,
totpInterval = null; totpInterval = null;
@ -30,7 +30,7 @@ angular
if (model.login.uri) { if (model.login.uri) {
$scope.cipher.showLaunch = model.login.uri.startsWith('http://') || model.login.uri.startsWith('https://'); $scope.cipher.showLaunch = model.login.uri.startsWith('http://') || model.login.uri.startsWith('https://');
var domain = utilsService.getDomain(model.login.uri); var domain = browserUtilsService.getDomain(model.login.uri);
if (domain) { if (domain) {
$scope.cipher.login.website = domain; $scope.cipher.login.website = domain;
} }

View File

@ -2,7 +2,7 @@ angular
.module('bit.vault') .module('bit.vault')
.controller('vaultViewGroupingController', function ($scope, cipherService, folderService, $q, $state, $stateParams, toastr, .controller('vaultViewGroupingController', function ($scope, cipherService, folderService, $q, $state, $stateParams, toastr,
syncService, $analytics, i18nService, stateService, utilsService, $timeout, $window, collectionService) { syncService, $analytics, i18nService, stateService, browserUtilsService, $timeout, $window, collectionService) {
var stateKey = 'viewGrouping', var stateKey = 'viewGrouping',
state = stateService.getState(stateKey) || {}; state = stateService.getState(stateKey) || {};
@ -60,7 +60,7 @@ angular
var cipherPromise = cipherService.getAllDecryptedForGrouping($scope.grouping.id, $scope.folderGrouping) var cipherPromise = cipherService.getAllDecryptedForGrouping($scope.grouping.id, $scope.folderGrouping)
.then(function (ciphers) { .then(function (ciphers) {
if (utilsService.isEdge()) { if (browserUtilsService.isEdge()) {
// Edge is super slow at sorting // Edge is super slow at sorting
decCiphers = ciphers; decCiphers = ciphers;
} }

View File

@ -9,9 +9,9 @@
return; return;
} }
var gaTrackingId = bgMain.utilsService.analyticsId(); var gaTrackingId = bgMain.browserUtilsService.analyticsId();
var gaFunc = null; var gaFunc = null;
var isFirefox = bgMain.utilsService.isFirefox(); var isFirefox = bgMain.browserUtilsService.isFirefox();
window.GoogleAnalyticsObject = 'ga'; window.GoogleAnalyticsObject = 'ga';
window[window.GoogleAnalyticsObject] = function (action, param1, param2, param3, param4) { window[window.GoogleAnalyticsObject] = function (action, param1, param2, param3, param4) {

View File

@ -0,0 +1,17 @@
import { BrowserType } from '../../enums/browserType.enum';
export interface BrowserUtilsService {
getBrowser(): BrowserType;
getBrowserString(): string;
isFirefox(): boolean;
isChrome(): boolean;
isEdge(): boolean;
isOpera(): boolean;
analyticsId(): string;
initListSectionItemListeners(doc: Document, angular: any): void;
getDomain(uriString: string): string;
inSidebar(theWindow: Window): boolean;
inTab(theWindow: Window): boolean;
inPopout(theWindow: Window): boolean;
inPopup(theWindow: Window): boolean;
}

View File

@ -1,19 +1,4 @@
import { BrowserType } from '../../enums/browserType.enum';
export interface UtilsService { export interface UtilsService {
getBrowser(): BrowserType;
getBrowserString(): string;
isFirefox(): boolean;
isChrome(): boolean;
isEdge(): boolean;
isOpera(): boolean;
analyticsId(): string;
initListSectionItemListeners(doc: Document, angular: any): void;
copyToClipboard(text: string, doc?: Document): void; copyToClipboard(text: string, doc?: Document): void;
getDomain(uriString: string): string;
getHostname(uriString: string): string; getHostname(uriString: string): string;
inSidebar(theWindow: Window): boolean;
inTab(theWindow: Window): boolean;
inPopout(theWindow: Window): boolean;
inPopup(theWindow: Window): boolean;
} }

View File

@ -1,7 +1,7 @@
import AppIdService from './appId.service'; import AppIdService from './appId.service';
import BrowserUtilsService from './browserUtils.service';
import ConstantsService from './constants.service'; import ConstantsService from './constants.service';
import TokenService from './token.service'; import TokenService from './token.service';
import UtilsService from './utils.service';
import EnvironmentUrls from '../models/domain/environmentUrls'; import EnvironmentUrls from '../models/domain/environmentUrls';
@ -32,11 +32,13 @@ export default class ApiService {
urlsSet: boolean = false; urlsSet: boolean = false;
baseUrl: string; baseUrl: string;
identityBaseUrl: string; identityBaseUrl: string;
deviceType: string;
logoutCallback: Function; logoutCallback: Function;
constructor(private tokenService: TokenService, private utilsService: UtilsService, constructor(private tokenService: TokenService, browserUtilsService: BrowserUtilsService,
logoutCallback: Function) { logoutCallback: Function) {
this.logoutCallback = logoutCallback; this.logoutCallback = logoutCallback;
this.deviceType = browserUtilsService.getBrowser().toString();
} }
setUrls(urls: EnvironmentUrls) { setUrls(urls: EnvironmentUrls) {
@ -86,7 +88,7 @@ export default class ApiService {
headers: new Headers({ headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
'Accept': 'application/json', 'Accept': 'application/json',
'Device-Type': this.utilsService.getBrowser().toString(), 'Device-Type': this.deviceType,
}), }),
method: 'POST', method: 'POST',
})); }));
@ -126,7 +128,7 @@ export default class ApiService {
cache: 'no-cache', cache: 'no-cache',
headers: new Headers({ headers: new Headers({
'Content-Type': 'application/json; charset=utf-8', 'Content-Type': 'application/json; charset=utf-8',
'Device-Type': this.utilsService.getBrowser().toString(), 'Device-Type': this.deviceType,
}), }),
method: 'POST', method: 'POST',
})); }));
@ -146,7 +148,7 @@ export default class ApiService {
headers: new Headers({ headers: new Headers({
'Accept': 'application/json', 'Accept': 'application/json',
'Authorization': authHeader, 'Authorization': authHeader,
'Device-Type': this.utilsService.getBrowser().toString(), 'Device-Type': this.deviceType,
}), }),
})); }));
@ -164,7 +166,7 @@ export default class ApiService {
cache: 'no-cache', cache: 'no-cache',
headers: new Headers({ headers: new Headers({
'Content-Type': 'application/json; charset=utf-8', 'Content-Type': 'application/json; charset=utf-8',
'Device-Type': this.utilsService.getBrowser().toString(), 'Device-Type': this.deviceType,
}), }),
method: 'POST', method: 'POST',
})); }));
@ -181,7 +183,7 @@ export default class ApiService {
cache: 'no-cache', cache: 'no-cache',
headers: new Headers({ headers: new Headers({
'Content-Type': 'application/json; charset=utf-8', 'Content-Type': 'application/json; charset=utf-8',
'Device-Type': this.utilsService.getBrowser().toString(), 'Device-Type': this.deviceType,
}), }),
method: 'POST', method: 'POST',
})); }));
@ -203,7 +205,7 @@ export default class ApiService {
'Accept': 'application/json', 'Accept': 'application/json',
'Authorization': authHeader, 'Authorization': authHeader,
'Content-Type': 'application/json; charset=utf-8', 'Content-Type': 'application/json; charset=utf-8',
'Device-Type': this.utilsService.getBrowser().toString(), 'Device-Type': this.deviceType,
}), }),
method: 'POST', method: 'POST',
})); }));
@ -226,7 +228,7 @@ export default class ApiService {
'Accept': 'application/json', 'Accept': 'application/json',
'Authorization': authHeader, 'Authorization': authHeader,
'Content-Type': 'application/json; charset=utf-8', 'Content-Type': 'application/json; charset=utf-8',
'Device-Type': this.utilsService.getBrowser().toString(), 'Device-Type': this.deviceType,
}), }),
method: 'PUT', method: 'PUT',
})); }));
@ -246,7 +248,7 @@ export default class ApiService {
cache: 'no-cache', cache: 'no-cache',
headers: new Headers({ headers: new Headers({
'Authorization': authHeader, 'Authorization': authHeader,
'Device-Type': this.utilsService.getBrowser().toString(), 'Device-Type': this.deviceType,
}), }),
method: 'DELETE', method: 'DELETE',
})); }));
@ -268,7 +270,7 @@ export default class ApiService {
'Accept': 'application/json', 'Accept': 'application/json',
'Authorization': authHeader, 'Authorization': authHeader,
'Content-Type': 'application/json; charset=utf-8', 'Content-Type': 'application/json; charset=utf-8',
'Device-Type': this.utilsService.getBrowser().toString(), 'Device-Type': this.deviceType,
}), }),
method: 'POST', method: 'POST',
})); }));
@ -291,7 +293,7 @@ export default class ApiService {
'Accept': 'application/json', 'Accept': 'application/json',
'Authorization': authHeader, 'Authorization': authHeader,
'Content-Type': 'application/json; charset=utf-8', 'Content-Type': 'application/json; charset=utf-8',
'Device-Type': this.utilsService.getBrowser().toString(), 'Device-Type': this.deviceType,
}), }),
method: 'PUT', method: 'PUT',
})); }));
@ -311,7 +313,7 @@ export default class ApiService {
cache: 'no-cache', cache: 'no-cache',
headers: new Headers({ headers: new Headers({
'Authorization': authHeader, 'Authorization': authHeader,
'Device-Type': this.utilsService.getBrowser().toString(), 'Device-Type': this.deviceType,
}), }),
method: 'DELETE', method: 'DELETE',
})); }));
@ -332,7 +334,7 @@ export default class ApiService {
headers: new Headers({ headers: new Headers({
'Accept': 'application/json', 'Accept': 'application/json',
'Authorization': authHeader, 'Authorization': authHeader,
'Device-Type': this.utilsService.getBrowser().toString(), 'Device-Type': this.deviceType,
}), }),
method: 'POST', method: 'POST',
})); }));
@ -352,7 +354,7 @@ export default class ApiService {
cache: 'no-cache', cache: 'no-cache',
headers: new Headers({ headers: new Headers({
'Authorization': authHeader, 'Authorization': authHeader,
'Device-Type': this.utilsService.getBrowser().toString(), 'Device-Type': this.deviceType,
}), }),
method: 'DELETE', method: 'DELETE',
})); }));
@ -372,7 +374,7 @@ export default class ApiService {
headers: new Headers({ headers: new Headers({
'Accept': 'application/json', 'Accept': 'application/json',
'Authorization': authHeader, 'Authorization': authHeader,
'Device-Type': this.utilsService.getBrowser().toString(), 'Device-Type': this.deviceType,
}), }),
})); }));
@ -430,7 +432,7 @@ export default class ApiService {
headers: new Headers({ headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
'Accept': 'application/json', 'Accept': 'application/json',
'Device-Type': this.utilsService.getBrowser().toString(), 'Device-Type': this.deviceType,
}), }),
method: 'POST', method: 'POST',
})); }));

View File

@ -5,6 +5,7 @@ import AutofillField from '../models/domain/autofillField';
import AutofillPageDetails from '../models/domain/autofillPageDetails'; import AutofillPageDetails from '../models/domain/autofillPageDetails';
import AutofillScript from '../models/domain/autofillScript'; import AutofillScript from '../models/domain/autofillScript';
import BrowserUtilsService from './browserUtils.service';
import CipherService from './cipher.service'; import CipherService from './cipher.service';
import TokenService from './token.service'; import TokenService from './token.service';
import TotpService from './totp.service'; import TotpService from './totp.service';
@ -92,7 +93,8 @@ var IsoProvinces: { [id: string]: string; } = {
export default class AutofillService { export default class AutofillService {
constructor(public cipherService: CipherService, public tokenService: TokenService, constructor(public cipherService: CipherService, public tokenService: TokenService,
public totpService: TotpService, public utilsService: UtilsService) { public totpService: TotpService, public utilsService: UtilsService,
public browserUtilsService: BrowserUtilsService) {
} }
getFormsWithPasswordFields(pageDetails: AutofillPageDetails): any[] { getFormsWithPasswordFields(pageDetails: AutofillPageDetails): any[] {
@ -167,7 +169,7 @@ export default class AutofillService {
}, { frameId: pd.frameId }); }, { frameId: pd.frameId });
if (options.cipher.type !== CipherType.Login || totpPromise || if (options.cipher.type !== CipherType.Login || totpPromise ||
(options.fromBackground && this.utilsService.isFirefox()) || options.skipTotp || (options.fromBackground && this.browserUtilsService.isFirefox()) || options.skipTotp ||
!options.cipher.login.totp || !this.tokenService.getPremium()) { !options.cipher.login.totp || !this.tokenService.getPremium()) {
return; return;
} }
@ -205,7 +207,7 @@ export default class AutofillService {
return; return;
} }
const tabDomain = UtilsService.getDomain(tab.url); const tabDomain = BrowserUtilsService.getDomain(tab.url);
if (tabDomain == null) { if (tabDomain == null) {
return; return;
} }

View File

@ -0,0 +1,84 @@
import BrowserUtilsService from './browserUtils.service';
import { BrowserType } from '../enums/browserType.enum';
describe('Browser Utils Service', () => {
describe('getDomain', () => {
it('should fail for invalid urls', () => {
expect(BrowserUtilsService.getDomain(null)).toBeNull();
expect(BrowserUtilsService.getDomain(undefined)).toBeNull();
expect(BrowserUtilsService.getDomain(' ')).toBeNull();
expect(BrowserUtilsService.getDomain('https://bit!:"_&ward.com')).toBeNull();
expect(BrowserUtilsService.getDomain('bitwarden')).toBeNull();
});
it('should handle urls without protocol', () => {
expect(BrowserUtilsService.getDomain('bitwarden.com')).toBe('bitwarden.com');
expect(BrowserUtilsService.getDomain('wrong://bitwarden.com')).toBe('bitwarden.com');
});
it('should handle valid urls', () => {
expect(BrowserUtilsService.getDomain('https://bitwarden')).toBe('bitwarden');
expect(BrowserUtilsService.getDomain('https://bitwarden.com')).toBe('bitwarden.com');
expect(BrowserUtilsService.getDomain('http://bitwarden.com')).toBe('bitwarden.com');
expect(BrowserUtilsService.getDomain('http://vault.bitwarden.com')).toBe('bitwarden.com');
expect(BrowserUtilsService.getDomain('https://user:password@bitwarden.com:8080/password/sites?and&query#hash')).toBe('bitwarden.com');
expect(BrowserUtilsService.getDomain('https://bitwarden.unknown')).toBe('bitwarden.unknown');
});
it('should support localhost and IP', () => {
expect(BrowserUtilsService.getDomain('https://localhost')).toBe('localhost');
expect(BrowserUtilsService.getDomain('https://192.168.1.1')).toBe('192.168.1.1');
});
});
describe('getBrowser', () => {
const original = navigator.userAgent;
// Reset the userAgent.
afterAll(() => {
Object.defineProperty(navigator, 'userAgent', {
value: original
});
});
it('should detect chrome', () => {
Object.defineProperty(navigator, 'userAgent', {
configurable: true,
value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36'
});
const browserUtilsService = new BrowserUtilsService();
expect(browserUtilsService.getBrowser()).toBe(BrowserType.Chrome);
});
it('should detect firefox', () => {
Object.defineProperty(navigator, 'userAgent', {
configurable: true,
value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0'
});
const browserUtilsService = new BrowserUtilsService();
expect(browserUtilsService.getBrowser()).toBe(BrowserType.Firefox);
});
it('should detect opera', () => {
Object.defineProperty(navigator, 'userAgent', {
configurable: true,
value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3175.3 Safari/537.36 OPR/49.0.2695.0 (Edition developer)'
});
const browserUtilsService = new BrowserUtilsService();
expect(browserUtilsService.getBrowser()).toBe(BrowserType.Opera);
});
it('should detect edge', () => {
Object.defineProperty(navigator, 'userAgent', {
configurable: true,
value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; ServiceUI 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063'
});
const browserUtilsService = new BrowserUtilsService();
expect(browserUtilsService.getBrowser()).toBe(BrowserType.Edge);
});
});
});

View File

@ -0,0 +1,215 @@
import * as tldjs from 'tldjs';
import { BrowserType } from '../enums/browserType.enum';
import { BrowserUtilsService as BrowserUtilsServiceInterface } from './abstractions/browserUtils.service';
const AnalyticsIds = {
[BrowserType.Chrome]: 'UA-81915606-6',
[BrowserType.Firefox]: 'UA-81915606-7',
[BrowserType.Opera]: 'UA-81915606-8',
[BrowserType.Edge]: 'UA-81915606-9',
[BrowserType.Vivaldi]: 'UA-81915606-15',
[BrowserType.Safari]: 'UA-81915606-16',
};
export default class BrowserUtilsService implements BrowserUtilsServiceInterface {
static getDomain(uriString: string): string {
if (uriString == null) {
return null;
}
uriString = uriString.trim();
if (uriString === '') {
return null;
}
if (uriString.startsWith('http://') || uriString.startsWith('https://')) {
try {
const url = new URL(uriString);
if (url.hostname === 'localhost' || BrowserUtilsService.validIpAddress(url.hostname)) {
return url.hostname;
}
const urlDomain = tldjs.getDomain(url.hostname);
return urlDomain != null ? urlDomain : url.hostname;
} catch (e) { }
}
const domain = tldjs.getDomain(uriString);
if (domain != null) {
return domain;
}
return null;
}
private static validIpAddress(ipString: string): boolean {
// tslint:disable-next-line
const ipRegex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
return ipRegex.test(ipString);
}
private browserCache: BrowserType = null;
private analyticsIdCache: string = null;
getBrowser(): BrowserType {
if (this.browserCache) {
return this.browserCache;
}
if (navigator.userAgent.indexOf('Firefox') !== -1 || navigator.userAgent.indexOf('Gecko/') !== -1) {
this.browserCache = BrowserType.Firefox;
} else if ((!!(window as any).opr && !!opr.addons) || !!(window as any).opera ||
navigator.userAgent.indexOf(' OPR/') >= 0) {
this.browserCache = BrowserType.Opera;
} else if (navigator.userAgent.indexOf(' Edge/') !== -1) {
this.browserCache = BrowserType.Edge;
} else if (navigator.userAgent.indexOf(' Vivaldi/') !== -1) {
this.browserCache = BrowserType.Vivaldi;
} else if ((window as any).chrome) {
this.browserCache = BrowserType.Chrome;
}
return this.browserCache;
}
getBrowserString(): string {
return BrowserType[this.getBrowser()].toLowerCase();
}
isFirefox(): boolean {
return this.getBrowser() === BrowserType.Firefox;
}
isChrome(): boolean {
return this.getBrowser() === BrowserType.Chrome;
}
isEdge(): boolean {
return this.getBrowser() === BrowserType.Edge;
}
isOpera(): boolean {
return this.getBrowser() === BrowserType.Opera;
}
isVivaldi(): boolean {
return this.getBrowser() === BrowserType.Vivaldi;
}
isSafari(): boolean {
return this.getBrowser() === BrowserType.Safari;
}
analyticsId(): string {
if (this.analyticsIdCache) {
return this.analyticsIdCache;
}
this.analyticsIdCache = AnalyticsIds[this.getBrowser()];
return this.analyticsIdCache;
}
initListSectionItemListeners(doc: Document, angular: any): void {
if (!doc) {
throw new Error('doc parameter required');
}
const sectionItems = doc.querySelectorAll(
'.list-section-item:not([data-bw-events="1"])');
const sectionFormItems = doc.querySelectorAll(
'.list-section-item:not([data-bw-events="1"]) input, ' +
'.list-section-item:not([data-bw-events="1"]) select, ' +
'.list-section-item:not([data-bw-events="1"]) textarea');
sectionItems.forEach((item) => {
(item as HTMLElement).dataset.bwEvents = '1';
item.addEventListener('click', (e) => {
if (e.defaultPrevented) {
return;
}
const el = e.target as HTMLElement;
// Some elements will already focus properly
if (el.tagName != null) {
switch (el.tagName.toLowerCase()) {
case 'label': case 'input': case 'textarea': case 'select':
return;
default:
break;
}
}
const cell = el.closest('.list-section-item');
if (!cell) {
return;
}
const textFilter = 'input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"])';
const text = cell.querySelectorAll(textFilter + ', textarea');
const checkbox = cell.querySelectorAll('input[type="checkbox"]');
const select = cell.querySelectorAll('select');
if (text.length > 0) {
(text[0] as HTMLElement).focus();
} else if (select.length > 0) {
(select[0] as HTMLElement).focus();
} else if (checkbox.length > 0) {
const cb = checkbox[0] as HTMLInputElement;
cb.checked = !cb.checked;
if (angular) {
angular.element(checkbox[0]).triggerHandler('click');
}
}
}, false);
});
sectionFormItems.forEach((item) => {
const itemCell = item.closest('.list-section-item');
(itemCell as HTMLElement).dataset.bwEvents = '1';
item.addEventListener('focus', (e: Event) => {
const el = e.target as HTMLElement;
const cell = el.closest('.list-section-item');
if (!cell) {
return;
}
cell.classList.add('active');
}, false);
item.addEventListener('blur', (e: Event) => {
const el = e.target as HTMLElement;
const cell = el.closest('.list-section-item');
if (!cell) {
return;
}
cell.classList.remove('active');
}, false);
});
}
getDomain(uriString: string): string {
return BrowserUtilsService.getDomain(uriString);
}
inSidebar(theWindow: Window): boolean {
return theWindow.location.search !== '' && theWindow.location.search.indexOf('uilocation=sidebar') > -1;
}
inTab(theWindow: Window): boolean {
return theWindow.location.search !== '' && theWindow.location.search.indexOf('uilocation=tab') > -1;
}
inPopout(theWindow: Window): boolean {
return theWindow.location.search !== '' && theWindow.location.search.indexOf('uilocation=popout') > -1;
}
inPopup(theWindow: Window): boolean {
return theWindow.location.search === '' || theWindow.location.search.indexOf('uilocation=') === -1 ||
theWindow.location.search.indexOf('uilocation=popup') > -1;
}
}

View File

@ -1,4 +1,4 @@
import UtilsService from './utils.service'; import BrowserUtilsService from './browserUtils.service';
export default class ConstantsService { export default class ConstantsService {
static readonly environmentUrlsKey: string = 'environmentUrls'; static readonly environmentUrlsKey: string = 'environmentUrls';
@ -57,8 +57,8 @@ export default class ConstantsService {
twoFactorProviderInfo: any[]; twoFactorProviderInfo: any[];
constructor(i18nService: any, utilsService: UtilsService) { constructor(i18nService: any, browserUtilsService: BrowserUtilsService) {
if (utilsService.isEdge()) { if (browserUtilsService.isEdge()) {
// delay for i18n fetch // delay for i18n fetch
setTimeout(() => { setTimeout(() => {
this.bootstrap(i18nService); this.bootstrap(i18nService);

View File

@ -1,9 +1,9 @@
import UtilsService from '../services/utils.service'; import BrowserUtilsService from '../services/browserUtils.service';
export default function i18nService(utilsService: UtilsService) { export default function i18nService(browserUtilsService: BrowserUtilsService) {
const edgeMessages: any = {}; const edgeMessages: any = {};
if (utilsService.isEdge()) { if (browserUtilsService.isEdge()) {
fetch('../_locales/en/messages.json').then((file) => { fetch('../_locales/en/messages.json').then((file) => {
return file.json(); return file.json();
}).then((locales) => { }).then((locales) => {

View File

@ -1,16 +1,16 @@
import BrowserUtilsService from './browserUtils.service';
import CipherService from './cipher.service'; import CipherService from './cipher.service';
import CollectionService from './collection.service'; import CollectionService from './collection.service';
import ConstantsService from './constants.service'; import ConstantsService from './constants.service';
import CryptoService from './crypto.service'; import CryptoService from './crypto.service';
import FolderService from './folder.service'; import FolderService from './folder.service';
import UtilsService from './utils.service';
import { StorageService } from './abstractions/storage.service'; import { StorageService } from './abstractions/storage.service';
export default class LockService { export default class LockService {
constructor(private cipherService: CipherService, private folderService: FolderService, constructor(private cipherService: CipherService, private folderService: FolderService,
private collectionService: CollectionService, private cryptoService: CryptoService, private collectionService: CollectionService, private cryptoService: CryptoService,
private utilsService: UtilsService, private storageService: StorageService, private browserUtilsService: BrowserUtilsService, private storageService: StorageService,
private setIcon: Function, private refreshBadgeAndMenu: Function) { private setIcon: Function, private refreshBadgeAndMenu: Function) {
this.checkLock(); this.checkLock();
setInterval(() => this.checkLock(), 10 * 1000); // check every 10 seconds setInterval(() => this.checkLock(), 10 * 1000); // check every 10 seconds
@ -81,9 +81,9 @@ export default class LockService {
// Helpers // Helpers
private sidebarViewName(): string { private sidebarViewName(): string {
if ((window as any).chrome.sidebarAction && this.utilsService.isFirefox()) { if ((window as any).chrome.sidebarAction && this.browserUtilsService.isFirefox()) {
return 'sidebar'; return 'sidebar';
} else if (this.utilsService.isOpera() && (typeof opr !== 'undefined') && opr.sidebarAction) { } else if (this.browserUtilsService.isOpera() && (typeof opr !== 'undefined') && opr.sidebarAction) {
return 'sidebar_panel'; return 'sidebar_panel';
} }

View File

@ -1,36 +1,6 @@
import UtilsService from './utils.service'; import UtilsService from './utils.service';
import { BrowserType } from '../enums/browserType.enum';
describe('Utils Service', () => { describe('Utils Service', () => {
describe('getDomain', () => {
it('should fail for invalid urls', () => {
expect(UtilsService.getDomain(null)).toBeNull();
expect(UtilsService.getDomain(undefined)).toBeNull();
expect(UtilsService.getDomain(' ')).toBeNull();
expect(UtilsService.getDomain('https://bit!:"_&ward.com')).toBeNull();
expect(UtilsService.getDomain('bitwarden')).toBeNull();
});
it('should handle urls without protocol', () => {
expect(UtilsService.getDomain('bitwarden.com')).toBe('bitwarden.com');
expect(UtilsService.getDomain('wrong://bitwarden.com')).toBe('bitwarden.com');
});
it('should handle valid urls', () => {
expect(UtilsService.getDomain('https://bitwarden')).toBe('bitwarden');
expect(UtilsService.getDomain('https://bitwarden.com')).toBe('bitwarden.com');
expect(UtilsService.getDomain('http://bitwarden.com')).toBe('bitwarden.com');
expect(UtilsService.getDomain('http://vault.bitwarden.com')).toBe('bitwarden.com');
expect(UtilsService.getDomain('https://user:password@bitwarden.com:8080/password/sites?and&query#hash')).toBe('bitwarden.com');
expect(UtilsService.getDomain('https://bitwarden.unknown')).toBe('bitwarden.unknown');
});
it('should support localhost and IP', () => {
expect(UtilsService.getDomain('https://localhost')).toBe('localhost');
expect(UtilsService.getDomain('https://192.168.1.1')).toBe('192.168.1.1');
});
});
describe('getHostname', () => { describe('getHostname', () => {
it('should fail for invalid urls', () => { it('should fail for invalid urls', () => {
expect(UtilsService.getHostname(null)).toBeNull(); expect(UtilsService.getHostname(null)).toBeNull();
@ -59,55 +29,4 @@ describe('Utils Service', () => {
expect(UtilsService.newGuid()).toMatch(validGuid); expect(UtilsService.newGuid()).toMatch(validGuid);
}); });
}); });
describe('getBrowser', () => {
const original = navigator.userAgent;
// Reset the userAgent.
afterAll(() => {
Object.defineProperty(navigator, 'userAgent', {
value: original
});
});
it('should detect chrome', () => {
Object.defineProperty(navigator, 'userAgent', {
configurable: true,
value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36'
});
const utilsService = new UtilsService();
expect(utilsService.getBrowser()).toBe(BrowserType.Chrome);
});
it('should detect firefox', () => {
Object.defineProperty(navigator, 'userAgent', {
configurable: true,
value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0'
});
const utilsService = new UtilsService();
expect(utilsService.getBrowser()).toBe(BrowserType.Firefox);
});
it('should detect opera', () => {
Object.defineProperty(navigator, 'userAgent', {
configurable: true,
value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3175.3 Safari/537.36 OPR/49.0.2695.0 (Edition developer)'
});
const utilsService = new UtilsService();
expect(utilsService.getBrowser()).toBe(BrowserType.Opera);
});
it('should detect edge', () => {
Object.defineProperty(navigator, 'userAgent', {
configurable: true,
value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; ServiceUI 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063'
});
const utilsService = new UtilsService();
expect(utilsService.getBrowser()).toBe(BrowserType.Edge);
});
});
}); });

View File

@ -1,16 +1,5 @@
import * as tldjs from 'tldjs';
import { BrowserType } from '../enums/browserType.enum';
import { UtilsService as UtilsServiceInterface } from './abstractions/utils.service'; import { UtilsService as UtilsServiceInterface } from './abstractions/utils.service';
const AnalyticsIds = {
[BrowserType.Chrome]: 'UA-81915606-6',
[BrowserType.Firefox]: 'UA-81915606-7',
[BrowserType.Opera]: 'UA-81915606-8',
[BrowserType.Edge]: 'UA-81915606-9',
[BrowserType.Vivaldi]: 'UA-81915606-15',
[BrowserType.Safari]: 'UA-81915606-16',
};
export default class UtilsService implements UtilsServiceInterface { export default class UtilsService implements UtilsServiceInterface {
static copyToClipboard(text: string, doc?: Document): void { static copyToClipboard(text: string, doc?: Document): void {
doc = doc || document; doc = doc || document;
@ -136,37 +125,6 @@ export default class UtilsService implements UtilsServiceInterface {
return decodeURIComponent(escape(encodedString)); return decodeURIComponent(escape(encodedString));
} }
static getDomain(uriString: string): string {
if (uriString == null) {
return null;
}
uriString = uriString.trim();
if (uriString === '') {
return null;
}
if (uriString.startsWith('http://') || uriString.startsWith('https://')) {
try {
const url = new URL(uriString);
if (url.hostname === 'localhost' || UtilsService.validIpAddress(url.hostname)) {
return url.hostname;
}
const urlDomain = tldjs.getDomain(url.hostname);
return urlDomain != null ? urlDomain : url.hostname;
} catch (e) { }
}
const domain = tldjs.getDomain(uriString);
if (domain != null) {
return domain;
}
return null;
}
static getHostname(uriString: string): string { static getHostname(uriString: string): string {
if (uriString == null) { if (uriString == null) {
return null; return null;
@ -187,159 +145,6 @@ export default class UtilsService implements UtilsServiceInterface {
return null; return null;
} }
private static validIpAddress(ipString: string): boolean {
// tslint:disable-next-line
const ipRegex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
return ipRegex.test(ipString);
}
private browserCache: BrowserType = null;
private analyticsIdCache: string = null;
getBrowser(): BrowserType {
if (this.browserCache) {
return this.browserCache;
}
if (navigator.userAgent.indexOf('Firefox') !== -1 || navigator.userAgent.indexOf('Gecko/') !== -1) {
this.browserCache = BrowserType.Firefox;
} else if ((!!(window as any).opr && !!opr.addons) || !!(window as any).opera ||
navigator.userAgent.indexOf(' OPR/') >= 0) {
this.browserCache = BrowserType.Opera;
} else if (navigator.userAgent.indexOf(' Edge/') !== -1) {
this.browserCache = BrowserType.Edge;
} else if (navigator.userAgent.indexOf(' Vivaldi/') !== -1) {
this.browserCache = BrowserType.Vivaldi;
} else if ((window as any).chrome) {
this.browserCache = BrowserType.Chrome;
}
return this.browserCache;
}
getBrowserString(): string {
return BrowserType[this.getBrowser()].toLowerCase();
}
isFirefox(): boolean {
return this.getBrowser() === BrowserType.Firefox;
}
isChrome(): boolean {
return this.getBrowser() === BrowserType.Chrome;
}
isEdge(): boolean {
return this.getBrowser() === BrowserType.Edge;
}
isOpera(): boolean {
return this.getBrowser() === BrowserType.Opera;
}
isVivaldi(): boolean {
return this.getBrowser() === BrowserType.Vivaldi;
}
isSafari(): boolean {
return this.getBrowser() === BrowserType.Safari;
}
analyticsId(): string {
if (this.analyticsIdCache) {
return this.analyticsIdCache;
}
this.analyticsIdCache = AnalyticsIds[this.getBrowser()];
return this.analyticsIdCache;
}
initListSectionItemListeners(doc: Document, angular: any): void {
if (!doc) {
throw new Error('doc parameter required');
}
const sectionItems = doc.querySelectorAll(
'.list-section-item:not([data-bw-events="1"])');
const sectionFormItems = doc.querySelectorAll(
'.list-section-item:not([data-bw-events="1"]) input, ' +
'.list-section-item:not([data-bw-events="1"]) select, ' +
'.list-section-item:not([data-bw-events="1"]) textarea');
sectionItems.forEach((item) => {
(item as HTMLElement).dataset.bwEvents = '1';
item.addEventListener('click', (e) => {
if (e.defaultPrevented) {
return;
}
const el = e.target as HTMLElement;
// Some elements will already focus properly
if (el.tagName != null) {
switch (el.tagName.toLowerCase()) {
case 'label': case 'input': case 'textarea': case 'select':
return;
default:
break;
}
}
const cell = el.closest('.list-section-item');
if (!cell) {
return;
}
const textFilter = 'input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"])';
const text = cell.querySelectorAll(textFilter + ', textarea');
const checkbox = cell.querySelectorAll('input[type="checkbox"]');
const select = cell.querySelectorAll('select');
if (text.length > 0) {
(text[0] as HTMLElement).focus();
} else if (select.length > 0) {
(select[0] as HTMLElement).focus();
} else if (checkbox.length > 0) {
const cb = checkbox[0] as HTMLInputElement;
cb.checked = !cb.checked;
if (angular) {
angular.element(checkbox[0]).triggerHandler('click');
}
}
}, false);
});
sectionFormItems.forEach((item) => {
const itemCell = item.closest('.list-section-item');
(itemCell as HTMLElement).dataset.bwEvents = '1';
item.addEventListener('focus', (e: Event) => {
const el = e.target as HTMLElement;
const cell = el.closest('.list-section-item');
if (!cell) {
return;
}
cell.classList.add('active');
}, false);
item.addEventListener('blur', (e: Event) => {
const el = e.target as HTMLElement;
const cell = el.closest('.list-section-item');
if (!cell) {
return;
}
cell.classList.remove('active');
}, false);
});
}
getDomain(uriString: string): string {
return UtilsService.getDomain(uriString);
}
getHostname(uriString: string): string { getHostname(uriString: string): string {
return UtilsService.getHostname(uriString); return UtilsService.getHostname(uriString);
} }
@ -347,21 +152,4 @@ export default class UtilsService implements UtilsServiceInterface {
copyToClipboard(text: string, doc?: Document) { copyToClipboard(text: string, doc?: Document) {
UtilsService.copyToClipboard(text, doc); UtilsService.copyToClipboard(text, doc);
} }
inSidebar(theWindow: Window): boolean {
return theWindow.location.search !== '' && theWindow.location.search.indexOf('uilocation=sidebar') > -1;
}
inTab(theWindow: Window): boolean {
return theWindow.location.search !== '' && theWindow.location.search.indexOf('uilocation=tab') > -1;
}
inPopout(theWindow: Window): boolean {
return theWindow.location.search !== '' && theWindow.location.search.indexOf('uilocation=popout') > -1;
}
inPopup(theWindow: Window): boolean {
return theWindow.location.search === '' || theWindow.location.search.indexOf('uilocation=') === -1 ||
theWindow.location.search.indexOf('uilocation=popup') > -1;
}
} }