Opening .url Files in Ubuntu

When browsing the web with Chrome for Android, I save the URLs on my Nextcloud server by sharing using the Nextcloud App. Each URL is then stored as a .url file looking like this

[InternetShortcut]
URL=https://devio.wordpress.com/

Today I noticed that those .url files cannot be opened on Ubuntu, i.e. a double-click won’t start a browser with the contained URL.

Instead, I get a an error dialog

Could not display “<HTML page title>.url”.

There is no application installed for “Internet shortcut” files.
Do you want to search for an application to open this file?

No     Yes

Screenshot from 2020-03-22 07-40-58.png

Clicking the Yes button, a toast message appears

mimetype required.png

which you have to click before it disappears, which finally opens the software installer:

unable to find software.png

Not good.

Surprisingly, Firefox does not register itself as an application to handle the .url file extension on Ubuntu. It also does not know that the Windows Firefox would know how to open the file.

More surprisingly, Ubuntu knows that .url files are “Internet shortcut” files, and have the associated MIME type application/x-mswinurl.

So I had to solve two problems:

  • Retrieve the URL stored in a .url file
  • Start Firefox using this URL using Ubuntu’s MIME type handling

Retrieving the URL stored in a .url file

As shown above, a .url file is simply a text file in .ini format. In it’s simplest form, it contains a section [InternetShortcut] with a single Key “URL=”. The key’s value is the URL to navigate to.

With a little help from askubuntu, I figured out the command to extract the URL value

grep -Po 'URL=\K[^ ]+' *.url

Using the result of the grep operation as argument for firefox would look something like this:

firefox `grep -Po 'URL=\K[^ ]+' "$1"`

After a bit of digging, I found how you can manually add MIME type handlers in Ubuntu. Following those instructions, I created a file

/usr/share/applications/mswinurl.desktop

(you need sudo in this directory) with the following content (spoiler: don’t copy this yet!):

[Desktop Entry]
Name=Firefox Shortcut
GenericName=Firefox Shortcut

Type=Application
Exec=firefox `grep -Po 'URL=\K[^ ]+' %U`
TryExec=firefox
MimeType=application/x-mswinurl;
Icon=firefox

However, this did not work as intended, as I got an error message complaining about the backtick `. So, if I cannot have shell operations in the .desktop file, let’s create a batch file

/usr/local/bin/runurl

and place the shell magic there:

firefox `grep -Po 'URL=\K[^ ]+' "$1"` &

Don’t forget to make the batch file executable using

sudo chmod 755 runurl

and reference the runurl script rather than Firefox in /usr/share/applications/mswinurl.desktop:

[Desktop Entry]
Name=Firefox Shortcut
GenericName=Firefox Shortcut

Type=Application
Exec=runurl %U
TryExec=firefox
MimeType=application/x-mswinurl;
Icon=firefox

After creating the file, run

 sudo update-desktop-database

to register the new .desktop file.

Double-clicking a .url file now opens the URL in a new Firefox tab.

Detecting Screen Orientation Change

Browsers provide different means to detect screen orientation:

Documentation in the Mozilla Developer Network (linked above) states the first to be deprecated but currently still in the WhatWG Living Standard, whereas its documentation on the latter differs from the W3C documentation.

According to documentation, detection of screen orientation change can be achieved by implementing handlers for the events

  • window.orientationchange
  • screen.orientation.change
  • window.matchMedia() listener
  • window.resize

but specific browsers may not support all of these events, with window.resize being the catch-all solution if everything else fails.

So based on SO answers and this blog and this blog I came up with a solution that currently seems to work, and a couple of findings:

  • window.orientation gives the angle on mobile browsers only – desktop browsers always contain 0 (zero).
  • Similarly, window.onorientationchange is only supported by mobile browsers.
  • screen.orientation (and its browser-spezific siblings mozOrientation and msOrientation) contains the angle in its angle property. IE11 does support support screen.orientation on Win7. Mobile Chrome (35) and the Android 4.4.2 Browser do not seem to support it either.
  • Of the browsers I tested, none seem to implement the event screen.orientation.onchange.
  • Orientation change can be detected using the window.matchMedia() listener on both mobile and desktop browsers which support mediaqueries and its orientation selector.
  • In desktop browsers, orientation can only be derived from $(window).width() and $(window).height(), or from the .matches property of a matchMedia listener.

Note that all this need not apply for older browsers, not even the values of window.orientation! (See SO, SO, SO, Giff’s note)

So here now is my JavaScript code for screen orientation change detection:

function doOnOrientationChange(src)
{
  if (window.console && console.log) 
    console.log("width " + $(window).width() + " height " + $(window).height());

  var orientation = { 
    angle: window.orientation,
    type: ("onorientationchange" in window) ? "mobile" : "desktop"  
  };

  if (window.screen) {
    var o = window.screen.orientation || window.screen.mozOrientation 
      || window.screen.msOrientation || orientation;
    orientation = { angle: o.angle, type: o.type };
  } else if ((window.orientation === 0) || window.orientation) {
    orientation = { angle: window.orientation, type: "" + window.orientation + " degrees" };
  }
 
  if (!("onorientationchange" in window)) {
    var w = $(window).width(), h =$(window).height();
    var a = (w > h) ? 90 : 0;
    orientation.angle = a;
    if (window.console && console.log) 
      console.log("angle := " + a + " " + orientation.angle);
  }
 
  var jsonOrientation = JSON.stringify(
    { angle: orientation.angle, type: orientation.type });

  switch(orientation.angle) 
  { 
    case -90:
    case 90:
      // we are in landscape mode
      $().toastmessage('showNoticeToast', src + ' landscape ' + " " + jsonOrientation);
      if (window.console && window.console.log) console.log(src + ' landscape ' + " " + jsonOrientation);
      $("#orientation").text(src + ' landscape ' + " " + jsonOrientation);
      break; 
    case 0:
    case 180:
      // we are in portrait mode
      $().toastmessage('showNoticeToast', src + ' portrait ' + " " + jsonOrientation);
      if (window.console && window.console.log) console.log(src + ' portrait ' + " " + jsonOrientation);
      $("#orientation").text(src + ' portrait ' + " " + jsonOrientation);
      break; 
    default:
      // we have no idea
      $().toastmessage('showNoticeToast', src + ' unknown ' + " " + jsonOrientation);
      if (window.console && window.console.log) console.log(src + ' unknown ' + " " + jsonOrientation);
      $("#orientation").text(src + ' unknown ' + " " + jsonOrientation);
      break; 
  }
}

$(function () {

  if ("onorientationchange" in window) 
    window.addEventListener('orientationchange', 
      function() { doOnOrientationChange("window.orientationchange"); });
  //window.addEventListener('resize', 
  //    function() { doOnOrientationChange("window.resize") });
  if (window.screen && window.screen.orientation && window.screen.orientation.addEventListener)
    window.screen.orientation.addEventListener('change', 
      function() { doOnOrientationChange("screen.orientation.change"); });

  if (window.matchMedia) {
    var mql = window.matchMedia("(orientation: portrait)");
    mql.addListener(function(m) {
      if (m.matches) {
        doOnOrientationChange("mql-portrait");
      } else {
        doOnOrientationChange("mql-landscape");
      }
    });
  }

  doOnOrientationChange("init");
});

(I put the window.resize handler into comments because it generates too may events on desktop browsers.)

In this sample code, detection change only causes output of angle and orientation type to

  • $().toastmessage() – a jQuery extension
  • console.log
  • $(“#orientation”).text() – a jQuery call

Of course, your handlers may perform some useful actions…

Retrieving Chrome tabs suspended by The Great Suspender

After a recent Windows crash, restarting Chrome would not open all the windows and tabs I had collected, despite its nice “Restore previous session” question.

None of the tips I found on the net helped recovering those tabs:

Anyway, I found that the tabs suspended by The Great Suspender were most likely not contained in the Tabs file, so where is that information stored?

By looking at the source it turns out that TGS stores its data in an IndexedDB.

To access this database, you need to open TGS’s extension URL chrome-extension://klbibkeccnjlkjkiokjodocebajanakg/suspended.html. Using F12 (Developer Tools) and selecting Resources, you find “tgs” under IndexedDB, containing the tables gsCurrentSessions, gsSuspendedTabInfo, gsPreviews, and gsSavedSessions.

Unfortunately, currently there is no way to export this data directly from the DevTools, so we need a little bit of scripting magic.

After copying and pasting from MDN’s IndexedDB API documentation and sample code and some trial and error, I finally completed a small Javascript script which, when entered in the Developer Tools’ Console, results in a JSON-formatted list of all suspended tabs:

var db; var result=""; var record=0;
var dbor = window.indexedDB.open("tgs");
dbor.onsuccess = function(event) { 
  db = dbor.result;
  db.transaction(["gsSuspendedTabInfo"], "readonly")
    .objectStore("gsSuspendedTabInfo")
    .openCursor()
    .onsuccess= function(event) {
      var c=event.target.result;
      if (c){
        result+=JSON.stringify(c.value)+"\r\n";
        console.log(record++);
        c.continue();
      } else {
        console.log(result);
      }
    };
};

A single JSON record of a suspended tab looks like this:

{
  "date":"2015-12-14T18:40:55.563Z",
  "title":".validate() | jQuery Validation Plugin",
  "url":"http://jqueryvalidation.org/validate#toptions",
  "favicon":"chrome://favicon/http://jqueryvalidation.org/validate#toptions",
  "pinned":false,
  "index":3,
  "windowId":2898,
  "id":1281
}

Of course, you can process each tab’s record (c.value in the code above) any way you need.

Browser Screenshot Extensions

If you want to take a screenshot of your current browser window, there’s always good old ALT-Printscreen, but this function captures the whole window, not just the contents, and copies it to the clipboard. Then you still need to open a graphics editor, such as Paint.Net, to crop, edit, and save the image.

There are, however, a couple of browser extensions to simplify the process, and support capturing the complete page contents, rather than just the visible part of the page.

Here’s the list of extensions I use:

Firefox

In Firefox, I use Screengrab (fix version). It allows you to save or copy-to-clipboard the complete page, the visible part, or a selected area of the current page.

In the settings, you can define the pattern of the file name of the saved image (default: HTML Title and timestamp), and the text that is generated at the top of the image (default: URL). The option “Quickly save” won’t prompt you for a file name.

I love this extension for Firefox – however, if the screenshot gets too big (about 1.5Mb on Win32, 3Mb on Win32), it silently fails and generates .png files of size 0).

Chrome

The extension Screen Capture (by Google) is now unsupported, and it did not work (read: the menu buttons did not invoke any recognizable action) on the latest versions of Chrome.

The extension Awesome Screenshot: Capture & Annotate supports capturing the complete page, the visible and a selected part of the page. After capturing, a simply picture editor allows you to crop the picture, or add simple graphics and text to the image. The file name of the saved image defaults to the page’s Title, but can be edited in the Save As dialog.

Unfortunately, only the command “Capture visible part of page” works on Facebook pages – both “entire page” and “selected area” fail to capture.

Finally, the extension Full Page Screen Capture simply generates an image of the complete page, and displays it in a new tab. From there, you need to invoke Save (ctrl-S) to save the image to the default directory. File name pattern is “screencapture-” plus the current URL. This extension provides no options.

Chrome, Firefox reset Flash plugin if display: property changes (Update)

To resolve the mystery of Flash plugins restarting when their display property changes (directly or inherited), I created a couple of plain and simple HTML files to test a set of operations to toggle visibity in IE 9 (9.0.8112.16421), Chrome 18 (18.0.1025.168 m), and Firefox 12 (12.0).

The Flash player has version 11.1.102.55, and the Flash object is initialized by swfobject.js.

Each of the 3 browsers were tested on 2 different pages with 3 different toggle operations:

  • Page 1 contained the Flash object inside an iframe inside a div.

DOM hierarchy:
<div><iframe><html etc><div class=”flashContent”>

  • Page 2 contains the Flash object inside a div.

DOM hierarchy:
<div><div class=”flashContent”>

The toggle operations were

  • assign a .hidden class setting visibility to hidden
.hidden { visibility: hidden; width: 0px; height: 0px; }
  • assign a .displaynone class setting display to none
.displaynone { display: none; }
  • using jquery .show() and .hide()

.show() actually does not clear the display: property, but sets it to ‘block’, ‘inline’ etc, which might interfere with other operations, such as addClass(“displaynone”), etc.

Results for Page 1 (using iframe):

IE 9 Chrome 18 Firefox 12
toggling top-level div
hidden ok ok ok
display:none ok ok restart
show/hide ok ok restart
toggling iframe
hidden ok ok ok
display:none ok ok restart
show/hide ok ok restart

Results for Page 2 (directly embedded Flash):

IE 9 Chrome 18 Firefox 12
toggling top-level div
hidden ok ok ok
display:none ok restart restart
show/hide ok restart restart
toggling flash object
hidden ok ok ok
display:none ok restart restart
show/hide ok restart restart

The only method to set a Flash object hidden and visible again while keeping the object running turns out to be setting a CSS class with visibility:hidden.

So I put the code (swf inside iframe) that worked in plain HTML files and added it into a DotNetNuke installation. It almost worked.

A new issue occurred: IE9 was not able to display a Flash object once the visibility:hidden style was removed! F12 did not help me to make the object visible again. Strangely though, using JavaScript to re-assign some other iframe to the same src URL caused the Flash to display again!

$("#someotheriframe").attr("src", 
    $("#someotheriframe").attr("src"));

A DNN page simply contains too much generated code (ASP.Net, MS Ajax, DNN framework, jQuery, various controls and .js files) so that I did not think it was worth debugging the issue further. My JavaScript functions to toggle visibility now contain a browser detection based on $.browser to decide which alternative to choose.

Chrome, Firefox reset Flash plugin if display: property changes

Working on a web site that dynamically (i.e. upon user action) shows and hides HTML content and Flash content, everything worked as expected in IE9.

When I checked the same page in Chrome 18 and Firefox 12, however, the Flash object would reset everytime I invoked the jQuery .show() method on the object (initialization takes some time for some of the objects I embed).

This behavior has already been mentioned in blogs and support forums.

My solution was to roll-back some of the changes I made, and put the Flash object back to its iframe. Toggling the iframe display does not seem to affect Flash.

Other people worked around this behavior by setting visibility and size, or adding/removing a class that defines display:none.