diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..65932d2edc0 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true + +# Matches multiple files with brace expansion notation +# Set default charset +[*.{js,ts,less}] +charset = utf-8 +indent_style = space +indent_size = 4 diff --git a/.gitignore b/.gitignore index ec9ce7f067c..8923a5a9b55 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ webfonts/ *.crx *.pem *.zip +releases/ +package-lock.json diff --git a/README.md b/README.md index a9dc57c36e7..1e4c035cb20 100644 --- a/README.md +++ b/README.md @@ -27,19 +27,31 @@ The bitwarden browser extension is written using the Chrome Web Extension API an By default the extension is targeting the production API. If you are running the [Core](https://github.com/bitwarden/core) API locally, you'll need to switch the extension to target your local instance. Open `src/services/apiService.js` and set `self.baseUrl` and `self.identityBaseUrl` to your local API instance (ex. `http://localhost:5000`). Then run the following commands: - -- `npm install` -- `gulp build` +```bash +npm install +npm run dev:watch +``` You can now load the extension into your browser through the browser's extension tools page: - Chrome/Opera: 1. Type `chrome://extensions` in your address bar to bring up the extensions page. 2. Enable developer mode (checkbox) - 3. Click the "Load unpacked extension" button, navigate to the `src` folder of your local extension instance, and click "Ok". + 3. Click the "Load unpacked extension" button, navigate to the `dist` folder of your local extension instance, and click "Ok". - Firefox 1. Type `about:debugging` in your address bar to bring up the add-ons page. - 2. Click the `Load Temporary Add-on` button, navigate to the `src/manifest.json` file, and "Open". + 2. Click the `Load Temporary Add-on` button, navigate to the `dist/manifest.json` file, and "Open". + +### Release + +To build the the extension for production the following commands should be run: + +```bash +npm run prod +gulp dist:[browser] +``` + +Where `[browser]` is the web browser to target. Currently the following web browsers are supported: `chrome`, `edge`, `firefox` and `opera`. # Contribute diff --git a/gulpfile.js b/gulpfile.js index 121a61b7938..9a0eb1b388c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,504 +1,122 @@ -var gulp = require('gulp'), - rimraf = require('rimraf'), - concat = require('gulp-concat'), - rename = require('gulp-rename'), - less = require('gulp-less'), - preprocess = require('gulp-preprocess'), - runSequence = require('run-sequence'), +const gulp = require('gulp'), + gulpif = require('gulp-if'), + filter = require('gulp-filter'), + replace = require('gulp-replace'), jshint = require('gulp-jshint'), - merge = require('merge-stream'), - browserify = require('browserify'), - source = require('vinyl-source-stream'), googleWebFonts = require('gulp-google-webfonts'), - webpack = require('webpack-stream'), jeditor = require("gulp-json-editor"), - gulpUtil = require('gulp-util'), child = require('child_process'), zip = require('gulp-zip'), manifest = require('./src/manifest.json'), - xmlpoke = require('gulp-xmlpoke'), - embedTemplates = require('gulp-angular-embed-templates'); + xmlpoke = require('gulp-xmlpoke'); -var paths = {}; -paths.dist = './dist/'; -paths.libDir = './src/lib/'; -paths.npmDir = './node_modules/'; -paths.popupDir = './src/popup/'; -paths.lessDir = paths.popupDir + 'less/'; -paths.cssDir = paths.popupDir + 'css/'; +const paths = { + releases: './releases/', + dist: './dist/', + libDir: './src/lib/', + npmDir: './node_modules/', + popupDir: './src/popup/', + cssDir: './src/popup/css/' +}; -gulp.task('lint', function () { - return gulp.src([ - paths.popupDir + '**/*.js', - './src/services/**/*.js', - './src/notification/**/*.js', - './src/models/**/*.js', - './src/scripts/**/*.js', - //'./src/content/**/*.js', - './src/overlay/**/*.js', - './src/background.js' - ]) - .pipe(jshint()) - .pipe(jshint.reporter('default')); -}); - -gulp.task('build', function (cb) { - return runSequence( - 'clean', - ['browserify', 'webpack', 'lib', 'less', 'lint', 'webfonts'], - cb); -}); - -gulp.task('clean:css', function (cb) { - return rimraf(paths.cssDir, cb); -}); - -gulp.task('clean:lib', function (cb) { - return rimraf(paths.libDir, cb); -}); - -gulp.task('clean', ['clean:css', 'clean:lib']); - -gulp.task('lib', ['clean:lib'], function () { - var libs = [ - { - src: [ - paths.npmDir + 'bootstrap/dist/**/*', - '!' + paths.npmDir + 'bootstrap/dist/**/npm.js', - '!' + paths.npmDir + 'bootstrap/dist/**/css/*theme*', - '!' + paths.npmDir + 'bootstrap/**/*.min*' - ], - dest: paths.libDir + 'bootstrap' - }, - { - src: paths.npmDir + 'font-awesome/css/font-awesome.css', - dest: paths.libDir + 'font-awesome/css' - }, - { - src: paths.npmDir + 'font-awesome/fonts/*', - dest: paths.libDir + 'font-awesome/fonts' - }, - { - src: paths.npmDir + 'jquery/dist/jquery.js', - dest: paths.libDir + 'jquery' - }, - { - src: paths.npmDir + 'angular/angular.js', - dest: paths.libDir + 'angular' - }, - { - src: paths.npmDir + 'angular-animate/angular-animate.js', - dest: paths.libDir + 'angular-animate' - }, - { - src: paths.npmDir + 'angular-ui-router/release/angular-ui-router.js', - dest: paths.libDir + 'angular-ui-router' - }, - { - src: [paths.npmDir + 'angular-toastr/dist/angular-toastr.tpls.js', - paths.npmDir + 'angular-toastr/dist/angular-toastr.css'], - dest: paths.libDir + 'angular-toastr' - }, - { - src: paths.npmDir + 'ngclipboard/dist/ngclipboard.js', - dest: paths.libDir + 'ngclipboard' - }, - { - src: paths.npmDir + 'clipboard/dist/clipboard.js', - dest: paths.libDir + 'clipboard' - }, - { - src: paths.npmDir + 'q/q.js', - dest: paths.libDir + 'q' - }, - { - src: [paths.npmDir + 'sweetalert/dist/sweetalert.css', paths.npmDir + 'sweetalert/dist/sweetalert-dev.js', - paths.npmDir + 'angular-sweetalert/SweetAlert.js'], - dest: paths.libDir + 'sweetalert' - }, - { - src: [paths.npmDir + 'angulartics-google-analytics/lib/angulartics*.js', - paths.npmDir + 'angulartics/src/angulartics.js' - ], - dest: paths.libDir + 'angulartics' - }, - { - src: paths.npmDir + 'ng-infinite-scroll/build/ng-infinite-scroll.js', - dest: paths.libDir + 'ng-infinite-scroll' - }, - { - src: paths.npmDir + 'papaparse/papaparse.js', - dest: paths.libDir + 'papaparse' - } - ]; - - var tasks = libs.map(function (lib) { - return gulp.src(lib.src).pipe(gulp.dest(lib.dest)); - }); - - return merge(tasks); -}); - -gulp.task('browserify', ['browserify:tldjs']); - -gulp.task('browserify:tldjs', function () { - return browserify(paths.npmDir + 'tldjs/index.js', { standalone: 'tldjs' }) - .bundle() - .pipe(source('tld.js')) - .pipe(gulp.dest(paths.libDir + 'tldjs')); -}); - -gulp.task('webpack', ['webpack:forge']); - -gulp.task('webpack:forge', function () { - var forgeDir = paths.npmDir + '/node-forge/lib/'; - - return gulp.src([ - forgeDir + 'pbkdf2.js', - forgeDir + 'aes.js', - forgeDir + 'hmac.js', - forgeDir + 'sha256.js', - forgeDir + 'random.js', - forgeDir + 'forge.js' - ]).pipe(webpack({ - output: { - filename: 'forge.js', - library: 'forge', - libraryTarget: 'umd' - }, - node: { - Buffer: false, - process: false, - crypto: false, - setImmediate: false - } - })).pipe(gulp.dest(paths.libDir + 'forge')); -}); - -gulp.task('less', function () { - return gulp.src(paths.lessDir + 'popup.less') - .pipe(less()) - .pipe(gulp.dest(paths.cssDir)); -}); - -gulp.task('watch', function () { - gulp.watch(paths.lessDir + '*.less', ['less']); -}); - -gulp.task('dist:clean', function (cb) { - return rimraf(paths.dist + '**/*', cb); -}); - -gulp.task('dist:move', function () { - var moves = [ - { - src: 'src/_locales/**/*', - dest: paths.dist + '_locales' - }, - { - src: [ - 'src/content/**/*', - '!src/content/field.js', - '!src/content/overlay.js' - ], - dest: paths.dist + 'content' - }, - { - src: 'src/images/**/*', - dest: paths.dist + 'images' - }, - { - src: 'src/notification/**/*', - dest: paths.dist + 'notification' - }, - { - src: 'src/popup/index.html', - dest: paths.dist + 'popup' - }, - { - src: 'src/popup/css/webfonts/**/*', - dest: paths.dist + 'popup/css/webfonts' - }, - { - src: paths.libDir + 'font-awesome/fonts/**/*', - dest: paths.dist + 'popup/fonts' - }, - { - src: 'src/services/**/*', - dest: paths.dist + 'services' - }, - { - src: paths.libDir + 'forge/**/*', - dest: paths.dist + 'lib/forge' - }, - { - src: paths.libDir + 'jquery/**/*', - dest: paths.dist + 'lib/jquery' - }, - { - src: paths.libDir + 'tldjs/**/*', - dest: paths.dist + 'lib/tldjs' - }, - { - src: paths.libDir + 'q/**/*', - dest: paths.dist + 'lib/q' - }, - { - src: 'src/models/**/*', - dest: paths.dist + 'models' - }, - { - src: 'src/scripts/analytics.js', - dest: paths.dist + 'scripts' - }, - { - src: [ - 'src/background.*', - 'src/manifest.json' - ], - dest: paths.dist - } - ]; - - var tasks = moves.map(function (move) { - return gulp.src(move.src).pipe(gulp.dest(move.dest)); - }); - - return merge(tasks); -}); - -gulp.task('dist:css', function () { - distCss({}); -}); - -gulp.task('dist:css:edge', function () { - distCss({ edge: true }); -}); - -gulp.task('dist:css:firefox', function () { - distCss({ firefox: true }); -}); - -function distCss(preprocessContext) { - return gulp - .src([ - // libs - paths.libDir + '**/*.css', - '!' + paths.libDir + '**/*.min.css', - // app - paths.cssDir + 'popup.css', - paths.cssDir + 'webfonts.css' - ]) - .pipe(preprocess({ context: preprocessContext })) - .pipe(concat(paths.dist + 'popup/css/popup.css')) - .pipe(gulp.dest('.')); -} - -gulp.task('dist:js', function () { - return distJs(false); -}); - -gulp.task('dist:js:edge', function () { - return distJs(true); -}); - -function distJs(edge) { - var appTask = gulp - .src([ - // models/scripts - './src/models/**/*.js', - './src/scripts/*.js', - // app - paths.popupDir + 'app/app.js', - paths.popupDir + 'app/**/*Module.js', - paths.popupDir + 'app/**/*.js' - ]) - .pipe(embedTemplates({ - basePath: './src/popup/', - minimize: { empty: true } - })) - .pipe(concat(paths.dist + 'popup/app.js')) - .pipe(gulp.dest('.')); - - var libTask = gulp - .src([ - paths.libDir + 'jquery/jquery.js', - paths.libDir + 'bootstrap/js/bootstrap.js', - edge ? './src/edge/angular.js' : (paths.libDir + 'angular/angular.js'), - paths.libDir + '**/*.js', - '!' + paths.libDir + 'q/**/*', - '!' + paths.libDir + 'tldjs/**/*', - '!' + paths.libDir + 'forge/**/*', - '!' + paths.libDir + '**/*.min.js' - ]) - .pipe(concat(paths.dist + 'popup/lib.js')) - .pipe(gulp.dest('.')); - - return merge(appTask, libTask); -} - -gulp.task('dist:preprocess', function () { - return gulp - .src([ - paths.dist + 'popup/index.html' - ], { base: '.' }) - .pipe(preprocess({ context: {} })) - .pipe(gulp.dest('.')); -}); - -gulp.task('dist', ['build'], function (cb) { - return dist({}, cb); -}); - -gulp.task('dist:edge', ['build'], function (cb) { - return dist({ edge: true }, cb); -}); - -gulp.task('dist:firefox', ['build'], function (cb) { - return dist({ firefox: true }, cb); -}); - -function dist(o, cb) { - var distCss = o.edge ? 'dist:css:edge' : o.firefox ? 'dist:css:firefox' : 'dist:css'; - var distJs = o.edge ? 'dist:js:edge' : 'dist:js'; - - return runSequence( - 'dist:clean', - ['dist:move', distCss, distJs], - 'dist:preprocess', - cb); -} - -var sidebarActionManifestObj = { +const sidebarActionManifestObj = { "default_title": "bitwarden", "default_panel": "popup/index.html?uilocation=sidebar", "default_icon": "images/icon19.png" }; -gulp.task('dist-firefox', ['dist:firefox'], function (cb) { - gulp.src(paths.dist + 'manifest.json') - .pipe(jeditor(function (manifest) { - manifest.applications = { - gecko: { - id: '{446900e4-71c2-419f-a6a7-df9c091e268b}', - strict_min_version: '42.0' - } - }; +function dist(browserName, manifest) { + return gulp.src(paths.dist + '**/*') + .pipe(gulpif(browserName !== 'edge', filter(['**', '!dist/edge/**/*']))) + .pipe(gulpif('popup/index.html', replace('__BROWSER__', browserName))) + .pipe(gulpif('manifest.json', jeditor(manifest))) + .pipe(zip(`dist-${browserName}.zip`)) + .pipe(gulp.dest(paths.releases)); +} - manifest['sidebar_action'] = sidebarActionManifestObj; - return manifest; - })) - .pipe(gulp.dest(paths.dist)); - return zipDist('dist-firefox'); -}); +gulp.task('dist', ['dist:firefox', 'dist:chrome', 'dist:opera', 'dist:edge']); -gulp.task('dist-opera', ['dist'], function (cb) { - gulp.src(paths.dist + 'manifest.json') - .pipe(jeditor(function (manifest) { - manifest['sidebar_action'] = sidebarActionManifestObj; - return manifest; - })) - .pipe(gulp.dest(paths.dist)); - return zipDist('dist-opera'); -}); +gulp.task('dist:firefox', (cb) => { + return dist('firefox', (manifest) => { + manifest.applications = { + gecko: { + id: '{446900e4-71c2-419f-a6a7-df9c091e268b}', + strict_min_version: '42.0' + } + }; -gulp.task('dist-edge', ['dist:edge'], function (cb) { - // move dist to temp extension folder - new Promise(function (resolve, reject) { - gulp.src(paths.dist + '**/*') - .on('error', reject) - .pipe(gulp.dest('temp/Extension/')) - .on('end', resolve); - }).then(function () { - // move windows store files to temp folder - return new Promise(function (resolve, reject) { - gulp.src('store/windows/**/*') - .on('error', reject) - .pipe(gulp.dest('temp/')) - .on('end', resolve); - }); - }).then(function () { - // delete dist folder - return new Promise(function (resolve, reject) { - rimraf(paths.dist, function () { - resolve(); - }) - }); - }).then(function () { - // move temp back to dist - return new Promise(function (resolve, reject) { - gulp.src('temp/**/*') - .on('error', reject) - .pipe(gulp.dest(paths.dist)) - .on('end', resolve); - }); - }).then(function () { - // delete temp folder - return new Promise(function (resolve, reject) { - rimraf('temp', function () { - resolve(); - }) - }); - }).then(function () { - // move src edge folder to dist - return new Promise(function (resolve, reject) { - gulp.src(['src/edge/**/*', '!src/edge/angular.js']) - .on('error', reject) - .pipe(gulp.dest(paths.dist + 'Extension/edge')) - .on('end', resolve); - }); - }).then(function () { - // modify manifest with edge preload stuff - return new Promise(function (resolve, reject) { - gulp.src(paths.dist + 'Extension/manifest.json') - .pipe(jeditor(function (manifest) { - manifest['-ms-preload'] = { - backgroundScript: 'edge/backgroundScriptsAPIBridge.js', - contentScript: 'edge/contentScriptsAPIBridge.js' - }; - return manifest; - })) - .on('error', reject) - .pipe(gulp.dest(paths.dist + 'Extension')) - .on('end', resolve); - }); - }).then(function () { - // modify appxmanifest - return new Promise(function (resolve, reject) { - gulp.src(paths.dist + '/AppxManifest.xml') - .pipe(xmlpoke({ - replacements: [{ - xpath: '/p:Package/p:Identity/@Version', - value: manifest.version + '.0', - namespaces: { - 'p': 'http://schemas.microsoft.com/appx/manifest/foundation/windows10' - } - }] - })) - .on('error', reject) - .pipe(gulp.dest(paths.dist)) - .on('end', resolve); - }); - }).then(function () { - // makeappx.exe must be in your system's path already - child.spawn('makeappx.exe', ['pack', '/h', 'SHA256', '/d', paths.dist, '/p', paths.dist + 'bitwarden.appx']); - cb(); - }, function () { - cb(); + manifest['sidebar_action'] = sidebarActionManifestObj; + return manifest; }); }); -gulp.task('dist-other', ['dist'], function (cb) { - return zipDist('dist-other'); +gulp.task('dist:opera', (cb) => { + return dist('opera', (manifest) => { + manifest['sidebar_action'] = sidebarActionManifestObj; + return manifest; + }); }); -function zipDist(fileName) { - return gulp.src(paths.dist + '**/*') - .pipe(zip(fileName + '.zip')) - .pipe(gulp.dest(paths.dist)); +gulp.task('dist:chrome', (cb) => { + return dist('chrome', (manifest) => { + return manifest; + }); +}); + +// Since Edge extensions require makeappx to be run we temporarily store it in a folder. +gulp.task('dist:edge', (cb) => { + const edgePath = paths.releases + 'Edge/'; + const extensionPath = edgePath + 'Extension/'; + + return copyDistEdge(paths.dist + '**/*', extensionPath) + .then(copyAssetsEdge('./store/windows/**/*', edgePath)) + .then(() => { + // makeappx.exe must be in your system's path already + child.spawn('makeappx.exe', ['pack', '/h', 'SHA256', '/d', edgePath, '/p', paths.releases + 'dist-edge.appx']); + return cb; + }, () => { + return cb; + }); +}); + +function copyDistEdge(source, dest) { + return new Promise((resolve, reject) => { + gulp.src(source) + .on('error', reject) + .pipe(gulpif('popup/index.html', replace('__BROWSER__', 'edge'))) + .pipe(gulpif('manifest.json', jeditor((manifest) => { + manifest['-ms-preload'] = { + backgroundScript: 'edge/backgroundScriptsAPIBridge.js', + contentScript: 'edge/contentScriptsAPIBridge.js' + }; + return manifest; + }))) + .pipe(gulp.dest(dest)) + .on('end', resolve); + }); } -gulp.task('webfonts', function () { +function copyAssetsEdge(source, dest) { + return new Promise((resolve, reject) => { + gulp.src(source) + .on('error', reject) + .pipe(gulpif('AppxManifest.xml', xmlpoke({ + replacements: [{ + xpath: '/p:Package/p:Identity/@Version', + value: manifest.version + '.0', + namespaces: { + 'p': 'http://schemas.microsoft.com/appx/manifest/foundation/windows10' + } + }] + }))) + .pipe(gulp.dest(dest)) + .on('end', resolve); + }); +} + +gulp.task('build', ['lint', 'webfonts']); + +gulp.task('webfonts', () => { return gulp.src('./webfonts.list') .pipe(googleWebFonts({ fontsDir: 'webfonts', @@ -507,20 +125,20 @@ gulp.task('webfonts', function () { .pipe(gulp.dest(paths.cssDir)); }); -function npmCommand(commands, cb) { - var npmLogger = (buffer) => { - buffer.toString() - .split(/\n/) - .forEach((message) => gulpUtil.log(message)); - }; - var npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm'; - var npmChild = child.spawn(npmCommand, commands); - npmChild.stdout.on('data', npmLogger); - npmChild.stderr.on('data', npmLogger); - npmChild.stderr.on('close', cb); - return npmChild; -} +// LEGACY CODE! -gulp.task('webext:firefox', function (cb) { - return npmCommand(['run', 'start:firefox'], cb); +gulp.task('lint', () => { + return gulp.src([ + paths.popupDir + '**/*.js', + './src/services/**/*.js', + './src/notification/**/*.js', + './src/scripts/**/*.js', + //'./src/content/**/*.js', + './src/overlay/**/*.js', + './src/background.js' + ]) + .pipe(jshint({ + esversion: 6 + })) + .pipe(jshint.reporter('default')); }); diff --git a/package.json b/package.json index 0b9b1f560df..74e7d5c1198 100644 --- a/package.json +++ b/package.json @@ -2,47 +2,66 @@ "name": "bitwarden", "version": "0.0.0", "scripts": { - "start:firefox": "web-ext run --source-dir ./dist/" + "start:firefox": "web-ext run --source-dir ./dist/", + "dev": "gulp build && webpack --config webpack.dev.js", + "dev:watch": "gulp build && webpack --config webpack.dev.js --watch", + "prod": "gulp build && webpack --config webpack.prod.js", + "lint": "tslint src/**/*.ts || true", + "lint:fix": "tslint src/**/*.ts --fix" }, "devDependencies": { "angular": "1.6.6", "angular-animate": "1.6.6", "angular-sweetalert": "1.1.2", "angular-toastr": "2.1.1", - "angular-ui-router": "0.4.2", "angulartics": "1.4.0", "angulartics-google-analytics": "0.4.0", "bootstrap": "3.3.7", - "browserify": "14.4.0", "child_process": "1.0.2", + "clean-webpack-plugin": "^0.1.17", "clipboard": "1.7.1", + "copy-webpack-plugin": "^4.2.0", + "css-loader": "^0.28.7", + "extract-text-webpack-plugin": "^3.0.1", + "file-loader": "^1.1.5", "font-awesome": "4.7.0", "gulp": "3.9.1", - "gulp-angular-embed-templates": "2.3.0", - "gulp-concat": "2.6.1", + "gulp-filter": "5.0.1", "gulp-google-webfonts": "0.0.14", + "gulp-if": "^2.0.2", "gulp-jshint": "2.0.4", "gulp-json-editor": "2.2.1", - "gulp-less": "3.3.2", - "gulp-preprocess": "2.0.0", - "gulp-rename": "1.2.2", - "gulp-util": "3.0.8", + "gulp-replace": "^0.6.1", "gulp-xmlpoke": "0.2.1", "gulp-zip": "4.0.0", + "html-loader": "^0.5.1", + "html-webpack-plugin": "^2.30.1", "jquery": "3.2.1", "jshint": "2.9.5", - "merge-stream": "1.0.1", + "less": "^3.0.0-alpha.3", + "less-loader": "^4.0.5", "ng-infinite-scroll": "1.3.0", "ngclipboard": "1.1.1", "node-forge": "0.7.1", "papaparse": "4.3.5", - "q": "1.5.0", - "rimraf": "2.6.1", - "run-sequence": "2.1.0", + "style-loader": "^0.19.0", "sweetalert": "1.1.3", "tldjs": "2.0.0", - "vinyl-source-stream": "1.1.0", + "ts-loader": "^3.0.5", + "tslint": "^5.8.0", + "tslint-loader": "^3.5.3", + "typescript": "^2.5.3", "web-ext": "2.0.0", - "webpack-stream": "4.0.0" + "webpack": "^3.8.1", + "webpack-merge": "^4.1.0" + }, + "dependencies": { + "@types/angular": "^1.6.34", + "@types/chrome": "0.0.51", + "@types/jquery": "^3.2.16", + "@types/node-forge": "0.6.10", + "@types/tldjs": "1.7.1", + "@types/webcrypto": "^0.0.28", + "@uirouter/angularjs": "^1.0.10" } } diff --git a/src/background.d.ts b/src/background.d.ts new file mode 100644 index 00000000000..a4b6712cd44 --- /dev/null +++ b/src/background.d.ts @@ -0,0 +1,5 @@ +declare function escape(s: string): string; +declare function unescape(s: string): string; +declare var opr: any; +declare var tldjs: any; +declare var forge: any; diff --git a/src/background.html b/src/background.html index 0ecb2203cd5..53a7f242954 100644 --- a/src/background.html +++ b/src/background.html @@ -1,33 +1,6 @@
- - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file +