Return-Path: X-Original-To: apmail-cordova-commits-archive@www.apache.org Delivered-To: apmail-cordova-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id B2AE9EE1D for ; Wed, 23 Jan 2013 00:22:04 +0000 (UTC) Received: (qmail 95963 invoked by uid 500); 23 Jan 2013 00:22:04 -0000 Delivered-To: apmail-cordova-commits-archive@cordova.apache.org Received: (qmail 95950 invoked by uid 500); 23 Jan 2013 00:22:04 -0000 Mailing-List: contact commits-help@cordova.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: callback-dev@cordova.apache.org Delivered-To: mailing list commits@cordova.apache.org Received: (qmail 95810 invoked by uid 99); 23 Jan 2013 00:22:04 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 23 Jan 2013 00:22:04 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 13E3182410B; Wed, 23 Jan 2013 00:22:04 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: purplecabbage@apache.org To: commits@cordova.apache.org X-Mailer: ASF-Git Admin Mailer Subject: [13/16] wp7 commit: adding config.xml, and inappbrowser support Message-Id: <20130123002204.13E3182410B@tyr.zones.apache.org> Date: Wed, 23 Jan 2013 00:22:04 +0000 (UTC) adding config.xml, and inappbrowser support Project: http://git-wip-us.apache.org/repos/asf/cordova-wp7/repo Commit: http://git-wip-us.apache.org/repos/asf/cordova-wp7/commit/2a94c127 Tree: http://git-wip-us.apache.org/repos/asf/cordova-wp7/tree/2a94c127 Diff: http://git-wip-us.apache.org/repos/asf/cordova-wp7/diff/2a94c127 Branch: refs/heads/master Commit: 2a94c1279d929191857ebb2fefca00359823d3ea Parents: 8dac142 Author: Jesse MacFadyen Authored: Tue Jan 22 15:01:37 2013 -0800 Committer: Jesse MacFadyen Committed: Tue Jan 22 15:01:37 2013 -0800 ---------------------------------------------------------------------- example/CordovaExample.csproj | 1 + example/config.xml | 49 +++ framework/WPCordovaClassLib.csproj | 6 + templates/full/CordovaLib/WPCordovaClassLib.dll | Bin 0 -> 163840 bytes .../standalone/cordovalib/Commands/InAppBrowser.cs | 266 +++++++++++++++ templates/standalone/cordovalib/ConfigHandler.cs | 249 ++++++++++++++ .../standalone/cordovalib/CordovaView.xaml.cs | 38 ++- 7 files changed, 606 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-wp7/blob/2a94c127/example/CordovaExample.csproj ---------------------------------------------------------------------- diff --git a/example/CordovaExample.csproj b/example/CordovaExample.csproj index 0f2e49b..aba325c 100644 --- a/example/CordovaExample.csproj +++ b/example/CordovaExample.csproj @@ -96,6 +96,7 @@ + Designer http://git-wip-us.apache.org/repos/asf/cordova-wp7/blob/2a94c127/example/config.xml ---------------------------------------------------------------------- diff --git a/example/config.xml b/example/config.xml new file mode 100644 index 0000000..cac0c09 --- /dev/null +++ b/example/config.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + http://git-wip-us.apache.org/repos/asf/cordova-wp7/blob/2a94c127/framework/WPCordovaClassLib.csproj ---------------------------------------------------------------------- diff --git a/framework/WPCordovaClassLib.csproj b/framework/WPCordovaClassLib.csproj index 5b4ac1e..27cc0b1 100644 --- a/framework/WPCordovaClassLib.csproj +++ b/framework/WPCordovaClassLib.csproj @@ -128,6 +128,9 @@ CordovaLib\Commands\ImageExifHelper.cs + + CordovaLib\Commands\InAppBrowser.cs + CordovaLib\Commands\Media.cs @@ -140,6 +143,9 @@ CordovaLib\Commands\Notification.cs + + CordovaLib\ConfigHandler.cs + CordovaLib\CordovaCommandCall.cs http://git-wip-us.apache.org/repos/asf/cordova-wp7/blob/2a94c127/templates/full/CordovaLib/WPCordovaClassLib.dll ---------------------------------------------------------------------- diff --git a/templates/full/CordovaLib/WPCordovaClassLib.dll b/templates/full/CordovaLib/WPCordovaClassLib.dll new file mode 100644 index 0000000..73fd83a Binary files /dev/null and b/templates/full/CordovaLib/WPCordovaClassLib.dll differ http://git-wip-us.apache.org/repos/asf/cordova-wp7/blob/2a94c127/templates/standalone/cordovalib/Commands/InAppBrowser.cs ---------------------------------------------------------------------- diff --git a/templates/standalone/cordovalib/Commands/InAppBrowser.cs b/templates/standalone/cordovalib/Commands/InAppBrowser.cs new file mode 100644 index 0000000..7408421 --- /dev/null +++ b/templates/standalone/cordovalib/Commands/InAppBrowser.cs @@ -0,0 +1,266 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using Microsoft.Phone.Controls; +using System.Diagnostics; +using System.Runtime.Serialization; +using WPCordovaClassLib.Cordova; +using WPCordovaClassLib.Cordova.Commands; +using WPCordovaClassLib.Cordova.JSON; +using Microsoft.Phone.Shell; +using Microsoft.Phone.Tasks; + +namespace WPCordovaClassLib.Cordova.Commands +{ + [DataContract] + public class BrowserOptions + { + [DataMember] + public string url; + + [DataMember] + public bool isGeolocationEnabled; + } + + public class InAppBrowser : BaseCommand + { + + private static WebBrowser browser; + private static ApplicationBarIconButton backButton; + private static ApplicationBarIconButton fwdButton; + + public void open(string options) + { + string[] args = JSON.JsonHelper.Deserialize(options); + //BrowserOptions opts = JSON.JsonHelper.Deserialize(options); + string urlLoc = args[0]; + string target = args[1]; +/* + _self - opens in the Cordova WebView if url is in the white-list, else it opens in the InAppBrowser + _blank - always open in the InAppBrowser + _system - always open in the system web browser +*/ + switch (target) + { + case "_blank" : + ShowInAppBrowser(urlLoc); + break; + case "_self" : + ShowCordovaBrowser(urlLoc); + break; + case "_system" : + ShowSystemBrowser(urlLoc); + break; + } + + + } + + private void ShowCordovaBrowser(string url) + { + Uri loc = new Uri(url,UriKind.RelativeOrAbsolute); + Deployment.Current.Dispatcher.BeginInvoke(() => + { + PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame; + if (frame != null) + { + PhoneApplicationPage page = frame.Content as PhoneApplicationPage; + if (page != null) + { + CordovaView cView = page.FindName("CordovaView") as CordovaView; + if(cView != null) + { + WebBrowser br = cView.Browser; + br.Navigate(loc); + } + } + + } + }); + } + + private void ShowSystemBrowser(string url) + { + WebBrowserTask webBrowserTask = new WebBrowserTask(); + webBrowserTask.Uri = new Uri(url, UriKind.Absolute); + webBrowserTask.Show(); + } + + + // Display an inderminate progress indicator + private void ShowInAppBrowser(string url) + { + Uri loc = new Uri(url); + + Deployment.Current.Dispatcher.BeginInvoke(() => + { + if (browser != null) + { + //browser.IsGeolocationEnabled = opts.isGeolocationEnabled; + browser.Navigate(loc); + } + else + { + PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame; + if (frame != null) + { + PhoneApplicationPage page = frame.Content as PhoneApplicationPage; + WebBrowser br = (page.FindName("CordovaView") as CordovaView).Browser; + + if (page != null) + { + Grid grid = page.FindName("LayoutRoot") as Grid; + if (grid != null) + { + browser = new WebBrowser(); + browser.Navigate(loc); + + browser.LoadCompleted += new System.Windows.Navigation.LoadCompletedEventHandler(browser_LoadCompleted); + + browser.Navigating += new EventHandler(browser_Navigating); + browser.NavigationFailed += new System.Windows.Navigation.NavigationFailedEventHandler(browser_NavigationFailed); + browser.Navigated += new EventHandler(browser_Navigated); + browser.IsScriptEnabled = true; + //browser.IsGeolocationEnabled = opts.isGeolocationEnabled; + grid.Children.Add(browser); + } + + ApplicationBar bar = new ApplicationBar(); + bar.BackgroundColor = Colors.Gray; + bar.IsMenuEnabled = false; + + backButton = new ApplicationBarIconButton(); + backButton.Text = "Back"; + backButton.IconUri = new Uri("/Images/appbar.back.rest.png", UriKind.Relative); + backButton.Click += new EventHandler(backButton_Click); + //backButton.IsEnabled = false; + bar.Buttons.Add(backButton); + + + fwdButton = new ApplicationBarIconButton(); + fwdButton.Text = "Forward"; + fwdButton.IconUri = new Uri("/Images/appbar.next.rest.png", UriKind.Relative); + fwdButton.Click += new EventHandler(fwdButton_Click); + //fwdButton.IsEnabled = false; + bar.Buttons.Add(fwdButton); + + ApplicationBarIconButton closeBtn = new ApplicationBarIconButton(); + closeBtn.Text = "Close"; + closeBtn.IconUri = new Uri("/Images/appbar.close.rest.png", UriKind.Relative); + closeBtn.Click += new EventHandler(closeBtn_Click); + bar.Buttons.Add(closeBtn); + + page.ApplicationBar = bar; + } + + } + } + }); + } + + void browser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e) + { + + } + + void fwdButton_Click(object sender, EventArgs e) + { + if (browser != null) + { + try + { + //browser.GoForward(); + browser.InvokeScript("execScript", "history.forward();"); + } + catch(Exception) + { + + } + } + } + + void backButton_Click(object sender, EventArgs e) + { + if (browser != null) + { + try + { + //browser.GoBack(); + browser.InvokeScript("execScript", "history.back();"); + } + catch (Exception) + { + + } + } + } + + void closeBtn_Click(object sender, EventArgs e) + { + this.close(); + } + + + public void close(string options="") + { + if (browser != null) + { + Deployment.Current.Dispatcher.BeginInvoke(() => + { + PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame; + if (frame != null) + { + PhoneApplicationPage page = frame.Content as PhoneApplicationPage; + if (page != null) + { + Grid grid = page.FindName("LayoutRoot") as Grid; + if (grid != null) + { + grid.Children.Remove(browser); + } + page.ApplicationBar = null; + } + } + browser = null; + }); + } + } + + void browser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e) + { + //if (browser != null) + //{ + // backButton.IsEnabled = browser.CanGoBack; + // fwdButton.IsEnabled = browser.CanGoForward; + //} + string message = "{\"type\":\"locationChanged\", \"location\":\"" + e.Uri.AbsoluteUri + "\"}"; + PluginResult result = new PluginResult(PluginResult.Status.OK, message); + result.KeepCallback = true; + this.DispatchCommandResult(result); + } + + void browser_NavigationFailed(object sender, System.Windows.Navigation.NavigationFailedEventArgs e) + { + string message = "{\"type\":\"navigationError\",\"location\":\"" + e.Uri.AbsoluteUri + "\"}"; + PluginResult result = new PluginResult(PluginResult.Status.ERROR, message); + result.KeepCallback = true; + this.DispatchCommandResult(result); + } + + void browser_Navigating(object sender, NavigatingEventArgs e) + { + string message = "{\"type\":\"locationAboutToChange\",\"location\":\"" + e.Uri.AbsoluteUri + "\"}"; + PluginResult result = new PluginResult(PluginResult.Status.OK, message); + result.KeepCallback = true; + this.DispatchCommandResult(result); + } + + } +} http://git-wip-us.apache.org/repos/asf/cordova-wp7/blob/2a94c127/templates/standalone/cordovalib/ConfigHandler.cs ---------------------------------------------------------------------- diff --git a/templates/standalone/cordovalib/ConfigHandler.cs b/templates/standalone/cordovalib/ConfigHandler.cs new file mode 100644 index 0000000..d90e9b5 --- /dev/null +++ b/templates/standalone/cordovalib/ConfigHandler.cs @@ -0,0 +1,249 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Windows; +using System.Windows.Resources; +using System.Xml.Linq; + +namespace WPCordovaClassLib.CordovaLib +{ + class ConfigHandler + { + protected List AllowedPlugins; + protected List AllowedDomains; + protected Dictionary Preferences; + + protected bool AllowAllDomains = false; + protected bool AllowAllPlugins = false; + + public ConfigHandler() + { + AllowedPlugins = new List(); + AllowedDomains = new List(); + Preferences = new Dictionary(); + } + + public string GetPreference(string key) + { + return Preferences[key]; + } + +/* + - (BOOL)URLIsAllowed:(NSURL*)url +{ + if (self.expandedWhitelist == nil) { + return NO; + } + + if (self.allowAll) { + return YES; + } + + // iterate through settings ExternalHosts, check for equality + NSEnumerator* enumerator = [self.expandedWhitelist objectEnumerator]; + id regex = nil; + NSString* urlHost = [url host]; + + // if the url host IS found in the whitelist, load it in the app (however UIWebViewNavigationTypeOther kicks it out to Safari) + // if the url host IS NOT found in the whitelist, we do nothing + while (regex = [enumerator nextObject]) { + NSPredicate* regex_test = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex]; + + if ([regex_test evaluateWithObject:urlHost] == YES) { + // if it matches at least one rule, return + return YES; + } + } + + NSLog(@"%@", [self errorStringForURL:url]); + // if we got here, the url host is not in the white-list, do nothing + return NO; +}*/ + protected static string[] AllowedSchemes = {"http","https","ftp","ftps"}; + protected bool SchemeIsAllowed(string scheme) + { + return AllowedSchemes.Contains(scheme); + } + + protected string PathAndQuery(Uri uri) + { + string result = uri.LocalPath; + if (uri.Query.Length > 0) + { + result += uri.Query; + } + return result; + } + + protected void AddWhiteListEntry(string origin, bool allowSubdomains) + { + + if (origin == "*") + { + AllowAllDomains = true; + } + + if (AllowAllDomains) + { + return; + } + + string hostMatchingRegex = ""; + string hostName; + + try + { + + Uri uri = new Uri(origin.Replace("*", "replaced-text"), UriKind.Absolute); + + string tempHostName = uri.Host.Replace("replaced-text", "*"); + //if (uri.HostNameType == UriHostNameType.Dns){} + // starts with wildcard match - we make the first '.' optional (so '*.org.apache.cordova' will match 'org.apache.cordova') + if (tempHostName.StartsWith("*.")) + { //"(\\s{0}|*.)" + hostName = @"\w*.*" + tempHostName.Substring(2).Replace(".", @"\.").Replace("*", @"\w*"); + } + else + { + hostName = tempHostName.Replace(".", @"\.").Replace("*", @"\w*"); + } + + // "^https?://" + hostMatchingRegex = uri.Scheme + "://" + hostName + PathAndQuery(uri); + //Debug.WriteLine("Adding regex :: " + hostMatchingRegex); + AllowedDomains.Add(hostMatchingRegex); + + } + catch (Exception) + { + Debug.WriteLine("Invalid Whitelist entry (probably missing the protocol):: " + origin); + } + + } + + /** + + An access request is granted for a given URI if there exists an item inside the access-request list such that: + + - The URI's scheme component is the same as scheme; and + - if subdomains is false or if the URI's host component is not a domain name (as defined in [RFC1034]), the URI's host component is the same as host; or + - if subdomains is true, the URI's host component is either the same as host, or is a subdomain of host (as defined in [RFC1034]); and + - the URI's port component is the same as port. + + **/ + + public bool URLIsAllowed(string url) + { + // easy case first + if (AllowAllDomains ) + { + return true; + } + else + { + // start simple + Uri uri = new Uri(url,UriKind.RelativeOrAbsolute); + if (uri.IsAbsoluteUri) + { + if (this.SchemeIsAllowed(uri.Scheme)) + { + // additional test because our pattern will always have a trailing '/' + string matchUrl = url; + if (PathAndQuery(uri) == "/") + { + matchUrl = url + "/"; + } + foreach (string pattern in AllowedDomains) + { + if (Regex.IsMatch(matchUrl, pattern)) + { + // make sure it is at the start, and not part of the query string + // special case :: http://some.other.domain/page.html?x=1&g=http://build.apache.org/ + if ( Regex.IsMatch(uri.Scheme + "://" + uri.Host + "/", pattern) || + (!Regex.IsMatch(PathAndQuery(uri), pattern))) + { + return true; + } + } + } + } + } + else + { + return true; + } + } + return false; + } + + public bool IsPluginAllowed(string key) + { + return AllowAllPlugins || AllowedPlugins.Contains(key); + } + + + public void LoadAppPackageConfig() + { + StreamResourceInfo streamInfo = Application.GetResourceStream(new Uri("config.xml", UriKind.Relative)); + + if (streamInfo != null) + { + StreamReader sr = new StreamReader(streamInfo.Stream); + //This will Read Keys Collection for the xml file + XDocument document = XDocument.Parse(sr.ReadToEnd()); + + var plugins = from results in document.Descendants("plugin") + select new { name = (string)results.Attribute("name") }; + + + foreach (var plugin in plugins) + { + Debug.WriteLine("plugin " + plugin.name); + if (plugin.name == "*") + { + AllowAllPlugins = true; + break; + } + else + { + AllowedPlugins.Add(plugin.name); + } + } + + var preferences = from results in document.Descendants("preference") + select new + { + name = (string)results.Attribute("name"), + value = (string)results.Attribute("value") + }; + + foreach (var pref in preferences) + { + Debug.WriteLine("pref" + pref.name + ", " + pref.value); + } + + var accessList = from results in document.Descendants("access") + select new + { + origin = (string)results.Attribute("origin"), + subdomains = (string)results.Attribute("subdomains") == "true" + }; + + foreach (var accessElem in accessList) + { + AddWhiteListEntry(accessElem.origin, accessElem.subdomains); + } + } + else + { + // no config.xml, allow all + AllowAllDomains = true; + AllowAllPlugins = true; + } + } + } +} http://git-wip-us.apache.org/repos/asf/cordova-wp7/blob/2a94c127/templates/standalone/cordovalib/CordovaView.xaml.cs ---------------------------------------------------------------------- diff --git a/templates/standalone/cordovalib/CordovaView.xaml.cs b/templates/standalone/cordovalib/CordovaView.xaml.cs index 27f286f..135d3d9 100644 --- a/templates/standalone/cordovalib/CordovaView.xaml.cs +++ b/templates/standalone/cordovalib/CordovaView.xaml.cs @@ -38,6 +38,7 @@ using WPCordovaClassLib.Cordova; using System.Threading; using Microsoft.Phone.Shell; using WPCordovaClassLib.Cordova.JSON; +using WPCordovaClassLib.CordovaLib; @@ -77,6 +78,8 @@ namespace WPCordovaClassLib protected DOMStorageHelper domStorageHelper; protected OrientationHelper orientationHelper; + private ConfigHandler configHandler; + public System.Windows.Controls.Grid _LayoutRoot { get @@ -146,9 +149,12 @@ namespace WPCordovaClassLib } + configHandler = new ConfigHandler(); + configHandler.LoadAppPackageConfig(); + // initializes native execution logic - this.nativeExecution = new NativeExecution(ref this.CordovaBrowser); - this.bmHelper = new BrowserMouseHelper(ref this.CordovaBrowser); + nativeExecution = new NativeExecution(ref this.CordovaBrowser); + bmHelper = new BrowserMouseHelper(ref this.CordovaBrowser); } @@ -378,6 +384,12 @@ namespace WPCordovaClassLib void GapBrowser_Navigating(object sender, NavigatingEventArgs e) { + if (!configHandler.URLIsAllowed(e.Uri.ToString())) + { + e.Cancel = true; + return; + } + this.PageDidChange = true; // Debug.WriteLine("GapBrowser_Navigating to :: " + e.Uri.ToString()); this.nativeExecution.ResetAllCommands(); @@ -429,7 +441,27 @@ namespace WPCordovaClassLib } else { - this.nativeExecution.ProcessCommand(commandCallParams); + if (configHandler.IsPluginAllowed(commandCallParams.Service)) + { + nativeExecution.ProcessCommand(commandCallParams); + } + else + { + Debug.WriteLine("Error::Plugin not allowed in config.xml. " + commandCallParams.Service); + } + } + } + + public void LoadPage(string url) + { + try + { + Uri newLoc = new Uri(url,UriKind.RelativeOrAbsolute); + CordovaBrowser.Navigate(newLoc); + } + catch(Exception) + { + } }