miscoded

the web is a hack

Subscribe to RSS feed

Dragonfly feedback follow-up

I've gone through the comments on my previous Dragonfly post, here's a more-or-less organised overview. I've reported BitBucket issues on the requests I found really good - sorry about this bit of favouritism, but I encourage everyone to report issues themselves and they should all be considered on their merits whether reported by insiders or end users wink. I hope nothing important is missing, and sorry about the lack of proper credits on several points.

General feedback and feature requests
  • JavaScript profiling is a required feature, both timing and memory usage (planned).
  • Make UI take up less space (not sure what the plans are, but I'll let the designers know that several users thought the new design requires too much space..)
  • Add a mode that does not require reloading to get script context, and optionally let Dragonfly tab/window follow current opera tab automatically. (This is DFL-811 internally - because I personally think several other things are more important I have not created a twin issue at BitBucket but feel free to do so.).
  • Show IFRAME contents in DOM view (This is DFL-74 internally, I've cloned it to unable to traverse IFRAMES/FRAMES on BitBucket in case somebody wants to pick it up wink)
  • Multiline console (seems shift-enter isn't enough smile - and seems shift-enter to type multiple lines has disappeared in the experimental version anyway..)
  • Picture previews in CSS/DOM inspectors
  • With OS's "Group similar taskbar buttons" enabled, avoid being grouped with the main window's taskbar button
  • Being able to run Dragonfly in a tab inside Opera
  • Being able to use the JS console as an interpreter, not attached to a page.
  • Optional mode that strips vendor prefixes from CSS if Opera knows the non-prefixed attribute
  • A ruler or grid in the utilities, suggested by Daniel Hendrycks. Daniel, since you're on BitBucket perhaps you can add some issues there for these requests?

F.V. requests "native appearance and behaviour, keyboard accessibility and consistency". Well, that would be good but it's a bit too high-level to be truly useful. I've had a quick look at keyboard accessibility, and came across a few issues:Probably more, it would be good to get more eyes (or rather fingers, actually) on this.

Specific minor requests / bug fixes

  • Show User JavaScript filenames in UI, requested by lucideer. Though I first filed a BitBucket issue with some further testing I concluded it's a core problem, so send file name of user scripts through scope in OnNewScript is the one that needs fixing.
  • More efficient pane resizing
  • Parts of DF become inaccessible when docked in a small window - enable scrolling the entire UI
  • Copy script address - reported here
  • Cleaner network request view (or default to "raw") - now, the entire network feature will see a lot of work as shown under "Core upgrade: Resource Monitor" on the Dragonfly roadmap. As a consequence I guess the UI will change dramatically, and probably for the better.
  • There's a redraw issue with the inspection area under "Scripts" - can't find a bug here, though it's a really obvious problem so perhaps it's just too well known to bug it?
  • Easier way to open DF from menus - Michael, isn't right-click - > "Inspect element" easy enough though?


Questions (aka features that are there but too hard to find..)

Where is the place i can see global objects? All i can see is dom properties of html objects?


If you're at a breakpoint, clicking "Global scope" at the top of the call stack shows the properties of the global object in the inspection pane. If you are not at a breakpoint I guess the fastest way is to type "top" into the command line and click the returned link. Perhaps it would be more convenient if the inspection pane would default to showing the global object initially?

Originally posted by hellspork:

One crazy-awesome thing would richer support on "view source"/apply changes. If "view source" had a small selector pane, which allowed the picking of elements to edit.


Right-click, "Inspect element", expand it and double-click it. You can now edit the source of a specific element, in its current state in the DOM.

Originally posted by hellspork:

An engine improvement that allows the export of a loaded page, by bundling cache files with a master cache document?



That's basically what Opera's "File > Save" does if you save the page as a Web Archive. wink

Thanks for all the feedback! If you are on BitBucket please follow Opera Dragonfly, and feel free to report new issues there.

yandex error secrecy

The Yandex.ru webmail monitors itself and sends information about certain unexpected error conditions back to the server.

Most servers would just shrug and record the error. However, mail.yandex.ru seems a bit more..secretive about it. Perhaps because secrecy is a Russian thing, perhaps to amuse browser QA, or perhaps they just try to take web debugging to another level..? Here is the full text/plain document they return when the script says "Oops, an error happened":



Ssshhhh! Don't tell anyone.

Dragonfly improved - and a public roadmap!


I'm playing with the now official experimental Dragonfly version - if you haven't tested it yet, please do try it out. Great look, many nice UI touches.

What's even more important: we're also releasing a public roadmap so you can see what features are planned in the near- and long term. From comments on this blog and elsewhere I know that many of your are impatient with the slow development so far - mostly because much of the work needs to be done in Opera's core functionality where we add hooks for debugging and expose required information. It's been a long and complicated process which you've been spared from knowing about wink. The roadmap now gives a pretty clear overview of what features are pending core implementations, and what features Dragonfly should be able to go ahead and implement without waiting for core fixes.

So, a big step forward for Dragonfly itself, and more information about where it's headed.

Suggestions for the roadmap, anyone?

US Airways enter issue explained

, ,

I thought the analysis of the US Airways login issue would be best presented as an illustration of the code paths. Not that I'm an expert at UML, but Lucidchart made it pretty easy to draw this (as a bonus, I saw a few issues that might be Opera bugs and I'll go analyse them shortly).

So, we're looking at a script that wants to handle the enter key and call form.submit() itself, hence it needs to block the default "pressing enter submits form" action of the web browser. Note also that if you submit the form by pressing enter, the form's submit event will fire. The site is apparently confused because the submit event fires in Opera when you press enter, while it doesn't in other browsers. Why does the script fail to prevent the default action of the enter key?


This chart shows the code execution paths of the different browsers - how IE and Opera go on a detour by running the javascript: URL immediately, while Firefox and Chrome just finish the current thread.
For reference, here is the code again - now slightly re-ordered with the functions appearing in the order they are called:
<form method="post" action="#" onsubmit="javascript:return original_onsubmit();">
	<input type="text" value="Press Enter">
</form>
<script type="text/javascript">
if(document.addEventListener)
	{
	f.addEventListener('submit',foobar,false);
	userName.addEventListener('keypress',loginkeypress,false);
	}
else if(document.attachEvent)
	{
	f.attachEvent('submit',foobar);
	userName.attachEvent("onkeypress",loginkeypress);
	}

function loginkeypress(e)
	{
	e = e?e:window.event;
	if(e != null && e.keyCode == 13)
		{
		if(e.preventDefault)
			{
			e.preventDefault();
			}
		else if(window.event)
			{
			e.returnValue = false;
			}
		document.location = 'javascript:submitform()';
		return false;
		}
	}
function submitform()
	{
	if(f.onsubmit() != false)
		{
		f.submit();
		}
	}

function original_onsubmit()
	{
	if((typeof(window.event) != "undefined") && (window.event != null))
		{
		window.event.returnValue = true;
		}
	return true;
	}


function foobar()
	{
	alert('FAIL');
	}

</script>


And a textual description - for those of you who disable images smile :
  • kicking off from the keypress thread, we see that the keyCode is 13 and call preventDefault() to avoid submitting the form
  • within the event handler, we set document.location='javascript:submitform()'. This executes immediately (in IE and Opera - timing of JS URL execution test case), pausing the event thread to go into submitform()
  • inside submitform() we find the curiosity
    if(f.onsubmit() != false)

    Now, this actually calls one of the form's two submit handlers directly (there is one from the onsubmit="" attribute and another one from the addEventListener call). So, into it we step, and..
  • here it starts messing about with window.event
    if((typeof(window.event) != "undefined") && (window.event != null)) 
    { 
    window.event.returnValue = true; 

    presumably believing that window.event is the event object from the submit event. But remember, this stuff started with a *keypress* event and the call to onsubmit is not from Opera's internal event processing - it's from a JS URL running from "inside" the keypress.. So window.event is the event object of the keypress event, and setting returnValue=true effectively cancels the earlier preventDefault() call.

So when all is said and done, the javascript: URL has called form.submit() and returns to finish off the keypress event thread, the event is not cancelled after all, it triggers another form submit and thus the submit listener from the addEventListener call.

If IE and Opera follow the same path (except for IE not supporting preventDefault()), why doesn't the same thing happen in IE? Because of another quirk: in IE's event handler model, the return value from functions added with attachEvent() matters. It is significant that the loginkeypress function returns false (see returning false from event handler test case). Since the event handler in Opera was added with addEventListener(), what the function returns is ignored. Hence, in IE the state of the enter key's default action is toggled three times: disabled, enabled and then disabled again, while in Opera it's disabled and then enabled again.

To debug this, I used mostly opera.postError() / console.log(), some Dragonfly (in this case my brilliant colleague Kåre already did the harder part - extracting the problematic code from the live web site.). As the main problem was whether the key's default event should be disabled, adding quick postError() calls where the script set returnValue or called preventDefault() was the fastest way to understand the code flow. (Stepping through a script is more useful if there are many variables you would like to examine while the script runs. If you just need to understand the code flow, logging is usually faster than stepping).

As I always start debugging in Opera, I tend to litter scripts with opera.postError() calls. With a problem like this, where much of the work actually is figuring out why it works in other browsers, this can be a bit inconvenient. Luckily, I don't need to spend time on replacing all postError calls with console.log before running a test case in Firefox or Chrome - I simply paste this snippet above the code:
if(!window.opera){
	if(!window.console){var opera = { 'postError' :  function(){ for(i=0; this.OperaPostErrorsOK&&i<arguments.length;i++){this.OperaPostErrorsOK = confirm(arguments[i]); }   } , 'OperaPostErrorsOK' : true  };
	}else{var opera={'postError':console.log}}
}

and opera.postError() works cross-browser wink

Once we've understood the problem, the next step is naturally to find a way to solve it and make Opera work with the site. If the problem comes down to us breaking a spec or being generally buggy, how to fix it is obvious. With a problem like this, however, where all parts of the puzzle are basically by design and the issue is caused by an unfortunate combination of correctly supported features, finding the right fix is harder...

Ways we might solve this (doing just one of these would fix the site):


  1. make returning false from event listener cancel the default (even if the event listener was added with addEventListener() or attachEvent() )
  2. drop support for event.returnValue
  3. make preventDefault() "override" event.returnValue, if the former was called setting the latter to true is ignored
  4. un-set window.event while we execute javascript: URLs
  5. change scheduler to run javascript: URLs after current thread rather than immediately


Doing 1 means mixing even more of the IE-model's event handling logic into the standardised API - and we have absolutely no idea how much this would break, it would be a very risky change in terms of site compat.

2 is in line with other on-going attempts at dropping IE-specific event stuff (attach/detachEvent) but perhaps a bit premature. Also, IMHO event.returnValue is a nicer API than event.preventDefault() because the latter can not be undone. Doing this might have a small site compat impact: we might break scripts that rely on event.returnValue, but we believe that few scripts do since Firefox doesn't support it.

3 and 4 would be Opera-specific hacks. I find it highly unlikely that we would break anything, but we would diverge more from the other browsers in extremely subtle ways.

5 has a high risk of regressions - changing the order we run scripts in, is a deep change and in the past we've always needed several months to find and fix regressions. These regressions can also be extremely severe (for example the problem that prevents adding Facebook notes - now sitepatched in browser.js - is related to scheduling and probably a regression from changes made more than a year ago). This fix will however align our scheduling with Firefox and WebKit. (WebKit has very strange quirks here though - it seems for example calling location.replace('javascript:...') will make it ignore scripts in the rest of the document, or something like that.)

I recommend fix 5 - probably the most complicated fix, but in the long term the right thing to do.

...all that hard work because of a script that does entirely pointless and nonsensical things..?

That's browser QA in a nutshell. We're used to it.

how to improve your browser sniffing, NBC style

, , ,

One of the nice things about browser.js is that to maintain it, we need a "web code microscope" - a tool that zooms in on broken code and notifies us of any changes. And sometimes, looking at the microscopic changes and seeing sites change for the better is indeed a positive sight.

One such minor change just occurred at NBC, where browser sniffing used to break video playback in Opera. Investigating the change, we see that NBC has radically simplified and (IMO) improved their browser sniffing.

They used to include a .js file named direct.js which is no longer linked to (though still on the server), instead we have a new file indirect.js. Among other changes, this method:
this.browserDetection = function() 
{ 
var result = new Object(); 

var data = navigator.userAgent; 

if (data.indexOf("MSIE") != -1) 
result.app = "MSIE"; 
if (data.indexOf("Firefox") != -1) 
result.app = "Firefox"; 
if (data.indexOf("Safari") != -1) 
result.app = "Safari"; 

var index = data.indexOf(result.app); 
if (index == -1) 
return; 

// All other browsers we're concerned with, the version is AFTER the browser name... 
if (result.app != "Safari") { 
result.version = parseFloat(data.substring(index + (result.app.length + 1) )); 
} else { 
// why is safari such a pain? 
var startString = "Version/" 
var start = data.indexOf(startString); 
result.version = parseFloat(data.substring(start + startString.length, index)); 
} 
return result; 
}


was replaced by

this.browserDetection = function() 
{ 
var result = new Object(); 
return result; 
}


That's a terrific improvement. Please help the web by copying NBC-style browser sniffing into all your future projects! wink

Challenge: analyse how browsers enter US Airlines' membership website

, , ,

We recently investigated a bug report about this US Airways login form. Pressing enter to log in fails - but only in Opera. The question is why? Read on if you want to try to analyse the problem..

Read more...

missing @ping

,

I keep copying link targets from Google search and it seems they do more and more tracking of what links you actually click, meaning I get ugly links like this:

http://www.google.com/url?sa=t&source=web&cd=1&ved=0CBgQFjAA&url=http%3A%2F%2Fmy.opera.com%2F&ei=1MllTK-sOMXeOI-X_LAN&usg=AFQjCNG_uUyN-AGGcy7-8dfAnE4DhRIErQ&sig2=ahsl1Q5a_-bTgd2Dx69o5w


instead of the actual target URL. It's very annoying - and even more annoying is the fact that the ping attribute on A tags is postphoned beyond HTML5. If the proposed ping attribute was standardised and supported by browsers, Google could use that for its tracking metrics and I wouldn't have to remove all the junk sad

Amazon's surgical server-side sniffing

,

This is an excerpt of what Firefox gets when loading Amazon music sampler:
var amznJQ = {
        _a: [] , _s :  [], _d: [], _l: [], 
_o: [], _c: [], _cs: [],
        addLogical: function() { 
            this._l.push(arguments) 
        },
        addStyle: function() { 
            this._s.push( arguments) 
        },
        declareAvailable: function() {
            this._d.push(arguments)
        },
        available: function() {
            this._a.push(arguments)
        },
        onReady: function() {
            this._o.push(arguments)
        },
        onCompletion: function() {
            this._c.push(arguments)
        },
        completedStage: function() {
            this._cs.push(arguments)
        }

If Opera asks for that page, we get this:
var amznJQ = {
        addLogical: function ( ) { },
        addStyle: function() { },
        declareAvailable: function () {},
        available: function() {},
        onReady: function() { },
        onCompletion: function () {},
        completedStage: function() {}[/url]
That can only be described as [i]surgical[/i] browser sniffing: very carefully make sure functions are empty and variables not declared if the browser is not on your list of friends. Hey Amazon, browser sniffing is uncool even though you pretend to be surgeons :(