8 months ago
Saturday, June 13, 2009
Fixing Maximize Bookmarklets in Safari 4
When Apple released the Safari 4 public beta at the end of February, I discovered an annoying bug: the little Javascript bookmarklet that I use to maximize the browser window didn’t work anymore. I find the behavior of Safari’s green zoom button extremely annoying. It doesn’t expand the browser window to fill the screen, but instead only resizes it to fit the content of the current page. This is typically some weird height and width that causes every subsequent page you view scroll vertically and horizontally.
Of course, Apple’s Human Interface Guidelines for Resizing and Zooming Windows explain that the zoom button isn’t actually supposed to expand the window to fill the screen, but toggle the window between a standard application-defined size and a user-defined size. Regardless of what the human interface guidelines say, though, I still don’t like what it does, and I’m apparently not the only person. There are hundreds of pages asking how to fix it.
In case you’re wondering, the bookmarklet is:
I simply put the bookmarklet first in my bookmark bar, which lets me maximize Safari by hitting ⌘1. I didn’t write this little snippet of Javascript, but I can’t remember where I found it and there are versions of it all over the Web.
So, back to the real story. Upon further investigation, I determined that the bookmarklet only works when the Safari window contains a single tab. If the window has more than one tab, the bookmarklet doesn’t work. I wasn’t the only one who noticed this bug, as I found the post Safari 4 breaks bookmarklets? on This Is the Green Room.
I figured the problem would be fixed when the final version of Safari 4 was released, but when that happened this past week, the problem remained. Googling a bit, I found WebKit bug 24218, which notes that the Javascript window.moveTo and resizeTo methods do not work in Safari windows with more than one tab.
Since the bug was marked unconfirmed and didn’t have any activity on it for months, I decided to download the WebKit source and try to track down the cause of the problem. I checked out the WebKit code and built it according to the instructions. After digging a bit and setting some breakpoints, I figured out that the following method in WebCore/page/DOMWindow.cpp implements window.moveTo in the Javascript engine:
{
if (!m_frame)
return;
Page* page = m_frame->page();
if (!page)
return;
if (m_frame != page->mainFrame())
return;
FloatRect fr = page->chrome()->windowRect();
FloatRect sr = screenAvailableRect(page->mainFrame()->view());
fr.setLocation(sr.location());
FloatRect update = fr;
update.move(x, y);
// Security check (the spec talks about UniversalBrowserWrite to disable this check…)
adjustWindowRect(sr, fr, update);
page->chrome()->setWindowRect(fr);
}
This method basically does some adjustments with the screen and window rectangles, and then finally invokes Chrome::setWindowRect to perform the actual move of the window. Stepping through the method when clicking the bookmarklet with one and two tabs showed that all the rectangle calculations were resulting in the same values regardless of the number of tabs. The invocation of setWindowRect simply wasn’t moving the window when there was more than one tab.
After a bit of indirection through some other classes, Chrome::setWindowRect calls WebChromeClient::setWindowRect in mac/WebCoreSupport/WebChromeClient.mm:
{
NSRect windowRect = toDeviceSpace(rect, [m_webView window]);
[[m_webView _UIDelegateForwarder] webView:m_webView setFrame:windowRect];
}
This method basically translates the rectangle coordinates between the scale used by the window and the WebView itself, and then sends setFrame:windowRect: to WebUIDelegate. This delegate is the bridge between WebKit’s Javascript engine and the actual Cocoa browser window (I think), and WebKit is passing it the correct coordinates to move the window. WebUIDelegate, though, simply doesn’t move the window as it should when there is more than one tab, which leads me to believe the bug is in Safari itself, not WebKit.
I updated the WebKit bug and filed a new bug for Safari on Radar. I thought for a moment that this behavior might actually be intentional, to prevent pages from resizing the browser window via Javascript when there are other open tabs that would be affected. But since the bookmarklet works in Safari 3, I tend to doubt this new behavior is by design as it would break a lot of existing Javascript. Scripts embedded in an actual page are similarly unable to move or resize the window when there’s more than one tab open, so the problem doesn’t have anything to do with the Javascript being in a bookmarklet.
Follow Sakuzaku on Twitter. Click here.




Sam
Saturday, June 13, 2009
10:39 pm
A good example of why this might be intentional is something like Adobe’s download manager. Safari has a weird way of handling whether a link is opened in a new window or a new tab. In the case of adobe’s downloader, it gets opened in a new tab, and then tries to resize down to a smaller download window size - which would kill the rest of your tabs.
Whatever the issue is, every other browser on the planet seems to work fine - so Safari needs to figure it out.
Maciej Stachowiak
Sunday, June 14, 2009
3:25 am
Doesn’t sound intentional to me. What’s the Radar bug number?
joem
Sunday, June 14, 2009
9:51 am
I wonder if this is related to a problem I’ve had with the official Delicious bookmarklet ever since I started using Safari 4. In Safari 3, the bookmarklet caused a new, small window to pop up with the Delicious.com tagging stuff inside. But now in Safari 4, the same bookmarklet creates a new tab, instead, and doesn’t resize anything. (At least in this instance the fact that it doesn’t resize is a good thing.)
I should note that this behavior with the Delicious bookmarklet is the same regardless of having multiple or just one tab open.
Cody Robbins
Sunday, June 14, 2009
2:37 pm
@Maciej: The Radar bug number is 6970303.
@joem: The problem only occurs when there is more than one tab in the window, so I think whatever’s going on with the Delicious bookmarklet is something different. What’s the Javascript in it, out of curiosity?
joem
Monday, June 15, 2009
1:23 pm
Not sure if this’ll come through your comment system, but here goes. This is the Delicious bookmarklet:
I guess the
window.openbit should be making a new window instead of a new tab, but it isn’t.Cody Robbins
Tuesday, June 16, 2009
9:15 am
@joem: Maybe this is related to your problem: http://littlegreenfootballs.com/article/32911_Safari_4_Ups_and_Downs.
joem
Tuesday, June 23, 2009
2:08 pm
Yeah. Sounds like it.
sam1am
Monday, July 13, 2009
11:20 am
Seen any progress on your bug report? I also filed one with Apple (#6970616) but haven’t seen a peep on it.
lolzilla
Tuesday, July 28, 2009
11:58 pm
This is so annoying, we need a fix. :sad2:
Cody Robbins
Friday, August 7, 2009
10:15 pm
@samiam: Yeah, they marked mine as a duplicate of #6537031, which they said they are aware of and fixing. We’re at Safari 4.0.2, though, and it’s still not currently fixed.
Byron McCollum
Tuesday, August 11, 2009
3:22 pm
Still broken in 4.0.3.
Cowicide
Saturday, September 5, 2009
2:29 pm
This is crazy… still broken in latest Safari… what?!!
Ryan
Sunday, October 18, 2009
3:26 pm
If you’re running your own WebKit build, you can override Safari’s behavior. Modify
WebChromeClient::setWindowRect()inWebChromeClient.mm:{
NSRect windowRect = toDeviceSpace(rect, [m_webView window]);
[[m_webView _UIDelegateForwarder] webView:m_webView setFrame:windowRect];
// Safari ignores window.resize/move if multiple tabs are open.
// We can force the resize with this snippet from WebDefaultUIDelegate.m.
[[m_webView window] setFrame:windowRect display:YES];
}
I’ve barely tested it, but it seems to work well.
Cody Robbins
Monday, October 19, 2009
6:32 pm
@Ryan: Wow, nice!