Actually, this issue was discovered while developing Cordova app, that's why I can't switch to the new WebView but have to stay with Cordova's extended WebBrowser. But I have created a simple test without Corodova to verify that the issue is with WebBrowser itself and not Cordova.
I have the following code for my test page:
<!DOCTYPE html>
<html>
<head>
<title>Mobile sandbox</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, height=device-height, user-scalable=no">
<script type="text/javascript" src="libs/jquery/jquery.min.js"></script>
</head>
<body>
<style>
@-ms-viewport{width:device-width; height:device-height;} /* we need this because according to this article http://trentwalton.com/2013/01/16/windows-phone-8-viewport-fix/ meta viewport does not work across WP devices but -ms-viewport works */
html, body {
margin: 0;
padding: 0;
overflow: hidden; /* crop away everything, don't show scrollbars */
}
#allContainer {
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
background-color: green;
position: absolute; /* to make 100% work */
border: 5px solid red; /* to see where it gets cropped */
}
</style>
<div id="allContainer">
<div>
<button id="measureDimensions">Measure dimensions</button>
</div>
</div>
<script>
$(function () {
$("#measureDimensions").click(function () {
var dims =
"Screen: " +
screen.width + "*" + screen.height + "\r\n" +
"Window: " +
window.innerWidth + "*" + window.innerHeight + "\r\n" +
"Html: " +
$("html").outerWidth() + "*" + $("html").outerHeight() + "\r\n" +
"Body: " +
$("body").outerWidth() + "*" + $("body").outerHeight() + "\r\n" +
"#allContainer: " +
$("#allContainer").outerWidth() + "*" + $("#allContainer").outerHeight();
alert(dims);
});
});
</script>
</body>
</html>
Also, jQuery is needed. I load it into WebBrowser control. Here is my MainPage.xaml:
<phone:PhoneApplicationPage
x:Class="PhoneBrowserSL.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="PortraitOrLandscape"
xmlns:phonec="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
shell:SystemTray.IsVisible="True" Loaded="PhoneApplicationPage_Loaded">
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot">
<phonec:WebBrowser
x:Name="MiniBrowser"
IsScriptEnabled="True"
Foreground="White"
Background="Black"
Navigated="MiniBrowser_Navigated"
Loaded="MiniBrowser_Loaded"
Unloaded="MiniBrowser_Unloaded"
ScriptNotify="MiniBrowser_ScriptNotify"
LoadCompleted="MiniBrowser_LoadCompleted"
Navigating="MiniBrowser_Navigating"
NavigationFailed="MiniBrowser_NavigationFailed"
IsGeolocationEnabled="True">
</phonec:WebBrowser>
</Grid>
</phone:PhoneApplicationPage>
And my MainPage.xaml.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using PhoneBrowserSL.Resources;
using System.Diagnostics;
using Microsoft.Phone.Info;
namespace PhoneBrowserSL
{
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
}
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
// hack from http://developer.nokia.com/community/wiki/How_to_set_WebBrowser_control_viewport_dimensions_on_Windows_Phone
string width = MiniBrowser.ActualWidth.ToString();
string height = MiniBrowser.ActualHeight.ToString();
string screenWidth = Application.Current.Host.Content.ActualWidth.ToString();
string screenHeight = Application.Current.Host.Content.ActualHeight.ToString();
MessageBox.Show("Screen:"+
screenWidth + "*" + screenHeight + Environment.NewLine +
"WebBrowser:" + width + "*" + height);
MiniBrowser.Navigate(new Uri(String.Format("www/index.html#width={0}&height={1}", width, height),
UriKind.Relative));
}
void MiniBrowser_Loaded(object sender, RoutedEventArgs e)
{
Debug.WriteLine("MiniBrowser_Loaded");
}
void MiniBrowser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
Debug.WriteLine("MiniBrowser_LoadCompleted :: " + e.Uri.ToString());
}
void MiniBrowser_Navigating(object sender, NavigatingEventArgs e)
{
Debug.WriteLine("MiniBrowser_Navigating :: " + e.Uri.ToString());
}
/*
* This method does the work of routing commands
* NotifyEventArgs.Value contains a string passed from JS
* If the command already exists in our map, we will just attempt to call the method(action) specified, and pass the args along
* Otherwise, we create a new instance of the command, add it to the map, and call it ...
* This method may also receive JS error messages caught by window.onerror, in any case where the commandStr does not appear to be a valid command
* it is simply output to the debugger output, and the method returns.
*
**/
void MiniBrowser_ScriptNotify(object sender, NotifyEventArgs e)
{
string commandStr = e.Value;
string commandName = commandStr.Split('/').FirstOrDefault();
Debug.WriteLine("MiniBrowser_ScriptNotify :: " + commandName);
}
private void MiniBrowser_Unloaded(object sender, RoutedEventArgs e)
{
Debug.WriteLine("MiniBrowser_Unloaded");
}
private void MiniBrowser_NavigationFailed(object sender, System.Windows.Navigation.NavigationFailedEventArgs e)
{
Debug.WriteLine("MiniBrowser_NavigationFailed :: " + e.Uri.ToString());
}
private void MiniBrowser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
Debug.WriteLine("MiniBrowser_Navigated :: " + e.Uri.ToString());
}
}
}
Nothing special, just automatically generated template with some custom debug code to see screen dimensions.
I'm aware that if I use device-height & width, I won't get pixel-accurate representation, as it is explained in this article: http://developer.nokia.com/community/wiki/How_to_set_WebBrowser_control_viewport_dimensions_on_Windows_Phone
I tried to implement the "hash hack" as described there, but it requires full page reload on orientation changes because WP does not support dynamic changes to vieport dimensions. Thus for now I stay with device-height & width, and it seems working fine on WP8.
On both WP8 and WP8.1 emulators, screen resolution is 480*800 and WebBrowser size is 480*768 (height is smaller because of status bar), and it is always correctly reported from C# code.
But here comes the main issue.
On WP8 emulator my test gives me the following output from alert:
Vertical:
Screen: 480*800
Window: 480*768
Html: 480*768
Body: 480*0 (0 is OK here - because #allcontainer is absolute positioned and flows out of body)
#allContainer: 480*768
Horizontal:
Screen: 480*800
Window: 728*480 (728 is OK here - because status bar in horizontal view gets wider)
Html: 728*480
Body: 728*0
#allContainer: 728*480
Everything seems just fine, I see my entire green box with the red border.
But on WP8.1 everything is "borken":
Vertical:
Screen: 480*800
Window: 480*768
Html: 480*800 (why did you get taller than window???)
Body: 480*0
#allContainer: 480*800
Now my green box is taller and I see no bottom border any more.
Horizontal:
Screen: 480*800
Window: 800*527 (where did you get 527 from???? and why do you ignore the status bar for width???)
Html: 800*480 (why don't your size match the actually available screen size 728*480 at all????)
Body: 800*0
#allContainer: 800*480
Now my green box is shorter (because WP set viewport window height to 527) and I see nasty white space below it.
Is the device-width/height behavior on WP8.1 by design or it's a bug which should be reported (and where?). How do I create a layout which requires a div with full page height if the browser renders it wrong?
I'm using WebView on Windows Store App 8.1 and Windows Phone 8.1 and I think the problem is the same. What is working fine on the Windows Store App, the result on Windows Phone 8.1 drives me crazy.
If you change the Width of your browser, as changing the orientation from portrait to landscape, on the phone the page is not re-rendered but scaled to the new size. This is probably the cause of your problem.
But not only this. Even the first display at a given width is not originally rendered to that size. It is rendered for an unknown size x and scaled to fit in your browser.
This scaling performs a css definition with "em" ad absurbum. Text with "1em" should have about the same size on different displays. But the initial zoom for fitting makes it impossible. And Microsoft forgot to make the zoom adjustable.
It's a matter of the viewport. In my opinion, the default viewport is much to high for phone displays. Because I have to display computed html pages, I have the chance to adjust the viewport in html. With
<meta name="viewport" content="width=380" />
I come next to that what it should be. But on an orientation change while displaying a page, I can't replace the viewport value, but for me it is acceptable to have a little bigger text in landscape.
Setting a viewport with an initial scale like this
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
is not better, because scale information is not considered.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With