it's days I'm trying to figure out how to make a chrome extension I'm building working...with AngularJS.
Problem is that I cannot successfully access remote resources; as long as I didn't use angular, my way to communicate between the main html and the sandbox was with a pub/sub javascript I found on the web pkg.js and in this way I could call external urls in the main.js and pass retrieved data back to subscribed javascripts.
I have heavily simplified my code so that you can better understand the architecture. Here are some test resources that works perfectly apart from the fact...that angular does not work :/
My manifest has the following relevant parts:
{
...
"background": {
"scripts": ["new/ext/background.js"],
"persistent": false
},
"permissions": [
"tabs",
"http://*.mysite.com/*"
],
"sandbox": {
"pages": [ "new/testmain.html" ]
},
"content_security_policy": "script-src 'self' https://ajax.googleapis.com; object-src 'self'"
}
My background.js is very simple: it just creates a tab and calls an html which will host the angular code:
chrome.browserAction.onClicked.addListener(function (tab) {
chrome.tabs.create({'url': chrome.extension.getURL('/new/main.html')}, function (tab) { });
});
The main.html is, too, pretty easy: just some script includes and the iframe:
<!doctype html>
<html>
<head>
<title>Testing angular</title>
<script type='text/javascript' src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.js"></script>
<!-- custom scripts -->
<script type="text/javascript" src="/lib/pkg.js"></script>
</head>
<body style="height: 100%">
<h1>Iframe below</h1>
<iframe id="iframe" src="testmain.html" width="100%" height="100%" frameborder="none"></iframe>
<script type="text/javascript" src="testmain.js"></script>
</body>
</html>
The testmain.js just subscribes to an event and, in case receives it, it publishes dummy data:
iframe = document.getElementById("iframe");
$.pkg.init(iframe.contentWindow);
$.pkg.listen("items", function () {
console.log("[testmain.js] received items message");
items = [
{ name:"foo", age: 12 },
{ name:"bar", age: 11 },
{ name:"mickey", age: 15},
{ name: "donald", age: 27}
];
$.pkg.send("response", [items]);
console.log("[testmain.js] sent response");
});
Now let's come to testmain.html:
<!DOCTYPE html>
<html data-ng-app="App" data-ng-csp>
<head>
<title></title>
</head>
<body ng-controller="TodoCtrl">
<div ng-repeat="item in testitems">
<ul>
<li>{{item.name}}</li>
</ul>
</div>
<script type='text/javascript' src="lib/jquery.min.js"></script>
<script type="text/javascript" src="lib/angular.min.js"></script>
<script src="/lib/pkg.js"></script>
<script src="app/testapp.js"></script>
</body>
</html>
And, finally, here is the testapp.js:
var App = angular.module('App', []);
App.controller('TodoCtrl', function($scope) {
//test
$.pkg.init(window.top);
$.pkg.send("items");
console.log("[testapp.js] sent message items");
$.pkg.listen("response", function(res){
console.log("[testapp.js] received " + res.length + " items");
$scope.testitems = res;
for (i = 0; i < $scope.testitems.length; i++) {
console.log("[testapp.js] testitem: " + $scope.testitems[i].name);
}
});
});
The controller just inits the communication with destination = html containing the iframe. It then sends a message requesting for data which is then sent back (listens).
If I try from the console to run any manual command to get the $scope variable, no success at all:
document.getElementById('iframe')
Sandbox access violation: Blocked a frame at "chrome-extension://jcnppfndabbcgbdcnjncnoddmhmkmmbb" from accessing a frame at "chrome-extension://jcnppfndabbcgbdcnjncnoddmhmkmmbb". The frame being accessed is sandboxed and lacks the "allow-same-origin" flag.
Sandbox access violation: Blocked a frame at "chrome-extension://jcnppfndabbcgbdcnjncnoddmhmkmmbb" from accessing a frame at "chrome-extension://jcnppfndabbcgbdcnjncnoddmhmkmmbb". The frame being accessed is sandboxed and lacks the "allow-same-origin" flag.
Sandbox access violation: Blocked a frame at "chrome-extension://jcnppfndabbcgbdcnjncnoddmhmkmmbb" from accessing a frame at "chrome-extension://jcnppfndabbcgbdcnjncnoddmhmkmmbb". The frame being accessed is sandboxed and lacks the "allow-same-origin" flag.
<iframe id="iframe" src="testmain.html" width="100%" height="100%" frameborder="none"></iframe>
This is what happens in the console:
[testapp.js] sent message items testapp.js:8
[testmain.js] received items message testmain.js:5
[testmain.js] sent response testmain.js:13
[testapp.js] received 4 items testapp.js:10
[testapp.js] testitem: pippo testapp.js:13
[testapp.js] testitem: pluto testapp.js:13
[testapp.js] testitem: paperino testapp.js:13
[testapp.js] testitem: minnie testapp.js:13
as you may notice, communication DO occur, $scope.testitems variable is set correctly. BUT nothing is rendered on the iframe :(
Can anyone help me figure out how to come out of all of this?
Btw, I was in a some way forced to do so - the first version which was on plnkr gladly used the $http inside the controller, but this appears to be prohibited in chrome extensions...
I also tried to use amplifyjs but didn't come to out to make the communication work between an iframe and its container.
Thanks a lot!
Related
I am trying to make a functional connection to GA API according to the official docs - https://developers.google.com/analytics/devguides/reporting/core/v4/quickstart/web-js. I did everything needed to set it up however the solution is not working. The weird thing is that the button for the authentication is loaded and its working up until the point where I select the google account to login with. After that however loading appears and the auth window gets closed. There is no error in the console, nothing happens at all.
I use it on localhost currently but I've also tried it on a server with the same result. After selecting the account, every next attempt doesn't even require choosing the account so the window just opens, loading appears and closes again without anything happening.
screenshot
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello Analytics Reporting API V4</title>
<meta name="google-signin-client_id" content="<CLIENT-ID>">
<meta name="google-signin-scope" content="https://www.googleapis.com/auth/analytics.readonly">
</head>
<body>
<h1>Hello Analytics Reporting API V4</h1>
<!-- The API response will be printed here. -->
<textarea cols="80" rows="20" id="query-output"></textarea>
<!-- The Sign-in button. This will run `queryReports()` on success. -->
<p class="g-signin2" data-onsuccess="queryReports"></p>
<script>
const VIEW_ID = '<VIEW-ID>';
// Query the API and print the results to the page.
function queryReports() {
gapi.client.request({
path: '/v4/reports:batchGet',
root: 'https://analyticsreporting.googleapis.com/',
method: 'POST',
body: {
reportRequests: [
{
viewId: VIEW_ID,
dateRanges: [
{
startDate: '7daysAgo',
endDate: 'today'
}
],
metrics: [
{
expression: 'ga:sessions'
}
]
}
]
}
}).then(displayResults, console.error.bind(console));
}
function displayResults(response) {
var formattedJson = JSON.stringify(response.result, null, 2);
document.getElementById('query-output').value = formattedJson;
}
</script>
<!-- Load the JavaScript API client and Sign-in library. -->
<script src="https://apis.google.com/js/client:platform.js"></script>
</body>
</html>
Is there something wrong in the code which I missed?
I am trying to follow the simplest of examples of how to setup up Google's "
One tap sign-up and automatic sign-in" in a web page. Just copy and pasting the code provided by google, I get an exception "No credentials available". Why? How to fix it?
This is my web page content ...
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title of the document</title>
<link rel="icon" href="favicon.ico" type="image/x-icon" />
<script src="https://smartlock.google.com/client"></script>
<script>
console.log( "window.location.origin=" + window.location.origin);
window.onGoogleYoloLoad = (googleyolo) => {
console.log("Google one tap ready");
};
const retrievePromise = googleyolo.retrieve({
supportedAuthMethods: [
"https://accounts.google.com",
"googleyolo://id-and-password"
],
supportedIdTokenProviders: [
{
uri: "https://accounts.google.com",
clientId: "xxxx-86peetl434op89ug41lg1vv8sjspfupp.apps.googleusercontent.com"
}
]
});
</script>
</head>
<body>
Content of the document......
</body>
</html>
The Authorized JavaScript origins has been correctly setup. I am confident of that. The URL for the above content is: https://s3-ap-southeast-2.amazonaws.com/sbd-aws-sdk-delphi-22/index.html
Upon hitting the page (browser=Chrome), the resultant javascript console is ..
window.location.origin=https://s3-ap-southeast-2.amazonaws.com
Google one tap ready
index.html:1 Uncaught (in promise)
a {type: "noCredentialsAvailable", name: "OpenYoloError", message:
"noCredentialsAvailable: No credential is available for the current user."}
message:
"noCredentialsAvailable: No credential is available for the current user."
Context: When I make this web page hit, I am logged into Google.
What am I doing wrong?
The problem is that there is currently no user signed in. The library has very little support for this, as a work around, when no Google Accounts are active and no credentials are available. You can simply show the traditional Google Sign-In button and have the user click to proceed through the flow to activate their Google Account in the browser.
https://developers.google.com/identity/sign-in/web/
i want to display the array data i have in the background when the get data button is clicked but nothing is done when i click the button am very new for chrome ext. thanks.
manifest.json:
{
"name":"modz",
"manifest_version":2,
"version":"1.0",
"description":"this ext. will help you record all the urls you have visited",
"background": {
"scripts": ["background.js"]
},
"browser_action":
{
"default_icon":"icon.png",
"default_popup":"popup.html"
},
"permissions":[
"tabs"
]
}
background.js:
var data=[];
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
var url = tab.url;
if (url!=="undefined" && changeInfo.status=="complete")
{
data.push (tab.url);
alert(data.length);
}
}
);
chrome.runtime.onMessage.addListener(function(message,sender,sendrespose){
//send the array data back
});
popup.js:
document.addEventListener('DOMContentLoaded', function () {
document.getElementById('btn').addEventListener('click',function(){
chrome.runtime.sendMessage("getdata");
});
});
popup.html
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="C:\Users\modz\Desktop\modz_extention\popup.js"></script>
<style type="text/css">
body{
width:440px;
}
</style>
</head>
<body>
<input id="btn" type="button" value="get data" />
<div id="data"></div>
</body>
</html>
The official reference for messaging is here. In your case, you’d want background.js to have
chrome.runtime.onMessage.addListener(function(message,sender,sendResponse){
sendResponse({"dataArray":data});
});
popup.js would have
document.addEventListener('DOMContentLoaded', function () {
document.getElementById('btn').addEventListener('click',function(){
chrome.runtime.sendMessage({},function(response){
document.getElementById("data").textContent = response.dataArray.toString();
});
});
});
This would also work in a content script. But if the content script were running at the default document_end, it wouldn’t need the DOMContentLoaded event, since document_end happens afterward.
This is actually sending an empty message (the empty object {}). If there were different messages you wanted to send, you’d want to change that. This is also why message isn’t used in background.js.
Since you’re not really sending a message, another approach is to use getBackgroundPage. background.js wouldn’t need the listener, and popup.js would have:
chrome.runtime.getBackgroundPage(function(backgroundWindow){
document.getElementById("data").textContent = backgroundWindow.data.toString();
});
Two more things:
popup.html can’t use the absolute path to popup.js. Put both in the extension directory, and use a relative path: src="popup.js".
Google recommends that you transition background pages to event pages. The biggest difference is that you can’t have the global variable data in event pages (you can, but it gets reset when the event page reloads). If you have trouble getting that working, I’d recommend getting your extension working as a background page, and then posting another question.
I am trying to test an index from my local machine. I created a simple HTML page with a query box that sends the query to ES using the elasticsearch.js client. Both the index and the browser are on my desktop, so there shouldn't be a cross origin problem, but I keep getting an error that states:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:9200/portal/book/_search?q=title%3Ahistoire. This can be fixed by moving the resource to the same domain or enabling CORS.
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:9200/. This can be fixed by moving the resource to the same domain or enabling CORS.
I tried enabling CORS, but get the same error. Here are the index's settings:
{
"portal": {
"settings": {
"index": {
"creation_date": "1421788614558",
"uuid": "jg-iHjnSTDGHODY0_x4Ysw",
"number_of_replicas": "1",
"http": {
"cors": {
"enabled": "true",
"allow-origin": "/(http://)?localhost(:[0-9]+)?/"
}
},
"number_of_shards": "5",
"version": {
"created": "1040299"
}
}
}
}
}
index.html
<!DOCTYPE html >
<html ng-app="portal">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.min.css">
<script src="bower_components/angular/angular.min.js"></script>
<script src="bower_components/elasticsearch/elasticsearch.min.js"></script>
<script src="js/app.js"></script>
<script src="js/controllers.js"></script>
</head>
<body ng-controller="SearchCtrl">
<div class="container-fluid">
<div class="row-fluid">
<span class="span3">
<input class="input-block-level" ng-model="queryTerm" type="text">
</span>
<button ng-click="search()" class="btn" type="button">Search</button>
</div>
<div class="row-fluid">
Found {{ results.hits.total }}
<div class="span4">
<li ng-repeat="doc in results.hits.hits">
{{ doc._source.title }}
</li>
</div>
</div>
</div>
</body>
</html>
app.js
angular.module('portal', [
'controllers',
]);
var client = new elasticsearch.Client({
host: 'http://localhost:9200',
apiVersion: '1.3',
});
controller.js
angular.module('controllers', []).controller('SearchCtrl',
function($scope) {
$scope.search = function(query) {
$scope.results = client.search({
index: 'portal',
type: 'book',
q: 'title:' + $scope.queryTerm
}, function (error, response) {console.log('could not execute query!')}
);
};
}
);
Add following line to your config yml file
http.cors.enabled : true
http.cors.allow-origin: "*"
http.cors.allow-methods: OPTIONS, HEAD, GET, POST, PUT, DELETE
http.cors.allow-headers: X-Requested-With,X-Auth-Token,Content-Type,Content-Length
http.cors.allow-credentials: true
and kill elastic search java process on unix based system like is demonstrated on this topic response https://stackoverflow.com/a/41644614/11079315
I don't know why you are getting a cross domain error to begin with. Are you opening your front end as file://.... ? Totally random guess and not sure it matters. If you are and want to run your UI through a web server, you can open a terminal, cd into that dir and run 'python -m SimpleHTTPServer 7000'. Now your UI is running on localhost:7000.
For CORS settings, see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/modules-http.html and, if you are using Chrome, http://www.williamjohnbert.com/2013/06/allow-cors-with-localhost-in-chrome/ might help.
In your config you should at least set 'http.cors.enabled: true' and 'http.cors.allow-credentials: true'. The other defaults should be sensible. You may also want to modify the 'http.cors.max-age' setting in case old settings are cached.
Use the js console, net tab in your browser to see what headers you are getting back.
I need to parse a content that is dynamically generated by a javascript from a remote page.
For example, I need to get the price from this page: http://www.alibaba.com/product-detail/BRG-Newest-Fashional-Protective-Case-For_1666645206.html#J-wrapper
but the price is generated by a javascript, so when I download the page with ajax, it downloads only the html file without the results of the scripts.
So I tried to embed in the background an iframe and then parse the document inside this iframe, but there is a security issue that doesn't let me parse it.
Do you know if there is another way I can do it?
The function that I use is this:
$.ajax({
url: url,
dataType: 'text',
success: function(html, _, xhr) {});
but the resulting HTML is without the scripts information, so the price is empty.
I tried to use also:
<html lang="en">
<head>
<meta charset="utf-8">
<title>contents demo</title>
<script src="http://code.jquery.com/jquery-1.10.2.js"></script>
</head>
<body>
<iframe src="http://api.jquery.com/" width="80%" height="600" id="frameDemo"></iframe>
<script>
document.getElementById("frameDemo").onload = function() {
var contents = $( "#frameDemo" ).contents();
}
</script>
</body>
</html>
but I get this error:
Uncaught SecurityError: Failed to read the 'contentDocument' property from 'HTMLIFrameElement':
Blocked a frame with origin "null" from accessing a frame with origin "api.jquery.com".
The frame requesting access has a protocol of "file", the frame being accessed has a protocol of "http".
Protocols must match.
Instead of using an iframe, try using a chrome app webview. It will run in a segregated container without the restrictions you referenced. But you can still communicate with it.