Merge MC -> JM
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 10 Nov 2011 12:06:26 -0800
changeset 82948 3ef25a8cf1728c475a7a0a51b0aa26394ec5f0b6
parent 82947 1ade8548963c6e86a5923295f7a37199fa961bd9 (current diff)
parent 81768 a41fa578cfc111f8d63a2ca38d0293f8cbde54c7 (diff)
child 82949 8b9d7d9b2325c89e51ffecce0ba333ad4bfb7a45
push id519
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 00:38:35 +0000
treeherdermozilla-beta@788ea1ef610b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone11.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge MC -> JM
browser/components/sessionstore/test/browser/browser_476161.js
browser/components/sessionstore/test/browser/browser_476161_sample.html
browser/devtools/styleinspector/csshtmltree.xhtml
browser/devtools/styleinspector/cssruleview.xhtml
browser/themes/gnomestripe/browser/Geolocation-16.png
browser/themes/gnomestripe/browser/Geolocation-64.png
browser/themes/gnomestripe/browser/Go-arrow.png
browser/themes/gnomestripe/browser/Info.png
browser/themes/gnomestripe/browser/KUI-close.png
browser/themes/gnomestripe/browser/Makefile.in
browser/themes/gnomestripe/browser/Privacy-16.png
browser/themes/gnomestripe/browser/Privacy-32.png
browser/themes/gnomestripe/browser/Privacy-48.png
browser/themes/gnomestripe/browser/Privacy-64.png
browser/themes/gnomestripe/browser/Secure.png
browser/themes/gnomestripe/browser/Security-broken.png
browser/themes/gnomestripe/browser/Toolbar-small.png
browser/themes/gnomestripe/browser/Toolbar.png
browser/themes/gnomestripe/browser/aboutCertError.css
browser/themes/gnomestripe/browser/aboutPrivateBrowsing.css
browser/themes/gnomestripe/browser/aboutSessionRestore-window-icon.png
browser/themes/gnomestripe/browser/aboutSessionRestore.css
browser/themes/gnomestripe/browser/aboutSyncTabs.css
browser/themes/gnomestripe/browser/actionicon-tab.png
browser/themes/gnomestripe/browser/browser.css
browser/themes/gnomestripe/browser/devtools/arrows.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/ltr-end-pressed.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/ltr-end-selected-pressed.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/ltr-end-selected.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/ltr-end.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/ltr-middle-pressed.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/ltr-middle-selected-pressed.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/ltr-middle-selected.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/ltr-middle.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/ltr-start-pressed.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/ltr-start-selected-pressed.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/ltr-start-selected.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/ltr-start.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/rtl-end-pressed.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/rtl-end-selected-pressed.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/rtl-end-selected.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/rtl-end.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/rtl-middle-pressed.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/rtl-middle-selected-pressed.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/rtl-middle-selected.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/rtl-middle.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/rtl-start-pressed.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/rtl-start-selected-pressed.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/rtl-start-selected.png
browser/themes/gnomestripe/browser/devtools/breadcrumbs/rtl-start.png
browser/themes/gnomestripe/browser/devtools/csshtmltree.css
browser/themes/gnomestripe/browser/devtools/gcli.css
browser/themes/gnomestripe/browser/devtools/search.png
browser/themes/gnomestripe/browser/engineManager.css
browser/themes/gnomestripe/browser/feeds/audioFeedIcon.png
browser/themes/gnomestripe/browser/feeds/audioFeedIcon16.png
browser/themes/gnomestripe/browser/feeds/feedIcon.png
browser/themes/gnomestripe/browser/feeds/feedIcon16.png
browser/themes/gnomestripe/browser/feeds/subscribe-ui.css
browser/themes/gnomestripe/browser/feeds/subscribe.css
browser/themes/gnomestripe/browser/feeds/videoFeedIcon.png
browser/themes/gnomestripe/browser/feeds/videoFeedIcon16.png
browser/themes/gnomestripe/browser/fullscreen-video.css
browser/themes/gnomestripe/browser/icon.png
browser/themes/gnomestripe/browser/identity.png
browser/themes/gnomestripe/browser/inspector.css
browser/themes/gnomestripe/browser/jar.mn
browser/themes/gnomestripe/browser/monitor.png
browser/themes/gnomestripe/browser/monitor_16-10.png
browser/themes/gnomestripe/browser/page-livemarks.png
browser/themes/gnomestripe/browser/pageInfo.css
browser/themes/gnomestripe/browser/pageInfo.png
browser/themes/gnomestripe/browser/places/bookmarksMenu.png
browser/themes/gnomestripe/browser/places/bookmarksToolbar.png
browser/themes/gnomestripe/browser/places/calendar.png
browser/themes/gnomestripe/browser/places/downloads.png
browser/themes/gnomestripe/browser/places/editBookmarkOverlay.css
browser/themes/gnomestripe/browser/places/livemark-item.png
browser/themes/gnomestripe/browser/places/organizer.css
browser/themes/gnomestripe/browser/places/organizer.xml
browser/themes/gnomestripe/browser/places/pageStarred.png
browser/themes/gnomestripe/browser/places/places.css
browser/themes/gnomestripe/browser/places/query.png
browser/themes/gnomestripe/browser/places/searching_16.png
browser/themes/gnomestripe/browser/places/starPage.png
browser/themes/gnomestripe/browser/places/starred48.png
browser/themes/gnomestripe/browser/places/tag.png
browser/themes/gnomestripe/browser/places/toolbarDropMarker.png
browser/themes/gnomestripe/browser/places/unsortedBookmarks.png
browser/themes/gnomestripe/browser/places/unstarred48.png
browser/themes/gnomestripe/browser/preferences/Options-sync.png
browser/themes/gnomestripe/browser/preferences/Options.png
browser/themes/gnomestripe/browser/preferences/aboutPermissions.css
browser/themes/gnomestripe/browser/preferences/alwaysAsk.png
browser/themes/gnomestripe/browser/preferences/applications.css
browser/themes/gnomestripe/browser/preferences/mail.png
browser/themes/gnomestripe/browser/preferences/preferences.css
browser/themes/gnomestripe/browser/preview.png
browser/themes/gnomestripe/browser/sanitizeDialog.css
browser/themes/gnomestripe/browser/searchbar.css
browser/themes/gnomestripe/browser/section_collapsed-rtl.png
browser/themes/gnomestripe/browser/section_collapsed.png
browser/themes/gnomestripe/browser/section_expanded.png
browser/themes/gnomestripe/browser/setDesktopBackground.css
browser/themes/gnomestripe/browser/sync-128.png
browser/themes/gnomestripe/browser/sync-16-throbber.png
browser/themes/gnomestripe/browser/sync-16.png
browser/themes/gnomestripe/browser/sync-24-throbber.png
browser/themes/gnomestripe/browser/sync-32.png
browser/themes/gnomestripe/browser/sync-bg.png
browser/themes/gnomestripe/browser/sync-desktopIcon.png
browser/themes/gnomestripe/browser/sync-mobileIcon.png
browser/themes/gnomestripe/browser/sync-notification-24.png
browser/themes/gnomestripe/browser/syncCommon.css
browser/themes/gnomestripe/browser/syncProgress.css
browser/themes/gnomestripe/browser/syncQuota.css
browser/themes/gnomestripe/browser/syncSetup.css
browser/themes/gnomestripe/browser/tabbrowser/alltabs.png
browser/themes/gnomestripe/browser/tabbrowser/connecting.png
browser/themes/gnomestripe/browser/tabbrowser/loading.png
browser/themes/gnomestripe/browser/tabbrowser/tab-overflow-border.png
browser/themes/gnomestripe/browser/tabbrowser/tab.png
browser/themes/gnomestripe/browser/tabbrowser/tabDragIndicator.png
browser/themes/gnomestripe/browser/tabview/edit-light.png
browser/themes/gnomestripe/browser/tabview/search.png
browser/themes/gnomestripe/browser/tabview/stack-expander.png
browser/themes/gnomestripe/browser/tabview/tabview.css
browser/themes/gnomestripe/browser/tabview/tabview.png
browser/themes/gnomestripe/browser/urlbar-arrow.png
browser/themes/pinstripe/browser/Geolocation-16.png
browser/themes/pinstripe/browser/Geolocation-64.png
browser/themes/pinstripe/browser/Info.png
browser/themes/pinstripe/browser/KUI-background.png
browser/themes/pinstripe/browser/KUI-close.png
browser/themes/pinstripe/browser/Makefile.in
browser/themes/pinstripe/browser/Privacy-16.png
browser/themes/pinstripe/browser/Privacy-32.png
browser/themes/pinstripe/browser/Privacy-48.png
browser/themes/pinstripe/browser/Privacy-64.png
browser/themes/pinstripe/browser/Search.png
browser/themes/pinstripe/browser/Secure-Glyph-White.png
browser/themes/pinstripe/browser/Toolbar-lion.png
browser/themes/pinstripe/browser/Toolbar.png
browser/themes/pinstripe/browser/aboutCertError.css
browser/themes/pinstripe/browser/aboutPrivateBrowsing.css
browser/themes/pinstripe/browser/aboutSessionRestore-window-icon.png
browser/themes/pinstripe/browser/aboutSessionRestore.css
browser/themes/pinstripe/browser/aboutSyncTabs.css
browser/themes/pinstripe/browser/actionicon-tab.png
browser/themes/pinstripe/browser/browser.css
browser/themes/pinstripe/browser/devtools/arrows.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/ltr-end-pressed.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/ltr-end-selected-pressed.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/ltr-end-selected.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/ltr-end.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/ltr-middle-pressed.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/ltr-middle-selected-pressed.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/ltr-middle-selected.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/ltr-middle.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/ltr-start-pressed.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/ltr-start-selected-pressed.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/ltr-start-selected.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/ltr-start.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/rtl-end-pressed.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/rtl-end-selected-pressed.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/rtl-end-selected.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/rtl-end.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/rtl-middle-pressed.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/rtl-middle-selected-pressed.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/rtl-middle-selected.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/rtl-middle.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/rtl-start-pressed.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/rtl-start-selected-pressed.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/rtl-start-selected.png
browser/themes/pinstripe/browser/devtools/breadcrumbs/rtl-start.png
browser/themes/pinstripe/browser/devtools/csshtmltree.css
browser/themes/pinstripe/browser/devtools/gcli.css
browser/themes/pinstripe/browser/devtools/search.png
browser/themes/pinstripe/browser/devtools/toolbarbutton-close.png
browser/themes/pinstripe/browser/engineManager.css
browser/themes/pinstripe/browser/feeds/audioFeedIcon.png
browser/themes/pinstripe/browser/feeds/audioFeedIcon16.png
browser/themes/pinstripe/browser/feeds/feedIcon.png
browser/themes/pinstripe/browser/feeds/feedIcon16.png
browser/themes/pinstripe/browser/feeds/subscribe-ui.css
browser/themes/pinstripe/browser/feeds/subscribe.css
browser/themes/pinstripe/browser/feeds/videoFeedIcon.png
browser/themes/pinstripe/browser/feeds/videoFeedIcon16.png
browser/themes/pinstripe/browser/fullscreen-video.css
browser/themes/pinstripe/browser/home.png
browser/themes/pinstripe/browser/hud-style-check-box-checked.png
browser/themes/pinstripe/browser/hud-style-check-box-empty.png
browser/themes/pinstripe/browser/hud-style-dropmarker-double-arrows.png
browser/themes/pinstripe/browser/hud-style-expander-closed.png
browser/themes/pinstripe/browser/hud-style-expander-open.png
browser/themes/pinstripe/browser/hud-style-new-folder-plus-sign.png
browser/themes/pinstripe/browser/hud-style-twisties.png
browser/themes/pinstripe/browser/icon.png
browser/themes/pinstripe/browser/identity.png
browser/themes/pinstripe/browser/inspector.css
browser/themes/pinstripe/browser/jar.mn
browser/themes/pinstripe/browser/keyhole-circle-lion.png
browser/themes/pinstripe/browser/keyhole-circle.png
browser/themes/pinstripe/browser/livemark-item.png
browser/themes/pinstripe/browser/menu-back.png
browser/themes/pinstripe/browser/menu-forward.png
browser/themes/pinstripe/browser/monitor.png
browser/themes/pinstripe/browser/monitor_16-10.png
browser/themes/pinstripe/browser/page-livemarks.png
browser/themes/pinstripe/browser/pageInfo.css
browser/themes/pinstripe/browser/places/allBookmarks.png
browser/themes/pinstripe/browser/places/bookmarksMenu.png
browser/themes/pinstripe/browser/places/bookmarksToolbar.png
browser/themes/pinstripe/browser/places/downloads.png
browser/themes/pinstripe/browser/places/editBookmarkOverlay.css
browser/themes/pinstripe/browser/places/expander-closed-active.png
browser/themes/pinstripe/browser/places/expander-closed.png
browser/themes/pinstripe/browser/places/expander-open-active.png
browser/themes/pinstripe/browser/places/expander-open.png
browser/themes/pinstripe/browser/places/folderDropArrow.png
browser/themes/pinstripe/browser/places/history.png
browser/themes/pinstripe/browser/places/minus-active.png
browser/themes/pinstripe/browser/places/minus.png
browser/themes/pinstripe/browser/places/organizer.css
browser/themes/pinstripe/browser/places/places.css
browser/themes/pinstripe/browser/places/plus-active.png
browser/themes/pinstripe/browser/places/plus.png
browser/themes/pinstripe/browser/places/query.png
browser/themes/pinstripe/browser/places/searching_16.png
browser/themes/pinstripe/browser/places/star-icons.png
browser/themes/pinstripe/browser/places/starPage.png
browser/themes/pinstripe/browser/places/starred48.png
browser/themes/pinstripe/browser/places/tag.png
browser/themes/pinstripe/browser/places/toolbar-lion.png
browser/themes/pinstripe/browser/places/toolbar.png
browser/themes/pinstripe/browser/places/toolbarDropMarker.png
browser/themes/pinstripe/browser/places/twisty-closed.gif
browser/themes/pinstripe/browser/places/twisty-open.gif
browser/themes/pinstripe/browser/places/unfiledBookmarks.png
browser/themes/pinstripe/browser/places/unstarred48.png
browser/themes/pinstripe/browser/preferences/Options-sync.png
browser/themes/pinstripe/browser/preferences/Options.png
browser/themes/pinstripe/browser/preferences/aboutPermissions.css
browser/themes/pinstripe/browser/preferences/alwaysAsk.png
browser/themes/pinstripe/browser/preferences/application.png
browser/themes/pinstripe/browser/preferences/applications.css
browser/themes/pinstripe/browser/preferences/preferences.css
browser/themes/pinstripe/browser/preferences/saveFile.png
browser/themes/pinstripe/browser/preview.png
browser/themes/pinstripe/browser/reload-stop-go.png
browser/themes/pinstripe/browser/sanitizeDialog.css
browser/themes/pinstripe/browser/searchbar-dropmarker.png
browser/themes/pinstripe/browser/searchbar.css
browser/themes/pinstripe/browser/section_collapsed-rtl.png
browser/themes/pinstripe/browser/section_collapsed.png
browser/themes/pinstripe/browser/section_expanded.png
browser/themes/pinstripe/browser/setDesktopBackground.css
browser/themes/pinstripe/browser/shared.inc
browser/themes/pinstripe/browser/sync-128.png
browser/themes/pinstripe/browser/sync-16.png
browser/themes/pinstripe/browser/sync-32.png
browser/themes/pinstripe/browser/sync-bg.png
browser/themes/pinstripe/browser/sync-desktopIcon.png
browser/themes/pinstripe/browser/sync-mobileIcon.png
browser/themes/pinstripe/browser/sync-notification-24.png
browser/themes/pinstripe/browser/sync-throbber.png
browser/themes/pinstripe/browser/syncCommon.css
browser/themes/pinstripe/browser/syncProgress.css
browser/themes/pinstripe/browser/syncQuota.css
browser/themes/pinstripe/browser/syncSetup.css
browser/themes/pinstripe/browser/tabbrowser/alltabs-box-bkgnd-icon-lion.png
browser/themes/pinstripe/browser/tabbrowser/alltabs-box-bkgnd-icon.png
browser/themes/pinstripe/browser/tabbrowser/connecting.png
browser/themes/pinstripe/browser/tabbrowser/loading.png
browser/themes/pinstripe/browser/tabbrowser/newtab.png
browser/themes/pinstripe/browser/tabbrowser/tab-arrow-left.png
browser/themes/pinstripe/browser/tabbrowser/tab-arrow-right.png
browser/themes/pinstripe/browser/tabbrowser/tab-bottom-hover-active.png
browser/themes/pinstripe/browser/tabbrowser/tab-bottom-normal-active.png
browser/themes/pinstripe/browser/tabbrowser/tab-bottom-selected-active.png
browser/themes/pinstripe/browser/tabbrowser/tab-overflow-border.png
browser/themes/pinstripe/browser/tabbrowser/tab-top-hover-active.png
browser/themes/pinstripe/browser/tabbrowser/tab-top-normal-active.png
browser/themes/pinstripe/browser/tabbrowser/tab-top-selected-active.png
browser/themes/pinstripe/browser/tabbrowser/tabDragIndicator.png
browser/themes/pinstripe/browser/tabbrowser/tabbar-bottom-bg-active.png
browser/themes/pinstripe/browser/tabbrowser/tabbar-bottom-bg-inactive.png
browser/themes/pinstripe/browser/tabbrowser/tabbar-top-bg-active.png
browser/themes/pinstripe/browser/tabbrowser/tabbar-top-bg-inactive.png
browser/themes/pinstripe/browser/tabview/close.png
browser/themes/pinstripe/browser/tabview/edit-light.png
browser/themes/pinstripe/browser/tabview/search.png
browser/themes/pinstripe/browser/tabview/stack-expander.png
browser/themes/pinstripe/browser/tabview/tabview-lion.png
browser/themes/pinstripe/browser/tabview/tabview.css
browser/themes/pinstripe/browser/tabview/tabview.png
browser/themes/pinstripe/browser/toolbarbutton-dropmarker-lion.png
browser/themes/pinstripe/browser/toolbarbutton-dropmarker.png
browser/themes/pinstripe/browser/urlbar-arrow.png
browser/themes/pinstripe/browser/urlbar-history-dropmarker.png
browser/themes/pinstripe/browser/urlbar-popup-blocked.png
browser/themes/winstripe/browser/Geolocation-16.png
browser/themes/winstripe/browser/Geolocation-64.png
browser/themes/winstripe/browser/Info-aero.png
browser/themes/winstripe/browser/Info.png
browser/themes/winstripe/browser/KUI-background.png
browser/themes/winstripe/browser/KUI-close.png
browser/themes/winstripe/browser/Makefile.in
browser/themes/winstripe/browser/Privacy-16-aero.png
browser/themes/winstripe/browser/Privacy-16.png
browser/themes/winstripe/browser/Privacy-32-aero.png
browser/themes/winstripe/browser/Privacy-32.png
browser/themes/winstripe/browser/Privacy-48-aero.png
browser/themes/winstripe/browser/Privacy-48.png
browser/themes/winstripe/browser/Privacy-64-aero.png
browser/themes/winstripe/browser/Privacy-64.png
browser/themes/winstripe/browser/Secure24-aero.png
browser/themes/winstripe/browser/Secure24.png
browser/themes/winstripe/browser/Toolbar-inverted.png
browser/themes/winstripe/browser/Toolbar.png
browser/themes/winstripe/browser/aboutCertError.css
browser/themes/winstripe/browser/aboutPrivateBrowsing.css
browser/themes/winstripe/browser/aboutSessionRestore-window-icon-aero.png
browser/themes/winstripe/browser/aboutSessionRestore-window-icon.png
browser/themes/winstripe/browser/aboutSessionRestore.css
browser/themes/winstripe/browser/aboutSyncTabs.css
browser/themes/winstripe/browser/actionicon-tab.png
browser/themes/winstripe/browser/appmenu-dropmarker.png
browser/themes/winstripe/browser/appmenu-icons.png
browser/themes/winstripe/browser/browser-aero.css
browser/themes/winstripe/browser/browser.css
browser/themes/winstripe/browser/devtools/arrows.png
browser/themes/winstripe/browser/devtools/breadcrumbs/ltr-end-pressed.png
browser/themes/winstripe/browser/devtools/breadcrumbs/ltr-end-selected-pressed.png
browser/themes/winstripe/browser/devtools/breadcrumbs/ltr-end-selected.png
browser/themes/winstripe/browser/devtools/breadcrumbs/ltr-end.png
browser/themes/winstripe/browser/devtools/breadcrumbs/ltr-middle-pressed.png
browser/themes/winstripe/browser/devtools/breadcrumbs/ltr-middle-selected-pressed.png
browser/themes/winstripe/browser/devtools/breadcrumbs/ltr-middle-selected.png
browser/themes/winstripe/browser/devtools/breadcrumbs/ltr-middle.png
browser/themes/winstripe/browser/devtools/breadcrumbs/ltr-start-pressed.png
browser/themes/winstripe/browser/devtools/breadcrumbs/ltr-start-selected-pressed.png
browser/themes/winstripe/browser/devtools/breadcrumbs/ltr-start-selected.png
browser/themes/winstripe/browser/devtools/breadcrumbs/ltr-start.png
browser/themes/winstripe/browser/devtools/breadcrumbs/rtl-end-pressed.png
browser/themes/winstripe/browser/devtools/breadcrumbs/rtl-end-selected-pressed.png
browser/themes/winstripe/browser/devtools/breadcrumbs/rtl-end-selected.png
browser/themes/winstripe/browser/devtools/breadcrumbs/rtl-end.png
browser/themes/winstripe/browser/devtools/breadcrumbs/rtl-middle-pressed.png
browser/themes/winstripe/browser/devtools/breadcrumbs/rtl-middle-selected-pressed.png
browser/themes/winstripe/browser/devtools/breadcrumbs/rtl-middle-selected.png
browser/themes/winstripe/browser/devtools/breadcrumbs/rtl-middle.png
browser/themes/winstripe/browser/devtools/breadcrumbs/rtl-start-pressed.png
browser/themes/winstripe/browser/devtools/breadcrumbs/rtl-start-selected-pressed.png
browser/themes/winstripe/browser/devtools/breadcrumbs/rtl-start-selected.png
browser/themes/winstripe/browser/devtools/breadcrumbs/rtl-start.png
browser/themes/winstripe/browser/devtools/csshtmltree.css
browser/themes/winstripe/browser/devtools/gcli.css
browser/themes/winstripe/browser/devtools/search.png
browser/themes/winstripe/browser/devtools/toolbarbutton-close.png
browser/themes/winstripe/browser/engineManager.css
browser/themes/winstripe/browser/feeds/audioFeedIcon-aero.png
browser/themes/winstripe/browser/feeds/audioFeedIcon.png
browser/themes/winstripe/browser/feeds/audioFeedIcon16-aero.png
browser/themes/winstripe/browser/feeds/audioFeedIcon16.png
browser/themes/winstripe/browser/feeds/feedIcon-aero.png
browser/themes/winstripe/browser/feeds/feedIcon.png
browser/themes/winstripe/browser/feeds/feedIcon16-aero.png
browser/themes/winstripe/browser/feeds/feedIcon16.png
browser/themes/winstripe/browser/feeds/subscribe-ui.css
browser/themes/winstripe/browser/feeds/subscribe.css
browser/themes/winstripe/browser/feeds/videoFeedIcon-aero.png
browser/themes/winstripe/browser/feeds/videoFeedIcon.png
browser/themes/winstripe/browser/feeds/videoFeedIcon16-aero.png
browser/themes/winstripe/browser/feeds/videoFeedIcon16.png
browser/themes/winstripe/browser/fullscreen-video.css
browser/themes/winstripe/browser/icon-aero.png
browser/themes/winstripe/browser/icon.png
browser/themes/winstripe/browser/identity-aero.png
browser/themes/winstripe/browser/identity.png
browser/themes/winstripe/browser/inspector.css
browser/themes/winstripe/browser/jar.mn
browser/themes/winstripe/browser/keyhole-forward-mask.svg
browser/themes/winstripe/browser/livemark-folder-aero.png
browser/themes/winstripe/browser/livemark-folder.png
browser/themes/winstripe/browser/livemark-item-aero.png
browser/themes/winstripe/browser/livemark-item.png
browser/themes/winstripe/browser/mainwindow-dropdown-arrow-aero.png
browser/themes/winstripe/browser/mainwindow-dropdown-arrow-inverted.png
browser/themes/winstripe/browser/mainwindow-dropdown-arrow.png
browser/themes/winstripe/browser/menu-back-aero.png
browser/themes/winstripe/browser/menu-back.png
browser/themes/winstripe/browser/menu-forward-aero.png
browser/themes/winstripe/browser/menu-forward.png
browser/themes/winstripe/browser/monitor.png
browser/themes/winstripe/browser/monitor_16-10.png
browser/themes/winstripe/browser/pageInfo-aero.png
browser/themes/winstripe/browser/pageInfo.css
browser/themes/winstripe/browser/pageInfo.png
browser/themes/winstripe/browser/places/allBookmarks-aero.png
browser/themes/winstripe/browser/places/allBookmarks.png
browser/themes/winstripe/browser/places/bookmark.png
browser/themes/winstripe/browser/places/bookmarksMenu-aero.png
browser/themes/winstripe/browser/places/bookmarksMenu.png
browser/themes/winstripe/browser/places/bookmarksToolbar-aero.png
browser/themes/winstripe/browser/places/bookmarksToolbar.png
browser/themes/winstripe/browser/places/calendar-aero.png
browser/themes/winstripe/browser/places/calendar.png
browser/themes/winstripe/browser/places/downloads.png
browser/themes/winstripe/browser/places/editBookmark.png
browser/themes/winstripe/browser/places/editBookmarkOverlay.css
browser/themes/winstripe/browser/places/history-aero.png
browser/themes/winstripe/browser/places/history.png
browser/themes/winstripe/browser/places/libraryToolbar-aero.png
browser/themes/winstripe/browser/places/libraryToolbar.png
browser/themes/winstripe/browser/places/organizer-aero.css
browser/themes/winstripe/browser/places/organizer.css
browser/themes/winstripe/browser/places/places-aero.css
browser/themes/winstripe/browser/places/places.css
browser/themes/winstripe/browser/places/query-aero.png
browser/themes/winstripe/browser/places/query.png
browser/themes/winstripe/browser/places/searching_16-aero.png
browser/themes/winstripe/browser/places/searching_16.png
browser/themes/winstripe/browser/places/starred48-aero.png
browser/themes/winstripe/browser/places/starred48.png
browser/themes/winstripe/browser/places/tag-aero.png
browser/themes/winstripe/browser/places/tag.png
browser/themes/winstripe/browser/places/toolbarDropMarker-aero.png
browser/themes/winstripe/browser/places/toolbarDropMarker.png
browser/themes/winstripe/browser/places/unsortedBookmarks-aero.png
browser/themes/winstripe/browser/places/unsortedBookmarks.png
browser/themes/winstripe/browser/places/unstarred48.png
browser/themes/winstripe/browser/preferences/Options-aero.png
browser/themes/winstripe/browser/preferences/Options-sync.png
browser/themes/winstripe/browser/preferences/Options.png
browser/themes/winstripe/browser/preferences/aboutPermissions.css
browser/themes/winstripe/browser/preferences/alwaysAsk-aero.png
browser/themes/winstripe/browser/preferences/alwaysAsk.png
browser/themes/winstripe/browser/preferences/application-aero.png
browser/themes/winstripe/browser/preferences/application.png
browser/themes/winstripe/browser/preferences/applications.css
browser/themes/winstripe/browser/preferences/mail-aero.png
browser/themes/winstripe/browser/preferences/mail.png
browser/themes/winstripe/browser/preferences/plugin-aero.png
browser/themes/winstripe/browser/preferences/preferences.css
browser/themes/winstripe/browser/preferences/saveFile-aero.png
browser/themes/winstripe/browser/preferences/saveFile.png
browser/themes/winstripe/browser/preview.png
browser/themes/winstripe/browser/reload-stop-go.png
browser/themes/winstripe/browser/sanitizeDialog.css
browser/themes/winstripe/browser/searchbar.css
browser/themes/winstripe/browser/section_collapsed-rtl.png
browser/themes/winstripe/browser/section_collapsed.png
browser/themes/winstripe/browser/section_expanded.png
browser/themes/winstripe/browser/setDesktopBackground.css
browser/themes/winstripe/browser/sync-128.png
browser/themes/winstripe/browser/sync-16.png
browser/themes/winstripe/browser/sync-32.png
browser/themes/winstripe/browser/sync-bg.png
browser/themes/winstripe/browser/sync-desktopIcon.png
browser/themes/winstripe/browser/sync-mobileIcon.png
browser/themes/winstripe/browser/sync-notification-24.png
browser/themes/winstripe/browser/sync-throbber.png
browser/themes/winstripe/browser/syncCommon.css
browser/themes/winstripe/browser/syncProgress.css
browser/themes/winstripe/browser/syncQuota.css
browser/themes/winstripe/browser/syncSetup.css
browser/themes/winstripe/browser/tabbrowser/alltabs.png
browser/themes/winstripe/browser/tabbrowser/connecting.png
browser/themes/winstripe/browser/tabbrowser/loading.png
browser/themes/winstripe/browser/tabbrowser/newtab-inverted.png
browser/themes/winstripe/browser/tabbrowser/newtab.png
browser/themes/winstripe/browser/tabbrowser/tab-arrow-left-inverted.png
browser/themes/winstripe/browser/tabbrowser/tab-arrow-left.png
browser/themes/winstripe/browser/tabbrowser/tab-overflow-border.png
browser/themes/winstripe/browser/tabbrowser/tab.png
browser/themes/winstripe/browser/tabbrowser/tabDragIndicator.png
browser/themes/winstripe/browser/tabview/close.png
browser/themes/winstripe/browser/tabview/edit-light.png
browser/themes/winstripe/browser/tabview/grain.png
browser/themes/winstripe/browser/tabview/search.png
browser/themes/winstripe/browser/tabview/stack-expander.png
browser/themes/winstripe/browser/tabview/tabview-inverted.png
browser/themes/winstripe/browser/tabview/tabview.css
browser/themes/winstripe/browser/tabview/tabview.png
browser/themes/winstripe/browser/urlbar-arrow.png
browser/themes/winstripe/browser/urlbar-history-dropmarker.png
browser/themes/winstripe/browser/urlbar-popup-blocked.png
dom/interfaces/threads/Makefile.in
dom/interfaces/threads/nsIDOMWorkers.idl
gfx/ots/ots-fix-gcc46.patch
js/src/ctypes/CTypes.cpp
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/FoldConstants.cpp
js/src/frontend/ParseNode.cpp
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/SemanticAnalysis.cpp
js/src/gc/Statistics.cpp
js/src/jsanalyze.cpp
js/src/jsapi-tests/testThreadGC.cpp
js/src/jsapi-tests/testThreads.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jsclass.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsinfer.cpp
js/src/jsinterp.cpp
js/src/jsmath.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsopcode.cpp
js/src/jsopcode.tbl
js/src/jsproxy.cpp
js/src/jsprvtd.h
js/src/jsreflect.cpp
js/src/jsstr.cpp
js/src/jstracer.cpp
js/src/jstypedarray.cpp
js/src/jstypedarray.h
js/src/jsxdrapi.cpp
js/src/jsxml.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/FastOps.cpp
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/MethodJIT.h
js/src/shell/js.cpp
js/src/shell/jsworkers.cpp
js/src/vm/RegExpObject-inl.h
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
js/src/vm/Stack.h
js/src/vm/String-inl.h
js/src/vm/String.h
js/xpconnect/src/XPCComponents.cpp
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/dombindings.cpp
js/xpconnect/src/dombindings.h
js/xpconnect/src/nsXPConnect.cpp
js/xpconnect/src/xpcprivate.h
js/xpconnect/src/xpcpublic.h
layout/reftests/svg/as-image/img-foreignObject-embed-2-helper.svg
layout/reftests/svg/as-image/img-foreignObject-embed-2.html
toolkit/components/startup/nsTryToClose.js
toolkit/components/startup/nsTryToClose.manifest
widget/public/nsIMenuRollup.h
xpcom/idl-parser/xpidllex.py
xpcom/idl-parser/xpidlyacc.py
new file mode 100644
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,41 @@
+# .gitignore - List of filenames git should ignore
+
+# Filenames that should be ignored wherever they appear
+*~
+*.pyc
+*.pyo
+TAGS
+tags
+ID
+.DS_Store*
+
+# Vim swap files.
+.*.sw[a-z]
+
+# User files that may appear at the root
+.mozconfig
+mozconfig
+configure
+config.cache
+config.log
+
+# Empty marker file that's generated when we check out NSS
+security/manager/.nss.checkout
+
+# Build directories
+obj/*
+
+# Build directories for js shell
+*/_DBG.OBJ/
+*/_OPT.OBJ/
+
+# SpiderMonkey configury
+js/src/configure
+js/src/autom4te.cache
+# SpiderMonkey test result logs
+js/src/tests/results-*.html
+js/src/tests/results-*.txt
+
+# Java HTML5 parser classes
+parser/html/java/htmlparser/
+parser/html/java/javaparser/
--- a/.hgtags
+++ b/.hgtags
@@ -65,8 +65,9 @@ a95d426422816513477e5863add1b00ac7041dcb
 138f593553b66c9f815e8f57870c19d6347f7702 UPDATE_PACKAGING_R14
 9eae975b3d6fb7748fe5a3c0113d449b1c7cc0b2 AURORA_BASE_20110524
 138f593553b66c9f815e8f57870c19d6347f7702 UPDATE_PACKAGING_R14
 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R14
 5eb553dd2ceae5f88d80f27afc5ef3935c5d43b0 AURORA_BASE_20110705
 41b84b87c816403e1b74963d8094cff0406c989e AURORA_BASE_20110816
 c0983049bcaa9551e5f276d5a77ce154c151e0b0 AURORA_BASE_20110927
 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R15
+54bfd8bf682e295ffd7f22fa921ca343957b6c1c AURORA_BASE_20111108
--- a/Makefile.in
+++ b/Makefile.in
@@ -208,16 +208,18 @@ ifneq ($(OS_ARCH)_$(GNU_CC), WINNT_)
 # No point in clobbering if PGO has been explicitly disabled.
 ifndef NO_PROFILE_GUIDED_OPTIMIZE
 maybe_clobber_profiledbuild: clean
 else
 maybe_clobber_profiledbuild:
 endif
 else
 maybe_clobber_profiledbuild:
+	$(RM) $(DIST)/bin/*.pgc
+	find $(DIST)/$(MOZ_APP_NAME) -name "*.pgc" -exec mv {} $(DIST)/bin \;
 endif
 
 .PHONY: maybe_clobber_profiledbuild
 
 # Look for R_386_PC32 relocations in shared libs, these
 # break x86_64 builds and SELinux users.
 ifeq ($(OS_TARGET)_$(TARGET_XPCOM_ABI),Linux_x86-gcc3)
 scheck::
--- a/accessible/public/nsIAccessibleEvent.idl
+++ b/accessible/public/nsIAccessibleEvent.idl
@@ -54,17 +54,17 @@ interface nsIDOMNode;
  * the event and its target. To listen to in-process accessibility invents,
  * make your object an nsIObserver, and listen for accessible-event by 
  * using code something like this:
  *   nsCOMPtr<nsIObserverService> observerService = 
  *     do_GetService("@mozilla.org/observer-service;1", &rv);
  *   if (NS_SUCCEEDED(rv)) 
  *     rv = observerService->AddObserver(this, "accessible-event", PR_TRUE);
  */
-[scriptable, uuid(fd1378c5-c606-4a5e-a321-8e7fc107e5cf)]
+[scriptable, uuid(7f66a33a-9ed7-4fd4-87a8-e431b0f43368)]
 interface nsIAccessibleEvent : nsISupports
 {
   /**
    * An object has been created.
    */
   const unsigned long EVENT_SHOW = 0x0001;
 
   /**
@@ -275,17 +275,22 @@ interface nsIAccessibleEvent : nsISuppor
   const unsigned long EVENT_DOCUMENT_ATTRIBUTES_CHANGED = 0x002A;
 
   /**
    * The contents of the document have changed.
    */
   const unsigned long EVENT_DOCUMENT_CONTENT_CHANGED = 0x002B;
 
   const unsigned long EVENT_PROPERTY_CHANGED = 0x002C;
-  const unsigned long EVENT_SELECTION_CHANGED = 0x002D;
+
+  /**
+   * A slide changed in a presentation document or a page boundary was
+   * crossed in a word processing document.
+   */
+  const unsigned long EVENT_PAGE_CHANGED = 0x002D;
 
   /**
    * A text object's attributes changed.
    * Also see EVENT_OBJECT_ATTRIBUTE_CHANGED.
    */
   const unsigned long EVENT_TEXT_ATTRIBUTE_CHANGED = 0x002E;
 
   /**
@@ -432,25 +437,19 @@ interface nsIAccessibleEvent : nsISuppor
   const unsigned long EVENT_HYPERTEXT_NLINKS_CHANGED = 0x0054;
 
   /**
    * An object's attributes changed. Also see EVENT_TEXT_ATTRIBUTE_CHANGED.
    */
   const unsigned long EVENT_OBJECT_ATTRIBUTE_CHANGED = 0x0055;
 
   /**
-   * A slide changed in a presentation document or a page boundary was
-   * crossed in a word processing document.
-   */
-  const unsigned long EVENT_PAGE_CHANGED = 0x0056;
-
-  /**
    * Help make sure event map does not get out-of-line.
    */
-  const unsigned long EVENT_LAST_ENTRY = 0x0057;
+  const unsigned long EVENT_LAST_ENTRY = 0x0056;
 
   /**
    * The type of event, based on the enumerated event values
    * defined in this interface.
    */
   readonly attribute unsigned long eventType;
   
   /**
--- a/accessible/src/atk/nsAccessibleWrap.cpp
+++ b/accessible/src/atk/nsAccessibleWrap.cpp
@@ -1084,20 +1084,34 @@ nsAccessibleWrap::FirePlatformEvent(AccE
         nsCOMPtr<nsIAccessibleValue> value(do_QueryObject(accessible));
         if (value) {    // Make sure this is a numeric value
             // Don't fire for MSAA string value changes (e.g. text editing)
             // ATK values are always numeric
             g_object_notify( (GObject*)atkObj, "accessible-value" );
         }
       } break;
 
-    case nsIAccessibleEvent::EVENT_SELECTION_CHANGED:
-        MAI_LOG_DEBUG(("\n\nReceived: EVENT_SELECTION_CHANGED\n"));
-        g_signal_emit_by_name(atkObj, "selection_changed");
-        break;
+    case nsIAccessibleEvent::EVENT_SELECTION:
+    case nsIAccessibleEvent::EVENT_SELECTION_ADD:
+    case nsIAccessibleEvent::EVENT_SELECTION_REMOVE:
+    {
+      // XXX: dupe events may be fired
+      MAI_LOG_DEBUG(("\n\nReceived: EVENT_SELECTION_CHANGED\n"));
+      AccSelChangeEvent* selChangeEvent = downcast_accEvent(aEvent);
+      g_signal_emit_by_name(nsAccessibleWrap::GetAtkObject(selChangeEvent->Widget()),
+                            "selection_changed");
+      break;
+    }
+
+    case nsIAccessibleEvent::EVENT_SELECTION_WITHIN:
+    {
+      MAI_LOG_DEBUG(("\n\nReceived: EVENT_SELECTION_CHANGED\n"));
+      g_signal_emit_by_name(atkObj, "selection_changed");
+      break;
+    }
 
     case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED:
         MAI_LOG_DEBUG(("\n\nReceived: EVENT_TEXT_SELECTION_CHANGED\n"));
         g_signal_emit_by_name(atkObj, "text_selection_changed");
         break;
 
     case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED:
       {
--- a/accessible/src/base/AccEvent.cpp
+++ b/accessible/src/base/AccEvent.cpp
@@ -326,16 +326,38 @@ AccCaretMoveEvent::CreateXPCOMObject()
 {
   nsAccEvent* event = new nsAccCaretMoveEvent(this);
   NS_IF_ADDREF(event);
   return event;
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
+// AccSelChangeEvent
+////////////////////////////////////////////////////////////////////////////////
+
+AccSelChangeEvent::
+  AccSelChangeEvent(nsAccessible* aWidget, nsAccessible* aItem,
+                    SelChangeType aSelChangeType) :
+    AccEvent(0, aItem, eAutoDetect, eCoalesceSelectionChange),
+    mWidget(aWidget), mItem(aItem), mSelChangeType(aSelChangeType),
+    mPreceedingCount(0), mPackedEvent(nsnull)
+{
+  if (aSelChangeType == eSelectionAdd) {
+    if (mWidget->GetSelectedItem(1))
+      mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
+    else
+      mEventType = nsIAccessibleEvent::EVENT_SELECTION;
+  } else {
+    mEventType = nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
 // AccTableChangeEvent
 ////////////////////////////////////////////////////////////////////////////////
 
 AccTableChangeEvent::
   AccTableChangeEvent(nsAccessible* aAccessible, PRUint32 aEventType,
                       PRInt32 aRowOrColIndex, PRInt32 aNumRowsOrCols) :
   AccEvent(aEventType, aAccessible),
   mRowOrColIndex(aRowOrColIndex), mNumRowsOrCols(aNumRowsOrCols)
--- a/accessible/src/base/AccEvent.h
+++ b/accessible/src/base/AccEvent.h
@@ -77,16 +77,19 @@ public:
      //    subtree or the same node, only the umbrella event on the ancestor
      //    will be emitted.
      eCoalesceFromSameSubtree,
 
     // eCoalesceOfSameType : For events of the same type, only the newest event
     // will be processed.
     eCoalesceOfSameType,
 
+    // eCoalesceSelectionChange: coalescence of selection change events.
+    eCoalesceSelectionChange,
+
      // eRemoveDupes : For repeat events, only the newest event in queue
      //    will be emitted.
      eRemoveDupes,
 
      // eDoNotEmit : This event is confirmed as a duplicate, do not emit it.
      eDoNotEmit
   };
 
@@ -120,16 +123,17 @@ public:
   enum EventGroup {
     eGenericEvent,
     eStateChangeEvent,
     eTextChangeEvent,
     eMutationEvent,
     eHideEvent,
     eShowEvent,
     eCaretMoveEvent,
+    eSelectionChangeEvent,
     eTableChangeEvent
   };
 
   static const EventGroup kEventGroup = eGenericEvent;
   virtual unsigned int GetEventGroups() const
   {
     return 1U << eGenericEvent;
   }
@@ -322,20 +326,47 @@ public:
 private:
   PRInt32 mCaretOffset;
 };
 
 
 /**
  * Accessible widget selection change event.
  */
-class AccSelectionChangeEvent : public AccEvent
+class AccSelChangeEvent : public AccEvent
 {
 public:
+  enum SelChangeType {
+    eSelectionAdd,
+    eSelectionRemove
+  };
 
+  AccSelChangeEvent(nsAccessible* aWidget, nsAccessible* aItem,
+                    SelChangeType aSelChangeType);
+
+  virtual ~AccSelChangeEvent() { }
+
+  // AccEvent
+  static const EventGroup kEventGroup = eSelectionChangeEvent;
+  virtual unsigned int GetEventGroups() const
+  {
+    return AccEvent::GetEventGroups() | (1U << eSelectionChangeEvent);
+  }
+
+  // AccSelChangeEvent
+  nsAccessible* Widget() const { return mWidget; }
+
+private:
+  nsRefPtr<nsAccessible> mWidget;
+  nsRefPtr<nsAccessible> mItem;
+  SelChangeType mSelChangeType;
+  PRUint32 mPreceedingCount;
+  AccSelChangeEvent* mPackedEvent;
+
+  friend class NotificationController;
 };
 
 
 /**
  * Accessible table change event.
  */
 class AccTableChangeEvent : public AccEvent
 {
--- a/accessible/src/base/NotificationController.cpp
+++ b/accessible/src/base/NotificationController.cpp
@@ -46,16 +46,20 @@
 #include "nsTextAccessible.h"
 #include "FocusManager.h"
 #include "TextUpdater.h"
 
 #include "mozilla/dom/Element.h"
 
 using namespace mozilla::a11y;
 
+// Defines the number of selection add/remove events in the queue when they
+// aren't packed into single selection within event.
+const unsigned int kSelChangeCountToPack = 5;
+
 ////////////////////////////////////////////////////////////////////////////////
 // NotificationCollector
 ////////////////////////////////////////////////////////////////////////////////
 
 NotificationController::NotificationController(nsDocAccessible* aDocument,
                                                nsIPresShell* aPresShell) :
   mObservingState(eNotObservingRefresh), mDocument(aDocument),
   mPresShell(aPresShell)
@@ -486,16 +490,36 @@ NotificationController::CoalesceEvents()
             accEvent->mEventRule == tailEvent->mEventRule &&
             accEvent->mNode == tailEvent->mNode) {
           tailEvent->mEventRule = AccEvent::eDoNotEmit;
           return;
         }
       }
     } break; // case eRemoveDupes
 
+    case AccEvent::eCoalesceSelectionChange:
+    {
+      AccSelChangeEvent* tailSelChangeEvent = downcast_accEvent(tailEvent);
+      PRInt32 index = tail - 1;
+      for (; index >= 0; index--) {
+        AccEvent* thisEvent = mEvents[index];
+        if (thisEvent->mEventRule == tailEvent->mEventRule) {
+          AccSelChangeEvent* thisSelChangeEvent =
+            downcast_accEvent(thisEvent);
+
+          // Coalesce selection change events within same control.
+          if (tailSelChangeEvent->mWidget == thisSelChangeEvent->mWidget) {
+            CoalesceSelChangeEvents(tailSelChangeEvent, thisSelChangeEvent, index);
+            return;
+          }
+        }
+      }
+
+    } break; // eCoalesceSelectionChange
+
     default:
       break; // case eAllowDupes, eDoNotEmit
   } // switch
 }
 
 void
 NotificationController::ApplyToSiblings(PRUint32 aStart, PRUint32 aEnd,
                                         PRUint32 aEventType, nsINode* aNode,
@@ -507,16 +531,96 @@ NotificationController::ApplyToSiblings(
         accEvent->mEventRule != AccEvent::eDoNotEmit && accEvent->mNode &&
         accEvent->mNode->GetNodeParent() == aNode->GetNodeParent()) {
       accEvent->mEventRule = aEventRule;
     }
   }
 }
 
 void
+NotificationController::CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
+                                                AccSelChangeEvent* aThisEvent,
+                                                PRInt32 aThisIndex)
+{
+  aTailEvent->mPreceedingCount = aThisEvent->mPreceedingCount + 1;
+
+  // Pack all preceding events into single selection within event
+  // when we receive too much selection add/remove events.
+  if (aTailEvent->mPreceedingCount >= kSelChangeCountToPack) {
+    aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_WITHIN;
+    aTailEvent->mAccessible = aTailEvent->mWidget;
+    aThisEvent->mEventRule = AccEvent::eDoNotEmit;
+
+    // Do not emit any preceding selection events for same widget if they
+    // weren't coalesced yet.
+    if (aThisEvent->mEventType != nsIAccessibleEvent::EVENT_SELECTION_WITHIN) {
+      for (PRInt32 jdx = aThisIndex - 1; jdx >= 0; jdx--) {
+        AccEvent* prevEvent = mEvents[jdx];
+        if (prevEvent->mEventRule == aTailEvent->mEventRule) {
+          AccSelChangeEvent* prevSelChangeEvent =
+            downcast_accEvent(prevEvent);
+          if (prevSelChangeEvent->mWidget == aTailEvent->mWidget)
+            prevSelChangeEvent->mEventRule = AccEvent::eDoNotEmit;
+        }
+      }
+    }
+    return;
+  }
+
+  // Pack sequential selection remove and selection add events into
+  // single selection change event.
+  if (aTailEvent->mPreceedingCount == 1 &&
+      aTailEvent->mItem != aThisEvent->mItem) {
+    if (aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
+        aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
+      aThisEvent->mEventRule = AccEvent::eDoNotEmit;
+      aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
+      aTailEvent->mPackedEvent = aThisEvent;
+      return;
+    }
+
+    if (aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
+        aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
+      aTailEvent->mEventRule = AccEvent::eDoNotEmit;
+      aThisEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
+      aThisEvent->mPackedEvent = aThisEvent;
+      return;
+    }
+  }
+
+  // Unpack the packed selection change event because we've got one
+  // more selection add/remove.
+  if (aThisEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION) {
+    if (aThisEvent->mPackedEvent) {
+      aThisEvent->mPackedEvent->mEventType =
+        aThisEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ?
+          nsIAccessibleEvent::EVENT_SELECTION_ADD :
+          nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
+
+      aThisEvent->mPackedEvent->mEventRule =
+        AccEvent::eCoalesceSelectionChange;
+
+      aThisEvent->mPackedEvent = nsnull;
+    }
+
+    aThisEvent->mEventType =
+      aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ?
+        nsIAccessibleEvent::EVENT_SELECTION_ADD :
+        nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
+
+    return;
+  }
+
+  // Convert into selection add since control has single selection but other
+  // selection events for this control are queued.
+  if (aTailEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION)
+    aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
+}
+
+void
 NotificationController::CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
                                                     AccHideEvent* aThisEvent)
 {
   // XXX: we need a way to ignore SplitNode and JoinNode() when they do not
   // affect the text within the hypertext.
 
   AccTextChangeEvent* textEvent = aThisEvent->mTextChangeEvent;
   if (!textEvent)
--- a/accessible/src/base/NotificationController.h
+++ b/accessible/src/base/NotificationController.h
@@ -246,21 +246,21 @@ private:
    * @param aEventRule       the event rule to be applied
    *                         (should be eDoNotEmit or eAllowDupes)
    */
   void ApplyToSiblings(PRUint32 aStart, PRUint32 aEnd,
                        PRUint32 aEventType, nsINode* aNode,
                        AccEvent::EEventRule aEventRule);
 
   /**
-   * Do not emit one of two given reorder events fired for DOM nodes in the case
-   * when one DOM node is in parent chain of second one.
+   * Coalesce two selection change events within the same select control.
    */
-  void CoalesceReorderEventsFromSameTree(AccEvent* aAccEvent,
-                                         AccEvent* aDescendantAccEvent);
+  void CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
+                               AccSelChangeEvent* aThisEvent,
+                               PRInt32 aThisIndex);
 
   /**
    * Coalesce text change events caused by sibling hide events.
    */
   void CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
                                    AccHideEvent* aThisEvent);
   void CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
                                    AccShowEvent* aThisEvent);
--- a/accessible/src/base/nsAccessibilityService.h
+++ b/accessible/src/base/nsAccessibilityService.h
@@ -468,17 +468,17 @@ static const char kEventTypeNames[][40] 
   "minimize start",                          // EVENT_MINIMIZE_START
   "minimize end",                            // EVENT_MINIMIZE_END
   "document load complete",                  // EVENT_DOCUMENT_LOAD_COMPLETE
   "document reload",                         // EVENT_DOCUMENT_RELOAD
   "document load stopped",                   // EVENT_DOCUMENT_LOAD_STOPPED
   "document attributes changed",             // EVENT_DOCUMENT_ATTRIBUTES_CHANGED
   "document content changed",                // EVENT_DOCUMENT_CONTENT_CHANGED
   "property changed",                        // EVENT_PROPERTY_CHANGED
-  "selection changed",                       // EVENT_SELECTION_CHANGED
+  "page changed",                           // EVENT_PAGE_CHANGED
   "text attribute changed",                  // EVENT_TEXT_ATTRIBUTE_CHANGED
   "text caret moved",                        // EVENT_TEXT_CARET_MOVED
   "text changed",                            // EVENT_TEXT_CHANGED
   "text inserted",                           // EVENT_TEXT_INSERTED
   "text removed",                            // EVENT_TEXT_REMOVED
   "text updated",                            // EVENT_TEXT_UPDATED
   "text selection changed",                  // EVENT_TEXT_SELECTION_CHANGED
   "visible data changed",                    // EVENT_VISIBLE_DATA_CHANGED
@@ -509,17 +509,16 @@ static const char kEventTypeNames[][40] 
   "hyperlink number of anchors changed",     // EVENT_HYPERLINK_NUMBER_OF_ANCHORS_CHANGED
   "hyperlink selected link changed",         // EVENT_HYPERLINK_SELECTED_LINK_CHANGED
   "hypertext link activated",                // EVENT_HYPERTEXT_LINK_ACTIVATED
   "hypertext link selected",                 // EVENT_HYPERTEXT_LINK_SELECTED
   "hyperlink start index changed",           // EVENT_HYPERLINK_START_INDEX_CHANGED
   "hypertext changed",                       // EVENT_HYPERTEXT_CHANGED
   "hypertext links count changed",           // EVENT_HYPERTEXT_NLINKS_CHANGED
   "object attribute changed",                // EVENT_OBJECT_ATTRIBUTE_CHANGED
-  "page changed"                             // EVENT_PAGE_CHANGED
 };
 
 /**
  * Map nsIAccessibleRelation constants to strings. Used by
  * nsIAccessibleRetrieval::getStringRelationType() method.
  */
 static const char kRelationTypeNames[][20] = {
   "unknown",             // RELATION_NUL
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -1037,42 +1037,33 @@ nsDocAccessible::AttributeChangedImpl(ns
   if (aAttribute == nsGkAtoms::aria_busy) {
     bool isOn = aContent->AttrValueIs(aNameSpaceID, aAttribute,
                                         nsGkAtoms::_true, eCaseMatters);
     nsRefPtr<AccEvent> event = new AccStateChangeEvent(aContent, states::BUSY, isOn);
     FireDelayedAccessibleEvent(event);
     return;
   }
 
-  if (aAttribute == nsGkAtoms::selected ||
+  // ARIA or XUL selection
+  if ((aContent->IsXUL() && aAttribute == nsGkAtoms::selected) ||
       aAttribute == nsGkAtoms::aria_selected) {
-    // ARIA or XUL selection
+    nsAccessible* item = GetAccessible(aContent);
+    nsAccessible* widget =
+      nsAccUtils::GetSelectableContainer(item, item->State());
+    if (widget) {
+      AccSelChangeEvent::SelChangeType selChangeType =
+        aContent->AttrValueIs(aNameSpaceID, aAttribute,
+                              nsGkAtoms::_true, eCaseMatters) ?
+          AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
 
-    nsAccessible *multiSelect =
-      nsAccUtils::GetMultiSelectableContainer(aContent);
-    // XXX: Multi selects are handled here only (bug 414302).
-    if (multiSelect) {
-      // Need to find the right event to use here, SELECTION_WITHIN would
-      // seem right but we had started using it for something else
-      FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN,
-                                 multiSelect->GetNode(),
-                                 AccEvent::eAllowDupes);
-
-      static nsIContent::AttrValuesArray strings[] =
-        {&nsGkAtoms::_empty, &nsGkAtoms::_false, nsnull};
-      if (aContent->FindAttrValueIn(kNameSpaceID_None, aAttribute,
-                                    strings, eCaseMatters) >= 0) {
-        FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_SELECTION_REMOVE,
-                                   aContent);
-        return;
-      }
-
-      FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_SELECTION_ADD,
-                                 aContent);
+      nsRefPtr<AccEvent> event =
+        new AccSelChangeEvent(widget, item, selChangeType);
+      FireDelayedAccessibleEvent(event);
     }
+    return;
   }
 
   if (aAttribute == nsGkAtoms::contenteditable) {
     nsRefPtr<AccEvent> editableChangeEvent =
       new AccStateChangeEvent(aContent, states::EDITABLE);
     FireDelayedAccessibleEvent(editableChangeEvent);
     return;
   }
@@ -1204,17 +1195,28 @@ void nsDocAccessible::ContentAppended(ns
 {
 }
 
 void nsDocAccessible::ContentStateChanged(nsIDocument* aDocument,
                                           nsIContent* aContent,
                                           nsEventStates aStateMask)
 {
   if (aStateMask.HasState(NS_EVENT_STATE_CHECKED)) {
-    nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent);
+    nsAccessible* item = GetAccessible(aContent);
+    if (item) {
+      nsAccessible* widget = item->ContainerWidget();
+      if (widget && widget->IsSelect()) {
+        AccSelChangeEvent::SelChangeType selChangeType =
+          aContent->AsElement()->State().HasState(NS_EVENT_STATE_CHECKED) ?
+            AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
+        nsRefPtr<AccEvent> event = new AccSelChangeEvent(widget, item,
+                                                         selChangeType);
+        FireDelayedAccessibleEvent(event);
+      }
+    }
   }
 
   if (aStateMask.HasState(NS_EVENT_STATE_INVALID)) {
     nsRefPtr<AccEvent> event =
       new AccStateChangeEvent(aContent, states::INVALID, true);
     FireDelayedAccessibleEvent(event);
    }
 }
--- a/accessible/src/base/nsRootAccessible.cpp
+++ b/accessible/src/base/nsRootAccessible.cpp
@@ -468,16 +468,18 @@ nsRootAccessible::ProcessDOMEvent(nsIDOM
 
     nsRefPtr<AccEvent> accEvent =
       new AccStateChangeEvent(accessible, states::EXPANDED, isEnabled);
     nsEventShell::FireEvent(accEvent);
     return;
   }
 
   if (treeItemAccessible && eventType.EqualsLiteral("select")) {
+    // XXX: We shouldn't be based on DOM select event which doesn't provide us
+    // any context info. We should integrate into nsTreeSelection instead.
     // If multiselect tree, we should fire selectionadd or selection removed
     if (FocusMgr()->HasDOMFocus(targetNode)) {
       nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSel =
         do_QueryInterface(targetNode);
       nsAutoString selType;
       multiSel->GetSelType(selType);
       if (selType.IsEmpty() || !selType.EqualsLiteral("single")) {
         // XXX: We need to fire EVENT_SELECTION_ADD and EVENT_SELECTION_REMOVE
--- a/accessible/src/html/nsHTMLSelectAccessible.cpp
+++ b/accessible/src/html/nsHTMLSelectAccessible.cpp
@@ -411,69 +411,17 @@ nsHTMLSelectOptionAccessible::SetSelecte
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLSelectOptionAccessible: Widgets
 
 nsAccessible*
 nsHTMLSelectOptionAccessible::ContainerWidget() const
 {
-  if (mParent && mParent->IsListControl()) {
-    nsAccessible* grandParent = mParent->Parent();
-    if (grandParent && grandParent->IsCombobox())
-      return grandParent;
-
-    return mParent;
-  }
-  return nsnull;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// nsHTMLSelectOptionAccessible: static methods
-
-void
-nsHTMLSelectOptionAccessible::SelectionChangedIfOption(nsIContent *aPossibleOptionNode)
-{
-  if (!aPossibleOptionNode ||
-      aPossibleOptionNode->Tag() != nsGkAtoms::option ||
-      !aPossibleOptionNode->IsHTML()) {
-    return;
-  }
-
-  nsAccessible *multiSelect =
-    nsAccUtils::GetMultiSelectableContainer(aPossibleOptionNode);
-  if (!multiSelect)
-    return;
-
-  nsAccessible *option = GetAccService()->GetAccessible(aPossibleOptionNode);
-  if (!option)
-    return;
-
-
-  nsRefPtr<AccEvent> selWithinEvent =
-    new AccEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN, multiSelect);
-
-  if (!selWithinEvent)
-    return;
-
-  option->GetDocAccessible()->FireDelayedAccessibleEvent(selWithinEvent);
-
-  PRUint64 state = option->State();
-  PRUint32 eventType;
-  if (state & states::SELECTED) {
-    eventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
-  }
-  else {
-    eventType = nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
-  }
-
-  nsRefPtr<AccEvent> selAddRemoveEvent = new AccEvent(eventType, option);
-
-  if (selAddRemoveEvent)
-    option->GetDocAccessible()->FireDelayedAccessibleEvent(selAddRemoveEvent);
+  return mParent && mParent->IsListControl() ? mParent : nsnull;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLSelectOptionAccessible: private methods
 
 nsIContent*
 nsHTMLSelectOptionAccessible::GetSelectState(PRUint64* aState)
 {
@@ -733,36 +681,32 @@ nsHTMLComboboxAccessible::AreItemsOperab
 {
   nsIComboboxControlFrame* comboboxFrame = do_QueryFrame(GetFrame());
   return comboboxFrame && comboboxFrame->IsDroppedDown();
 }
 
 nsAccessible*
 nsHTMLComboboxAccessible::CurrentItem()
 {
-  // No current item for collapsed combobox.
-  return SelectedOption(true);
+  return AreItemsOperable() ? mListAccessible->CurrentItem() : nsnull;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLComboboxAccessible: protected
 
 nsAccessible*
-nsHTMLComboboxAccessible::SelectedOption(bool aIgnoreIfCollapsed) const
+nsHTMLComboboxAccessible::SelectedOption() const
 {
   nsIFrame* frame = GetFrame();
   nsIComboboxControlFrame* comboboxFrame = do_QueryFrame(frame);
-  if (comboboxFrame) {
-    if (aIgnoreIfCollapsed && !comboboxFrame->IsDroppedDown())
-      return nsnull;
+  if (!comboboxFrame)
+    return nsnull;
 
-    frame = comboboxFrame->GetDropDown();
-  }
-
-  nsIListControlFrame* listControlFrame = do_QueryFrame(frame);
+  nsIListControlFrame* listControlFrame =
+    do_QueryFrame(comboboxFrame->GetDropDown());
   if (listControlFrame) {
     nsCOMPtr<nsIContent> activeOptionNode = listControlFrame->GetCurrentOption();
     if (activeOptionNode) {
       nsDocAccessible* document = GetDocAccessible();
       if (document)
         return document->GetAccessible(activeOptionNode);
     }
   }
@@ -853,8 +797,24 @@ void nsHTMLComboboxListAccessible::GetBo
   if (!frame) {
     *aBoundingFrame = nsnull;
     return;
   }
 
   *aBoundingFrame = frame->GetParent();
   aBounds = (*aBoundingFrame)->GetRect();
 }
+
+////////////////////////////////////////////////////////////////////////////////
+// nsHTMLComboboxListAccessible: Widgets
+
+bool
+nsHTMLComboboxListAccessible::IsActiveWidget() const
+{
+  return mParent && mParent->IsActiveWidget();
+}
+
+bool
+nsHTMLComboboxListAccessible::AreItemsOperable() const
+{
+  return mParent && mParent->AreItemsOperable();
+}
+
--- a/accessible/src/html/nsHTMLSelectAccessible.h
+++ b/accessible/src/html/nsHTMLSelectAccessible.h
@@ -125,18 +125,16 @@ public:
                                           PRInt32 *aSetSize);
 
   // ActionAccessible
   virtual PRUint8 ActionCount();
 
   // Widgets
   virtual nsAccessible* ContainerWidget() const;
 
-  static void SelectionChangedIfOption(nsIContent *aPossibleOption);
-
 protected:
   // nsAccessible
   virtual nsIFrame* GetBoundsFrame();
 
 private:
   
   /**
    * Get Select element's accessible state
@@ -214,17 +212,17 @@ public:
 
 protected:
   // nsAccessible
   virtual void CacheChildren();
 
   /**
    * Return selected option.
    */
-  nsAccessible* SelectedOption(bool aIgnoreIfCollapsed = false) const;
+  nsAccessible* SelectedOption() const;
 
 private:
   nsRefPtr<nsHTMLComboboxListAccessible> mListAccessible;
 };
 
 /*
  * A class that represents the window that lives to the right
  * of the drop down button inside the Select. This is the window
@@ -241,11 +239,15 @@ public:
 
   // nsAccessNode
   virtual nsIFrame* GetFrame() const;
   virtual bool IsPrimaryForNode() const;
 
   // nsAccessible
   virtual PRUint64 NativeState();
   virtual void GetBoundsRect(nsRect& aBounds, nsIFrame** aBoundingFrame);
+
+  // Widgets
+  virtual bool IsActiveWidget() const;
+  virtual bool AreItemsOperable() const;
 };
 
 #endif
--- a/accessible/src/mac/mozAccessible.mm
+++ b/accessible/src/mac/mozAccessible.mm
@@ -185,19 +185,17 @@ GetNativeFromGeckoAccessible(nsIAccessib
   if (!generalAttributes) {
     // standard attributes that are shared and supported by all generic elements.
     generalAttributes = [[NSArray alloc] initWithObjects:  NSAccessibilityChildrenAttribute, 
                                                            NSAccessibilityParentAttribute,
                                                            NSAccessibilityRoleAttribute,
                                                            NSAccessibilityTitleAttribute,
                                                            NSAccessibilityValueAttribute,
                                                            NSAccessibilitySubroleAttribute,
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
                                                            NSAccessibilityRoleDescriptionAttribute,
-#endif
                                                            NSAccessibilityPositionAttribute,
                                                            NSAccessibilityEnabledAttribute,
                                                            NSAccessibilitySizeAttribute,
                                                            NSAccessibilityWindowAttribute,
                                                            NSAccessibilityFocusedAttribute,
                                                            NSAccessibilityHelpAttribute,
                                                            NSAccessibilityTitleUIElementAttribute,
                                                            kTopLevelUIElementAttribute,
@@ -231,20 +229,18 @@ GetNativeFromGeckoAccessible(nsIAccessib
   if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) 
     return [self position];
   if ([attribute isEqualToString:NSAccessibilitySubroleAttribute])
     return [self subrole];
   if ([attribute isEqualToString:NSAccessibilityEnabledAttribute])
     return [NSNumber numberWithBool:[self isEnabled]];
   if ([attribute isEqualToString:NSAccessibilityValueAttribute])
     return [self value];
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
   if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute])
     return NSAccessibilityRoleDescription([self role], nil);
-#endif
   if ([attribute isEqualToString: (NSString*) kInstanceDescriptionAttribute])
     return [self customDescription];
   if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
     return [NSNumber numberWithBool:[self isFocused]];
   if ([attribute isEqualToString:NSAccessibilitySizeAttribute])
     return [self size];
   if ([attribute isEqualToString:NSAccessibilityWindowAttribute])
     return [self window];
--- a/accessible/src/mac/mozActionElements.mm
+++ b/accessible/src/mac/mozActionElements.mm
@@ -221,34 +221,30 @@ enum CheckboxValue {
 }
 
 - (NSArray *)accessibilityActionNames
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   if ([self isEnabled]) {
     return [NSArray arrayWithObjects:NSAccessibilityPressAction,
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
                                      NSAccessibilityShowMenuAction,
-#endif
                                      nil];
   }
   return nil;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (NSString *)accessibilityActionDescription:(NSString *)action
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
   if ([action isEqualToString:NSAccessibilityShowMenuAction])
     return @"show menu";
-#endif
   return [super accessibilityActionDescription:action];
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (void)accessibilityPerformAction:(NSString *)action
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
--- a/accessible/src/mac/mozTextAccessible.mm
+++ b/accessible/src/mac/mozTextAccessible.mm
@@ -44,19 +44,17 @@ extern const NSString *kTopLevelUIElemen
   static NSArray *supportedAttributes = nil;
   if (!supportedAttributes) {
     // standard attributes that are shared and supported by all generic elements.
     supportedAttributes = [[NSArray alloc] initWithObjects:NSAccessibilityParentAttribute, // required
                                                            NSAccessibilityRoleAttribute,   // required
                                                            NSAccessibilityTitleAttribute,
                                                            NSAccessibilityValueAttribute, // required
                                                            NSAccessibilitySubroleAttribute,
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
                                                            NSAccessibilityRoleDescriptionAttribute,
-#endif
                                                            NSAccessibilityPositionAttribute, // required
                                                            NSAccessibilitySizeAttribute, // required
                                                            NSAccessibilityWindowAttribute, // required
                                                            NSAccessibilityFocusedAttribute, // required
                                                            NSAccessibilityEnabledAttribute, // required
                                                            kTopLevelUIElementAttribute, // required (on OS X 10.4+)
                                                            kInstanceDescriptionAttribute, // required (on OS X 10.4+)
                                                            /* text-specific attributes */
@@ -244,19 +242,17 @@ extern const NSString *kTopLevelUIElemen
   static NSArray *supportedAttributes = nil;
   if (!supportedAttributes) {
     // standard attributes that are shared and supported by all generic elements.
     supportedAttributes = [[NSArray alloc] initWithObjects:NSAccessibilityParentAttribute, // required
                                                            NSAccessibilityRoleAttribute,   // required
                                                            NSAccessibilityTitleAttribute,
                                                            NSAccessibilityValueAttribute, // required
                                                            NSAccessibilityHelpAttribute,
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
                                                            NSAccessibilityRoleDescriptionAttribute,
-#endif
                                                            NSAccessibilityPositionAttribute, // required
                                                            NSAccessibilitySizeAttribute, // required
                                                            NSAccessibilityWindowAttribute, // required
                                                            NSAccessibilityFocusedAttribute, // required
                                                            NSAccessibilityEnabledAttribute, // required
                                                            NSAccessibilityChildrenAttribute, // required
                                                            NSAccessibilityHelpAttribute,
                                                            // NSAccessibilityExpandedAttribute, // required
@@ -276,52 +272,46 @@ extern const NSString *kTopLevelUIElemen
 }
 
 - (NSArray *)accessibilityActionNames
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   if ([self isEnabled]) {
     return [NSArray arrayWithObjects:NSAccessibilityConfirmAction,
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
                                      NSAccessibilityShowMenuAction,
-#endif
                                      nil];
   }
   return nil;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (NSString *)accessibilityActionDescription:(NSString *)action
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
   if ([action isEqualToString:NSAccessibilityShowMenuAction])
     return @"show menu";
-#endif
   if ([action isEqualToString:NSAccessibilityConfirmAction])
     return @"confirm";
     
   return [super accessibilityActionDescription:action];
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (void)accessibilityPerformAction:(NSString *)action
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   // both the ShowMenu and Click action do the same thing.
   if ([self isEnabled]) {
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
     if ([action isEqualToString:NSAccessibilityShowMenuAction])
       [self showMenu];
-#endif
     if ([action isEqualToString:NSAccessibilityConfirmAction])
       [self confirm];
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (void)showMenu
--- a/accessible/src/msaa/nsEventMap.h
+++ b/accessible/src/msaa/nsEventMap.h
@@ -85,17 +85,17 @@ static const PRUint32 gWinEventMap[] = {
   kEVENT_WIN_UNKNOWN,                                // nsIAccessibleEvent::EVENT_MINIMIZE_START
   kEVENT_WIN_UNKNOWN,                                // nsIAccessibleEvent::EVENT_MINIMIZE_END
   IA2_EVENT_DOCUMENT_LOAD_COMPLETE,                  // nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE
   IA2_EVENT_DOCUMENT_RELOAD,                         // nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD
   IA2_EVENT_DOCUMENT_LOAD_STOPPED,                   // nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED
   IA2_EVENT_DOCUMENT_ATTRIBUTE_CHANGED,              // nsIAccessibleEvent::EVENT_DOCUMENT_ATTRIBUTES_CHANGED
   IA2_EVENT_DOCUMENT_CONTENT_CHANGED,                // nsIAccessibleEvent::EVENT_DOCUMENT_CONTENT_CHANGED
   kEVENT_WIN_UNKNOWN,                                // nsIAccessibleEvent::EVENT_PROPERTY_CHANGED
-  kEVENT_WIN_UNKNOWN,                                // nsIAccessibleEvent::EVENT_SELECTION_CHANGED
+  IA2_EVENT_PAGE_CHANGED,                            // nsIAccessibleEvent::IA2_EVENT_PAGE_CHANGED
   IA2_EVENT_TEXT_ATTRIBUTE_CHANGED,                  // nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED
   IA2_EVENT_TEXT_CARET_MOVED,                        // nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED
   IA2_EVENT_TEXT_CHANGED,                            // nsIAccessibleEvent::EVENT_TEXT_CHANGED
   IA2_EVENT_TEXT_INSERTED,                           // nsIAccessibleEvent::EVENT_TEXT_INSERTED
   IA2_EVENT_TEXT_REMOVED,                            // nsIAccessibleEvent::EVENT_TEXT_REMOVED
   IA2_EVENT_TEXT_UPDATED,                            // nsIAccessibleEvent::EVENT_TEXT_UPDATED
   IA2_EVENT_TEXT_SELECTION_CHANGED,                  // nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED
   IA2_EVENT_VISIBLE_DATA_CHANGED,                    // nsIAccessibleEvent::EVENT_VISIBLE_DATA_CHANGED
@@ -126,12 +126,11 @@ static const PRUint32 gWinEventMap[] = {
   IA2_EVENT_HYPERLINK_NUMBER_OF_ANCHORS_CHANGED,     // nsIAccessibleEvent::EVENT_HYPERLINK_NUMBER_OF_ANCHORS_CHANGED
   IA2_EVENT_HYPERLINK_SELECTED_LINK_CHANGED,         // nsIAccessibleEvent::EVENT_HYPERLINK_SELECTED_LINK_CHANGED
   IA2_EVENT_HYPERTEXT_LINK_ACTIVATED,                // nsIAccessibleEvent::EVENT_HYPERTEXT_LINK_ACTIVATED
   IA2_EVENT_HYPERTEXT_LINK_SELECTED,                 // nsIAccessibleEvent::EVENT_HYPERTEXT_LINK_SELECTED
   IA2_EVENT_HYPERLINK_START_INDEX_CHANGED,           // nsIAccessibleEvent::EVENT_HYPERLINK_START_INDEX_CHANGED
   IA2_EVENT_HYPERTEXT_CHANGED,                       // nsIAccessibleEvent::EVENT_HYPERTEXT_CHANGED
   IA2_EVENT_HYPERTEXT_NLINKS_CHANGED,                // nsIAccessibleEvent::EVENT_HYPERTEXT_NLINKS_CHANGED
   IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED,                // nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED
-  IA2_EVENT_PAGE_CHANGED,                            // nsIAccessibleEvent::EVENT_PAGE_CHANGED
   kEVENT_LAST_ENTRY                                  // nsIAccessibleEvent::EVENT_LAST_ENTRY
 };
 
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -10,17 +10,19 @@ const EVENT_FOCUS = nsIAccessibleEvent.E
 const EVENT_NAME_CHANGE = nsIAccessibleEvent.EVENT_NAME_CHANGE;
 const EVENT_MENU_START = nsIAccessibleEvent.EVENT_MENU_START;
 const EVENT_MENU_END = nsIAccessibleEvent.EVENT_MENU_END;
 const EVENT_MENUPOPUP_START = nsIAccessibleEvent.EVENT_MENUPOPUP_START;
 const EVENT_MENUPOPUP_END = nsIAccessibleEvent.EVENT_MENUPOPUP_END;
 const EVENT_OBJECT_ATTRIBUTE_CHANGED = nsIAccessibleEvent.EVENT_OBJECT_ATTRIBUTE_CHANGED;
 const EVENT_REORDER = nsIAccessibleEvent.EVENT_REORDER;
 const EVENT_SCROLLING_START = nsIAccessibleEvent.EVENT_SCROLLING_START;
+const EVENT_SELECTION = nsIAccessibleEvent.EVENT_SELECTION;
 const EVENT_SELECTION_ADD = nsIAccessibleEvent.EVENT_SELECTION_ADD;
+const EVENT_SELECTION_REMOVE = nsIAccessibleEvent.EVENT_SELECTION_REMOVE;
 const EVENT_SELECTION_WITHIN = nsIAccessibleEvent.EVENT_SELECTION_WITHIN;
 const EVENT_SHOW = nsIAccessibleEvent.EVENT_SHOW;
 const EVENT_STATE_CHANGE = nsIAccessibleEvent.EVENT_STATE_CHANGE;
 const EVENT_TEXT_ATTRIBUTE_CHANGED = nsIAccessibleEvent.EVENT_TEXT_ATTRIBUTE_CHANGED;
 const EVENT_TEXT_CARET_MOVED = nsIAccessibleEvent.EVENT_TEXT_CARET_MOVED;
 const EVENT_TEXT_INSERTED = nsIAccessibleEvent.EVENT_TEXT_INSERTED;
 const EVENT_TEXT_REMOVED = nsIAccessibleEvent.EVENT_TEXT_REMOVED;
 const EVENT_TEXT_SELECTION_CHANGED = nsIAccessibleEvent.EVENT_TEXT_SELECTION_CHANGED;
--- a/accessible/tests/mochitest/events/Makefile.in
+++ b/accessible/tests/mochitest/events/Makefile.in
@@ -77,17 +77,19 @@ include $(topsrcdir)/config/rules.mk
 		test_focus_name.html \
 		test_focus_selects.html \
 		test_focus_tabbox.xul \
 		test_focus_tree.xul \
 		test_menu.xul \
 		test_mutation.html \
 		test_mutation.xhtml \
 		test_scroll.xul \
+		test_selection_aria.html \
 		test_selection.html \
+		test_selection.xul \
 		test_statechange.html \
 		test_text_alg.html \
 		test_text.html \
 		test_textattrchange.html \
 		test_tree.xul \
 		test_valuechange.html \
 		$(NULL)
 
--- a/accessible/tests/mochitest/events/test_selection.html
+++ b/accessible/tests/mochitest/events/test_selection.html
@@ -17,78 +17,97 @@
           src="../events.js"></script>
   <script type="application/javascript"
           src="../states.js"></script>
 
   <script type="application/javascript">
     ////////////////////////////////////////////////////////////////////////////
     // Invokers
 
-    function addSelection(aNode, aOption)
-    {
-      this.DOMNode = aNode;
-      this.optionNode = aOption;
-
-      this.eventSeq = [
-        new invokerChecker(EVENT_SELECTION_WITHIN, getAccessible(this.DOMNode)),
-        new invokerChecker(EVENT_SELECTION_ADD, getAccessible(this.optionNode))
-      ];
-
-      this.invoke = function addselection_invoke() {
-        synthesizeMouse(this.optionNode, 1, 1, {});
-      };
-
-      this.getID = function addselection_getID() {
-        return prettyName(this.optionNode) + " added to selection";
-      };
-    }
-
     ////////////////////////////////////////////////////////////////////////////
     // Do tests
 
-    var gQueue = null;
+    //gA11yEventDumpToConsole = true; // debuggin
 
-    //var gA11yEventDumpID = "eventdump"; // debug stuff
-
+    var gQueue = null;
     function doTests()
     {
       gQueue = new eventQueue();
 
-      var select = document.getElementById("toppings");
-      var option = document.getElementById("onions");
-      gQueue.push(new addSelection(select, option));
+      // open combobox
+      gQueue.push(new synthClick("combobox",
+                                 new invokerChecker(EVENT_FOCUS, "cb1_item1")));
+      gQueue.push(new synthDownKey("cb1_item1",
+                                   new invokerChecker(EVENT_SELECTION, "cb1_item2")));
+
+      // closed combobox
+      gQueue.push(new synthEscapeKey("combobox",
+                                     new invokerChecker(EVENT_FOCUS, "combobox")));
+      gQueue.push(new synthDownKey("cb1_item2",
+                                   new invokerChecker(EVENT_SELECTION, "cb1_item3")));
+
+      // listbox
+      gQueue.push(new synthClick("lb1_item1",
+                                 new invokerChecker(EVENT_SELECTION, "lb1_item1")));
+      gQueue.push(new synthDownKey("lb1_item1",
+                                   new invokerChecker(EVENT_SELECTION, "lb1_item2")));
+
+      // multiselectable listbox
+      gQueue.push(new synthClick("lb2_item1",
+                                 new invokerChecker(EVENT_SELECTION, "lb2_item1")));
+      gQueue.push(new synthDownKey("lb2_item1",
+                                   new invokerChecker(EVENT_SELECTION_ADD, "lb2_item2"),
+                                   { shiftKey: true }));
+      gQueue.push(new synthUpKey("lb2_item2",
+                                 new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item2"),
+                                 { shiftKey: true }));
+      gQueue.push(new synthKey("lb2_item1", " ", { ctrlKey: true },
+                               new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item1")));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
 
 <body>
 
   <a target="_blank"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=569653"
-     title="Make selection events async">
-    Mozilla Bug 569653
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=414302"
+     title="Incorrect selection events in HTML, XUL and ARIA">
+    Mozilla Bug 414302
   </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
-  <p>Pizza</p>
-  <select id="toppings" name="toppings" multiple size=5>
-    <option value="mushrooms">mushrooms
-    <option value="greenpeppers">green peppers
-    <option value="onions" id="onions">onions
-    <option value="tomatoes">tomatoes
-    <option value="olives">olives
+  <select id="combobox">
+    <option id="cb1_item1" value="mushrooms">mushrooms
+    <option id="cb1_item2" value="greenpeppers">green peppers
+    <option id="cb1_item3" value="onions" id="onions">onions
+    <option id="cb1_item4" value="tomatoes">tomatoes
+    <option id="cb1_item5" value="olives">olives
   </select>
 
-  <div id="testContainer">
-    <iframe id="iframe"></iframe>
-  </div>
+  <select id="listbox" size=5>
+    <option id="lb1_item1" value="mushrooms">mushrooms
+    <option id="lb1_item2" value="greenpeppers">green peppers
+    <option id="lb1_item3" value="onions" id="onions">onions
+    <option id="lb1_item4" value="tomatoes">tomatoes
+    <option id="lb1_item5" value="olives">olives
+  </select>
+
+  <p>Pizza</p>
+  <select id="listbox2" multiple size=5>
+    <option id="lb2_item1" value="mushrooms">mushrooms
+    <option id="lb2_item2" value="greenpeppers">green peppers
+    <option id="lb2_item3" value="onions" id="onions">onions
+    <option id="lb2_item4" value="tomatoes">tomatoes
+    <option id="lb2_item5" value="olives">olives
+  </select>
+
   <div id="eventdump"></div>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_selection.xul
@@ -0,0 +1,244 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="Selection event tests">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+  <script type="application/javascript"
+          src="../common.js" />
+  <script type="application/javascript"
+          src="../states.js" />
+  <script type="application/javascript"
+          src="../events.js" />
+
+  <script type="application/javascript">
+    function advanceTab(aTabsID, aDirection, aNextTabID)
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_SELECTION, aNextTabID)
+      ];
+
+      this.invoke = function advanceTab_invoke()
+      {
+        getNode(aTabsID).advanceSelectedTab(aDirection, true);
+      }
+
+      this.getID = function synthFocus_getID()
+      {
+        return "advanceTab on " + prettyName(aTabsID) + " to " + prettyName(aNextTabID);
+      }
+    }
+
+    function select4FirstItems(aID)
+    {
+      this.listboxNode = getNode(aID);
+      this.eventSeq = [
+        new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(0)),
+        new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(1)),
+        new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(2)),
+        new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(3))
+      ];
+
+      this.invoke = function select4FirstItems_invoke()
+      {
+        synthesizeKey("VK_DOWN", { shiftKey: true }); // selects two items
+        synthesizeKey("VK_DOWN", { shiftKey: true });
+        synthesizeKey("VK_DOWN", { shiftKey: true });
+      }
+
+      this.getID = function select4FirstItems_getID()
+      {
+        return "select 4 first items for " + prettyName(aID);
+      }
+    }
+
+    function unselect4FirstItems(aID)
+    {
+      this.listboxNode = getNode(aID);
+      this.eventSeq = [
+        new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(3)),
+        new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(2)),
+        new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(1)),
+        new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(0))
+      ];
+
+      this.invoke = function unselect4FirstItems_invoke()
+      {
+        synthesizeKey("VK_UP", { shiftKey: true });
+        synthesizeKey("VK_UP", { shiftKey: true });
+        synthesizeKey("VK_UP", { shiftKey: true });
+        synthesizeKey(" ", { ctrlKey: true }); // unselect first item
+      }
+
+      this.getID = function unselect4FirstItems_getID()
+      {
+        return "unselect 4 first items for " + prettyName(aID);
+      }
+    }
+
+    function selectAllItems(aID)
+    {
+      this.listboxNode = getNode(aID);
+      this.eventSeq = [
+        new invokerChecker(EVENT_SELECTION_WITHIN, getAccessible(this.listboxNode))
+      ];
+
+      this.invoke = function selectAllItems_invoke()
+      {
+        synthesizeKey("VK_END", { shiftKey: true });
+      }
+
+      this.getID = function selectAllItems_getID()
+      {
+        return "select all items for " + prettyName(aID);
+      }
+    }
+
+    function unselectAllItemsButFirst(aID)
+    {
+      this.listboxNode = getNode(aID);
+      this.eventSeq = [
+        new invokerChecker(EVENT_SELECTION_WITHIN, getAccessible(this.listboxNode))
+      ];
+
+      this.invoke = function unselectAllItemsButFirst_invoke()
+      {
+        synthesizeKey("VK_HOME", { shiftKey: true });
+      }
+
+      this.getID = function unselectAllItemsButFirst_getID()
+      {
+        return "unselect all items for " + prettyName(aID);
+      }
+    }
+
+    function unselectSelectItem(aID)
+    {
+      this.listboxNode = getNode(aID);
+      this.eventSeq = [
+        new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(0)),
+        new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(0))
+      ];
+
+      this.invoke = function unselectSelectItem_invoke()
+      {
+        synthesizeKey(" ", { ctrlKey: true }); // select item
+        synthesizeKey(" ", { ctrlKey: true }); // unselect item
+      }
+
+      this.getID = function unselectSelectItem_getID()
+      {
+        return "unselect and then select first item for " + prettyName(aID);
+      }
+    }
+
+    /**
+     * Do tests.
+     */
+    var gQueue = null;
+
+    //gA11yEventDumpToConsole = true; // debuggin
+
+    function doTests()
+    {
+      gQueue = new eventQueue();
+
+      //////////////////////////////////////////////////////////////////////////
+      // tabbox
+      gQueue.push(new advanceTab("tabs", 1, "tab3"));
+
+      //////////////////////////////////////////////////////////////////////////
+      // listbox
+      gQueue.push(new synthClick("lb1_item1",
+                                 new invokerChecker(EVENT_SELECTION, "lb1_item1")));
+      gQueue.push(new synthDownKey("lb1_item1",
+                                   new invokerChecker(EVENT_SELECTION, "lb1_item2")));
+
+      //////////////////////////////////////////////////////////////////////////
+      // multiselectable listbox
+      gQueue.push(new synthClick("lb2_item1",
+                                 new invokerChecker(EVENT_SELECTION, "lb2_item1")));
+      gQueue.push(new synthDownKey("lb2_item1",
+                                   new invokerChecker(EVENT_SELECTION_ADD, "lb2_item2"),
+                                   { shiftKey: true }));
+      gQueue.push(new synthUpKey("lb2_item2",
+                                 new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item2"),
+                                 { shiftKey: true }));
+      gQueue.push(new synthKey("lb2_item1", " ", { ctrlKey: true },
+                               new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item1")));
+
+      //////////////////////////////////////////////////////////////////////////
+      // selection event coalescence
+
+      // fire 4 selection_add events
+      gQueue.push(new select4FirstItems("listbox2"));
+      // fire 4 selection_remove events
+      gQueue.push(new unselect4FirstItems("listbox2"));
+      // fire selection_within event
+      gQueue.push(new selectAllItems("listbox2"));
+      // fire selection_within event
+      gQueue.push(new unselectAllItemsButFirst("listbox2"));
+      // fire selection_remove/add events
+      gQueue.push(new unselectSelectItem("listbox2"));
+
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </script>
+
+  <hbox flex="1" style="overflow: auto;">
+    <body xmlns="http://www.w3.org/1999/xhtml">
+      <a target="_blank"
+         href="https://bugzilla.mozilla.org/show_bug.cgi?id=414302"
+         title="Incorrect selection events in HTML, XUL and ARIA">
+        Mozilla Bug 414302
+      </a>
+      <p id="display"></p>
+      <div id="content" style="display: none"></div>
+      <pre id="test">
+      </pre>
+    </body>
+
+    <tabbox id="tabbox" selectedIndex="1">
+      <tabs id="tabs">
+        <tab id="tab1" label="tab1"/>
+        <tab id="tab2" label="tab2"/>
+        <tab id="tab3" label="tab3"/>
+        <tab id="tab4" label="tab4"/>
+      </tabs>
+      <tabpanels>
+        <tabpanel><!-- tabpanel First elements go here --></tabpanel>
+        <tabpanel><button id="b1" label="b1"/></tabpanel>
+        <tabpanel><button id="b2" label="b2"/></tabpanel>
+        <tabpanel></tabpanel>
+      </tabpanels>
+    </tabbox>
+
+    <listbox id="listbox">
+      <listitem id="lb1_item1" label="item1"/>
+      <listitem id="lb1_item2" label="item2"/>
+    </listbox>
+
+    <listbox id="listbox2" seltype="multiple">
+      <listitem id="lb2_item1" label="item1"/>
+      <listitem id="lb2_item2" label="item2"/>
+      <listitem id="lb2_item3" label="item3"/>
+      <listitem id="lb2_item4" label="item4"/>
+      <listitem id="lb2_item5" label="item5"/>
+      <listitem id="lb2_item6" label="item6"/>
+      <listitem id="lb2_item7" label="item7"/>
+    </listbox>
+
+  </hbox>
+</window>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_selection_aria.html
@@ -0,0 +1,112 @@
+<html>
+
+<head>
+  <title>ARIA selection event testing</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+  <script type="application/javascript"
+          src="../states.js"></script>
+
+  <script type="application/javascript">
+    ////////////////////////////////////////////////////////////////////////////
+    // Invokers
+
+    function selectTreeItem(aTreeID, aItemID)
+    {
+      this.treeNode = getNode(aTreeID);
+      this.itemNode = getNode(aItemID);
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_SELECTION, aItemID)
+      ];
+
+      this.invoke = function selectTreeItem_invoke() {
+        var itemNode = this.treeNode.querySelector("*[aria-selected='true']");
+        if (itemNode)
+          itemNode.removeAttribute("aria-selected", "true");
+
+        this.itemNode.setAttribute("aria-selected", "true");
+      }
+
+      this.getID = function selectTreeItem_getID()
+      {
+        return "selectTreeItem " + prettyName(aItemID);
+      }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Do tests
+
+    var gQueue = null;
+
+    //var gA11yEventDumpID = "eventdump"; // debug stuff
+
+    function doTests()
+    {
+      gQueue = new eventQueue();
+
+      gQueue.push(new selectTreeItem("tree", "treeitem1"));
+      gQueue.push(new selectTreeItem("tree", "treeitem1a"));
+      gQueue.push(new selectTreeItem("tree", "treeitem1a1"));
+
+      gQueue.push(new selectTreeItem("tree2", "tree2item1"));
+      gQueue.push(new selectTreeItem("tree2", "tree2item1a"));
+      gQueue.push(new selectTreeItem("tree2", "tree2item1a1"));
+
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </script>
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=569653"
+     title="Make selection events async">
+    Mozilla Bug 569653
+  </a>
+
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <div id="tree" role="tree">
+    <div id="treeitem1" role="treeitem">Canada
+      <div id="treeitem1a" role="treeitem">- Ontario
+        <div id="treeitem1a1" role="treeitem">-- Toronto</div>
+      </div>
+      <div id="treeitem1b" role="treeitem">- Manitoba</div>
+    </div>
+    <div id="treeitem2" role="treeitem">Germany</div>
+    <div id="treeitem3" role="treeitem">Russia</div>
+  </div>
+
+  <div id="tree2" role="tree" aria-multiselectable="true">
+    <div id="tree2item1" role="treeitem">Canada
+      <div id="tree2item1a" role="treeitem">- Ontario
+        <div id="tree2item1a1" role="treeitem">-- Toronto</div>
+      </div>
+      <div id="tree2item1b" role="treeitem">- Manitoba</div>
+    </div>
+    <div id="tree2item2" role="treeitem">Germany</div>
+    <div id="tree2item3" role="treeitem">Russia</div>
+  </div>
+
+  <div id="eventdump"></div>
+</body>
+</html>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -999,20 +999,24 @@ pref("services.sync.prefs.sync.spellchec
 pref("services.sync.prefs.sync.xpinstall.whitelist.required", true);
 #endif
 
 // Disable the error console
 pref("devtools.errorconsole.enabled", false);
 
 // Enable the Inspector
 pref("devtools.inspector.enabled", true);
+pref("devtools.inspector.htmlHeight", 112);
 
 // Enable the style inspector
 pref("devtools.styleinspector.enabled", true);
 
+// Enable the rules view
+pref("devtools.ruleview.enabled", true);
+
 // Enable the Scratchpad tool.
 pref("devtools.scratchpad.enabled", true);
 
 // Enable tools for Chrome development.
 pref("devtools.chrome.enabled", false);
 
 // Disable the GCLI enhanced command line.
 pref("devtools.gcli.enable", false);
@@ -1055,8 +1059,11 @@ pref("devtools.editor.component", "orion
 // Whether the character encoding menu is under the main Firefox button. This
 // preference is a string so that localizers can alter it.
 pref("browser.menu.showCharacterEncoding", "chrome://browser/locale/browser.properties");
 
 // Allow using tab-modal prompts when possible.
 pref("prompts.tab_modal.enabled", true);
 // Whether the Panorama should animate going in/out of tabs
 pref("browser.panorama.animate_zoom", true);
+
+// Enable the DOM full-screen API.
+pref("full-screen-api.enabled", true);
--- a/browser/base/content/aboutHome.css
+++ b/browser/base/content/aboutHome.css
@@ -364,13 +364,21 @@ body[dir=rtl] #restorePreviousSession::b
   position: absolute;
   color: rgb(150,150,150);
   font-size: .8em;
   width: 100%;
   text-align: center;
   bottom: 2%;
 }
 
+#syncLinksContainer {
+  padding-top: 1em;
+}
+
+.sync-link {
+  padding: 1em;
+}
+
 @media all and (max-height: 370px) {
   #bottomSection {
     visibility: hidden;
   }
 }
--- a/browser/base/content/aboutHome.xhtml
+++ b/browser/base/content/aboutHome.xhtml
@@ -97,11 +97,15 @@
         <button id="restorePreviousSession">&historyRestoreLastSession.label;</button>
       </div>
     </div>
 
     <div id="bottomSection">
       <div id="aboutMozilla">
         <a href="http://www.mozilla.com/about/">&abouthome.aboutMozilla;</a>
       </div>
+      <div id="syncLinksContainer">
+        <a href="javascript:void(0);" class="sync-link" id="setupSyncLink">&abouthome.syncSetup.label;</a>
+        <a href="javascript:void(0);" class="sync-link" id="pairDeviceLink">&abouthome.pairDevice.label;</a>
+      </div>
     </div>
   </body>
 </html>
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -140,22 +140,18 @@
              oncommand="PlacesCommandHook.showPlacesOrganizer('AllBookmarks');"/>
     <command id="Browser:ShowAllHistory"
              oncommand="PlacesCommandHook.showPlacesOrganizer('History');"/>
   </commandset>
 
   <commandset id="inspectorCommands">
     <command id="Inspector:Inspect"
              oncommand="InspectorUI.toggleInspection();"/>
-    <command id="Inspector:Previous"
-             oncommand="InspectorUI.inspectPrevious();"
-             disabled="true"/>
-    <command id="Inspector:Next"
-             oncommand="InspectorUI.inspectNext();"
-             disabled="true"/>
+    <command id="Inspector:Sidebar"
+             oncommand="InspectorUI.toggleSidebar();"/>
   </commandset>
 
   <broadcasterset id="mainBroadcasterSet">
     <broadcaster id="viewBookmarksSidebar" autoCheck="false" label="&bookmarksButton.label;"
                  type="checkbox" group="sidebar" sidebarurl="chrome://browser/content/bookmarks/bookmarksPanel.xul"
                  oncommand="toggleSidebar('viewBookmarksSidebar');"/>
 
     <!-- for both places and non-places, the sidebar lives at
--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -206,16 +206,34 @@ let gSyncUI = {
   },
 
   onLoginFinish: function SUI_onLoginFinish() {
     // Clear out any login failure notifications
     let title = this._stringBundle.GetStringFromName("error.login.title");
     this.clearError(title);
   },
 
+  // Set visibility of "Setup Sync" link
+  showSetupSyncAboutHome: function SUI_showSetupSyncAboutHome(toShow) {
+    let browsers = gBrowser.browsers;
+    for (let i = 0; i < browsers.length; i++) {
+      let b = browsers[i];
+      if ("about:home" == b.currentURI.spec) {
+        b.contentDocument.getElementById("setupSyncLink").hidden = !toShow;
+      }
+    }
+  },
+
+  onSetupComplete: function SUI_onSetupComplete() {
+    // Remove "setup sync" link in about:home if it is open. 
+    this.showSetupSyncAboutHome(false);
+
+    onLoginFinish();
+  },
+
   onLoginError: function SUI_onLoginError() {
     // if login fails, any other notifications are essentially moot
     Weave.Notifications.removeAll();
 
     // if we haven't set up the client, don't show errors
     if (this._needsSetup()) {
       this.updateUI();
       return;
@@ -250,16 +268,18 @@ let gSyncUI = {
   },
 
   onLogout: function SUI_onLogout() {
     this.updateUI();
   },
 
   onStartOver: function SUI_onStartOver() {
     this.clearError();
+    // Make "setup sync" link visible in about:home if it is open. 
+    this.showSetupSyncAboutHome(true);
   },
 
   onQuotaNotice: function onQuotaNotice(subject, data) {
     let title = this._stringBundle.GetStringFromName("warning.sync.quota.label");
     let description = this._stringBundle.GetStringFromName("warning.sync.quota.description");
     let buttons = [];
     buttons.push(new Weave.NotificationButton(
       this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.label"),
@@ -286,26 +306,50 @@ let gSyncUI = {
     if (this._needsSetup())
       this.openSetup();
     else
       this.doSync();
   },
 
   //XXXzpao should be part of syncCommon.js - which we might want to make a module...
   //        To be fixed in a followup (bug 583366)
-  openSetup: function SUI_openSetup() {
+
+  /**
+   * Invoke the Sync setup wizard.
+   *
+   * @param wizardType
+   *        Indicates type of wizard to launch:
+   *          null    -- regular set up wizard
+   *          "pair"  -- pair a device first
+   *          "reset" -- reset sync
+   */
+
+  openSetup: function SUI_openSetup(wizardType) {
     let win = Services.wm.getMostRecentWindow("Weave:AccountSetup");
     if (win)
       win.focus();
     else {
       window.openDialog("chrome://browser/content/syncSetup.xul",
-                        "weaveSetup", "centerscreen,chrome,resizable=no");
+                        "weaveSetup", "centerscreen,chrome,resizable=no",
+                        wizardType);
     }
   },
 
+  openAddDevice: function () {
+    if (!Weave.Utils.ensureMPUnlocked())
+      return;
+
+    let win = Services.wm.getMostRecentWindow("Sync:AddDevice");
+    if (win)
+      win.focus();
+    else
+      window.openDialog("chrome://browser/content/syncAddDevice.xul",
+                        "syncAddDevice", "centerscreen,chrome,resizable=no");
+  },
+
   openQuotaDialog: function SUI_openQuotaDialog() {
     let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
     if (win)
       win.focus();
     else
       Services.ww.activeWindow.openDialog(
         "chrome://browser/content/syncQuota.xul", "",
         "centerscreen,chrome,dialog,modal");
@@ -457,17 +501,17 @@ let gSyncUI = {
         break;
       case "weave:service:sync:delayed":
         this.onSyncDelay();
         break;
       case "weave:service:quota:remaining":
         this.onQuotaNotice();
         break;
       case "weave:service:setup-complete":
-        this.onLoginFinish();
+        this.onSetupComplete();
         break;
       case "weave:service:login:start":
         this.onActivityStart();
         break;
       case "weave:service:login:finish":
         this.onLoginFinish();
         break;
       case "weave:ui:login:error":
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2670,25 +2670,31 @@ function PageProxyClickHandler(aEvent)
  *  to the DOM for unprivileged pages.
  */
 function BrowserOnAboutPageLoad(document) {
   if (/^about:home$/i.test(document.documentURI)) {
     let ss = Components.classes["@mozilla.org/browser/sessionstore;1"].
              getService(Components.interfaces.nsISessionStore);
     if (!ss.canRestoreLastSession)
       document.getElementById("sessionRestoreContainer").hidden = true;
+    // Sync-related links
+    if (Services.prefs.prefHasUserValue("services.sync.username")) {
+      document.getElementById("setupSyncLink").hidden = true;
+    }
   }
 }
 
 /**
  * Handle command events bubbling up from error page content
  */
 function BrowserOnClick(event) {
     // Don't trust synthetic events
-    if (!event.isTrusted || event.target.localName != "button")
+    if (!event.isTrusted ||
+        (event.target.localName != "button" &&
+         event.target.className != "sync-link"))
       return;
 
     var ot = event.originalTarget;
     var errorDoc = ot.ownerDocument;
 
     // If the event came from an ssl error page, it is probably either the "Add
     // Exception…" or "Get me out of here!" button
     if (/^about:certerror/.test(errorDoc.documentURI)) {
@@ -2808,16 +2814,26 @@ function BrowserOnClick(event) {
     else if (/^about:home$/i.test(errorDoc.documentURI)) {
       if (ot == errorDoc.getElementById("restorePreviousSession")) {
         let ss = Cc["@mozilla.org/browser/sessionstore;1"].
                  getService(Ci.nsISessionStore);
         if (ss.canRestoreLastSession)
           ss.restoreLastSession();
         errorDoc.getElementById("sessionRestoreContainer").hidden = true;
       }
+      else if (ot == errorDoc.getElementById("pairDeviceLink")) {
+        if (Services.prefs.prefHasUserValue("services.sync.username")) {
+          gSyncUI.openAddDevice();
+        } else {
+          gSyncUI.openSetup("pair");
+        }
+      }
+      else if (ot == errorDoc.getElementById("setupSyncLink")) {
+        gSyncUI.openSetup(null);
+      }
     }
 }
 
 /**
  * Re-direct the browser to a known-safe page.  This function is
  * used when, for example, the user browses to a known malware page
  * and is presented with about:blocked.  The "Get me out of here!"
  * button should take the user to the default start page so that even
@@ -3887,22 +3903,61 @@ var FullScreen = {
       this._isAnimating = false;
       // This is needed if they use the context menu to quit fullscreen
       this._isPopupOpen = false;
 
       this.cleanup();
     }
   },
 
+  exitDomFullScreen : function(e) {
+    document.mozCancelFullScreen();
+  },
+
   enterDomFullScreen : function(event) {
     if (!document.mozFullScreen) {
       return;
     }
+
+    // We receive "mozfullscreenchange" events for each subdocument which
+    // is an ancestor of the document containing the element which requested
+    // full-screen. Only add listeners and show warning etc when the event we
+    // receive is targeted at the chrome document, i.e. only once every time
+    // we enter DOM full-screen mode.
+    let targetDoc = event.target.ownerDocument ? event.target.ownerDocument : event.target;
+    if (targetDoc != document) {
+      // However, if we receive a "mozfullscreenchange" event for a document
+      // which is not a subdocument of the currently selected tab, we know that
+      // we've switched tabs since the request to enter full-screen was made,
+      // so we should exit full-screen since the "full-screen document" isn't
+      // acutally visible.
+      if (targetDoc.defaultView.top != gBrowser.contentWindow) {
+        document.mozCancelFullScreen();
+      }
+      return;
+    }
+
+    let focusManger = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
+    if (focusManger.activeWindow != window) {
+      // The top-level window has lost focus since the request to enter
+      // full-screen was made. Cancel full-screen.
+      document.mozCancelFullScreen();
+      return;
+    }
+
     this.showWarning(true);
 
+    // Exit DOM full-screen mode upon open, close, or change tab.
+    gBrowser.tabContainer.addEventListener("TabOpen", this.exitDomFullScreen);
+    gBrowser.tabContainer.addEventListener("TabClose", this.exitDomFullScreen);
+    gBrowser.tabContainer.addEventListener("TabSelect", this.exitDomFullScreen);
+
+    // Exit DOM full-screen mode when the browser window loses focus (ALT+TAB, etc).
+    window.addEventListener("deactivate", this.exitDomFullScreen, true);
+
     // Cancel any "hide the toolbar" animation which is in progress, and make
     // the toolbar hide immediately.
     clearInterval(this._animationInterval);
     clearTimeout(this._animationTimeout);
     this._isAnimating = false;
     this._shouldAnimate = false;
     this.mouseoverToggle(false);
 
@@ -3925,16 +3980,20 @@ var FullScreen = {
       gPrefService.removeObserver("browser.fullscreen", this);
 
       let fullScrToggler = document.getElementById("fullscr-toggler");
       if (fullScrToggler) {
         fullScrToggler.removeEventListener("mouseover", this._expandCallback, false);
         fullScrToggler.removeEventListener("dragenter", this._expandCallback, false);
       }
       this.cancelWarning();
+      gBrowser.tabContainer.removeEventListener("TabOpen", this.exitDomFullScreen);
+      gBrowser.tabContainer.removeEventListener("TabClose", this.exitDomFullScreen);
+      gBrowser.tabContainer.removeEventListener("TabSelect", this.exitDomFullScreen);
+      window.removeEventListener("deactivate", this.exitDomFullScreen, true);
     }
   },
 
   observe: function(aSubject, aTopic, aData)
   {
     if (aData == "browser.fullscreen.autohide") {
       if (gPrefService.getBoolPref("browser.fullscreen.autohide")) {
         gBrowser.mPanelContainer.addEventListener("mousemove",
@@ -8148,17 +8207,17 @@ let DownloadMonitorPanel = {
       return;
     }
 
     // Find the download with the longest remaining time
     let numPaused = 0;
     let maxTime = -Infinity;
     let dls = gDownloadMgr.activeDownloads;
     while (dls.hasMoreElements()) {
-      let dl = dls.getNext().QueryInterface(Ci.nsIDownload);
+      let dl = dls.getNext();
       if (dl.state == gDownloadMgr.DOWNLOAD_DOWNLOADING) {
         // Figure out if this download takes longer
         if (dl.speed > 0 && dl.size > 0)
           maxTime = Math.max(maxTime, (dl.size - dl.amountTransferred) / dl.speed);
         else
           maxTime = -1;
       }
       else if (dl.state == gDownloadMgr.DOWNLOAD_PAUSED)
@@ -8895,24 +8954,26 @@ function toggleAddonBar() {
   let addonBar = document.getElementById("addon-bar");
   setToolbarVisibility(addonBar, addonBar.collapsed);
 }
 
 var Scratchpad = {
   prefEnabledName: "devtools.scratchpad.enabled",
 
   openScratchpad: function SP_openScratchpad() {
-    const SCRATCHPAD_WINDOW_URL = "chrome://browser/content/scratchpad.xul";
-    const SCRATCHPAD_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
-
-    return Services.ww.openWindow(null, SCRATCHPAD_WINDOW_URL, "_blank",
-                                  SCRATCHPAD_WINDOW_FEATURES, null);
-  },
+    return this.ScratchpadManager.openScratchpad();
+  }
 };
 
+XPCOMUtils.defineLazyGetter(Scratchpad, "ScratchpadManager", function() {
+  let tmp = {};
+  Cu.import("resource:///modules/devtools/scratchpad-manager.jsm", tmp);
+  return tmp.ScratchpadManager;
+});
+
 
 XPCOMUtils.defineLazyGetter(window, "gShowPageResizers", function () {
 #ifdef XP_WIN
   // Only show resizers on Windows 2000 and XP
   let sysInfo = Components.classes["@mozilla.org/system-info;1"]
                           .getService(Components.interfaces.nsIPropertyBag2);
   return parseFloat(sysInfo.getProperty("version")) < 6;
 #else
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -961,16 +961,22 @@
       <tabbrowser id="content" disablehistory="true"
                   flex="1" contenttooltip="aHTMLTooltip"
                   tabcontainer="tabbrowser-tabs"
                   contentcontextmenu="contentAreaContextMenu"
                   autocompletepopup="PopupAutoComplete"
                   onclick="return contentAreaClick(event, false);"/>
       <statuspanel id="statusbar-display" inactive="true"/>
     </vbox>
+    <splitter id="devtools-side-splitter" hidden="true"/>
+    <vbox id="devtools-sidebar-box" hidden="true"
+          style="min-width: 18em; width: 22em; max-width: 42em;" persist="width">
+      <toolbar id="devtools-sidebar-toolbar" nowindowdrag="true"/>
+      <deck id="devtools-sidebar-deck" flex="1"/>
+    </vbox>
     <vbox id="browser-border-end" hidden="true" layer="true"/>
   </hbox>
 
   <hbox id="full-screen-warning-container" hidden="true" fadeout="true">
     <hbox style="min-width: 100%;" pack="center"> <!-- Inner hbox needed due to bug 579776. -->
       <hbox id="full-screen-warning-message">
         <description id="full-screen-warning-text" value="&domFullScreenWarning.label;"></description>
       </hbox>
@@ -995,16 +1001,20 @@
           <toolbarbutton id="inspector-inspect-toolbutton"
                          label="&inspectButton.label;"
                          accesskey="&inspectButton.accesskey;"
                          command="Inspector:Inspect"/>
           <arrowscrollbox id="inspector-breadcrumbs"
                           flex="1" orient="horizontal"
                           clicktoscroll="true"/>
           <hbox id="inspector-tools">
+            <toolbarbutton id="inspector-style-button"
+                           label="&inspectStyleButton.label;"
+                           accesskey="&inspectStyleButton.accesskey;"
+                           command="Inspector:Sidebar"/>
             <!-- registered tools go here -->
           </hbox>
 #ifndef XP_MACOSX
           <toolbarbutton id="highlighter-closebutton"
                          oncommand="InspectorUI.closeInspectorUI(false);"
                          tooltiptext="&inspectCloseButton.tooltiptext;"/>
 #endif
           <resizer id="inspector-end-resizer"
@@ -1048,16 +1058,20 @@
   </svg:svg>
 #endif
 #ifdef XP_MACOSX
   <svg:svg height="0">
     <svg:mask id="pinstripe-keyhole-forward-mask" maskContentUnits="objectBoundingBox">
       <svg:rect x="0" y="0" width="1" height="1" fill="white"/>
       <svg:circle cx="-0.41" cy="0.5" r="0.65"/>
     </svg:mask>
+    <svg:mask id="pinstripe-urlbar-back-button-mask" maskContentUnits="userSpaceOnUse">
+      <svg:rect x="0" y="-5" width="10000" height="55" fill="white"/>
+      <svg:circle cx="-9" cy="11" r="15"/>
+    </svg:mask>
     <svg:mask id="pinstripe-tab-ontop-left-curve-mask" maskContentUnits="userSpaceOnUse">
       <svg:circle cx="9" cy="3" r="3" fill="white"/>
       <svg:rect x="9" y="0" width="3" height="3" fill="white"/>
       <svg:rect x="6" y="3" width="6" height="19" fill="white"/>
       <svg:rect x="1" y="17" width="5" height="5" fill="white"/>
       <svg:circle cx="1" cy="17" r="5"/>
       <svg:rect x="0" y="22" width="12" height="1" fill="white"/>
     </svg:mask>
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -272,19 +272,20 @@ nsContextMenu.prototype = {
     }
 
     // Reload image depends on an image that's not fully loaded
     this.showItem("context-reloadimage", (this.onImage && !this.onCompletedImage));
 
     // View image depends on having an image that's not standalone
     // (or is in a frame), or a canvas.
     this.showItem("context-viewimage", (this.onImage &&
-                  (!this.onStandaloneImage || this.inFrame)) || this.onCanvas);
+                  (!this.inSyntheticDoc || this.inFrame)) || this.onCanvas);
 
-    this.showItem("context-viewvideo", this.onVideo);
+    // View video depends on not having a standalone video.
+    this.showItem("context-viewvideo", this.onVideo && (!this.inSyntheticDoc || this.inFrame));
     this.setItemAttr("context-viewvideo",  "disabled", !this.mediaURL);
 
     // View background image depends on whether there is one.
     this.showItem("context-viewbgimage", shouldShow && !this._hasMultipleBGImages);
     this.showItem("context-sep-viewbgimage", shouldShow && !this._hasMultipleBGImages);
     document.getElementById("context-viewbgimage")
             .disabled = !this.hasBGImage;
 
@@ -461,32 +462,32 @@ nsContextMenu.prototype = {
       this.shouldDisplay = false;
       return;
     }
 
     // Initialize contextual info.
     this.onImage           = false;
     this.onLoadedImage     = false;
     this.onCompletedImage  = false;
-    this.onStandaloneImage = false;
     this.onCanvas          = false;
     this.onVideo           = false;
     this.onAudio           = false;
     this.onTextInput       = false;
     this.onKeywordField    = false;
     this.mediaURL          = "";
     this.onLink            = false;
     this.onMailtoLink      = false;
     this.onSaveableLink    = false;
     this.link              = null;
     this.linkURL           = "";
     this.linkURI           = null;
     this.linkProtocol      = "";
     this.onMathML          = false;
     this.inFrame           = false;
+    this.inSyntheticDoc    = false;
     this.hasBGImage        = false;
     this.bgImageURL        = "";
     this.onEditableArea    = false;
     this.isDesignMode      = false;
 
     // Clear any old spellchecking items from the menu, this used to
     // be in the menu hiding code but wasn't getting called in all
     // situations. Here, we can ensure it gets cleaned up any time the
@@ -495,33 +496,33 @@ nsContextMenu.prototype = {
     InlineSpellCheckerUI.clearSuggestionsFromMenu();
     InlineSpellCheckerUI.clearDictionaryListFromMenu();
 
     InlineSpellCheckerUI.uninit();
 
     // Remember the node that was clicked.
     this.target = aNode;
 
+    // Check if we are in a synthetic document (stand alone image, video, etc.).
+    this.inSyntheticDoc =  this.target.ownerDocument.mozSyntheticDocument;
     // First, do checks for nodes that never have children.
     if (this.target.nodeType == Node.ELEMENT_NODE) {
       // See if the user clicked on an image.
       if (this.target instanceof Ci.nsIImageLoadingContent &&
           this.target.currentURI) {
         this.onImage = true;
 
         var request =
           this.target.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
         if (request && (request.imageStatus & request.STATUS_SIZE_AVAILABLE))
           this.onLoadedImage = true;
         if (request && (request.imageStatus & request.STATUS_LOAD_COMPLETE))
           this.onCompletedImage = true;
 
         this.mediaURL = this.target.currentURI.spec;
-        if (this.target.ownerDocument instanceof ImageDocument)
-          this.onStandaloneImage = true;
       }
       else if (this.target instanceof HTMLCanvasElement) {
         this.onCanvas = true;
       }
       else if (this.target instanceof HTMLVideoElement) {
         this.onVideo = true;
         this.mediaURL = this.target.currentSrc || this.target.src;
       }
--- a/browser/base/content/syncProgress.js
+++ b/browser/base/content/syncProgress.js
@@ -39,34 +39,67 @@
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://services-sync/main.js");
 
 let gProgressBar;
 let gCounter = 0;
 
 function onLoad(event) {
-  Services.obs.addObserver(increaseProgressBar, "weave:engine:sync:finish", false);
-  Services.obs.addObserver(increaseProgressBar, "weave:engine:sync:error", false);
+  Services.obs.addObserver(onEngineSync, "weave:engine:sync:finish", false);
+  Services.obs.addObserver(onEngineSync, "weave:engine:sync:error", false);
+  Services.obs.addObserver(onServiceSync, "weave:service:sync:finish", false);
+  Services.obs.addObserver(onServiceSync, "weave:service:sync:error", false);
+
   gProgressBar = document.getElementById('uploadProgressBar');
 
   if (Services.prefs.getPrefType("services.sync.firstSync") != Ci.nsIPrefBranch.PREF_INVALID) {
-    gProgressBar.max = Weave.Engines.getEnabled().length;
     gProgressBar.style.display = "inline";
   }
   else {
     gProgressBar.style.display = "none";
   }
 }
 
 function onUnload(event) {
-  Services.obs.removeObserver(increaseProgressBar, "weave:engine:sync:finish");
-  Services.obs.removeObserver(increaseProgressBar, "weave:engine:sync:error");
+  cleanUpObservers();
+}
+
+function cleanUpObservers() {
+  try {
+    Services.obs.removeObserver(onEngineSync, "weave:engine:sync:finish", false);
+    Services.obs.removeObserver(onEngineSync, "weave:engine:sync:error", false);
+    Services.obs.removeObserver(onServiceSync, "weave:service:sync:finish", false);
+    Services.obs.removeObserver(onServiceSync, "weave:service:sync:error", false);
+  }
+  catch (e) {
+    // may be double called by unload & exit. Ignore.
+  }
 }
 
-function increaseProgressBar(){
+function onEngineSync(subject, topic, data) {
+  // The Clients engine syncs first. At this point we don't necessarily know
+  // yet how many engines will be enabled, so we'll ignore the Clients engine
+  // and evaluate how many engines are enabled when the first "real" engine
+  // syncs.
+  if (data == "clients") {
+    return;
+  }
+
+  if (!gCounter &&
+      Services.prefs.getPrefType("services.sync.firstSync") != Ci.nsIPrefBranch.PREF_INVALID) {
+    gProgressBar.max = Weave.Engines.getEnabled().length;
+  }
+
   gCounter += 1;
   gProgressBar.setAttribute("value", gCounter);
 }
 
+function onServiceSync(subject, topic, data) {
+  // To address the case where 0 engines are synced, we will fill the
+  // progress bar so the user knows that the sync has finished.
+  gProgressBar.setAttribute("value", gProgressBar.max);
+  cleanUpObservers();
+}
+
 function closeTab() {
   window.close();
 }
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1103,40 +1103,43 @@
         <parameter name="aCharset"/>
         <parameter name="aPostData"/>
         <parameter name="aLoadInBackground"/>
         <parameter name="aAllowThirdPartyFixup"/>
         <body>
           <![CDATA[
             var aFromExternal;
             var aRelatedToCurrent;
+            var aIsUTF8;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aReferrerURI          = params.referrerURI;
               aCharset              = params.charset;
               aPostData             = params.postData;
               aLoadInBackground     = params.inBackground;
               aAllowThirdPartyFixup = params.allowThirdPartyFixup;
               aFromExternal         = params.fromExternal;
               aRelatedToCurrent     = params.relatedToCurrent;
+              aIsUTF8               = params.isUTF8;
             }
 
             var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
                          Services.prefs.getBoolPref("browser.tabs.loadInBackground");
             var owner = bgLoad ? null : this.selectedTab;
             var tab = this.addTab(aURI, {
                                   referrerURI: aReferrerURI,
                                   charset: aCharset,
                                   postData: aPostData,
                                   ownerTab: owner,
                                   allowThirdPartyFixup: aAllowThirdPartyFixup,
                                   fromExternal: aFromExternal,
-                                  relatedToCurrent: aRelatedToCurrent});
+                                  relatedToCurrent: aRelatedToCurrent,
+                                  isUTF8: aIsUTF8});
             if (!bgLoad)
               this.selectedTab = tab;
 
             return tab;
          ]]>
         </body>
       </method>
 
@@ -1199,28 +1202,30 @@
         <parameter name="aPostData"/>
         <parameter name="aOwner"/>
         <parameter name="aAllowThirdPartyFixup"/>
         <body>
           <![CDATA[
             var aFromExternal;
             var aRelatedToCurrent;
             var aSkipAnimation;
+            var aIsUTF8;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aReferrerURI          = params.referrerURI;
               aCharset              = params.charset;
               aPostData             = params.postData;
               aOwner                = params.ownerTab;
               aAllowThirdPartyFixup = params.allowThirdPartyFixup;
               aFromExternal         = params.fromExternal;
               aRelatedToCurrent     = params.relatedToCurrent;
               aSkipAnimation        = params.skipAnimation;
+              aIsUTF8               = params.isUTF8;
             }
 
             this._browsers = null; // invalidate cache
 
             // if we're adding tabs, we're past interrupt mode, ditch the owner
             if (this.mCurrentTab.owner)
               this.mCurrentTab.owner = null;
 
@@ -1358,16 +1363,18 @@
               // the document successfully loads
               b.userTypedValue = aURI;
 
               let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
               if (aAllowThirdPartyFixup)
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
               if (aFromExternal)
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
+              if (aIsUTF8)
+                flags |= Ci.nsIWebNavigation.LOAD_FLAGS_URI_IS_UTF8;
               try {
                 b.loadURIWithFlags(aURI, flags, aReferrerURI, aCharset, aPostData);
               } catch (ex) {
                 Cu.reportError(ex);
               }
             }
 
             // We start our browsers out as inactive, and then maintain
@@ -2427,17 +2434,17 @@
                 offset *= -1;
               this.tabContainer.advanceSelectedTab(offset, true);
               aEvent.stopPropagation();
               aEvent.preventDefault();
           }
 #else
           if (aEvent.ctrlKey && !aEvent.shiftKey && !aEvent.metaKey &&
               aEvent.keyCode == KeyEvent.DOM_VK_F4 &&
-              this.mTabBox.handleCtrlPageUpDown) {
+              !this.mCurrentTab.pinned) {
             this.removeCurrentTab({animate: true});
             aEvent.stopPropagation();
             aEvent.preventDefault();
           }
 #endif
         ]]></body>
       </method>
 
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -201,16 +201,17 @@ include $(topsrcdir)/config/rules.mk
                  browser_selectTabAtIndex.js \
                  browser_tab_dragdrop.js \
                  browser_tab_dragdrop2.js \
                  browser_tab_dragdrop2_frame1.xul \
                  browser_tabfocus.js \
                  browser_tabs_isActive.js \
                  browser_tabs_owner.js \
                  browser_urlbarCopying.js \
+                 browser_urlbarEnter.js \
                  browser_urlbarTrimURLs.js \
                  browser_urlHighlight.js \
                  browser_visibleFindSelection.js \
                  browser_visibleTabs.js \
                  browser_visibleTabs_contextMenu.js \
                  browser_visibleTabs_bookmarkAllPages.js \
                  browser_visibleTabs_bookmarkAllTabs.js \
                  browser_visibleTabs_tabPreview.js \
--- a/browser/base/content/test/browser_aboutHome.js
+++ b/browser/base/content/test/browser_aboutHome.js
@@ -5,16 +5,19 @@
 registerCleanupFunction(function() {
   // Ensure we don't pollute prefs for next tests.
   try {
     Services.prefs.clearUserPref("network.cookies.cookieBehavior");
   } catch (ex) {}
   try {
     Services.prefs.clearUserPref("network.cookie.lifetimePolicy");
   } catch (ex) {}
+  try {
+    Services.prefs.clearUserPref("services.sync.username");
+  } catch (ex) {}
 });
 
 let gTests = [
 
 {
   desc: "Check that rejecting cookies does not prevent page from working",
   setup: function ()
   {
@@ -109,16 +112,126 @@ let gTests = [
     ok(snippetsElt, "Found snippets element");
     is(snippetsElt.getElementsByTagName("span").length, 1,
        "A default snippet is visible.");
 
     executeSoon(runNextTest);
   }
 },
 
+{
+  desc: "Check sync links visibility before and after Sync setup",
+  setup: function ()
+  {
+    try {
+      Services.prefs.clearUserPref("services.sync.username");
+    } catch (ex) {}
+    Services.obs.notifyObservers(null, "weave:service:ready", null);
+  },
+  run: function ()
+  {
+    let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
+    let pairLink = doc.getElementById("pairDeviceLink");
+    let setupLink = doc.getElementById("setupSyncLink");
+
+    ok(pairLink, "Found 'Pair Device' link");
+    ok(setupLink, "Found 'Set Up Sync' link");
+    ok(!pairLink.hidden, "'Pair' link is visible before setup");
+    ok(!setupLink.hidden, "'Set Up' link is visible before setup");
+
+    Services.obs.notifyObservers(null, "weave:service:setup-complete", null);
+
+    executeSoon(function () {
+      setupLink = doc.getElementById("setupSyncLink");
+      ok(setupLink.hidden, "'Set Up' link is hidden after setup");
+      ok(!pairLink.hidden, "'Pair' link is visible after setup");
+
+      executeSoon(runNextTest);
+    });
+  }
+},
+
+{
+  desc: "Check sync links visibility before and after Sync unlink",
+  setup: function ()
+  {
+    Services.prefs.setCharPref("services.sync.username", "someuser@domain.com");
+    Services.obs.notifyObservers(null, "weave:service:ready", null);
+  },
+  run: function ()
+  {
+    let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
+    let pairLink = doc.getElementById("pairDeviceLink");
+    let setupLink = doc.getElementById("setupSyncLink");
+
+    ok(!pairLink.hidden, "'Pair' link is visible before unlink");
+    ok(setupLink.hidden, "'Set Up' link is hidden before unlink");
+
+    Services.obs.notifyObservers(null, "weave:service:start-over", null);
+
+    executeSoon(function () {
+      setupLink = doc.getElementById("setupSyncLink");
+      ok(!setupLink.hidden, "'Set Up' link is visible after unlink");
+      ok(!pairLink.hidden, "'Pair' link is visible after unlink");
+      executeSoon(runNextTest);
+    });
+  }
+},
+
+{
+  desc: "Check Pair Device link opens correct dialog with Sync account ",
+  setup: function ()
+  {
+    Services.prefs.setCharPref("services.sync.username", "someuser@domain.com");
+    Services.obs.notifyObservers(null, "weave:service:ready", null);
+  },
+  run: function ()
+  {
+    expectDialogWindow("Sync:AddDevice");
+    let browser = gBrowser.selectedTab.linkedBrowser;
+    let button = browser.contentDocument.getElementById("pairDeviceLink");
+    EventUtils.sendMouseEvent({type: "click"}, button, browser.contentWindow);
+  }
+},
+
+{
+  desc: "Check Pair Device link opens correct dialog without Sync account",
+  setup: function ()
+  {
+    try {
+      Services.prefs.clearUserPref("services.sync.username");
+    } catch (ex) {}
+    Services.obs.notifyObservers(null, "weave:service:ready", null);
+  },
+  run: function ()
+  {
+    expectDialogWindow("Weave:AccountSetup");
+    let browser = gBrowser.selectedTab.linkedBrowser;
+    let button = browser.contentDocument.getElementById("pairDeviceLink");
+    EventUtils.sendMouseEvent({type: "click"}, button, browser.contentWindow);
+  }
+},
+
+{
+  desc: "Check Sync Setup link opens correct dialog (without Sync account)",
+  setup: function ()
+  {
+    try {
+      Services.prefs.clearUserPref("services.sync.username");
+    } catch (ex) {}
+    Services.obs.notifyObservers(null, "weave:service:ready", null);
+  },
+  run: function ()
+  {
+    expectDialogWindow("Weave:AccountSetup");
+    let browser = gBrowser.selectedTab.linkedBrowser;
+    let button = browser.contentDocument.getElementById("setupSyncLink");
+    EventUtils.sendMouseEvent({type: "click"}, button, browser.contentWindow);
+  }
+},
 ];
 
 function test()
 {
   waitForExplicitFinish();
 
   // browser-chrome test harness inits browser specifying an hardcoded page
   // and this causes nsIBrowserHandler.defaultArgs to not be evaluated since
@@ -154,16 +267,32 @@ function runNextTest()
       executeSoon(test.run);
     }, true);
   }
   else {
     finish();
   }
 }
 
+function expectDialogWindow(expectedDialog) {
+  Services.ww.registerNotification(function onWindow(subject, topic) {
+    let win = subject.QueryInterface(Components.interfaces.nsIDOMWindow);
+    win.addEventListener("load", function onLoad() {
+      win.removeEventListener("load", onLoad, false);
+      let wintype = win.document.documentElement.getAttribute("windowtype");
+      if (topic == "domwindowopened" && wintype == expectedDialog) {
+        Services.ww.unregisterNotification(onWindow);
+        // Clean up dialog.
+        win.close();
+        executeSoon(runNextTest);
+      }
+    }, false);
+  });
+}
+
 function getStorage()
 {
   let aboutHomeURI = Services.io.newURI("moz-safe-about:home", null, null);
   let principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"].
                   getService(Components.interfaces.nsIScriptSecurityManager).
                   getCodebasePrincipal(Services.io.newURI("about:home", null, null));
   let dsm = Components.classes["@mozilla.org/dom/storagemanager;1"].
             getService(Components.interfaces.nsIDOMStorageManager);
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_urlbarEnter.js
@@ -0,0 +1,69 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TEST_VALUE = "example.com/\xF7?\xF7";
+const START_VALUE = "example.com/%C3%B7?%C3%B7";
+
+function test() {
+  waitForExplicitFinish();
+  runNextTest();
+}
+
+function locationBarEnter(aEvent, aClosure) {
+  executeSoon(function() {
+    gURLBar.focus();
+    EventUtils.synthesizeKey("VK_RETURN", aEvent);
+    addPageShowListener(aClosure);
+  });
+}
+
+function runNextTest() {
+  let test = gTests.shift();
+  if (!test) {
+    finish();
+    return;
+  }
+  
+  info("Running test: " + test.desc);
+  let tab = gBrowser.selectedTab = gBrowser.addTab(START_VALUE);
+  addPageShowListener(function() {
+    locationBarEnter(test.event, function() {
+      test.check(tab);
+
+      // Clean up
+      while (gBrowser.tabs.length > 1)
+        gBrowser.removeTab(gBrowser.selectedTab)
+      runNextTest();
+    });
+  });
+}
+
+let gTests = [
+  { desc: "Simple return keypress",
+    event: {},
+    check: checkCurrent
+  },
+
+  { desc: "Alt+Return keypress",
+    event: { altKey: true },
+    check: checkNewTab,
+  },
+]
+
+function checkCurrent(aTab) {
+  is(gURLBar.value, TEST_VALUE, "Urlbar should preserve the value on return keypress");
+  is(gBrowser.selectedTab, aTab, "New URL was loaded in the current tab");
+}
+
+function checkNewTab(aTab) {
+  is(gURLBar.value, TEST_VALUE, "Urlbar should preserve the value on return keypress");
+  isnot(gBrowser.selectedTab, aTab, "New URL was loaded in a new tab");
+}
+
+function addPageShowListener(aFunc) {
+  gBrowser.selectedBrowser.addEventListener("pageshow", function loadListener() {
+    gBrowser.selectedBrowser.removeEventListener("pageshow", loadListener, false);
+    aFunc();
+  });
+}
+
--- a/browser/base/content/test/subtst_contextmenu.html
+++ b/browser/base/content/test/subtst_contextmenu.html
@@ -13,16 +13,18 @@ Browser context menu subtest.
 <img id="test-image" src="ctxmenu-image.png">
 <canvas id="test-canvas" width="100" height="100" style="background-color: blue"></canvas>
 <video controls id="test-video-ok"  src="video.ogg" width="100" height="100" style="background-color: green"></video>
 <video controls id="test-video-bad" src="bogus.duh" width="100" height="100" style="background-color: orange"></video>
 <video controls id="test-video-bad2" width="100" height="100" style="background-color: yellow">
   <source src="bogus.duh" type="video/durrrr;">
 </video>
 <iframe id="test-iframe" width="98"  height="98" style="border: 1px solid black"></iframe>
+<iframe id="test-video-in-iframe" src="video.ogg" width="98" height="98" style="border: 1px solid black"></iframe>
+<iframe id="test-image-in-iframe" src="ctxmenu-image.png" width="98" height="98" style="border: 1px solid black"></iframe>
 <textarea id="test-textarea">chssseesbbbie</textarea> <!-- a weird word which generates only one suggestion -->
 <div id="test-contenteditable" contenteditable="true">chssseefsbbbie</div> <!-- a more weird word which generates no suggestions -->
 <input id="test-input-spellcheck" type="text" spellcheck="true" autofocus value="prodkjfgigrty"> <!-- this one also generates one suggestion -->
 <div contextmenu="myMenu">
   <p id="test-pagemenu" hopeless="true">I've got a context menu!</p>
   <menu id="myMenu" type="context">
     <menuitem label="Plain item" onclick="document.getElementById('test-pagemenu').removeAttribute('hopeless');"></menuitem>
     <menuitem label="Disabled item" disabled></menuitem>
--- a/browser/base/content/test/test_contextmenu.html
+++ b/browser/base/content/test/test_contextmenu.html
@@ -428,20 +428,78 @@ function runTest(testNum) {
                                "---",                       null,
                                "context-viewframesource",   true,
                                "context-viewframeinfo",     true], null,
                           "---",                  null,
                           "context-viewsource",   true,
                           "context-viewinfo",     true
                          ].concat(inspectItems));
         closeContextMenu();
+        openContextMenuFor(video_in_iframe); // Invoke context menu for next test.
+        break;
+
+    case 12:
+        // Context menu for a video in an iframe
+        checkContextMenu(["context-media-play",         true,
+                          "context-media-mute",         true,
+                          "context-media-hidecontrols", true,
+                          "context-video-showstats",    true,
+                          "context-video-fullscreen",   true,
+                          "---",                        null,
+                          "context-viewvideo",          true,
+                          "context-copyvideourl",       true,
+                          "---",                        null,
+                          "context-savevideo",          true,
+                          "context-video-saveimage",    true,
+                          "context-sendvideo",          true,
+                          "frame",                null,
+                              ["context-showonlythisframe", true,
+                               "context-openframeintab",    true,
+                               "context-openframe",         true,
+                               "---",                       null,
+                               "context-reloadframe",       true,
+                               "---",                       null,
+                               "context-bookmarkframe",     true,
+                               "context-saveframe",         true,
+                               "---",                       null,
+                               "context-printframe",        true,
+                               "---",                       null,
+                               "context-viewframeinfo",     true], null].concat(inspectItems));
+        closeContextMenu();
+        openContextMenuFor(image_in_iframe); // Invoke context menu for next test.
+        break;
+
+    case 13:
+        // Context menu for an image in an iframe
+        checkContextMenu(["context-viewimage",            true,
+                          "context-copyimage-contents",   true,
+                          "context-copyimage",            true,
+                          "---",                          null,
+                          "context-saveimage",            true,
+                          "context-sendimage",            true,
+                          "context-setDesktopBackground", true,
+                          "context-viewimageinfo",        true,
+                          "frame",                null,
+                              ["context-showonlythisframe", true,
+                               "context-openframeintab",    true,
+                               "context-openframe",         true,
+                               "---",                       null,
+                               "context-reloadframe",       true,
+                               "---",                       null,
+                               "context-bookmarkframe",     true,
+                               "context-saveframe",         true,
+                               "---",                       null,
+                               "context-printframe",        true,
+                               "---",                       null,
+                               "context-viewframeinfo",     true], null].concat(inspectItems));
+        closeContextMenu();
         openContextMenuFor(textarea, false, true); // Invoke context menu for next test, but wait for the spellcheck.
         break;
 
-    case 12:
+    case 14:
         // Context menu for textarea
         checkContextMenu(["*chubbiness",         true, // spelling suggestion
                           "spell-add-to-dictionary", true,
                           "---",                 null,
                           "context-undo",        false,
                           "---",                 null,
                           "context-cut",         false,
                           "context-copy",        false,
@@ -456,17 +514,17 @@ function runTest(testNum) {
                                "---",                          null,
                                "spell-add-dictionaries",       true], null,
                          ].concat(inspectItems));
 
         closeContextMenu();
         openContextMenuFor(contenteditable); // Invoke context menu for next test.
         break;
 
-    case 13:
+    case 15:
         // Context menu for contenteditable
         checkContextMenu(["spell-no-suggestions", false,
                           "spell-add-to-dictionary", true,
                           "---",                 null,
                           "context-undo",        false,
                           "---",                 null,
                           "context-cut",         false,
                           "context-copy",        false,
@@ -481,17 +539,17 @@ function runTest(testNum) {
                                "---",                          null,
                                "spell-add-dictionaries",       true], null
                          ].concat(inspectItems));
 
         closeContextMenu();
         openContextMenuFor(inputspell); // Invoke context menu for next test.
         break;
 
-    case 14:
+    case 16:
         // Context menu for spell-check input
         checkContextMenu(["*prodigality",        true, // spelling suggestion
                           "spell-add-to-dictionary", true,
                           "---",                 null,
                           "context-undo",        false,
                           "---",                 null,
                           "context-cut",         false,
                           "context-copy",        false,
@@ -506,23 +564,23 @@ function runTest(testNum) {
                                "---",                          null,
                                "spell-add-dictionaries",       true], null
                          ].concat(inspectItems));
 
         closeContextMenu();
         openContextMenuFor(link); // Invoke context menu for next test.
         break;
 
-    case 15:
+    case 17:
         executeCopyCommand("cmd_copyLink", "http://mozilla.com/");
         closeContextMenu();
         openContextMenuFor(pagemenu); // Invoke context menu for next test.
         break;
 
-    case 16:
+    case 18:
         // Context menu for element with assigned content context menu
         checkContextMenu(["+Plain item",          {type: "", icon: "", checked: false, disabled: false},
                           "+Disabled item",       {type: "", icon: "", checked: false, disabled: true},
                           "+Item w/ textContent", {type: "", icon: "", checked: false, disabled: false},
                           "---",                  null,
                           "+Checkbox",            {type: "checkbox", icon: "", checked: true, disabled: false},
                           "---",                  null,
                           "+Radio1",              {type: "checkbox", icon: "", checked: true, disabled: false},
@@ -555,17 +613,17 @@ function runTest(testNum) {
                           "context-viewinfo",     true
                          ].concat(inspectItems));
 
         invokeItemAction("0");
         closeContextMenu();
         openContextMenuFor(pagemenu, true); // Invoke context menu for next test.
         break;
 
-    case 17:
+    case 19:
         // Context menu for element with assigned content context menu
         // The shift key should bypass content context menu processing
         checkContextMenu(["context-back",         false,
                           "context-forward",      false,
                           "context-reload",       true,
                           "context-stop",         false,
                           "---",                  null,
                           "context-bookmarkpage", true,
@@ -599,17 +657,17 @@ function runTest(testNum) {
   }
 
 }
 
 
 var testNum = 1;
 var subwindow, chromeWin, contextMenu, lastElement;
 var text, link, mailto, input, img, canvas, video_ok, video_bad, video_bad2,
-    iframe, textarea, contenteditable, inputspell, pagemenu;
+    iframe, video_in_iframe, image_in_iframe, textarea, contenteditable, inputspell, pagemenu;
 
 function startTest() {
     netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
     chromeWin = subwindow
                     .QueryInterface(Ci.nsIInterfaceRequestor)
                     .getInterface(Ci.nsIWebNavigation)
                     .QueryInterface(Ci.nsIDocShellTreeItem)
                     .rootTreeItem
@@ -631,16 +689,19 @@ function startTest() {
     mailto = subwindow.document.getElementById("test-mailto");
     input  = subwindow.document.getElementById("test-input");
     img    = subwindow.document.getElementById("test-image");
     canvas = subwindow.document.getElementById("test-canvas");
     video_ok   = subwindow.document.getElementById("test-video-ok");
     video_bad  = subwindow.document.getElementById("test-video-bad");
     video_bad2 = subwindow.document.getElementById("test-video-bad2");
     iframe = subwindow.document.getElementById("test-iframe");
+    video_in_iframe = subwindow.document.getElementById("test-video-in-iframe").contentDocument.getElementsByTagName("video")[0];
+    video_in_iframe.pause();
+    image_in_iframe = subwindow.document.getElementById("test-image-in-iframe").contentDocument.getElementsByTagName("img")[0];
     textarea = subwindow.document.getElementById("test-textarea");
     contenteditable = subwindow.document.getElementById("test-contenteditable");
     contenteditable.focus(); // content editable needs to be focused to enable spellcheck
     inputspell = subwindow.document.getElementById("test-input-spellcheck");
     pagemenu = subwindow.document.getElementById("test-pagemenu");
 
     contextMenu.addEventListener("popupshown", function() { runTest(++testNum); }, false);
     runTest(1);
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -321,16 +321,20 @@
           function loadCurrent() {
             let flags = Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
             // Pass LOAD_FLAGS_DISALLOW_INHERIT_OWNER to prevent any loads from
             // inheriting the currently loaded document's principal, unless this
             // URL is marked as safe to inherit (e.g. came from a bookmark
             // keyword).
             if (!mayInheritPrincipal)
               flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_OWNER;
+            // If the value wasn't typed, we know that we decoded the value as
+            // UTF-8 (see losslessDecodeURI)
+            if (!this.valueIsTyped)
+              flags |= Ci.nsIWebNavigation.LOAD_FLAGS_URI_IS_UTF8;
             gBrowser.loadURIWithFlags(url, flags, null, null, postData);
           }
 
           // Focus the content area before triggering loads, since if the load
           // occurs in a new tab, we want focus to be restored to the content
           // area when the current tab is re-selected.
           gBrowser.selectedBrowser.focus();
 
@@ -356,16 +360,18 @@
 
             if (where == "current") {
               loadCurrent();
             } else {
               this.handleRevert();
               let params = { allowThirdPartyFixup: true, postData: postData };
               if (altEnter)
                 params.inBackground = false;
+              if (!this.valueIsTyped)
+                params.isUTF8 = true;
               openUILinkIn(url, where, params);
             }
           } else {
             loadCurrent();
           }
         ]]></body>
       </method>
 
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -194,16 +194,18 @@ function openLinkIn(url, where, params) 
   var aFromChrome           = params.fromChrome;
   var aAllowThirdPartyFixup = params.allowThirdPartyFixup;
   var aPostData             = params.postData;
   var aCharset              = params.charset;
   var aReferrerURI          = params.referrerURI;
   var aRelatedToCurrent     = params.relatedToCurrent;
   var aInBackground         = params.inBackground;
   var aDisallowInheritPrincipal = params.disallowInheritPrincipal;
+  // Currently, this parameter works only for where=="tab" or "current"
+  var aIsUTF8               = params.isUTF8;
 
   if (where == "save") {
     saveURL(url, null, null, true, null, aReferrerURI);
     return;
   }
   const Cc = Components.classes;
   const Ci = Components.interfaces;
 
@@ -267,30 +269,33 @@ function openLinkIn(url, where, params) 
 
   switch (where) {
   case "current":
     let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
     if (aAllowThirdPartyFixup)
       flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
     if (aDisallowInheritPrincipal)
       flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_OWNER;
+    if (aIsUTF8)
+      flags |= Ci.nsIWebNavigation.LOAD_FLAGS_URI_IS_UTF8;
     w.gBrowser.loadURIWithFlags(url, flags, aReferrerURI, null, aPostData);
     break;
   case "tabshifted":
     loadInBackground = !loadInBackground;
     // fall through
   case "tab":
     let browser = w.gBrowser;
     browser.loadOneTab(url, {
                        referrerURI: aReferrerURI,
                        charset: aCharset,
                        postData: aPostData,
                        inBackground: loadInBackground,
                        allowThirdPartyFixup: aAllowThirdPartyFixup,
-                       relatedToCurrent: aRelatedToCurrent});
+                       relatedToCurrent: aRelatedToCurrent,
+                       isUTF8: aIsUTF8});
     break;
   }
 
   // If this window is active, focus the target window. Otherwise, focus the
   // content but don't raise the window, since the URI we just loaded may have
   // resulted in a new frontmost window (e.g. "javascript:window.open('');").
   var fm = Components.classes["@mozilla.org/focus-manager;1"].
              getService(Components.interfaces.nsIFocusManager);
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -634,18 +634,18 @@ BrowserGlue.prototype = {
                       }
                     }
                   ];
 
     // Set pref to indicate we've shown the notification.
     var currentVersion = Services.prefs.getIntPref("browser.rights.version");
     Services.prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true);
 
-    var box = notifyBox.appendNotification(notifyRightsText, "about-rights", null, notifyBox.PRIORITY_INFO_LOW, buttons);
-    box.persistence = 3; // arbitrary number, just so bar sticks around for a bit
+    var notification = notifyBox.appendNotification(notifyRightsText, "about-rights", null, notifyBox.PRIORITY_INFO_LOW, buttons);
+    notification.persistence = -1; // Until user closes it
   },
 
   _showUpdateNotification: function BG__showUpdateNotification() {
     Services.prefs.clearUserPref("app.update.postupdate");
 
     var um = Cc["@mozilla.org/updates/update-manager;1"].
              getService(Ci.nsIUpdateManager);
     try {
@@ -704,20 +704,20 @@ BrowserGlue.prototype = {
                         accessKey: key,
                         popup:     null,
                         callback: function(aNotificationBar, aButton) {
                           browser.selectedTab = browser.addTab(url);
                         }
                       }
                     ];
 
-      let box = notifyBox.appendNotification(text, "post-update-notification",
-                                             null, notifyBox.PRIORITY_INFO_LOW,
-                                             buttons);
-      box.persistence = 3;
+      let notification = notifyBox.appendNotification(text, "post-update-notification",
+                                                      null, notifyBox.PRIORITY_INFO_LOW,
+                                                      buttons);
+      notification.persistence = -1; // Until user closes it
     }
 
     if (actions.indexOf("showAlert") == -1)
       return;
 
     let notifier;
     try {
       notifier = Cc["@mozilla.org/alerts-service;1"].
@@ -756,16 +756,17 @@ BrowserGlue.prototype = {
     catch (e) {
     }
   },
 
 #ifdef MOZ_TELEMETRY_REPORTING
   _showTelemetryNotification: function BG__showTelemetryNotification() {
     const PREF_TELEMETRY_PROMPTED = "toolkit.telemetry.prompted";
     const PREF_TELEMETRY_ENABLED  = "toolkit.telemetry.enabled";
+    const PREF_TELEMETRY_REJECTED  = "toolkit.telemetry.rejected";
     const PREF_TELEMETRY_INFOURL  = "toolkit.telemetry.infoURL";
     const PREF_TELEMETRY_SERVER_OWNER = "toolkit.telemetry.server_owner";
     // This is used to reprompt users when privacy message changes
     const TELEMETRY_PROMPT_REV = 2;
 
     var telemetryPrompted = null;
     try {
       telemetryPrompted = Services.prefs.getIntPref(PREF_TELEMETRY_PROMPTED);
@@ -787,49 +788,53 @@ BrowserGlue.prototype = {
     var brandBundle     = Services.strings.createBundle("chrome://branding/locale/brand.properties");
 
     var productName        = brandBundle.GetStringFromName("brandFullName");
     var serverOwner        = Services.prefs.getCharPref(PREF_TELEMETRY_SERVER_OWNER);
     var telemetryPrompt    = browserBundle.formatStringFromName("telemetryPrompt", [productName, serverOwner], 2);
 
     var buttons = [
                     {
-                      label:     browserBundle.GetStringFromName("telemetryYesButtonLabel"),
+                      label:     browserBundle.GetStringFromName("telemetryYesButtonLabel2"),
                       accessKey: browserBundle.GetStringFromName("telemetryYesButtonAccessKey"),
                       popup:     null,
                       callback:  function(aNotificationBar, aButton) {
                         Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);
                       }
                     },
                     {
                       label:     browserBundle.GetStringFromName("telemetryNoButtonLabel"),
                       accessKey: browserBundle.GetStringFromName("telemetryNoButtonAccessKey"),
                       popup:     null,
-                      callback:  function(aNotificationBar, aButton) {}
+                      callback:  function(aNotificationBar, aButton) {
+                        Services.prefs.setBoolPref(PREF_TELEMETRY_REJECTED, true);
+                      }
                     }
                   ];
 
     // Set pref to indicate we've shown the notification.
     Services.prefs.setIntPref(PREF_TELEMETRY_PROMPTED, TELEMETRY_PROMPT_REV);
 
     var notification = notifyBox.appendNotification(telemetryPrompt, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons);
-    notification.persistence = 6; // arbitrary number, just so bar sticks around for a bit
+    notification.setAttribute("hideclose", true);
+    notification.persistence = -1;  // Until user closes it
 
     let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
     let link = notification.ownerDocument.createElementNS(XULNS, "label");
     link.className = "text-link telemetry-text-link";
     link.setAttribute("value", browserBundle.GetStringFromName("telemetryLinkLabel"));
     link.addEventListener('click', function() {
       // Open the learn more url in a new tab
       browser.selectedTab = browser.addTab(Services.prefs.getCharPref(PREF_TELEMETRY_INFOURL));
       // Remove the notification on which the user clicked
       notification.parentNode.removeNotification(notification, true);
       // Add a new notification to that tab, with no "Learn more" link
-      var notifyBox = browser.getNotificationBox();
-      notifyBox.appendNotification(telemetryPrompt, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons);
+      notifyBox = browser.getNotificationBox();
+      notification = notifyBox.appendNotification(telemetryPrompt, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons);
+      notification.persistence = -1; // Until user closes it
     }, false);
     let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
     description.appendChild(link);
   },
 #endif
 
   _showPluginUpdatePage: function BG__showPluginUpdatePage() {
     Services.prefs.setBoolPref(PREF_PLUGINS_NOTIFYUSER, false);
@@ -1083,20 +1088,20 @@ BrowserGlue.prototype = {
                       popup:     null,
                       callback:  function(aNotificationBar, aButton) {
                         browser.selectedTab = browser.addTab(url);
                       }
                     }
                   ];
 
     var notifyBox = browser.getNotificationBox();
-    var box = notifyBox.appendNotification(text, title, null,
-                                           notifyBox.PRIORITY_CRITICAL_MEDIUM,
-                                           buttons);
-    box.persistence = -1; // Until user closes it
+    var notification = notifyBox.appendNotification(text, title, null,
+                                                    notifyBox.PRIORITY_CRITICAL_MEDIUM,
+                                                    buttons);
+    notification.persistence = -1; // Until user closes it
   },
 
   _migrateUI: function BG__migrateUI() {
     const UI_VERSION = 5;
     const BROWSER_DOCURL = "chrome://browser/content/browser.xul#";
     let currentUIVersion = 0;
     try {
       currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
--- a/browser/components/safebrowsing/content/malware-warden.js
+++ b/browser/components/safebrowsing/content/malware-warden.js
@@ -52,24 +52,24 @@ function PROT_MalwareWarden() {
     this.prefs_.getPref(kMalwareWardenEnabledPref, null);
 
   // Get notifications when the malware warden enabled pref changes
   var malwareWardenPrefObserver =
     BindToObject(this.onMalwareWardenEnabledPrefChanged, this);
   this.prefs_.addObserver(kMalwareWardenEnabledPref, malwareWardenPrefObserver);
 
   // Add a test chunk to the database
-  var testData = "mozilla.com/firefox/its-an-attack.html";
+  var testData = "mozilla.org/firefox/its-an-attack.html";
 
   var testUpdate =
     "n:1000\ni:test-malware-simple\nad:1\n" +
     "a:1:32:" + testData.length + "\n" +
     testData;
     
-  testData = "mozilla.com/firefox/its-a-trap.html";
+  testData = "mozilla.org/firefox/its-a-trap.html";
   testUpdate +=
     "n:1000\ni:test-phish-simple\nad:1\n" +
     "a:1:32:" + testData.length + "\n" +
     testData;
 
   var dbService_ = Cc["@mozilla.org/url-classifier/dbservice;1"]
                    .getService(Ci.nsIUrlClassifierDBService);
 
--- a/browser/components/safebrowsing/content/test/browser_bug400731.js
+++ b/browser/components/safebrowsing/content/test/browser_bug400731.js
@@ -3,32 +3,32 @@
 function test() {
   waitForExplicitFinish();
   
   gBrowser.selectedTab = gBrowser.addTab();
   
   // Navigate to malware site.  Can't use an onload listener here since
   // error pages don't fire onload
   window.addEventListener("DOMContentLoaded", testMalware, true);
-  content.location = "http://www.mozilla.com/firefox/its-an-attack.html";
+  content.location = "http://www.mozilla.org/firefox/its-an-attack.html";
 }
 
 function testMalware() {
   window.removeEventListener("DOMContentLoaded", testMalware, true);
 
   // Confirm that "Ignore this warning" is visible - bug 422410
   var el = content.document.getElementById("ignoreWarningButton");
   ok(el, "Ignore warning button should be present for malware");
   
   var style = content.getComputedStyle(el, null);
   is(style.display, "inline", "Ignore Warning button should be display:inline for malware");
   
   // Now launch the phishing test
   window.addEventListener("DOMContentLoaded", testPhishing, true);
-  content.location = "http://www.mozilla.com/firefox/its-a-trap.html";
+  content.location = "http://www.mozilla.org/firefox/its-a-trap.html";
 }
 
 function testPhishing() {
   window.removeEventListener("DOMContentLoaded", testPhishing, true);
   
   var el = content.document.getElementById("ignoreWarningButton");
   ok(el, "Ignore warning button should be present for phishing");
   
--- a/browser/components/safebrowsing/content/test/browser_bug415846.js
+++ b/browser/components/safebrowsing/content/test/browser_bug415846.js
@@ -32,17 +32,17 @@ function testNormal_PopupListener() {
   var reportMenu = document.getElementById("menu_HelpPopup_reportPhishingtoolmenu");
   var errorMenu = document.getElementById("menu_HelpPopup_reportPhishingErrortoolmenu");
   is(reportMenu.hidden, false, "Report phishing menu should be visible on normal sites");
   is(errorMenu.hidden, true, "Report error menu item should be hidden on normal sites");
   menu.hidePopup();
   
   // Now launch the phishing test.  Can't use onload here because error pages don't
   // fire normal load events.
-  content.location = "http://www.mozilla.com/firefox/its-a-trap.html";
+  content.location = "http://www.mozilla.org/firefox/its-a-trap.html";
   setTimeout(testPhishing, 2000);
 }
 
 function testPhishing() {
   menu.addEventListener("popupshown", testPhishing_PopupListener, false);
   menu.openPopup(null, "", 0, 0, false, null);
 }
 
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -112,21 +112,16 @@ eg: browser.docShell["allow" + aCapabili
 XXX keep these in sync with all the attributes starting
     with "allow" in /docshell/base/nsIDocShell.idl
 */
 const CAPABILITIES = [
   "Subframes", "Plugins", "Javascript", "MetaRedirects", "Images",
   "DNSPrefetch", "Auth", "WindowControl"
 ];
 
-// These keys are for internal use only - they shouldn't be part of the JSON
-// that gets saved to disk nor part of the strings returned by the API.
-const INTERNAL_KEYS = ["_tabStillLoading", "_hosts", "_formDataSaved",
-                       "_shouldRestore", "_host", "_scheme"];
-
 // These are tab events that we listen to.
 const TAB_EVENTS = ["TabOpen", "TabClose", "TabSelect", "TabShow", "TabHide",
                     "TabPinned", "TabUnpinned"];
 
 #ifndef XP_WIN
 #define BROKEN_WM_Z_ORDER
 #endif
 
@@ -135,16 +130,21 @@ Cu.import("resource://gre/modules/Servic
 // debug.js adds NS_ASSERT. cf. bug 669196
 Cu.import("resource://gre/modules/debug.js");
 
 XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
   Cu.import("resource://gre/modules/NetUtil.jsm");
   return NetUtil;
 });
 
+XPCOMUtils.defineLazyGetter(this, "ScratchpadManager", function() {
+  Cu.import("resource:///modules/devtools/scratchpad-manager.jsm");
+  return ScratchpadManager;
+});
+
 XPCOMUtils.defineLazyServiceGetter(this, "CookieSvc",
   "@mozilla.org/cookiemanager;1", "nsICookieManager2");
 
 #ifdef MOZ_CRASHREPORTER
 XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
   "@mozilla.org/xre/app-info;1", "nsICrashReporter");
 #endif
 
@@ -206,16 +206,19 @@ SessionStoreService.prototype = {
 
   // time in milliseconds when the session was started (saved across sessions),
   // defaults to now if no session was restored or timestamp doesn't exist
   _sessionStartTime: Date.now(),
 
   // states for all currently opened windows
   _windows: {},
 
+  // internal states for all open windows (data we need to associate, but not write to disk)
+  _internalWindows: {},
+
   // states for all recently closed windows
   _closedWindows: [],
 
   // not-"dirty" windows usually don't need to have their data updated
   _dirtyWindows: {},
 
   // collection of session states yet to be restored
   _statesToRestore: {},
@@ -369,16 +372,21 @@ SessionStoreService.prototype = {
 
           // make sure that at least the first window doesn't have anything hidden
           delete this._initialState.windows[0].hidden;
           // Since nothing is hidden in the first window, it cannot be a popup
           delete this._initialState.windows[0].isPopup;
           // We don't want to minimize and then open a window at startup.
           if (this._initialState.windows[0].sizemode == "minimized")
             this._initialState.windows[0].sizemode = "normal";
+          // clear any lastSessionWindowID attributes since those don't matter
+          // during normal restore
+          this._initialState.windows.forEach(function(aWindow) {
+            delete aWindow.__lastSessionWindowID;
+          });
         }
       }
       catch (ex) { debug("The session file is invalid: " + ex); }
     }
 
     if (this._resume_from_crash) {
       // create a backup if the session data file exists
       try {
@@ -540,27 +548,33 @@ SessionStoreService.prototype = {
       // quit-application notification so the browser is about to exit.
       if (this._loadState == STATE_QUITTING)
         return;
       this._lastSessionState = null;
       let openWindows = {};
       this._forEachBrowserWindow(function(aWindow) {
         Array.forEach(aWindow.gBrowser.tabs, function(aTab) {
           delete aTab.linkedBrowser.__SS_data;
+          delete aTab.linkedBrowser.__SS_tabStillLoading;
+          delete aTab.linkedBrowser.__SS_formDataSaved;
+          delete aTab.linkedBrowser.__SS_hostSchemeData;
           if (aTab.linkedBrowser.__SS_restoreState)
             this._resetTabRestoringState(aTab);
         });
         openWindows[aWindow.__SSi] = true;
       });
       // also clear all data about closed tabs and windows
       for (let ix in this._windows) {
-        if (ix in openWindows)
+        if (ix in openWindows) {
           this._windows[ix]._closedTabs = [];
-        else
+        }
+        else {
           delete this._windows[ix];
+          delete this._internalWindows[ix];
+        }
       }
       // also clear all data about closed windows
       this._closedWindows = [];
       // give the tabbrowsers a chance to clear their histories first
       var win = this._getMostRecentBrowserWindow();
       if (win)
         win.setTimeout(function() { _this.saveState(true); }, 0);
       else if (this._loadState == STATE_RUNNING)
@@ -787,16 +801,20 @@ SessionStoreService.prototype = {
         this._loadState == STATE_QUITTING)
       return;
 
     // assign it a unique identifier (timestamp)
     aWindow.__SSi = "window" + Date.now();
 
     // and create its data object
     this._windows[aWindow.__SSi] = { tabs: [], selected: 0, _closedTabs: [], busy: false };
+
+    // and create its internal data object
+    this._internalWindows[aWindow.__SSi] = { hosts: {} }
+
     if (!this._isWindowLoaded(aWindow))
       this._windows[aWindow.__SSi]._restoring = true;
     if (!aWindow.toolbar.visible)
       this._windows[aWindow.__SSi].isPopup = true;
     
     // perform additional initialization when the first window is loading
     if (this._loadState == STATE_STOPPED) {
       this._loadState = STATE_RUNNING;
@@ -958,17 +976,19 @@ SessionStoreService.prototype = {
     if (this._loadState == STATE_RUNNING) { // window not closed during a regular shut-down 
       // update all window data for a last time
       this._collectWindowData(aWindow);
       
       if (isFullyLoaded) {
         winData.title = aWindow.content.document.title || tabbrowser.selectedTab.label;
         winData.title = this._replaceLoadingTitle(winData.title, tabbrowser,
                                                   tabbrowser.selectedTab);
-        this._updateCookies([winData]);
+        let windows = {};
+        windows[aWindow.__SSi] = winData;
+        this._updateCookies(windows);
       }
 
 #ifndef XP_MACOSX
       // Until we decide otherwise elsewhere, this window is part of a series
       // of closing windows to quit.
       winData._shouldRestore = true;
 #endif
 
@@ -979,16 +999,17 @@ SessionStoreService.prototype = {
         delete winData.busy;
 
         this._closedWindows.unshift(winData);
         this._capClosedWindows();
       }
       
       // clear this window from the list
       delete this._windows[aWindow.__SSi];
+      delete this._internalWindows[aWindow.__SSi];
       
       // save the state without this window to disk
       this.saveStateDelayed();
     }
     
     for (let i = 0; i < tabbrowser.tabs.length; i++) {
       this.onTabRemove(aWindow, tabbrowser.tabs[i], true);
     }
@@ -1036,16 +1057,19 @@ SessionStoreService.prototype = {
     let browser = aTab.linkedBrowser;
     browser.removeEventListener("load", this, true);
     browser.removeEventListener("pageshow", this, true);
     browser.removeEventListener("change", this, true);
     browser.removeEventListener("input", this, true);
     browser.removeEventListener("DOMAutoComplete", this, true);
 
     delete browser.__SS_data;
+    delete browser.__SS_tabStillLoading;
+    delete browser.__SS_formDataSaved;
+    delete browser.__SS_hostSchemeData;
 
     // If this tab was in the middle of restoring or still needs to be restored,
     // we need to reset that state. If the tab was restoring, we will attempt to
     // restore the next tab.
     let previousState;
     if (previousState = browser.__SS_restoreState) {
       this._resetTabRestoringState(aTab);
       if (previousState == TAB_STATE_RESTORING)
@@ -1115,33 +1139,35 @@ SessionStoreService.prototype = {
     // overwriting tabs). We want to return early if the tab hasn't been restored yet.
     if ((aEvent.type != "load" && !aEvent.persisted) ||
         (aBrowser.__SS_restoreState &&
          aBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)) {
       return;
     }
     
     delete aBrowser.__SS_data;
+    delete aBrowser.__SS_tabStillLoading;
+    delete aBrowser.__SS_formDataSaved;
     this.saveStateDelayed(aWindow);
     
     // attempt to update the current URL we send in a crash report
     this._updateCrashReportURL(aWindow);
   },
 
   /**
    * Called when a browser sends the "input" notification 
    * @param aWindow
    *        Window reference
    * @param aBrowser
    *        Browser reference
    */
   onTabInput: function sss_onTabInput(aWindow, aBrowser) {
-    if (aBrowser.__SS_data)
-      delete aBrowser.__SS_data._formDataSaved;
-    
+    // deleting __SS_formDataSaved will cause us to recollect form data
+    delete aBrowser.__SS_formDataSaved;
+
     this.saveStateDelayed(aWindow, 3000);
   },
 
   /**
    * When a tab is selected, save session data
    * @param aWindow
    *        Window reference
    */
@@ -1577,16 +1603,20 @@ SessionStoreService.prototype = {
     }
 
     // Merge closed windows from this session with ones from last session
     if (lastSessionState._closedWindows) {
       this._closedWindows = this._closedWindows.concat(lastSessionState._closedWindows);
       this._capClosedWindows();
     }
 
+    if (lastSessionState.scratchpads) {
+      ScratchpadManager.restoreSession(lastSessionState.scratchpads);
+    }
+
     // Set data that persists between sessions
     this._recentCrashes = lastSessionState.session &&
                           lastSessionState.session.recentCrashes || 0;
     this._sessionStartTime = lastSessionState.session &&
                              lastSessionState.session.startTime ||
                              this._sessionStartTime;
 
     this._lastSessionState = null;
@@ -1694,17 +1724,17 @@ SessionStoreService.prototype = {
    */
   _collectTabData: function sss_collectTabData(aTab, aFullData) {
     var tabData = { entries: [] };
     var browser = aTab.linkedBrowser;
     
     if (!browser || !browser.currentURI)
       // can happen when calling this function right after .addTab()
       return tabData;
-    else if (browser.__SS_data && browser.__SS_data._tabStillLoading) {
+    else if (browser.__SS_data && browser.__SS_tabStillLoading) {
       // use the data to be restored when the tab hasn't been completely loaded
       tabData = browser.__SS_data;
       if (aTab.pinned)
         tabData.pinned = true;
       else
         delete tabData.pinned;
       tabData.hidden = aTab.hidden;
 
@@ -1729,20 +1759,21 @@ SessionStoreService.prototype = {
     if (history && browser.__SS_data &&
         browser.__SS_data.entries[history.index] &&
         browser.__SS_data.entries[history.index].url == browser.currentURI.spec &&
         history.index < this._sessionhistory_max_entries - 1 && !aFullData) {
       tabData = browser.__SS_data;
       tabData.index = history.index + 1;
     }
     else if (history && history.count > 0) {
+      browser.__SS_hostSchemeData = [];
       try {
         for (var j = 0; j < history.count; j++) {
           let entry = this._serializeHistoryEntry(history.getEntryAtIndex(j, false),
-                                                  aFullData, aTab.pinned);
+                                                  aFullData, aTab.pinned, browser.__SS_hostSchemeData);
           tabData.entries.push(entry);
         }
         // If we make it through the for loop, then we're ok and we should clear
         // any indicator of brokenness.
         delete aTab.__SS_broken_history;
       }
       catch (ex) {
         // In some cases, getEntryAtIndex will throw. This seems to be due to
@@ -1821,25 +1852,26 @@ SessionStoreService.prototype = {
    * Get an object that is a serialized representation of a History entry
    * Used for data storage
    * @param aEntry
    *        nsISHEntry instance
    * @param aFullData
    *        always return privacy sensitive data (use with care)
    * @param aIsPinned
    *        the tab is pinned and should be treated differently for privacy
+   * @param aHostSchemeData
+   *        an array of objects with host & scheme keys
    * @returns object
    */
   _serializeHistoryEntry:
-    function sss_serializeHistoryEntry(aEntry, aFullData, aIsPinned) {
+    function sss_serializeHistoryEntry(aEntry, aFullData, aIsPinned, aHostSchemeData) {
     var entry = { url: aEntry.URI.spec };
 
     try {
-      entry._host = aEntry.URI.host;
-      entry._scheme = aEntry.URI.scheme;
+      aHostSchemeData.push({ host: aEntry.URI.host, scheme: aEntry.URI.scheme });
     }
     catch (ex) {
       // We just won't attempt to get cookies for this entry.
     }
 
     if (aEntry.title && aEntry.title != entry.url) {
       entry.title = aEntry.title;
     }
@@ -1932,17 +1964,17 @@ SessionStoreService.prototype = {
     }
     
     if (aEntry.childCount > 0) {
       entry.children = [];
       for (var i = 0; i < aEntry.childCount; i++) {
         var child = aEntry.GetChildAt(i);
         if (child) {
           entry.children.push(this._serializeHistoryEntry(child, aFullData,
-                                                          aIsPinned));
+                                                          aIsPinned, aHostSchemeData));
         }
         else { // to maintain the correct frame order, insert a dummy entry 
           entry.children.push({ url: "about:blank" });
         }
         // don't try to restore framesets containing wyciwyg URLs (cf. bug 424689 and bug 450595)
         if (/^wyciwyg:\/\//.test(entry.children[i].url)) {
           delete entry.children;
           break;
@@ -2030,17 +2062,17 @@ SessionStoreService.prototype = {
    * and innerHTML content of WYSIWYG editors
    * @param aWindow
    *        Window reference
    */
   _updateTextAndScrollData: function sss_updateTextAndScrollData(aWindow) {
     var browsers = aWindow.gBrowser.browsers;
     this._windows[aWindow.__SSi].tabs.forEach(function (tabData, i) {
       if (browsers[i].__SS_data &&
-          browsers[i].__SS_data._tabStillLoading)
+          browsers[i].__SS_tabStillLoading)
         return; // ignore incompletely initialized tabs
       try {
         this._updateTextAndScrollDataForTab(aWindow, browsers[i], tabData);
       }
       catch (ex) { debug(ex); } // get as much data as possible, ignore failures (might succeed the next time)
     }, this);
   },
 
@@ -2067,19 +2099,19 @@ SessionStoreService.prototype = {
                             this._getSelectedPageStyle(aBrowser.contentWindow);
     if (selectedPageStyle)
       aTabData.pageStyle = selectedPageStyle;
     else if (aTabData.pageStyle)
       delete aTabData.pageStyle;
     
     this._updateTextAndScrollDataForFrame(aWindow, aBrowser.contentWindow,
                                           aTabData.entries[tabIndex],
-                                          !aTabData._formDataSaved, aFullData,
+                                          !aBrowser.__SS_formDataSaved, aFullData,
                                           !!aTabData.pinned);
-    aTabData._formDataSaved = true;
+    aBrowser.__SS_formDataSaved = true;
     if (aBrowser.currentURI.spec == "about:config")
       aTabData.entries[tabIndex].formdata = {
         "#textbox": aBrowser.contentDocument.getElementById("textbox").value
       };
   },
 
   /**
    * go through all subframes and store all form data, the current
@@ -2246,92 +2278,133 @@ SessionStoreService.prototype = {
    * @param aHosts
    *        the hash that will be used to store hosts eg, { hostname: true }
    * @param aCheckPrivacy
    *        should we check the privacy level for https
    * @param aIsPinned
    *        is the entry we're evaluating for a pinned tab; used only if
    *        aCheckPrivacy
    */
-  _extractHostsForCookies:
-    function sss__extractHostsForCookies(aEntry, aHosts, aCheckPrivacy, aIsPinned) {
-
-    // _host and _scheme may not be set (for about: urls for example), in which
-    // case testing _scheme will be sufficient.
-    if (/https?/.test(aEntry._scheme) && !aHosts[aEntry._host] &&
-        (!aCheckPrivacy ||
-         this._checkPrivacyLevel(aEntry._scheme == "https", aIsPinned))) {
-      // By setting this to true or false, we can determine when looking at
-      // the host in _updateCookies if we should check for privacy.
-      aHosts[aEntry._host] = aIsPinned;
-    }
-    else if (aEntry._scheme == "file") {
-      aHosts[aEntry._host] = true;
+  _extractHostsForCookiesFromEntry:
+    function sss__extractHostsForCookiesFromEntry(aEntry, aHosts, aCheckPrivacy, aIsPinned) {
+
+    let host = aEntry._host,
+        scheme = aEntry._scheme;
+
+    // If host & scheme aren't defined, then we are likely here in the startup
+    // process via _splitCookiesFromWindow. In that case, we'll turn aEntry.url
+    // into an nsIURI and get host/scheme from that. This will throw for about:
+    // urls in which case we don't need to do anything.
+    if (!host && !scheme) {
+      try {
+        let uri = this._getURIFromString(aEntry.url);
+        host = uri.host;
+        scheme = uri.scheme;
+        this._extractHostsForCookiesFromHostScheme(host, scheme, aHosts, aCheckPrivacy, aIsPinned);
+      }
+      catch(ex) { }
     }
 
     if (aEntry.children) {
       aEntry.children.forEach(function(entry) {
         this._extractHostsForCookies(entry, aHosts, aCheckPrivacy, aIsPinned);
       }, this);
     }
   },
 
   /**
+   * extract the base domain from a host & scheme
+   * @param aHost
+   *        the host of a uri (usually via nsIURI.host)
+   * @param aScheme
+   *        the scheme of a uri (usually via nsIURI.scheme)
+   * @param aHosts
+   *        the hash that will be used to store hosts eg, { hostname: true }
+   * @param aCheckPrivacy
+   *        should we check the privacy level for https
+   * @param aIsPinned
+   *        is the entry we're evaluating for a pinned tab; used only if
+   *        aCheckPrivacy
+   */
+  _extractHostsForCookiesFromHostScheme:
+    function sss__extractHostsForCookiesFromHostScheme(aHost, aScheme, aHosts, aCheckPrivacy, aIsPinned) {
+    // host and scheme may not be set (for about: urls for example), in which
+    // case testing scheme will be sufficient.
+    if (/https?/.test(aScheme) && !aHosts[aHost] &&
+        (!aCheckPrivacy ||
+         this._checkPrivacyLevel(aScheme == "https", aIsPinned))) {
+      // By setting this to true or false, we can determine when looking at
+      // the host in _updateCookies if we should check for privacy.
+      aHosts[aHost] = aIsPinned;
+    }
+    else if (aScheme == "file") {
+      aHosts[aHost] = true;
+    }
+  },
+
+  /**
    * store all hosts for a URL
    * @param aWindow
    *        Window reference
    */
   _updateCookieHosts: function sss_updateCookieHosts(aWindow) {
-    var hosts = this._windows[aWindow.__SSi]._hosts = {};
-
-    this._windows[aWindow.__SSi].tabs.forEach(function(aTabData) {
-      aTabData.entries.forEach(function(entry) {
-        this._extractHostsForCookies(entry, hosts, true, !!aTabData.pinned);
-      }, this);
-    }, this);
+    var hosts = this._internalWindows[aWindow.__SSi].hosts = {};
+
+    // Since _updateCookiesHosts is only ever called for open windows during a
+    // session, we can call into _extractHostsForCookiesFromHostScheme directly
+    // using data that is attached to each browser.
+    for (let i = 0; i < aWindow.gBrowser.tabs.length; i++) {
+      let tab = aWindow.gBrowser.tabs[i];
+      let hostSchemeData = tab.linkedBrowser.__SS_hostSchemeData || [];
+      for (let j = 0; j < hostSchemeData.length; j++) {
+        this._extractHostsForCookiesFromHostScheme(hostSchemeData[j].host,
+                                                   hostSchemeData[j].scheme,
+                                                   hosts, true, tab.pinned);
+      }
+    }
   },
 
   /**
    * Serialize cookie data
    * @param aWindows
-   *        array of Window references
+   *        JS object containing window data references
+   *        { id: winData, etc. }
    */
   _updateCookies: function sss_updateCookies(aWindows) {
     function addCookieToHash(aHash, aHost, aPath, aName, aCookie) {
       // lazily build up a 3-dimensional hash, with
       // aHost, aPath, and aName as keys
       if (!aHash[aHost])
         aHash[aHost] = {};
       if (!aHash[aHost][aPath])
         aHash[aHost][aPath] = {};
       aHash[aHost][aPath][aName] = aCookie;
     }
 
-    // collect the cookies per window
-    for (var i = 0; i < aWindows.length; i++)
-      aWindows[i].cookies = [];
-
     var jscookies = {};
     var _this = this;
     // MAX_EXPIRY should be 2^63-1, but JavaScript can't handle that precision
     var MAX_EXPIRY = Math.pow(2, 62);
-    aWindows.forEach(function(aWindow) {
-      if (!aWindow._hosts)
+
+    for (let [id, window] in Iterator(aWindows)) {
+      window.cookies = [];
+      let internalWindow = this._internalWindows[id];
+      if (!internalWindow.hosts)
         return;
-      for (var [host, isPinned] in Iterator(aWindow._hosts)) {
+      for (var [host, isPinned] in Iterator(internalWindow.hosts)) {
         let list;
         try {
           list = CookieSvc.getCookiesFromHost(host);
         }
         catch (ex) {
           debug("getCookiesFromHost failed. Host: " + host);
         }
         while (list && list.hasMoreElements()) {
           var cookie = list.getNext().QueryInterface(Ci.nsICookie2);
-          // aWindow._hosts will only have hosts with the right privacy rules,
+          // window._hosts will only have hosts with the right privacy rules,
           // so there is no need to do anything special with this call to
           // _checkPrivacyLevel.
           if (cookie.isSession && _this._checkPrivacyLevel(cookie.isSecure, isPinned)) {
             // use the cookie's host, path, and name as keys into a hash,
             // to make sure we serialize each cookie only once
             if (!(cookie.host in jscookies &&
                   cookie.path in jscookies[cookie.host] &&
                   cookie.name in jscookies[cookie.host][cookie.path])) {
@@ -2340,26 +2413,25 @@ SessionStoreService.prototype = {
               if (cookie.path) jscookie.path = cookie.path;
               if (cookie.name) jscookie.name = cookie.name;
               if (cookie.isSecure) jscookie.secure = true;
               if (cookie.isHttpOnly) jscookie.httponly = true;
               if (cookie.expiry < MAX_EXPIRY) jscookie.expiry = cookie.expiry;
 
               addCookieToHash(jscookies, cookie.host, cookie.path, cookie.name, jscookie);
             }
-            aWindow.cookies.push(jscookies[cookie.host][cookie.path][cookie.name]);
+            window.cookies.push(jscookies[cookie.host][cookie.path][cookie.name]);
           }
         }
       }
-    });
-
-    // don't include empty cookie sections
-    for (i = 0; i < aWindows.length; i++)
-      if (aWindows[i].cookies.length == 0)
-        delete aWindows[i].cookies;
+
+      // don't include empty cookie sections
+      if (!window.cookies.length)
+        delete window.cookies;
+    }
   },
 
   /**
    * Store window dimensions, visibility, sidebar
    * @param aWindow
    *        Window reference
    */
   _updateWindowFeatures: function sss_updateWindowFeatures(aWindow) {
@@ -2408,28 +2480,29 @@ SessionStoreService.prototype = {
         else { // always update the window features (whose change alone never triggers a save operation)
           this._updateWindowFeatures(aWindow);
         }
       });
       this._dirtyWindows = [];
     }
     
     // collect the data for all windows
-    var total = [], windows = [];
+    var total = [], windows = {}, ids = [];
     var nonPopupCount = 0;
     var ix;
     for (ix in this._windows) {
       if (this._windows[ix]._restoring) // window data is still in _statesToRestore
         continue;
       total.push(this._windows[ix]);
-      windows.push(ix);
+      ids.push(ix);
+      windows[ix] = this._windows[ix];
       if (!this._windows[ix].isPopup)
         nonPopupCount++;
     }
-    this._updateCookies(total);
+    this._updateCookies(windows);
 
     // collect the data for all windows yet to be restored
     for (ix in this._statesToRestore) {
       for each (let winData in this._statesToRestore[ix].windows) {
         total.push(winData);
         if (!winData.isPopup)
           nonPopupCount++;
       }
@@ -2470,34 +2543,38 @@ SessionStoreService.prototype = {
         return null;
 
       lastClosedWindowsCopy = [];
     }
 
     if (activeWindow) {
       this.activeWindowSSiCache = activeWindow.__SSi || "";
     }
-    ix = windows.indexOf(this.activeWindowSSiCache);
+    ix = ids.indexOf(this.activeWindowSSiCache);
     // We don't want to restore focus to a minimized window or a window which had all its
     // tabs stripped out (doesn't exist).
     if (ix != -1 && total[ix] && total[ix].sizemode == "minimized")
       ix = -1;
 
     let session = {
       state: this._loadState == STATE_RUNNING ? STATE_RUNNING_STR : STATE_STOPPED_STR,
       lastUpdate: Date.now(),
       startTime: this._sessionStartTime,
       recentCrashes: this._recentCrashes
     };
+    
+    // get open Scratchpad window states too
+    var scratchpads = ScratchpadManager.getSessionState();
 
     return {
       windows: total,
       selectedWindow: ix + 1,
       _closedWindows: lastClosedWindowsCopy,
-      session: session
+      session: session,
+      scratchpads: scratchpads
     };
   },
 
   /**
    * serialize session data for a window 
    * @param aWindow
    *        Window reference
    * @returns string
@@ -2505,20 +2582,22 @@ SessionStoreService.prototype = {
   _getWindowState: function sss_getWindowState(aWindow) {
     if (!this._isWindowLoaded(aWindow))
       return this._statesToRestore[aWindow.__SS_restoreID];
     
     if (this._loadState == STATE_RUNNING) {
       this._collectWindowData(aWindow);
     }
     
-    var total = [this._windows[aWindow.__SSi]];
-    this._updateCookies(total);
+    var winData = this._windows[aWindow.__SSi];
+    let windows = {};
+    windows[aWindow.__SSi] = winData;
+    this._updateCookies(windows);
     
-    return { windows: total };
+    return { windows: [winData] };
   },
 
   _collectWindowData: function sss_collectWindowData(aWindow) {
     if (!this._isWindowLoaded(aWindow))
       return;
     
     // update the internal state data for this window
     this._saveWindowHistory(aWindow);
@@ -2695,16 +2774,20 @@ SessionStoreService.prototype = {
     }
     if (aOverwriteTabs || root._firstTabs) {
       this._windows[aWindow.__SSi]._closedTabs = winData._closedTabs || [];
     }
     
     this.restoreHistoryPrecursor(aWindow, tabs, winData.tabs,
       (aOverwriteTabs ? (parseInt(winData.selected) || 1) : 0), 0, 0);
 
+    if (aState.scratchpads) {
+      ScratchpadManager.restoreSession(aState.scratchpads);
+    }
+
     // This will force the keypress listener that Panorama has to attach if it
     // isn't already. This will be the case if tab view wasn't entered or there
     // were only visible tabs when TabView.init was first called.
     aWindow.TabView.init();
 
     // set smoothScroll back to the original value
     tabstrip.smoothScroll = smoothScroll;
 
@@ -2826,17 +2909,17 @@ SessionStoreService.prototype = {
       if (tabData.hidden)
         tabbrowser.hideTab(tab);
       else
         tabbrowser.showTab(tab);
 
       for (let name in tabData.attributes)
         this.xulAttributes[name] = true;
 
-      tabData._tabStillLoading = true;
+      browser.__SS_tabStillLoading = true;
 
       // keep the data around to prevent dataloss in case
       // a tab gets closed before it's been properly restored
       browser.__SS_data = tabData;
       browser.__SS_restoreState = TAB_STATE_NEEDS_RESTORE;
       tab.setAttribute("pending", "true");
 
       // Make sure that set/getTabValue will set/read the correct data by
@@ -2893,17 +2976,17 @@ SessionStoreService.prototype = {
    * @param aTabData
    *        Array of tab data
    * @param aIdMap
    *        Hash for ensuring unique frame IDs
    */
   restoreHistory:
     function sss_restoreHistory(aWindow, aTabs, aTabData, aIdMap, aDocIdentMap) {
     var _this = this;
-    while (aTabs.length > 0 && (!aTabData[0]._tabStillLoading || !aTabs[0].parentNode)) {
+    while (aTabs.length > 0 && (!aTabs[0].linkedBrowser.__SS_tabStillLoading || !aTabs[0].parentNode)) {
       aTabs.shift(); // this tab got removed before being completely restored
       aTabData.shift();
     }
     if (aTabs.length == 0) {
       // At this point we're essentially ready for consumers to read/write data
       // via the sessionstore API so we'll send the SSWindowStateReady event.
       this._setWindowStateReady(aWindow);
       return; // no more tabs to restore
@@ -3289,41 +3372,63 @@ SessionStoreService.prototype = {
         if (!hasExpectedURL(aDocument, aURL))
           return;
 
         let node = key.charAt(0) == "#" ? aDocument.getElementById(key.slice(1)) :
                                           XPathHelper.resolve(aDocument, key);
         if (!node)
           continue;
 
+        let eventType;
         let value = aData[key];
         if (typeof value == "string" && node.type != "file") {
           if (node.value == value)
             continue; // don't dispatch an input event for no change
 
           node.value = value;
-
-          let event = aDocument.createEvent("UIEvents");
-          event.initUIEvent("input", true, true, aDocument.defaultView, 0);
-          node.dispatchEvent(event);
+          eventType = "input";
         }
-        else if (typeof value == "boolean")
+        else if (typeof value == "boolean") {
+          if (node.checked == value)
+            continue; // don't dispatch a change event for no change
+
           node.checked = value;
-        else if (typeof value == "number")
+          eventType = "change";
+        }
+        else if (typeof value == "number") {
+          // We saved the value blindly since selects take more work to determine
+          // default values. So now we should check to avoid unnecessary events.
+          if (node.selectedIndex == value)
+            continue;
+
           try {
             node.selectedIndex = value;
+            eventType = "change";
           } catch (ex) { /* throws for invalid indices */ }
-        else if (value && value.fileList && value.type == "file" && node.type == "file")
+        }
+        else if (value && value.fileList && value.type == "file" && node.type == "file") {
           node.mozSetFileNameArray(value.fileList, value.fileList.length);
+          eventType = "input";
+        }
         else if (value && typeof value.indexOf == "function" && node.options) {
           Array.forEach(node.options, function(aOpt, aIx) {
             aOpt.selected = value.indexOf(aIx) > -1;
+
+            // Only fire the event here if this wasn't selected by default
+            if (!aOpt.defaultSelected)
+              eventType = "change";
           });
         }
-        // NB: dispatching "change" events might have unintended side-effects
+
+        // Fire events for this node if applicable
+        if (eventType) {
+          let event = aDocument.createEvent("UIEvents");
+          event.initUIEvent(eventType, true, true, aDocument.defaultView, 0);
+          node.dispatchEvent(event);
+        }
       }
     }
 
     let selectedPageStyle = aBrowser.__SS_restore_pageStyle;
     function restoreTextDataAndScrolling(aContent, aData, aPrefix) {
       if (aData.formdata)
         restoreFormData(aContent.document, aData.formdata, aData.url);
       if (aData.innerHTML) {
@@ -3525,16 +3630,17 @@ SessionStoreService.prototype = {
 
 #ifndef XP_MACOSX
     // We want to restore closed windows that are marked with _shouldRestore.
     // We're doing this here because we want to control this only when saving
     // the file.
     while (oState._closedWindows.length) {
       let i = oState._closedWindows.length - 1;
       if (oState._closedWindows[i]._shouldRestore) {
+        delete oState._closedWindows[i]._shouldRestore;
         oState.windows.unshift(oState._closedWindows.pop());
       }
       else {
         // We only need to go until we hit !needsRestore since we're going in reverse
         break;
       }
     }
 #endif
@@ -3993,23 +4099,26 @@ SessionStoreService.prototype = {
     function sss__splitCookiesFromWindow(aWinState, aTargetWinState) {
     if (!aWinState.cookies || !aWinState.cookies.length)
       return;
 
     // Get the hosts for history entries in aTargetWinState
     let cookieHosts = {};
     aTargetWinState.tabs.forEach(function(tab) {
       tab.entries.forEach(function(entry) {
-        this._extractHostsForCookies(entry, cookieHosts, false)
+        this._extractHostsForCookiesFromEntry(entry, cookieHosts, false);
       }, this);
     }, this);
 
     // By creating a regex we reduce overhead and there is only one loop pass
     // through either array (cookieHosts and aWinState.cookies).
     let hosts = Object.keys(cookieHosts).join("|").replace("\\.", "\\.", "g");
+    // If we don't actually have any hosts, then we don't want to do anything.
+    if (!hosts.length)
+      return;
     let cookieRegex = new RegExp(".*(" + hosts + ")");
     for (let cIndex = 0; cIndex < aWinState.cookies.length;) {
       if (cookieRegex.test(aWinState.cookies[cIndex].host)) {
         aTargetWinState.cookies =
           aTargetWinState.cookies.concat(aWinState.cookies.splice(cIndex, 1));
         continue;
       }
       cIndex++;
@@ -4021,28 +4130,17 @@ SessionStoreService.prototype = {
    * (see http://www.json.org/ for more information).
    *
    * The inverse operation consists of JSON.parse(JSON_string).
    *
    * @param aJSObject is the object to be converted
    * @returns the object's JSON representation
    */
   _toJSONString: function sss_toJSONString(aJSObject) {
-    // We never want to save __lastSessionWindowID across sessions, but we do
-    // want it exported to consumers when running (eg. Private Browsing).
-    let internalKeys = INTERNAL_KEYS;
-    if (this._loadState == STATE_QUITTING) {
-      internalKeys = internalKeys.slice();
-      internalKeys.push("__lastSessionWindowID");
-    }
-    function exclude(key, value) {
-      // returning undefined results in the exclusion of that key
-      return internalKeys.indexOf(key) == -1 ? value : undefined;
-    }
-    return JSON.stringify(aJSObject, exclude);
+    return JSON.stringify(aJSObject);
   },
 
   _sendRestoreCompletedNotifications: function sss_sendRestoreCompletedNotifications() {
     // not all windows restored, yet
     if (this._restoreCount > 1) {
       this._restoreCount--;
       return;
     }
--- a/browser/components/sessionstore/test/browser/Makefile.in
+++ b/browser/components/sessionstore/test/browser/Makefile.in
@@ -45,16 +45,18 @@ relativesrcdir  = browser/components/ses
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 # browser_506482.js is disabled because of frequent failures (bug 538672)
 # browser_526613.js is disabled because of frequent failures (bug 534489)
 
 _BROWSER_TEST_FILES = \
 	head.js \
+	browser_form_restore_events.js \
+	browser_form_restore_events_sample.html \
 	browser_248970_a.js \
 	browser_248970_b.js \
 	browser_248970_b_sample.html \
 	browser_339445.js \
 	browser_339445_sample.html \
 	browser_345898.js \
 	browser_346337.js \
 	browser_346337_sample.html \
@@ -93,18 +95,16 @@ include $(topsrcdir)/config/rules.mk
 	browser_464620_a.html \
 	browser_464620_b.js \
 	browser_464620_b.html \
 	browser_464620_xd.html \
 	browser_465215.js \
 	browser_465223.js \
 	browser_466937.js \
 	browser_466937_sample.html \
-	browser_476161.js \
-	browser_476161_sample.html \
 	browser_477657.js \
 	browser_480148.js \
 	browser_480893.js \
 	browser_483330.js \
 	browser_485482.js \
 	browser_485482_sample.html \
 	browser_485563.js \
 	browser_490040.js \
@@ -144,16 +144,17 @@ include $(topsrcdir)/config/rules.mk
 	browser_615394-SSWindowState_events.js \
 	browser_618151.js \
 	browser_623779.js \
 	browser_624727.js \
 	browser_625257.js \
 	browser_628270.js \
 	browser_635418.js \
 	browser_636279.js \
+	browser_644409-scratchpads.js \
 	browser_645428.js \
 	browser_659591.js \
 	browser_662812.js \
 	browser_665702-state_session.js \
 	browser_682507.js \
 	browser_687710.js \
 	browser_687710_2.js \
 	browser_694378.js \
--- a/browser/components/sessionstore/test/browser/browser_579868.js
+++ b/browser/components/sessionstore/test/browser/browser_579868.js
@@ -45,17 +45,17 @@ function test() {
 
     // Tell the session storer that the tab is pinned
     let newTabState = '{"entries":[{"url":"about:robots"}],"pinned":true,"userTypedValue":"Hello World!"}';
     ss.setTabState(tab1, newTabState);
 
     // Undo pinning
     gBrowser.unpinTab(tab1);
 
-    is(tab1.linkedBrowser.__SS_data._tabStillLoading, true, 
+    is(tab1.linkedBrowser.__SS_tabStillLoading, true,
        "_tabStillLoading should be true.");
 
     // Close and restore tab
     gBrowser.removeTab(tab1);
     let savedState = JSON.parse(ss.getClosedTabData(window))[0].state;
     isnot(savedState.pinned, true, "Pinned should not be true");
     tab1 = ss.undoCloseTab(window, 0);
 
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser/browser_644409-scratchpads.js
@@ -0,0 +1,59 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const testState = {
+  windows: [{
+    tabs: [
+      { entries: [{ url: "about:blank" }] },
+    ]
+  }],
+  scratchpads: [
+    { text: "text1", executionContext: 1 },
+    { text: "", executionContext: 2, filename: "test.js" }
+  ]
+};
+
+// only finish() when correct number of windows opened
+var restored = [];
+function addState(state) {
+  restored.push(state);
+
+  if (restored.length == testState.scratchpads.length) {
+    ok(statesMatch(restored, testState.scratchpads),
+      "Two scratchpad windows restored");
+
+    Services.ww.unregisterNotification(windowObserver);
+    finish();
+  }
+}
+
+function test() {
+  waitForExplicitFinish();
+
+  Services.ww.registerNotification(windowObserver);
+
+  ss.setBrowserState(JSON.stringify(testState));
+}
+
+function windowObserver(aSubject, aTopic, aData) {
+  if (aTopic == "domwindowopened") {     
+    let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
+    win.addEventListener("load", function() {
+      if (win.Scratchpad) {
+        let state = win.Scratchpad.getState();
+        win.close();
+        addState(state);
+      }
+    }, false);
+  }
+}
+
+function statesMatch(restored, states) {
+  return states.every(function(state) {
+    return restored.some(function(restoredState) {
+      return state.filename == restoredState.filename &&
+             state.text == restoredState.text &&
+             state.executionContext == restoredState.executionContext;
+    })
+  });
+}
\ No newline at end of file
rename from browser/components/sessionstore/test/browser/browser_476161.js
rename to browser/components/sessionstore/test/browser/browser_form_restore_events.js
--- a/browser/components/sessionstore/test/browser/browser_476161.js
+++ b/browser/components/sessionstore/test/browser/browser_form_restore_events.js
@@ -30,39 +30,70 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 function test() {
-  /** Test for Bug 476161 **/
-  
+  /** Originally a test for Bug 476161, but then expanded to include all input types in bug 640136 **/
+
   waitForExplicitFinish();
-  
+
+  let file = Components.classes["@mozilla.org/file/directory_service;1"]
+             .getService(Components.interfaces.nsIProperties)
+             .get("TmpD", Components.interfaces.nsIFile);
+
   let testURL = "http://mochi.test:8888/browser/" +
-    "browser/components/sessionstore/test/browser/browser_476161_sample.html";
+    "browser/components/sessionstore/test/browser/browser_form_restore_events_sample.html";
   let tab = gBrowser.addTab(testURL);
   tab.linkedBrowser.addEventListener("load", function(aEvent) {
     tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
     let doc = tab.linkedBrowser.contentDocument;
-    
-    doc.getElementById("modify1").value += Math.random();
-    doc.getElementById("modify2").value += " " + Date.now();
-    
+
+    // text fields
+    doc.getElementById("modify01").value += Math.random();
+    doc.getElementById("modify02").value += " " + Date.now();
+
+    // textareas
+    doc.getElementById("modify03").value += Math.random();
+    doc.getElementById("modify04").value += " " + Date.now();
+
+    // file
+    doc.getElementById("modify05").value = file.path;
+
+    // select
+    doc.getElementById("modify06").selectedIndex = 1;
+    var multipleChange = doc.getElementById("modify07");
+    Array.forEach(multipleChange.options, function(option) option.selected = true);
+
+    // checkbox
+    doc.getElementById("modify08").checked = true;
+    doc.getElementById("modify09").checked = false;
+
+    // radio
+    // select one then another in the same group - only last one should get event on restore
+    doc.getElementById("modify10").checked = true;
+    doc.getElementById("modify11").checked = true;
+
+
     let tab2 = gBrowser.duplicateTab(tab);
     tab2.linkedBrowser.addEventListener("load", function(aEvent) {
       tab2.linkedBrowser.removeEventListener("load", arguments.callee, true);
       let doc = tab2.linkedBrowser.contentDocument;
-      let changed = doc.getElementById("changed").textContent.trim().split();
-      
-      is(changed.sort().join(" "), "modify1 modify2",
-         "input events were only dispatched for modified text fields");
-      
+      let inputFired = doc.getElementById("inputFired").textContent.trim().split();
+      let changeFired = doc.getElementById("changeFired").textContent.trim().split();
+
+      is(inputFired.sort().join(" "), "modify01 modify02 modify03 modify04 modify05",
+         "input events were only dispatched for modified input, textarea fields");
+
+      is(changeFired.sort().join(" "), "modify06 modify07 modify08 modify09 modify11",
+         "change events were only dispatched for modified select, checkbox, radio fields");
+
       // clean up
       gBrowser.removeTab(tab2);
       gBrowser.removeTab(tab);
-      
+
       finish();
     }, true);
   }, true);
 }
rename from browser/components/sessionstore/test/browser/browser_476161_sample.html
rename to browser/components/sessionstore/test/browser/browser_form_restore_events_sample.html
--- a/browser/components/sessionstore/test/browser/browser_476161_sample.html
+++ b/browser/components/sessionstore/test/browser/browser_form_restore_events_sample.html
@@ -1,24 +1,98 @@
 <!DOCTYPE html>
-<title>Test for bug 476161</title>
+<title>Test for form restore events (originally bug 476161)</title>
 
 <script>
 
 document.addEventListener("input", function(aEvent) {
   var inputEl = aEvent.originalTarget;
-  var changedEl = document.getElementById("changed");
-  
+  var changedEl = document.getElementById("inputFired");
+  changedEl.textContent += " " + inputEl.id;
+}, false);
+
+document.addEventListener("change", function(aEvent) {
+  var inputEl = aEvent.originalTarget;
+  var changedEl = document.getElementById("changeFired");
   changedEl.textContent += " " + inputEl.id;
 }, false);
 
 </script>
 
+<!-- input events -->
 <h3>Text fields with changed text</h3>
 <input type="text" id="modify1">
 <input type="text" id="modify2" value="preset value">
+<input type="text" id="modify01">
+<input type="text" id="modify02" value="preset value">
 
 <h3>Text fields with unchanged text</h3>
 <input type="text" id="unchanged1">
 <input type="text" id="unchanged2" value="preset value">
+<input type="text" id="unchanged01">
+<input type="text" id="unchanged02" value="preset value">
+
+<h3>Textarea with changed text</h3>
+<textarea id="modify03"></textarea>
+<textarea id="modify04">preset value</textarea>
+
+<h3>Textarea with unchanged text</h3>
+<textarea id="unchanged03"></textarea>
+<textarea id="unchanged04">preset value</textarea>
+
+<h3>file field with changed value</h3>
+<input type="file" id="modify05">
+
+<h3>file field with unchanged value</h3>
+<input type="file" id="unchanged05">
+
+<!-- change events -->
+
+<h3>Select menu with changed selection</h3>
+<select id="modify06">
+  <option value="one">one</option>
+  <option value="two">two</option>
+  <option value="three">three</option>
+</select>
+
+<h3>Select menu with unchanged selection (change event still fires)</h3>
+<select id="unchanged06">
+  <option value="one">one</option>
+  <option value="two" selected>two</option>
+  <option value="three">three</option>
+</select>
+
+<h3>Multiple Select menu with changed selection</h3>
+<select id="modify07" multiple>
+  <option value="one">one</option>
+  <option value="two" selected>two</option>
+  <option value="three">three</option>
+</select>
+
+<h3>Select menu with unchanged selection</h3>
+<select id="unchanged07" multiple>
+  <option value="one">one</option>
+  <option value="two" selected>two</option>
+  <option value="three" selected>three</option>
+</select>
+
+<h3>checkbox with changed value</h3>
+<input type="checkbox" id="modify08">
+<input type="checkbox" id="modify09" checked>
+
+<h3>checkbox with unchanged value</h3>
+<input type="checkbox" id="unchanged08">
+<input type="checkbox" id="unchanged09" checked>
+
+<h3>radio with changed value</h3>
+<input type="radio" id="modify10"  name="group">Radio 1</input>
+<input type="radio" id="modify11"  name="group">Radio 2</input>
+<input type="radio" id="modify12" name="group" checked>Radio 3</input>
+
+<h3>radio with unchanged value</h3>
+<input type="radio" id="unchanged10"  name="group2">Radio 4</input>
+<input type="radio" id="unchanged11"  name="group2">Radio 5</input>
+<input type="radio" id="unchanged12" name="group2" checked>Radio 6</input>
 
 <h3>Changed field IDs</h3>
 <div id="changed"></div>
+<div id="inputFired"></div>
+<div id="changeFired"></div>
--- a/browser/components/shell/src/nsWindowsShellService.cpp
+++ b/browser/components/shell/src/nsWindowsShellService.cpp
@@ -257,16 +257,19 @@ LaunchHelper(nsAutoString& aPath)
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindowsShellService::ShortcutMaintenance()
 {
   nsresult rv;
 
+  // XXX App ids were updated to a constant install path hash,
+  // XXX this code can be removed after a few upgrade cycles.
+
   // Launch helper.exe so it can update the application user model ids on
   // shortcuts in the user's taskbar and start menu. This keeps older pinned
   // shortcuts grouped correctly after major updates. Note, we also do this
   // through the upgrade installer script, however, this is the only place we
   // have a chance to trap links created by users who do control the install/
   // update process of the browser.
 
   nsCOMPtr<nsIWinTaskbar> taskbarInfo =
--- a/browser/config/mozconfigs/macosx32/debug
+++ b/browser/config/mozconfigs/macosx32/debug
@@ -1,12 +1,9 @@
-# Don't use the standard mozconfig. We don't want universal for a debug build. 
-#. $topsrcdir/build/macosx/universal/mozconfig
-
-ac_add_options --with-macos-sdk=/Developer/SDKs/MacOSX10.5.sdk
+. $topsrcdir/build/macosx/mozconfig.leopard
 ac_add_options --enable-debug
 ac_add_options --enable-trace-malloc
 
 # Enable parallel compiling
 mk_add_options MOZ_MAKE_FLAGS="-j4"
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
--- a/browser/config/version.txt
+++ b/browser/config/version.txt
@@ -1,1 +1,1 @@
-10.0a1
+11.0a1
--- a/browser/devtools/Makefile.in
+++ b/browser/devtools/Makefile.in
@@ -46,16 +46,13 @@ include $(DEPTH)/config/autoconf.mk
 
 include $(topsrcdir)/config/config.mk
 
 DIRS = \
   highlighter \
   webconsole \
   sourceeditor \
   styleinspector \
+  scratchpad \
   shared \
   $(NULL)
 
-ifdef ENABLE_TESTS
-DIRS += scratchpad/test
-endif
-
 include $(topsrcdir)/config/rules.mk
--- a/browser/devtools/highlighter/TreePanel.jsm
+++ b/browser/devtools/highlighter/TreePanel.jsm
@@ -222,17 +222,24 @@ TreePanel.prototype = {
   {
     let treeBox = null;
     let toolbar = this.IUI.toolbar.nextSibling; // Addons bar, typically
     let toolbarParent =
       this.IUI.browser.ownerDocument.getElementById("browser-bottombox");
     treeBox = this.document.createElement("vbox");
     treeBox.id = "inspector-tree-box";
     treeBox.state = "open"; // for the registerTools API.
-    treeBox.minHeight = 10;
+    try {
+      treeBox.height =
+        Services.prefs.getIntPref("devtools.inspector.htmlHeight");
+    } catch(e) {
+      treeBox.height = 112;
+    }
+                      
+    treeBox.minHeight = 64;
     treeBox.flex = 1;
     toolbarParent.insertBefore(treeBox, toolbar);
 
     this.IUI.toolbar.setAttribute("treepanel-open", "true");
 
     treeBox.appendChild(this.treeIFrame);
 
     let boundLoadedInitializeTreePanel = function loadedInitializeTreePanel()
@@ -257,16 +264,17 @@ TreePanel.prototype = {
    * Close the TreePanel.
    */
   close: function TP_close()
   {
     if (this.openInDock) {
       this.IUI.toolbar.removeAttribute("treepanel-open");
 
       let treeBox = this.container;
+      Services.prefs.setIntPref("devtools.inspector.htmlHeight", treeBox.height);
       let treeBoxParent = treeBox.parentNode;
       treeBoxParent.removeChild(treeBox);
     } else {
       this.container.hidePopup();
     }
 
     if (this.treePanelDiv) {
       this.treePanelDiv.ownerPanel = null;
@@ -453,16 +461,19 @@ TreePanel.prototype = {
     // position the editor
     editor.style.left = editorLeft + "px";
     editor.style.top = editorTop + "px";
 
     // set and select the text
     editorInput.value = aAttrVal;
     editorInput.select();
 
+    // remove tree key navigation events
+    this.treeIFrame.removeEventListener("keypress", this.IUI, false);
+
     // listen for editor specific events
     this.bindEditorEvent(editor, "click", function(aEvent) {
       aEvent.stopPropagation();
     });
     this.bindEditorEvent(editor, "dblclick", function(aEvent) {
       aEvent.stopPropagation();
     });
     this.bindEditorEvent(editor, "keypress",
@@ -510,18 +521,22 @@ TreePanel.prototype = {
    * Handle keypress events in the editor.
    * @param aEvent
    *        The keyboard event.
    */
   handleEditorKeypress: function TP_handleEditorKeypress(aEvent)
   {
     if (aEvent.which == this.window.KeyEvent.DOM_VK_RETURN) {
       this.saveEditor();
+      aEvent.preventDefault();
+      aEvent.stopPropagation();
     } else if (aEvent.keyCode == this.window.KeyEvent.DOM_VK_ESCAPE) {
       this.closeEditor();
+      aEvent.preventDefault();
+      aEvent.stopPropagation();
     }
   },
 
   /**
    * Close the editor and cleanup.
    */
   closeEditor: function TP_closeEditor()
   {
@@ -541,16 +556,19 @@ TreePanel.prototype = {
     this.unbindEditorEvent(editor, "keypress");
 
     // clean up after the editor
     editorInput.value = "";
     editorInput.blur();
     this.editingContext = null;
     this.editingEvents = {};
 
+    // re-add navigation listener
+    this.treeIFrame.addEventListener("keypress", this.IUI, false);
+
     // event notification
     Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED,
                                   null);
   },
 
   /**
    * Commit the edits made in the editor, then close it.
    */
@@ -562,16 +580,17 @@ TreePanel.prototype = {
     // set the new attribute value on the original target DOM element
     this.editingContext.repObj.setAttribute(this.editingContext.attrName,
                                               editorInput.value);
 
     // update the HTML tree attribute value
     this.editingContext.attrObj.innerHTML = editorInput.value;
 
     this.IUI.isDirty = true;
+    this.IUI.nodeChanged(this.registrationObject);
 
     // event notification
     Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.EDITOR_SAVED,
                                   null);
 
     this.closeEditor();
   },
 
--- a/browser/devtools/highlighter/inspector.jsm
+++ b/browser/devtools/highlighter/inspector.jsm
@@ -45,16 +45,17 @@ const Cu = Components.utils;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
 var EXPORTED_SYMBOLS = ["InspectorUI"];
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource:///modules/TreePanel.jsm");
+Cu.import("resource:///modules/devtools/CssRuleView.jsm");
 
 const INSPECTOR_INVISIBLE_ELEMENTS = {
   "head": true,
   "base": true,
   "basefont": true,
   "isindex": true,
   "link": true,
   "meta": true,
@@ -73,19 +74,25 @@ const INSPECTOR_NOTIFICATIONS = {
 
   // Fires once the Inspector completes the initialization and opens up on
   // screen.
   OPENED: "inspector-opened",
 
   // Fires once the Inspector is closed.
   CLOSED: "inspector-closed",
 
+  // Fires when the Inspector is reopened after tab-switch.
+  STATE_RESTORED: "inspector-state-restored",
+
   // Fires when the Tree Panel is opened and initialized.
   TREEPANELREADY: "inspector-treepanel-ready",
 
+  // Fires when the CSS Rule View is opened and initialized.
+  RULEVIEWREADY: "inspector-ruleview-ready",
+
   // Event notifications for the attribute-value editor
   EDITOR_OPENED: "inspector-editor-opened",
   EDITOR_CLOSED: "inspector-editor-closed",
   EDITOR_SAVED: "inspector-editor-saved",
 };
 
 ///////////////////////////////////////////////////////////////////////////
 //// Highlighter
@@ -735,16 +742,17 @@ function InspectorUI(aWindow)
 }
 
 InspectorUI.prototype = {
   browser: null,
   tools: null,
   toolEvents: null,
   inspecting: false,
   treePanelEnabled: true,
+  ruleViewEnabled: true,
   isDirty: false,
   store: null,
 
   /**
    * Toggle the inspector interface elements on or off.
    *
    * @param aEvent
    *        The event that requested the UI change. Toolbar button or menu.
@@ -754,16 +762,67 @@ InspectorUI.prototype = {
     if (this.isInspectorOpen) {
       this.closeInspectorUI();
     } else {
       this.openInspectorUI();
     }
   },
 
   /**
+   * Show the Sidebar.
+   */
+  showSidebar: function IUI_showSidebar()
+  {
+    this.sidebarBox.removeAttribute("hidden");
+    this.sidebarSplitter.removeAttribute("hidden");
+    this.stylingButton.checked = true;
+
+    // Activate the first tool in the sidebar, only if none previously-
+    // selected. We'll want to do a followup to remember selected tool-states.
+    if (!Array.some(this.sidebarToolbar.children,
+      function(btn) btn.hasAttribute("checked"))) {
+        let firstButtonId = this.getToolbarButtonId(this.sidebarTools[0].id);
+        this.chromeDoc.getElementById(firstButtonId).click();
+    }
+  },
+
+  /**
+   * Hide the Sidebar.
+   */
+  hideSidebar: function IUI_hideSidebar()
+  {
+    this.sidebarBox.setAttribute("hidden", "true");
+    this.sidebarSplitter.setAttribute("hidden", "true");
+    this.stylingButton.checked = false;
+  },
+
+  /**
+   * Show or hide the sidebar. Called from the Styling button on the
+   * highlighter toolbar.
+   */
+  toggleSidebar: function IUI_toggleSidebar()
+  {
+    if (!this.isSidebarOpen) {
+      this.showSidebar();
+    } else {
+      this.hideSidebar();
+    }
+  },
+
+  /**
+   * Getter to test if the Sidebar is open or not.
+   */
+  get isSidebarOpen()
+  {
+    return this.stylingButton.checked &&
+          !this.sidebarBox.hidden &&
+          !this.sidebarSplitter.hidden;
+  },
+
+  /**
    * Toggle the status of the inspector, starting or stopping it. Invoked
    * from the toolbar's Inspect button.
    */
   toggleInspection: function IUI_toggleInspection()
   {
     if (this.inspecting) {
       this.stopInspecting();
     } else {
@@ -835,16 +894,21 @@ InspectorUI.prototype = {
       this.chromeDoc.getElementById("inspector-inspect-toolbutton");
 
     this.initTools();
 
     if (this.treePanelEnabled) {
       this.treePanel = new TreePanel(this.chromeWin, this);
     }
 
+    if (Services.prefs.getBoolPref("devtools.ruleview.enabled") &&
+        !this.toolRegistered("ruleview")) {
+      this.registerRuleView();
+    }
+
     if (Services.prefs.getBoolPref("devtools.styleinspector.enabled") &&
         !this.toolRegistered("styleinspector")) {
       this.stylePanel = new StyleInspector(this.chromeWin, this);
     }
 
     this.toolbar.hidden = false;
     this.inspectMenuitem.setAttribute("checked", true);
 
@@ -855,16 +919,41 @@ InspectorUI.prototype = {
 
     this.progressListener = new InspectorProgressListener(this);
 
     // initialize the highlighter
     this.initializeHighlighter();
   },
 
   /**
+   * Register the Rule View in the Sidebar.
+   */
+  registerRuleView: function IUI_registerRuleView()
+  {
+    let isOpen = this.isRuleViewOpen.bind(this);
+
+    this.ruleViewObject = {
+      id: "ruleview",
+      label: this.strings.GetStringFromName("ruleView.label"),
+      tooltiptext: this.strings.GetStringFromName("ruleView.tooltiptext"),
+      accesskey: this.strings.GetStringFromName("ruleView.accesskey"),
+      context: this,
+      get isOpen() isOpen(),
+      show: this.openRuleView,
+      hide: this.closeRuleView,
+      onSelect: this.selectInRuleView,
+      panel: null,
+      unregister: this.destroyRuleView,
+      sidebar: true,
+    };
+
+    this.registerTool(this.ruleViewObject);
+  },
+
+  /**
    * Register and initialize any included tools.
    */
   initTools: function IUI_initTools()
   {
     // Extras go here.
   },
 
   /**
@@ -950,16 +1039,19 @@ InspectorUI.prototype = {
     this.stopInspecting();
     this.browser.removeEventListener("keypress", this, true);
 
     this.saveToolState(this.winID);
     this.toolsDo(function IUI_toolsHide(aTool) {
       this.unregisterTool(aTool);
     }.bind(this));
 
+    // close the sidebar
+    this.hideSidebar();
+
     if (this.highlighter) {
       this.highlighter.highlighterContainer.removeEventListener("keypress",
                                                                 this,
                                                                 true);
       this.highlighter.destroy();
       this.highlighter = null;
     }
 
@@ -1061,16 +1153,29 @@ InspectorUI.prototype = {
       }
     }
 
     this.breadcrumbs.update();
 
     this.toolsSelect(aScroll);
   },
 
+  /**
+   * Called when the highlighted node is changed by a tool.
+   *
+   * @param object aUpdater
+   *        The tool that triggered the update (if any), that tool's
+   *        onChanged will not be called.
+   */
+  nodeChanged: function IUI_nodeChanged(aUpdater)
+  {
+    this.highlighter.highlight();
+    this.toolsOnChanged(aUpdater);
+  },
+
   /////////////////////////////////////////////////////////////////////////
   //// Event Handling
 
   highlighterReady: function IUI_highlighterReady()
   {
     // Setup the InspectorStore or restore state
     this.initializeStore();
 
@@ -1213,16 +1318,118 @@ InspectorUI.prototype = {
             event.stopPropagation();
             break;
         }
         break;
     }
   },
 
   /////////////////////////////////////////////////////////////////////////
+  //// CssRuleView methods
+
+  /**
+   * Is the cssRuleView open?
+   */
+  isRuleViewOpen: function IUI_isRuleViewOpen()
+  {
+    return this.isSidebarOpen && this.ruleButton.hasAttribute("checked") &&
+      (this.sidebarDeck.selectedPanel == this.getToolIframe(this.ruleViewObject));
+  },
+
+  /**
+   * Convenience getter to retrieve the Rule Button.
+   */
+  get ruleButton()
+  {
+    return this.chromeDoc.getElementById(
+      this.getToolbarButtonId(this.ruleViewObject.id));
+  },
+
+  /**
+   * Open the CssRuleView.
+   */
+  openRuleView: function IUI_openRuleView()
+  {
+    let iframe = this.getToolIframe(this.ruleViewObject);
+    if (iframe.getAttribute("src")) {
+      // We're already loading this tool, let it finish.
+      return;
+    }
+
+    let boundLoadListener = function() {
+      iframe.removeEventListener("load", boundLoadListener, true);
+      let doc = iframe.contentDocument;
+
+      let winID = this.winID;
+      let ruleViewStore = this.store.getValue(winID, "ruleView");
+      if (!ruleViewStore) {
+        ruleViewStore = {};
+        this.store.setValue(winID, "ruleView", ruleViewStore);
+      }
+
+      this.ruleView = new CssRuleView(doc, ruleViewStore);
+
+      this.boundRuleViewChanged = this.ruleViewChanged.bind(this);
+      this.ruleView.element.addEventListener("CssRuleViewChanged",
+                                             this.boundRuleViewChanged);
+
+      doc.documentElement.appendChild(this.ruleView.element);
+      this.ruleView.highlight(this.selection);
+      Services.obs.notifyObservers(null,
+        INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, null);
+    }.bind(this);
+
+    iframe.addEventListener("load", boundLoadListener, true);
+
+    iframe.setAttribute("src", "chrome://browser/content/devtools/cssruleview.xul");
+  },
+
+  /**
+   * Stub to Close the CSS Rule View. Does nothing currently because the
+   * Rule View lives in the sidebar.
+   */
+  closeRuleView: function IUI_closeRuleView()
+  {
+    // do nothing for now
+  },
+
+  /**
+   * Update the selected node in the Css Rule View.
+   * @param {nsIDOMnode} the selected node.
+   */
+  selectInRuleView: function IUI_selectInRuleView(aNode)
+  {
+    if (this.ruleView)
+      this.ruleView.highlight(aNode);
+  },
+
+  ruleViewChanged: function IUI_ruleViewChanged()
+  {
+    this.isDirty = true;
+    this.nodeChanged(this.ruleViewObject);
+  },
+
+  /**
+   * Destroy the rule view.
+   */
+  destroyRuleView: function IUI_destroyRuleView()
+  {
+    let iframe = this.getToolIframe(this.ruleViewObject);
+    iframe.parentNode.removeChild(iframe);
+
+    if (this.ruleView) {
+      this.ruleView.element.removeEventListener("CssRuleViewChanged",
+                                                this.boundRuleViewChanged);
+      delete boundRuleViewChanged;
+      this.ruleView.clear();
+      delete this.ruleView;
+    }
+  },
+
+  /////////////////////////////////////////////////////////////////////////
   //// Utility Methods
 
   /**
    * inspect the given node, highlighting it on the page and selecting the
    * correct row in the tree panel
    *
    * @param aNode
    *        the element in the document to inspect
@@ -1362,135 +1569,257 @@ InspectorUI.prototype = {
    * @returns String
    */
   getToolbarButtonId: function IUI_createButtonId(anId)
   {
     return "inspector-" + anId + "-toolbutton";
   },
 
   /**
+   * Save a registered tool's callback for a specified event.
+   * @param aWidget xul:widget
+   * @param aEvent a DOM event name
+   * @param aCallback Function the click event handler for the button
+   */
+  bindToolEvent: function IUI_bindToolEvent(aWidget, aEvent, aCallback)
+  {
+    this.toolEvents[aWidget.id + "_" + aEvent] = aCallback;
+    aWidget.addEventListener(aEvent, aCallback, false);
+  },
+
+  /**
    * Register an external tool with the inspector.
    *
    * aRegObj = {
    *   id: "toolname",
    *   context: myTool,
-   *   label: "Button label",
+   *   label: "Button or tab label",
    *   icon: "chrome://somepath.png",
    *   tooltiptext: "Button tooltip",
    *   accesskey: "S",
    *   isOpen: object.property, (getter) returning true if tool is open.
    *   onSelect: object.method,
    *   show: object.method, called to show the tool when button is pressed.
    *   hide: object.method, called to hide the tool when button is pressed.
    *   dim: object.method, called to disable a tool during highlighting.
    *   unregister: object.method, called when tool should be destroyed.
-   *   panel: myTool.panel
+   *   panel: myTool.panel, set if tool is in a separate panel, null otherwise.
+   *   sidebar: boolean, true if tool lives in sidebar tab.
    * }
    *
    * @param aRegObj Object
    *        The Registration Object used to register this tool described
    *        above. The tool should cache this object for later deregistration.
    */
   registerTool: function IUI_registerTool(aRegObj)
   {
     if (this.toolRegistered(aRegObj.id)) {
       return;
     }
 
     this.tools[aRegObj.id] = aRegObj;
 
     let buttonContainer = this.chromeDoc.getElementById("inspector-tools");
-    let btn = this.chromeDoc.createElement("toolbarbutton");
+    let btn;
+
+    // if this is a sidebar tool, create the sidebar features for it and bail.
+    if (aRegObj.sidebar) {
+      this.createSidebarTool(aRegObj);
+      return;
+    }
+
+    btn = this.chromeDoc.createElement("toolbarbutton");
     let buttonId = this.getToolbarButtonId(aRegObj.id);
     btn.setAttribute("id", buttonId);
     btn.setAttribute("label", aRegObj.label);
     btn.setAttribute("tooltiptext", aRegObj.tooltiptext);
     btn.setAttribute("accesskey", aRegObj.accesskey);
     btn.setAttribute("image", aRegObj.icon || "");
-    buttonContainer.appendChild(btn);
+    buttonContainer.insertBefore(btn, this.stylingButton);
 
-    /**
-     * Save a registered tool's callback for a specified event.
-     * @param aWidget xul:widget
-     * @param aEvent a DOM event name
-     * @param aCallback Function the click event handler for the button
-     */
-    let toolEvents = this.toolEvents;
-    function bindToolEvent(aWidget, aEvent, aCallback) {
-      toolEvents[aWidget.id + "_" + aEvent] = aCallback;
-      aWidget.addEventListener(aEvent, aCallback, false);
-    }
-
-    bindToolEvent(btn, "click",
+    this.bindToolEvent(btn, "click",
       function IUI_toolButtonClick(aEvent) {
         if (btn.checked) {
           this.toolHide(aRegObj);
         } else {
           this.toolShow(aRegObj);
         }
       }.bind(this));
 
+    // if the tool has a panel, register the popuphiding event
     if (aRegObj.panel) {
-      bindToolEvent(aRegObj.panel, "popuphiding",
+      this.bindToolEvent(aRegObj.panel, "popuphiding",
         function IUI_toolPanelHiding() {
           btn.checked = false;
         });
     }
   },
 
+  get sidebarBox()
+  {
+    return this.chromeDoc.getElementById("devtools-sidebar-box");
+  },
+
+  get sidebarToolbar()
+  {
+    return this.chromeDoc.getElementById("devtools-sidebar-toolbar");
+  },
+
+  get sidebarDeck()
+  {
+    return this.chromeDoc.getElementById("devtools-sidebar-deck");
+  },
+
+  get sidebarSplitter()
+  {
+    return this.chromeDoc.getElementById("devtools-side-splitter");
+  },
+
+  get stylingButton()
+  {
+    return this.chromeDoc.getElementById("inspector-style-button");
+  },
+
+  /**
+   * Creates a tab and tabpanel for our tool to reside in.
+   * @param {Object} aRegObj the Registration Object for our tool.
+   */
+  createSidebarTool: function IUI_createSidebarTab(aRegObj)
+  {
+    // toolbutton elements
+    let btn = this.chromeDoc.createElement("toolbarbutton");
+    let buttonId = this.getToolbarButtonId(aRegObj.id);
+
+    btn.id = buttonId;
+    btn.setAttribute("label", aRegObj.label);
+    btn.setAttribute("tooltiptext", aRegObj.tooltiptext);
+    btn.setAttribute("accesskey", aRegObj.accesskey);
+    btn.setAttribute("image", aRegObj.icon || "");
+    btn.setAttribute("type", "radio");
+    btn.setAttribute("group", "sidebar-tools");
+    this.sidebarToolbar.appendChild(btn);
+
+    // create tool iframe
+    let iframe = this.chromeDoc.createElement("iframe");
+    iframe.id = "devtools-sidebar-iframe-" + aRegObj.id;
+    iframe.setAttribute("flex", "1");
+    this.sidebarDeck.appendChild(iframe);
+
+    // wire up button to show the iframe
+    this.bindToolEvent(btn, "click", function showIframe() {
+      this.toolShow(aRegObj);
+    }.bind(this));
+  },
+
+  /**
+   * Return the registered object's iframe.
+   * @param aRegObj see registerTool function.
+   * @return iframe or null
+   */
+  getToolIframe: function IUI_getToolIFrame(aRegObj)
+  {
+    return this.chromeDoc.getElementById("devtools-sidebar-iframe-" + aRegObj.id);
+  },
+
   /**
    * Show the specified tool.
    * @param aTool Object (see comment for IUI_registerTool)
    */
   toolShow: function IUI_toolShow(aTool)
   {
+    let btn = this.chromeDoc.getElementById(this.getToolbarButtonId(aTool.id));
+    btn.setAttribute("checked", "true");
+    if (aTool.sidebar) {
+      this.sidebarDeck.selectedPanel = this.getToolIframe(aTool);
+      this.sidebarTools.forEach(function(other) {
+        if (other != aTool)
+          this.chromeDoc.getElementById(
+            this.getToolbarButtonId(other.id)).removeAttribute("checked");
+      }.bind(this));
+    }
+
     aTool.show.call(aTool.context, this.selection);
-    this.chromeDoc.getElementById(this.getToolbarButtonId(aTool.id)).checked = true;
   },
 
   /**
    * Hide the specified tool.
    * @param aTool Object (see comment for IUI_registerTool)
    */
   toolHide: function IUI_toolHide(aTool)
   {
     aTool.hide.call(aTool.context);
-    this.chromeDoc.getElementById(this.getToolbarButtonId(aTool.id)).checked = false;
+
+    let btn = this.chromeDoc.getElementById(this.getToolbarButtonId(aTool.id));
+    btn.removeAttribute("checked");
+  },
+
+  /**
+   * Unregister the events associated with the registered tool's widget.
+   * @param aWidget XUL:widget (toolbarbutton|panel).
+   * @param aEvent a DOM event.
+   */
+  unbindToolEvent: function IUI_unbindToolEvent(aWidget, aEvent)
+  {
+    let toolEvent = aWidget.id + "_" + aEvent;
+    aWidget.removeEventListener(aEvent, this.toolEvents[toolEvent], false);
+    delete this.toolEvents[toolEvent]
   },
 
   /**
    * Unregister the registered tool, unbinding click events for the buttons
    * and showing and hiding events for the panel.
    * @param aRegObj Object
    *        The registration object used to register the tool.
    */
   unregisterTool: function IUI_unregisterTool(aRegObj)
   {
+    // if this is a sidebar tool, use the sidebar unregistration method
+    if (aRegObj.sidebar) {
+      this.unregisterSidebarTool(aRegObj);
+      return;
+    }
+
     let button = this.chromeDoc.getElementById(this.getToolbarButtonId(aRegObj.id));
+    let buttonContainer = this.chromeDoc.getElementById("inspector-tools");
 
-    /**
-     * Unregister the events associated with the registered tool's widget.
-     * @param aWidget XUL:widget (toolbarbutton|panel).
-     * @param aEvent a DOM event.
-     */
-    let toolEvents = this.toolEvents;
-    function unbindToolEvent(aWidget, aEvent) {
-      let toolEvent = aWidget.id + "_" + aEvent;
-      aWidget.removeEventListener(aEvent, toolEvents[toolEvent], false);
-      delete toolEvents[toolEvent]
-    };
+    // unbind click events on button
+    this.unbindToolEvent(button, "click");
 
-    let buttonContainer = this.chromeDoc.getElementById("inspector-tools");
-    unbindToolEvent(button, "click");
+    // unbind panel popuphiding events if present.
+    if (aRegObj.panel)
+      this.unbindToolEvent(aRegObj.panel, "popuphiding");
 
-    if (aRegObj.panel)
-      unbindToolEvent(aRegObj.panel, "popuphiding");
-
+    // remove the button from its container
     buttonContainer.removeChild(button);
 
+    // call unregister callback and remove from collection
+    if (aRegObj.unregister)
+      aRegObj.unregister.call(aRegObj.context);
+
+    delete this.tools[aRegObj.id];
+  },
+
+  /**
+   * Unregister the registered sidebar tool, unbinding click events for the
+   * button.
+   * @param aRegObj Object
+   *        The registration object used to register the tool.
+   */
+  unregisterSidebarTool: function IUI_unregisterSidebarTool(aRegObj)
+  {
+    // unbind tool button click event
+    let buttonId = this.getToolbarButtonId(aRegObj.id);
+    let btn = this.chromeDoc.getElementById(buttonId);
+    this.unbindToolEvent(btn, "click");
+
+    // remove sidebar buttons and tools
+    this.sidebarToolbar.removeChild(btn);
+
+    // call unregister callback and remove from collection, this also removes
+    // the iframe.
     if (aRegObj.unregister)
       aRegObj.unregister.call(aRegObj.context);
 
     delete this.tools[aRegObj.id];
   },
 
   /**
    * Save a list of open tools to the inspector store.
@@ -1512,23 +1841,34 @@ InspectorUI.prototype = {
    * Restore tools previously save using saveToolState().
    *
    * @param aWinID The ID of the window to which the associated tools are to be
    *               restored.
    */
   restoreToolState: function IUI_restoreToolState(aWinID)
   {
     let openTools = this.store.getValue(aWinID, "openTools");
+    let activeSidebarTool;
     if (openTools) {
       this.toolsDo(function IUI_toolsOnShow(aTool) {
         if (aTool.id in openTools) {
+          if (aTool.sidebar && !this.isSidebarOpen) {
+            this.showSidebar();
+            activeSidebarTool = aTool;
+          }
           this.toolShow(aTool);
         }
       }.bind(this));
+      this.sidebarTools.forEach(function(tool) {
+        if (tool != activeSidebarTool)
+          this.chromeDoc.getElementById(
+            this.getToolbarButtonId(tool.id)).removeAttribute("checked");
+      }.bind(this));
     }
+    Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.STATE_RESTORED, null);
   },
 
   /**
    * For each tool in the tools collection select the current node that is
    * selected in the highlighter
    * @param aScroll boolean
    *        Do you want to scroll the treepanel?
    */
@@ -1543,35 +1883,63 @@ InspectorUI.prototype = {
   },
 
   /**
    * Dim or undim each tool in the tools collection
    * @param aState true = dim, false = undim
    */
   toolsDim: function IUI_toolsDim(aState)
   {
-    this.toolsDo(function IUI_toolsOnSelect(aTool) {
+    this.toolsDo(function IUI_toolsDim(aTool) {
       if (aTool.isOpen && "dim" in aTool) {
         aTool.dim.call(aTool.context, aState);
       }
     });
   },
 
   /**
+   * Notify registered tools of changes to the highlighted element.
+   *
+   * @param object aUpdater
+   *        The tool that triggered the update (if any), that tool's
+   *        onChanged will not be called.
+   */
+  toolsOnChanged: function IUI_toolsChanged(aUpdater)
+  {
+    this.toolsDo(function IUI_toolsOnChanged(aTool) {
+      if (aTool.isOpen && ("onChanged" in aTool) && aTool != aUpdater) {
+        aTool.onChanged.call(aTool.context);
+      }
+    });
+  },
+
+  /**
    * Loop through all registered tools and pass each into the provided function
    * @param aFunction The function to which each tool is to be passed
    */
   toolsDo: function IUI_toolsDo(aFunction)
   {
     for each (let tool in this.tools) {
       aFunction(tool);
     }
   },
 
   /**
+   * Convenience getter to retrieve only the sidebar tools.
+   */
+  get sidebarTools()
+  {
+    let sidebarTools = [];
+    for each (let tool in this.tools)
+      if (tool.sidebar)
+        sidebarTools.push(tool);
+    return sidebarTools;
+  },
+
+  /**
    * Check if a tool is registered?
    * @param aId The id of the tool to check
    */
   toolRegistered: function IUI_toolRegistered(aId)
   {
     return aId in this.tools;
   },
 
@@ -1745,18 +2113,23 @@ InspectorProgressListener.prototype = {
   function IPL_onStateChange(aProgress, aRequest, aFlag, aStatus)
   {
     // Remove myself if the Inspector is no longer open.
     if (!this.IUI.isInspectorOpen) {
       this.destroy();
       return;
     }
 
-    // Skip non-start states.
-    if (!(aFlag & Ci.nsIWebProgressListener.STATE_START)) {
+    let isStart = aFlag & Ci.nsIWebProgressListener.STATE_START;
+    let isDocument = aFlag & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
+    let isNetwork = aFlag & Ci.nsIWebProgressListener.STATE_IS_NETWORK;
+    let isRequest = aFlag & Ci.nsIWebProgressListener.STATE_IS_REQUEST;
+
+    // Skip non-interesting states.
+    if (!isStart || !isDocument || !isRequest || !isNetwork) {
       return;
     }
 
     // If the request is about to happen in a new window, we are not concerned
     // about the request.
     if (aProgress.DOMWindow != this.IUI.win) {
       return;
     }
--- a/browser/devtools/highlighter/test/Makefile.in
+++ b/browser/devtools/highlighter/test/Makefile.in
@@ -60,15 +60,19 @@ include $(topsrcdir)/config/rules.mk
 		browser_inspector_editor.js \
 		browser_inspector_bug_566084_location_changed.js \
 		browser_inspector_infobar.js \
 		browser_inspector_bug_690361.js \
 		browser_inspector_bug_672902_keyboard_shortcuts.js \
 		browser_inspector_keybindings.js \
 		browser_inspector_breadcrumbs.html \
 		browser_inspector_breadcrumbs.js \
+		browser_inspector_bug_699308_iframe_navigation.js \
+        browser_inspector_changes.js \
+        browser_inspector_ruleviewstore.js \
+        browser_inspector_duplicate_ruleview.js \
 		$(NULL)
 
 # Disabled due to constant failures
 # 		browser_inspector_treePanel_click.js \
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/highlighter/test/browser_inspector_bug_699308_iframe_navigation.js
@@ -0,0 +1,77 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let iframe;
+let iframeLoads = 0;
+let checksAfterLoads = false;
+
+function startTest() {
+  ok(window.InspectorUI, "InspectorUI variable exists");
+  Services.obs.addObserver(runInspectorTests,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, null);
+  InspectorUI.toggleInspectorUI();
+}
+
+function runInspectorTests() {
+  Services.obs.removeObserver(runInspectorTests,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, null);
+
+  iframe = content.document.querySelector("iframe");
+  ok(iframe, "found the iframe element");
+
+  ok(InspectorUI.inspecting, "Inspector is highlighting");
+  ok(InspectorUI.isInspectorOpen, "Inspector is open");
+
+  Services.obs.addObserver(finishTest,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
+
+  iframe.addEventListener("load", onIframeLoad, false);
+
+  executeSoon(function() {
+    iframe.contentWindow.location = "javascript:location.reload()";
+  });
+}
+
+function onIframeLoad() {
+  if (++iframeLoads != 2) {
+    executeSoon(function() {
+      iframe.contentWindow.location = "javascript:location.reload()";
+    });
+    return;
+  }
+
+  iframe.removeEventListener("load", onIframeLoad, false);
+
+  ok(InspectorUI.inspecting, "Inspector is highlighting after iframe nav");
+  ok(InspectorUI.isInspectorOpen, "Inspector Panel is open after iframe nav");
+
+  checksAfterLoads = true;
+
+  InspectorUI.closeInspectorUI();
+}
+
+function finishTest() {
+  Services.obs.removeObserver(finishTest,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED);
+
+  is(iframeLoads, 2, "iframe loads");
+  ok(checksAfterLoads, "the Inspector tests got the chance to run after iframe reloads");
+  ok(!InspectorUI.isInspectorOpen, "Inspector Panel is not open");
+
+  iframe = null;
+  gBrowser.removeCurrentTab();
+  executeSoon(finish);
+}
+
+function test() {
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onBrowserLoad() {
+    gBrowser.selectedBrowser.removeEventListener("load", onBrowserLoad, true);
+    waitForFocus(startTest, content);
+  }, true);
+
+  content.location = "data:text/html,<p>bug 699308 - test iframe navigation" +
+    "<iframe src='data:text/html,hello world'></iframe>";
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/highlighter/test/browser_inspector_changes.js
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Inspect Tests.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Dave Camp <dcamp@mozilla.com>
+ *   Rob Campbell <rcampbell@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+let doc;
+let testDiv;
+
+function createDocument()
+{
+  doc.body.innerHTML = '<div id="testdiv">Test div!</div>';
+  doc.title = "Inspector Change Test";
+  startInspectorTests();
+}
+
+
+function getInspectorProp(aName)
+{
+  for each (let view in InspectorUI.stylePanel.cssHtmlTree.propertyViews) {
+    if (view.name == aName) {
+      return view;
+    }
+  }
+  return null;
+}
+
+function startInspectorTests()
+{
+  ok(InspectorUI, "InspectorUI variable exists");
+  Services.obs.addObserver(runInspectorTests,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+  InspectorUI.toggleInspectorUI();
+}
+
+function runInspectorTests()
+{
+  Services.obs.removeObserver(runInspectorTests, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
+  testDiv = doc.getElementById("testdiv");
+
+  testDiv.style.fontSize = "10px";
+
+  InspectorUI.inspectNode(testDiv);
+  InspectorUI.stopInspecting();
+
+  // Start up the style inspector panel...
+  Services.obs.addObserver(stylePanelTests, "StyleInspector-populated", false);
+
+  executeSoon(function() {
+    InspectorUI.showSidebar();
+    document.getElementById(InspectorUI.getToolbarButtonId("styleinspector")).click();
+  });
+}
+
+function stylePanelTests()
+{
+  Services.obs.removeObserver(stylePanelTests, "StyleInspector-populated");
+
+  ok(InspectorUI.isSidebarOpen, "Inspector Sidebar is open");
+  ok(InspectorUI.stylePanel.cssHtmlTree, "Style Panel has a cssHtmlTree");
+
+  let propView = getInspectorProp("font-size");
+  is(propView.value, "10px", "Style inspector should be showing the correct font size.");
+
+  Services.obs.addObserver(stylePanelAfterChange, "StyleInspector-populated", false);
+
+  testDiv.style.fontSize = "15px";
+  InspectorUI.nodeChanged();
+}
+
+function stylePanelAfterChange()
+{
+  Services.obs.removeObserver(stylePanelAfterChange, "StyleInspector-populated");
+
+  let propView = getInspectorProp("font-size");
+  is(propView.value, "15px", "Style inspector should be showing the new font size.");
+
+  stylePanelNotActive();
+}
+
+function stylePanelNotActive()
+{
+  // Tests changes made while the style panel is not active.
+  InspectorUI.ruleButton.click();
+  executeSoon(function() {
+    testDiv.style.fontSize = "20px";
+    Services.obs.addObserver(stylePanelAfterSwitch, "StyleInspector-populated", false);
+    document.getElementById(InspectorUI.getToolbarButtonId("styleinspector")).click();
+  });
+}
+
+function stylePanelAfterSwitch()
+{
+  Services.obs.removeObserver(stylePanelAfterSwitch, "StyleInspector-populated");
+
+  let propView = getInspectorProp("font-size");
+  is(propView.value, "20px", "Style inspector should be showing the newest font size.");
+
+  Services.obs.addObserver(finishTest, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
+  executeSoon(function() {
+    InspectorUI.closeInspectorUI(true);
+  });
+}
+
+function finishTest()
+{
+  Services.obs.removeObserver(finishTest,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED);
+  gBrowser.removeCurrentTab();
+  finish();
+}
+
+function test()
+{
+  waitForExplicitFinish();
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function() {
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+    doc = content.document;
+    waitForFocus(createDocument, content);
+  }, true);
+
+  content.location = "data:text/html,basic tests for inspector";
+}
+
new file mode 100644
--- /dev/null
+++ b/browser/devtools/highlighter/test/browser_inspector_duplicate_ruleview.js
@@ -0,0 +1,126 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+
+let div;
+let tab1;
+let tab2;
+let tab1window;
+
+function inspectorTabOpen1()
+{
+  ok(window.InspectorUI, "InspectorUI variable exists");
+  ok(!InspectorUI.inspecting, "Inspector is not highlighting");
+  ok(InspectorUI.store.isEmpty(), "Inspector.store is empty");
+
+  Services.obs.addObserver(inspectorUIOpen1,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+  InspectorUI.openInspectorUI();
+}
+
+function inspectorUIOpen1()
+{
+  Services.obs.removeObserver(inspectorUIOpen1,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+
+  // Make sure the inspector is open.
+  ok(InspectorUI.inspecting, "Inspector is highlighting");
+  ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
+  ok(!InspectorUI.isSidebarOpen, "Inspector Sidebar is not open");
+  ok(!InspectorUI.store.isEmpty(), "InspectorUI.store is not empty");
+  is(InspectorUI.store.length, 1, "Inspector.store.length = 1");
+
+  // Highlight a node.
+  div = content.document.getElementsByTagName("div")[0];
+  InspectorUI.inspectNode(div);
+  is(InspectorUI.selection, div, "selection matches the div element");
+
+  Services.obs.addObserver(inspectorRuleViewOpened,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false);
+
+  InspectorUI.showSidebar();
+  InspectorUI.openRuleView();
+}
+
+function inspectorRuleViewOpened() {
+  Services.obs.removeObserver(inspectorRuleViewOpened,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY);
+
+  // Open the second tab.
+  tab2 = gBrowser.addTab();
+  gBrowser.selectedTab = tab2;
+
+  gBrowser.selectedBrowser.addEventListener("load", function(evt) {
+    gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee,
+      true);
+    waitForFocus(inspectorTabOpen2, content);
+  }, true);
+
+  content.location = "data:text/html,<p>tab 2: the inspector should close now";
+}
+
+function inspectorTabOpen2()
+{
+  // Make sure the inspector is closed.
+  ok(!InspectorUI.inspecting, "Inspector is not highlighting");
+  ok(!InspectorUI.treePanel, "Inspector Tree Panel is closed");
+  ok(!InspectorUI.isSidebarOpen, "Inspector Sidebar is not open");
+  is(InspectorUI.store.length, 1, "Inspector.store.length = 1");
+
+  Services.obs.addObserver(inspectorFocusTab1,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false);
+  // Switch back to tab 1.
+  executeSoon(function() {
+    gBrowser.selectedTab = tab1;
+  });
+}
+
+function inspectorFocusTab1()
+{
+  Services.obs.removeObserver(inspectorFocusTab1,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false);
+  Services.obs.addObserver(inspectorRuleTrap,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false);
+
+  // Make sure the inspector is open.
+  ok(InspectorUI.inspecting, "Inspector is highlighting");
+  ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
+  is(InspectorUI.store.length, 1, "Inspector.store.length = 1");
+  is(InspectorUI.selection, div, "selection matches the div element");
+  ok(InspectorUI.isSidebarOpen, "sidebar is open");
+  ok(InspectorUI.isRuleViewOpen(), "rule view is open");
+  is(InspectorUI.ruleView.doc.documentElement.children.length, 1, "RuleView elements.length == 1");
+
+  requestLongerTimeout(4);
+  executeSoon(function() {
+    InspectorUI.closeInspectorUI();
+    gBrowser.removeCurrentTab(); // tab 1
+    gBrowser.removeCurrentTab(); // tab 2
+    finish();
+  });
+}
+
+function inspectorRuleTrap()
+{
+  Services.obs.removeObserver(inspectorRuleTrap,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false);
+  is(InspectorUI.ruleView.doc.documentElement.children.length, 1, "RuleView elements.length == 1");
+}
+
+function test()
+{
+  waitForExplicitFinish();
+
+  tab1 = gBrowser.addTab();
+  gBrowser.selectedTab = tab1;
+  gBrowser.selectedBrowser.addEventListener("load", function(evt) {
+    gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee,
+      true);
+    waitForFocus(inspectorTabOpen1, content);
+  }, true);
+
+  content.location = "data:text/html,<p>tab switching tests for inspector" +
+    "<div>tab 1</div>";
+}
+
--- a/browser/devtools/highlighter/test/browser_inspector_editor.js
+++ b/browser/devtools/highlighter/test/browser_inspector_editor.js
@@ -29,36 +29,45 @@ function setupEditorTests()
   Services.obs.addObserver(setupHTMLPanel, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
   InspectorUI.toggleInspectorUI();
 }
 
 function setupHTMLPanel()
 {
   Services.obs.removeObserver(setupHTMLPanel, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
   Services.obs.addObserver(runEditorTests, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
-  InspectorUI.treePanel.open();
+  InspectorUI.toolShow(InspectorUI.treePanel.registrationObject);
 }
 
 function runEditorTests()
 {
   Services.obs.removeObserver(runEditorTests, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
   InspectorUI.stopInspecting();
+  InspectorUI.inspectNode(doc.body, true);
 
   // setup generator for async test steps
   editorTestSteps = doEditorTestSteps();
 
   // add step listeners
   Services.obs.addObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, false);
   Services.obs.addObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, false);
   Services.obs.addObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, false);
 
   // start the tests
   doNextStep();
 }
 
+function highlighterTrap()
+{
+  // bug 696107
+  Services.obs.removeObserver(highlighterTrap, InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+  ok(false, "Highlighter moved. Shouldn't be here!");
+  finishUp();
+}
+
 function doEditorTestSteps()
 {
   let treePanel = InspectorUI.treePanel;
   let editor = treePanel.treeBrowserDocument.getElementById("attribute-editor");
   let editorInput = treePanel.treeBrowserDocument.getElementById("attribute-editor-input");
 
   // Step 1: grab and test the attribute-value nodes in the HTML panel, then open editor
   let attrValNode_id = treePanel.treeBrowserDocument.querySelectorAll(".nodeValue.editable[data-attributeName='id']")[0];
@@ -73,16 +82,19 @@ function doEditorTestSteps()
     EventUtils.synthesizeMouse(attrValNode_id, 2, 2, {clickCount: 2}, attrValNode_id.ownerDocument.defaultView);
   });
 
   yield; // End of Step 1
 
 
   // Step 2: validate editing session, enter new attribute value into editor, and save input
   ok(InspectorUI.treePanel.editingContext, "Step 2: editor session started");
+  let selection = InspectorUI.selection;
+
+  ok(selection, "Selection is: " + selection);
 
   let editorVisible = editor.classList.contains("editing");
   ok(editorVisible, "editor popup visible");
 
   // check if the editor popup is "near" the correct position
   let editorDims = editor.getBoundingClientRect();
   let attrValNodeDims = attrValNode_id.getBoundingClientRect();
   let editorPositionOK = (editorDims.left >= (attrValNodeDims.left - editorDims.width - 5)) &&
@@ -98,25 +110,32 @@ function doEditorTestSteps()
 
   is(treePanel.editingContext.repObj, div, "editor session has correct reference to div");
   is(treePanel.editingContext.attrObj, attrValNode_id, "editor session has correct reference to `id` attribute-value node in HTML panel");
   is(treePanel.editingContext.attrName, "id", "editor session knows correct attribute-name");
 
   editorInput.value = "Hello World";
   editorInput.focus();
 
-  // hit <enter> to save the inputted value
+  Services.obs.addObserver(highlighterTrap,
+      InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+
+  // hit <enter> to save the textbox value
   executeSoon(function() {
+    // Extra key to test that keyboard handlers have been removed. bug 696107.
+    EventUtils.synthesizeKey("VK_LEFT", {}, attrValNode_id.ownerDocument.defaultView);
     EventUtils.synthesizeKey("VK_RETURN", {}, attrValNode_id.ownerDocument.defaultView);
   });
 
   // two `yield` statements, to trap both the "SAVED" and "CLOSED" events that will be triggered
   yield;
   yield; // End of Step 2
 
+  // remove this from previous step
+  Services.obs.removeObserver(highlighterTrap, InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
 
   // Step 3: validate that the previous editing session saved correctly, then open editor on `class` attribute value
   ok(!treePanel.editingContext, "Step 3: editor session ended");
   editorVisible = editor.classList.contains("editing");
   ok(!editorVisible, "editor popup hidden");
   attrValNodeHighlighted = attrValNode_id.classList.contains("editingAttributeValue");
   ok(!attrValNodeHighlighted, "`id` attribute-value node is no longer editor-highlighted");
   is(div.getAttribute("id"), "Hello World", "`id` attribute-value successfully updated");
@@ -205,26 +224,24 @@ function doEditorTestSteps()
   // Step 8: validate that the editor was closed and that the editing was not saved
   ok(!treePanel.editingContext, "Step 8: editor session ended");
   editorVisible = editor.classList.contains("editing");
   ok(!editorVisible, "editor popup hidden");
   is(div.getAttribute("id"), "Hello World", "`id` attribute-value *not* updated");
   is(attrValNode_id.innerHTML, "Hello World", "attribute-value node in HTML panel *not* updated");
 
   // End of Step 8
+  executeSoon(finishUp);
+}
 
+function finishUp() {
   // end of all steps, so clean up
   Services.obs.removeObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, false);
   Services.obs.removeObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, false);
   Services.obs.removeObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, false);
-
-  executeSoon(finishUp);
-}
-
-function finishUp() {
   doc = div = null;
   InspectorUI.closeInspectorUI();
   gBrowser.removeCurrentTab();
   finish();
 }
 
 function test()
 {
--- a/browser/devtools/highlighter/test/browser_inspector_initialization.js
+++ b/browser/devtools/highlighter/test/browser_inspector_initialization.js
@@ -71,16 +71,17 @@ function runInspectorTests()
     InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
   Services.obs.addObserver(treePanelTests,
     InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
 
   ok(InspectorUI.toolbar, "we have the toolbar.");
   ok(!InspectorUI.toolbar.hidden, "toolbar is visible");
   ok(InspectorUI.inspecting, "Inspector is inspecting");
   ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
+  ok(!InspectorUI.isSidebarOpen, "Inspector Sidebar is not open");
   ok(InspectorUI.highlighter, "Highlighter is up");
   InspectorUI.inspectNode(doc.body);
   InspectorUI.stopInspecting();
 
   InspectorUI.treePanel.open();
 }
 
 function treePanelTests()
@@ -88,33 +89,45 @@ function treePanelTests()
   Services.obs.removeObserver(treePanelTests,
     InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
   Services.obs.addObserver(stylePanelTests,
     "StyleInspector-opened", false);
 
   ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open");
 
   executeSoon(function() {
-    InspectorUI.stylePanel.open(doc.body);
+    InspectorUI.showSidebar();
+    document.getElementById(InspectorUI.getToolbarButtonId("styleinspector")).click();
   });
 }
 
 function stylePanelTests()
 {
   Services.obs.removeObserver(stylePanelTests, "StyleInspector-opened");
-  Services.obs.addObserver(runContextMenuTest,
-    InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
+
+  ok(InspectorUI.isSidebarOpen, "Inspector Sidebar is open");
+  ok(InspectorUI.stylePanel.cssHtmlTree, "Style Panel has a cssHtmlTree");
 
-  ok(InspectorUI.stylePanel.isOpen(), "Style Panel is Open");
-  ok(InspectorUI.stylePanel.cssHtmlTree, "Style Panel has a cssHtmlTree");
+  InspectorUI.ruleButton.click();
+  executeSoon(function() {
+    ruleViewTests();
+  });
+}
+
+function ruleViewTests()
+{
+  Services.obs.addObserver(runContextMenuTest,
+      InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
+
+  ok(InspectorUI.isRuleViewOpen(), "Rule View is open");
+  ok(InspectorUI.ruleView, "InspectorUI has a cssRuleView");
 
   executeSoon(function() {
     InspectorUI.closeInspectorUI();
   });
-
 }
 
 function runContextMenuTest()
 {
   Services.obs.removeObserver(runContextMenuTest, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
   Services.obs.addObserver(inspectNodesFromContextTest, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
   salutation = doc.getElementById("salutation");
   ok(salutation, "hello, context menu test!");
@@ -186,16 +199,21 @@ function inspectNodesFromContextTestTrap
 function finishInspectorTests()
 {
   Services.obs.removeObserver(finishInspectorTests,
     InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED);
 
   ok(!InspectorUI.highlighter, "Highlighter is gone");
   ok(!InspectorUI.treePanel, "Inspector Tree Panel is closed");
   ok(!InspectorUI.inspecting, "Inspector is not inspecting");
+  ok(!InspectorUI.isSidebarOpen, "Inspector Sidebar is closed");
+  ok(!InspectorUI.stylePanel, "Inspector Style Panel is gone");
+  ok(!InspectorUI.ruleView, "Inspector Rule View is gone");
+  is(InspectorUI.sidebarToolbar.children.length, 0, "No items in the Sidebar toolbar");
+  is(InspectorUI.sidebarDeck.children.length, 0, "No items in the Sidebar deck");
   ok(!InspectorUI.toolbar, "toolbar is hidden");
 
   gBrowser.removeCurrentTab();
   finish();
 }
 
 function test()
 {
new file mode 100644
--- /dev/null
+++ b/browser/devtools/highlighter/test/browser_inspector_ruleviewstore.js
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Inspector Tab Switch Tests.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Rob Campbell <rcampbell@mozilla.com>
+ *   Mihai Șucan <mihai.sucan@gmail.com>
+ *   Dave Camp <dcamp@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Tests that properties disabled in the rule view survive a tab switch.
+ */
+
+let div;
+let tab1;
+
+function waitForRuleView(aCallback)
+{
+  if (InspectorUI.ruleView) {
+    aCallback();
+    return;
+  }
+
+  let ruleViewFrame = InspectorUI.getToolIframe(InspectorUI.ruleViewObject);
+  ruleViewFrame.addEventListener("load", function(evt) {
+    ruleViewFrame.removeEventListener(evt.type, arguments.callee, true);
+    executeSoon(function() {
+      aCallback();
+    });
+  }, true);
</