By supporting the Opera Crypto Wallet, Dapp developers can gain access to millions of crypto users on Opera platform. To do so, Dapp developers need simply add a few lines of code to set up integration with the Opera Crypto Wallet.
There are two steps in requesting a user connect their Opera Crypto Wallet to your dApp, without the need for SDK installation.
Check the global variable to see if the client is Opera.
let isOperaWallet = false;
if(window.ethereum){
isOperaWallet = window.ethereum.isOpera;
}
if(isOperaWallet){
// add Opera Wallet as the first option in the wallet connection panel.
}
If they’re using the Opera Crypto Wallet, then you can request the user connect the wallet following EIP-1193. Opera Browser will then prompt the Opera Crypto Wallet and ask the user to connect the wallet to the Dapp.
if(window.ethereum){
window.ethereum.request({method: 'eth_requestAccounts'})
}
When adding Opera Crypto Wallet as an option in the wallet connection panel, please use the official Opera Wallet logo.
To download the Opera Crypto Wallet logo, please click here.
The following libraries also allow you to connect to the Opera Wallet:
The implementation of Opera Wallet follows EIP-1193. You can connect the wallet with library implemented EIP-1193, such as web3.js or ether.js.
You can access the Opera Crypto Wallet by downloading Opera’s Crypto Browser. You can download it here.
To set up the wallet, click the wallet icon on the left sidebar. You can set up a new wallet or restore your existing wallet with your recovery phrase.
The wallet also covers implementation on EIP-3326. As a result, you can use wallet_switchEthereumChain
to request the user switch networks. The wallet will display a popup allowing users to switch networks in one click.
The wallet also covers implementation on EIP-3085. As a result, you can use wallet_addEthereumChain
to request the user add a new network.
Opera now supports COLR/CPAL fonts which are vector based fonts with color information. As fonts become available, we can expect Unicode-based emojis to become more interesting.
By default touchpad events create synchronous synthetic ctrl+wheel events that allow documents to cancel them. This can unfortunately greatly increase the time between a user action and changes on the screen. To improve the situation the browser has changed so that if the first synthetic ctrl+wheel event is let through, then no more synthetic events will be created. If you do want to cancel pinch zoom, you need to cancel all the events. You can read more about this in the design document
With Intl.RelativeTimeFormat()
you can now get relative time descriptions for any supported locale.
Two simple examples below:
formatter = new Intl.RelativeTimeFormat("en");
formatter.format(2.5, "hour"); // "in 2.5 hours"
formatter = new Intl.RelativeTimeFormat("sv", {numeric: "auto"}); // Swedish, text or numbers.
formatter.format(-1, "week"); // "förra veckan" (last week)
You can read more about what you can do with Intl.RelativeTimeFormat by reading what the feature developers say about it.
The default credentials mode for module script requests is changing from “omit” to “same-origin”, providing credentials to same-origin module script requests and their descendant scripts (static & dynamic imports). The previous behavior can be surprising in that it is misaligned with other high-level features like the Fetch API, and could not be reused for the main request, causing a second server connection which added latency.
Unfortunately speechSynthesis.speak() has been abused so it will now only be available once the user has interacted with the page. With this change, everything that makes sounds follows the same autoplay policy.
Opera now only allows creation of
MediaElementAudioSourceNode
, MediaStreamAudioSourceNode
, and MediaStreamAudioDestinationNode
elements using an AudioContext
. Previously these could be created using an OfflineAudioContext
,
but that does not comply with the specification and made little sense.
The normal way to embed MediaStreams is to assign them to scrObject
, but until now URL.createObject()
has also worked. It has been deprecated since 2013 though and with this release, that
obsolete method has been removed.
ServiceWorkers can no longer import scripts with importScripts() once the ServiceWorker has been installed.
A site can request to store data persistently, that is, in a way that will not be exposed
to automatic deletion just because of lack of storage space. In this version a site can
request the user’s permission to do so through the
persistent-storage
property in the Permission API.
document.origin
The document.origin
property was not cross-browser
and the supported, cross-browser, way to access ones origin is with self.origin
so in
this release document.origin
was removed.
Text encoding and decoding can now be done with streams to enable you to easily convert streams of binary data to text and vice-versa.
For many years, the fullscreen API has had a vendor prefix (which all browser vendors have reused), but coming in Opera 58, and other Chromium 71 browser, there is a fully unprefixed fullscreen API.
It is common to repeat color stops in a gradient to create single colored bands like this:
linear-gradient(45deg, black 25%, black 50%, white 50%, white 75%)
With the new CSS4 specification comes support for a more compact syntax where the repeated color name is no longer needed:
linear-gradient(45deg, black 25% 50%, white 50% 75%)
left
and right
for text-underline-position
Vertical text has had slightly undefined positioning of underlines. With the
newly added values
left
and right
for text-underline-position
, it should now be possible to make a cross-browser
declaration of where an underline should be positioned.
Certain Shadow DOM v1 CSS selectors did not match in the correct order. In Opera 58, :host() and :host-context() pseudo classes as well as ::slotted() arguments will be taken into account when calculating a selectors specificity.
AnimationEvent
and TransitionEvent
, without the WebKit prefix, has been available for some time
and in this Opera version the prefixed versions were removed after usage dropped had sufficiently.
If you’re interested in experimenting with features that are in the pipeline for future versions of Opera, we recommend following our Opera Developer stream.
You can also see features that we and other Chromium/Blink contributors are working on by looking at the Chrome Platform Status page.
]]>Dialogs (authentication prompt, payment request etc.) require proper context when shown to the user. When the browser is in fullscreen mode - an immersive mode - that context is not available. To help the user get the right context when a dialog is displayed, the browser will now leave fullscreen mode in those cases.
ontouch*
APIs default to disabled on desktop platformsThe ontouch*
event attributes on Window
, Document
and Element
are
now disabled by default on desktop platforms. This is to avoid confusion when
feature detecting touch support - use navigator.maxTouchPoints
for
touchscreen detection and window.TouchEvent
touch event detection instead. To
support both touch and mouse input, consider using the Pointer Events API.
Event listeners for the associated touch events can still be added via
EventTarget.prototype.addEventListener
, like:
document.addEventListener("touchstart", ...);
postMessage
methodsAn optional PostMessageOptions
dictionary
can now be passed to the
postMessage
method on DedicatedWorkerGlobalScope
, Worker
, ServiceWorker
, ServiceWorker
, and Window
.
Thus what was previously written as:
window.postMessage('my message', 'https://origin.example.com');
can now also be written as:
window.postMessage('my message', { targetOrigin: 'https://origin.example.com' });
Opus
codec is now supported in mp4 (ISO-BMFF) containers with Media Source Extensions (MSE).An “intervention” happens when the user agent (the browser) willfully violates
the request of a web application in order to improve performance, security or
eliminate behavior that is considered annoying to a user. These interventions
can now be reported back to the page author either by using the
Report-To
HTTP Response header or the
ReportingObserver
API.
TLS 1.3 is a new version of the TLS protocol that sports a simpler, less error-prone design which improves both efficiency and security. This is achieved by, among other things, reducing the number of round-trips needed to setup a connection, and remove a number of insecure legacy options.
<rp>
element is now display: none
by default even outside a <ruby>
element.Symbol.prototype.description
Symbol.prototype.description
provides a more straight-forward way to access
the description of a Symbol
rather than the previously more indirect access
via Symbol.prototype.toString
.
The WebUSB feature can now be accessed from within a dedicated worker. This for example allows communication with an USB device to be offloaded from the main thread - which can improve performance.
The Application Cache feature, after having been deprecated since Opera 54 (Chromium 67), is no longer exposed in non-secure contexts.
It was previously possible to access <frame>
elements by name via their
parent <frameset>
. This was a non-standard extension, which has now been
removed.
The “V0” versions of the Custom Elements and Shadow DOM APIs has been deprecated, and is expected to be removed in Opera 60 (Chromium 73). Please watch out for deprecation warnings and migrate any usage to the “V1” APIs:
At the time of writing, the latter specification is in the process of being split up and included into the DOM Standard, HTML Standard and CSS Scoping.
To understand what the differences are between “V0” and “V1” of Shadow DOM, see What’s New in Shadow DOM v1 (by examples) by Hayato Ito, for a very thorough list.
HTML Imports allowed importing HTML documents into other HTML documents. This feature was never widely supported, and is thus being deprecated. Like Custom Elements “V0” and Shadow DOM “V0” above, it’s also expected to be removed in Opera 60 (Chromium 73).
If you’re interested in experimenting with features that are in the pipeline for future versions of Opera, we recommend following our Opera Developer stream.
You can also see features that we and other Chromium/Blink contributors are working on by looking at the Chrome Platform Status page.
]]>In this release there is mostly incremental changes, but we are looking forward to seeing what people can do with the new CSS conical gradient support. Below is a quick overview of changes. Follow the links for more details, specifications and examples.
The web already has linear gradients where the colour depends on the
distance to a line, and radial gradients where the colour depends on
the distance to a point. Now it also gets conic gradients where the
colour depends on the angular distance to a line through the new
conic-gradient
function.
Example:
Blink, the Chromium/Opera browser engine, has gotten some new CSS
properties that adapt to
text direction. So padding-inline-start
will apply to either left or
right depending on what side you start reading. Other examples of
newly added properties are border-block-end-color
,
margin-inline-start
and padding-inline-end
.
CSS Scroll Snap allows the page author to determine what is suitable places in a document to snap to. This could be used to avoid cutting off content or to align content that is as large as the viewport correctly. This is a common problem in a tile based user interface.
Previously this problem has been solved through JavaScript but doing it in CSS allows a much smoother user experience.
“Cutouts” is an area on a mobile phone where only part of the screen is available to the web page, like the notch at the top of some phones. With the display cutout support, it becomes possible to layout content around the cutout in an adaptive way.
In CSS the necessary data can be extracted from the env
function. See the specification
for details.
The interpretation of height percentages for row tracks and gutters will change to be compliant with the specification in the next release, but already in Opera 56 you will see warnings if you are using height calculations that might change.
OffscreenCanvas is a canvas intended to be used in workers. Sometimes painting requires heavy calculation more suitable for a webworker and then OffsceenCanvases will come in handy. They have almost the same API as ordinary canvases so moving code to a worker should be easy.
stalled
removed from HTMLMediaElementsThere used to be an event stalled
that fired for Media Source
Extension (MSE) players if no data had been added for 3 seconds. This
was not useful since the media player can be fine receiving data less
often depending on buffer sizes and transfer chunk sizes. The event
has now been removed.
It is now possible for a web developer to query the browser about what encryption schemes it supports through EME. This is useful because there is no general agreement across all platforms about what encryption schemes should be supported.
Element.toggleAttribute()
addedWith
Element.toggleAttribute
an attribute can be removed if it exists or added if it does not. This
is especially useful for boolean attributes such as disabled
or
readonly
. Example:
// Switch disabled mode of "the button".
var theButton = document.getElementById("the-button");
theButton.toggleAttribute("disabled");
The official way to create Touch objects is through the Touch()
constructor so document.createTouchList()
had no use and is now
removed.
Through the ReportingObserver API it becomes possible to collect information such as deprecation warnings and manually (through scripts) handle them. For instance by sending them to a server.
Array.prototype.flat()
/ flatMap()
V8, the JavaScript engine, now has support for flat()
and
flatMap()
on arrays, allowing code to easily expand sub-arrays in
place.
x = [ [1, 1], [2, 3], 5 ];
y = x.flat(); // y = [ 1, 1, 2, 3, 5 ]
Using flatMap()
allows code to write a function that controls how
each array element is expanded.
The Keyboard Map API allows web applications to get a descriptive string for different keyboard keys. This can be of use when telling a user what key to press, in for instance a game.
Example from the specification:
navigator.keyboard.getLayoutMap().then(function(map) {
var keyUp = map.get("KeyW");
showUserDialog("Press " + keyUp + " to move up.");
});
Approximate network information is now available through both JavaScript and HTTP Client Hints. More details can be found on the Chromium feature status page.
Web Locks allows better synchronization between tabs that share the same resource, something that has become more important as more APIs are asynchronous. This is most useful when storage resources such as IndexedDB and WebSQL are used in a non-atomic way, but can be used for any purpose.
Example from the link above:
navigator.locks.request('my_resource', async lock => {
// The lock has been acquired.
await do_something();
await do_something_else();
// Now the lock will be released.
});
If you’re interested in experimenting with features that are in the pipeline for future versions of Opera, we recommend following our Opera Developer stream.
You can also see features that we and other Chromium/Blink contributors are working on by looking at the Chrome Platform Status page.
]]>Sensor data is used in many native applications to enable experiences like immersive gaming, fitness tracking, and
augmented or virtual reality. This data is now available to web applications using the Generic Sensor API.
The Generic Sensors API provides a foundation for sensors in the form of a base Sensor
interface and associated
abstract operations. Concrete sensors are provided on top of this specification. The following are some of the concrete
sensors:
Additional resources:
SVG2 requires <foreignObject>
to be a stacking context.
Making <foreignObject>
a stacking context means HTML content within a <foreignObject>
will integrate better with other content.
DOMTokenList.replace()
now returns a boolean value indicating if the replacement was successful or not. This allows code that
for instance takes different paths depending on whether a replacement occurred, to avoid an extra condition using
contains()
.
Custom Elements can now extend HTML elements to inherit the semantics of native, built-in elements. This avoids reimplementing built-in functionality such as accessibility, semantics and JavaScript methods/properties.
Mouse events (mousedown
, auxclick
, mouseup
) will now be dispatched for back and forward buttons on mice with more
than four buttons. This allows back and forward mouse buttons to be prevented by applications, such as games, that wish
to override them.
On Windows the right-hand Alt key serves as AltGraph (ISO-Level-3-Shift) on some layouts, such as many European language layouts, to allow generating additional printable-characters. Internally the key generates Ctrl+Alt modifiers, so that Opera reports all of Control, Alt and AltGraph in the flags for these keys. In this change, Opera distinguishes AltGraph from Ctrl+Alt under Windows for consistency with these modifiers on other platforms.
For developers this removes an edge-case from keyboard event modifier handling. If an app handles
keydown
/keypress
/keyup
to implement shortcuts, it will no longer need workarounds to cope with certain (mainly
European) keyboard layouts. For example, if an app uses Ctrl+# as a shortcut then previously the app would need to check
for both Ctrl, and for AltGraph, otherwise French users would not be able to use it. This change applies to Windows
only.
JavaScript now supports a numeric primitive for arbitrary precision integers.
Previously, numbers in JavaScript were represented as double-precision floats, thus giving them limited precision. Using
the BigInt()
function and ‘n'
suffix on numeric literals you can store and operate on large integers beyond the safe
integer limit for numbers.
Formatting contexts will now behave exactly like floats do when they are positioned. In other words, they no longer
consider the shape-outside
property of the float when positioning is determined, and are instead positioned according
to their margin box. The new behavior can be seen in this example by
changing the height of the flex
class. This also affects how new formatting contexts are sized and positioned.
Client Hints enable origins to receive device-specific
preferences in the HTTP request headers. The Accept-CH-Lifetime
header field
adds a client hint that allows origins to persist their opt-in policy for a specified period so they can receive client
hints on navigation requests. Additionally, on the first page load, this feature provides hints for all subresources of
the page.
For more on Client Hints, see Automating Resource Selection with Client Hints by Ilya Grigorik.
Stream API support has been extended with TransformStream
.
Transform streams enable transforming data in stream form. It can for example be
used to pipe between a ReadableStream
and a WritableStream
. The following example uses a TransformStream
to decode
text received in a streaming response body:
function textDecodeTransform() {
const decoder = new TextDecoder();
return new TransformStream({
transform(chunk, controller) {
controller.enqueue(decoder.decode(chunk, { stream: true }));
}
});
}
fetch(url).then(response => {
// response.body is a stream of Uint8Array chunks.
// But if we want chunks of text:
const stream = response.body.pipeThrough(textDecodeTransform());
// …
});
The <slot>
element can now participate in a
flat layout tree, with UA style display: contents
. Before this change, a CSS selector would not match a <slot>
element. Now selectors match and children will inherit from the <slot>
element.
HTTP-Based Public Key Pinning (HPKP) was intended to allow websites to send an HTTP header that pins one or more of the public keys present in the site’s certificate chain. It has very low adoption, and although it provides security against certificate misissuance, it also creates risks of denial of service and hostile pinning.
To defend against certificate misissuance, web developers should use the Expect-CT
header, including its reporting
function. Expect-CT
is safer than HPKP due to the flexibility it gives site operators to recover from configuration
errors, and due to the built-in support offered by a number of CAs.
We expect to remove this in Opera 56 (Chromium 69).
AppCache over HTTP is deprecated. AppCache is a powerful feature that allows offline and persistent access to an origin. Allowing AppCache to be used over non-secure contexts makes it an attack vector for cross-site scripting hacks. Removal is expected in Opera 56 (Chromium 69).
Two Webkit-prefixed CSS properties were been removed in this release - -webkit-box-flex-group
and
-webkit-box-lines
. Percent (%) values are no longer accepted by the -webkit-line-clamp
property.
If you’re interested in experimenting with features that are in the pipeline for future versions of Opera, we recommend following our Opera Developer stream.
]]><canvas>
The new rendering context allows for more efficient rendering of an image to a
canvas. Instead of creating an <img>
element and rendering it to the canvas,
which causes multiple copies of the image to be stored in memory, an
ImageBitmap
object can now be displayed in the canvas directly, effectively by
transferring ownership of the pixel data to the canvas element.
const image = await createImageBitmap(imageBlob);
const canvas = document.createElement('canvas');
const context = canvas.getContext('bitmaprenderer');
context.transferFromImageBitmap(image);
Since the source image can be fetched and decoded asynchronously, this can be used in e.g. a WebGL game to load textures on the fly, without jank.
The CSS Typed Object Model exposes the values of CSS properties to scripts as typed JavaScript objects, instead of as CSS formatted strings. This makes it more straight-forward to write code that reads and manipulates CSS properties, and the code will typically execute faster.
Opera 53 implements the object model for a subset of CSS properties.
A new asynchronous clipboard API has been added that is simpler to use than the
old document.execCommand("copy")
API, and that integrates with the
Permissions API.
With this new API, copying some text to the clipboard is as simple as
try {
await navigator.clipboard.writeText("some text");
} catch {
console.error("Could not copy text to the clipboard!");
}
and reading text from the clipboard is equally easy:
const text = await navigator.clipboard.readText();
console.log("Clipboard text:", text);
For more information about the new API and how to use it, see Unblocking Clipboard Access.
The AudioWorklet
API provides a synchronous JavaScript execution context that allows developers
to programmatically control audio output with lower latency and higher stability
than the legacy ScriptProcessorNode
API, which will be deprecated eventually.
Demos and other AudioWorklet
resources can be found at Google Chrome
Labs.
add
and
accumulate
keywords will not throw errors. They will soon be supported by
Opera.calc()
expression is now supported in media queries.rgb()
and rgba()
functions now allow floating point values.deviceorientation
, deviceorientationabsolute
and devicemotion
events
are now by default restricted to top-level documents and same-origin frames,
as if the feature policy
for those features was set to self
.Request
object now supports the keepalive flag,
which can be set when constructing the object, and which allows a fetch to
continue after the tab is closed.The new AbortSignal
and AbortController
makes it possible to cancel fetches.
const controller = new AbortController();
const { signal } = controller
// Start a fetch controlled by the above controller.
fetch(url, { signal }).then(...);
// Abort the fetch.
controller.abort();
<textarea>
and <select>
elements now support the autocomplete
attribute.click
,
input
and change
, in that order. Previously, the input
event was not
fired.window.focus()
to switch focus to another
page, fullscreen mode is exited.MediaStreamTrack.getCapabilities()
returns a MediaTrackCapabilities
object with details about the browser’s
capabilities.Function.prototype.toString()
function now returns the function’s source code exactly as it was written,
including white-space and comments.try
/catch
statement can now omit the binding parameter.
In other words, if the actual exception is not interesting, the short form
try { ... } catch { ... }
can be used.String.prototype.trimStart()
and String.prototype.trimEnd()
are now
supported for trimming leading and trailing white-space characters from
strings, complementing the existing String.prototype.trim()
. The
non-standard trimLeft()
/trimRight()
functions remain as aliases for
backwards compatibility.Array.prototype.values()
is now supported. It returns an iterator over the
values at each index in the array.The grid-
prefix has been removed from the CSS gutter properties grid-gap
,
grid-row-gap
and grid-column-gap
, whose names now become just gap
,
row-gap
and column-gap
.
The default value is normal
for all three. The old names remain as aliases
for the new names. The property column-gap
already existed as part of CSS
Multi-column Layout.
Elements with a transform property and with the display
property set to
table-row
, table-row-group
, table-header-group
, table-footer-group
,
table-cell
or table-caption
, are now containing blocks for fixed
position elements.
The autoplay
attribute is now only honored
when one of the following conditions are met:
same-origin
with a response whose type is CORS
. This is a security measure
recently added to the Fetch specification.FetchEvent.clientId
returns an empty string instead of null
when it isn’t
set, which can be the case for instance during a navigation request.RTCRtpSender
object now supports the dtmf
attribute, replacing the RTCPeerConnection.createDTMFSender()
function,
which will be deprecated eventually.The object-position
and perspective-origin
properties no longer accept
three-part values such as top right 20%
. Valid position values must have 1,
2 or 4 parts.
This also applies to basic shapes and gradients.
ImageCapture.prototype.setOptions()
has been removed following a change in
the specification.Document.prototype.createTouch()
and Document.prototype.createTouchList()
have been removed following changes in the specification.AudioParam.prototype.value
changes has been removed
following a change in the specification. To smooth the value of AudioParam
changes, use AudioParam.prototype.setTargetAtTime()
.If you’re interested in experimenting with features that are in the pipeline for future versions of Opera, we recommend following our Opera Developer stream.
]]>With paintlets it is possible to write a script that does the background rendering of an element. This can be more flexible than generating images on the server side or using client side generated data urls for backgrounds.
The image below is a rather silly example of what can be done with paintlets.
This effect was accomplished by adding a paint
module
to the paint worklet, and then, in its paint()
method, drawing on
the supplied ctx
as if it had been a canvas. It is also possible to
supply configuration from CSS to the script, something that can make
paintlets easier to use than other ways to generate background images.
<!-- demo.html -->
<style>
textarea {
background-image: paint(circlearcpainter);
--circle-arc-pixel-size: 24;
}
</style>
<!-- Textarea is a good demo since it can be resized. -->
<textarea></textarea>
<script>
CSS.paintWorklet.addModule('circle_arc.js');
</script>
The module itself is defined with the javascript below.
// circle_arc.js
class CircleArcPainter {
circle_arc(ctx, x, y, radius, angle) {
let angleInRad = (angle * 0.9 / 2 + 10) * Math.PI / 180;
ctx.fillStyle = 'yellow';
ctx.beginPath();
ctx.arc(x, y, radius,
angleInRad, Math.PI * 2 - angleInRad, false);
ctx.lineTo(x, y);
ctx.closePath();
ctx.fill();
}
static get inputProperties() { return ['--circle-arc-pixel-size']; }
paint(ctx, geom, properties) {
const css_prop = properties.get("--circle-arc-pixel-size");
const size = css_prop ? parseInt(css_prop.toString()) : 100;
ctx.fillStyle = "black";
ctx.fillRect(0, 0, geom.width, geom.height);
for (let x = 0; x < geom.width/size; x++) {
for (let y = 0; y < geom.height/size; y++) {
const circle_size = Math.abs(
Math.sin((x + y) / 6)) * size / 4 + size / 12;
const opening = Math.random() * 90;
this.circle_arc(ctx, (x + 0.5) * size, (y + 0.5) * size,
circle_size, opening);
}
}
}
}
// Register our class under a specific name
registerPaint('circlearcpainter', CircleArcPainter);
With the new Server Timing API there is a well defined way for servers to pass performance information to the browser through HTTP headers. With this information web pages can make even better informed decisions.
In Developer Tools this information is displayed in the Network view by selecting the resource and activating the Timing tab.
:any-link
pseudo selector can be used to style both visited and non-visited
links at the same time.rgb
and hsl
functions now take an optional
fourth alpha
value, making them identical to rgba
and hsla
.display:
contents
allows an element to wrap other elements without creating a box for
itself.assignedElements()
on <slot>
elements, which acts
like assignedNodes()
but only returns element nodes.HTMLAnchorElement.relList
will now give an accurate mapping of the rel
attribute.document.all
can no longer be overwritten.sync-xhr
will allow a site to block synchronous
XMLHttpRequests. Synchronous XMLHttpRequests block scripts until a
server has responded which can make them harmful for the user
experience. If a site embeds untrusted, possibly malicious, content
(think ads), this adds a tool to lock that content down a bit.Request.destination
is added to give service workers a better understanding of why a
resource is fetched.toJSON()
method has been added to PerformanceResourceTiming
,
PerformanceLongTaskTiming
and TaskAttributionTiming
.If you’re interested in experimenting with features that are in the pipeline for future versions of Opera, we recommend following our Opera Developer stream.
]]>In order to mitigate the recently disclosed security vulnerability called “Spectre”, the following restrictions that affect the functionality of the web platform have been applied:
SharedArrayBuffer
functionality has been removed.performance.now()
has been clamped to 100μs.Both of the above serve to reduce the possibility of performing a side-channel attack to read sensitive user data, by removing potential ways to acquire a high-precision timing source.
Responsive web applications are often written using CSS media queries or window.onresize
to build components that
adapt to different viewport sizes. Both of these are global signals, thus requiring the overall viewport to change in
order for the site to respond accordingly. With the Resize Observer API, web
applications can observe changes to sizes of elements on a
page with finer granularity.
The following code snippet uses the Resize Observer API to observe size changes to an element:
const ro = new ResizeObserver((entries) => {
for (const entry of entries) {
const cr = entry.contentRect;
console.log('Element: ', entry.target);
console.log(`Element size: ${cr.width}px × ${cr.height}px`);
console.log(`Element padding: ${cr.top}px / ${cr.left}px`);
}
});
// Observe one or multiple elements
ro.observe(someElement);
import.meta
Developers writing JavaScript modules often want access to host-specific metadata about the
current module. To make this easier, support for the import.meta
property has been added. This property currently exposes the module URL as import.meta.url
. Authors of libraries may want to
access the URL of the module being bundled into the library to more easily resolve resources
relative to the module file as opposed to the current HTML document.
offset-path
property can now be
animated, allowing for even more creative ways to move an element
around.text-decoration-skip-ink
CSS
property has been added, allowing developers better control over how underline
and overline decorations are drawn
when they cross over a glyph.PointerEvent
with pointerType === "mouse"
are now fractional, resulting in more precise mouse measurements.\p{…}
and \P{…}
for regular expressions that have the ‘u
’ flag set has been added, allowing developers to create more
powerful Unicode-aware regular expressions.Intl.NumberFormat.prototype.formatToParts()
has been added to
assist with local-aware formatting of strings produced by internationalization formatters.preload
value
for <video>
and <audio>
elements has been changed to metadata
in order to reduce bandwidth and resource usage,
by only loading resource metadata and not the media resource itself. This aligns Opera with other browser
implementations."NotSupportedError"
DOMException
is now thrown when
a media element’s playbackRate is set to a value
that is not supported
by Opera, like a negative value.getUserMedia()
now returns a rejected
Promise with a DOMException
or OverconstrainedError
when there is an error.Request
can now be specified using
the cache
option and accessed using
Request.prototype.cache
. For reload requests, the
"reload"
value is now also exposed through said property.camera
and
microphone
permissions.HTMLElement.prototype.focus
, accepts the
preventScroll
option, to allow focusing an element without scrolling to it.transform-box
CSS property has
been added. This allow developers better control over what percentages are resolved against in the
transform
and
transform-origin
properties on SVG elements.AudioWorklet
, an API that
exposes low-level audio processing capability
to support custom AudioNodes, is now available behind the experimental flag.RTCPeerConnection
interface now supports addTrack()
for single stream use cases, as well as removeTrack()
, getSenders()
and ontrack
. A minimal version of the
RTCRtpSender
interface is now also supported.window.alert()
no longer
brings a backgrounded tab to the foreground but instead shows the alert when the user switches to the background tab.getMatchedCSSRules
has
been removed. Developers
can use the Blink polyfill instead.Element
can no longer host more than one Shadow Root. This
behavior has been deprecated since Opera 32.nextHopProtocol
and Paint Timing API,
the non-standardized chrome.loadTimes
API is being deprecated.If you’re interested in experimenting with features that are in the pipeline for future versions of Opera, we recommend following our Opera Developer stream.
]]>New JavaScript module import syntax allows developers to dynamically load code into modules and scripts at runtime. This makes it possible to load parts of an application lazily, which can be used to improve application start-up time or to avoid loading seldom used parts until they are actually needed.
button.addEventListener('click', event => {
import('./dialogBox.js')
.then(dialogBox => {
dialogBox.open();
})
.catch(error => {
/* Error handling */
});
});
The code example above shows how to use the import(specifier) function to import JavaScript after an event.
Asynchronous iteration is made more convenient with the addition of asynchronous generator functions
async function* generateAsync() {
while(condition) {
const an_object = await produceAnObject();
yield an_object.some_property;
}
}
and the asynchronous iteration protocol
for await (const item of generateAsync()) {
console.log({ item });
}
Read more about this syntax and its possibilites on Jake Archibald’s blog or in this summary of the “Asynchronous Iteration” proposal.
Using the new Device Memory API developers can estimate the total amount of RAM on the device. This information could be used to deliver a suitably scaled version of the site to lower-end devices, or help put collected performance data into context to better understand the behavior of a web application.
beforeprint
and afterprint
events as part of the printing standard, allowing developers to annotate the
printed copy and edit the annotation after the printing command is done
executing.Promise.prototype.finally()
,
a callback can now be registered to be invoked after a Promise
has been
either fulfilled or rejected.Intl.PluralRules
API
allows developers to build applications that understand pluralization of a
given language by indicating which plural form applies for a given number and
language.MediaStreamTrack.applyConstraints()
is now supported for local video
MediaStreamTracks
,
including tracks obtained from
getUserMedia()
,
capture from media elements
or screen capture.getItem()
rather than an anonymous getter, so attempting to access a
key using getItem()
will now return null
rather than undefined
. Thanks
to Intel for the contribution!getItem()
, removeItem()
, and clear()
are now enumerable. Thanks
to Intel for making this happen!EventTarget.addEventListener()
and EventTarget.removeEventListener()
when
the callback argument is not an EventListener
, null
, or undefined
.Promise
type now return a rejected promise instead of throwing an
exception when the property access fails./deep/
or >>>
selector,
as well as ::shadow
,
are now removed
from CSS dynamic profile, following their
deprecation
in Opera 32 / Chromium 45.HTMLAllCollection
,
HTMLCollection
,
HTMLFormControlsCollection
and
HTMLOptionsCollection
are no longer enumerable, so they are now left out of calls to Object.keys()
or for-in
loops.If you’re interested in experimenting with features that are in the pipeline for future versions of Opera, we recommend following our Opera Developer stream.
]]>There are times when a web page want to adapt to the user’s network. To help a web page with that information there is now the Network Information API which can give a rough indication of the user’s network, including bandwidth and round-trip time.
This is still under development but the parts that exist today, rtt
,
effectiveType
and downlink
, should be enough for most use cases.
The plan is that these signals will also become available as HTTP request headers and enabled via Client Hints.
Previously a font file could only contain one weight/variant of a font. So you had one file for bold, one file for semi-bold, one for stretched, one for normal and so on. With OpenType Font Variations you can combine those variants efficiently in a single file. This should allow web pages to be smaller and load faster than before, while also giving them access to an infinite number of font variants (not that I recommend using more than a few in a page).
Animation based on Amstelvar and Decovar
Stretch, style, and weight can be adjusted by giving a numerical value
to
respective CSS property,
or by setting the
font-variation-settings
CSS property.
It is now possible to stream animations directly from a canvas or video element to anything that can handle a stream. Previously it was only possible to stream from capture hardware on the computer.
Through the W3C Media Capture from DOM Elements API you can
call captureStream()
on any HTMLMediaElement
and send it to another
video element or stream it remotely through WebRTC. Or process or manipulate
it in other ways.
<data>
and
<time>
. We
used to have support for the time element but it has been missing for a
while. This gives web developers a standardized way to store
machine-readable data.#RRGGBBAA
and #RGBA
.accept-language
headers from language settings to make it more likely users receive content in the language they expect.getStreamById
method on RTCPeerConnection
has now been removed.SharedWorker.workerStart
has been removed, following its deprecation and removal from other major browsers.<ol>.start
has been set to 1.If you’re interested in experimenting with features that are in the pipeline for future versions of Opera, we recommend following our Opera Developer stream.
]]>At Opera, we have always preferred SSH Keys authentication method over weak passwords. However, as we have had to oversee thousands of servers grouped in multiple services with SSH Keys distributed among them, it turned out that there was no suitable solution on the market which met our needs. Available systems either were not prepared to work in a flexible manner or required a lot of hassle to distribute custom-built packages with authentication modules. That lead us to develop our own solution.
Using flexible backend structures for synchronization, database and Web UI, we have created a system that not only allows us to manage accesses but also to tune plenty of possible SSH hardening settings per user. Diverse service specification (from servers using only server-to-server access and to servers available for all users that are in our company LDAP) resulted in a very flexible solution. Still, it has not lost it’s simple and responsive interface - because of the people who work on this!
Today, after few years of using, fixing and expanding our system with more features, we would like to allow everyone to use SKA - SSH Key Authority. No matter if you manage two or three servers or you’re part of LDAP-based organization with thousands of users, SSH Key Authority can help you in maintaining access and proper auditing of changes every day.
SKA is released under Apache License 2.0, and is hosted on GitHub.
The system will continue to be developed and updated and we plan to add a few more features, such as support for two-factor authentication with time-based tokens, in the near future.
Finally, a big kudos to the Opera IT Team for development and bug catching, and to all Opera Employees for their extensive testing and feature requests!
]]>While no generalized metric perfectly captures when a page is loaded in all cases, First Paint and First Contentful Paint are invaluable numbers to measure critical user moments during loading. To give developers better insight into their site’s loading performance, the new Paint Timing API exposes metrics that capture First Paint and First Contentful Paint.
font-display
Downloadable web fonts are often used to create more visually rich web experiences. Historically,
Opera has delayed rendering text until the specified font is available, to ensure visual
correctness. However, downloading a font can take as long as several seconds on a poor connection,
significantly delaying the time until a user sees content. Opera now supports the CSS font-display
property as part of an @font-face
descriptor, allowing developers to specify how and when Opera displays text content while downloading fonts.
Starting in Opera 47, PasswordCredential
also contains the user’s password, alleviating
the need for a custom fetch()
to access a stored password.
Some changes has also been made to
to better align with the work being done in the Web Authentication Working Group.
This includes the deprecation of requireUserMediation
, which has been renamed to preventSilentAccess
.
PushManager.supportedContentEncodings
can be used to detect where it can be used.PushSubscription.expirationTime
is now available, notifying sites when and if a subscription expires.pointermove
and mousemove
events are now delivered once per animation frame (analog to requestAnimationFrame
), matching the current functionality of scroll and TouchEvents
.:focus-within
CSS pseudo-class is now available, affecting any element the :focus
pseudo-class affects, as well as any element with a descendant affected by :focus
.frames()
timing function is now available, allowing for proper frame-based animations. (Animations where all frames have exactly the same duration, including its first and last frames.)InputEvent
now allows user input to be managed by script, enhancing the details provided to editable elements.beforeunload
dialog triggered when the user leaves a site is now only shown if the frame attempting to display it has ever received a user gesture or user interaction, though the beforeunload
event is still dispatched regardless.getElementsByTagName()
now accepts qualified names in response to an update to the DOM specification./deep/
combinator which has been removed from the current specification, now behaves like the descendant combinator, which is effectively a no-op. Previously it would enable selecting descendants in shadow trees.Navigator.vibrate()
now immediately return false
if the user hasn’t explicitly tapped on the frame or any embedded frame, matching existing behavior for cross-origin iframes
.WEBKIT_KEYFRAME_RULE
and WEBKIT_KEYFRAMES_RULE
have been removed in favor of the unprefixed standardized APIs, KEYFRAME_RULE
and KEYFRAMES_RULE
.WebKitAnimationEvent
and WebKitTransitionEvent
has been removed from document.createEvent()
.NodeIterator.filter
and TreeWalker.filter
no longer wrap JavaScript objects, and .prototype
has been removed from window.NodeFilter
.RTCPeerConnection.prototype.getStreamById()
is being removed, and a polyfill is recommended as a replacement.SVGPathElement.prototype.getPathSegAtLength()
has been deprecated, and will be removed in Opera 49 (Chromium 62), since it has been been removed from the SVGPathElement interface in the SVG2 spec.Headers.prototype.getAll()
has been removed from the Fetch API in line with its removal from the spec.If you’re interested in experimenting with features that are in the pipeline for future versions of Opera, we recommend following our Opera Developer stream.
]]>Opera now supports animated PNG, or APNG for short. APNG is a file format that works similarly to GIF. The difference is that APNG is smaller and supports both 24-bit images and 8-bit transparency. It has become quite popular recently, particularly since Apple adopted the APNG file format for the iOS 10 iMessage apps. APNG was also supported in Presto-based Opera 12.
Watch the APNG demo and learn more about this format.
This was actually added in Opera 44, but we missed to write about it!
This might still be a bit rough in the edges, but in general SVG favicons should now work in Opera! (Not yet in Chromium.) To see this in action, check some of the WHATWG standards which use SVG favicons. Firefox also supports SVG favicons, and it was also supported in Presto-based Opera 12.
Opera now throttles expensive background tabs. This reduces the processing power required for background tabs and improves battery life and browsing performance. Try it out yourself with this background timer throttling demo.
As a web developer, you also can help with reducing the work that your page or app does while being
in a background tab by using
requestAnimationFrame
instead of timers to drive animations, and the
Page Visibility API to stop
unnecessary work when the page is hidden.
The Service Worker navigation preload API enables the browser to preload navigation requests while a service worker is starting up. See Speed up Service Worker with Navigation Preloads by Jake Archibald for more information.
MediaError
’s message
property to obtain greater detail about a MediaError produced by <audio>
or <video>
.WritableStreams
are now available as part of the Streams API for processing streams of data, while providing a standard abstraction for writing streaming data to a sink with built-in backpressure and queuing.ReadableStreams
and WritableStreams
via the pipeTo()
and pipeThrough()
methods, allowing easier consumption of streaming data.requestAnimationFrame
, ensuring that input is processed as part of the document lifecycle and creating a more efficient and adaptive input response.worker-src
Content Security Policy directive restricts which URLs may be loaded as a Worker
, SharedWorker
, or ServiceWorker
.<dialog>
element has changed from display: inline to block by default to better align with the spec.hover: on-demand
and any-hover: on-demand
media queries have been removed.decodeAudioData
now detaches the given ArrayBuffer before decoding, removing all content from the object and making it unable to be reused or examined.-internal-media-controls-cast-button
CSS selector has been removed in favor of the Remote Playback API.-internal-media-controls-text-track-list*
CSS selectors have been removed in favor of custom-built video controls.SVGTests.requiredFeatures
attribute has been removed following its removal from the spec.initDeviceMotionEvent()
and initDeviceOrientationEvent()
were removed in favor of DeviceOrientationEvent()
and DeviceMotionEvent()
, following a spec trend of moving away from initialization functions and toward constructors.sample
property will now be included in a violation report (and associated SecurityPolicyViolationEvent
object) if a report-sample
expression is present in the violated directive.Notification.requestPermission()
from non-main frames has been deprecated to align the requirements for notification permission with requirements for push notifications, and ease friction for developers.If you’re interested in experimenting with features that are in the pipeline for future versions of Opera, we recommend following our Opera Developer stream.
]]>IndexedDB 2.0 is now supported in Opera, making it simpler to work with large data sets in the browser. IDB 2.0 features new schema management, bulk action methods, and more standardized handling of failures.
The structure of a site’s database has large performance impacts and can be difficult to change. To simplify updates, object stores and indexes can now be renamed in-place after a refactoring. Sites can also use more natural keys without worrying about a performance penalty thanks to binary keys, which allow compact representations for custom keys.
For more information about what IndexedDB 2.0 brings, check out Bevis Tseng’s summary.
Third-party content, such as advertising, that automatically redirects
the page can annoy users and create security issues. Because of this,
developers are able to put third-party content inside sandboxed
iframes
to prevent this behavior. However, in some cases this type of
content needs to navigate the top-level page when clicked, like a
standard advertisement.
To address this, Opera 45 now supports the new iframe
sandbox
keyword
allow-top-navigation-by-user-activation. This
keyword gives sandboxed iframes
the ability to navigate the
top-level page when triggered by user interaction, while still
blocking auto-redirects.
removeRange()
, a new function, developers can now programmatically remove a specified text Range.selectionDirection
, selectionStart
, and selectionEnd
, Opera will now return null
when it would have thrown an InvalidStateError DOMException
.setBaseAndExtent()
now throws an IndexSizeError DOMException
to better align with spec.DocumentType
node inputs, setBaseAndExtent()
, extend()
, and collapse()
now throw InvalidNodeTypeError DOMException
to better align with spec.Selection.addRange()
now ignores an additional range if it overlaps with an existing range, rather than merging the two ranges.getRangeAt()
now always returns a new Range
with position normalization.Workers
and SharedWorkers
can now be created using data:
URLs. Note that such workers will have an opaque origin.PointerEvents.getCoalescedEvents()
allows developers to access all input events since the last time a PointerEvent
was delivered, making it easier for drawing apps to create smoother curves using a precise history of points.download
, fullscreen
and remoteplayback
buttons using the new ControlsList API.color-gamut
Media Query.display: flow-root
is now supported. An effect of flow-root is that it envelops floats, and can be used as a replacement for the clearfix hack.SVGPoint
, SVGRect
, and SVGMatrix
have been transferred to new interfaces outside of Geometry.PointerEvent.tangentialPressure
and PointerEvent.twist attributes are now supported on Opera for Mac to provide more information to stylus devices and painting apps.AudioContextLatencyCategory
enables the developer to easily make conscious tradeoffs between latency, power, and CPU efficiency.Apple-interchange-newline
, Apple-converted-space
, Apple-paste-as-quotation
, Apple-style-span
, and Apple-tab-span
have been deprecated as they are non-standard CSS classes.usemap
attributes now use case-sensitive matching rather than compatibility caseless to better align with spec.cancelBubble
is now considered an alias to stopPropagation()
when set to true, and does not do anything when set to false.VTTRegion
interface functions, addRegion()
and removeRegion()
, have been removed from the WebVTT spec and are therefore being removed from Opera.data:
URLs have been deprecated to further protect users from spoofing and phishing attempts.HTMLEmbedElement
or HTMLObjectElement
can no longer be called as a function, since the legacy caller has been removed.sampleRate
member of an AudioBufferOptions
dictionary instead of a context
argument, simplifying the interface and emphasizing that AudioBuffers
can be shared between AudioContexts
.FileReaderSync
API has been deprecated in service workers, as the service worker spec requires all types of synchronous requests to be initiated outside of a service worker.motion-path
, motion-offset
, and motion-rotation
CSS properties have been removed in favor of the new versions: offset-path
, offset-distance
, offset-rotate
.AudioSourceNode
interface has been removed as it was not part of the WebAudio spec.webkitdropzone
attribute has been removed as it was not widely adopted.If you’re interested in experimenting with features that are in the pipeline for future versions of Opera, we recommend following our Opera Developer stream.
]]>CSS Grid is now available. CSS Grid supports a two-dimensional grid-based layout system, optimized for responsive user interface design. Elements within the grid can be specified to span multiple columns or rows. Elements positioned in a CSS grid can also be named, making layout code easier to understand. There are lots of excellent resources available to learn more:
The WebAssembly API has been enabled by default, allowing developers to run near-native code in the browser without a plugin. WebAssembly is essentially a better replacement for asm.js. We believe it has the potential to bring browser games to the next level.
Opera 44 also adds support for the Credential Management API. This gives users a simpler sign-in process across devices and provides websites with more control over the usage of credentials. The website can use password-based sign-ins via this API. Once logged in, users will be automatically signed back into a site, even if their session has expired.
caret-color
property enables developers to specify the color of the text input cursor.text-decoration-skip: ink
can be used to make underlines skip descenders, the portion of letters that extend below the text’s baseline.text-decoration
properties are now available, allowing developers to specify visual effects such as line color and style.AudioContext.getOutputTimestamp()
method enables developers to synchronize DOMHighResTimeStamp
and AudioContext.currentTime
values.AudioBufferSourceNode
, OscillatorNode
, and ConstantSourceNode
now inherit from AudioScheduledSourceNode
, consolidating functionality.cancelAndHoldAtTime
function cancels future AudioParam
events with times greater than or equal to cancelTime
, allowing developers to preserve the value of the scheduled time in a direct way.OfflineAudioCompletionEvent
and AudioProcessingEvent
.Response
class now supports the .redirected attribute to help web developers avoid untrustworthy responses and reduce the risk of open redirectors.padStart
and padEnd
formatting tools enable text padding, facilitating tasks like aligning console output or printing numbers with a fixed number of digits.on<event>
attributes, ongotpointercapture
and onlostpointercapture
are now part of the GlobalEventHandlers
mixin.fieldset.elements
now returns an HTMLCollection
instead of an HTMLFormControlsCollection
to better align with spec.usemap
attribute now requires case-sensitive matching.client.postMessage(message, transfer)
in a service worker will now use MessageEvent
instead of ServiceWorkerMessageEvent
.<keygen>
element has been removed, causing it to no longer display any controls nor submit form element data, to align with other browsers.<cursor>
element has been removed, but cursor icons can still be set via the cursor
CSS property.HTMLEmbedElement
and HTMLObjectElement
, so the interfaces will now throw exceptions rather than having their instances called as functions.webkit
-prefixed IndexedDB global aliases have been removed, after their deprecation in M38 (Opera 25).webkitClearResourceTimings()
, webkitSetResourceTimingBufferSize()
, and onwebkitresourcetimingbufferfull
has been removed from the Performance
interface, in favor of clearResourceTimings()
, setResourceTimingBufferSize()
, and onresourcetimingbufferfull
.-internal-media-controls-text-track-list*
and related pseudo-elements are deprecated and will be removed in M59 (Opera 46). Use custom controls for custom text track selection.webkitCancelRequestAnimationFrame
has been removed in favor of cancelAnimationFrame
.webkit
prefix has been removed from AudioContext
and OfflineAudioContext
.If you’re interested in experimenting with features that are in the pipeline for future versions of Opera, we recommend following our Opera Developer stream.
]]>Opera will now prerender pages that are typed into the address bar, before the user hits enter, which can make the page finish loading much sooner.
If you can predict what the user’s likely next navigation will be, you can start a prerender from
the current page by using <link rel=prerender>
.
This has actually been supported since Opera 15, but this seemed like a good opportunity for a
reminder. 🙂 For more information about how to use prerender
and other resource hints, see
Resource Hints – What is Preload, Prefetch, and Preconnect?
by Brian Jackson.
Opera now allows users to select link text instead of always dragging the link (like in Opera 12). The user can select by dragging left or right, and start a drag-and-drop by dragging up or down.
Not being able to select text to copy and paste is often frustrating for users. But in some cases it
can be justified, like if a link is being used to act as a button, or if text is used only for
decoration. To disable text selection in general, CSS user-select: none
can be used.
When some content changes above the viewport (e.g., an image is loaded), what used to happen is that
the rest of the content is pushed down. With the scroll anchoring
intervention the scroll position is updated to keep the content currently in the viewport stable.
This behavior can be disabled for a specific subtree or for the whole page by using CSS overflow-anchor: none
.
See this video for a comparison with and without this
intervention.
position: sticky
,
a new way to position elements. A position: sticky
element is relatively-positioned, but becomes
position: fixed
after the user reaches a certain scroll position. This results in smoother scroll
without jumps compared to trying to achieve the same thing with scroll
events and JavaScript.
For more info see this article
by Paul Kinlan.system-ui
is now supported, which matches the platform’s default UI font.touch-action: pinch-zoom
is now supported.Referrer-Policy
headerThe new Referrer-Policy
HTTP header allows sites to forward site traffic by URL without leaking the user’s session identifier
or other private information.
keyboardEvent.isComposing
The KeyboardEvent
interface now supports the
isComposing
attribute
which returns true
if this event is fired between compositionstart
and compositionend
events.
ImageBitmapRenderingContext
can be used to reduce memory consumption and compositing overhead by rendering pixel data in the form
of an ImageBitmap
.
ConstantSourceNode
is a new audio source node that produces a constant output mixed with an AudioParam
.ChannelSplitterNode
interface attributes are now read-only: channelCount
, which is defined by numberOfOutputs
in createChannelSplitter()
, and channelCountMode
, which is set to explicit.PannerNode.rolloffFactor
now clamps to the nominal range of a PannerNode’s distance model to describe the volume reduction rate as the source moves away from the listener.audio
HTML element and decodeAudioData()
.decodeAudioData()
, expanding the variety of audio codecs supported by the Web Audio API.slotchange
events bubble, but no longer re-fires, at a slot
’s assignedSlot
.<script src="foo" type="something/something">
) was deprecated (generated a message in the console) in Opera 42 and now in Opera 43 are no longer pre-fetched. This typically results in less wasted fetches. <script data-src="foo" type="something/something">
is recommended instead.speedOfSound
, dopplerFactor
, and setVelocity
.RTCPeerConnection
now accepts iceTransportPolicy
as an RTCConfiguration
parameter as well as iceTransports
.RTCPeerConnection
is now available without a webkit prefix, though webkitRTCPeerConnection
still remains.reflected-xss
directive has been removed from Content Security Policy 2 since it was solely a wrapper for the X-XSS-Protection
header and provided no additional functionality.MediaStreamTrack.getSources()
method has been removed in favor of MediaDevices.enumerateDevices()
.referrer
directive is no longer supported in favor of the new Referrer-Policy
header mentioned above.MIDIMessageEvent.receivedTime
has been deprecated in favor of Event.timeStamp
, since Event.timeStamp
now supports high-resolution monotonic time instead of epoch time.If you’re interested in experimenting with features that are in the pipeline for future versions of Opera, we recommend following our Opera Developer stream.
]]>Opera is now the first of the major browsers to add a built-in currency converter.
The user select the price they want to convert on the page and Opera will automatically show it in their local currency. Opera 42 supports conversion in 32 currencies based on daily values from the European Central Bank.
Currency symbols are supported, but for ambiguous symbols (e.g. “$”, “kr”) we recommend using the ISO 4217 alphabetic code before or after the amount, as selectable text. This makes it unambiguous for Opera’s currency converter what to convert from, but it should also be helpful for end users and other tools in general.
<p>USD 50</p>
<p>995 SEK</p>
The PointerEvent
standard is now supported. It gives developers a unified API for handling “pointer” user input, whether that is mouse, touch, pen, etc. PointerEvent
also does not block scrolling, which should reduce jank during scrolling. When using TouchEvent
, the same performance benefit can be achieved using passive event listeners. The touch-action
CSS property is now also supported, to allow reacting to gestures such as panning. For more information, see Pointing the Way Forward by Sérgio Gomes.
The auxclick
event is now fired for non-primary mouse buttons, instead of click
.
async
and await
The async
and await
keywords are now supported, which allows writing Promise-based JavaScript with a neater syntax, and enables use of try
/catch
to catch rejected promises.
async function myFirstAsyncFunction() {
try {
const fulfilledValue = await promise;
}
catch (rejectedValue) {
// …
}
}
For more information, see Async functions - making promises friendly by Jake Archibald.
The hyphens
CSS property is now supported (on Mac), which can be used to enable automatic hyphenation when text is broken across multiple lines. Possible values are none
, manual
(initial value) and auto
.
For none
, hyphenation will not happen even when ­
is used. For manual
, hyphenation only happens for ­
. For auto
, automatic hyphenation happens, based on the language specified in the lang
attribute.
html { hyphens: auto; }
code { hyphens: manual; }
See demo.
once
event listener optionThe once
event listener option` is now supported, for a convenient way to only invoke an event listener once and then remove the listener.
element.addEventListener('click', function(event) {
// ...one-time handling of the click event...
}, {once: true});
For more information, see Once Upon an Event Listener by Jeff Posnick.
Persistent storage is now on by default.
new MediaStreamTrackEvent(type, options)
and new AudioNode(context, options)
constructors are now available.
document.write()
interventionCross-origin and parser-blocking scripts injected using document.write()
will no longer load over 2G connections. See this article by Paul Kinlan for more information.
click
event is no longer fired for non-primary mouse buttons. In most cases this is desirable; click
event handlers are most often intended to handle primary clicks. But if you need to handle non-primary clicks (e.g., middle mouse button), the auxclick
event can be used. The contextmenu
event can also be used to handle clicks that would open a context menu (typically right-click).BaseAudioContext
will replace AudioContext
in the Web Audio API.webkit
prefix.MediaStream
constructor is now available without prefix alongside the existing webkitMediaStream
.<script src="foo" type="something/something">
) is deprecated and will from M56 (Opera 43) no longer be pre-fetched. This typically results in less wasted fetches. <script data-src="foo" type="something/something">
is recommended instead.<textarea maxlength="">
and <textarea minlength="">
have been updated to count each line break as one character, instead of two.webkit
prefix has been removed from the imageSmoothingEnabled
property of CanvasRenderingContext2D
.If you’re interested in experimenting with features that are in the pipeline for future versions of Opera, we recommend following our Opera Developer stream.
]]>Want to get started with progressive web apps, but not sure where to start? This page will list the best resources we know of to help you understand Progressive Web Apps (PWAs), get started and learn things in depth.
Make sure to bookmark this page, as this is a living document that we’ll be adding to from time to time.
These articles introduce the concept of Progressive Web apps and serve as a jumping point to learn more about them.
One of the best things about PWAs are that they can be added to the home screen of your Android device, just like native apps can. This section contains relevant articles covering the ‘Add to Home Screen’ capability, as well as a few articles on how to make your web app UI running smoothly.
Progressive Web Apps are capable of running offline just like native apps. This is primarily through a feature called ‘Service Worker’. Service Workers are great, and besides being used to make web apps run offline, are also required for other functionality like Push Notifications and Background Sync.
Push notifications are helpful in notifying the user of important, relevant or timely events, even if your site isn’t open in the browser, or even when the browser is closed. With the Push API spec and Service Workers, you can finally implement this in your progressive web app too. Be careful not to spam your users!
This section lists out a number of tools which might end up saving you a lot of time and effort when coding up for your PWAs.
Take a look at what others are doing with Progressive Web Apps. Get inspiration and see how it has benefitted others in key business metrics.
Books to help you learn about PWAs.
Custom elements form the foundation of web components. The initial version of the API, also known as Custom Elements v0, has been supported since Opera 20 & Chrome 33. With the v0 API, a new custom element was defined using document.registerElement()
.
Since then, the spec has been updated based on web developer and browser vendor feedback. The improved API is called Custom Elements v1. The new hip & trendy way of defining a custom element is through customElements.define()
. The v0 API is now deprecated and will be removed in a future release.
For more details, check out Eric Bidelman’s custom elements guide.
ParentNode
& ChildNode
We now support the following convenience methods on ParentNode and ChildNode for working with DOM trees:
ParentNode.prototype.prepend()
ParentNode.prototype.append()
ChildNode.prototype.before()
ChildNode.prototype.after()
ChildNode.prototype.replaceWith()
CanvasRenderingContext2D.prototype.imageSmoothingQuality
CanvasRenderingContext2D.prototype.imageSmoothingQuality
allows developers to balance performance and image quality by adjusting resolution when scaling.
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
const image = new Image();
image.src = 'image.png';
image.onload = function() {
context.imageSmoothingEnabled = true;
context.imageSmoothingQuality = 'high'; // or 'low', or 'medium'
context.drawImage(image, 0, 0, 320, 180);
};
The BroadcastChannel API allows same-origin scripts to send messages to other browsing contexts. It can be thought of as a simple message bus that allows publish-subscribe semantics between windows, tabs, iframe
s, web workers, and service workers. Think of it as a simpler, same-origin version of the good ol’ postMessage()
API.
For more information, check out this article.
CacheQueryOptions
The full set of CacheQueryOptions
is now supported, making it easier to find the cached responses you’re looking for. Here’s the complete list of available options:
ignoreSearch
ignoreMethod
ignoreVary
cacheName
See Jeff Posnick’s excellent article for more information.
text-size-adjust
The text-size-adjust
property lets web developers control and disable the text autosizing feature which increases font sizes on mobile.
user-select
You can now use user-select
instead of -webkit-user-select
in CSS. The user-select
property makes it possible to specify which elements in the document can be selected by the user and how.
unload
handlers now blockedNavigations initiated in an unload
handler are now blocked. Instead, any prior navigation will continue. This matches the behavior in Firefox, and matches Edge’s behavior more closely than before.
Node.prototype.getRootNode
Sites can use Node.prototype.getRootNode(options) to obtain the root for a given node.
CECPQ1 is a post-quantum cipher suite: one that is designed to provide confidentiality even against an attacker who possesses a large quantum computer. It is a key-agreement algorithm plugged into TLS that combines X25519 and NewHope, a ring-learning-with-errors primitive. Even if NewHope turns out to be breakable, the X25519 key-agreement will ensure that it provides at least the security of our existing connections.
Note that this is only an experiment. In fact, the plan is to discontinue this experiment within two years, hopefully by replacing it with something better. See “Experimenting with post-quantum cryptography” for more details.
URL.createObjectURL
and URL.revokeObjectURL
are now deprecated in service worker contexts.
The MediaStream API dropped MediaStream.prototype.ended
a long time ago. Its usage has been deprecated since Opera 32 & Chromium 45. As of this release, the ended
event is no longer supported.
Similarly, the File API spec once removed the FileError
interface. It has been deprecated since 2013. Any usage of FileError
triggered a warning in the DevTools console since Opera 40 & Chromium 53. As of this release, FileError
is no longer supported.
Support for the non-standard TouchEvent.prototype.initTouchEvent
has been removed, after being deprecated since Opera 36 & Chromium 49. Use the Touch
and TouchEvent
constructors instead.
To more closely match other browser’s behavior, window.external.IsSearchProviderInstalled
and window.external.AddSearchProvider
(originally intended to add search engines programmatically) are now both no-ops. This functionality was never implemented in Safari. In IE10, these methods are (mostly) no-ops: IsSearchProviderInstalled
always returns 2
, and AddSearchProvider
always returns S_OK
. Firefox still implements this, but notes that it may be removed at any time.
KeyboardEvent.prototype.keyIdentifier
has been removed. Use KeyboardEvent.prototype.key
(or its polyfill) instead.
If you’re interested in experimenting with features that are in the pipeline for future versions of Opera, we recommend following our Opera Developer stream.
]]><input pattern="…">
The u
flag (which stands for Unicode) is now applied to any regular expressions compiled through the pattern
attribute for <input>
and <textarea>
elements.
This enables more Unicode-friendly features, and generally more sensible behavior.
<input pattern="[🍪🎂🍩]">
<!--
The input only validates when it is either 🍪, 🎂, or 🍩.
(Previously, those strings would fail validation.)
-->
<iframe sandbox="allow-presentation">
The Presentation API defines the nw allow-presentation
sandboxing flag for <iframe sandbox="…">
which gives web developers control over whether an iframe
can start a presentation session. By default, the Presentation API is disabled in sandboxed <iframe>
s.
All browser vendors finally agreed on the Shadow DOM spec, called v1. The new Shadow DOM APIs include:
Element.attachShadow
Event.prototype.composed
Event.prototype.composedPath()
HTMLSlotElement
Slotable.prototype.assignedSlot
For more information, check out Hayato Ito’s overview of the differences between Shadow DOM v0 and v1.
MediaStreamTrack
Constraints APIThe following MediaStreamTrack
methods are now supported:
getCapabilities
getConstraints
getSettings
applyConstraints
These APIs allow getting, setting, and querying constraints on a MediaStreamTrack
.
Additionally, MediaTrackConstraints
instances now have video
and audio
properties.
getUserMedia
The navigator.mediaDevices.getUserMedia()
API, which returns a promise, is now supported. Also, the unprefixed version of navigator.getUserMedia()
(which uses callbacks) is now available. For more info, see Sam Dutton’s write-up.
filter
The CSS filter
property is now supported in its unprefixed form. For now, -webkit-filter
is still supported as an alias for filter
. A demo featuring SpongeBob SquarePants is available.
-webkit-user-select: all
Chromium already supported -webkit-user-select
, the prefixed version of user-select
, but it didn’t recognize every value the spec defines. As of this update, the -webkit-user-select: all
is supported. This property/value pair makes it so that if a selection would contain only part of an element, then the selection must contain the entire element including all its descendants.
For example, if you apply this on all <p>
elements, and you then try to select a single word in a paragraph, the entire paragraph is selected automatically.
The CSS Tricks Almanac entry for user-select
features a nice demo.
3D-positioned descendants are now flattened by an ancestor that has opacity. Previously that didn’t happen if that ancestor also specified transform-style: preserve-3d
. A visual demo explains this better than words ever could.
will-change: transform
Generally, all content is re-rastered when its transform scale changes, unless will-change: transform
is applied to it. In other words, will-change: transform
effectively means “please apply the transformation quickly, without taking the additional time for rasterization”. This only applies to scaling that happens via JavaScript manipulation, and does not apply to CSS animations. For more information, see the technical summary for this change. A demo is available.
Synthetic events (i.e. those who are created by JavaScript, and are thus untrusted) are now prevented from executing their default action. click
is the only exception for backwards compatibility with legacy content. This change matches the spec and other browsers.
The HTML spec was changed so that <label>
elements aren’t form-associated elements anymore. As a result, support for the form
attribute on <label>
elements was removed, and labelElement.form
is now an alias for labelElement.control.form
(instead of mapping to the form
attribute value).
HTTP/0.9, the predecessor to HTTP/1.x, is now deprecated, and will be removed in a future release. Its replacement, HTTP/1.0, was standardized 20 years ago.
Support for DHE-based TLS ciphers has been removed, after being deprecated in Chromium 51 & Opera 38. Servers should upgrade to ECDHE ciphers instead.
A long time ago, the File API spec was changed to remove the FileError
interface. It has been deprecated since 2013. As we’re preparing to remove this interface entirely in a future release, any usage of FileError
now triggers a warning in the DevTools console.
The TextEncoder API no longer accepts an argument that specifies the encoding. Instead, it always uses UTF-8. If an argument is passed, it is ignored. This means that new TextEncoder('utf-16')
and new TextEncoder('utf-16be')
no longer work.
If you’re interested in experimenting with features that are in the pipeline for future versions of Opera, we recommend following our Opera Developer stream.
]]>ES2016 introduces an arithmetic operator equivalent of Math.pow(base, exponent)
, in which the lefthand-side expression serves as the base value, and the righthand-side expression serves as the exponent.
4 ** 3;
// → 64
let value = 4;
value **= 3;
value;
// → 64
Response
construction with ReadableStream
It’s now possible to construct your own ReadableStream
instances and to use them as bodies for Response
objects, including from within service workers. This functionality is part of the Fetch standard. See Jeff Posnick’s write-up for details, or Jake Archibald’s in-depth blog post on streams for even more info.
Requests made using the Fetch API can use a specific referrer policy, which affects the value of the Referer
HTTP header that’s sent to remote servers. This can be done by adding a referrerPolicy
property to the options object passed to fetch
, which accepts the following values:
'no-referrer'
'no-referrer-when-downgrade'
'origin'
'origin-when-cross-origin'
'unsafe-url'
See Ehsan Akhgari’s write-up for a straight-forward explanation of these values.
fetch(url, { 'referrerPolicy': 'no-referrer' })
.then(function(response) {
// Do something with the `response`…
});
CanvasRenderingContext2D
instances now support the filter
property that applies effects to primitives drawn to the canvas. The filter
value is parsed in the same way as CSS filters.
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
context.filter = 'saturate(6.3)';
createImageBitmap
optionsAn ImageBitmapOptions
object is an options
object that can be passed to createImageBitmap()
.
createImageBitmap(image, options).then(function(response) {
// Do something with the `response`…
});
The available options are:
colorSpaceConversion
indicates whether the image is decoded using color space conversion. Either 'none'
or 'default'
(default). The value 'default'
indicates that implementation-specific behavior is used.imageOrientation
indicates whether the image is presented as-is or flipped vertically. Either 'flipY'
or 'none'
(default).premultiplyAlpha
indicates that the bitmap color channels are premultiplied by the alpha channel. One of 'none'
, 'premultiply'
, or 'default'
(default).resizeWidth
indicates the new width.resizeHeight
indicates the new height.resizeQuality
specifies an image scaling algorithm. One of 'pixelated'
, high'
, 'medium'
, or 'low'
(default).<track kind>
values are no longer treated as subtitlesInvalid values for <track kind>
were previously treated as 'subtitles'
. This means that any new values would be treated as subtitles by old browsers. Chromium now uses 'metadata'
instead as the “invalid value default”, to avoid browsers showing <track>
s with unrecognized values.
HTMLMediaElement.prototype.srcObject
The srcObject
property on HTMLMediaElement instances enables associating a MediaStream
to a media element using a simple assignment. Previously, achieving this required first obtaining a URL associated to the MediaStream
, and then associating this URL to the media element.
Currently, only MediaStream
objects are accepted because it is the only type currently supported by other major browsers, but Chromium aims to support more types in the future. (Per the spec, a MediaProvider
can be a MediaStream
, a MediaSource
, or a Blob
.)
AudioParam
instances now have readonly minValue
and maxValue
properties that specify the minimum and maximum values the AudioParam
can have. The value
is clamped to lie in this range
Automation methods for the position and orientation coordinates of PannerNode
and the position{X,Y,Z}
, up{X,Y,Z}
, and forward{X,Y,Z}
vectors of AudioListener
are now available. This allows smooth changes in the coordinates using AudioParam
methods.
Chromium now implements the reduction
property on DynamicsCompressorNode
instances as a readonly float, matching the spec. Previously, the value was represented as an AudioParam
.
IDBKeyRange.prototype.includes()
The includes
method on IDBKeyRange
instances tests whether or not a key exists within the bounds of the range. Until all other browsers support this natively, a polyfill can be used.
RTCCertificate
s in IndexedDBRTCCertificate
s are self-signed certificates used in the DTLS handshake when setting up a connection with an RTCPeerConnection
. Chromium now allows web applications to store RTCCertificate
s by implementing the structured clone algorithm. This means RTCCertificate
s can be saved and loaded from an IndexedDB database, saving the cost of generating new certificates for new sessions.
The Performance Observer API is essentially a streaming interface that can be used to gather low-level timing information asynchronously, as it’s gathered by the browser. Marc Cohen’s write-up does a great job explaining it.
When using alert()
, confirm()
or onbeforeunload
, Chromium’s old behavior was to block JavaScript while waiting for the result, but to allow all declarative animations to continue. As of this update, Chromium makes all main-thread tasks (such as <marquee>
and CSS 2D animations) also pause during this interval.
Here’s a demo showing the impact of alert()
on a CSS 2D animation.
contain
mentThe CSS contain
property indicates that an element and its contents are, as much as possible, independent of the rest of the document tree. This allows the browser to recalculate layout (contain: layout
), style (contain: style
), paint (contain: paint
), size (contain: size
), or any combination of them for a limited area of the DOM and not the entire page. For more details, check out Paul Lewis’ explanation.
font-variant-caps
& font-variant-numeric
The CSS properties font-variant-caps
and font-variant-numeric
are now supported.
meter { -webkit-appearance: none; }
Previously, there was no way to completely disable the browser’s default rendering of <meter>
elements and to restyle them using CSS. As of Chrome 52 / Opera 39, this finally became possible using -webkit-appearance: none
.
The static position of absolutely-positioned children used to be set as though they were a 0×0 flex item. The CSS Flexible Box Layout spec has since been updated to take such children fully out of flow and to set the static position based on align
and justify
properties. Check out the demo.
Alternative Services allow an origin serving an http://
or https://
resource to nominate additional origins that the client can choose to request the resource from instead when making subsequent requests. This can be used, for example, as a protocol upgrade mechanism, for connection pooling, or for load balancing.
This is done through the Alt-Used
HTTP header.
For example, if the resource at https://origin.example.com/foo
returns the following response headers:
Alt-Used: https://alternate.example.net
…and https://origin.example.com/bar
is requested afterwards, then the browser may fetch either https://origin.example.com/bar
or https://alternate.example.net/bar
.
strict-dynamic
The 'strict-dynamic'
source expression allows scripts loaded via nonce- or hash-based whitelists to load other scripts. Deploying CSP is now simpler than ever before. A demo is available.
X-Frame-Options
is now ignored when present inside a <meta>
tag, e.g. <meta http-equiv="X-Frame-Options" contents="DENY">
, matching the spec. Use an HTTP header (X-Frame-Options: DENY
) instead.
A non-standard and little-used variant of the postMessage()
API is being deprecated, specifically postMessage(message, transferables, targetOrigin)
. Use postMessage(message, targetOrigin, transferables)
instead.
The ended
event & property and the onended
event handler have been removed from the Media Capture and Streams spec and are now being deprecated in Chromium.
If you’re interested in experimenting with features that are in the pipeline for future versions of Opera, we recommend following our Opera Developer stream.
]]>If you follow the Progressive Web App scene you’ve probably already seen multiple examples of the Air Horner app in mobile browsers such as Opera Mobile and Chrome for Android. However it is not easy to get your favorite PWA (such as Air Horner) to be a primary citizen on your favorite desktop operating system.
Browser makers are working on getting better support for PWAs in desktop, but it’s not quite ready yet. This is why I started the PWAify project. This project is powered by Node.js and Electron, which already have a huge community and provide ease of use for new and experienced developers. The PWAify project is still in the early experimental stages — use it at your own risk.
The goal of the PWAify project is to provide a solution for PWAs on desktop while we wait for browsers to develop support. In addition this project allows developers to experiment, give better feedback to browser makers, and improve the Web App Manifest specification.
To create a sample app with PWAify follow the latest documentation on GitHub.
Currently the simplest use case is to just provide an HTTPS URL to the remote app. For example:
pwaify https://voice-memos.appspot.com/
There are some advanced options for the tool as well. You can specify the platform of your choice and an icon file once you are ready to distribute your app:
pwaify https://voice-memos.appspot.com/ \
--platforms=darwin \
--icon chrome-touch-icon-384x384.icns
When you run the pwaify
command it fetches and processes the manifest.json
for the given PWA. Some early work is in place to simplify the process of generating desktop icons by looking at icons provided in the manifest. The current downside of the pwaify
approach is the size of the executable for each application, but there is already huge list of Electron apps out there that require the download. In my mind I try not to worry about the download size. Instead I try to focus on the future of progressive apps instead.
Here’s an example of a simple progressive web app working on Windows, Ubuntu and OS X:
For the purposes of the screenshot I rebuilt the Voice Memos app on all platforms. It is also possible to setup a build server for your application once you want to add multi-platform support. You can use the PWAify tool today by pointing it at your own app or one of the apps in the pwa.rocks list.
Besides the Voice Memos application, my second favorite example is the 2048 tile game, by using PWAify with 2048-opera-pwa.surge.sh I can easily jump back to the saved game state after I restart the application.
The PWAify project is open source, so you can fork it and extend functionality. For example you can add auto-update and custom UI for your application. In the spirit of keeping as much as possible of the application on the web I suggest only introducing minimal changes to the application wrapper. Treat the PWAify’d version of the app just like any other desktop browser would treat it.
Recently Microsoft announced that the Windows Store will start supporting the W3C Web App Manifest. This will help you distribute the applications for Windows Store users, but you don’t have wait for Microsoft. There is nothing stopping you today to build one version of your web application and get it in the hands of your users on desktop. You can even go beyond Windows users and distribute on other operating systems.
Some future ideas for the PWAify project itself include:
manifest.json
from the server, which would allow dynamically changing certain properties of the application by updating the manifest.json
remotely.Tools like Electron and PWAify put yourself in control of application distribution while allowing you to use the latest web technologies. Write one awesome progressive web application for multiple distribution targets!
Feel free to file an issue against the PWAify project if you have other ideas that will benefit the future of progressive web apps.
]]>Last week, I presented at Google’s Progressive Web App Dev Summit in Amsterdam. In my talk, I covered novel UX patterns in progressive web apps, emerging markets and Opera Mini compatibility, and introduced a couple of new ideas to solve some of the discoverability issues progressive web apps struggle with.
My talk there was followed by a browser panel, in which I participated as well. You can find a video of it below.
If you have any questions about progressive web apps, do check out our other articles or ask a question.
]]>We’re excited to release a Labs build of Opera for Android with support for two experimental features that enhance the discoverability and use of progressive web apps.
Earlier this month, Alex Russell wrote:
Wouldn’t it be great if there were a button in the URL bar that appeared whenever you landed on a PWA that you could always tap to save it to your homescreen?
We have been exploring this idea for a while, and are previewing an early version of this idea in this Labs build.
If you load a site that passes the criteria to qualify as a progressive web app, a small phone icon is shown to the left of the URL bar, labeling it as such. Note that this is different from the “add to home screen” install banner, which requires a user engagement check.
Give it a spin on one of the sites listed on pwa.rocks and let us know how it goes.
A few weeks ago, there were some discussions around the lack of visibility of URLs in progressive web apps. What to do for instance if you want to find out the URL of a page inside a progressive web app?
I want people to be able to copy URLs. I want people to be able to hack URLs. I’m not ashamed of my URLs …I’m downright proud.
Also here, we’ve been doing some explorations: initially, we thought we could surface the URL by requiring the user to long-press anywhere in the web app and show it in a context menu. However, after further investigations, this turned out to be harder than expected — e.g. what to do with form fields, where context menus serve a different purpose? Then we had the idea to somehow connect it to the “pull-to-refresh” spinner, as a secondary gesture to the left or right. You can see in the video how this works. Quite useful, and it’s discoverable, without getting in the way.
These are just early proposals, and we may or may not include them in a final Opera for Android build. In the meanwhile, we’re happy to hear your feedback: give it a spin and let us know what you think.
]]>Hi! Please tell us a little about yourselves.
Constance Okoghenun, @okoghenun: I’m from Lagos, Nigeria. I work as a UI Specialist for Konga, Nigeria’s largest online marketplace helping tens of thousands of merchants selling over 200,000 products on our platform. At Konga I help to build products that provide great shopping experiences for our customers. I also help organise and speak from time to time at Usable (formerly UXLagos), where designers, developers and enthusiasts here in Lagos and, recently, Abuja Nigeria, have monthly conversations about how to improve the experiences we deliver to our customers.
I love the web and have been working on the web for about 5 years now. It has been a delight to see it mature and become better as a platform, enabling me and other developers and designers to offer increasingly better experiences to our users.
Eugene Mutai, @kn9ts: I’m from Nairobi, Kenya. I’m currently working for/with Andela. Andela is new startup, with a revolutionary model founded on the principle that brilliance is evenly distributed around the world, but opportunity is not. Andela is transforming the brightest minds in Africa into world-class developers, who then are fused into startups or Fortune 500 companies all over the world. I am currently working as a full-time full-stack developer for RBI, a Canadian multinational company (and one of the largest fast food brands in the world) whose brands include Burger King and Tim Hortons.
I have been a web enthusiast and developer for at least four years now. I am also a Google Developer Expert on Web Technologies, attributed by one of my community contributions such as being the lead manager of the Nairobi JavaScript Community growing it to over 450 members, and I regularly speak at a variety of programming and tech meetups.
We met online because you were talking about Progressive Web Apps (PWAs) on Twitter. What is it about them that excites you?
Eugene: I haven’t talked about progressive web apps on Twitter only; I have evangelized about them wherever I got the chance to. My last talk on progressive web apps was at Nairobi Tech Week, the largest tech event in Sub-Saharan Africa this year. I’ve also written a blog post about it — Progressive Webs Apps: Africa may be the biggest gainer — which is a robust answer to the above question.
In summary, I am excited that finally web technology is earning its place on mobile. PWAs are changing the way web apps have been viewed: a secondary, less-intuitive and less-performant alternative to using a mobile app’s service. While settling in on mobile, leveraging its greatest advantage over native mobile apps — very low friction in usage — it may solve problems that make the daily usage of native mobile applications in Africa a challenge, e.g. the size of apps and requirement of new downloads every time they are updated, among many others.
On a side note, as a passionate web developer, it’s been tons of losing development battles with Android developers, and I’m finally unquantifiably happy we are becoming a force to be reckoned with.
Constance: There are lots of exciting things about progressive web apps and the new range of possibilities they bring to the web. If I were to pick just one feature of progressive web apps that I’m super-excited about, it’s the ability to detect and handle offline / unreliable network conditions with service workers.
This is vitally important, as web apps finally now have a space on user home screens — so being slow to load or breaking because of network unreliability is not acceptable. And, in my part of the world, where getting good connectivity speeds on mobile is pretty difficult, service workers present an opportunity for developers to build apps that aren’t just fast, but which are also resilient.
An example of this is Konga’s PWA: when we detect that the user is offline and has items in their shopping cart, we provide them the option of going through an offline checkout experience, thereby preventing internet connectivity from becoming a bottleneck when they shop with us.
Constance, what are the characteristics of the Nigerian market that make PWAs an appealing development route?
Constance: Data is pretty expensive in Nigeria. Over the last 12 months, data prices have become lower, but not low enough to make the majority of the Nigerian internet market data insensitive. On average, it costs about NGN 1000 (about $5) to get 1GB of data. In a country where minimum wage is about NGN 18,000 (about $90) that is already 5.6% of the monthly salaries. So, as you can imagine, Nigerians are extremely data sensitive.
A huge number of smartphone users do not download their apps and never update their apps. Instead, people side-load apps and other content from third parties who have these apps downloaded to a PC and for a small fee will install apps from their computers to users’ phones. Among younger, more tech-savvy Nigerians, the app Xender is really popular as it allows them skip app downloads and just send it from one device to another.
As you can imagine, these workarounds for downloading apps from the app store create all sorts of problems for developers — from app distribution, to getting the most recent, bug-free versions of apps in the hands of users.
With PWAs giving web apps a place in the user’s home screen, without the download overhead of native apps, this becomes more exciting; developers in Nigeria can now always give a great and up-to-date experience to their users. Also, they have the ability to re-engage with their users via push notifications, and improve conversions.
Eugene, is it similar in Kenya?
Eugene: The average pay is Kenya is approximately $300-400 a month. On a good month, with tough choices made in budgeting, one gets to barely spare $30 after covering monthly expenses. It costs $1 for 135MB of data and $5 for 1GB of data. On average a user has 15+ apps installed (extrapolated from global mobile app install stats). At least half of these apps demand to be updated after a day or two (best case scenario is weekly).
If each app is on average 15MB, this means 150MB of data used up on downloads from re-installations without considering each user’s unique personal data usage. If at least $5 of data is being used for updates only, I’d say this is very challenging to a user if they are is to keep up with these updates. Some updates also are just bug fixes — I kid you not, no one would update the app for that (including me) unless the bug is not negligible.
Constance, what were the challenges of making the Konga PWA?
Constance: The biggest for us at Konga was time: we had only about six weeks to build a stable version of our app and we did it in time. Thinking offline-first was something we had to get used to doing. Apart from those, building our PWA wasn’t very difficult.
And how was user feedback for your PWA, Constance?
Constance: Though we haven’t officially launched the app yet, feedback has been overwhelmingly positive. It requires 84% less data to complete the first transaction vs the previous mobile web experience, and 63% less data for initial load, and we are not even done building and optimizing the app. It certainly will be a delight for our users when it gets into their hands.
To quote our CEO Shola Adekoya:
We estimate that with our new light, super-fast, UX-rich browsing capability, customers’ data consumption will fall dramatically.
Back to Eugene now. How was user feedback to your evangelism, Eugene?
Eugene: I’m been satisfactorily happy with the feedback I have got from my blog post and all my talks on PWAs. In the last talk I gave, at one point a member of the audience who was an Android developer (among many others in the room) who became anxious and disturbed that PWAs would render native Android developers jobless. The best answer I could give was we’re only moving in the mobile department since we’re now in a sharing economy, and not kicking them out, and the hype was to only create awareness to all web tech developers and not to cause panic among Android developers.
Do you think those Android developers’ concerns are justified? Do you see PWAs becoming dominant in the kind of markets that you work in?
Eugene: No, I don’t think they’re justified. First of all, awareness about PWAs is something that needs to be urgently addressed. Every time I give a talk on PWAs the facial response of the audience is like “Why didn’t I know about this? This is revolutionary!” and the question that always follows is “Where can I find more information and learn about PWAs?”
Furthermore, I don’t envision PWAs being dominant in the mobile apps space, but I see their presence vastly increasing once knowledge about them is in the hands of every web developer. PWAs solve a number of challenges for entrepreneurs: it’s a cross-platform solution, there’s very low friction compared to native apps, there’s no APK size limitation, and so on.
Constance: I also don’t think that fear of PWAs is justified. I think both PWAs and native SDKs are just tools in a good developer’s tool box. There are use cases where a PWA would be perfect, and there are others for which a native app is best. Each developer just needs to evaluate when to use what. Moreover, there is nothing stopping native mobile developers from working on the web; Konga’s PWA was built by both our current mobile team (Android and iOS developers) and our front-end team.
What else would you like to see browsers do to make PWAs more widely available and accepted?
Constance: Lots of developers use frameworks; it would be great to see vendors reach out to the maintainers of those frameworks and work with them improving their tooling and processes, so that it’s super easy to build PWAs with their frameworks and tools. More importantly, I’d like to see a more consistent implementation of the web specs that PWAs are based on.
Eugene: Opera and Google Chrome have done a wonderful job so far. When talking about PWAs being installable, the “Add to home screen” option isn’t that convincing. I’d like to see something like “Install Web App” or “Add to Your Apps”.
Browsers should offer APIs so that PWAs can compete on an equal footing as native mobile apps. I am aware these missing APIs such as Bluetooth, NFC, USB access and so forth are currently being worked on. So this is just a matter of time.
Thank you both!
]]>