Opening a mailto: link from a chrome packaged app - google-chrome

I've created a chrome packaged app which comes down from the Chrome Store as a CRX. I'd like to let the user send mail from the app, using a mailto: link. It appears that this is not allowed, because of security restrictions (when I try in development, that's the error I get).
I found another thread where they said it worked by setting location.href, but that question is pretty old, and I'm assuming the security restriction may be new.
So… is there any way to open the user's local mail client from a chrome packaged app, so they can send a message?

A few remarks:
This question is about providing the mailto functionality from within an iframe in the main window.
Adding a link (with the appropriate href) works fine, but requires user-interaction (clicking the link): ...
Using location.href = ... is prevented from the Content Security Policy (which can possibly be relaxed - I didn't look further into it).
Using window.top.location.href = ... results in the following error:
Can't open same-window link to "mailto:..."; try target="_blank".
The solution:
It worked for me using:
window.open("mailto:...");
For the sake of completeness, below is the source code of a sample extension that illustrates the above
manifest.json:
{
"manifest_version": 2,
"name": "Test App",
"version": "0.0",
"app": {
"background": {
"scripts": ["background.js"]
}
}
}
background.js:
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create("wrapper.html");
});
wrapper.html:
<!DOCTYPE html>
<html>
<head></head>
<body><iframe src="main.html"></iframe></body>
</html>
main.html:
<!DOCTYPE html>
<html>
<head><script type="text/javascript" src="main.js"></script></head>
<body>
<form id="frm1">
<input type="email" id="inp1" placeHolder="Recipient's e-mail"
required /><br />
<input type="text" id="inp2" placeHolder="Message's subject"
required /><br />
<input type="submit" id="btn1" value="Send e-mail" />
</form>
</body>
</html>
main.js:
window.addEventListener("DOMContentLoaded", function() {
var frm1 = document.getElementById("frm1");
var inp1 = document.getElementById("inp1");
var inp2 = document.getElementById("inp2");
var btn1 = document.getElementById("btn1");
frm1.addEventListener("submit", function(evt) {
evt.preventDefault();
});
btn1.addEventListener("click", function() {
var email = inp1.value;
var subject = encodeURIComponent(inp2.value);
var mailToStr = "mailto:" + email + "?Subject=" + subject);
console.log(mailToStr);
window.open(mailToStr);
});
});

Related

Results not displaying on extension window but showing on browser page [duplicate]

This seems to be the easiest thing to do, but it's just not working. In a normal browser the .html and .js files works perfectly, but in the Chrome/Firefox extension the onClick function is not performing what it's supposed to do.
.js file:
function hellYeah(text) {
document.getElementById("text-holder").innerHTML = text;
}
.html file:
<!doctype html>
<html>
<head>
<title>
Getting Started Extension's Popup
</title>
<script src="popup.js"></script>
</head>
<body>
<div id="text-holder">
ha
</div>
<br />
<a onClick=hellYeah("xxx")>
hyhy
</a>
</body>
</html>
So basically once the user clicks "hyhy", "ha" should change into "xxx". And again - it works perfectly in the browser but does not work in the extension. Do you know why? Just in case I'm attaching the manifest.json below as well.
manifest.json:
{
"name": "My First Extension",
"version": "1.0",
"manifest_version": 2,
"description": "The first extension that I made.",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"http://api.flickr.com/"
]
}
Chrome Extensions don't allow you to have inline JavaScript (documentation).
The same goes for Firefox WebExtensions (documentation).
You are going to have to do something similar to this:
Assign an ID to the link (<a onClick=hellYeah("xxx")> becomes <a id="link">), and use addEventListener to bind the event. Put the following in your popup.js file:
document.addEventListener('DOMContentLoaded', function() {
var link = document.getElementById('link');
// onClick's logic below:
link.addEventListener('click', function() {
hellYeah('xxx');
});
});
popup.js should be loaded as a separate script file:
<script src="popup.js"></script>
Reason
This does not work, because Chrome forbids any kind of inline code in extensions via Content Security Policy.
Inline JavaScript will not be executed. This restriction bans both inline <script> blocks and inline event handlers (e.g. <button onclick="...">).
How to detect
If this is indeed the problem, Chrome would produce the following error in the console:
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' chrome-extension-resource:". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution.
To access a popup's JavaScript console (which is useful for debug in general), right-click your extension's button and select "Inspect popup" from the context menu.
More information on debugging a popup is available here.
How to fix
One needs to remove all inline JavaScript. There is a guide in Chrome documentation.
Suppose the original looks like:
<a onclick="handler()">Click this</a> <!-- Bad -->
One needs to remove the onclick attribute and give the element a unique id:
<a id="click-this">Click this</a> <!-- Fixed -->
And then attach the listener from a script (which must be in a .js file, suppose popup.js):
// Pure JS:
document.addEventListener('DOMContentLoaded', function() {
document.getElementById("click-this").addEventListener("click", handler);
});
// The handler also must go in a .js file
function handler() {
/* ... */
}
Note the wrapping in a DOMContentLoaded event. This ensures that the element exists at the time of execution. Now add the script tag, for instance in the <head> of the document:
<script src="popup.js"></script>
Alternative if you're using jQuery:
// jQuery
$(document).ready(function() {
$("#click-this").click(handler);
});
Relaxing the policy
Q: The error mentions ways to allow inline code. I don't want to / can't change my code, how do I enable inline scripts?
A: Despite what the error says, you cannot enable inline script:
There is no mechanism for relaxing the restriction against executing inline JavaScript. In particular, setting a script policy that includes 'unsafe-inline' will have no effect.
Update: Since Chrome 46, it's possible to whitelist specific inline code blocks:
As of Chrome 46, inline scripts can be whitelisted by specifying the base64-encoded hash of the source code in the policy. This hash must be prefixed by the used hash algorithm (sha256, sha384 or sha512). See Hash usage for <script> elements for an example.
However, I do not readily see a reason to use this, and it will not enable inline attributes like onclick="code".
I had the same problem, and didn´t want to rewrite the code, so I wrote a function to modify the code and create the inline declarated events:
function compile(qSel){
var matches = [];
var match = null;
var c = 0;
var html = $(qSel).html();
var pattern = /(<(.*?)on([a-zA-Z]+)\s*=\s*('|")(.*)('|")(.*?))(>)/mg;
while (match = pattern.exec(html)) {
var arr = [];
for (i in match) {
if (!isNaN(i)) {
arr.push(match[i]);
}
}
matches.push(arr);
}
var items_with_events = [];
var compiledHtml = html;
for ( var i in matches ){
var item_with_event = {
custom_id : "my_app_identifier_"+i,
code : matches[i][5],
on : matches[i][3],
};
items_with_events.push(item_with_event);
compiledHtml = compiledHtml.replace(/(<(.*?)on([a-zA-Z]+)\s*=\s*('|")(.*)('|")(.*?))(>)/m, "<$2 custom_id='"+item_with_event.custom_id+"' $7 $8");
}
$(qSel).html(compiledHtml);
for ( var i in items_with_events ){
$("[custom_id='"+items_with_events[i].custom_id+"']").bind(items_with_events[i].on, function(){
eval(items_with_events[i].code);
});
}
}
$(document).ready(function(){
compile('#content');
})
This should remove all inline events from the selected node, and recreate them with jquery instead.
I decide to publish my example that I used in my case. I tried to replace content in div using a script. My problem was that Chrome did not recognized / did not run that script.
In more detail What I wanted to do: To click on a link, and that link to "read" an external html file, that it will be loaded in a div section.
I found out that by placing the script before the DIV with ID that
was called, the script did not work.
If the script was in another DIV, also it does not work
The script must be coded using document.addEventListener('DOMContentLoaded', function() as it was told
<body>
<a id=id_page href ="#loving" onclick="load_services()"> loving </a>
<script>
// This script MUST BE under the "ID" that is calling
// Do not transfer it to a differ DIV than the caller "ID"
document.getElementById("id_page").addEventListener("click", function(){
document.getElementById("mainbody").innerHTML = '<object data="Services.html" class="loving_css_edit"; ></object>'; });
</script>
</body>
<div id="mainbody" class="main_body">
"here is loaded the external html file when the loving link will
be clicked. "
</div>
As already mentioned, Chrome Extensions don't allow to have inline JavaScript due to security reasons so you can try this workaround as well.
HTML file
<!doctype html>
<html>
<head>
<title>
Getting Started Extension's Popup
</title>
<script src="popup.js"></script>
</head>
<body>
<div id="text-holder">ha</div><br />
<a class="clickableBtn">
hyhy
</a>
</body>
</html>
<!doctype html>
popup.js
window.onclick = function(event) {
var target = event.target ;
if(target.matches('.clickableBtn')) {
var clickedEle = document.activeElement.id ;
var ele = document.getElementById(clickedEle);
alert(ele.text);
}
}
Or if you are having a Jquery file included then
window.onclick = function(event) {
var target = event.target ;
if(target.matches('.clickableBtn')) {
alert($(target).text());
}
}

How to get input information on web app like msg box using google script

I need output on web app which key information in input box. using html and java script I deployed as web app. once key the information I should get pop up msg on web app that information. Please help me out of this problem.
I have created HTML and javascript, using that data is getting capture in google spreadsheet but that information I should get on web app like pop up msg
Key information get in pop up msg in web app only
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<h2>US P2P Standard Notes</h2>
<label>G-case #: </label><input type= "#" id="username">
<button id="btn">Pass</button> <form action="">
<p> </p>
</form>
<script>
document.getElementById("btn").addEventListener("click",doStuff);
function doStuff(){
var uname = document.getElementById("username").value;
google.script.run.userClicked(uname);
document.getElementById("username").value ="";
}
</script>
</body>
</html>
function doGet() {
return HtmlService.createHtmlOutputFromFile("page");
}
function userClicked(name){
var url = "docs.google.com/spreadsheets/d/…";
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Data");
ws.appendRow([name + "This is G-case#"]);
}
Try this:
I have it running as a dialog. I assume that you can take it from here and turn it into a Web App.
The html file was name aq4.html:
<html>
<head>
<base target="_top">
</head>
<body>
<h2>US P2P Standard Notes</h2>
<label>G-case #: </label><input type="text" id="username" />
<input type="button" id="btn" value="Pass" onClick="doStuff();" />
<script>
function doStuff(){
var uname=document.getElementById("username").value;
google.script.run
.withSuccessHandler(function(){
document.getElementById("username").value ="";
})
.userClicked(uname);
}
</script>
</body>
</html>
Since there was nothing in your form, I removed it. I also changed your button style to the input version. I added a withSuccessHandler to remove the entered text.
This is the google script:
function userClicked(name){
var ss=SpreadsheetApp.getActive();
var ws=ss.getSheetByName("Sheet1");
ws.appendRow([name + "This is G-case#"]);
return;
}
function showDialog() {
var userInterface=HtmlService.createHtmlOutputFromFile('aq4');
SpreadsheetApp.getUi().showModelessDialog(userInterface, "My Page")
}
Your saying something about a pop up but I haven't a clue as to what you're trying to say. Perhaps, you can elaborate on that a bit.
Oh and this is what the current dialog looks like:

Upload a file to google drive by an anonymous user

I use a Google Apps Script for that.
Code.gs
function doGet(e) {
return HtmlService.createHtmlOutputFromFile('form.html').setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function uploadFile(form) {
var folderId = "folder_id";
try {
var folder = DriveApp.getFolderById(folderId);
var blob = form.picToLoad;
var file = folder.createFile(blob);
return "File uploaded successfully " + file.getUrl();
} catch (error) {
Logger.log(error);
return error.toString();
}
}
form.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<h1 id="main-heading">Main Heading</h1>
<br/>
<div id="formDiv">
<form id="myForm">
<input name="picToLoad" type="file" /><br/>
<input type="button" value="Submit" onclick="picUploadJs(this.parentNode)" />
</form>
</div>
<div id="status" style="display: none">
Uploading. Please wait...
</div>
</body>
<script>
function picUploadJs(frmData) {
document.getElementById('status').style.display = 'inline';
google.script.run
.withSuccessHandler(updateOutput)
.uploadFile(frmData)
};
function updateOutput() {
var outputDiv = document.getElementById('status');
outputDiv.innerHTML = "The File was UPLOADED!";
}
</script>
</html>
It all works fine when I'm authenticated within my domain (I use G Suite).
However, if I'm logged into Google as another user (e.g. a normal Gmail user) or not logged at all, I still can access the page, but the script doesn't execute properly with the following error in the console:
Error
google.script.run.withSuccessHandler(...).processForm is not a function
at picUploadJs (VM84 userCodeAppPanel:9)
at HTMLInputElement.onclick (VM104 userCodeAppPanel:1)
No additional logs are shown in the Log at the level of Apps Script.
I have deployed the script as:
Execute the app as: Me (and authorised it)
Who can access the app: Anyone, even anonymous
So, I think all should work fine and anyone should be able to upload a file to my drive. Unfortunately that's not the case.
Again, this happens only when I access the script from outside of my domain.
Can anyone see what's wrong?
As it was suggested in the comments by Zig Mandel, making a copy of the script and running it solved the problem. This restarted the process of authorisation so perhaps there was something wrongly initiallised with the permissions.

HTML Service - Uncaught NetworkError: Form submission failed

I am working through the google example code for HTML Service: Communicate with Server Functions.
I can't get the sample code to work for "Forms'. Is there an error in the code or is it something in my browser config?
The code is -
code.gs
function doGet() {
return HtmlService.createHtmlOutputFromFile('index');
}
function processForm(formObject) {
var formBlob = formObject.myFile;
var driveFile = DriveApp.createFile(formBlob);
return driveFile.getUrl();
}
index.html
<script>
function updateUrl(url) {
var div = document.getElementById('output');
div.innerHTML = 'Got it!';
}
</script>
<form id="myForm">
<input name="myFile" type="file" />
<input type="button" value="Submit"
onclick="google.script.run
.withSuccessHandler(updateUrl)
.processForm(this.parentNode)" />
</form>
<div id="output"></div>
The error in the debug windows of my browser is
Uncaught NetworkError: Form submission failed.
Thanks in advance. Will Brown.
This error is not due to a code problem at all, rather it is an interaction between a browser plug-in and Google's authentication services. See Issue 3339 and Issue 4394.
If you're using the LastPass password manager plugin, it must be disabled.
PS: #user3531933 - if you add your own answer, just leave a comment for me, and I'll happily delete this and leave it to you.

Google Apps Script Templated HTML Page Not Loading

I am using a similar technique as was presented in this previously asked question:
how to get value entered in UI created with the new HtmlService
However, I am seeing inconsistent behavior between web browsers and even on mobile. My problem is in some browsers (Chrome) my 2nd html page is not displaying, however, in Firefox it does.
I went so far as to use the same code was was presented by Eric Koleda in the above link. This is what I have:
function doGet(e) {
var t = HtmlService.createTemplateFromFile('page1.html');
t.action = ScriptApp.getService().getUrl();
return t.evaluate();
}
function doPost(e) {
Logger.log("In doPost = ");
var t = HtmlService.createTemplateFromFile('page2.html');
t.name = e.parameter.name;
t.comment = e.parameter.comment;
t.screenshot = e.parameter.screenshot;
return t.evaluate();
}
page1.html
<html>
<body>
<h1>Feedback Form</h1>
<form action="<?= action ?>" method="post">
Name: <input type="text" name="name" /><br/>
Comment:<br/>
<textarea name="comment"></textarea><br/>
<input type="submit" value="Submit" />
</form>
</body>
</html>
page2.html
<html>
<body>
<h1>Thanks</h1>
<p>Thank you for your feedback.</p>
Name: <?= name ?><br/>
Comment: <?= comment ?><br/>
</body>
</html>
Eric's code from his link runs fine in Chrome for me, so I am not sure why I am having this issue. Also, based on Corey G's comment in the above link, I am wondering if I should be using Templated HTML and just use HTML Service only, but Templated HTML seems to be a good fit for my app. Could it be related to my sites or something else? Thanks for your time. Larry King