Xamarin.Forms UWP - Setting WebView.Source as HtmlWebViewSource Processes Scripts Twice - xamarin

In Xamarin.Forms UWP, if I have a WebView whose Source property is set to type UrlWebViewSource, it works as expected. BUT, if the Source property is set to type HtmlWebViewSource where the Html property is set as a string, then any <script>s in the HTML are processed twice.
I've set up a brand new Xamarin.Forms project, where the Content of the MainPage is set to a WebView in the code-behind. I've set it up so that I can easily switch out between setting the WebView.Source to UrlWebViewSource or HtmlWebViewSource:
public MainPage()
{
InitializeComponent();
var urlSource = new UrlWebViewSource { Url = "https://htmlpreview.github.io/?https://github.com/joelbyrd/external-resources/blob/master/storefulfillment/Printing/Packing/GNC-Pick.htm" };
var htmlStringSource = new HtmlWebViewSource
{
Html = #"<!doctype html><html lang='en'><head><meta charset='utf-8'><meta http-equiv='x-ua-compatible' content='ie=edge'><meta name='viewport' content='width=device-width, initial-scale=1'><title>Test</title></head><body><div id='log'></div><p>some text</p><script>document.getElementById('log').innerHTML += `<p>Testing</p>`;</script></body></html>"
};
var webView = new WebView
{
Margin = new Thickness(0, 20, 0, 0),
Source = urlSource, // displays as expected
//Source = htmlStringSource // <script>'s are processed twice, appending "Testing" to the body twice
};
Content = webView;
}
It's a problem that the scripts are executed twice, probably for several reasons, but for one, if I'm appending content to the body with JavaScript, then it will be appended twice - not what I want. Here's the test HTML code I'm using:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Test</title>
</head>
<body>
<div id="log"></div>
<p>some text</p>
<script>
document.getElementById('log').innerHTML += `<p>Testing</p>`;
</script>
</body>
</html>
When I set the WebView.Source to type UrlWebViewSource, and set the Url property to "https://htmlpreview.github.io/?https://github.com/joelbyrd/external-resources/blob/master/storefulfillment/Printing/Packing/GNC-Pick.htm", I get the following expected output:
BUT When I set the WebView.Source to type HtmlWebViewSource, with the Html property set to the string above (spaces taken out and properly escaped), I get the following:
You can see that "Testing" has been added to the body twice. This only happens in UWP, so I suspect it's a bug. Can anyone tell me what's going on here, and if there's a workaround?

Related

Flutter web the type File is defined in multiple classes

I have a Flutter web project, where I would like to select a picture from the device and upload it to Firebase Storage. I found this solution:
Future<void> uploadToFirebase(File imageFile) async { //I get the error here
final filePath = 'images/${DateTime.now()}.png';
StorageTaskSnapshot snapshot = await FirebaseStorage.instance
.ref()
.child(filePath)
.putFile(imageFile)
.onComplete;
print("UploadToFirebase");
if (snapshot.error == null) {
final String downloadUrl = await snapshot.ref.getDownloadURL();
await Firestore.instance
.collection("images")
.add({"url": downloadUrl, "name": "${DateTime.now()}.png"});
} else {
print('Error from image repo ${snapshot.error.toString()}');
throw ('This file is not an image');
}
}
void uploadImage() async {
InputElement uploadInput = FileUploadInputElement();
uploadInput.click();
uploadInput.onChange.listen(
(changeEvent) {
final file = uploadInput.files.first;
final reader = FileReader();
reader.readAsDataUrl(file);
reader.onLoadEnd.listen(
(loadEndEvent) async {
print("Calling uploadToFirebase");
uploadToFirebase(file);
print("Done");
},
);
},
);
}
But this code has the following error in the line with the comment:
The name 'File' is defined in the libraries 'dart:html' and 'dart:io'.
Try using 'as prefix' for one of the import directives, or hiding the name from all but one of the imports.dartambiguous_import
After this I added a hide in my import dart html:
import 'dart:html' hide File;
However this resulted in another error in the uploadImage function, where I call uploadToFirebase(file):
The argument type 'File (where File is defined in C:\Users\Asus\Documents\flutter\bin\cache\pkg\sky_engine\lib\html\html_dart2js.dart)' can't be assigned to the parameter type 'File (where File is defined in C:\Users\Asus\Documents\flutter\bin\cache\pkg\sky_engine\lib\io\file.dart)'.dartargument_type_not_assignable
html_dart2js.dart(15975, 7): File is defined in C:\Users\Asus\Documents\flutter\bin\cache\pkg\sky_engine\lib\html\html_dart2js.dart
file.dart(241, 16): File is defined in C:\Users\Asus\Documents\flutter\bin\cache\pkg\sky_engine\lib\io\file.dart
My index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="12 órás eventek kezelésére">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="event_maker">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>event_maker</title>
<link rel="manifest" href="manifest.json">
</head>
<body>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/7.19.0/firebase-app.js"></script>
<!-- TODO: Add SDKs for Firebase products that you want to use
https://firebase.google.com/docs/web/setup#available-libraries -->
<script src="https://www.gstatic.com/firebasejs/7.19.0/firebase-firestore.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.19.0/firebase-analytics.js"></script>
</script>
<script>
// Your web app's Firebase configuration
var firebaseConfig = {
...
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
firebase.analytics();
</script>
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('flutter_service_worker.js');
});
}
</script>
<script src="main.dart.js" type="application/javascript"></script>
</body>
</html>
Any other ideas on how to solve this? Or is there a better way to upload a file with a web app?
I'm a beginner to Flutter, so sorry, if it is a dumb question. Thanks for your help in advance!
I think you're missing a for Firebase storage. Try adding the following line:
<script src="https://www.gstatic.com/firebasejs/7.19.0/firebase-storage.js"></script>
dart:html 'File' extends Blob <-use this if uploading from web
dart:io 'File' extends FileSystemEntity <-use this if uploading from a platform with file access
in your case
use .putBlob(imageFile)
instead of .putFile(imageFile)

Get Raw Html From the controller Spring thymeleaf

I have a controller that create model attribute and passes to the view "partial.html" to generate output
partial.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Home page</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body>
<p>
<span th:text="'Today is: ' + ${message}"></span>
</p>
</body>
</html>
and inside a controller method
model.addAttribute("message", search);
How to do I get Htlm Output to a string inside controller method?
like this
String htmlOutput="from partial.html";
Let's say you have a HTML file with two variable name and todayDate.
You want to process it and want to store it in a string / database / AWS S3.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
</head>
<body>
<p>Hello</p>
<p th:text="${name}"></p>
<p th:text="${todayDate}"></p>
</body>
</html>
Your HTML file location is src/main/resources/templates/home.html
By using the below function you can get the final processed HTML as:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<p>Hello</p>
<p>Manoj</p>
<p>30 November 2019</p>
</body>
</html>
import org.thymeleaf.context.Context;
#GetMapping("/")
public void process() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("templates/");
templateResolver.setCacheable(false);
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML");
// https://github.com/thymeleaf/thymeleaf/issues/606
templateResolver.setForceTemplateMode(true);
templateEngine.setTemplateResolver(templateResolver);
Context ctx = new Context();
SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
Calendar cal = Calendar.getInstance();
ctx.setVariable("todayDate", dateFormat.format(cal.getTime()));
ctx.setVariable("name", "Manoj");
final String result = templateEngine.process("home", ctx);
System.out.println("result:" + result);
}
If you're using the usual Spring MVC approach, as Joanna says you're doing things in the wrong order. The Controller creates the model and specifies the view, and then after that the view is rendered by the Thymeleaf template that uses the model.
If, on the other hand, you're trying to render Thymeleaf templates yourself (rather than sending them to the user's browser directly, maybe for use in HTML email or to store prerendered pages in a database or something), then you'd need to create your own Thymeleaf Template Engine to use. Refer to the "Creating and configuring the Template Engine" section of the documentation for details. You can create your own Engine, and then use its process method to get the result of the template to put into a variable for further use.
You may looking for this, getting directly the result HTML, just ignore the email part of the post.
Then, you can create this:
final Context ctx = new Context(locale);
ctx.setVariable("name", recipientName);
ctx.setVariable("subscriptionDate", new Date());
ctx.setVariable("hobbies", Arrays.asList("Cinema", "Sports", "Music"));
ctx.setVariable("imageResourceName", imageResourceName);
// so that we can reference it from HTML
final String htmlContent = this.templateEngine.process("html/email-inlineimage.html", ctx);
So you have the htmlContext of rendered thymeleaf template (with vars)
Once the control goes out to view processor (JSP/Thymeleaf etc), it will not be coming back to controller. You will be able to get the raw html response in a customFilter, but not in the Controller.

HtmlAgility - dealing with in-between html

Lets say I have a website "example.com". I call the following line
// Pass html content of the site.com to a string
string htmlCode = client.DownloadString("http://example.com");
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(WebUtility.HtmlDecode(htmlCode));
For some websites I have the exact html I want. But some sites return a html consist of forms, or with an empty body and some kind of scripts.
Example for script one:
<!DOCTYPE html>
<html><head>
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="-1">
<meta http-equiv="CacheControl" content="no-cache">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="shortcut icon" href="data:;base64,iVBORw0KGgo=">
<script>
(function(){
var securemsg;
var dosl7_common;
window["bobcmn"] = "111110111110102000000022000000052000000002a4b927ad200000096300000000300000000300000006/TSPD/300000008TSPD_101300000005https200000000200000000";
window.hQv=!!window.hQv;try{(function(){try{var __,i_,j_=1,O_=1,z_=1,s_=1,S_=1,Ji=1,li=1,oi=1,si=1;for(var Si=0;Si<i_;++Si)j_+=2,O_+=2,z_+=2,s_+=2,S_+=2,Ji+=2,li+=2,oi+=2,si+=3;__=j_+O_+z_+s_+S_+Ji+li+oi+si;window.SL===__&&(window.SL=++__)}catch(JI){window.SL=__}var OI=!0;function SI(_){_&&(OI=!1,document.cookie="brav=ad");return OI}function _j(){}SI(window[_j.name]===_j);SI("function"!==typeof ie9rgb4);SI(/\x3c/.test(function(){return"\x3c"})&!/x3d/.test(function(){return"'x3'+'d';"}));
var ij=window.attachEvent||/mobi/i.test(window["\x6e\x61vi\x67a\x74\x6f\x72"]["\x75\x73e\x72A\x67\x65\x6et"]),Ij=+new Date+6E5,Jj,lj,Oj=setTimeout,zj=ij?3E4:6E3;function Zj(){if(!document.querySelector)return!0;var _=+new Date,l=_>Ij;if(l)return SI(!1);l=lj&&Jj+zj<_;l=SI(l);Jj=_;lj||(lj=!0,Oj(function(){lj=!1},1));return l}Zj();var sj=[17795081,27611931586,1558153217];
function Sj(_){_="string"===typeof _?_:_.toString(36);var l=window[_];if(!l.toString)return;var O=""+l;window[_]=function(_,O){lj=!1;return l(_,O)};window[_].toString=function(){return O}}for(var si=0;si<sj.length;++si)Sj(sj[si]);SI(!1!==window.hQv);
(function iJ(){if(!Zj())return;var l=!1;function O(l){for(var z=0;l--;)z+=Z(document.documentElement,null);return z}function Z(l,z){var O="vi";z=z||new s;return o_(l,function(l){l.setAttribute("data-"+O,z._s());return Z(l,z)},null)}function s(){this.Lz=1;this.Jz=0;this.il=this.Lz;this.c=null;this._s=function(){this.c=this.Jz+this.il;if(!isFinite(this.c))return this.reset(),this._s();this.Jz=this.il;this.il=this.c;this.c=null;return this.il};this.reset=function(){this.Lz++;this.Jz=0;this.il=this.Lz}}
var S=!1;function z(l,z){if(!Zj())return;var O=document.createElement(l);z=z||document.body;z.appendChild(O);O&&O.style&&(O.style.display="none");Zj()}function J_(z,O){if(!Zj())return;O=O||z;var Z="|";function s(l){l=l.split(Z);var z=[];for(var O=0;O<l.length;++O){var S="",I_=l[O].split(",");for(var J_=0;J_<I_.length;++J_)S+=I_[J_][J_];z.push(S)}return z}var J_=0,o_="datalist,details,embed,figure,hrimg,strong,article,formaddress|audio,blockquote,area,source,input|canvas,form,link,tbase,option,details,article";
o_.split(Z);o_=s(o_);o_=new RegExp(o_.join(Z),"g");while(o_.exec(z))o_=new RegExp((""+new Date)[8],"g"),l&&(S=Zj()),++J_;return Zj()?O(J_&&1):void 0}function o_(l,O,Z){if(!Zj())return;(Z=Z||S)&&z("div",l);l=l.children;var s=0;for(var J_ in l){Z=l[J_];try{Z instanceof HTMLElement&&(O(Z),++s)}catch(o_){}}return Zj()?s:void 0}J_(iJ,O);Zj()})();var IJ=82;window.oz={zz:"0820fdace1017800ebdf62cbc35cbeca5d8b435652ee3d253bb2e03195f77060a34ecc0424666f18abca1759ee2fa744800dfad86d4269514242d4fceed9d9c70b54e28c9b8c3fbf20a4971c6cf7cf3e60654d34ea06fc0747a30d8d8807f58873200a982d1d45fb8ed817474e167ab24b6ec97b833fc5141c0ef332e22dc753"};function I(_){return 396>_}
function J(_){var l=arguments.length,O=[];for(var Z=1;Z<l;++Z)O.push(arguments[Z]-_);return String.fromCharCode.apply(String,O)}function L(_,l){_+=l;return _.toString(36)}(function JJ(l){return l?0:JJ(l)*JJ(l)})(OI);})();}catch(x){document.cookie='brav=oex'+x;}finally{ie9rgb4=void(0);};function ie9rgb4(a,b){return a>>b>>0};
})();
</script>
<script type="text/javascript" src="/TSPD/084fc6184bab20009b43f88181dfc281050b986fbf5cd6e7067eeb760574cf33392dd93acd61a34b?type=8"></script>
<script>
(function(){
var securemsg;
var dosl7_common;
window["blobfp"] = "1111111110112000003e82ff5ac71e30000004a91d2b9750979230f005996dcd100001c20be2e63e7a47a6a80ea7aac3f26b85092554439d9300000020http://re.security.f5aas.com/re/";
})();
</script>
<script type="text/javascript" src="/TSPD/084fc6184bab20009b43f88181dfc281050b986fbf5cd6e7067eeb760574cf33392dd93acd61a34b?type=11"></script>
<noscript>Please enable JavaScript to view the page content.</noscript>
</head><body>
</body></html>
How can I deal with this "ecnryption" system and get the final html I want that is on "example.com", that you see when you check the source on the browser?
Do you have a F5 Server in front of your webserver? Because I have a similar situation, where that Javascript gets injected in my code (which leads to problems).
This was due to page loading with an AJAX call. It could be solved by using PhantomJS web driver.

Callback can not be obtained from the Webview in cocos2d-x(v3.10)

I am in learning and trying to app development in the cocos2d-x.
How do I implement a callback from the WebView is?
I have been using the cocos2d-x v3.10.
test_index.html
<head>
<meta charset="utf-8">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="format-detection" content="telephone=no">
<meta name="robots" content="none" />
<meta name="viewport" content="width=device-width,height=device-height">
</head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
<script src="js/sp.js"></script>
<ul class="slideshow">
<li>
<a href="#" onclick='sp.call("test1")'>
<img src="img/test1.jpg" width="100%"/>
</li>
</ul>
<style>
.bx-default-pager {
display: none;
}
.bx-viewport {
border: none !important;
}
</style>
</body>
sp.js
(function(){
var sp = {};
var isAndroid = navigator.userAgent.match(/Android/);
var isIOs = navigator.userAgent.match(/iPhone/) || navigator.userAgent.match(/iPod/) || navigator.userAgent.match(/iPad/);
sp.call = function(text){
if(isAndroid){
window.Cocos2dx.call(text);
} else if (isIOs) {
window.location.href = "cocos2dx:" + text;
} else {
window.location.href = text;
}
};
window.sp = sp;
})();
test.cpp
bool TestScene::init()
{
auto webView = cocos2d::experimental::ui::WebView::create();
webView->loadURL("http://test/test_index.html");
webview->setONJSCallback(CC_CALLBACK_1(TestScene::callbackFromJS,this));
webView->setAnchorPoint(Point(0,0));
webView->setPosition(Point(0,150));
webView->setContentSize(Size(WINSIZE.width,WINSIZE.height-150));
this->addChild(webView,1);
}
void TestScene::callbackFromJS(cocos2d::experimental::ui::WebView* webview, std::string* url)
{
log("call this method");
}
Wrong use of the "setOnJSCallback"?
The JSCallback works like this.
It checks everytime a url is loaded, if that contains the JavascriptInterfaceScheme name if any defined for the webview. If it is there it wont load that url instead call the JSCallBack function. If not it will normally load the url. In #Plusmiles case you have not set any JavascriptInterfaceScheme name.
Below is the code which worked for me
cocos2d::experimental::ui::WebView * Practice::_webView;
_webView = experimental::ui::WebView::create();
_webView->setContentSize(Size(1200, 1200));
_webView->setPosition(Vec2(900,768));
_webView->setJavascriptInterfaceScheme("cocos2dx");
_webView->loadURL("https://example.com");
_webView->setOnJSCallback(&Practice::callbackFromJS);
this->addChild(_webView, 1);
void Practice::callbackFromJS(cocos2d::experimental::ui::WebView* webview, const std::string &answer) {
std::string response = answer;
}
to call from html file you need to set
window.location.href = "cocos2dx:" + text;
where cocos2dx is the JavascriptInterfaceScheme name set in the webview. Whenever it find this name in the url it will call the function with webview object as first parameter and url as second. So you need to avoid this webview in url if you want the url to load rather than calling JSCallback function.
I hope this answer will help.
So you can only access "https" but not "http"? This is because of new IOS 9 restrictions. You need to add the following to your plist in order to be able to access http
<key>NSAppTransportSecurity</key>
<dict>
<!--Include to allow all connection -->
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>

Cannot Load the HTML through HtmlAgilityPack

I try to parse HTML using HtmlAgilityPack using simple doc.load method by passing the URL, but it comes with the following result how can I resolve this issue?
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta http-equiv="Content-Script-Type" content="text/javascript">
<script type="text/javascript">
function setCookie(c_name, value, expiredays) {
var exdate = new Date();
exdate.setDate(exdate.getDate()+expiredays);
document.cookie = c_name + "=" + escape(value) + ((expiredays==null) ? "" : ";
expires=" + exdate.toGMTString()) + ";path=/"; }
function getHostUri()
{ var loc = document.location; return loc.toString(); }
setCookie('YPF8827340282Jdskjhfiw_928937459182JAX666', '202.142.170.42', 10);
setCookie('DOAReferrer', document.referrer, 10); location.href = getHostUri();
</script>
</head>
<body>
<noscript>This site requires JavaScript and Cookies to be enabled. Please change your browser settings or upgrade your browser.</noscript>
</body></html>
This site requires JavaScript and Cookies to be enabled.
Please change your browser settings or upgrade your
browser.
This Message says it all, the side needs javascript to be loaded, and HtmlAgilityPack is no JavascriptEngine!
The Load Method of the HtmlDocument can not interpret and execute Javascript-Code it´s just a simple "Download"-Function for static HTML-Sites.
What you could try to do is, with Firebug (or something else) check which HttpRequest are made to get the content, and this Requests you have to recreate in C# to get the HTML you want!
Here are some similar Questions:
Running Scripts in HtmlAgilityPack
C# - Get JavaScript variable value using HTMLAgilityPack
Calling javascript function from HtmlAgilityPack

Resources