Chrome DevTools Protocol: control new tabs - node.js

Need to be done: I need to simulate user interactions (journeys) across a chain of sites.
Question: Do you have any tips how to programatically controll a tab opened as a result of a simulated click?
My experience:
I'm using the chrome-remote-interface npm package.
I'm able to simulate a click with a custom ChromeController class which initializes the chrome-remote-interface and these methods:
async simulateClick(selector) {
return await this.evaluate(function (selector) {
document.querySelector(selector).click()
}, selector);
}
/**
* Shamelessly stolen from simple-headless-browser
*/
async evaluate (fn, ...args) {
const exp = args && args.length > 0 ? `(${String(fn)}).apply(null, ${JSON.stringify(args)})` : `(${String(fn)}).apply(null)`
const result = await this.client.Runtime.evaluate({
expression: exp,
returnByValue: true
})
return result
}
Now I would like to interact with the recently opened tab. I can get the targetId of the new tab with the experimenetal Target Domain (prototyping in node cli):
var targets;
chromeController.client.Target.getTargets().then(t => targets = t);
Which results in:
{ targetInfos:
[ { targetId: '97556479-cdb6-415c-97a1-6efa4e00b281',
type: 'page',
title: 'xxx/preview/239402/',
url: 'xxx/preview/239402/' },
{ targetId: 'bbfe11d5-8e4a-4879-9081-10bb7234209c',
type: 'page',
title: 'Document',
url: 'xxx/preview/239402/teaser/0/' } ] }
I am able to switch between the tabs with:
chromeController.client.Target.activateTarget({targetId:'xxx'})
However I'm not able to get any interaction with this, I can't find the connection, how to load it into the Page and Runtime objects.
I've searched in the docs and also tried googling: 'site:chromedevtools.github.io targetId' which only lead me to
> chromeController.client.Browser.getWindowForTarget({targetId: '97556479-cdb6-415c-97a1-6efa4e00b281'}).catch(e => console.log(e.message));
Promise { <pending> }
> 'Browser.getWindowForTarget' wasn't found
I've also tried to Target.setDiscoverTargets({discover: true}) and to close the original tab.
Thanks for any help!

Recently faced this same issue and in short I had to create a new dev tools protocol client for each new target I wanted control over.
My experience is with dev tools protocol using direct communication with websocket but the api is the same so it should be similar. So here is a summary of what I had to do.
Initially looking at the docs I would have assumed Target.attachToTarget should give us control of the new tab but I found that it didn't work.
My workaround was to create a listener that listened for the Target.targetCreated event which provides a targetInfos just like you found with Target.getTargets but for every new target created like a new tab, page, or iframe. Note: you need to enable Target.setDiscoverTargets in order to receive these events over the protocol.
[ { targetId: '97556479-cdb6-415c-97a1-6efa4e00b281',
type: 'page',
title: 'xxx/preview/239402/',
url: 'xxx/preview/239402/' },
{ targetId: 'bbfe11d5-8e4a-4879-9081-10bb7234209c',
type: 'page',
title: 'Document',
url: 'xxx/preview/239402/teaser/0/' } ] }
With that listener I looked for targets that were of type page, you could filter on a specific url if you know what the page will be. With the targetId in hand I requested available websocket targets following the HTTPEndpoints section near the bottom of the devtools home page.
GET /json or /json/list
A list of all available websocket targets.
[ {
"description": "",
"devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:9222/devtools/page/DAB7FB6187B554E10B0BD18821265734",
"id": "DAB7FB6187B554E10B0BD18821265734",
"title": "Yahoo",
"type": "page",
"url": "https://www.yahoo.com/",
"webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/DAB7FB6187B554E10B0BD18821265734"
} ]
I could then launch a new dev tools protocol client using the webSocketDebuggerUrl and have full control over the tab.
I know this is a pretty round about way but, its the only way I was able to make if work.
Although these days it's probably easier to use something like puppeteer to interface with multiple tabs in chrome if you can. Here is the source code to a puppeteer module that follows new tabs that could be good reference for trying to replicate it pageVideoStreamCollector.ts
This is a very late answer but just putting this here if anyone else has the same issue as help on chrome dev tools is very hard to come by. Hope it helps someone out.

I also am getting "Browser.getWindowForTarget wasn't found" on debian, google-chrome-unstable version 61

Related

Prevent popup if current tab url is not permitted in manifest v3

I'm writing a chrome extension and I want to either disable the popup entirely, or present a message if the url of the current tab is not permitted via the v3 manifest's host_permissions property.
This is for a dev support tool and we don't want it enabled for the production url. So if I set:
"host_permissions": [
"http://localhost:3000/",
"https://*.dev.some-site.com/",
"https://www.uat.some-site.com/"
]
...Then if the user is on www.some-site.com (or anywhere else), I want the popup to be disabled.
I can obtain the relevant url easily enough:
let [currentTab] = await chrome.tabs.query({ active: true, currentWindow: true });
const { url } = currentTab;
const cookieUrl = url.match(/https?:\/\/[^/]+\//)?.[0];
...and I can obtain the array of permitted file patterns with
chrome.runtime.getManifest().host_permissions
But how can I use this or anything else to prevent the popup? Also, converting that wild-card into a genuine regex will be a bit of a pain. Isn't there some out-of-the-box method to do all this??
Use chrome.declarativeContent API to enable the action popup for sites in host_permissions and disable it everywhere else by default.
When disabled, the icon becomes grayed out. Clicking it will show the built-in context menu, chrome.action.onClicked won't be triggered. The name of the extension in the puzzle-piece menu is also grayed out.
manifest.json:
"action": {"default_icon": "icon.png"},
"permissions": ["declarativeContent"],
background script:
chrome.action.disable();
chrome.runtime.onInstalled.addListener(() => {
chrome.declarativeContent.onPageChanged.removeRules(() => {
chrome.declarativeContent.onPageChanged.addRules([{
conditions: chrome.runtime.getManifest().host_permissions.map(h => {
const [, sub, host] = h.match(/:\/\/(\*\.)?([^/]+)/);
return new chrome.declarativeContent.PageStateMatcher({
pageUrl: sub ? {hostSuffix: '.' + host} : {hostEquals: host},
});
}),
actions: [new chrome.declarativeContent.ShowAction()],
}]);
});
});
For simplicity, I don't limit PageStateMatcher's schemes.

how to add urls to Flash white list in puppeteer

I develop small crawler with puppeteer in Node.js.
the target site has Flash contents, so I want to enable Flash in puppeteer.
by default, puppeteer is unable to use Flash and white list of permitted sites is empty.
I know how to enable Flash in puppeteer, but I don't know how to set white list.
How to do that?
Is there flag like this?
const browser = await puppeteer.launch({
headless: false,
args: [
'--ppapi-flash-path = {FLASH_PATH}',
'--white-url = {TARGET_URL}'
]
});
Or, is it only way to simply manipulate DOM in setting page in browser (ex.chrome://settings/content/flash)?
I've solved the problem myself.
I can't find the flag of Flash white list and manipulate DOM of chrome's setting page is too tiring for me, but I got useful some params.
1. 'userDataDir: {PROFILE_FILE}'
First, manually launch chrome or chromium, and add url to the list.
Then, set PROFILE_FILE as its user data path.
2. executablePath: '{PATH_TO_CHROME}'
Basically, run on chrome but not chromium, we can use Flash and HLS and etc. by default.
Set manually white list, and it works.
After much tinkering I found a Preferences file configuration that whitelists all flash content by default.
I've made a small puppeteer wrapper to make using this very easy: puppeteer.setExtra({allowFlash: true})
{
"profile": {
"content_settings": {
"exceptions": {
"flash_data": {
"*,*": {
"setting": {
"flashPreviouslyChanged": true
}
}
},
"permission_autoblocking_data": {
"*,*": {
"setting": {
"Flash": {
"dismiss_count": 1
}
}
}
},
"plugins": {
"*,*": {
"per_resource": {
"adobe-flash-player": 1
}
}
}
},
"pref_version": 1
}
}
}

Using Pubsub to push Gmail in node.js - How to set publish rights?

I'm trying grant publish rights to a topic using SetIamPolicy as has been described here: [Google Cloud Pub/Sub API - Push E-mail but no success yet - not sure what the exact call should look like.
For now I've made a json file containing this:
POST "https://pubsub.googleapis.com/v1beta2/{resource=projects/myproject/topics/mytopic}:setIamPolicy"
Content-type: application/json
{
"policy": {
"bindings": [{
"role": "roles/pubsub.publisher",
"members": ["serviceAccount:gmail-api-push#system.gserviceaccount.com"],
}],
}
}
as described here: http://developers.google.com/gmail/api/guides/push and call it like this: topics.SetIamPolicy('pubsub_policy.json'); - but setiampolicy is an undefined function. Any ideas? Googling has yielded absolutely nothing in terms of examples in node.js
I used the Google Developers Console
After a lot searching I found it was easier to just set the publish rights in the Google Developers Console.
click on your project
click on Big Data > Pub/Sub
If you haven't already done so, create a topic
Check the box next to that topic
Click the permissions button at the top
Add your permissions in the box that should look like this
I did find this documentation about setIamPolicy() for nodejs.
var myPolicy = {
bindings: [
{
role: 'roles/pubsub.subscriber',
members: ['serviceAccount:myotherproject#appspot.gserviceaccount.com']
}
]
};
topic.iam.setPolicy(myPolicy, function(err, policy, apiResponse) {});
subscription.iam.setPolicy(myPolicy, function(err, policy, apiResponse) {});
But I couldn't get it to work. topic.iam.setPolicy() didn't exist. All I saw was this:
//console.log(topic)
{
...
iam: {
resource: 'projects/[projectId]/topics/[topicName]',
makeReq_: [Function] }
}

FineUploaderBasic for Azure nothing shows up

var uploader = new qq.azure.FineUploaderBasic({
debug: true,
element: document.getElementById("fine-uploader-gallery"),
signature: {
endpoint: '#Url.Action("GetUploadSASUrl", "Upload")'
},
uploadSuccess: {
endpoint: '#Url.Action("ProcessImage", "Upload")'
},
scaling: {
sendOriginal: false,
sizes: [
{ name: "", maxSize: 800 }
]
},
validation: {
allowedExtensions: ['jpeg', 'jpg', 'png']
}
});
</script>
Trying to create a FineUploader Basic instance for Azure but nothing shows up. What am I doing wrong?
I've added a debug: true directive but nothing shows up in the console. The initial script tag is there and a div with the id of "fine-uploader-gallery" is there. All the scripts and CSS are on the page.
Why would you expect something to "show up"? You are using Fine Uploader's "core" mode, which assumes you are providing the entire UI yourself and simply making use of the API, options, and events to drive it. If you want to render a default but customizable UI, you should use UI mode instead. More information about all of these can be found on the docs site at http://docs.fineuploader.com. Before you go any further, you should spend some time familiarizing yourself with these options.

Issue in binding kendo grid with web api hosted on Azure

I am struggling in order to bind the kendo grid with Web API. Web API is hosted in the Azure worker role (not yet published). And the Web API is decorated in order to work with the cross domains i.e Enabled CORS for the Web API.
Let us look the code I have written.
Action method in the Web API look like the following.
public JToken Get()
{
JToken json = JObject.Parse(
"{ \"firstName\": \"John\",
\"lastName\": \"Smith\",
\"isAlive\": true,
\"age\": 25,
\"height_cm\": 167.6,
\"address\":
{
\"streetAddress\": \"21 2nd Street\",
\"city\": \"New York\",
\"state\": \"NY\",
\"postalCode\": \"10021-3100\"
},
\"phoneNumbers\":
[{
\"type\": \"home\",
\"number\": \"212 555-1234\"
},
{
\"type\": \"office\",
\"number\": \"646 555-4567\"
}]
}"
);
return json;
}
When I execute this web api we will get the emulator with the IP address as "127.0.0.1" and I used the controller "TestController".
When I execute the above api in the browser, I am getting the JSON data perfectly, but when I use the same to bind the grid, all is vain.
The code used to bind the data is
$("#dw_report_container1").kendoGrid({
dataSource: {
type: "json",
transport: {
read: {
url: "http://127.0.0.1/test",
dataType: "json"
}
},
},
height: 500,
scrollable: true,
selectable: true
});
I am getting no exception.
Finally, after a long struggle i got the solution :(. I find a strange thing when working with the kendo grid. Whenever we work with the models inside our project and returned the model to the datasource of the kendogrid, it internally creates a model for it. As i worked with sample json as above, it (kendogrid) doesn't create the model for the grid to bind. Hence the problem occurs. Eventually, what i concluded that it is better to have a schema/model in the datasource (kendo grid). Past i didn't :( Thank you.

Resources