mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 897060 - Display select dropdowns in the parent process. r=enndeakin
Original patch by roc.
This commit is contained in:
parent
9ed414bc32
commit
e36a075b54
@ -714,3 +714,18 @@ chatbox:-moz-full-screen-ancestor > .chat-titlebar {
|
||||
*:-moz-full-screen-ancestor #social-sidebar-box:not(:-moz-full-screen-ancestor) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Combobox dropdown renderer */
|
||||
#ContentSelectDropdown {
|
||||
max-height: 400px;
|
||||
}
|
||||
|
||||
.contentSelectDropdown-optgroup {
|
||||
font-weight: bold;
|
||||
/* color: menutext used to overwrite the disabled color */
|
||||
color: menutext;
|
||||
}
|
||||
|
||||
.contentSelectDropdown-ingroup {
|
||||
-moz-margin-start: 2em;
|
||||
}
|
||||
|
@ -125,6 +125,9 @@
|
||||
<!-- for url bar autocomplete -->
|
||||
<panel type="autocomplete-richlistbox" id="PopupAutoCompleteRichResult" noautofocus="true" hidden="true"/>
|
||||
|
||||
<!-- for select dropdowns -->
|
||||
<menupopup id="ContentSelectDropdown" rolluponmousewheel="true" hidden="true"/>
|
||||
|
||||
<!-- for invalid form error message -->
|
||||
<panel id="invalid-form-popup" type="arrow" orient="vertical" noautofocus="true" hidden="true" level="parent">
|
||||
<description/>
|
||||
@ -1025,7 +1028,8 @@
|
||||
flex="1" contenttooltip="aHTMLTooltip"
|
||||
tabcontainer="tabbrowser-tabs"
|
||||
contentcontextmenu="contentAreaContextMenu"
|
||||
autocompletepopup="PopupAutoComplete"/>
|
||||
autocompletepopup="PopupAutoComplete"
|
||||
selectpopup="ContentSelectDropdown"/>
|
||||
<chatbar id="pinnedchats" layer="true" mousethrough="always" hidden="true"/>
|
||||
<statuspanel id="statusbar-display" inactive="true"/>
|
||||
</vbox>
|
||||
|
@ -35,7 +35,7 @@
|
||||
<xul:vbox flex="1" class="browserContainer">
|
||||
<xul:stack flex="1" class="browserStack" anonid="browserStack">
|
||||
<xul:browser anonid="initialBrowser" type="content-primary" message="true" disablehistory="true"
|
||||
xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup"/>
|
||||
xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup,selectpopup"/>
|
||||
</xul:stack>
|
||||
</xul:vbox>
|
||||
</xul:hbox>
|
||||
@ -1473,6 +1473,10 @@
|
||||
|
||||
if (this.hasAttribute("autocompletepopup"))
|
||||
b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
|
||||
|
||||
if (this.hasAttribute("selectpopup"))
|
||||
b.setAttribute("selectpopup", this.getAttribute("selectpopup"));
|
||||
|
||||
b.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
|
||||
|
||||
// Create the browserStack container
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "mozilla/dom/HTMLOptionsCollection.h"
|
||||
#include "mozilla/dom/HTMLSelectElement.h"
|
||||
#include "mozilla/LookAndFeel.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace mozilla;
|
||||
@ -1816,6 +1817,14 @@ nsListControlFrame::MouseDown(nsIDOMEvent* aMouseEvent)
|
||||
} else {
|
||||
// NOTE: the combo box is responsible for dropping it down
|
||||
if (mComboboxFrame) {
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Content &&
|
||||
Preferences::GetBool("browser.tabs.remote", false)) {
|
||||
nsContentUtils::DispatchChromeEvent(mContent->OwnerDoc(), mContent,
|
||||
NS_LITERAL_STRING("mozshowdropdown"), true,
|
||||
false);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!IgnoreMouseEventForSelection(aMouseEvent)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ toolkit.jar:
|
||||
content/global/resetProfile.js (resetProfile.js)
|
||||
content/global/resetProfile.xul (resetProfile.xul)
|
||||
content/global/resetProfileProgress.xul (resetProfileProgress.xul)
|
||||
content/global/select-child.js (select-child.js)
|
||||
content/global/treeUtils.js (treeUtils.js)
|
||||
content/global/viewZoomOverlay.js (viewZoomOverlay.js)
|
||||
*+ content/global/bindings/autocomplete.xml (widgets/autocomplete.xml)
|
||||
|
13
toolkit/content/select-child.js
Normal file
13
toolkit/content/select-child.js
Normal file
@ -0,0 +1,13 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SelectContentHelper",
|
||||
"resource://gre/modules/SelectContentHelper.jsm");
|
||||
|
||||
addEventListener("mozshowdropdown", event => {
|
||||
if (!event.isTrusted)
|
||||
return;
|
||||
|
||||
new SelectContentHelper(event.target, this);
|
||||
});
|
@ -100,6 +100,13 @@
|
||||
this.messageManager.addMessageListener("DOMTitleChanged", this);
|
||||
this.messageManager.addMessageListener("ImageDocumentLoaded", this);
|
||||
this.messageManager.loadFrameScript("chrome://global/content/browser-child.js", true);
|
||||
|
||||
if (this.hasAttribute("selectpopup")) {
|
||||
this.messageManager.addMessageListener("Forms:ShowDropDown", this);
|
||||
this.messageManager.addMessageListener("Forms:HideDropDown", this);
|
||||
this.messageManager.loadFrameScript("chrome://global/content/select-child.js", true);
|
||||
}
|
||||
|
||||
this.webProgress._init();
|
||||
|
||||
let jsm = "resource://gre/modules/RemoteController.jsm";
|
||||
@ -119,17 +126,31 @@
|
||||
<method name="receiveMessage">
|
||||
<parameter name="aMessage"/>
|
||||
<body><![CDATA[
|
||||
let json = aMessage.json;
|
||||
let data = aMessage.data;
|
||||
switch (aMessage.name) {
|
||||
case "DOMTitleChanged":
|
||||
this._contentTitle = json.title;
|
||||
this._contentTitle = data.title;
|
||||
break;
|
||||
case "ImageDocumentLoaded":
|
||||
this._imageDocument = {
|
||||
width: json.width,
|
||||
height: json.height
|
||||
width: data.width,
|
||||
height: data.height
|
||||
};
|
||||
break;
|
||||
|
||||
case "Forms:ShowDropDown": {
|
||||
Cu.import("resource://gre/modules/SelectParentHelper.jsm");
|
||||
let dropdown = document.getElementById(this.getAttribute("selectpopup"));
|
||||
SelectParentHelper.populate(dropdown, data.options, data.selectedIndex);
|
||||
SelectParentHelper.open(this, dropdown, data.rect);
|
||||
break;
|
||||
}
|
||||
|
||||
case "Forms:HideDropDown": {
|
||||
Cu.import("resource://gre/modules/SelectParentHelper.jsm");
|
||||
let dropdown = document.getElementById(this.getAttribute("selectpopup"));
|
||||
SelectParentHelper.hide(dropdown);
|
||||
}
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
114
toolkit/modules/SelectContentHelper.jsm
Normal file
114
toolkit/modules/SelectContentHelper.jsm
Normal file
@ -0,0 +1,114 @@
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"SelectContentHelper"
|
||||
];
|
||||
|
||||
this.SelectContentHelper = function (aElement, aGlobal) {
|
||||
this.element = aElement;
|
||||
this.global = aGlobal;
|
||||
this.init();
|
||||
this.showDropDown();
|
||||
}
|
||||
|
||||
this.SelectContentHelper.prototype = {
|
||||
init: function() {
|
||||
this.global.addMessageListener("Forms:SelectDropDownItem", this);
|
||||
this.global.addMessageListener("Forms:DismissedDropDown", this);
|
||||
this.global.addEventListener("pagehide", this);
|
||||
},
|
||||
|
||||
uninit: function() {
|
||||
this.global.removeMessageListener("Forms:SelectDropDownItem", this);
|
||||
this.global.removeMessageListener("Forms:DismissedDropDown", this);
|
||||
this.global.removeEventListener("pagehide", this);
|
||||
this.element = null;
|
||||
this.global = null;
|
||||
},
|
||||
|
||||
showDropDown: function() {
|
||||
let rect = this._getBoundingContentRect();
|
||||
|
||||
this.global.sendAsyncMessage("Forms:ShowDropDown", {
|
||||
rect: rect,
|
||||
options: this._buildOptionList(),
|
||||
selectedIndex: this.element.selectedIndex,
|
||||
});
|
||||
},
|
||||
|
||||
_getBoundingContentRect: function() {
|
||||
let { top, left, width, height } = this.element.getBoundingClientRect();
|
||||
// We need to copy the info because the properties returned by getBoundingClientRect
|
||||
// are not writable.
|
||||
let rect = { left: left, top: top, width: width, height: height };
|
||||
|
||||
// step out of iframes and frames, offsetting scroll values
|
||||
let view = this.element.ownerDocument.defaultView;
|
||||
for (let frame = view; frame != this.global.content; frame = frame.parent) {
|
||||
// adjust client coordinates' origin to be top left of iframe viewport
|
||||
let r = frame.frameElement.getBoundingClientRect();
|
||||
let left = frame.getComputedStyle(frame.frameElement, "").borderLeftWidth;
|
||||
let top = frame.getComputedStyle(frame.frameElement, "").borderTopWidth;
|
||||
|
||||
rect.left += r.left + parseInt(left, 10);
|
||||
rect.top += r.top + parseInt(top, 10);
|
||||
}
|
||||
|
||||
return rect;
|
||||
},
|
||||
|
||||
_buildOptionList: function() {
|
||||
return buildOptionListForChildren(this.element);
|
||||
},
|
||||
|
||||
receiveMessage: function(message) {
|
||||
switch (message.name) {
|
||||
case "Forms:SelectDropDownItem":
|
||||
this.element.selectedIndex = message.data.value;
|
||||
|
||||
//intentional fall-through
|
||||
case "Forms:DismissedDropDown":
|
||||
this.uninit();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent: function(event) {
|
||||
switch (event.type) {
|
||||
case "pagehide":
|
||||
this.global.sendAsyncMessage("Forms:HideDropDown", {});
|
||||
this.uninit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function buildOptionListForChildren(node) {
|
||||
let result = [];
|
||||
for (let child = node.firstChild; child; child = child.nextSibling) {
|
||||
if (child.tagName == 'OPTION' || child.tagName == 'OPTGROUP') {
|
||||
let info = {
|
||||
tagName: child.tagName,
|
||||
textContent: child.tagName == 'OPTGROUP' ? child.getAttribute("label")
|
||||
: child.textContent,
|
||||
// XXX this uses a highlight color when this is the selected element.
|
||||
// We need to suppress such highlighting in the content process to get
|
||||
// the option's correct unhighlighted color here.
|
||||
// We also need to detect default color vs. custom so that a standard
|
||||
// color does not override color: menutext in the parent.
|
||||
// backgroundColor: computedStyle.backgroundColor,
|
||||
// color: computedStyle.color,
|
||||
children: child.tagName == 'OPTGROUP' ? buildOptionListForChildren(child) : []
|
||||
};
|
||||
result.push(info);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
89
toolkit/modules/SelectParentHelper.jsm
Normal file
89
toolkit/modules/SelectParentHelper.jsm
Normal file
@ -0,0 +1,89 @@
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"SelectParentHelper"
|
||||
];
|
||||
|
||||
let currentBrowser = null;
|
||||
|
||||
this.SelectParentHelper = {
|
||||
populate: function(popup, items, selectedIndex) {
|
||||
// Clear the current contents of the popup
|
||||
popup.textContent = "";
|
||||
populateChildren(popup, items, selectedIndex);
|
||||
},
|
||||
|
||||
open: function(browser, popup, rect) {
|
||||
currentBrowser = browser;
|
||||
this._registerListeners(popup);
|
||||
popup.hidden = false;
|
||||
|
||||
popup.openPopup(currentBrowser, "overlap", rect.left, rect.top + rect.height);
|
||||
},
|
||||
|
||||
hide: function(popup) {
|
||||
popup.hidePopup();
|
||||
},
|
||||
|
||||
handleEvent: function(event) {
|
||||
let popup = event.currentTarget;
|
||||
|
||||
switch (event.type) {
|
||||
case "command":
|
||||
if (event.target.hasAttribute("value")) {
|
||||
currentBrowser.messageManager.sendAsyncMessage("Forms:SelectDropDownItem", {
|
||||
value: event.target.value
|
||||
});
|
||||
}
|
||||
popup.hidePopup();
|
||||
break;
|
||||
|
||||
case "popuphidden":
|
||||
currentBrowser.messageManager.sendAsyncMessage("Forms:DismissedDropDown", {});
|
||||
currentBrowser = null;
|
||||
this._unregisterListeners(popup);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_registerListeners: function(popup) {
|
||||
popup.addEventListener("command", this);
|
||||
popup.addEventListener("popuphidden", this);
|
||||
},
|
||||
|
||||
_unregisterListeners: function(popup) {
|
||||
popup.removeEventListener("command", this);
|
||||
popup.removeEventListener("popuphidden", this);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
function populateChildren(element, options, selectedIndex, startIndex = 0, isGroup = false) {
|
||||
let index = startIndex;
|
||||
|
||||
for (let option of options) {
|
||||
let item = element.ownerDocument.createElement("menuitem");
|
||||
item.setAttribute("label", option.textContent);
|
||||
item.setAttribute("type", "radio");
|
||||
|
||||
if (index == selectedIndex) {
|
||||
item.setAttribute("checked", "true");
|
||||
}
|
||||
|
||||
element.appendChild(item);
|
||||
|
||||
if (option.children.length > 0) {
|
||||
item.classList.add("contentSelectDropdown-optgroup");
|
||||
item.setAttribute("disabled", "true");
|
||||
index = populateChildren(element, option.children, selectedIndex, index, true);
|
||||
} else {
|
||||
item.setAttribute("value", index++);
|
||||
|
||||
if (isGroup) {
|
||||
item.classList.add("contentSelectDropdown-ingroup")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
@ -25,6 +25,8 @@ EXTRA_JS_MODULES += [
|
||||
'RemoteSecurityUI.jsm',
|
||||
'RemoteWebNavigation.jsm',
|
||||
'RemoteWebProgress.jsm',
|
||||
'SelectContentHelper.jsm',
|
||||
'SelectParentHelper.jsm',
|
||||
'Sqlite.jsm',
|
||||
'Task.jsm',
|
||||
'TelemetryTimestamps.jsm',
|
||||
|
Loading…
Reference in New Issue
Block a user