| Index: safari/ext/background.js | 
| =================================================================== | 
| --- a/safari/ext/background.js | 
| +++ b/safari/ext/background.js | 
| @@ -52,6 +52,10 @@ | 
| { | 
| this._tab.activate(); | 
| }, | 
| +    close: function() | 
| +    { | 
| +      this._tab.close(); | 
| +    }, | 
| sendMessage: function(message, responseCallback) | 
| { | 
| this._messageProxy.sendMessage(message, responseCallback, {pageId: this._id}); | 
| @@ -119,7 +123,8 @@ | 
|  | 
| callback(matchedPages); | 
| }, | 
| -    onLoading: new ext._EventTarget() | 
| +    onLoading: new ext._EventTarget(), | 
| +    onPopup: new ext._EventTarget() | 
| }; | 
|  | 
| safari.application.addEventListener("close", function(event) | 
| @@ -523,6 +528,50 @@ | 
|  | 
| /* Message processing */ | 
|  | 
| +  var findPageAndFrame = function(tab, url) | 
| +  { | 
| +    var page = null; | 
| +    var frame = null; | 
| + | 
| +    var lastPage; | 
| +    var lastPageTopLevelFrame; | 
| + | 
| +    // find the parent frame and its page for this sub frame, | 
| +    // by matching its referrer with the URL of frames previously | 
| +    // loaded in the same tab. If there is more than one match, | 
| +    // the most recent loaded page and frame is preferred. | 
| +    for (var curPageId in pages) | 
| +    { | 
| +      var curPage = pages[curPageId]; | 
| +      if (tab && curPage._tab != tab) | 
| +        continue; | 
| + | 
| +      for (var i = 0; i < curPage._frames.length; i++) | 
| +      { | 
| +        var curFrame = curPage._frames[i]; | 
| + | 
| +        if (curFrame.url == url) | 
| +        { | 
| +          page = curPage; | 
| +          frame = curFrame; | 
| +        } | 
| + | 
| +        if (i == 0) | 
| +        { | 
| +          lastPage = curPage; | 
| +          lastPageTopLevelFrame = curFrame; | 
| +        } | 
| +      } | 
| +    } | 
| + | 
| +    // if we can't find the parent frame and its page, fall back to | 
| +    // the page most recently loaded in the tab and its top level frame | 
| +    return { | 
| +      page:  page  || lastPage, | 
| +      frame: frame || lastPageTopLevelFrame | 
| +    }; | 
| +  }; | 
| + | 
| safari.application.addEventListener("message", function(event) | 
| { | 
| switch (event.name) | 
| @@ -540,76 +589,50 @@ | 
| frameId = 0; | 
|  | 
| var isPrerendered = event.message.isPrerendered; | 
| -              var page = pages[pageId] = new Page( | 
| +              var page = new Page( | 
| pageId, | 
| event.target, | 
| event.message.url, | 
| isPrerendered | 
| ); | 
|  | 
| -              // when a new page is shown, forget the previous page associated | 
| -              // with its tab, and reset the toolbar item if necessary. | 
| -              // Note that it wouldn't be sufficient to do that when the old | 
| -              // page is unloading, because Safari dispatches window.onunload | 
| -              // only when reloading the page or following links, but not when | 
| -              // you enter a new URL in the address bar. | 
| -              if (!isPrerendered) | 
| -                replacePage(page); | 
| +              if (event.message.isPopup && !event.target._popupLoaded) | 
| +              { | 
| +                if (!("_opener" in event.target)) | 
| +                  event.target._opener = findPageAndFrame(null, event.message.referrer); | 
|  | 
| -              ext.pages.onLoading._dispatch(page); | 
| +                if (page.url != "about:blank") | 
| +                  event.target._popupLoaded = true; | 
| + | 
| +                ext.pages.onPopup._dispatch(page, event.target._opener); | 
| +              } | 
| + | 
| +              if (event.target.browserWindow) | 
| +              { | 
| +                pages[pageId] = page; | 
| + | 
| +                // when a new page is shown, forget the previous page associated | 
| +                // with its tab, and reset the toolbar item if necessary. | 
| +                // Note that it wouldn't be sufficient to do that when the old | 
| +                // page is unloading, because Safari dispatches window.onunload | 
| +                // only when reloading the page or following links, but not when | 
| +                // you enter a new URL in the address bar. | 
| +                if (!isPrerendered) | 
| +                  replacePage(page); | 
| + | 
| +                ext.pages.onLoading._dispatch(page); | 
| +              } | 
| } | 
| else | 
| { | 
| -              var page; | 
| -              var parentFrame; | 
| +              var parent = findPageAndFrame(event.target, event.message.referrer); | 
|  | 
| -              var lastPageId; | 
| -              var lastPage; | 
| -              var lastPageTopLevelFrame; | 
| +              pageId  = parent.page._id; | 
| +              frameId = parent.page._frames.length; | 
|  | 
| -              // find the parent frame and its page for this sub frame, | 
| -              // by matching its referrer with the URL of frames previously | 
| -              // loaded in the same tab. If there is more than one match, | 
| -              // the most recent loaded page and frame is preferred. | 
| -              for (var curPageId in pages) | 
| -              { | 
| -                var curPage = pages[curPageId]; | 
| -                if (curPage._tab != event.target) | 
| -                  continue; | 
| - | 
| -                for (var i = 0; i < curPage._frames.length; i++) | 
| -                { | 
| -                  var curFrame = curPage._frames[i]; | 
| - | 
| -                  if (curFrame.url == event.message.referrer) | 
| -                  { | 
| -                    pageId = curPageId; | 
| -                    page = curPage; | 
| -                    parentFrame = curFrame; | 
| -                  } | 
| - | 
| -                  if (i == 0) | 
| -                  { | 
| -                    lastPageId = curPageId; | 
| -                    lastPage = curPage; | 
| -                    lastPageTopLevelFrame = curFrame; | 
| -                  } | 
| -                } | 
| -              } | 
| - | 
| -              // if we can't find the parent frame and its page, fall back to | 
| -              // the page most recently loaded in the tab and its top level frame | 
| -              if (!page) | 
| -              { | 
| -                pageId = lastPageId; | 
| -                page = lastPage; | 
| -                parentFrame = lastPageTopLevelFrame; | 
| -              } | 
| - | 
| -              frameId = page._frames.length; | 
| -              page._frames.push({ | 
| +              parent.page._frames.push({ | 
| url: event.message.url, | 
| -                parent: parentFrame | 
| +                parent: parent.frame | 
| }); | 
| } | 
|  | 
|  |