chrome.webNavigation.onBeforeNavigate event is not executed immediately after window.location change - javascript

Note that I'm using Babel + Webpack to generate ES2017 code to ES5 (I'm using async await functionalities and wiling to use same code with less changes in old browsers).
My goal is here to execute # of codes on a specific tab. Those codes includes, form submits, clicking on links etc.. which redirects to a new webpage. What ever the code should be paused until the page is loaded doesn't matter what is the page it's navigating/redirecting. So to simulate the redirecting I'm using window.location = "https://facebook.com".
Every thing in short. The expected behavior is,
I open tab with url: 'https://google.com'
Change the window location to https://facebook.com
Display alert message once facebook loaded completely.
I have below helper classes with me.
tabs.js
/** Tabs class will provide promisify chrome.tabs functions */
export default class Tabs {
/** Creates a new tab using passed configurations
* #param {Object} config
* {Number} windowId
* {Number} index
* {String} url
* {Boolean} active
* {Boolean} pinned
* {Number} openerTabId ID of the tab that opened current tab. should be in the same window.
* #returns {Promise}
*/
static create(config) {
// Return new promise
return new Promise((resolve, reject) => {
// Creating new tab
chrome.tabs.create(config, (tab) => {
// Error
if (chrome.runtime.lastError)
reject(chrome.extension.lastError);
// Success
else
resolve(tab);
});
});
}
/**
*
* #param {Number} id
* #param {Object} config
* {String} code // javascript or css code to inject
* {String} file // javascript or css file to inject
* {Boolean} allFrames // inject to all frames
* {Number} frameId // frame index to inject
* {Boolean} matchAboutBlank // inject tp about:blank pages
* {String} runAt // "document_start" or "document_end" or "document_idle"
* #returns {Promise}
*/
static execute(id, config) {
// Return a new promise
return new Promise((resolve, reject) => {
// Execute a script
chrome.tabs.executeScript(id, config, (results) => {
// Error
if (chrome.runtime.lastError)
reject(chrome.runtime.las2tError);
// Success
else{
resolve(results);
// print "execute" just to get the idea when it's printed.
console.log('executed: ' + config.code);
}
});
})
}
/**
*
* #param {Number} id
* #returns {Promise}
*/
static get(id) {
// Return new promise
return new Promise((resolve, reject) => {
// Get tab
chrome.tabs.get(id, (tab) => {
// Error
if(chrome.runtime.lastError)
reject(chrome.runtime.lastError);
// Success
else
resolve(tab);
});
});
}
}
process.js
export default class Process {
/** Pause for a given time
* #param ms
* #returns {Promise}
*/
static timeout(ms) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, ms);
});
}
}
Finally my background.js is used to listen to webNavigation.onBeforeNavigate and webNavigation.onCompleted and to execute script.
Note that onBeforeNavigate and onCompleted provide details about each and every tab but when i run this i only have one tab. so I'm directly initializing isTabStatus without tabId validation.
background.js
import Tabs from "./tab/tabs";
import Process from "./commons/process";
// holds the status of new tab.
let isTabStatus = "loading";
/**
* Listeners
*/
chrome.webNavigation.onBeforeNavigate.addListener((info) => {
if(info.frameId === 0) {
console.log('before navigation status: loading ' + info.url);
isTabStatus = "loading";
}
});
chrome.webNavigation.onCompleted.addListener((info) => {
if(info.frameId === 0) {
console.log('on completed status: completed ' + info.url);
isTabStatus = "completed";
}
});
async function run() {
let tab = await Tabs.create({
url: "https://www.google.com"
});
let tabId = tab.id;
await waitUntilLoaded();
await Tabs.execute(tabId, {
code: "window.location = 'https://facebook.com'",
runAt: "document_idle"
});
await waitUntilLoaded();
await Tabs.execute(tabId, {
code: "alert('hello world')"
});
}
async function waitUntilLoaded () {
while(isTabStatus === "loading") {
await Process.timeout(400);
console.log('waiting');
}
}
run();
So, the expected behavior is,
create new tab
navigate to https://google.com
navigate to https://facebook.com
alert hello world.
But it actually does is,
create new tab
navigate to https://google.com
alert hello world
navigate to https://facebook.com
Finally the console logs as below.
before navigation status: loading https://www.google.com/
waiting
on completed status: completed https://www.google.lk/?gfe_rd=cr&ei=VIueWa2uFZOCuATNtJTIBA
waiting
execute: window.location = 'https://facebook.com'
before navigation status: loading https://facebook.com/
execute: alert('hello world')
on completed status: completed https://www.facebook.com/
In here alert has completed executed after onBeforeNavigate but actually alert message is displayed in the google page. After and only after I click ok (alert message ok), It redirects to facebook.
It's clear if i add console.log before alert as below.
await waitUntilLoaded();
console.log('****');
await Tabs.execute(tabId, {
code: "alert('hello world')"
});
Then the final log will be,
before navigation status: loading https://www.google.com/
waiting
on completed status: completed https://www.google.lk/?gfe_rd=cr&ei=TY-eWb_0D6baugTMqqWACQ
waiting
execute: window.location = 'https://facebook.com'
****
before navigation status: loading https://facebook.com/
execute: alert('hello world')
on completed status: completed https://www.facebook.com/
As you can see alert get executed immediately after window.location change. even before onBeforeNavigation callback
Find other details below.
mainfest.json
{
"manifest_version": 2,
"name": "Tax Executer",
"description": "",
"version": "1.0",
"browser_action": {
"default_icon": "images/icon.png",
"default_popup": "views/popup.html"
},
"permissions": [
"webNavigation",
"webRequest",
"activeTab",
"tabs",
"http://*/",
"https://*/",
"file:///*/"
],
"background": {
"page": "views/background.html"
},
"content_scripts": [{
"run_at": "document_start",
"matches": ["https://*/*", "file://*/*"],
"js": ["dist/content.js"]
}],
"automation": {}
}
package.json
{
"name": "babel_webpack_starter_project",
"version": "1.0.0",
"description": "helps you to get started with babel + webpack project quickly",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"webpack": "webpack",
"babel": "babel"
},
"repository": {
"type": "git",
"url": "git+https://github.com/SrineshNisala/babel_webpack_starter_project.git"
},
"keywords": [
"babel",
"webpack",
"starter"
],
"author": "SrineshNisala",
"license": "MIT",
"bugs": {
"url": "https://github.com/SrineshNisala/babel_webpack_starter_project/issues"
},
"homepage": "https://github.com/SrineshNisala/babel_webpack_starter_project#readme",
"devDependencies": {
"assert": "^1.4.1",
"babel-cli": "^6.24.1",
"babel-core": "^6.25.0",
"babel-loader": "^7.1.1",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-plugin-unassert": "^2.1.2",
"babel-preset-es2015": "^6.24.1",
"babel-preset-es2016": "^6.24.1",
"babel-preset-es2017": "^6.24.1",
"babel-preset-stage-1": "^6.24.1",
"webpack": "^3.4.1"
},
"dependencies": {
"enumify": "^1.0.4",
"socket.io-client": "^2.0.3"
}
}
webpack.config.js
const path = require('path');
module.exports = {
entry: {
background: "./src/background.js",
content: "./src/content.js",
popup: "./src/popup.js"
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: "[name].js"
},
module: {
loaders: [{
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: ["es2015", "es2016", "es2017", "stage-1"],
plugins: [
"transform-runtime",
// "babel-plugin-unassert"
]
}
}]
}
};
Instruction to run
To run, place all the files in a folder use npm install to install all the dependencies. and use npm run webpack to compile the code to dist folder. use the dist folder background.js as the background script.
I tried
Same process without babel or webpack, even without any promises. Find the background.js below
background.js
var isCompleted = false;
chrome.webNavigation.onBeforeNavigate.addListener((info) => {
if(info.frameId === 0) {
console.log('before navigation ' + info.url);
isCompleted = false;
}
});
chrome.webNavigation.onCompleted.addListener((info) => {
if(info.frameId === 0) {
console.log('on completed ' + info.url);
isCompleted = true;
}
});
// Create tab
chrome.tabs.create({
url: "https://google.com"
}, (tab) => {
console.log("tab created");
// Navigate to facebook.com
chrome.tabs.executeScript({
code: "window.location = 'https://www.facebook.com'",
runAt: "document_idle"
}, () => {
console.log("window location changed, current tabs is completed: " + isCompleted);
// Wait until facebook is loaded
if(!isCompleted) {
console.log('waiting until facebook is loaded');
setTimeout(() => {
// Alert hello world
chrome.tabs.executeScript({
code: "alert('hello world')",
runAt: "document_idle"
}, () => {
console.log("alert executed");
});
}, 5000);
} else {
console.log("**** tab status has not been changed even if it's navigating to facebook.com ****");
chrome.tabs.executeScript({
code: "alert('hello world')",
runAt: "document_idle"
}, () => {
console.log("alert executed");
});
}
});
});
Result I get still the same as before. console.log as below
tab created
before navigation https://google.com/
on completed https://www.google.lk/?gfe_rd=cr&ei=CVCfWervItzLugSemrqAAw
window location changed, current tabs is completed: true
**** tab status has not been changed even if it's navigating to facebook.com ****
before navigation https://www.facebook.com/
alert executed
on completed https://www.facebook.com/
I tried
Once we executed window.location, i guess there is a little time gap between current page unloading and new page is loading. So I listened to window.onBeforeUnload event in content script and passed a message to runtime once it's occurred.
content.js
window.addEventListener('beforeunload', (event) => {
chrome.runtime.sendMessage({type: "onbeforeunload"});
});
I listened to the message from background script.
background.js
var isCompleted = false;
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if(request.type === "onbeforeunload") {
console.log("page is unloading");
isCompleted = false;
}
}
);
But ended up getting this message even after webNavigation.onBeforeNavigate callback.

Related

browser.downloads.download - images disappearing after download

So I was tinkering with a firefox extension and came across something I can't explain. This extension downloads images from a certain site when a browser action (button) is clicked. Can confirm that the rest of the extension works perfectly and the code below has proper access to the response object.
const downloading = browser.downloads.download({
filename:response.fileName + '.jpg',
url:response.src,
headers:[{name:"Content-Type", value:"image/jpeg"}],
saveAs:true,
conflictAction:'uniquify'
});
const onStart = (id) => {console.log('started: '+id)};
const onError = (error) => {console.log(error)};
downloading.then(onStart, onError);
So the saveAs dialog pops up (filename with file extension populated), I click save, and then it downloads. As soon as the file finishes downloading it disappears from the folder it was saved in. I have no idea how this is happening.
Is this something wrong with my code, Firefox, or maybe a OS security action? Any help would be greatly appreciated.
Extra Information:
Firefox - 95.0.2 (64-bit)
macOS - 11.4 (20F71)
I had the same issue. You have to put download in background, background.js.
Attached sample of Thunderbird addon creates new menu entry in the message list and save raw message to the file on click.
If you look to the manifest.json, "background.js" script is defined in the "background" section. The background.js script is automatically loaded when the add-on is enabled during Thunderbird start or after the add-on has been manually enabled or installed.
See: onClicked event from the browserAction (John Bieling)
manifest.json:
{
"description": "buttons",
"manifest_version": 2,
"name": "button",
"version": "1.0",
"background": {
"scripts": ["background.js"]
},
"permissions": [
"menus","messagesRead","downloads"
],
"browser_action": {
"default_icon": {
"16": "icons/page-16.png",
"32": "icons/page-32.png"
}
}
}
background.js:
async function main() {
// create a new context menu entry in the message list
// the function defined in onclick will get passed a OnClickData obj
// https://thunderbird-webextensions.readthedocs.io/en/latest/menus.html#menus-onclickdata
await messenger.menus.create({
contexts: ["all"],
id: "edit_email_subject_entry",
onclick: (onClickData) => {
saveMsg(onClickData.selectedMessages?.messages);
},
title: "iktatEml"
});
messenger.browserAction.onClicked.addListener(async (tab) => {
let msgs = await messenger.messageDisplay.getDisplayedMessages(tab.id);
saveMsg(msgs);
})
}
async function saveMsg(MessageHeaders) {
if (MessageHeaders && MessageHeaders.length > 0) {
// get MessageHeader of first selected messages
// https://thunderbird-webextensions.readthedocs.io/en/latest/messages.html#messageheader
let MessageHeader = MessageHeaders[0];
let raw = await messenger.messages.getRaw(MessageHeader.id);
let blob = new Blob([raw], { type: "text;charset=utf-8" })
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/downloads
await browser.downloads.download({
'url': URL.createObjectURL(blob),
'filename': "xiktatx.eml",
'conflictAction': "overwrite",
'saveAs': false
});
} else {
console.log("No message selected");
}
}
main();

Problem stopping CORB from blocking requests

I have a Chrome extension that scrapes some values from a webpage based on some query selectors that are provided via an API call.
Relevant portion of manifest.json:
"background": {
"scripts": ["js/background.js"],
"persistent": false
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["js/jquery.min.js"]
}
],
"permissions": [
"<all_urls>",
"storage",
"activeTab"
]
}
js/background.js:
The idea here is that if a user has entered an atsmap value on their options page, we should perform an API call.
chrome.storage.sync.get(['atsmap'], function(result) {
if (result.atsmap) {
var url = "https://myurl.com/AtsMapping.aspx?AtsCode=" +
encodeURIComponent(result.atsmap)
fetch(url).then(r => r.text()).then(text => {
console.log(text);
response = JSON.stringify(text);
chrome.storage.sync.set({"fieldmapping": response}, function() {
console.log('Fieldmapping is set to ' + response);
});
})
}
return true;
});
This portion appears to be working properly, here is the console from the background page:
In popup.js (which is included at the bottom of popup.html), I call an inject.js script after the DOM is loaded:
// DOM Ready
$(() => {
'use strict';
chrome.tabs.executeScript({file: 'js/inject.js'}, () => {
// We don't need to inject code everwhere
// for example on chrome:// URIs so we just
// catch the error and log it as a warning.
if (chrome.runtime.lastError) {
console.warn(chrome.runtime.lastError.message);
}
});
// injected code will send an event with the parsed data
chrome.runtime.onMessage.addListener(handleInjectResults);
});
And finally, in js/inject.js, I get the value of fieldmapping from storage and attempt to use it:
(function () {
'use strict';
let fieldmap;
let message;
console.log("test");
chrome.storage.sync.get(['atsmap'], function(result) {
if (result.atsmap) {
chrome.storage.sync.get(['fieldmapping'], function(result) {
console.log('Value currently is ' + result.fieldmapping);
fieldmap = JSON.parse(result.fieldmapping);
console.log(fieldmap);
// <key> : { // ID of input on popup.js
// selector: <selector> // DOM selector of value in page
// value: <value> // value to use in popup.js
// }
if(fieldmap.AtsMapping[4].atsMapNotes == 'John Smith (2)') {
message = {
txtLName: {
selector: fieldmap.AtsMapping[6].lastName,
value: null
},
When I go to a demo page that I've setup for the scraping, then click my extension icon, rather than scraping the page for the form values, I get the following in the console:
I don't understand how, on inject.js line 32, I can console.log(fieldmap); and get what appears to be the proper response, and yet on inject.js line 39, the same fieldmap is undefined.
Any suggestions would be helpful as I'm completely lost here.

chrome.runtime.connectNative from external url

I have a Google Chrome extension which contains the following two files...
manifest.json
{
"key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcBHwzDvyBQ6bDppkIs9MP4ksKqCMyXQ/A52JivHZKh4YO/9vJsT3oaYhSpDCE9RPocOEQvwsHsFReW2nUEc6OLLyoCFFxIb7KkLGsmfakkut/fFdNJYh0xOTbSN8YvLWcqph09XAY2Y/f0AL7vfO1cuCqtkMt8hFrBGWxDdf9CQIDAQAB",
"name": "Native Messaging Example",
"version": "1.0",
"manifest_version": 2,
"description": "Send a message to a native application.",
"app": {
"launch": {
"local_path": "index.html"
}
},
"icons": {
"128": "icon-128.png"
},
"permissions": [
"nativeMessaging"
],
"externally_connectable": {
"matches": ["*://*.chrome-extension.com/*"]
},
"background": {
"scripts": ["background.js"]
}
}
background.js
var sendResponseCallBack;
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
sendResponseCallBack = sendResponse;
var message = {"comment": '*** ' + request['comment'] + ' ***'};
var useNative = false;
if (useNative) {
connect();
sendNativeMessage(message);
}
else {
sendResponseCallBack(message);
}
}
);
function connect() {
var hostName = "com.google.chrome.example.echo";
port = chrome.runtime.connectNative(hostName);
port.onMessage.addListener(onNativeMessage);
port.onDisconnect.addListener(onDisconnected);
}
function sendNativeMessage(message) {
port.postMessage(message);
}
function onNativeMessage(message) {
port.disconnect();
sendResponseCallBack(message);
}
I also configured the virtual host: chrome-extension.com to access to the url from a local server:
http://www.chrome-extension.com/
With the Chrome extension installed and enabled, if I access to:
http://www.chrome-extension.com/
and the variable useNative = false then I get a response from the plugin through: sendResponseCallBack(message);, but if useNative = true then I don't get any response from the plugin, I get: undefined and also the native operation which should take about 5 seconds, doesn't go thru because the undefined response is returned in 0 seconds.
I also have enabled another html page I access thru the extension url:
chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/calc-with-os.html
Inside that page I include the calc-with-os.js file which contains the above functions: connect() sendNativeMessage(message) onNativeMessage(message) and the function: chrome.runtime.connectNative works properly performing the native process in all its phases.
Any idea on how can I connect to a native process from an external url?
[EDIT: TRY NUMBER 2]
Based on the comment of: #wOxxOm I did the following modification to the code with the purpose of don't send the message to fast and wait for the native process to start, but it is not still working.
Any other suggestions?
var port = null;
var sendResponseCallBack;
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
sendResponseCallBack = sendResponse;
connect(request);
}
);
function connect(request) {
chrome.runtime.onConnect.addListener(function(p){
port = p;
port.onMessage.addListener(onNativeMessage);
port.onDisconnect.addListener(onDisconnected);
var message = {"comment": '*** ' + request['comment'] + ' ***'};
sendNativeMessage(message);
});
var hostName = "com.google.chrome.example.echo";
chrome.runtime.connectNative(hostName);
}
function sendNativeMessage(message) {
port.postMessage(message);
}
function onNativeMessage(message) {
port.disconnect();
sendResponseCallBack(message);
}

How to design Protractor page object navigation to avoid "window.angular is undefined" errors

I am having trouble with "window.angular is undefined" errors and I'm sure it has something to do with the asynchronous execution of JavaScript, but I don't know how to get around that. The login page and the initial landing page are non-Angular pages and the rest of the application is Angular. So, I need to login using a non-Angular page, then once the non-Angular landing page loads, open a drop down menu and click a link that loads an Angular page. It seems that all the actions just fly on, none of them waiting for navigation to complete before checking whether Angular is loaded or not.
I have this base class for page objects:
export class AbstractLoadable {
constructor(isAngularComponent) {
this.isAngularComponent = isAngularComponent;
}
initComponent() {
console.log("Initializing: " + this.isAngularComponent);
browser.waitForAngularEnabled(this.isAngularComponent);
if(this.isAngularComponent) {
console.log("Waiting for angular");
browser.waitForAngular();
}
return this;
}
}
I have this login page:
import {AbstractLoadable} from "./AbstractLoadable";
import {HomePage} from "./HomePage";
export class LoginPage extends AbstractLoadable {
constructor() {
super(false);
this.usernameInput = element(by.id("username"));
this.passwordInput = element(by.id("password"));
this.loginButton = element(by.css("[name='login']"));
}
load(baseUrl) {
browser.driver.get(baseUrl);
return this.initComponent();
}
login(username, password) {
this.usernameInput.sendKeys(username);
this.passwordInput.sendKeys(password);
this.loginButton.click();
return new HomePage().initComponent();
}
}
I have this Home page:
import {AbstractLoadable} from "./AbstractLoadable";
import {LoginPage} from "./LoginPage";
import {AngularPage} from "./AngularPage";
import {ExtendedExpectedConditions} from "../ExtendedExpectedConditions";
export class HomePage extends AbstractLoadable {
constructor() {
super(false);
this.menuButton = element(by.id("some locator"));
this.menuContainer = element(by.css("some locator"));
this.menuOptionLink = element(by.css("some locator"));
}
isMenuButtonPresent() {
return ExtendedExpectedConditions.isElementPresent(this.menuButton);
}
isMenuExpanded() {
return ExtendedExpectedConditions.isElementDisplayed(this.menuContainer);
}
expandMenu() {
this.isMenuButtonPresent().then(isPresent => {
if(!isPresent) {
ExtendedExpectedConditions.waitForElementVisible(this.menuButton, 120000)
}
});
this.isMenuExpanded().then(isExpanded => {
if(!isExpanded) {
this.menuButton.click();
ExtendedExpectedConditions.waitForElementVisible(this.menuContainer);
}
});
}
loadAngularPage() {
this.expandMenu();
this.menuOptionLink.click();
return new AngularPage().initComponent();
}
}
The wait methods are static utility methods in this class:
export class ExtendedExpectedConditions {
static waitForElementPresent(element, timeout = 30000) {
browser.wait(ExpectedConditions.presenceOf(element), timeout);
}
static waitForElementVisible(element, timeout = 30000) {
browser.wait(ExpectedConditions.visibilityOf(element), timeout);
}
static isElementPresent(element) {
return element.isPresent()
}
}
The angular page class has this constructor which passes 'true' to the base class constructor, indicating that it is an Angular page:
import {AbstractLoadable} from "./AbstractLoadable";
export class AngularPage extends AbstractLoadable {
constructor() {
super(true);
this.loadDialogButton = element(by.css("some locator"));
}
loadDialog() {
this.loadDialogButton.click();
//This class also extends the base class and has a constructor that passes true to the base class constructor, indicating that it is an Angular component
return new AngularDialog().initComponent();
}
}
When I tried to execute this test, I keep getting "window.angular is undefined" errors:
import {LoginPage} from "../pageobject/LoginPage.js";
describe("Test", () => {
it("Login and navigate", () => {
let hp = new LoginPage().load(browser.baseUrl).login("user", "pass");
let ap = hp.loadAngularPage();
let dialog = ap.loadDialog();
//isLoaded() checks visibility of dialog container element
expect(dialog.isLoaded()).toBeTruthy();
});
});
The console output is this:
Initializing: false
Initializing: false
Initializing: true
Waiting for angular
Initializing: true
Waiting for angular
Failed: Error while waiting for Protractor to sync with the page: "window.angular is undefined. This could be either because this is a non-angular page or because your test involves client-side navigation, which can interfere with Protractor's bootstrapping. See http://git.io/v4gXM for details"
Error: Error while waiting for Protractor to sync with the page: "window.angular is undefined. This could be either because this is a non-angular page or because your test involves client-side navigation, which can interfere with Protractor's bootstrapping. See http://git.io/v4gXM for details"
My package.json is this:
{
"name": "ui-tests",
"version": "1.0.0",
"description": "A description",
"scripts": {
"test": "node_modules/protractor/bin/protractor conf.js",
"start_selenium": "node_modules/protractor/node_modules/webdriver-manager/bin/webdriver-manager start",
"update_selenium": "node_modules/protractor/node_modules/webdriver-manager/bin/webdriver-manager update"
},
"dependencies": {
"babel-preset-es2015": "^6.24.1",
"babel-register": "^6.24.1",
"jasmine-reporters": "^2.2.1",
"protractor": "^5.1.2"
},
"keywords": [
"es6"
],
"babel": {
"presets": [
"es2015"
]
}
}
My conf.js is this:
require("babel-register");
exports.config = {
framework: 'jasmine2',
rootElement: 'body',
seleniumServerJar:'./node_modules/protractor/node_modules/webdriver-manager/selenium/selenium-server-standalone-3.4.0.jar',
chromeDriver: './node_modules/protractor/node_modules/webdriver-manager/selenium/chromedriver_2.30',
specs: ['tests/*Spec.js'],
capabilities: {
browserName: 'chrome',
acceptSslCerts: true,
trustAllSSLCertificates: true,
chromeOptions: {
args: ['--no-sandbox']
},
},
baseUrl: 'https://www.myurl.com',
suites: {
login: '../tests/theTestSpec.js'
},
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 3600000,
isVerbose: true
},
getPageTimeout: 120000,
allScriptsTimeout: 3600000,
delayBrowserTimeInSeconds: 0,
onPrepare: function() {
require("babel-register");
let origFn = browser.driver.controlFlow().execute;
browser.driver.controlFlow().execute = function () {
let args = arguments;
origFn.call(browser.driver.controlFlow(), function () {
return protractor.promise.delayed(this.delayBrowserTimeInSeconds * 100);
});
return origFn.apply(browser.driver.controlFlow(), args);
};
let getScreenSize = function() {
return browser.driver.executeScript(function() {
return {
width: window.screen.availWidth,
height: window.screen.availHeight
};
});
};
getScreenSize().then(function(screenSize) {
browser.driver.manage().window().setSize(screenSize.width, screenSize.height);
});
}
};
All protractor calls return promises. In your current code you create them but do not always wait for them to resolve. Everything that needs to happen after each other needs to be chained with the then keyword. promises
for example both of these checks are being done at the same time.
expandMenu() {
this.isMenuButtonPresent().then(isPresent => {
if(!isPresent) {
ExtendedExpectedConditions.waitForElementVisible(this.menuButton, 120000)
}
});
this.isMenuExpanded().then(isExpanded => {
if(!isExpanded) {
this.menuButton.click();
ExtendedExpectedConditions.waitForElementVisible(this.menuContainer);
}
});
}
And here 'this' is returned immediately while browser.waitForAngular() is waiting asynchronously.
initComponent() {
console.log("Initializing: " + this.isAngularComponent);
browser.waitForAngularEnabled(this.isAngularComponent);
if(this.isAngularComponent) {
console.log("Waiting for angular");
browser.waitForAngular();
}
return this;
}
you will have to refactor your functions to return promises, so you can chain them one after another.

Access Firefox' LocalStorage from extension

I build a Firefox Extension with an options page using this documentation
Implement a settings page
On this settings page, the user has the possibility to enter Username and Password which then are stored in localStorage.
function saveOptions() {
localStorage.setItem("hbz_username", document.querySelector("#username").value);
localStorage.setItem("hbz_pass", Aes.Ctr.encrypt(document.querySelector("#password").value, '12345678910111213', 128));
}
The addon manipulates the DOM of a website and generates links to a REST Webservice within HTML elements which are containing a string that matches a specific Regex pattern. When one clicks on that link, the extension should load the user credentials from localStorage
var username= localStorage.getItem("hbz_username");
var passwd = localStorage.getItem("hbz_pass");
But this doesn't work. When I'm trying it the way described in the above listed documentation, I get a "browser is not defined" Error in the F12 Console.
function restoreOptions() {
function setCurrentChoice(result) {
username = result.username;
}
function onError(error) {
console.log(`Error: ${error}`);
}
var getting = browser.storage.local.get("hbz_username");
getting.then(setCurrentChoice, onError);
}
So my question is: How do I access the localStorage from my extension?
Edit: Added the contents of my previous answer on the post to the question.
Sorry for the wait but I had to prepare a minified version of the plugin without any cryptography or such.
package.json
{
"title": "Addon From Scratch",
"name": "addon-from-scratch",
"version": "0.0.1",
"description": "A basic add-on",
"main": "index.js",
"author": "Chris",
"engines": {
"firefox": ">=38.0a1",
"fennec": ">=38.0a1"
},
"license": "MIT",
"keywords": [
"jetpack"
]
}
index.js
var self = require("sdk/self");
// a dummy function, to show how tests work.
// to see how to test this function, look at test/test-index.js
function dummy(text, callback) {
callback(text);
}
exports.dummy = dummy;
var tag = "body"
var buttons = require('sdk/ui/button/action');
var tabs = require("sdk/tabs");
var pageMod = require("sdk/page-mod");
var data = require("sdk/self").data;
var button = buttons.ActionButton({
id: "mozilla-link",
label: "Visit Mozilla",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onClick: handleClick
});
function handleClick(state) {
tabs.open("login.html")
}
function onOpened() {
console.log(`Options page opened`);
}
function onError(error) {
console.log(`Error: ${error}`);
}
pageMod.PageMod({
include: "*",
contentScriptFile: data.url("modify-content.js"),
onAttach: function(worker) {
worker.port.emit("getElements", tag);
worker.port.on("gotElement", function(elementContent) {
console.log(elementContent);
});
}
});
modify-content.js
self.port.on("getElements", function() {
var username= localStorage.getItem("hbz_username");
var passwd = localStorage.getItem("hbz_pass");
//here is where usually the DOM Manipulation takes place and the link to the REST Service is created. The link contains the username and the encrypted password as param
alert(username + passwd);
});
options.js
function saveOptions() {
localStorage.setItem("hbz_username", document.querySelector("#username").value);
localStorage.setItem("hbz_pass", document.querySelector("#password").value);
}
function restoreOptions() {
function setCurrentChoice() {
document.querySelector("#username").value = localStorage.getItem("hbz_username");
document.querySelector("#password").value = localStorage.getItem("hbz_pass");
}
function onError(error) {
console.log(`Error: ${error}`);
}
setCurrentChoice();
}
document.addEventListener("DOMContentLoaded", restoreOptions);
document.querySelector("form").addEventListener("submit", saveOptions);
login.html
<form>
Username:<input type="text" id="username" />
Password:<input type="password" id="password" />
<input type="submit" Value="Submit" />
</form>
<script src="options.js"></script>
As I mentioned, I already tried the solution from the above mentioned documentation. This is the code as it is actually. I hope this helps.

Categories

Resources