diff options
author | mad@chromium.org <mad@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-03 00:37:31 +0000 |
---|---|---|
committer | mad@chromium.org <mad@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-03 00:37:31 +0000 |
commit | 1c1c77a5021be0b902240a4f78009a8d8f71d1ac (patch) | |
tree | c1afb46b67de923afaac68340ddb0113b7306193 /chrome/renderer | |
parent | 23b3f6c67270a6aff5020c9a7279f4ed84192a02 (diff) | |
download | chromium_src-1c1c77a5021be0b902240a4f78009a8d8f71d1ac.zip chromium_src-1c1c77a5021be0b902240a4f78009a8d8f71d1ac.tar.gz chromium_src-1c1c77a5021be0b902240a4f78009a8d8f71d1ac.tar.bz2 |
Submitting change from http://codereview.chromium.org/276029/show
BUG=none
TEST=none
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@30778 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/renderer')
-rw-r--r-- | chrome/renderer/extensions/extension_process_bindings.cc | 92 | ||||
-rw-r--r-- | chrome/renderer/resources/extension_process_bindings.js | 93 |
2 files changed, 171 insertions, 14 deletions
diff --git a/chrome/renderer/extensions/extension_process_bindings.cc b/chrome/renderer/extensions/extension_process_bindings.cc index 2a69afd..fee736a 100644 --- a/chrome/renderer/extensions/extension_process_bindings.cc +++ b/chrome/renderer/extensions/extension_process_bindings.cc @@ -102,6 +102,9 @@ static L10nMessagesMap* GetL10nMessagesMap(const std::string extension_id) { } } +// A RenderViewVisitor class that iterates through the set of available +// views, looking for a view of the given type, in the given browser window +// and within the given extension. // Used to accumulate the list of views associated with an extension. class ExtensionViewAccumulator : public RenderViewVisitor { public: @@ -115,6 +118,8 @@ class ExtensionViewAccumulator : public RenderViewVisitor { index_(0) { } + v8::Local<v8::Array> views() { return views_; } + virtual bool Visit(RenderView* render_view) { if (!ViewTypeMatches(render_view->view_type(), view_type_)) return true; @@ -135,17 +140,26 @@ class ExtensionViewAccumulator : public RenderViewVisitor { if (!context.IsEmpty()) { v8::Local<v8::Value> window = context->Global(); DCHECK(!window.IsEmpty()); - views_->Set(v8::Integer::New(index_), window); - index_++; - if (view_type_ == ViewType::EXTENSION_BACKGROUND_PAGE) - return false; // There can be only one... + + if (!OnMatchedView(window)) + return false; } return true; } - v8::Local<v8::Array> views() { return views_; } - private: + // Called on each view found matching the search criteria. Returns false + // to terminate the iteration. + bool OnMatchedView(const v8::Local<v8::Value>& view_window) { + views_->Set(v8::Integer::New(index_), view_window); + index_++; + + if (view_type_ == ViewType::EXTENSION_BACKGROUND_PAGE) + return false; // There can be only one... + + return true; + } + // Returns true is |type| "isa" |match|. static bool ViewTypeMatches(ViewType::Type type, ViewType::Type match) { if (type == match) @@ -215,6 +229,10 @@ class ExtensionImpl : public ExtensionBase { return v8::FunctionTemplate::New(GetRenderViewId); } else if (name->Equals(v8::String::New("GetL10nMessage"))) { return v8::FunctionTemplate::New(GetL10nMessage); + } else if (name->Equals(v8::String::New("GetPopupView"))) { + return v8::FunctionTemplate::New(GetPopupView); + } else if (name->Equals(v8::String::New("GetPopupAnchorView"))) { + return v8::FunctionTemplate::New(GetPopupAnchorView); } else if (name->Equals(v8::String::New("SetExtensionActionIcon"))) { return v8::FunctionTemplate::New(SetExtensionActionIcon); } @@ -228,6 +246,53 @@ class ExtensionImpl : public ExtensionBase { return v8::String::New(GetStringResource<IDR_EXTENSION_API_JSON>()); } + static v8::Handle<v8::Value> PopupViewFinder( + const v8::Arguments& args, + ViewType::Type viewtype_to_find) { + // TODO(twiz) Correct the logic that ties the ownership of the pop-up view + // to the hosting view. At the moment we assume that there may only be + // a single pop-up view for a given extension. By doing so, we can find + // the pop-up view by simply searching for the only pop-up view present. + // We also assume that if the current view is a pop-up, we can find the + // hosting view by searching for a TOOLSTRIP view. + if (args.Length() != 0) + return v8::Undefined(); + + if (viewtype_to_find != ViewType::EXTENSION_POPUP && + viewtype_to_find != ViewType::EXTENSION_TOOLSTRIP) { + NOTREACHED() << L"Requesting invalid view type."; + } + + // Disallow searching for a host view if we are a popup view, and likewise + // if we are a toolstrip view. + RenderView* render_view = bindings_utils::GetRenderViewForCurrentContext(); + if (!render_view || + render_view->view_type() == viewtype_to_find) { + return v8::Undefined(); + } + + int browser_window_id = render_view->browser_window_id(); + ExtensionViewAccumulator popup_matcher(ExtensionIdForCurrentContext(), + browser_window_id, + viewtype_to_find); + RenderView::ForEach(&popup_matcher); + + if (0 == popup_matcher.views()->Length()) + return v8::Undefined(); + DCHECK(popup_matcher.views()->Has(0)); + + // Return the first view found. + return popup_matcher.views()->Get(v8::Integer::New(0)); + } + + static v8::Handle<v8::Value> GetPopupView(const v8::Arguments& args) { + return PopupViewFinder(args, ViewType::EXTENSION_POPUP); + } + + static v8::Handle<v8::Value> GetPopupAnchorView(const v8::Arguments& args) { + return PopupViewFinder(args, ViewType::EXTENSION_TOOLSTRIP); + } + static v8::Handle<v8::Value> GetExtensionViews(const v8::Arguments& args) { if (args.Length() != 2) return v8::Undefined(); @@ -242,15 +307,17 @@ class ExtensionImpl : public ExtensionBase { std::string view_type_string = *v8::String::Utf8Value(args[1]->ToString()); // |view_type| == ViewType::INVALID means getting any type of views. ViewType::Type view_type = ViewType::INVALID; - if (view_type_string == "TOOLSTRIP") { + if (view_type_string == ViewType::kToolstrip) { view_type = ViewType::EXTENSION_TOOLSTRIP; - } else if (view_type_string == "MOLE") { + } else if (view_type_string == ViewType::kMole) { view_type = ViewType::EXTENSION_MOLE; - } else if (view_type_string == "BACKGROUND") { + } else if (view_type_string == ViewType::kBackgroundPage) { view_type = ViewType::EXTENSION_BACKGROUND_PAGE; - } else if (view_type_string == "TAB") { + } else if (view_type_string == ViewType::kTabContents) { view_type = ViewType::TAB_CONTENTS; - } else if (view_type_string != "ALL") { + } else if (view_type_string == ViewType::kPopup) { + view_type = ViewType::EXTENSION_POPUP; + } else if (view_type_string != ViewType::kAll) { return v8::Undefined(); } @@ -414,7 +481,8 @@ class ExtensionImpl : public ExtensionBase { // A special request for setting the extension action icon. This function // accepts a canvas ImageData object, so it needs to do extra processing // before sending the request to the browser. - static v8::Handle<v8::Value> SetExtensionActionIcon(const v8::Arguments& args) { + static v8::Handle<v8::Value> SetExtensionActionIcon( + const v8::Arguments& args) { v8::Local<v8::Object> details = args[1]->ToObject(); v8::Local<v8::Object> image_data = details->Get(v8::String::New("imageData"))->ToObject(); diff --git a/chrome/renderer/resources/extension_process_bindings.js b/chrome/renderer/resources/extension_process_bindings.js index 3d3fb88..6a0584b 100644 --- a/chrome/renderer/resources/extension_process_bindings.js +++ b/chrome/renderer/resources/extension_process_bindings.js @@ -16,6 +16,8 @@ var chrome = chrome || {}; native function OpenChannelToTab(); native function GetRenderViewId(); native function GetL10nMessage(); + native function GetPopupAnchorView(); + native function GetPopupView(); native function SetExtensionActionIcon(); if (!chrome) @@ -187,6 +189,48 @@ var chrome = chrome || {}; }; } + // Helper function for positioning pop-up windows relative to DOM objects. + // Returns the absolute position of the given element relative to the hosting + // browser frame. + function findAbsolutePosition(domElement) { + var curleft = curtop = 0; + var parentNode = domElement.parentNode + + // Ascend through the parent hierarchy, taking into account object nesting + // and scoll positions. + if (domElement.offsetParent) { + do { + if (domElement.offsetLeft) curleft += domElement.offsetLeft; + if (domElement.offsetTop) curtop += domElement.offsetTop; + + if (domElement.scrollLeft) curleft -= domElement.scrollLeft; + if (domElement.scrollTop) curtop -= domElement.scrollTop; + + if (parentNode != domElement.offsetParent) { + while(parentNode != null && parentNode != domElement.offsetParent) { + if (parentNode.scrollLeft) curleft -= parentNode.scrollLeft; + if (parentNode.scrollTop) curtop -= parentNode.scrollTop; + parentNode = parentNode.parentNode; + } + } + } while ((domElement = domElement.offsetParent) != null); + } + + return { + top: curtop, + left: curleft + }; + } + + // Returns the coordiates of the rectangle encompassing the domElement, + // in browser coordinates relative to the frame hosting the element. + function getAbsoluteRect(domElement) { + var rect = findAbsolutePosition(domElement); + rect.width = domElement.width || 0; + rect.height = domElement.height || 0; + return rect; + } + // --- Setup additional api's not currently handled in common/extensions/api // Page action events send (pageActionId, {tabId, tabUrl}). @@ -221,6 +265,12 @@ var chrome = chrome || {}; new chrome.Event("toolstrip.onCollapsed." + renderViewId); } + function setupPopupEvents(renderViewId) { + chrome.experimental.popup = chrome.experimental.popup || {}; + chrome.experimental.popup.onClosed = + new chrome.Event("experimental.popup.onClosed." + renderViewId); + } + chromeHidden.onLoad.addListener(function (extensionId) { chrome.initExtension(extensionId); @@ -266,13 +316,23 @@ var chrome = chrome || {}; apiFunctions[apiFunction.name] = apiFunction; module[functionDef.name] = bind(apiFunction, function() { - chromeHidden.validate(arguments, this.definition.parameters); + // If the function is marked with a custom handler, then bypass + // validation of the arguments. This flag is required for + // extensions taking DOM objects as arguments. DOM objects + // cannot be described using the type system in extension_api.json, + // and so cannot be validated. + // A good practice is to extract the primitive data needed to + // service the request, and then invoke validate against that data. + // See popup.show(...) for an example. + if (!functionDef.customHandler) { + chromeHidden.validate(arguments, this.definition.parameters); + } if (this.handleRequest) return this.handleRequest.apply(this, arguments); else return sendRequest(this.name, arguments, - this.definition.parameters); + this.definition.parameters); }); }); } @@ -358,6 +418,34 @@ var chrome = chrome || {}; return GetL10nMessage(message_name, placeholders); } + apiFunctions["experimental.popup.show"].handleRequest = + function(url, showDetails, callback) { + if (!showDetails || !showDetails.relativeTo) { + throw new Error("showDetails.relativeTo argument missing."); + } + + var position = getAbsoluteRect(showDetails.relativeTo); + var popUpInfo = { + "url": url + }; + var domAnchor = position; + var modifiedArgs = [popUpInfo, domAnchor, callback]; + chromeHidden.validate(modifiedArgs, this.definition.parameters); + + return sendRequest(this.name, modifiedArgs, + this.definition.parameters); + } + + apiFunctions["experimental.extension.getPopupView"].handleRequest = + function() { + return GetPopupView(); + } + + apiFunctions["experimental.popup.getAnchorWindow"].handleRequest = + function() { + return GetPopupAnchorView(); + } + var canvas; function setIconCommon(details, name, parameters) { var EXTENSION_ACTION_ICON_SIZE = 19; @@ -425,6 +513,7 @@ var chrome = chrome || {}; setupBrowserActionEvent(extensionId); setupPageActionEvents(extensionId); setupToolstripEvents(GetRenderViewId()); + setupPopupEvents(GetRenderViewId()); }); if (!chrome.experimental) |