static - Dave's Blog

Search
My timeline on Mastodon

Tiny browser features: JSBrowser zoom

2018 May 10, 3:49

JSBrowser is a basic browser built as a Win10 JavaScript UWP app around the WebView HTML element. Its fun and relatively simple to implement tiny browser features in JavaScript and in this post I'm implementing zoom.

My plan to implement zoom is to add a zoom slider to the settings div that controls the scale of the WebView element via CSS transform. My resulting zoom change is in git and you can try the whole thing out in my JSBrowser fork.

Slider

I can implement the zoom settings slider as a range type input HTML element. This conveniently provides me a min, max, and step property and suits exactly my purposes. I chose some values that I thought would be reasonable so the browser can scale between half to 3x by increments of one quarter. This is a tiny browser feature after all so there's no custom zoom entry.

<a><label for="webviewZoom">Zoom</label><input type="range" min="50" max="300" step="25" value="100" id="webviewZoom" /></a>

To let the user know this slider is for controlling zoom, I make a label HTML element that says Zoom. The label HTML element has a for attribute which takes the id of another HTML element. This lets the browser know what the label is labelling and lets the browser do things like when the label is clicked to put focus on the slider.

Scale

There are no explicit scale APIs for WebView so to change the size of the content in the WebView we use CSS.

        this.applyWebviewZoom = state => {
const minValue = this.webviewZoom.getAttribute("min");
const maxValue = this.webviewZoom.getAttribute("max");
const scaleValue = Math.max(Math.min(parseInt(this.webviewZoom.value, 10), maxValue), minValue) / 100;

// Use setAttribute so they all change together to avoid weird visual glitches
this.webview.setAttribute("style", [
["width", (100 / scaleValue) + "%"],
["height", "calc(" + (-40 / scaleValue) + "px + " + (100 / scaleValue) + "%)"],
["transform", "scale(" + scaleValue + ")"]
].map(pair => pair[0] + ": " + pair[1]).join("; "));
};

Because the user changes the scale at runtime I accordingly replace the static CSS for the WebView element with the script above to programmatically modify the style of the WebView. I change the style with one setAttribute call to do my best to avoid the browser performing unnecessary work or displaying the WebView in an intermediate and incomplete state. Applying the scale to the element is as simple as adding 'transform: scale(X)' but then there are two interesting problems.

The first is that the size of the WebView is also scaled not just the content within it. To keep the WebView the same effective size so that it still fits properly into our browser UI, we must compensate for the scale in the WebView width and height. Accordingly, you can see that we scale up by scaleValue and then in width and height we divide by the scaleValue.

transform-origin: 0% 0%;

The other issue is that by default the scale transform's origin is the center of the WebView element. This means when scaled up all sides of the WebView would expand out. But when modifying the width and height those apply relative to the upper left of the element so our inverse scale application to the width and height above aren't quite enough. We also have to change the origin of the scale transform to match the origin of the changes to the width and height.

PermalinkCommentsbrowser css-transform javascript JS jsbrowser uwp webview win10

Tweet from Chris Heilmann

2016 Nov 2, 4:57
The static on TV is referred to as “hangyafoci” by Hungarians, which translates to “ant soccer” https://en.wikipedia.org/wiki/Noise_(video) 
PermalinkComments

WinRT Toast from PowerShell

2016 Jun 15, 3:54

I've made a PowerShell script to show system toast notifications with WinRT and PowerShell. Along the way I learned several interesting things.

First off calling WinRT from PowerShell involves a strange syntax. If you want to use a class you write [-Class-,-Namespace-,ContentType=WindowsRuntime] first to tell PowerShell about the type. For example here I create a ToastNotification object:

[void][Windows.UI.Notifications.ToastNotification,Windows.UI.Notifications,ContentType=WindowsRuntime];
$toast = New-Object Windows.UI.Notifications.ToastNotification -ArgumentList $xml;
And here I call the static method CreateToastNotifier on the ToastNotificationManager class:
[void][Windows.UI.Notifications.ToastNotificationManager,Windows.UI.Notifications,ContentType=WindowsRuntime];
$notifier = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($AppUserModelId);
With this I can call WinRT methods and this is enough to show a toast but to handle the click requires a little more work.

To handle the user clicking on the toast I need to listen to the Activated event on the Toast object. However Register-ObjectEvent doesn't handle WinRT events. To work around this I created a .NET event wrapper class to turn the WinRT event into a .NET event that Register-ObjectEvent can handle. This is based on Keith Hill's blog post on calling WinRT async methods in PowerShell. With the event wrapper class I can run the following to subscribe to the event:

function WrapToastEvent {
param($target, $eventName);

Add-Type -Path (Join-Path $myPath "PoshWinRT.dll")
$wrapper = new-object "PoshWinRT.EventWrapper[Windows.UI.Notifications.ToastNotification,System.Object]";
$wrapper.Register($target, $eventName);
}

[void](Register-ObjectEvent -InputObject (WrapToastEvent $toast "Activated") -EventName FireEvent -Action {
...
});

To handle the Activated event I want to put focus back on the PowerShell window that created the toast. To do this I need to call the Win32 function SetForegroundWindow. Doing so from PowerShell is surprisingly easy. First you must tell PowerShell about the function:

Add-Type @"
using System;
using System.Runtime.InteropServices;
public class PInvoke {
[DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hwnd);
}
"@
Then to call:
[PInvoke]::SetForegroundWindow((Get-Process -id $myWindowPid).MainWindowHandle);

But figuring out the HWND to give to SetForegroundWindow isn't totally straight forward. Get-Process exposes a MainWindowHandle property but if you start a cmd.exe prompt and then run PowerShell inside of that, the PowerShell process has 0 for its MainWindowHandle property. We must follow up process parents until we find one with a MainWindowHandle:

$myWindowPid = $pid;
while ($myWindowPid -gt 0 -and (Get-Process -id $myWindowPid).MainWindowHandle -eq 0) {
$myWindowPid = (gwmi Win32_Process -filter "processid = $($myWindowPid)" | select ParentProcessId).ParentProcessId;
}
PermalinkComments.net c# powershell toast winrt

Considerate MessagePort Usage

2013 Aug 7, 7:14
Sharing by leezie5. Two squirrels sharing food hanging from a bird feeder. Used under Creative Commons license Attribution-NonCommercial-NoDerivs 2.0 Generic.When writing a JavaScript library that uses postMessage and the message event, I must be considerate of other JS code that will be running along side my library. I shouldn't assume I'm the only sender and receiver on a caller provided MessagePort object. This means obviously I should use addEventListener("message" rather than the onmessage property (see related What if two programs did this?). But considering the actual messages traveling over the message channel I have the issue of accidentally processing another libraries messages and having another library accidentally process my own message. I have a few options for playing nice in this regard:
Require a caller provided unique MessagePort
This solves the problem but puts a lot of work on the caller who may not notice nor follow this requirement.
Uniquely mark my messages
To ensure I'm acting upon my own messages and not messages that happen to have similar properties as my own, I place a 'type' property on my postMessage data with a value of a URN unique to me and my JS library. Usually because its easy I use a UUID URN. There's no way someone will coincidentally produce this same URN. With this I can be sure I'm not processing someone else's messages. Of course there's no way to modify my postMessage data to prevent another library from accidentally processing my messages as their own. I can only hope they take similar steps as this and see that my messages are not their own.
Use caller provided MessagePort only to upgrade to new unique MessagePort
I can also make my own unique MessagePort for which only my library will have the end points. This does still require the caller to provide an initial message channel over which I can communicate my new unique MessagePort which means I still have the problems above. However it clearly reduces the surface area of the problem since I only need once message to communicate the new MessagePort.
The best solution is likely all of the above.
Photo is Sharing by leezie5. Two squirrels sharing food hanging from a bird feeder. Used under Creative Commons license Attribution-NonCommercial-NoDerivs 2.0 Generic.
PermalinkCommentsDOM html javascript messagechannel postMessage programming technical

2012 Oct 22, 11:24

PermalinkComments

2012 Oct 22, 11:24

PermalinkComments

Alex takes a few steps

2012 Aug 16, 4:06
From: David Risney
Views: 75
0 ratings
Time: 00:43 More in People & Blogs
PermalinkCommentsvideo

Alex walking via walker

2012 Aug 6, 4:44
From: David Risney
Views: 69
0 ratings
Time: 00:53 More in People & Blogs
PermalinkCommentsvideo

Fwd: Alex Water Lessons

2012 Mar 10, 9:42

PermalinkComments

Fwd: Alex Water Lessons

2012 Mar 10, 9:42

PermalinkComments

Fwd: High chair

2012 Feb 13, 12:58

PermalinkComments

Fwd: High chair

2012 Feb 13, 12:58

PermalinkComments

Alex and glo friend

2012 Jan 17, 4:52

PermalinkComments

Alex and glo friend

2012 Jan 17, 4:52

PermalinkComments

Musée McCord Museum’s photostream on Flickr.

2012 Jan 15, 10:37


Foot race, Dawson City, YT, about 1900Cricket match, McGill campus, Montreal, QC, about 1890Football game on campus, McGill University, Montreal, QC, about 1900S. S. "Nascopie" at sealing grounds, 1927

Musée McCord Museum’s photostream on Flickr.

PermalinkCommentsphoto old-timey black-and-white history

Alex on quilt

2012 Jan 14, 9:41

PermalinkComments

Alex on quilt

2012 Jan 14, 9:41

PermalinkComments

Alex asleep in baby chair

2012 Jan 14, 3:17

PermalinkComments

Alex asleep in baby chair

2012 Jan 14, 3:17

PermalinkComments

Alex in standing thing

2012 Jan 4, 9:30

PermalinkComments
Older Entries Creative Commons License Some rights reserved.