зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to inbound on a CLOSED TREE.
This commit is contained in:
Коммит
8c7c4ef7ab
|
@ -87,8 +87,6 @@
|
|||
"infojobs.net": "\\(Mobile#(Android; Mobile",
|
||||
// bug 828399, antena3.com
|
||||
"antena3.com": "\\(Mobile#(Android; Mobile",
|
||||
// bug 828401, ingdirect.es
|
||||
"ingdirect.es": "\\(Mobile#(Android; Mobile",
|
||||
// bug 828403, fotocasa.es
|
||||
"fotocasa.es": "\\(Mobile#(Android; Mobile",
|
||||
// bug 828406, orange.es
|
||||
|
@ -107,8 +105,6 @@
|
|||
"movistar.com.ve": "\\(Mobile#(Android; Mobile",
|
||||
// bug 828445, bumeran.com.ve
|
||||
"bumeran.com.ve": "\\(Mobile#(Android; Mobile",
|
||||
// bug 828448, petardas.com
|
||||
"petardas.com": "\\(Mobile#(Android; Mobile",
|
||||
// bug 843112, movil.bankinter.es
|
||||
"movil.bankinter.es": "\\(Mobile#(Android; Mobile",
|
||||
// bug 843114, einforma.com
|
||||
|
@ -139,16 +135,12 @@
|
|||
"citibank.com": "\\(Mobile#(Android; Mobile",
|
||||
// bug 843153, games.com
|
||||
"games.com": "\\(Mobile#(Android; Mobile",
|
||||
// bug 843156, orbitz.com
|
||||
"orbitz.com": "\\(Mobile#(Android; Mobile",
|
||||
// bug 843160, ehow.com
|
||||
"ehow.com": "\\(Mobile#(Android; Mobile",
|
||||
// bug 843162, urbanspoon.com
|
||||
"urbanspoon.com": "\\(Mobile#(Android; Mobile",
|
||||
// bug 843165, virginatlantic.com
|
||||
"virginatlantic.com": "\\(Mobile#(Android; Mobile",
|
||||
// bug 843168, cheaptickets.com
|
||||
"cheaptickets.com": "\\(Mobile#(Android; Mobile",
|
||||
// bug 843172, zimbio.com
|
||||
"zimbio.com": "\\(Mobile#(Android; Mobile",
|
||||
// bug 843176, tylted.com
|
||||
|
@ -171,8 +163,6 @@
|
|||
"hazipatika.com": "\\(Mobile#(Android; Mobile",
|
||||
// bug 878234, hvg.hu
|
||||
"hvg.hu": "\\(Mobile#(Android; Mobile",
|
||||
// bug 878236, jofogas.hu
|
||||
"jofogas.hu": "\\(Mobile#(Android; Mobile",
|
||||
// bug 878238, koponyeg.hu
|
||||
"koponyeg.hu": "\\(Mobile#(Android; Mobile",
|
||||
// bug 878240, kuruc.info
|
||||
|
@ -205,16 +195,12 @@
|
|||
"mondo.rs": "\\(Mobile#(Android; Mobile",
|
||||
// bug 878277, naslovi.net
|
||||
"naslovi.net": "\\(Mobile#(Android; Mobile",
|
||||
// bug 878284, softonic.com
|
||||
"softonic.com": "\\(Mobile#(Android; Mobile",
|
||||
// bug 878630, ask.com
|
||||
"ask.com": "\\(Mobile#(Android; Mobile",
|
||||
// bug 878632, banorte.com
|
||||
"banorte.com": "\\(Mobile#(Android; Mobile",
|
||||
// bug 878637, eluniversal.com.mx
|
||||
"eluniversal.com.mx": "\\(Mobile#(Android; Mobile",
|
||||
// bug 878640, hootsuite.com
|
||||
"hootsuite.com": "\\(Mobile#(Android; Mobile",
|
||||
// bug 878642, mercadolibre.com.mx
|
||||
"mercadolibre.com.mx": "\\(Mobile#(Android; Mobile",
|
||||
// bug 878645, olx.com.mx
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"revision": "336e9464c144268a8902bbb2d9026be3ff2b327f",
|
||||
"revision": "8a192fcf2927a866574996b4895426213e01a325",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -106,11 +106,6 @@ function appUpdater()
|
|||
this.bundle = Services.strings.
|
||||
createBundle("chrome://browser/locale/browser.properties");
|
||||
|
||||
this.updateBtn = document.getElementById("updateButton");
|
||||
|
||||
// The button label value must be set so its height is correct.
|
||||
this.setupUpdateButton("update.checkInsideButton");
|
||||
|
||||
let manualURL = Services.urlFormatter.formatURLPref("app.update.url.manual");
|
||||
let manualLink = document.getElementById("manualLink");
|
||||
manualLink.value = manualURL;
|
||||
|
@ -123,8 +118,7 @@ function appUpdater()
|
|||
}
|
||||
|
||||
if (this.isPending || this.isApplied) {
|
||||
this.setupUpdateButton("update.restart." +
|
||||
(this.isMajor ? "upgradeButton" : "updateButton"));
|
||||
this.selectPanel("apply");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -135,15 +129,19 @@ function appUpdater()
|
|||
|
||||
if (this.isDownloading) {
|
||||
this.startDownload();
|
||||
// selectPanel("downloading") is called from setupDownloadingUI().
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.updateEnabled && this.updateAuto) {
|
||||
this.selectPanel("checkingForUpdates");
|
||||
this.isChecking = true;
|
||||
this.checker.checkForUpdates(this.updateCheckListener, true);
|
||||
return;
|
||||
}
|
||||
// If app.update.enabled is false, we don't pop up an update dialog
|
||||
// automatically, but opening the About dialog is considered manually
|
||||
// checking for updates, so we always check.
|
||||
// If app.update.auto is false, we ask before downloading though,
|
||||
// in onCheckComplete.
|
||||
this.selectPanel("checkingForUpdates");
|
||||
this.isChecking = true;
|
||||
this.checker.checkForUpdates(this.updateCheckListener, true);
|
||||
// after checking, onCheckComplete() is called
|
||||
}
|
||||
|
||||
appUpdater.prototype =
|
||||
|
@ -180,13 +178,6 @@ appUpdater.prototype =
|
|||
this.um.activeUpdate.state == "downloading";
|
||||
},
|
||||
|
||||
// true when the update type is major.
|
||||
get isMajor() {
|
||||
if (this.update)
|
||||
return this.update.type == "major";
|
||||
return this.um.activeUpdate.type == "major";
|
||||
},
|
||||
|
||||
// true when updating is disabled by an administrator.
|
||||
get updateDisabledAndLocked() {
|
||||
return !this.updateEnabled &&
|
||||
|
@ -218,36 +209,54 @@ appUpdater.prototype =
|
|||
},
|
||||
|
||||
/**
|
||||
* Sets the deck's selected panel.
|
||||
* Sets the panel of the updateDeck.
|
||||
*
|
||||
* @param aChildID
|
||||
* The id of the deck's child to select.
|
||||
* The id of the deck's child to select, e.g. "apply".
|
||||
*/
|
||||
selectPanel: function(aChildID) {
|
||||
this.updateDeck.selectedPanel = document.getElementById(aChildID);
|
||||
this.updateBtn.disabled = (aChildID != "updateButtonBox");
|
||||
let panel = document.getElementById(aChildID);
|
||||
|
||||
let button = panel.querySelector("button");
|
||||
if (button) {
|
||||
if (aChildID == "downloadAndInstall") {
|
||||
let updateVersion = gAppUpdater.update.displayVersion;
|
||||
button.label = this.bundle.formatStringFromName("update.downloadAndInstallButton.label", [updateVersion], 1);
|
||||
button.accessKey = this.bundle.GetStringFromName("update.downloadAndInstallButton.accesskey");
|
||||
}
|
||||
this.updateDeck.selectedPanel = panel;
|
||||
if (!document.commandDispatcher.focusedElement || // don't steal the focus
|
||||
document.commandDispatcher.focusedElement.localName == "button") // except from the other buttons
|
||||
button.focus();
|
||||
|
||||
} else {
|
||||
this.updateDeck.selectedPanel = panel;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the update button's label and accesskey.
|
||||
*
|
||||
* @param aKeyPrefix
|
||||
* The prefix for the properties file entry to use for setting the
|
||||
* label and accesskey.
|
||||
* Check for addon compat, or start the download right away
|
||||
*/
|
||||
setupUpdateButton: function(aKeyPrefix) {
|
||||
this.updateBtn.label = this.bundle.GetStringFromName(aKeyPrefix + ".label");
|
||||
this.updateBtn.accessKey = this.bundle.GetStringFromName(aKeyPrefix + ".accesskey");
|
||||
if (!document.commandDispatcher.focusedElement ||
|
||||
document.commandDispatcher.focusedElement == this.updateBtn)
|
||||
this.updateBtn.focus();
|
||||
doUpdate: function() {
|
||||
// skip the compatibility check if the update doesn't provide appVersion,
|
||||
// or the appVersion is unchanged, e.g. nightly update
|
||||
if (!this.update.appVersion ||
|
||||
Services.vc.compare(gAppUpdater.update.appVersion,
|
||||
Services.appinfo.version) == 0) {
|
||||
this.startDownload();
|
||||
} else {
|
||||
this.checkAddonCompatibility();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles oncommand for the update button.
|
||||
* Handles oncommand for the "Restart to Update" button
|
||||
* which is presented after the download has been downloaded.
|
||||
*/
|
||||
buttonOnCommand: function() {
|
||||
if (this.isPending || this.isApplied) {
|
||||
buttonRestartAfterDownload: function() {
|
||||
if (!this.isPending && !this.isApplied)
|
||||
return;
|
||||
|
||||
// Notify all windows that an application quit has been requested.
|
||||
let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"].
|
||||
createInstance(Components.interfaces.nsISupportsPRBool);
|
||||
|
@ -268,27 +277,21 @@ appUpdater.prototype =
|
|||
|
||||
appStartup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit |
|
||||
Components.interfaces.nsIAppStartup.eRestart);
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles oncommand for the "Apply Update…" button
|
||||
* which is presented if we need to show the billboard or license.
|
||||
*/
|
||||
buttonApplyBillboard: function() {
|
||||
const URI_UPDATE_PROMPT_DIALOG = "chrome://mozapps/content/update/updates.xul";
|
||||
// Firefox no longer displays a license for updates and the licenseURL check
|
||||
// is just in case a distibution does.
|
||||
if (this.update && (this.update.billboardURL || this.update.licenseURL ||
|
||||
this.addons.length != 0)) {
|
||||
var ary = null;
|
||||
ary = Components.classes["@mozilla.org/supports-array;1"].
|
||||
createInstance(Components.interfaces.nsISupportsArray);
|
||||
ary.AppendElement(this.update);
|
||||
var openFeatures = "chrome,centerscreen,dialog=no,resizable=no,titlebar,toolbar=no";
|
||||
Services.ww.openWindow(null, URI_UPDATE_PROMPT_DIALOG, "", openFeatures, ary);
|
||||
window.close();
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectPanel("checkingForUpdates");
|
||||
this.isChecking = true;
|
||||
this.checker.checkForUpdates(this.updateCheckListener, true);
|
||||
var ary = null;
|
||||
ary = Components.classes["@mozilla.org/supports-array;1"].
|
||||
createInstance(Components.interfaces.nsISupportsArray);
|
||||
ary.AppendElement(this.update);
|
||||
var openFeatures = "chrome,centerscreen,dialog=no,resizable=no,titlebar,toolbar=no";
|
||||
Services.ww.openWindow(null, URI_UPDATE_PROMPT_DIALOG, "", openFeatures, ary);
|
||||
window.close(); // close the "About" window; updates.xul takes over.
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -326,21 +329,14 @@ appUpdater.prototype =
|
|||
// Firefox no longer displays a license for updates and the licenseURL
|
||||
// check is just in case a distibution does.
|
||||
if (gAppUpdater.update.billboardURL || gAppUpdater.update.licenseURL) {
|
||||
gAppUpdater.selectPanel("updateButtonBox");
|
||||
gAppUpdater.setupUpdateButton("update.openUpdateUI." +
|
||||
(this.isMajor ? "upgradeButton"
|
||||
: "applyButton"));
|
||||
gAppUpdater.selectPanel("applyBillboard");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gAppUpdater.update.appVersion ||
|
||||
Services.vc.compare(gAppUpdater.update.appVersion,
|
||||
Services.appinfo.version) == 0) {
|
||||
gAppUpdater.startDownload();
|
||||
return;
|
||||
}
|
||||
|
||||
gAppUpdater.checkAddonCompatibility();
|
||||
if (gAppUpdater.updateAuto) // automatically download and install
|
||||
gAppUpdater.doUpdate();
|
||||
else // ask
|
||||
gAppUpdater.selectPanel("downloadAndInstall");
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -474,9 +470,7 @@ appUpdater.prototype =
|
|||
return;
|
||||
}
|
||||
|
||||
this.selectPanel("updateButtonBox");
|
||||
this.setupUpdateButton("update.openUpdateUI." +
|
||||
(this.isMajor ? "upgradeButton" : "applyButton"));
|
||||
this.selectPanel("apply");
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -553,11 +547,9 @@ appUpdater.prototype =
|
|||
if (status == "applied" || status == "applied-service" ||
|
||||
status == "pending" || status == "pending-service") {
|
||||
// If the update is successfully applied, or if the updater has
|
||||
// fallen back to non-staged updates, show the Restart to Update
|
||||
// fallen back to non-staged updates, show the "Restart to Update"
|
||||
// button.
|
||||
self.selectPanel("updateButtonBox");
|
||||
self.setupUpdateButton("update.restart." +
|
||||
(self.isMajor ? "upgradeButton" : "updateButton"));
|
||||
self.selectPanel("apply");
|
||||
} else if (status == "failed") {
|
||||
// Background update has failed, let's show the UI responsible for
|
||||
// prompting the user to update manually.
|
||||
|
@ -572,9 +564,7 @@ appUpdater.prototype =
|
|||
Services.obs.removeObserver(arguments.callee, "update-staged");
|
||||
}, "update-staged", false);
|
||||
} else {
|
||||
this.selectPanel("updateButtonBox");
|
||||
this.setupUpdateButton("update.restart." +
|
||||
(this.isMajor ? "upgradeButton" : "updateButton"));
|
||||
this.selectPanel("apply");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -582,7 +572,6 @@ appUpdater.prototype =
|
|||
this.selectPanel("downloadFailed");
|
||||
break;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -50,17 +50,29 @@
|
|||
<vbox id="updateBox">
|
||||
#ifdef MOZ_UPDATER
|
||||
<deck id="updateDeck" orient="vertical">
|
||||
<hbox id="updateButtonBox" align="center">
|
||||
<hbox id="downloadAndInstall" align="center">
|
||||
<button id="downloadAndInstallButton" align="start"
|
||||
oncommand="gAppUpdater.doUpdate();"/>
|
||||
<!-- label and accesskey will be filled by JS -->
|
||||
<spacer flex="1"/>
|
||||
</hbox>
|
||||
<hbox id="apply" align="center">
|
||||
<button id="updateButton" align="start"
|
||||
oncommand="gAppUpdater.buttonOnCommand();"/>
|
||||
label="&update.updateButton.label;"
|
||||
accesskey="&update.updateButton.accesskey;"
|
||||
oncommand="gAppUpdater.buttonRestartAfterDownload();"/>
|
||||
<spacer flex="1"/>
|
||||
</hbox>
|
||||
<hbox id="applyBillboard" align="center">
|
||||
<button id="applyButtonBillboard" align="start"
|
||||
label="&update.applyButtonBillboard.label;"
|
||||
accesskey="&update.applyButtonBillboard.accesskey;"
|
||||
oncommand="gAppUpdater.buttonApplyBillboard();"/>
|
||||
<spacer flex="1"/>
|
||||
</hbox>
|
||||
<hbox id="checkingForUpdates" align="center">
|
||||
<image class="update-throbber"/><label>&update.checkingForUpdates;</label>
|
||||
</hbox>
|
||||
<hbox id="checkingAddonCompat" align="center">
|
||||
<image class="update-throbber"/><label>&update.checkingAddonCompat;</label>
|
||||
</hbox>
|
||||
<hbox id="downloading" align="center">
|
||||
<image class="update-throbber"/><label>&update.downloading.start;</label><label id="downloadStatus"/><label>&update.downloading.end;</label>
|
||||
</hbox>
|
||||
|
|
|
@ -457,10 +457,17 @@ let CustomizableUIInternal = {
|
|||
|
||||
// If the placements have items in them which are (now) no longer removable,
|
||||
// we shouldn't be moving them:
|
||||
if (node.parentNode != container && !this.isWidgetRemovable(node)) {
|
||||
if (provider == CustomizableUI.PROVIDER_API) {
|
||||
let widgetInfo = gPalette.get(id);
|
||||
if (!widgetInfo.removable && aArea != widgetInfo.defaultArea) {
|
||||
placementsToRemove.add(id);
|
||||
continue;
|
||||
}
|
||||
} else if (provider == CustomizableUI.PROVIDER_XUL &&
|
||||
node.parentNode != container && !this.isWidgetRemovable(node)) {
|
||||
placementsToRemove.add(id);
|
||||
continue;
|
||||
}
|
||||
} // Special widgets are always removable, so no need to check them
|
||||
|
||||
if (inPrivateWindow && provider == CustomizableUI.PROVIDER_API) {
|
||||
let widget = gPalette.get(id);
|
||||
|
@ -1736,7 +1743,7 @@ let CustomizableUIInternal = {
|
|||
source: aSource || "addon",
|
||||
instances: new Map(),
|
||||
currentArea: null,
|
||||
removable: false,
|
||||
removable: true,
|
||||
overflows: true,
|
||||
defaultArea: null,
|
||||
shortcutId: null,
|
||||
|
@ -1778,6 +1785,11 @@ let CustomizableUIInternal = {
|
|||
|
||||
if (aData.defaultArea && gAreas.has(aData.defaultArea)) {
|
||||
widget.defaultArea = aData.defaultArea;
|
||||
} else if (!widget.removable) {
|
||||
ERROR("Widget '" + widget.id + "' is not removable but does not specify " +
|
||||
"a valid defaultArea. That's not possible; it must specify a " +
|
||||
"valid defaultArea as well.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if ("type" in aData && gSupportedWidgetTypes.has(aData.type)) {
|
||||
|
@ -2383,11 +2395,13 @@ this.CustomizableUI = {
|
|||
* invoked when a user hides your view.
|
||||
* - tooltiptext: string to use for the tooltip of the widget
|
||||
* - label: string to use for the label of the widget
|
||||
* - removable: whether the widget is removable (optional, default: false)
|
||||
* - removable: whether the widget is removable (optional, default: true)
|
||||
* NB: if you specify false here, you must provide a
|
||||
* defaultArea, too.
|
||||
* - overflows: whether widget can overflow when in an overflowable
|
||||
* toolbar (optional, default: true)
|
||||
* - defaultArea: default area to add the widget to
|
||||
* (optional, default: none)
|
||||
* (optional, default: none; required if non-removable)
|
||||
* - shortcutId: id of an element that has a shortcut for this widget
|
||||
* (optional, default: null). This is only used to display
|
||||
* the shortcut as part of the tooltip for builtin widgets
|
||||
|
|
|
@ -61,7 +61,6 @@ const CustomizableWidgets = [{
|
|||
type: "view",
|
||||
viewId: "PanelUI-history",
|
||||
shortcutId: "key_gotoHistory",
|
||||
removable: true,
|
||||
defaultArea: CustomizableUI.AREA_PANEL,
|
||||
onViewShowing: function(aEvent) {
|
||||
// Populate our list of history
|
||||
|
@ -148,7 +147,6 @@ const CustomizableWidgets = [{
|
|||
}
|
||||
}, {
|
||||
id: "privatebrowsing-button",
|
||||
removable: true,
|
||||
shortcutId: "key_privatebrowsing",
|
||||
defaultArea: CustomizableUI.AREA_PANEL,
|
||||
onCommand: function(e) {
|
||||
|
@ -161,7 +159,6 @@ const CustomizableWidgets = [{
|
|||
}
|
||||
}, {
|
||||
id: "save-page-button",
|
||||
removable: true,
|
||||
shortcutId: "key_savePage",
|
||||
defaultArea: CustomizableUI.AREA_PANEL,
|
||||
onCommand: function(aEvent) {
|
||||
|
@ -174,7 +171,6 @@ const CustomizableWidgets = [{
|
|||
}
|
||||
}, {
|
||||
id: "find-button",
|
||||
removable: true,
|
||||
shortcutId: "key_find",
|
||||
defaultArea: CustomizableUI.AREA_PANEL,
|
||||
onCommand: function(aEvent) {
|
||||
|
@ -187,7 +183,6 @@ const CustomizableWidgets = [{
|
|||
}
|
||||
}, {
|
||||
id: "open-file-button",
|
||||
removable: true,
|
||||
shortcutId: "openFileKb",
|
||||
defaultArea: CustomizableUI.AREA_PANEL,
|
||||
onCommand: function(aEvent) {
|
||||
|
@ -202,7 +197,6 @@ const CustomizableWidgets = [{
|
|||
id: "developer-button",
|
||||
type: "view",
|
||||
viewId: "PanelUI-developer",
|
||||
removable: true,
|
||||
shortcutId: "key_devToolboxMenuItem",
|
||||
defaultArea: CustomizableUI.AREA_PANEL,
|
||||
onViewShowing: function(aEvent) {
|
||||
|
@ -265,7 +259,6 @@ const CustomizableWidgets = [{
|
|||
}
|
||||
}, {
|
||||
id: "add-ons-button",
|
||||
removable: true,
|
||||
shortcutId: "key_openAddons",
|
||||
defaultArea: CustomizableUI.AREA_PANEL,
|
||||
onCommand: function(aEvent) {
|
||||
|
@ -278,7 +271,6 @@ const CustomizableWidgets = [{
|
|||
}
|
||||
}, {
|
||||
id: "preferences-button",
|
||||
removable: true,
|
||||
defaultArea: CustomizableUI.AREA_PANEL,
|
||||
#ifdef XP_WIN
|
||||
label: "preferences-button.labelWin",
|
||||
|
@ -295,7 +287,6 @@ const CustomizableWidgets = [{
|
|||
}, {
|
||||
id: "zoom-controls",
|
||||
type: "custom",
|
||||
removable: true,
|
||||
defaultArea: CustomizableUI.AREA_PANEL,
|
||||
onBuild: function(aDocument) {
|
||||
const kPanelId = "PanelUI-popup";
|
||||
|
@ -441,7 +432,6 @@ const CustomizableWidgets = [{
|
|||
}, {
|
||||
id: "edit-controls",
|
||||
type: "custom",
|
||||
removable: true,
|
||||
defaultArea: CustomizableUI.AREA_PANEL,
|
||||
onBuild: function(aDocument) {
|
||||
let inPanel = (this.currentArea == CustomizableUI.AREA_PANEL);
|
||||
|
@ -536,7 +526,6 @@ const CustomizableWidgets = [{
|
|||
id: "feed-button",
|
||||
type: "view",
|
||||
viewId: "PanelUI-feeds",
|
||||
removable: true,
|
||||
defaultArea: CustomizableUI.AREA_PANEL,
|
||||
onClick: function(aEvent) {
|
||||
let win = aEvent.target.ownerDocument.defaultView;
|
||||
|
@ -576,7 +565,6 @@ const CustomizableWidgets = [{
|
|||
id: "characterencoding-button",
|
||||
type: "view",
|
||||
viewId: "PanelUI-characterEncodingView",
|
||||
removable: true,
|
||||
defaultArea: CustomizableUI.AREA_PANEL,
|
||||
maybeDisableMenu: function(aDocument) {
|
||||
let window = aDocument.defaultView;
|
||||
|
@ -783,7 +771,6 @@ const CustomizableWidgets = [{
|
|||
}
|
||||
}, {
|
||||
id: "email-link-button",
|
||||
removable: true,
|
||||
onCommand: function(aEvent) {
|
||||
let win = aEvent.view;
|
||||
win.MailIntegration.sendLinkForWindow(win.content);
|
||||
|
@ -801,7 +788,6 @@ if (Services.sysinfo.getProperty("hasWindowsTouchInterface")) {
|
|||
id: "switch-to-metro-button",
|
||||
label: "switch-to-metro-button2.label",
|
||||
tooltiptext: metroTooltip,
|
||||
removable: true,
|
||||
defaultArea: CustomizableUI.AREA_PANEL,
|
||||
showInPrivateBrowsing: false, /* See bug 928068 */
|
||||
onCommand: function(aEvent) {
|
||||
|
|
|
@ -45,4 +45,5 @@ skip-if = os == "mac"
|
|||
[browser_942581_unregisterArea_keeps_placements.js]
|
||||
[browser_943683_migration_test.js]
|
||||
[browser_944887_destroyWidget_should_destroy_in_palette.js]
|
||||
[browser_947987_removable_default.js]
|
||||
[browser_panel_toggle.js]
|
||||
|
|
|
@ -10,16 +10,17 @@ let gTests = [
|
|||
let navbar = document.getElementById("nav-bar");
|
||||
ok(CustomizableUI.inDefaultState, "Should start in default state");
|
||||
|
||||
CustomizableUI.createWidget({id: kWidgetId, removable: false, label: "Test"});
|
||||
let button = createDummyXULButton(kWidgetId, "Test non-removable inDefaultState handling");
|
||||
CustomizableUI.addWidgetToArea(kWidgetId, CustomizableUI.AREA_NAVBAR);
|
||||
button.setAttribute("removable", "false");
|
||||
ok(CustomizableUI.inDefaultState, "Should still be in default state after navbar addition");
|
||||
CustomizableUI.destroyWidget(kWidgetId);
|
||||
button.remove();
|
||||
|
||||
CustomizableUI.createWidget({id: kWidgetId, removable: false, label: "Test"});
|
||||
button = createDummyXULButton(kWidgetId, "Test non-removable inDefaultState handling");
|
||||
CustomizableUI.addWidgetToArea(kWidgetId, CustomizableUI.AREA_PANEL);
|
||||
button.setAttribute("removable", "false");
|
||||
ok(CustomizableUI.inDefaultState, "Should still be in default state after panel addition");
|
||||
CustomizableUI.destroyWidget(kWidgetId);
|
||||
|
||||
button.remove();
|
||||
ok(CustomizableUI.inDefaultState, "Should be in default state after destroying both widgets");
|
||||
},
|
||||
teardown: null
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/* 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/. */
|
||||
|
||||
let kWidgetId = "test-removable-widget-default";
|
||||
const kNavBar = CustomizableUI.AREA_NAVBAR;
|
||||
let widgetCounter = 0;
|
||||
let gTests = [
|
||||
{
|
||||
desc: "Sanity checks",
|
||||
run: function() {
|
||||
let brokenSpec = {id: kWidgetId + (widgetCounter++), removable: false};
|
||||
SimpleTest.doesThrow(function() CustomizableUI.createWidget(brokenSpec),
|
||||
"Creating non-removable widget without defaultArea should throw.");
|
||||
|
||||
// Widget without removable set should be removable:
|
||||
let wrapper = CustomizableUI.createWidget({id: kWidgetId + (widgetCounter++)});
|
||||
ok(CustomizableUI.isWidgetRemovable(wrapper.id), "Should be removable by default.");
|
||||
CustomizableUI.destroyWidget(wrapper.id);
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: "Test non-removable widget with defaultArea",
|
||||
run: function() {
|
||||
// Non-removable widget with defaultArea should work:
|
||||
let spec = {id: kWidgetId + (widgetCounter++), removable: false,
|
||||
defaultArea: kNavBar};
|
||||
let widgetWrapper;
|
||||
try {
|
||||
widgetWrapper = CustomizableUI.createWidget(spec);
|
||||
} catch (ex) {
|
||||
ok(false, "Creating a non-removable widget with a default area should not throw.");
|
||||
return;
|
||||
}
|
||||
|
||||
let placement = CustomizableUI.getPlacementOfWidget(spec.id);
|
||||
ok(placement, "Widget should be placed.");
|
||||
is(placement.area, kNavBar, "Widget should be in navbar");
|
||||
let singleWrapper = widgetWrapper.forWindow(window);
|
||||
ok(singleWrapper, "Widget should exist in window.");
|
||||
ok(singleWrapper.node, "Widget node should exist in window.");
|
||||
let expectedParent = CustomizableUI.getCustomizeTargetForArea(kNavBar, window);
|
||||
is(singleWrapper.node.parentNode, expectedParent, "Widget should be in navbar.");
|
||||
|
||||
let otherWin = yield openAndLoadWindow(true);
|
||||
placement = CustomizableUI.getPlacementOfWidget(spec.id);
|
||||
ok(placement, "Widget should be placed.");
|
||||
is(placement && placement.area, kNavBar, "Widget should be in navbar");
|
||||
|
||||
singleWrapper = widgetWrapper.forWindow(otherWin);
|
||||
ok(singleWrapper, "Widget should exist in other window.");
|
||||
if (singleWrapper) {
|
||||
ok(singleWrapper.node, "Widget node should exist in other window.");
|
||||
if (singleWrapper.node) {
|
||||
let expectedParent = CustomizableUI.getCustomizeTargetForArea(kNavBar, otherWin);
|
||||
is(singleWrapper.node.parentNode, expectedParent,
|
||||
"Widget should be in navbar in other window.");
|
||||
}
|
||||
}
|
||||
otherWin.close();
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
function asyncCleanup() {
|
||||
yield resetCustomization();
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
removeCustomToolbars();
|
||||
}
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
registerCleanupFunction(cleanup);
|
||||
runTests(gTests, asyncCleanup);
|
||||
}
|
||||
|
||||
|
|
@ -31,3 +31,4 @@ support-files =
|
|||
[browser_toolbar_tooltip.js]
|
||||
[browser_toolbar_webconsole_errors_count.js]
|
||||
[browser_spectrum.js]
|
||||
[browser_csstransformpreview.js]
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that the spectrum color picker works correctly
|
||||
|
||||
const TEST_URI = "data:text/html;charset=utf-8,<div></div>";
|
||||
const {CSSTransformPreviewer} = devtools.require("devtools/shared/widgets/CSSTransformPreviewer");
|
||||
|
||||
let doc, root;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
addTab(TEST_URI, () => {
|
||||
doc = content.document;
|
||||
root = doc.querySelector("div");
|
||||
startTests();
|
||||
});
|
||||
}
|
||||
|
||||
function endTests() {
|
||||
doc = root = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
||||
function startTests() {
|
||||
testCreateAndDestroyShouldAppendAndRemoveElements();
|
||||
}
|
||||
|
||||
function testCreateAndDestroyShouldAppendAndRemoveElements() {
|
||||
ok(root, "We have the root node to append the preview to");
|
||||
is(root.childElementCount, 0, "Root node is empty");
|
||||
|
||||
let p = new CSSTransformPreviewer(root);
|
||||
p.preview("matrix(1, -0.2, 0, 1, 0, 0)");
|
||||
ok(root.childElementCount > 0, "Preview has appended elements");
|
||||
ok(root.querySelector("canvas"), "Canvas preview element is here");
|
||||
|
||||
p.destroy();
|
||||
is(root.childElementCount, 0, "Destroying preview removed all nodes");
|
||||
|
||||
testCanvasDimensionIsConstrainedByMaxDim();
|
||||
}
|
||||
|
||||
function testCanvasDimensionIsConstrainedByMaxDim() {
|
||||
let p = new CSSTransformPreviewer(root);
|
||||
p.MAX_DIM = 500;
|
||||
p.preview("scale(1)", "center", 1000, 1000);
|
||||
|
||||
let canvas = root.querySelector("canvas");
|
||||
is(canvas.width, 500, "Canvas width is correct");
|
||||
is(canvas.height, 500, "Canvas height is correct");
|
||||
|
||||
p.destroy();
|
||||
|
||||
testCallingPreviewSeveralTimesReusesTheSameCanvas();
|
||||
}
|
||||
|
||||
function testCallingPreviewSeveralTimesReusesTheSameCanvas() {
|
||||
let p = new CSSTransformPreviewer(root);
|
||||
|
||||
p.preview("scale(1)", "center", 1000, 1000);
|
||||
let canvas = root.querySelector("canvas");
|
||||
|
||||
p.preview("rotate(90deg)");
|
||||
let canvases = root.querySelectorAll("canvas");
|
||||
is(canvases.length, 1, "Still one canvas element");
|
||||
is(canvases[0], canvas, "Still the same canvas element");
|
||||
p.destroy();
|
||||
|
||||
testCanvasDimensionAreCorrect();
|
||||
}
|
||||
|
||||
function testCanvasDimensionAreCorrect() {
|
||||
// Only test a few simple transformations
|
||||
let p = new CSSTransformPreviewer(root);
|
||||
|
||||
// Make sure we have a square
|
||||
let w = 200, h = w;
|
||||
p.MAX_DIM = w;
|
||||
|
||||
// We can't test the content of the canvas here, just that, given a max width
|
||||
// the aspect ratio of the canvas seems correct.
|
||||
|
||||
// Translate a square by its width, should be a rectangle
|
||||
p.preview("translateX(200px)", "center", w, h);
|
||||
let canvas = root.querySelector("canvas");
|
||||
is(canvas.width, w, "width is correct");
|
||||
is(canvas.height, h/2, "height is half of the width");
|
||||
|
||||
// Rotate on the top right corner, should be a rectangle
|
||||
p.preview("rotate(-90deg)", "top right", w, h);
|
||||
is(canvas.width, w, "width is correct");
|
||||
is(canvas.height, h/2, "height is half of the width");
|
||||
|
||||
// Rotate on the bottom left corner, should be a rectangle
|
||||
p.preview("rotate(90deg)", "top right", w, h);
|
||||
is(canvas.width, w/2, "width is half of the height");
|
||||
is(canvas.height, h, "height is correct");
|
||||
|
||||
// Scale from center, should still be a square
|
||||
p.preview("scale(2)", "center", w, h);
|
||||
is(canvas.width, w, "width is correct");
|
||||
is(canvas.height, h, "height is correct");
|
||||
|
||||
// Skew from center, 45deg, should be a rectangle
|
||||
p.preview("skew(45deg)", "center", w, h);
|
||||
is(canvas.width, w, "width is correct");
|
||||
is(canvas.height, h/2, "height is half of the height");
|
||||
|
||||
p.destroy();
|
||||
|
||||
testPreviewingInvalidTransformReturnsFalse();
|
||||
}
|
||||
|
||||
function testPreviewingInvalidTransformReturnsFalse() {
|
||||
let p = new CSSTransformPreviewer(root);
|
||||
ok(!p.preview("veryWow(muchPx) suchTransform(soDeg)"), "Returned false for invalid transform");
|
||||
ok(!p.preview("rotae(3deg)"), "Returned false for invalid transform");
|
||||
|
||||
// Verify the canvas is empty by checking the image data
|
||||
let canvas = root.querySelector("canvas"), ctx = canvas.getContext("2d");
|
||||
let data = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
|
||||
for (let i = 0, n = data.length; i < n; i += 4) {
|
||||
// Let's not log 250*250*4 asserts! Instead, just log when it fails
|
||||
let red = data[i];
|
||||
let green = data[i + 1];
|
||||
let blue = data[i + 2];
|
||||
let alpha = data[i + 3];
|
||||
if (red !== 0 || green !== 0 || blue !== 0 || alpha !== 0) {
|
||||
ok(false, "Image data is not empty after an invalid transformed was previewed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
is(p.preview("translateX(30px)"), true, "Returned true for a valid transform");
|
||||
endTests();
|
||||
}
|
|
@ -0,0 +1,389 @@
|
|||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* The CSSTransformPreview module displays, using a <canvas> a rectangle, with
|
||||
* a given width and height and its transformed version, given a css transform
|
||||
* property and origin. It also displays arrows from/to each corner.
|
||||
*
|
||||
* It is useful to visualize how a css transform affected an element. It can
|
||||
* help debug tricky transformations. It is used today in a tooltip, and this
|
||||
* tooltip is shown when hovering over a css transform declaration in the rule
|
||||
* and computed view panels.
|
||||
*
|
||||
* TODO: For now, it multiplies matrices itself to calculate the coordinates of
|
||||
* the transformed box, but that should be removed as soon as we can get access
|
||||
* to getQuads().
|
||||
*/
|
||||
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
/**
|
||||
* The TransformPreview needs an element to output a canvas tag.
|
||||
*
|
||||
* Usage example:
|
||||
*
|
||||
* let t = new CSSTransformPreviewer(myRootElement);
|
||||
* t.preview("rotate(45deg)", "top left", 200, 400);
|
||||
* t.preview("skew(19deg)", "center", 100, 500);
|
||||
* t.preview("matrix(1, -0.2, 0, 1, 0, 0)");
|
||||
* t.destroy();
|
||||
*
|
||||
* @param {nsIDOMElement} parentEl
|
||||
* Where the canvas will go
|
||||
*/
|
||||
function CSSTransformPreviewer(parentEl) {
|
||||
this.parentEl = parentEl;
|
||||
this.doc = this.parentEl.ownerDocument;
|
||||
this.canvas = null;
|
||||
this.ctx = null;
|
||||
}
|
||||
|
||||
module.exports.CSSTransformPreviewer = CSSTransformPreviewer;
|
||||
|
||||
CSSTransformPreviewer.prototype = {
|
||||
/**
|
||||
* The preview look-and-feel can be changed using these properties
|
||||
*/
|
||||
MAX_DIM: 250,
|
||||
PAD: 5,
|
||||
ORIGINAL_FILL: "#1F303F",
|
||||
ORIGINAL_STROKE: "#B2D8FF",
|
||||
TRANSFORMED_FILL: "rgba(200, 200, 200, .5)",
|
||||
TRANSFORMED_STROKE: "#B2D8FF",
|
||||
ARROW_STROKE: "#329AFF",
|
||||
ORIGIN_STROKE: "#329AFF",
|
||||
ARROW_TIP_HEIGHT: 10,
|
||||
ARROW_TIP_WIDTH: 8,
|
||||
CORNER_SIZE_RATIO: 6,
|
||||
|
||||
/**
|
||||
* Destroy removes the canvas from the parentelement passed in the constructor
|
||||
*/
|
||||
destroy: function() {
|
||||
if (this.canvas) {
|
||||
this.parentEl.removeChild(this.canvas);
|
||||
}
|
||||
if (this._hiddenDiv) {
|
||||
this.parentEl.removeChild(this._hiddenDiv);
|
||||
}
|
||||
this.parentEl = this.canvas = this.ctx = this.doc = null;
|
||||
},
|
||||
|
||||
_createMarkup: function() {
|
||||
this.canvas = this.doc.createElementNS(HTML_NS, "canvas");
|
||||
|
||||
this.canvas.setAttribute("id", "canvas");
|
||||
this.canvas.setAttribute("width", this.MAX_DIM);
|
||||
this.canvas.setAttribute("height", this.MAX_DIM);
|
||||
this.canvas.style.position = "relative";
|
||||
this.parentEl.appendChild(this.canvas);
|
||||
|
||||
this.ctx = this.canvas.getContext("2d");
|
||||
},
|
||||
|
||||
_getComputed: function(name, value, width, height) {
|
||||
if (!this._hiddenDiv) {
|
||||
// Create a hidden element to apply the style to
|
||||
this._hiddenDiv = this.doc.createElementNS(HTML_NS, "div");
|
||||
this._hiddenDiv.style.visibility = "hidden";
|
||||
this._hiddenDiv.style.position = "absolute";
|
||||
this.parentEl.appendChild(this._hiddenDiv);
|
||||
}
|
||||
|
||||
// Camelcase the name
|
||||
name = name.replace(/-([a-z]{1})/g, (m, letter) => letter.toUpperCase());
|
||||
|
||||
// Apply width and height to make sure computation is made correctly
|
||||
this._hiddenDiv.style.width = width + "px";
|
||||
this._hiddenDiv.style.height = height + "px";
|
||||
|
||||
// Show the hidden div, apply the style, read the computed style, hide the
|
||||
// hidden div again
|
||||
this._hiddenDiv.style.display = "block";
|
||||
this._hiddenDiv.style[name] = value;
|
||||
let computed = this.doc.defaultView.getComputedStyle(this._hiddenDiv);
|
||||
let computedValue = computed[name];
|
||||
this._hiddenDiv.style.display = "none";
|
||||
|
||||
return computedValue;
|
||||
},
|
||||
|
||||
_getMatrixFromTransformString: function(transformStr) {
|
||||
let matrix = transformStr.substring(0, transformStr.length - 1).
|
||||
substring(transformStr.indexOf("(") + 1).split(",");
|
||||
|
||||
matrix.forEach(function(value, index) {
|
||||
matrix[index] = parseFloat(value, 10);
|
||||
});
|
||||
|
||||
let transformMatrix = null;
|
||||
|
||||
if (matrix.length === 6) {
|
||||
// 2d transform
|
||||
transformMatrix = [
|
||||
[matrix[0], matrix[2], matrix[4], 0],
|
||||
[matrix[1], matrix[3], matrix[5], 0],
|
||||
[0, 0, 1, 0],
|
||||
[0, 0, 0, 1]
|
||||
];
|
||||
} else {
|
||||
// 3d transform
|
||||
transformMatrix = [
|
||||
[matrix[0], matrix[4], matrix[8], matrix[12]],
|
||||
[matrix[1], matrix[5], matrix[9], matrix[13]],
|
||||
[matrix[2], matrix[6], matrix[10], matrix[14]],
|
||||
[matrix[3], matrix[7], matrix[11], matrix[15]]
|
||||
];
|
||||
}
|
||||
|
||||
return transformMatrix;
|
||||
},
|
||||
|
||||
_getOriginFromOriginString: function(originStr) {
|
||||
let offsets = originStr.split(" ");
|
||||
offsets.forEach(function(item, index) {
|
||||
offsets[index] = parseInt(item, 10);
|
||||
});
|
||||
|
||||
return offsets;
|
||||
},
|
||||
|
||||
_multiply: function(m1, m2) {
|
||||
let m = [];
|
||||
for (let m1Line = 0; m1Line < m1.length; m1Line++) {
|
||||
m[m1Line] = 0;
|
||||
for (let m2Col = 0; m2Col < m2.length; m2Col++) {
|
||||
m[m1Line] += m1[m1Line][m2Col] * m2[m2Col];
|
||||
}
|
||||
}
|
||||
return [m[0], m[1]];
|
||||
},
|
||||
|
||||
_getTransformedPoint: function(matrix, point, origin) {
|
||||
let pointMatrix = [point[0] - origin[0], point[1] - origin[1], 1, 1];
|
||||
return this._multiply(matrix, pointMatrix);
|
||||
},
|
||||
|
||||
_getTransformedPoints: function(matrix, rect, origin) {
|
||||
return rect.map(point => {
|
||||
let tPoint = this._getTransformedPoint(matrix, [point[0], point[1]], origin);
|
||||
return [tPoint[0] + origin[0], tPoint[1] + origin[1]];
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* For canvas to avoid anti-aliasing
|
||||
*/
|
||||
_round: x => Math.round(x) + .5,
|
||||
|
||||
_drawShape: function(points, fillStyle, strokeStyle) {
|
||||
this.ctx.save();
|
||||
|
||||
this.ctx.lineWidth = 1;
|
||||
this.ctx.strokeStyle = strokeStyle;
|
||||
this.ctx.fillStyle = fillStyle;
|
||||
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(this._round(points[0][0]), this._round(points[0][1]));
|
||||
for (var i = 1; i < points.length; i++) {
|
||||
this.ctx.lineTo(this._round(points[i][0]), this._round(points[i][1]));
|
||||
}
|
||||
this.ctx.lineTo(this._round(points[0][0]), this._round(points[0][1]));
|
||||
this.ctx.fill();
|
||||
this.ctx.stroke();
|
||||
|
||||
this.ctx.restore();
|
||||
},
|
||||
|
||||
_drawArrow: function(x1, y1, x2, y2) {
|
||||
// do not draw if the line is too small
|
||||
if (Math.abs(x2-x1) < 20 && Math.abs(y2-y1) < 20) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.ctx.save();
|
||||
|
||||
this.ctx.strokeStyle = this.ARROW_STROKE;
|
||||
this.ctx.fillStyle = this.ARROW_STROKE;
|
||||
this.ctx.lineWidth = 1;
|
||||
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(this._round(x1), this._round(y1));
|
||||
this.ctx.lineTo(this._round(x2), this._round(y2));
|
||||
this.ctx.stroke();
|
||||
|
||||
this.ctx.beginPath();
|
||||
this.ctx.translate(x2, y2);
|
||||
let radians = Math.atan((y1 - y2) / (x1 - x2));
|
||||
radians += ((x1 >= x2) ? -90 : 90) * Math.PI / 180;
|
||||
this.ctx.rotate(radians);
|
||||
this.ctx.moveTo(0, 0);
|
||||
this.ctx.lineTo(this.ARROW_TIP_WIDTH / 2, this.ARROW_TIP_HEIGHT);
|
||||
this.ctx.lineTo(-this.ARROW_TIP_WIDTH / 2, this.ARROW_TIP_HEIGHT);
|
||||
this.ctx.closePath();
|
||||
this.ctx.fill();
|
||||
|
||||
this.ctx.restore();
|
||||
},
|
||||
|
||||
_drawOrigin: function(x, y) {
|
||||
this.ctx.save();
|
||||
|
||||
this.ctx.strokeStyle = this.ORIGIN_STROKE;
|
||||
this.ctx.fillStyle = this.ORIGIN_STROKE;
|
||||
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(x, y, 4, 0, 2 * Math.PI, false);
|
||||
this.ctx.stroke();
|
||||
this.ctx.fill();
|
||||
|
||||
this.ctx.restore();
|
||||
},
|
||||
|
||||
/**
|
||||
* Computes the largest width and height of all the given shapes and changes
|
||||
* all of the shapes' points (by reference) so they fit into the configured
|
||||
* MAX_DIM - 2*PAD area.
|
||||
* @return {Object} A {w, h} giving the size the canvas should be
|
||||
*/
|
||||
_fitAllShapes: function(allShapes) {
|
||||
let allXs = [], allYs = [];
|
||||
for (let shape of allShapes) {
|
||||
for (let point of shape) {
|
||||
allXs.push(point[0]);
|
||||
allYs.push(point[1]);
|
||||
}
|
||||
}
|
||||
let minX = Math.min.apply(Math, allXs);
|
||||
let maxX = Math.max.apply(Math, allXs);
|
||||
let minY = Math.min.apply(Math, allYs);
|
||||
let maxY = Math.max.apply(Math, allYs);
|
||||
|
||||
let spanX = maxX - minX;
|
||||
let spanY = maxY - minY;
|
||||
let isWide = spanX > spanY;
|
||||
|
||||
let cw = isWide ? this.MAX_DIM :
|
||||
this.MAX_DIM * Math.min(spanX, spanY) / Math.max(spanX, spanY);
|
||||
let ch = !isWide ? this.MAX_DIM :
|
||||
this.MAX_DIM * Math.min(spanX, spanY) / Math.max(spanX, spanY);
|
||||
|
||||
let mapX = x => this.PAD + ((cw - 2 * this.PAD) / (maxX - minX)) * (x - minX);
|
||||
let mapY = y => this.PAD + ((ch - 2 * this.PAD) / (maxY - minY)) * (y - minY);
|
||||
|
||||
for (let shape of allShapes) {
|
||||
for (let point of shape) {
|
||||
point[0] = mapX(point[0]);
|
||||
point[1] = mapY(point[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return {w: cw, h: ch};
|
||||
},
|
||||
|
||||
_drawShapes: function(shape, corner, transformed, transformedCorner) {
|
||||
this._drawOriginal(shape);
|
||||
this._drawOriginalCorner(corner);
|
||||
this._drawTransformed(transformed);
|
||||
this._drawTransformedCorner(transformedCorner);
|
||||
},
|
||||
|
||||
_drawOriginal: function(points) {
|
||||
this._drawShape(points, this.ORIGINAL_FILL, this.ORIGINAL_STROKE);
|
||||
},
|
||||
|
||||
_drawTransformed: function(points) {
|
||||
this._drawShape(points, this.TRANSFORMED_FILL, this.TRANSFORMED_STROKE);
|
||||
},
|
||||
|
||||
_drawOriginalCorner: function(points) {
|
||||
this._drawShape(points, this.ORIGINAL_STROKE, this.ORIGINAL_STROKE);
|
||||
},
|
||||
|
||||
_drawTransformedCorner: function(points) {
|
||||
this._drawShape(points, this.TRANSFORMED_STROKE, this.TRANSFORMED_STROKE);
|
||||
},
|
||||
|
||||
_drawArrows: function(shape, transformed) {
|
||||
this._drawArrow(shape[0][0], shape[0][1], transformed[0][0], transformed[0][1]);
|
||||
this._drawArrow(shape[1][0], shape[1][1], transformed[1][0], transformed[1][1]);
|
||||
this._drawArrow(shape[2][0], shape[2][1], transformed[2][0], transformed[2][1]);
|
||||
this._drawArrow(shape[3][0], shape[3][1], transformed[3][0], transformed[3][1]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Draw a transform preview
|
||||
*
|
||||
* @param {String} transform
|
||||
* The css transform value as a string, as typed by the user, as long
|
||||
* as it can be computed by the browser
|
||||
* @param {String} origin
|
||||
* Same as above for the transform-origin value. Defaults to "center"
|
||||
* @param {Number} width
|
||||
* The width of the container. Defaults to 200
|
||||
* @param {Number} height
|
||||
* The height of the container. Defaults to 200
|
||||
* @return {Boolean} Whether or not the preview could be created. Will return
|
||||
* false for instance if the transform is invalid
|
||||
*/
|
||||
preview: function(transform, origin="center", width=200, height=200) {
|
||||
// Create/clear the canvas
|
||||
if (!this.canvas) {
|
||||
this._createMarkup();
|
||||
}
|
||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
|
||||
// Get computed versions of transform and origin
|
||||
transform = this._getComputed("transform", transform, width, height);
|
||||
if (transform && transform !== "none") {
|
||||
origin = this._getComputed("transform-origin", origin, width, height);
|
||||
|
||||
// Get the matrix, origin and width height data for the previewed element
|
||||
let originData = this._getOriginFromOriginString(origin);
|
||||
let matrixData = this._getMatrixFromTransformString(transform);
|
||||
|
||||
// Compute the original box rect and transformed box rect
|
||||
let shapePoints = [
|
||||
[0, 0],
|
||||
[width, 0],
|
||||
[width, height],
|
||||
[0, height]
|
||||
];
|
||||
let transformedPoints = this._getTransformedPoints(matrixData, shapePoints, originData);
|
||||
|
||||
// Do the same for the corner triangle shape
|
||||
let cornerSize = Math.min(shapePoints[2][1] - shapePoints[1][1],
|
||||
shapePoints[1][0] - shapePoints[0][0]) / this.CORNER_SIZE_RATIO;
|
||||
let cornerPoints = [
|
||||
[shapePoints[1][0], shapePoints[1][1]],
|
||||
[shapePoints[1][0], shapePoints[1][1] + cornerSize],
|
||||
[shapePoints[1][0] - cornerSize, shapePoints[1][1]]
|
||||
];
|
||||
let transformedCornerPoints = this._getTransformedPoints(matrixData, cornerPoints, originData);
|
||||
|
||||
// Resize points to fit everything in the canvas
|
||||
let {w, h} = this._fitAllShapes([
|
||||
shapePoints,
|
||||
transformedPoints,
|
||||
cornerPoints,
|
||||
transformedCornerPoints,
|
||||
[originData]
|
||||
]);
|
||||
|
||||
this.canvas.setAttribute("width", w);
|
||||
this.canvas.setAttribute("height", h);
|
||||
|
||||
this._drawShapes(shapePoints, cornerPoints, transformedPoints, transformedCornerPoints)
|
||||
this._drawArrows(shapePoints, transformedPoints);
|
||||
this._drawOrigin(originData[0], originData[1]);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
|
@ -12,6 +12,7 @@ const {Spectrum} = require("devtools/shared/widgets/Spectrum");
|
|||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const {colorUtils} = require("devtools/css-color");
|
||||
const Heritage = require("sdk/core/heritage");
|
||||
const {CSSTransformPreviewer} = require("devtools/shared/widgets/CSSTransformPreviewer");
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
@ -100,7 +101,6 @@ let PanelFactory = {
|
|||
panel.setAttribute("hidden", true);
|
||||
panel.setAttribute("ignorekeys", true);
|
||||
|
||||
// Prevent the click used to close the panel from being consumed
|
||||
panel.setAttribute("consumeoutsideclicks", options.get("consumeOutsideClick"));
|
||||
panel.setAttribute("noautofocus", options.get("noAutoFocus"));
|
||||
panel.setAttribute("type", "arrow");
|
||||
|
@ -229,6 +229,10 @@ Tooltip.prototype = {
|
|||
return this.panel.state !== "closed" && this.panel.state !== "hiding";
|
||||
},
|
||||
|
||||
setSize: function(width, height) {
|
||||
this.panel.sizeTo(width, height);
|
||||
},
|
||||
|
||||
/**
|
||||
* Empty the tooltip's content
|
||||
*/
|
||||
|
@ -304,7 +308,8 @@ Tooltip.prototype = {
|
|||
* The container for all target nodes
|
||||
* @param {Function} targetNodeCb
|
||||
* A function that accepts a node argument and returns true or false
|
||||
* to signify if the tooltip should be shown on that node or not.
|
||||
* (or a promise that resolves or rejects) to signify if the tooltip
|
||||
* should be shown on that node or not.
|
||||
* Additionally, the function receives a second argument which is the
|
||||
* tooltip instance itself, to be used to add/modify the content of the
|
||||
* tooltip if needed. If omitted, the tooltip will be shown everytime.
|
||||
|
@ -312,7 +317,7 @@ Tooltip.prototype = {
|
|||
* An optional delay that will be observed before showing the tooltip.
|
||||
* Defaults to this.defaultShowDelay.
|
||||
*/
|
||||
startTogglingOnHover: function(baseNode, targetNodeCb, showDelay = this.defaultShowDelay) {
|
||||
startTogglingOnHover: function(baseNode, targetNodeCb, showDelay=this.defaultShowDelay) {
|
||||
if (this._basedNode) {
|
||||
this.stopTogglingOnHover();
|
||||
}
|
||||
|
@ -357,7 +362,12 @@ Tooltip.prototype = {
|
|||
},
|
||||
|
||||
_showOnHover: function(target) {
|
||||
if (this._targetNodeCb(target, this)) {
|
||||
let res = this._targetNodeCb(target, this);
|
||||
if (res && res.then) {
|
||||
res.then(() => {
|
||||
this.show(target);
|
||||
});
|
||||
} else if (res) {
|
||||
this.show(target);
|
||||
}
|
||||
},
|
||||
|
@ -527,6 +537,8 @@ Tooltip.prototype = {
|
|||
let w = options.naturalWidth || imgObj.naturalWidth;
|
||||
let h = options.naturalHeight || imgObj.naturalHeight;
|
||||
label.textContent = w + " x " + h;
|
||||
|
||||
this.setSize(vbox.width, vbox.height);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -583,6 +595,54 @@ Tooltip.prototype = {
|
|||
// Put the iframe in the tooltip
|
||||
this.content = iframe;
|
||||
|
||||
return def.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the content of the tooltip to be the result of CSSTransformPreviewer.
|
||||
* Meaning a canvas previewing a css transformation.
|
||||
*
|
||||
* @param {String} transform
|
||||
* The CSS transform value (e.g. "rotate(45deg) translateX(50px)")
|
||||
* @param {PageStyleActor} pageStyle
|
||||
* An instance of the PageStyleActor that will be used to retrieve
|
||||
* computed styles
|
||||
* @param {NodeActor} node
|
||||
* The NodeActor for the currently selected node
|
||||
* @return A promise that resolves when the tooltip content is ready, or
|
||||
* rejects if no transform is provided or is invalid
|
||||
*/
|
||||
setCssTransformContent: function(transform, pageStyle, node) {
|
||||
let def = promise.defer();
|
||||
|
||||
if (transform) {
|
||||
// Look into the computed styles to find the width and height and possibly
|
||||
// the origin if it hadn't been provided
|
||||
pageStyle.getComputed(node, {
|
||||
filter: "user",
|
||||
markMatched: false,
|
||||
onlyMatched: false
|
||||
}).then(styles => {
|
||||
let origin = styles["transform-origin"].value;
|
||||
let width = parseInt(styles["width"].value);
|
||||
let height = parseInt(styles["height"].value);
|
||||
|
||||
let root = this.doc.createElementNS(XHTML_NS, "div");
|
||||
let previewer = new CSSTransformPreviewer(root);
|
||||
this.content = root;
|
||||
if (!previewer.preview(transform, origin, width, height)) {
|
||||
// If the preview didn't work, reject the promise
|
||||
def.reject();
|
||||
} else {
|
||||
// Else, make sure the tooltip has the right size and resolve
|
||||
this.setSize(previewer.canvas.width, previewer.canvas.height);
|
||||
def.resolve();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
def.reject();
|
||||
}
|
||||
|
||||
return def.promise;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -507,21 +507,27 @@ CssHtmlTree.prototype = {
|
|||
*/
|
||||
_buildTooltipContent: function(target)
|
||||
{
|
||||
// If the hovered element is not a property view and is not a background
|
||||
// image, then don't show a tooltip
|
||||
let isPropertyValue = target.classList.contains("property-value");
|
||||
if (!isPropertyValue) {
|
||||
return false;
|
||||
}
|
||||
let propName = target.parentNode.querySelector(".property-name");
|
||||
let isBackgroundImage = propName.textContent === "background-image";
|
||||
if (!isBackgroundImage) {
|
||||
return false;
|
||||
// Test for image url
|
||||
if (target.classList.contains("theme-link")) {
|
||||
let propValue = target.parentNode;
|
||||
let propName = propValue.parentNode.querySelector(".property-name");
|
||||
if (propName.textContent === "background-image") {
|
||||
this.tooltip.setCssBackgroundImageContent(propValue.textContent);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill some content
|
||||
this.tooltip.setCssBackgroundImageContent(target.textContent);
|
||||
return true;
|
||||
// Test for css transform
|
||||
if (target.classList.contains("property-value")) {
|
||||
let def = promise.defer();
|
||||
let propValue = target;
|
||||
let propName = target.parentNode.querySelector(".property-name");
|
||||
if (propName.textContent === "transform") {
|
||||
this.tooltip.setCssTransformContent(propValue.textContent,
|
||||
this.pageStyle, this.viewedElement).then(def.resolve);
|
||||
return def.promise;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -1154,27 +1154,32 @@ CssRuleView.prototype = {
|
|||
* prepare some content for the tooltip
|
||||
*/
|
||||
_buildTooltipContent: function(target) {
|
||||
let isImageHref = target.classList.contains("theme-link") &&
|
||||
target.parentNode.classList.contains("ruleview-propertyvalue");
|
||||
let property = target.textProperty, def = promise.defer(), hasTooltip = false;
|
||||
|
||||
// If the inplace-editor is visible or if this is not a background image
|
||||
// don't show the tooltip
|
||||
if (!isImageHref) {
|
||||
return false;
|
||||
// Test for css transform
|
||||
if (property && property.name === "transform") {
|
||||
this.previewTooltip.setCssTransformContent(property.value, this.pageStyle,
|
||||
this._viewedElement).then(def.resolve);
|
||||
hasTooltip = true;
|
||||
}
|
||||
|
||||
// Retrieve the TextProperty for the hovered element
|
||||
let property = target.parentNode.textProperty;
|
||||
let href = property.rule.domRule.href;
|
||||
// Test for image
|
||||
let isImageHref = target.classList.contains("theme-link") &&
|
||||
target.parentNode.classList.contains("ruleview-propertyvalue");
|
||||
if (isImageHref) {
|
||||
property = target.parentNode.textProperty;
|
||||
this.previewTooltip.setCssBackgroundImageContent(property.value,
|
||||
property.rule.domRule.href);
|
||||
def.resolve();
|
||||
hasTooltip = true;
|
||||
}
|
||||
|
||||
// Fill some content
|
||||
this.previewTooltip.setCssBackgroundImageContent(property.value, href);
|
||||
if (hasTooltip) {
|
||||
this.colorPicker.revert();
|
||||
this.colorPicker.hide();
|
||||
}
|
||||
|
||||
// Hide the color picker tooltip if shown and revert changes
|
||||
this.colorPicker.revert();
|
||||
this.colorPicker.hide();
|
||||
|
||||
return true;
|
||||
return def.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1330,7 +1335,7 @@ CssRuleView.prototype = {
|
|||
/**
|
||||
* Update the highlighted element.
|
||||
*
|
||||
* @param {nsIDOMElement} aElement
|
||||
* @param {NodeActor} aElement
|
||||
* The node whose style rules we'll inspect.
|
||||
*/
|
||||
highlight: function CssRuleView_highlight(aElement)
|
||||
|
|
|
@ -53,6 +53,8 @@ support-files = browser_ruleview_pseudoelement.html
|
|||
[browser_bug913014_matched_expand.js]
|
||||
[browser_bug765105_background_image_tooltip.js]
|
||||
[browser_bug889638_rule_view_color_picker.js]
|
||||
[browser_bug726427_csstransform_tooltip.js]
|
||||
|
||||
[browser_bug940500_rule_view_pick_gradient_color.js]
|
||||
[browser_ruleview_original_source_link.js]
|
||||
support-files =
|
||||
|
|
|
@ -0,0 +1,206 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
let contentDoc;
|
||||
let inspector;
|
||||
let ruleView;
|
||||
let computedView;
|
||||
|
||||
const PAGE_CONTENT = [
|
||||
'<style type="text/css">',
|
||||
' #testElement {',
|
||||
' width: 500px;',
|
||||
' height: 300px;',
|
||||
' background: red;',
|
||||
' transform: skew(16deg);',
|
||||
' }',
|
||||
' .test-element {',
|
||||
' transform-origin: top left;',
|
||||
' transform: rotate(45deg);',
|
||||
' }',
|
||||
' div {',
|
||||
' transform: scaleX(1.5);',
|
||||
' transform-origin: bottom right;',
|
||||
' }',
|
||||
' [attr] {',
|
||||
' }',
|
||||
'</style>',
|
||||
'<div id="testElement" class="test-element" attr="value">transformed element</div>'
|
||||
].join("\n");
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
|
||||
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
|
||||
contentDoc = content.document;
|
||||
waitForFocus(createDocument, content);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,rule view css transform tooltip test";
|
||||
}
|
||||
|
||||
function createDocument() {
|
||||
contentDoc.body.innerHTML = PAGE_CONTENT;
|
||||
|
||||
openRuleView((aInspector, aRuleView) => {
|
||||
inspector = aInspector;
|
||||
ruleView = aRuleView;
|
||||
startTests();
|
||||
});
|
||||
}
|
||||
|
||||
function startTests() {
|
||||
inspector.selection.setNode(contentDoc.querySelector("#testElement"));
|
||||
inspector.once("inspector-updated", testTransformTooltipOnIDSelector);
|
||||
}
|
||||
|
||||
function endTests() {
|
||||
contentDoc = inspector = ruleView = computedView = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
||||
function testTransformTooltipOnIDSelector() {
|
||||
info("Testing that a transform tooltip appears on the #ID rule");
|
||||
|
||||
let panel = ruleView.previewTooltip.panel;
|
||||
ok(panel, "The XUL panel exists for the rule-view preview tooltips");
|
||||
|
||||
let {valueSpan} = getRuleViewProperty("#testElement", "transform");
|
||||
assertTooltipShownOn(ruleView.previewTooltip, valueSpan, () => {
|
||||
// The transform preview is canvas, so there's not much we can test, so for
|
||||
// now, let's just be happy with the fact that the tooltips is shown!
|
||||
ok(true, "Tooltip shown on the transform property of the #ID rule");
|
||||
ruleView.previewTooltip.hide();
|
||||
executeSoon(testTransformTooltipOnClassSelector);
|
||||
});
|
||||
}
|
||||
|
||||
function testTransformTooltipOnClassSelector() {
|
||||
info("Testing that a transform tooltip appears on the .class rule");
|
||||
|
||||
let {valueSpan} = getRuleViewProperty(".test-element", "transform");
|
||||
assertTooltipShownOn(ruleView.previewTooltip, valueSpan, () => {
|
||||
// The transform preview is canvas, so there's not much we can test, so for
|
||||
// now, let's just be happy with the fact that the tooltips is shown!
|
||||
ok(true, "Tooltip shown on the transform property of the .class rule");
|
||||
ruleView.previewTooltip.hide();
|
||||
executeSoon(testTransformTooltipOnTagSelector);
|
||||
});
|
||||
}
|
||||
|
||||
function testTransformTooltipOnTagSelector() {
|
||||
info("Testing that a transform tooltip appears on the tag rule");
|
||||
|
||||
let {valueSpan} = getRuleViewProperty("div", "transform");
|
||||
assertTooltipShownOn(ruleView.previewTooltip, valueSpan, () => {
|
||||
// The transform preview is canvas, so there's not much we can test, so for
|
||||
// now, let's just be happy with the fact that the tooltips is shown!
|
||||
ok(true, "Tooltip shown on the transform property of the tag rule");
|
||||
ruleView.previewTooltip.hide();
|
||||
executeSoon(testTransformTooltipNotShownOnInvalidTransform);
|
||||
});
|
||||
}
|
||||
|
||||
function testTransformTooltipNotShownOnInvalidTransform() {
|
||||
info("Testing that a transform tooltip does not appear for invalid values");
|
||||
|
||||
let ruleEditor;
|
||||
for (let rule of ruleView._elementStyle.rules) {
|
||||
if (rule.matchedSelectors[0] === "[attr]") {
|
||||
ruleEditor = rule.editor;
|
||||
}
|
||||
}
|
||||
ruleEditor.addProperty("transform", "muchTransform(suchAngle)", "");
|
||||
|
||||
let {valueSpan} = getRuleViewProperty("[attr]", "transform");
|
||||
assertTooltipNotShownOn(ruleView.previewTooltip, valueSpan, () => {
|
||||
executeSoon(testTransformTooltipOnComputedView);
|
||||
});
|
||||
}
|
||||
|
||||
function testTransformTooltipOnComputedView() {
|
||||
info("Testing that a transform tooltip appears in the computed view too");
|
||||
|
||||
inspector.sidebar.select("computedview");
|
||||
computedView = inspector.sidebar.getWindowForTab("computedview").computedview.view;
|
||||
let doc = computedView.styleDocument;
|
||||
|
||||
let panel = computedView.tooltip.panel;
|
||||
let {valueSpan} = getComputedViewProperty("transform");
|
||||
|
||||
assertTooltipShownOn(computedView.tooltip, valueSpan, () => {
|
||||
// The transform preview is canvas, so there's not much we can test, so for
|
||||
// now, let's just be happy with the fact that the tooltips is shown!
|
||||
ok(true, "Tooltip shown on the computed transform property");
|
||||
computedView.tooltip.hide();
|
||||
executeSoon(endTests);
|
||||
});
|
||||
}
|
||||
|
||||
function assertTooltipShownOn(tooltip, element, cb) {
|
||||
// If there is indeed a show-on-hover on element, the xul panel will be shown
|
||||
tooltip.panel.addEventListener("popupshown", function shown() {
|
||||
tooltip.panel.removeEventListener("popupshown", shown, true);
|
||||
cb();
|
||||
}, true);
|
||||
tooltip._showOnHover(element);
|
||||
}
|
||||
|
||||
function assertTooltipNotShownOn(tooltip, element, cb) {
|
||||
// The only way to make sure the tooltip is not shown is try and show it, wait
|
||||
// for a given amount of time, and then check if it's shown or not
|
||||
tooltip._showOnHover(element);
|
||||
setTimeout(() => {
|
||||
ok(!tooltip.isShown(), "The tooltip did not appear on hover of the element");
|
||||
cb();
|
||||
}, tooltip.defaultShowDelay + 100);
|
||||
}
|
||||
|
||||
function getRule(selectorText) {
|
||||
let rule;
|
||||
|
||||
[].forEach.call(ruleView.doc.querySelectorAll(".ruleview-rule"), aRule => {
|
||||
let selector = aRule.querySelector(".ruleview-selector-matched");
|
||||
if (selector && selector.textContent === selectorText) {
|
||||
rule = aRule;
|
||||
}
|
||||
});
|
||||
|
||||
return rule;
|
||||
}
|
||||
|
||||
function getRuleViewProperty(selectorText, propertyName) {
|
||||
let prop;
|
||||
|
||||
let rule = getRule(selectorText);
|
||||
if (rule) {
|
||||
// Look for the propertyName in that rule element
|
||||
[].forEach.call(rule.querySelectorAll(".ruleview-property"), property => {
|
||||
let nameSpan = property.querySelector(".ruleview-propertyname");
|
||||
let valueSpan = property.querySelector(".ruleview-propertyvalue");
|
||||
|
||||
if (nameSpan.textContent === propertyName) {
|
||||
prop = {nameSpan: nameSpan, valueSpan: valueSpan};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
function getComputedViewProperty(name) {
|
||||
let prop;
|
||||
[].forEach.call(computedView.styleDocument.querySelectorAll(".property-view"), property => {
|
||||
let nameSpan = property.querySelector(".property-name");
|
||||
let valueSpan = property.querySelector(".property-value");
|
||||
|
||||
if (nameSpan.textContent === name) {
|
||||
prop = {nameSpan: nameSpan, valueSpan: valueSpan};
|
||||
}
|
||||
});
|
||||
return prop;
|
||||
}
|
|
@ -142,8 +142,9 @@ function testComputedView() {
|
|||
|
||||
let panel = computedView.tooltip.panel;
|
||||
let {valueSpan} = getComputedViewProperty("background-image");
|
||||
let uriSpan = valueSpan.querySelector(".theme-link");
|
||||
|
||||
assertTooltipShownOn(computedView.tooltip, valueSpan, () => {
|
||||
assertTooltipShownOn(computedView.tooltip, uriSpan, () => {
|
||||
let images = panel.getElementsByTagName("image");
|
||||
is(images.length, 1, "Tooltip contains an image");
|
||||
ok(images[0].src === "chrome://global/skin/icons/warning-64.png");
|
||||
|
|
|
@ -3,6 +3,17 @@
|
|||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<!ENTITY aboutDialog.title "About &brandFullName;">
|
||||
|
||||
<!-- LOCALIZATION NOTE update.applyButton.*, update.upgradeButton.*):
|
||||
# Only one button is present at a time.
|
||||
# The button when displayed is located directly under the Firefox version in
|
||||
# the about dialog (see bug 596813 for screenshots).
|
||||
-->
|
||||
<!ENTITY update.updateButton.label "Restart to Update">
|
||||
<!ENTITY update.updateButton.accesskey "R">
|
||||
<!ENTITY update.applyButtonBillboard.label "Apply Update…">
|
||||
<!ENTITY update.applyButtonBillboard.accesskey "A">
|
||||
|
||||
|
||||
<!-- LOCALIZATION NOTE (warningDesc.version): This is a warning about the experimental nature of Nightly and Aurora builds. It is only shown in those versions. -->
|
||||
<!ENTITY warningDesc.version "&brandShortName; is experimental and may be unstable.">
|
||||
<!-- LOCALIZATION NOTE (warningDesc.telemetryDesc): This is a notification that Nightly/Aurora builds automatically send Telemetry data back to Mozilla. It is only shown in those versions. "It" refers to brandShortName. -->
|
||||
|
@ -41,8 +52,6 @@
|
|||
|
||||
<!-- LOCALIZATION NOTE (update.checkingForUpdates): try to make the localized text short (see bug 596813 for screenshots). -->
|
||||
<!ENTITY update.checkingForUpdates "Checking for updates…">
|
||||
<!-- LOCALIZATION NOTE (update.checkingAddonCompat): try to make the localized text short (see bug 596813 for screenshots). -->
|
||||
<!ENTITY update.checkingAddonCompat "Checking Add-on compatibility…">
|
||||
<!-- LOCALIZATION NOTE (update.noUpdatesFound): try to make the localized text short (see bug 596813 for screenshots). -->
|
||||
<!ENTITY update.noUpdatesFound "&brandShortName; is up to date">
|
||||
<!-- LOCALIZATION NOTE (update.adminDisabled): try to make the localized text short (see bug 596813 for screenshots). -->
|
||||
|
|
|
@ -191,24 +191,10 @@ sanitizeEverythingWarning2=All history will be cleared.
|
|||
# provided that the user has modified the default set of history items to clear.
|
||||
sanitizeSelectedWarning=All selected items will be cleared.
|
||||
|
||||
# Check for Updates in the About Dialog - button labels and accesskeys
|
||||
# LOCALIZATION NOTE - all of the following update buttons labels will only be
|
||||
# displayed one at a time. So, if a button is displayed nothing else will
|
||||
# be displayed alongside of the button. The button when displayed is located
|
||||
# directly under the Firefox version in the about dialog (see bug 596813 for
|
||||
# screenshots).
|
||||
update.checkInsideButton.label=Check for Updates
|
||||
update.checkInsideButton.accesskey=C
|
||||
update.resumeButton.label=Resume Downloading %S…
|
||||
update.resumeButton.accesskey=D
|
||||
update.openUpdateUI.applyButton.label=Apply Update…
|
||||
update.openUpdateUI.applyButton.accesskey=A
|
||||
update.restart.updateButton.label=Restart to Update
|
||||
update.restart.updateButton.accesskey=R
|
||||
update.openUpdateUI.upgradeButton.label=Upgrade Now…
|
||||
update.openUpdateUI.upgradeButton.accesskey=U
|
||||
update.restart.upgradeButton.label=Upgrade Now
|
||||
update.restart.upgradeButton.accesskey=U
|
||||
# LOCALIZATION NOTE (downloadAndInstallButton.label): %S is replaced by the
|
||||
# version of the update: "Update to 28.0".
|
||||
update.downloadAndInstallButton.label=Update to %S
|
||||
update.downloadAndInstallButton.accesskey=U
|
||||
|
||||
# RSS Pretty Print
|
||||
feedShowFeedNew=Subscribe to '%S'…
|
||||
|
|
|
@ -32,6 +32,11 @@ static const int kOpusSamplingRate = 48000;
|
|||
// The duration of an Opus frame, and it must be 2.5, 5, 10, 20, 40 or 60 ms.
|
||||
static const int kFrameDurationMs = 20;
|
||||
|
||||
// The supported sampling rate of input signal (Hz),
|
||||
// must be one of the following. Will resampled to 48kHz otherwise.
|
||||
static const int kOpusSupportedInputSamplingRates[5] =
|
||||
{8000, 12000, 16000, 24000, 48000};
|
||||
|
||||
namespace {
|
||||
|
||||
// An endian-neutral serialization of integers. Serializing T in little endian
|
||||
|
@ -146,12 +151,14 @@ OpusTrackEncoder::Init(int aChannels, int aSamplingRate)
|
|||
if (aChannels <= 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
// The granule position is required to be incremented at a rate of 48KHz, and
|
||||
// it is simply calculated as |granulepos = samples * (48000/source_rate)|,
|
||||
// that is, the source sampling rate must divide 48000 evenly.
|
||||
// If this constraint is not satisfied, we resample the input to 48kHz.
|
||||
if (!((aSamplingRate >= 8000) && (kOpusSamplingRate / aSamplingRate) *
|
||||
aSamplingRate == kOpusSamplingRate)) {
|
||||
|
||||
// According to www.opus-codec.org, creating an opus encoder requires the
|
||||
// sampling rate of source signal be one of 8000, 12000, 16000, 24000, or
|
||||
// 48000. If this constraint is not satisfied, we resample the input to 48kHz.
|
||||
nsTArray<int> supportedSamplingRates;
|
||||
supportedSamplingRates.AppendElements(kOpusSupportedInputSamplingRates,
|
||||
MOZ_ARRAY_LENGTH(kOpusSupportedInputSamplingRates));
|
||||
if (!supportedSamplingRates.Contains(aSamplingRate)) {
|
||||
int error;
|
||||
mResampler = speex_resampler_init(mChannels,
|
||||
aSamplingRate,
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "WebMBufferedParser.h"
|
||||
#include "mozilla/dom/TimeRanges.h"
|
||||
#include "VorbisUtils.h"
|
||||
#include <algorithm>
|
||||
|
||||
#define VPX_DONT_DEFINE_STDINT_TYPES
|
||||
#include "vpx/vp8dx.h"
|
||||
|
@ -147,6 +148,7 @@ WebMReader::WebMReader(AbstractMediaDecoder* aDecoder)
|
|||
mOpusParser(nullptr),
|
||||
mOpusDecoder(nullptr),
|
||||
mSkip(0),
|
||||
mSeekPreroll(0),
|
||||
#endif
|
||||
mVideoTrack(0),
|
||||
mAudioTrack(0),
|
||||
|
@ -218,10 +220,20 @@ nsresult WebMReader::ResetDecode()
|
|||
res = NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Ignore failed results from vorbis_synthesis_restart. They
|
||||
// aren't fatal and it fails when ResetDecode is called at a
|
||||
// time when no vorbis data has been read.
|
||||
vorbis_synthesis_restart(&mVorbisDsp);
|
||||
if (mAudioCodec == NESTEGG_CODEC_VORBIS) {
|
||||
// Ignore failed results from vorbis_synthesis_restart. They
|
||||
// aren't fatal and it fails when ResetDecode is called at a
|
||||
// time when no vorbis data has been read.
|
||||
vorbis_synthesis_restart(&mVorbisDsp);
|
||||
#ifdef MOZ_OPUS
|
||||
} else if (mAudioCodec == NESTEGG_CODEC_OPUS) {
|
||||
if (mOpusDecoder) {
|
||||
// Reset the decoder.
|
||||
opus_multistream_decoder_ctl(mOpusDecoder, OPUS_RESET_STATE);
|
||||
mSkip = mOpusParser->mPreSkip;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
mVideoPackets.Reset();
|
||||
mAudioPackets.Reset();
|
||||
|
@ -431,6 +443,8 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
|
|||
|
||||
mInfo.mAudio.mChannels = mOpusParser->mChannels;
|
||||
mInfo.mAudio.mChannels = mInfo.mAudio.mChannels > 2 ? 2 : mInfo.mAudio.mChannels;
|
||||
mChannels = mInfo.mAudio.mChannels;
|
||||
mSeekPreroll = params.seek_preroll;
|
||||
#endif
|
||||
} else {
|
||||
Cleanup();
|
||||
|
@ -627,6 +641,7 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset)
|
|||
if (ret < 0)
|
||||
return false;
|
||||
NS_ASSERTION(ret == frames, "Opus decoded too few audio samples");
|
||||
CheckedInt64 startTime = tstamp_usecs;
|
||||
|
||||
// Trim the initial frames while the decoder is settling.
|
||||
if (mSkip > 0) {
|
||||
|
@ -643,7 +658,7 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset)
|
|||
nsAutoArrayPtr<AudioDataValue> trimBuffer(new AudioDataValue[samples]);
|
||||
for (int i = 0; i < samples; i++)
|
||||
trimBuffer[i] = buffer[skipFrames*channels + i];
|
||||
|
||||
startTime = startTime + FramesToUsecs(skipFrames, rate);
|
||||
frames = keepFrames;
|
||||
buffer = trimBuffer;
|
||||
|
||||
|
@ -708,14 +723,12 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset)
|
|||
NS_WARNING("Int overflow converting WebM audio duration");
|
||||
return false;
|
||||
}
|
||||
|
||||
CheckedInt64 time = tstamp_usecs;
|
||||
CheckedInt64 time = startTime - (mCodecDelay / NS_PER_USEC);
|
||||
if (!time.isValid()) {
|
||||
NS_WARNING("Int overflow adding total_duration and tstamp_usecs");
|
||||
NS_WARNING("Int overflow shifting tstamp by codec delay");
|
||||
nestegg_free_packet(aPacket);
|
||||
return false;
|
||||
};
|
||||
|
||||
AudioQueue().Push(new AudioData(mDecoder->GetResource()->Tell(),
|
||||
time.value(),
|
||||
duration.value(),
|
||||
|
@ -982,7 +995,11 @@ nsresult WebMReader::Seek(int64_t aTarget, int64_t aStartTime, int64_t aEndTime,
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
uint32_t trackToSeek = mHasVideo ? mVideoTrack : mAudioTrack;
|
||||
int r = nestegg_track_seek(mContext, trackToSeek, aTarget * NS_PER_USEC);
|
||||
uint64_t target = aTarget * NS_PER_USEC;
|
||||
if (mSeekPreroll) {
|
||||
target = std::max(static_cast<uint64_t>(aStartTime * NS_PER_USEC), target - mSeekPreroll);
|
||||
}
|
||||
int r = nestegg_track_seek(mContext, trackToSeek, target);
|
||||
if (r != 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
|
|
@ -197,6 +197,7 @@ private:
|
|||
nsAutoPtr<OpusParser> mOpusParser;
|
||||
OpusMSDecoder *mOpusDecoder;
|
||||
int mSkip; // Number of samples left to trim before playback.
|
||||
uint64_t mSeekPreroll; // Number of nanoseconds that must be discarded after seeking.
|
||||
#endif
|
||||
|
||||
// Queue of video and audio packets that have been read but not decoded. These
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
|
||||
int
|
||||
AppendHeaderName(uint8_t* aRetBuf, const char* aName, int aLength)
|
||||
AppendHeaderName(uint8_t* aRetBuf, int aBufferSize, const char* aName,
|
||||
int aLength)
|
||||
{
|
||||
int headerLength = aLength + 3;
|
||||
|
||||
|
@ -17,13 +18,15 @@ AppendHeaderName(uint8_t* aRetBuf, const char* aName, int aLength)
|
|||
aRetBuf[1] = (headerLength & 0xFF00) >> 8;
|
||||
aRetBuf[2] = headerLength & 0x00FF;
|
||||
|
||||
memcpy(&aRetBuf[3], aName, aLength);
|
||||
memcpy(&aRetBuf[3], aName, (aLength < aBufferSize - 3)? aLength
|
||||
: aBufferSize - 3);
|
||||
|
||||
return headerLength;
|
||||
}
|
||||
|
||||
int
|
||||
AppendHeaderBody(uint8_t* aRetBuf, uint8_t* aData, int aLength)
|
||||
AppendHeaderBody(uint8_t* aRetBuf, int aBufferSize, const uint8_t* aData,
|
||||
int aLength)
|
||||
{
|
||||
int headerLength = aLength + 3;
|
||||
|
||||
|
@ -31,7 +34,8 @@ AppendHeaderBody(uint8_t* aRetBuf, uint8_t* aData, int aLength)
|
|||
aRetBuf[1] = (headerLength & 0xFF00) >> 8;
|
||||
aRetBuf[2] = headerLength & 0x00FF;
|
||||
|
||||
memcpy(&aRetBuf[3], aData, aLength);
|
||||
memcpy(&aRetBuf[3], aData, (aLength < aBufferSize - 3)? aLength
|
||||
: aBufferSize - 3);
|
||||
|
||||
return headerLength;
|
||||
}
|
||||
|
|
|
@ -251,8 +251,10 @@ private:
|
|||
nsTArray<nsAutoPtr<ObexHeader> > mHeaders;
|
||||
};
|
||||
|
||||
int AppendHeaderName(uint8_t* aRetBuf, const char* aName, int aLength);
|
||||
int AppendHeaderBody(uint8_t* aRetBuf, uint8_t* aData, int aLength);
|
||||
int AppendHeaderName(uint8_t* aRetBuf, int aBufferSize, const char* aName,
|
||||
int aLength);
|
||||
int AppendHeaderBody(uint8_t* aRetBuf, int aBufferSize, const uint8_t* aData,
|
||||
int aLength);
|
||||
int AppendHeaderEndOfBody(uint8_t* aRetBuf);
|
||||
int AppendHeaderLength(uint8_t* aRetBuf, int aObjectLength);
|
||||
int AppendHeaderConnectionId(uint8_t* aRetBuf, int aConnectionId);
|
||||
|
|
|
@ -46,6 +46,13 @@ static const uint32_t kUpdateProgressBase = 50 * 1024;
|
|||
*/
|
||||
static const uint32_t kPutRequestHeaderSize = 6;
|
||||
|
||||
/*
|
||||
* The format of the appended header of an PUT request is
|
||||
* [headerId:1][header length:4]
|
||||
* P.S. Length of name header is 4 since unicode is 2 bytes per char.
|
||||
*/
|
||||
static const uint32_t kPutRequestAppendHeaderSize = 5;
|
||||
|
||||
StaticRefPtr<BluetoothOppManager> sBluetoothOppManager;
|
||||
static bool sInShutdown = false;
|
||||
}
|
||||
|
@ -970,6 +977,15 @@ BluetoothOppManager::ClientDataHandler(UnixSocketRawData* aMessage)
|
|||
mRemoteMaxPacketLength =
|
||||
(((int)(aMessage->mData[5]) << 8) | aMessage->mData[6]);
|
||||
|
||||
// The length of file name exceeds maximum length.
|
||||
int fileNameByteLen = (mFileName.Length() + 1) * 2;
|
||||
int headerLen = kPutRequestHeaderSize + kPutRequestAppendHeaderSize;
|
||||
if (fileNameByteLen > mRemoteMaxPacketLength - headerLen) {
|
||||
BT_WARNING("The length of file name is aberrant.");
|
||||
SendDisconnectRequest();
|
||||
return;
|
||||
}
|
||||
|
||||
SendPutHeaderRequest(mFileName, mFileLength);
|
||||
} else if (mLastCommand == ObexRequestCode::Put) {
|
||||
if (mWaitingToSendPutFinal) {
|
||||
|
@ -1056,7 +1072,8 @@ BluetoothOppManager::SendPutHeaderRequest(const nsAString& aFileName,
|
|||
fileName[len * 2 + 1] = 0x00;
|
||||
|
||||
int index = 3;
|
||||
index += AppendHeaderName(&req[index], (char*)fileName, (len + 1) * 2);
|
||||
index += AppendHeaderName(&req[index], mRemoteMaxPacketLength - index,
|
||||
(char*)fileName, (len + 1) * 2);
|
||||
index += AppendHeaderLength(&req[index], aFileSize);
|
||||
|
||||
SendObexData(req, ObexRequestCode::Put, index);
|
||||
|
@ -1069,9 +1086,8 @@ void
|
|||
BluetoothOppManager::SendPutRequest(uint8_t* aFileBody,
|
||||
int aFileBodyLength)
|
||||
{
|
||||
int packetLeftSpace = mRemoteMaxPacketLength - kPutRequestHeaderSize;
|
||||
|
||||
if (!mConnected) return;
|
||||
int packetLeftSpace = mRemoteMaxPacketLength - kPutRequestHeaderSize;
|
||||
if (aFileBodyLength > packetLeftSpace) {
|
||||
BT_WARNING("Not allowed such a small MaxPacketLength value");
|
||||
return;
|
||||
|
@ -1082,7 +1098,8 @@ BluetoothOppManager::SendPutRequest(uint8_t* aFileBody,
|
|||
uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
|
||||
|
||||
int index = 3;
|
||||
index += AppendHeaderBody(&req[index], aFileBody, aFileBodyLength);
|
||||
index += AppendHeaderBody(&req[index], mRemoteMaxPacketLength - index,
|
||||
aFileBody, aFileBodyLength);
|
||||
|
||||
SendObexData(req, ObexRequestCode::Put, index);
|
||||
delete [] req;
|
||||
|
|
|
@ -46,6 +46,13 @@ static const uint32_t kUpdateProgressBase = 50 * 1024;
|
|||
*/
|
||||
static const uint32_t kPutRequestHeaderSize = 6;
|
||||
|
||||
/*
|
||||
* The format of the appended header of an PUT request is
|
||||
* [headerId:1][header length:4]
|
||||
* P.S. Length of name header is 4 since unicode is 2 bytes per char.
|
||||
*/
|
||||
static const uint32_t kPutRequestAppendHeaderSize = 5;
|
||||
|
||||
StaticRefPtr<BluetoothOppManager> sBluetoothOppManager;
|
||||
static bool sInShutdown = false;
|
||||
}
|
||||
|
@ -984,6 +991,15 @@ BluetoothOppManager::ClientDataHandler(UnixSocketRawData* aMessage)
|
|||
mRemoteMaxPacketLength =
|
||||
(((int)(aMessage->mData[5]) << 8) | aMessage->mData[6]);
|
||||
|
||||
// The length of file name exceeds maximum length.
|
||||
int fileNameByteLen = (mFileName.Length() + 1) * 2;
|
||||
int headerLen = kPutRequestHeaderSize + kPutRequestAppendHeaderSize;
|
||||
if (fileNameByteLen > mRemoteMaxPacketLength - headerLen) {
|
||||
BT_WARNING("The length of file name is aberrant.");
|
||||
SendDisconnectRequest();
|
||||
return;
|
||||
}
|
||||
|
||||
SendPutHeaderRequest(mFileName, mFileLength);
|
||||
} else if (mLastCommand == ObexRequestCode::Put) {
|
||||
if (mWaitingToSendPutFinal) {
|
||||
|
@ -1070,7 +1086,8 @@ BluetoothOppManager::SendPutHeaderRequest(const nsAString& aFileName,
|
|||
fileName[len * 2 + 1] = 0x00;
|
||||
|
||||
int index = 3;
|
||||
index += AppendHeaderName(&req[index], (char*)fileName, (len + 1) * 2);
|
||||
index += AppendHeaderName(&req[index], mRemoteMaxPacketLength - index,
|
||||
(char*)fileName, (len + 1) * 2);
|
||||
index += AppendHeaderLength(&req[index], aFileSize);
|
||||
|
||||
SendObexData(req, ObexRequestCode::Put, index);
|
||||
|
@ -1083,9 +1100,8 @@ void
|
|||
BluetoothOppManager::SendPutRequest(uint8_t* aFileBody,
|
||||
int aFileBodyLength)
|
||||
{
|
||||
int packetLeftSpace = mRemoteMaxPacketLength - kPutRequestHeaderSize;
|
||||
|
||||
if (!mConnected) return;
|
||||
int packetLeftSpace = mRemoteMaxPacketLength - kPutRequestHeaderSize;
|
||||
if (aFileBodyLength > packetLeftSpace) {
|
||||
BT_WARNING("Not allowed such a small MaxPacketLength value");
|
||||
return;
|
||||
|
@ -1096,11 +1112,11 @@ BluetoothOppManager::SendPutRequest(uint8_t* aFileBody,
|
|||
uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
|
||||
|
||||
int index = 3;
|
||||
index += AppendHeaderBody(&req[index], aFileBody, aFileBodyLength);
|
||||
index += AppendHeaderBody(&req[index], mRemoteMaxPacketLength - index,
|
||||
aFileBody, aFileBodyLength);
|
||||
|
||||
SendObexData(req, ObexRequestCode::Put, index);
|
||||
delete [] req;
|
||||
|
||||
mSentFileLength += aFileBodyLength;
|
||||
}
|
||||
|
||||
|
|
|
@ -346,6 +346,15 @@ child:
|
|||
*/
|
||||
HandleLongTap(CSSIntPoint point);
|
||||
|
||||
/**
|
||||
* Requests handling of releasing a long tap. |aPoint| is in CSS pixels,
|
||||
* relative to the current scroll offset. In the case the "contextmenu"
|
||||
* event generated by the preceding HandleLongTap call was not handled,
|
||||
* this message is expected to generate a "mousedown" and "mouseup"
|
||||
* series of events
|
||||
*/
|
||||
HandleLongTapUp(CSSIntPoint point);
|
||||
|
||||
/**
|
||||
* Notifies the child that the parent has begun or finished transforming
|
||||
* the visible child content area. Useful for showing/hiding scrollbars.
|
||||
|
|
|
@ -285,6 +285,7 @@ TabChild::TabChild(ContentChild* aManager, const TabContext& aContext, uint32_t
|
|||
, mTriedBrowserInit(false)
|
||||
, mOrientation(eScreenOrientation_PortraitPrimary)
|
||||
, mUpdateHitRegion(false)
|
||||
, mContextMenuHandled(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -1660,6 +1661,18 @@ TabChild::RecvHandleLongTap(const CSSIntPoint& aPoint)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TabChild::RecvHandleLongTapUp(const CSSIntPoint& aPoint)
|
||||
{
|
||||
if (mContextMenuHandled) {
|
||||
mContextMenuHandled = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
RecvHandleSingleTap(aPoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TabChild::RecvNotifyTransformBegin(const ViewID& aViewId)
|
||||
{
|
||||
|
|
|
@ -219,6 +219,7 @@ public:
|
|||
virtual bool RecvHandleDoubleTap(const CSSIntPoint& aPoint);
|
||||
virtual bool RecvHandleSingleTap(const CSSIntPoint& aPoint);
|
||||
virtual bool RecvHandleLongTap(const CSSIntPoint& aPoint);
|
||||
virtual bool RecvHandleLongTapUp(const CSSIntPoint& aPoint);
|
||||
virtual bool RecvNotifyTransformBegin(const ViewID& aViewId);
|
||||
virtual bool RecvNotifyTransformEnd(const ViewID& aViewId);
|
||||
virtual bool RecvActivate();
|
||||
|
@ -498,6 +499,7 @@ private:
|
|||
bool mTriedBrowserInit;
|
||||
ScreenOrientation mOrientation;
|
||||
bool mUpdateHitRegion;
|
||||
bool mContextMenuHandled;
|
||||
|
||||
DISALLOW_EVIL_CONSTRUCTORS(TabChild);
|
||||
};
|
||||
|
|
|
@ -521,6 +521,13 @@ void TabParent::HandleLongTap(const CSSIntPoint& aPoint, int32_t aModifiers)
|
|||
}
|
||||
}
|
||||
|
||||
void TabParent::HandleLongTapUp(const CSSIntPoint& aPoint, int32_t aModifiers)
|
||||
{
|
||||
if (!mIsDestroyed) {
|
||||
unused << SendHandleLongTapUp(aPoint);
|
||||
}
|
||||
}
|
||||
|
||||
void TabParent::NotifyTransformBegin(ViewID aViewId)
|
||||
{
|
||||
if (!mIsDestroyed) {
|
||||
|
@ -713,6 +720,15 @@ bool TabParent::SendHandleLongTap(const CSSIntPoint& aPoint)
|
|||
return PBrowserParent::SendHandleLongTap(AdjustTapToChildWidget(aPoint));
|
||||
}
|
||||
|
||||
bool TabParent::SendHandleLongTapUp(const CSSIntPoint& aPoint)
|
||||
{
|
||||
if (mIsDestroyed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return PBrowserParent::SendHandleLongTapUp(AdjustTapToChildWidget(aPoint));
|
||||
}
|
||||
|
||||
bool TabParent::SendHandleDoubleTap(const CSSIntPoint& aPoint)
|
||||
{
|
||||
if (mIsDestroyed) {
|
||||
|
|
|
@ -196,6 +196,7 @@ public:
|
|||
void HandleDoubleTap(const CSSIntPoint& aPoint, int32_t aModifiers);
|
||||
void HandleSingleTap(const CSSIntPoint& aPoint, int32_t aModifiers);
|
||||
void HandleLongTap(const CSSIntPoint& aPoint, int32_t aModifiers);
|
||||
void HandleLongTapUp(const CSSIntPoint& aPoint, int32_t aModifiers);
|
||||
void NotifyTransformBegin(ViewID aViewId);
|
||||
void NotifyTransformEnd(ViewID aViewId);
|
||||
void Activate();
|
||||
|
@ -217,6 +218,7 @@ public:
|
|||
bool SendRealTouchEvent(WidgetTouchEvent& event);
|
||||
bool SendHandleSingleTap(const CSSIntPoint& aPoint);
|
||||
bool SendHandleLongTap(const CSSIntPoint& aPoint);
|
||||
bool SendHandleLongTapUp(const CSSIntPoint& aPoint);
|
||||
bool SendHandleDoubleTap(const CSSIntPoint& aPoint);
|
||||
|
||||
virtual PDocumentRendererParent*
|
||||
|
|
|
@ -2770,8 +2770,6 @@ WifiWorker.prototype = {
|
|||
let network = msg.data.network;
|
||||
let info = msg.data.info;
|
||||
|
||||
netFromDOM(network, null);
|
||||
|
||||
WifiManager.configureHttpProxy(network, info, function(ok) {
|
||||
if (ok) {
|
||||
// If configured network is current connected network
|
||||
|
@ -2794,8 +2792,6 @@ WifiWorker.prototype = {
|
|||
let network = msg.data.network;
|
||||
let info = msg.data.info;
|
||||
|
||||
netFromDOM(network, null);
|
||||
|
||||
// To compatiable with DHCP returned info structure, do translation here
|
||||
info.ipaddr_str = info.ipaddr;
|
||||
info.proxy_str = info.proxy;
|
||||
|
|
|
@ -518,6 +518,7 @@ nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent)
|
|||
const TapGestureInput& tapGestureInput = aEvent.AsTapGestureInput();
|
||||
switch (tapGestureInput.mType) {
|
||||
case TapGestureInput::TAPGESTURE_LONG: rv = OnLongPress(tapGestureInput); break;
|
||||
case TapGestureInput::TAPGESTURE_LONG_UP: rv = OnLongPressUp(tapGestureInput); break;
|
||||
case TapGestureInput::TAPGESTURE_UP: rv = OnSingleTapUp(tapGestureInput); break;
|
||||
case TapGestureInput::TAPGESTURE_CONFIRMED: rv = OnSingleTapConfirmed(tapGestureInput); break;
|
||||
case TapGestureInput::TAPGESTURE_DOUBLE: rv = OnDoubleTap(tapGestureInput); break;
|
||||
|
@ -797,8 +798,11 @@ AsyncPanZoomController::ConvertToGecko(const ScreenPoint& aPoint, CSSIntPoint* a
|
|||
// NOTE: This isn't *quite* LayoutDevicePoint, we just don't have a name
|
||||
// for this coordinate space and it maps the closest to LayoutDevicePoint.
|
||||
LayoutDevicePoint layoutPoint = LayoutDevicePoint(result.x, result.y);
|
||||
CSSPoint cssPoint = layoutPoint / mFrameMetrics.mDevPixelsPerCSSPixel;
|
||||
*aOut = gfx::RoundedToInt(cssPoint);
|
||||
{ // scoped lock to access mFrameMetrics
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
CSSPoint cssPoint = layoutPoint / mFrameMetrics.mDevPixelsPerCSSPixel;
|
||||
*aOut = gfx::RoundedToInt(cssPoint);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -808,8 +812,6 @@ nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent)
|
|||
APZC_LOG("%p got a long-press in state %d\n", this, mState);
|
||||
nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
|
||||
if (controller) {
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
|
||||
int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
|
||||
CSSIntPoint geckoScreenPoint;
|
||||
if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
|
||||
|
@ -820,12 +822,26 @@ nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent)
|
|||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::OnLongPressUp(const TapGestureInput& aEvent) {
|
||||
APZC_LOG("%p got a long-tap-up in state %d\n", this, mState);
|
||||
nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
|
||||
if (controller) {
|
||||
int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
|
||||
CSSIntPoint geckoScreenPoint;
|
||||
if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
|
||||
controller->HandleLongTapUp(geckoScreenPoint, modifiers);
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
}
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) {
|
||||
APZC_LOG("%p got a single-tap-up in state %d\n", this, mState);
|
||||
nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
|
||||
// If mAllowZoom is true we wait for a call to OnSingleTapConfirmed before
|
||||
// sending event to content
|
||||
if (controller && !mAllowZoom) {
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
|
||||
int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
|
||||
CSSIntPoint geckoScreenPoint;
|
||||
if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
|
||||
|
@ -839,10 +855,7 @@ nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEven
|
|||
nsEventStatus AsyncPanZoomController::OnSingleTapConfirmed(const TapGestureInput& aEvent) {
|
||||
APZC_LOG("%p got a single-tap-confirmed in state %d\n", this, mState);
|
||||
nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
|
||||
// If zooming is disabled, we handle this in OnSingleTapUp
|
||||
if (controller && mAllowZoom) {
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
|
||||
if (controller) {
|
||||
int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
|
||||
CSSIntPoint geckoScreenPoint;
|
||||
if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
|
||||
|
@ -857,9 +870,7 @@ nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent)
|
|||
APZC_LOG("%p got a double-tap in state %d\n", this, mState);
|
||||
nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
|
||||
if (controller) {
|
||||
|
||||
if (mAllowZoom) {
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
|
||||
CSSIntPoint geckoScreenPoint;
|
||||
if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
|
||||
|
|
|
@ -350,30 +350,23 @@ protected:
|
|||
nsEventStatus OnScaleEnd(const PinchGestureInput& aEvent);
|
||||
|
||||
/**
|
||||
* Helper method for long press gestures.
|
||||
*
|
||||
* XXX: Implement this.
|
||||
* Helper methods for long press gestures.
|
||||
*/
|
||||
nsEventStatus OnLongPress(const TapGestureInput& aEvent);
|
||||
nsEventStatus OnLongPressUp(const TapGestureInput& aEvent);
|
||||
|
||||
/**
|
||||
* Helper method for single tap gestures.
|
||||
*
|
||||
* XXX: Implement this.
|
||||
*/
|
||||
nsEventStatus OnSingleTapUp(const TapGestureInput& aEvent);
|
||||
|
||||
/**
|
||||
* Helper method for a single tap confirmed.
|
||||
*
|
||||
* XXX: Implement this.
|
||||
*/
|
||||
nsEventStatus OnSingleTapConfirmed(const TapGestureInput& aEvent);
|
||||
|
||||
/**
|
||||
* Helper method for double taps.
|
||||
*
|
||||
* XXX: Implement this.
|
||||
*/
|
||||
nsEventStatus OnDoubleTap(const TapGestureInput& aEvent);
|
||||
|
||||
|
|
|
@ -49,6 +49,13 @@ public:
|
|||
*/
|
||||
virtual void HandleLongTap(const CSSIntPoint& aPoint, int32_t aModifiers) = 0;
|
||||
|
||||
/**
|
||||
* Requests handling of releasing a long tap. |aPoint| is in CSS pixels,
|
||||
* relative to the current scroll offset. HandleLongTapUp will always be
|
||||
* preceeded by HandleLongTap
|
||||
*/
|
||||
virtual void HandleLongTapUp(const CSSIntPoint& aPoint, int32_t aModifiers) = 0;
|
||||
|
||||
/**
|
||||
* Requests sending a mozbrowserasyncscroll domevent to embedder.
|
||||
* |aContentRect| is in CSS pixels, relative to the current cssPage.
|
||||
|
|
|
@ -159,7 +159,10 @@ nsEventStatus GestureEventListener::HandleInputEvent(const InputData& aEvent)
|
|||
}
|
||||
}
|
||||
|
||||
if (mState == GESTURE_WAITING_SINGLE_TAP &&
|
||||
if (mState == GESTURE_LONG_TAP_UP) {
|
||||
HandleLongTapUpEvent(event);
|
||||
mState = GESTURE_NONE;
|
||||
} else if (mState == GESTURE_WAITING_SINGLE_TAP &&
|
||||
event.mTime - mTapStartTime > MAX_TAP_TIME) {
|
||||
// Extended taps are immediately dispatched as single taps
|
||||
CancelLongTapTimeoutTask();
|
||||
|
@ -167,19 +170,25 @@ nsEventStatus GestureEventListener::HandleInputEvent(const InputData& aEvent)
|
|||
mState = GESTURE_NONE;
|
||||
} else if (mState == GESTURE_WAITING_SINGLE_TAP) {
|
||||
CancelLongTapTimeoutTask();
|
||||
HandleSingleTapUpEvent(event);
|
||||
nsEventStatus tapupEvent = HandleSingleTapUpEvent(event);
|
||||
|
||||
// We were not waiting for anything but a single tap has happened that
|
||||
// may turn into a double tap. Wait a while and if it doesn't turn into
|
||||
// a double tap, send a single tap instead.
|
||||
mState = GESTURE_WAITING_DOUBLE_TAP;
|
||||
if (tapupEvent == nsEventStatus_eIgnore) {
|
||||
// We were not waiting for anything but a single tap has happened that
|
||||
// may turn into a double tap. Wait a while and if it doesn't turn into
|
||||
// a double tap, send a single tap instead.
|
||||
mState = GESTURE_WAITING_DOUBLE_TAP;
|
||||
|
||||
mDoubleTapTimeoutTask =
|
||||
NewRunnableMethod(this, &GestureEventListener::TimeoutDoubleTap);
|
||||
mDoubleTapTimeoutTask =
|
||||
NewRunnableMethod(this, &GestureEventListener::TimeoutDoubleTap);
|
||||
|
||||
mAsyncPanZoomController->PostDelayedTask(
|
||||
mDoubleTapTimeoutTask,
|
||||
MAX_TAP_TIME);
|
||||
mAsyncPanZoomController->PostDelayedTask(
|
||||
mDoubleTapTimeoutTask,
|
||||
MAX_TAP_TIME);
|
||||
|
||||
} else if (tapupEvent == nsEventStatus_eConsumeNoDefault) {
|
||||
// We sent the tapup into content without waiting for a double tap
|
||||
mState = GESTURE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
mLastTapEndTime = event.mTime;
|
||||
|
@ -308,6 +317,13 @@ nsEventStatus GestureEventListener::HandleLongTapEvent(const MultiTouchInput& aE
|
|||
return mAsyncPanZoomController->HandleInputEvent(tapEvent);
|
||||
}
|
||||
|
||||
nsEventStatus GestureEventListener::HandleLongTapUpEvent(const MultiTouchInput& aEvent)
|
||||
{
|
||||
TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_LONG_UP, aEvent.mTime,
|
||||
aEvent.mTouches[0].mScreenPoint, aEvent.modifiers);
|
||||
return mAsyncPanZoomController->HandleInputEvent(tapEvent);
|
||||
}
|
||||
|
||||
nsEventStatus GestureEventListener::HandleTapCancel(const MultiTouchInput& aEvent)
|
||||
{
|
||||
mTapStartTime = 0;
|
||||
|
@ -320,6 +336,7 @@ nsEventStatus GestureEventListener::HandleTapCancel(const MultiTouchInput& aEven
|
|||
break;
|
||||
|
||||
case GESTURE_WAITING_DOUBLE_TAP:
|
||||
case GESTURE_LONG_TAP_UP:
|
||||
mState = GESTURE_NONE;
|
||||
break;
|
||||
default:
|
||||
|
@ -360,7 +377,7 @@ void GestureEventListener::TimeoutLongTap()
|
|||
mLongTapTimeoutTask = nullptr;
|
||||
// If the tap has not been released, this is a long press.
|
||||
if (mState == GESTURE_WAITING_SINGLE_TAP) {
|
||||
mState = GESTURE_NONE;
|
||||
mState = GESTURE_LONG_TAP_UP;
|
||||
|
||||
HandleLongTapEvent(mLastTouchInput);
|
||||
}
|
||||
|
|
|
@ -85,7 +85,10 @@ protected:
|
|||
// may be mistaken for a tap.
|
||||
GESTURE_WAITING_SINGLE_TAP,
|
||||
// A single tap has happened for sure, and we're waiting for a second tap.
|
||||
GESTURE_WAITING_DOUBLE_TAP
|
||||
GESTURE_WAITING_DOUBLE_TAP,
|
||||
// A long tap has happened, wait for the tap to be released in case we need
|
||||
// to fire a click event in the case the long tap was not handled.
|
||||
GESTURE_LONG_TAP_UP
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -120,6 +123,12 @@ protected:
|
|||
*/
|
||||
nsEventStatus HandleLongTapEvent(const MultiTouchInput& aEvent);
|
||||
|
||||
/**
|
||||
* Attempts to handle release of long tap. This is used to fire click
|
||||
* events in the case the context menu was not invoked.
|
||||
*/
|
||||
nsEventStatus HandleLongTapUpEvent(const MultiTouchInput& aEvent);
|
||||
|
||||
/**
|
||||
* Attempts to handle a tap event cancellation. This happens when we think
|
||||
* something was a tap but it actually wasn't. In general, this will not
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "mozilla/layers/GeckoContentController.h"
|
||||
#include "mozilla/layers/CompositorParent.h"
|
||||
#include "mozilla/layers/APZCTreeManager.h"
|
||||
#include "base/task.h"
|
||||
#include "Layers.h"
|
||||
#include "TestLayers.h"
|
||||
|
||||
|
@ -23,16 +24,35 @@ using ::testing::_;
|
|||
using ::testing::NiceMock;
|
||||
using ::testing::AtLeast;
|
||||
|
||||
class Task;
|
||||
|
||||
class MockContentController : public GeckoContentController {
|
||||
public:
|
||||
MOCK_METHOD1(RequestContentRepaint, void(const FrameMetrics&));
|
||||
MOCK_METHOD2(HandleDoubleTap, void(const CSSIntPoint&, int32_t));
|
||||
MOCK_METHOD2(HandleSingleTap, void(const CSSIntPoint&, int32_t));
|
||||
MOCK_METHOD2(HandleLongTap, void(const CSSIntPoint&, int32_t));
|
||||
MOCK_METHOD2(HandleLongTapUp, void(const CSSIntPoint&, int32_t));
|
||||
MOCK_METHOD3(SendAsyncScrollDOMEvent, void(bool aIsRoot, const CSSRect &aContentRect, const CSSSize &aScrollableSize));
|
||||
MOCK_METHOD2(PostDelayedTask, void(Task* aTask, int aDelayMs));
|
||||
};
|
||||
|
||||
class MockContentControllerDelayed : public MockContentController {
|
||||
public:
|
||||
|
||||
void PostDelayedTask(Task* aTask, int aDelayMs) {
|
||||
mCurrentTask = aTask;
|
||||
}
|
||||
|
||||
Task* GetDelayedTask() {
|
||||
return mCurrentTask;
|
||||
}
|
||||
|
||||
private:
|
||||
Task *mCurrentTask;
|
||||
};
|
||||
|
||||
|
||||
class TestAPZCContainerLayer : public ContainerLayer {
|
||||
public:
|
||||
TestAPZCContainerLayer()
|
||||
|
@ -47,8 +67,9 @@ class TestAPZCContainerLayer : public ContainerLayer {
|
|||
class TestAsyncPanZoomController : public AsyncPanZoomController {
|
||||
public:
|
||||
TestAsyncPanZoomController(uint64_t aLayersId, MockContentController* aMcc,
|
||||
APZCTreeManager* aTreeManager = nullptr)
|
||||
: AsyncPanZoomController(aLayersId, aTreeManager, aMcc)
|
||||
APZCTreeManager* aTreeManager = nullptr,
|
||||
GestureBehavior aBehavior = DEFAULT_GESTURES)
|
||||
: AsyncPanZoomController(aLayersId, aTreeManager, aMcc, aBehavior)
|
||||
{}
|
||||
|
||||
void SetFrameMetrics(const FrameMetrics& metrics) {
|
||||
|
@ -150,6 +171,28 @@ ApzcPinch(AsyncPanZoomController* aApzc, int aFocusX, int aFocusY, float aScale)
|
|||
0));
|
||||
}
|
||||
|
||||
static nsEventStatus
|
||||
ApzcDown(AsyncPanZoomController* apzc, int aX, int aY, int& aTime) {
|
||||
MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime, 0);
|
||||
mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(aX, aY), ScreenSize(0, 0), 0, 0));
|
||||
return apzc->ReceiveInputEvent(mti);
|
||||
}
|
||||
|
||||
static nsEventStatus
|
||||
ApzcUp(AsyncPanZoomController* apzc, int aX, int aY, int& aTime) {
|
||||
MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, 0);
|
||||
mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(aX, aY), ScreenSize(0, 0), 0, 0));
|
||||
return apzc->ReceiveInputEvent(mti);
|
||||
}
|
||||
|
||||
static nsEventStatus
|
||||
ApzcTap(AsyncPanZoomController* apzc, int aX, int aY, int& aTime, int aTapLength) {
|
||||
nsEventStatus status = ApzcDown(apzc, aX, aY, aTime);
|
||||
EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
|
||||
aTime += aTapLength;
|
||||
return ApzcUp(apzc, aX, aY, aTime);
|
||||
}
|
||||
|
||||
TEST(AsyncPanZoomController, Constructor) {
|
||||
// RefCounted class can't live in the stack
|
||||
nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
|
||||
|
@ -433,6 +476,77 @@ TEST(AsyncPanZoomController, OverScrollPanning) {
|
|||
EXPECT_EQ(pointOut, ScreenPoint(0, 90));
|
||||
}
|
||||
|
||||
TEST(AsyncPanZoomController, ShortPress) {
|
||||
nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
|
||||
nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();
|
||||
nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(
|
||||
0, mcc, tm, AsyncPanZoomController::USE_GESTURE_DETECTOR);
|
||||
|
||||
apzc->SetFrameMetrics(TestFrameMetrics());
|
||||
apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
|
||||
apzc->UpdateZoomConstraints(false, CSSToScreenScale(1.0), CSSToScreenScale(1.0));
|
||||
|
||||
EXPECT_CALL(*mcc, HandleSingleTap(CSSIntPoint(10, 10), 0)).Times(1);
|
||||
|
||||
int time = 0;
|
||||
nsEventStatus status = ApzcTap(apzc, 10, 10, time, 100);
|
||||
EXPECT_EQ(nsEventStatus_eIgnore, status);
|
||||
|
||||
apzc->Destroy();
|
||||
}
|
||||
|
||||
TEST(AsyncPanZoomController, MediumPress) {
|
||||
nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
|
||||
nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();
|
||||
nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(
|
||||
0, mcc, tm, AsyncPanZoomController::USE_GESTURE_DETECTOR);
|
||||
|
||||
apzc->SetFrameMetrics(TestFrameMetrics());
|
||||
apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
|
||||
apzc->UpdateZoomConstraints(false, CSSToScreenScale(1.0), CSSToScreenScale(1.0));
|
||||
|
||||
EXPECT_CALL(*mcc, HandleSingleTap(CSSIntPoint(10, 10), 0)).Times(1);
|
||||
|
||||
int time = 0;
|
||||
nsEventStatus status = ApzcTap(apzc, 10, 10, time, 400);
|
||||
EXPECT_EQ(nsEventStatus_eIgnore, status);
|
||||
|
||||
apzc->Destroy();
|
||||
}
|
||||
|
||||
TEST(AsyncPanZoomController, LongPress) {
|
||||
nsRefPtr<MockContentControllerDelayed> mcc = new MockContentControllerDelayed();
|
||||
nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();
|
||||
nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(
|
||||
0, mcc, tm, AsyncPanZoomController::USE_GESTURE_DETECTOR);
|
||||
|
||||
apzc->SetFrameMetrics(TestFrameMetrics());
|
||||
apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
|
||||
apzc->UpdateZoomConstraints(false, CSSToScreenScale(1.0), CSSToScreenScale(1.0));
|
||||
|
||||
int time = 0;
|
||||
|
||||
nsEventStatus status = ApzcDown(apzc, 10, 10, time);
|
||||
EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
|
||||
|
||||
Task* t = mcc->GetDelayedTask();
|
||||
|
||||
EXPECT_TRUE(nullptr != t);
|
||||
EXPECT_CALL(*mcc, HandleLongTap(CSSIntPoint(10, 10), 0)).Times(1);
|
||||
EXPECT_CALL(*mcc, HandleLongTapUp(CSSIntPoint(10, 10), 0)).Times(1);
|
||||
EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
|
||||
|
||||
// Manually invoke the longpress while the touch is currently down.
|
||||
t->Run();
|
||||
|
||||
time += 1000;
|
||||
|
||||
status = ApzcUp(apzc, 10, 10, time);
|
||||
EXPECT_EQ(nsEventStatus_eIgnore, status);
|
||||
|
||||
apzc->Destroy();
|
||||
}
|
||||
|
||||
// Layer tree for HitTesting1
|
||||
static already_AddRefed<mozilla::layers::Layer>
|
||||
CreateTestLayerTree1(nsRefPtr<LayerManager>& aLayerManager, nsTArray<nsRefPtr<Layer> >& aLayers) {
|
||||
|
|
|
@ -567,6 +567,24 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
virtual void HandleLongTapUp(const CSSIntPoint& aPoint,
|
||||
int32_t aModifiers) MOZ_OVERRIDE
|
||||
{
|
||||
if (MessageLoop::current() != mUILoop) {
|
||||
// We have to send this message from the "UI thread" (main
|
||||
// thread).
|
||||
mUILoop->PostTask(
|
||||
FROM_HERE,
|
||||
NewRunnableMethod(this, &RemoteContentController::HandleLongTapUp,
|
||||
aPoint, aModifiers));
|
||||
return;
|
||||
}
|
||||
if (mRenderFrame) {
|
||||
TabParent* browser = static_cast<TabParent*>(mRenderFrame->Manager());
|
||||
browser->HandleLongTapUp(aPoint, aModifiers);
|
||||
}
|
||||
}
|
||||
|
||||
void ClearRenderFrame() { mRenderFrame = nullptr; }
|
||||
|
||||
virtual void SendAsyncScrollDOMEvent(bool aIsRoot,
|
||||
|
|
|
@ -166,7 +166,7 @@ pref("dom.experimental_forms", true);
|
|||
pref("dom.forms.number", true);
|
||||
// Don't enable <input type=color> yet as we don't have a color picker
|
||||
// implemented for Android (bug 875750)
|
||||
pref("dom.forms.color", false);
|
||||
pref("dom.forms.color", true);
|
||||
|
||||
/* extension manager and xpinstall */
|
||||
pref("xpinstall.whitelist.add", "addons.mozilla.org");
|
||||
|
|
|
@ -1515,9 +1515,13 @@ public class GeckoAppShell
|
|||
public static boolean isNetworkLinkUp() {
|
||||
ConnectivityManager cm = (ConnectivityManager)
|
||||
getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
NetworkInfo info = cm.getActiveNetworkInfo();
|
||||
if (info == null || !info.isConnected())
|
||||
try {
|
||||
NetworkInfo info = cm.getActiveNetworkInfo();
|
||||
if (info == null || !info.isConnected())
|
||||
return false;
|
||||
} catch (SecurityException se) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1525,8 +1529,12 @@ public class GeckoAppShell
|
|||
public static boolean isNetworkLinkKnown() {
|
||||
ConnectivityManager cm = (ConnectivityManager)
|
||||
getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
if (cm.getActiveNetworkInfo() == null)
|
||||
try {
|
||||
if (cm.getActiveNetworkInfo() == null)
|
||||
return false;
|
||||
} catch (SecurityException se) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -220,7 +220,11 @@ public class GeckoNetworkManager extends BroadcastReceiver {
|
|||
return NetworkType.NETWORK_NONE;
|
||||
}
|
||||
|
||||
NetworkInfo ni = cm.getActiveNetworkInfo();
|
||||
NetworkInfo ni = null;
|
||||
try {
|
||||
ni = cm.getActiveNetworkInfo();
|
||||
} catch (SecurityException se) {} // if we don't have the permission, fall through to null check
|
||||
|
||||
if (ni == null) {
|
||||
return NetworkType.NETWORK_NONE;
|
||||
}
|
||||
|
|
|
@ -266,6 +266,7 @@ gbjar.sources += [
|
|||
'preferences/SyncPreference.java',
|
||||
'PrefsHelper.java',
|
||||
'PrivateTab.java',
|
||||
'prompts/ColorPickerInput.java',
|
||||
'prompts/IconGridInput.java',
|
||||
'prompts/Prompt.java',
|
||||
'prompts/PromptInput.java',
|
||||
|
@ -314,6 +315,7 @@ gbjar.sources += [
|
|||
'widget/AllCapsTextView.java',
|
||||
'widget/AnimatedHeightLayout.java',
|
||||
'widget/ArrowPopup.java',
|
||||
'widget/BasicColorPicker.java',
|
||||
'widget/ButtonToast.java',
|
||||
'widget/CheckableLinearLayout.java',
|
||||
'widget/ClickableWhenDisabledEditText.java',
|
||||
|
@ -430,6 +432,7 @@ ANDROID_RESFILES += [
|
|||
'resources/drawable-hdpi/bookmark_folder_closed.png',
|
||||
'resources/drawable-hdpi/bookmark_folder_opened.png',
|
||||
'resources/drawable-hdpi/close.png',
|
||||
'resources/drawable-hdpi/color_picker_row_bg.9.png',
|
||||
'resources/drawable-hdpi/copy.png',
|
||||
'resources/drawable-hdpi/cut.png',
|
||||
'resources/drawable-hdpi/favicon.png',
|
||||
|
@ -578,6 +581,7 @@ ANDROID_RESFILES += [
|
|||
'resources/drawable-mdpi/bookmarkdefaults_favicon_addons.png',
|
||||
'resources/drawable-mdpi/bookmarkdefaults_favicon_support.png',
|
||||
'resources/drawable-mdpi/close.png',
|
||||
'resources/drawable-mdpi/color_picker_row_bg.9.png',
|
||||
'resources/drawable-mdpi/copy.png',
|
||||
'resources/drawable-mdpi/cut.png',
|
||||
'resources/drawable-mdpi/desktop_notification.png',
|
||||
|
@ -718,6 +722,7 @@ ANDROID_RESFILES += [
|
|||
'resources/drawable-xhdpi/bookmark_folder_closed.png',
|
||||
'resources/drawable-xhdpi/bookmark_folder_opened.png',
|
||||
'resources/drawable-xhdpi/close.png',
|
||||
'resources/drawable-xhdpi/color_picker_row_bg.9.png',
|
||||
'resources/drawable-xhdpi/copy.png',
|
||||
'resources/drawable-xhdpi/cut.png',
|
||||
'resources/drawable-xhdpi/favicon.png',
|
||||
|
@ -818,6 +823,7 @@ ANDROID_RESFILES += [
|
|||
'resources/drawable/action_bar_button.xml',
|
||||
'resources/drawable/action_bar_button_inverse.xml',
|
||||
'resources/drawable/bookmark_folder.xml',
|
||||
'resources/drawable/color_picker_checkmark.xml',
|
||||
'resources/drawable/divider_horizontal.xml',
|
||||
'resources/drawable/divider_vertical.xml',
|
||||
'resources/drawable/handle_end_level.xml',
|
||||
|
@ -867,11 +873,13 @@ ANDROID_RESFILES += [
|
|||
'resources/layout/arrow_popup.xml',
|
||||
'resources/layout/autocomplete_list.xml',
|
||||
'resources/layout/autocomplete_list_item.xml',
|
||||
'resources/layout/basic_color_picker_dialog.xml',
|
||||
'resources/layout/bookmark_edit.xml',
|
||||
'resources/layout/bookmark_folder_row.xml',
|
||||
'resources/layout/bookmark_item_row.xml',
|
||||
'resources/layout/browser_search.xml',
|
||||
'resources/layout/browser_toolbar.xml',
|
||||
'resources/layout/color_picker_row.xml',
|
||||
'resources/layout/datetime_picker.xml',
|
||||
'resources/layout/doorhanger.xml',
|
||||
'resources/layout/doorhanger_button.xml',
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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/. */
|
||||
|
||||
package org.mozilla.gecko.prompts;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.widget.BasicColorPicker;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.view.View;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.LinearLayout.LayoutParams;
|
||||
|
||||
public class ColorPickerInput extends PromptInput {
|
||||
public static final String INPUT_TYPE = "color";
|
||||
public static final String LOGTAG = "GeckoColorPickerInput";
|
||||
|
||||
private boolean mShowAdvancedButton = true;
|
||||
private int mInitialColor;
|
||||
|
||||
public ColorPickerInput(JSONObject obj) {
|
||||
super(obj);
|
||||
String init = obj.optString("value");
|
||||
mInitialColor = Color.rgb(Integer.parseInt(init.substring(1,3), 16),
|
||||
Integer.parseInt(init.substring(3,5), 16),
|
||||
Integer.parseInt(init.substring(5,7), 16));
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(Context context) throws UnsupportedOperationException {
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
mView = inflater.inflate(R.layout.basic_color_picker_dialog, null);
|
||||
|
||||
BasicColorPicker cp = (BasicColorPicker) mView.findViewById(R.id.colorpicker);
|
||||
cp.setColor(mInitialColor);
|
||||
|
||||
return mView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
BasicColorPicker cp = (BasicColorPicker) mView.findViewById(R.id.colorpicker);
|
||||
int color = cp.getColor();
|
||||
return "#" + Integer.toHexString(color).substring(2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getScrollable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canApplyInputStyle() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import org.mozilla.gecko.util.GeckoEventResponder;
|
|||
import org.mozilla.gecko.GeckoEvent;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.gecko.widget.DateTimePicker;
|
||||
import org.mozilla.gecko.prompts.ColorPickerInput;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
|
||||
|
@ -108,8 +109,11 @@ public class Prompt implements OnClickListener, OnCancelListener, OnItemClickLis
|
|||
}
|
||||
}
|
||||
|
||||
private View applyInputStyle(View view) {
|
||||
view.setPadding(mInputPaddingSize, 0, mInputPaddingSize, 0);
|
||||
private View applyInputStyle(View view, PromptInput input) {
|
||||
// Don't add padding to color picker views
|
||||
if (input.canApplyInputStyle()) {
|
||||
view.setPadding(mInputPaddingSize, 0, mInputPaddingSize, 0);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
|
@ -333,12 +337,14 @@ public class Prompt implements OnClickListener, OnCancelListener, OnItemClickLis
|
|||
|
||||
if (length == 1) {
|
||||
root = mInputs[0].getView(mContext);
|
||||
applyInputStyle(root, mInputs[0]);
|
||||
scrollable |= mInputs[0].getScrollable();
|
||||
} else if (length > 1) {
|
||||
LinearLayout linearLayout = new LinearLayout(mContext);
|
||||
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
||||
for (int i = 0; i < length; i++) {
|
||||
View content = mInputs[i].getView(mContext);
|
||||
applyInputStyle(content, mInputs[i]);
|
||||
linearLayout.addView(content);
|
||||
scrollable |= mInputs[i].getScrollable();
|
||||
}
|
||||
|
@ -346,11 +352,11 @@ public class Prompt implements OnClickListener, OnCancelListener, OnItemClickLis
|
|||
}
|
||||
|
||||
if (scrollable) {
|
||||
builder.setView(applyInputStyle(root));
|
||||
builder.setView(root);
|
||||
} else {
|
||||
ScrollView view = new ScrollView(mContext);
|
||||
view.addView(root);
|
||||
builder.setView(applyInputStyle(view));
|
||||
builder.setView(view);
|
||||
}
|
||||
} catch(Exception ex) {
|
||||
Log.e(LOGTAG, "Error showing prompt inputs", ex);
|
||||
|
|
|
@ -350,6 +350,8 @@ public class PromptInput {
|
|||
return new LabelInput(obj);
|
||||
} else if (IconGridInput.INPUT_TYPE.equals(type)) {
|
||||
return new IconGridInput(obj);
|
||||
} else if (ColorPickerInput.INPUT_TYPE.equals(type)) {
|
||||
return new ColorPickerInput(obj);
|
||||
} else {
|
||||
for (String dtType : DateTimeInput.INPUT_TYPES) {
|
||||
if (dtType.equals(type)) {
|
||||
|
@ -375,4 +377,8 @@ public class PromptInput {
|
|||
public boolean getScrollable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean canApplyInputStyle() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 218 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 212 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 224 B |
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="ring"
|
||||
android:innerRadius="15dip"
|
||||
android:thickness="4dip"
|
||||
android:useLevel="false">
|
||||
<solid android:color="@android:color/white"/>
|
||||
</shape>
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.mozilla.gecko.widget.BasicColorPicker android:id="@+id/colorpicker"
|
||||
android:layout_height="0dip"
|
||||
android:layout_weight="1"
|
||||
android:drawSelectorOnTop="true"
|
||||
android:choiceMode="singleChoice"
|
||||
android:divider="@android:color/transparent"
|
||||
android:dividerHeight="0dip"
|
||||
android:listSelector="#22FFFFFF"
|
||||
android:layout_width="fill_parent"/>
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearance.Widget.TextView"
|
||||
style="@style/Widget.ListItem"
|
||||
android:background="@drawable/color_picker_row_bg"
|
||||
android:checkMark="@android:color/transparent"/>
|
|
@ -0,0 +1,139 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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/. */
|
||||
|
||||
package org.mozilla.gecko.widget;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.CheckedTextView;
|
||||
import android.widget.ListView;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
|
||||
public class BasicColorPicker extends ListView {
|
||||
private final static String LOGTAG = "GeckoBasicColorPicker";
|
||||
private final static List<Integer> DEFAULT_COLORS = Arrays.asList(Color.rgb(215,57,32),
|
||||
Color.rgb(255,134,5),
|
||||
Color.rgb(255,203,19),
|
||||
Color.rgb(95,173,71),
|
||||
Color.rgb(84,201,168),
|
||||
Color.rgb(33,161,222),
|
||||
Color.rgb(16,36,87),
|
||||
Color.rgb(91,32,103),
|
||||
Color.rgb(212,221,228),
|
||||
Color.BLACK);
|
||||
|
||||
private static Drawable mCheckDrawable = null;
|
||||
private int mSelected = 0;
|
||||
final private ColorPickerListAdapter mAdapter;
|
||||
|
||||
public BasicColorPicker(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public BasicColorPicker(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public BasicColorPicker(Context context, AttributeSet attrs, int style) {
|
||||
this(context, attrs, style, DEFAULT_COLORS);
|
||||
}
|
||||
|
||||
public BasicColorPicker(Context context, AttributeSet attrs, int style, List<Integer> colors) {
|
||||
super(context, attrs, style);
|
||||
mAdapter = new ColorPickerListAdapter(context, new ArrayList<Integer>(colors));
|
||||
setAdapter(mAdapter);
|
||||
|
||||
setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
mSelected = position;
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public int getColor() {
|
||||
return mAdapter.getItem(mSelected);
|
||||
}
|
||||
|
||||
public void setColor(int color) {
|
||||
if (!DEFAULT_COLORS.contains(color)) {
|
||||
mSelected = mAdapter.getCount();
|
||||
mAdapter.add(color);
|
||||
} else {
|
||||
mSelected = DEFAULT_COLORS.indexOf(color);
|
||||
}
|
||||
|
||||
setSelection(mSelected);
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private Drawable getCheckDrawable() {
|
||||
if (mCheckDrawable == null) {
|
||||
Resources res = getContext().getResources();
|
||||
|
||||
TypedValue typedValue = new TypedValue();
|
||||
getContext().getTheme().resolveAttribute(android.R.attr.listPreferredItemHeight, typedValue, true);
|
||||
DisplayMetrics metrics = new android.util.DisplayMetrics();
|
||||
((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(metrics);
|
||||
int height = (int) typedValue.getDimension(metrics);
|
||||
|
||||
Drawable background = res.getDrawable(R.drawable.color_picker_row_bg);
|
||||
Rect r = new Rect();
|
||||
background.getPadding(r);
|
||||
height -= r.top + r.bottom;
|
||||
|
||||
mCheckDrawable = res.getDrawable(R.drawable.color_picker_checkmark);
|
||||
mCheckDrawable.setBounds(0, 0, height, height);
|
||||
}
|
||||
|
||||
return mCheckDrawable;
|
||||
}
|
||||
|
||||
private class ColorPickerListAdapter extends ArrayAdapter<Integer> {
|
||||
private final List<Integer> mColors;
|
||||
|
||||
public ColorPickerListAdapter(Context context, List<Integer> colors) {
|
||||
super(context, R.layout.color_picker_row, colors);
|
||||
mColors = colors;
|
||||
}
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View v = super.getView(position, convertView, parent);
|
||||
|
||||
Drawable d = v.getBackground();
|
||||
d.setColorFilter(getItem(position), PorterDuff.Mode.MULTIPLY);
|
||||
v.setBackground(d);
|
||||
|
||||
Drawable check = null;
|
||||
CheckedTextView checked = ((CheckedTextView) v);
|
||||
if (mSelected == position) {
|
||||
check = getCheckDrawable();
|
||||
}
|
||||
|
||||
checked.setCompoundDrawables(check, null, null, null);
|
||||
checked.setText("");
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/* 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/. */
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Prompt.jsm");
|
||||
|
||||
function ColorPicker() {
|
||||
}
|
||||
|
||||
ColorPicker.prototype = {
|
||||
_initial: 0,
|
||||
_domWin: null,
|
||||
_title: "",
|
||||
|
||||
get strings() {
|
||||
delete this.strings;
|
||||
return this.strings = Services.strings.createBundle("chrome://browser/locale/browser.properties");
|
||||
},
|
||||
|
||||
init: function(aParent, aTitle, aInitial) {
|
||||
this._domWin = aParent;
|
||||
this._initial = aInitial;
|
||||
this._title = aTitle;
|
||||
},
|
||||
|
||||
open: function(aCallback) {
|
||||
let p = new Prompt({ title: this._title,
|
||||
buttons: [
|
||||
this.strings.GetStringFromName("inputWidgetHelper.set"),
|
||||
this.strings.GetStringFromName("inputWidgetHelper.cancel")
|
||||
] })
|
||||
.addColorPicker({ value: this._initial })
|
||||
.show((data) => {
|
||||
if (data.button == 0)
|
||||
aCallback.done(data.color0);
|
||||
else
|
||||
aCallback.done(this._initial);
|
||||
});
|
||||
},
|
||||
|
||||
classID: Components.ID("{430b987f-bb9f-46a3-99a5-241749220b29}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIColorPicker])
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ColorPicker]);
|
|
@ -105,3 +105,7 @@ component {a78d7e59-b558-4321-a3d6-dffe2f1e76dd} Snippets.js
|
|||
contract @mozilla.org/snippets;1 {a78d7e59-b558-4321-a3d6-dffe2f1e76dd}
|
||||
category profile-after-change Snippets @mozilla.org/snippets;1
|
||||
category update-timer Snippets @mozilla.org/snippets;1,getService,snippets-update-timer,browser.snippets.updateInterval,86400
|
||||
|
||||
# ColorPicker.js
|
||||
component {430b987f-bb9f-46a3-99a5-241749220b29} ColorPicker.js
|
||||
contract @mozilla.org/colorpicker;1 {430b987f-bb9f-46a3-99a5-241749220b29}
|
||||
|
|
|
@ -13,6 +13,7 @@ XPIDL_MODULE = 'MobileComponents'
|
|||
EXTRA_COMPONENTS += [
|
||||
'AddonUpdateService.js',
|
||||
'BlocklistPrompt.js',
|
||||
'ColorPicker.js',
|
||||
'ContentDispatchChooser.js',
|
||||
'ContentPermissionPrompt.js',
|
||||
'DownloadManagerUI.js',
|
||||
|
|
|
@ -558,6 +558,7 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
|
|||
@BINPATH@/components/AddonUpdateService.js
|
||||
@BINPATH@/components/BlocklistPrompt.js
|
||||
@BINPATH@/components/BrowserCLH.js
|
||||
@BINPATH@/components/ColorPicker.js
|
||||
@BINPATH@/components/ContentDispatchChooser.js
|
||||
@BINPATH@/components/ContentPermissionPrompt.js
|
||||
@BINPATH@/components/DirectoryProvider.js
|
||||
|
|
|
@ -117,6 +117,14 @@ Prompt.prototype = {
|
|||
});
|
||||
},
|
||||
|
||||
addColorPicker: function(aOptions) {
|
||||
return this._addInput({
|
||||
type: "color",
|
||||
value: aOptions.value,
|
||||
id: aOptions.id
|
||||
});
|
||||
},
|
||||
|
||||
addLabel: function(aOptions) {
|
||||
return this._addInput({
|
||||
type: "label",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 : */
|
||||
/* 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/. */
|
||||
|
@ -57,8 +58,9 @@ NS_HIDDEN_(ContentSnifferCache*) gDataSniffers = nullptr;
|
|||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIOService, nsIOService::GetInstance)
|
||||
|
||||
#include "nsDNSService2.h"
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsDNSService, Init)
|
||||
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIDNSService,
|
||||
nsDNSService::GetXPCOMSingleton)
|
||||
|
||||
#include "nsProtocolProxyService.h"
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsProtocolProxyService, Init)
|
||||
|
||||
|
@ -825,7 +827,7 @@ static const mozilla::Module::CIDEntry kNeckoCIDs[] = {
|
|||
{ &kNS_SERVERSOCKET_CID, false, nullptr, nsServerSocketConstructor },
|
||||
{ &kNS_UDPSOCKET_CID, false, nullptr, nsUDPSocketConstructor },
|
||||
{ &kNS_SOCKETPROVIDERSERVICE_CID, false, nullptr, nsSocketProviderService::Create },
|
||||
{ &kNS_DNSSERVICE_CID, false, nullptr, nsDNSServiceConstructor },
|
||||
{ &kNS_DNSSERVICE_CID, false, nullptr, nsIDNSServiceConstructor },
|
||||
{ &kNS_IDNSERVICE_CID, false, nullptr, nsIDNServiceConstructor },
|
||||
{ &kNS_EFFECTIVETLDSERVICE_CID, false, nullptr, nsEffectiveTLDServiceConstructor },
|
||||
{ &kNS_SIMPLEURI_CID, false, nullptr, nsSimpleURIConstructor },
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
/* 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/. */
|
||||
|
||||
#include "mozilla/net/ChildDNSService.h"
|
||||
#include "nsIDNSListener.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsIXPConnect.h"
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsIProtocolProxyService.h"
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
#include "mozilla/net/DNSRequestChild.h"
|
||||
#include "mozilla/net/DNSListenerProxy.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ChildDNSService
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static ChildDNSService *gChildDNSService;
|
||||
static const char kPrefNameDisablePrefetch[] = "network.dns.disablePrefetch";
|
||||
|
||||
ChildDNSService* ChildDNSService::GetSingleton()
|
||||
{
|
||||
MOZ_ASSERT(IsNeckoChild());
|
||||
|
||||
if (!gChildDNSService) {
|
||||
gChildDNSService = new ChildDNSService();
|
||||
}
|
||||
|
||||
NS_ADDREF(gChildDNSService);
|
||||
return gChildDNSService;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS3(ChildDNSService,
|
||||
nsIDNSService,
|
||||
nsPIDNSService,
|
||||
nsIObserver)
|
||||
|
||||
ChildDNSService::ChildDNSService()
|
||||
: mFirstTime(true)
|
||||
, mOffline(false)
|
||||
{
|
||||
MOZ_ASSERT(IsNeckoChild());
|
||||
}
|
||||
|
||||
ChildDNSService::~ChildDNSService()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ChildDNSService::nsIDNSService
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSService::AsyncResolve(const nsACString &hostname,
|
||||
uint32_t flags,
|
||||
nsIDNSListener *listener,
|
||||
nsIEventTarget *target_,
|
||||
nsICancelable **result)
|
||||
{
|
||||
NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE);
|
||||
|
||||
if (mDisablePrefetch && (flags & RESOLVE_SPECULATE)) {
|
||||
return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
|
||||
}
|
||||
|
||||
// Support apps being 'offline' even if parent is not: avoids DNS traffic by
|
||||
// apps that have been told they are offline.
|
||||
if (mOffline) {
|
||||
flags |= RESOLVE_OFFLINE;
|
||||
}
|
||||
|
||||
// make sure JS callers get notification on the main thread
|
||||
nsCOMPtr<nsIEventTarget> target = target_;
|
||||
nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener);
|
||||
if (wrappedListener && !target) {
|
||||
nsCOMPtr<nsIThread> mainThread;
|
||||
NS_GetMainThread(getter_AddRefs(mainThread));
|
||||
target = do_QueryInterface(mainThread);
|
||||
}
|
||||
if (target) {
|
||||
// Guarantee listener freed on main thread. Not sure we need this in child
|
||||
// (or in parent in nsDNSService.cpp) but doesn't hurt.
|
||||
listener = new DNSListenerProxy(listener, target);
|
||||
}
|
||||
|
||||
nsRefPtr<DNSRequestChild> childReq =
|
||||
new DNSRequestChild(nsCString(hostname), flags, listener, target);
|
||||
|
||||
childReq->StartRequest();
|
||||
|
||||
childReq.forget(result);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSService::CancelAsyncResolve(const nsACString &aHostname,
|
||||
uint32_t aFlags,
|
||||
nsIDNSListener *aListener,
|
||||
nsresult aReason)
|
||||
{
|
||||
if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) {
|
||||
return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
|
||||
}
|
||||
|
||||
// TODO: keep a hashtable of pending requests, so we can obey cancel semantics
|
||||
// (call OnLookupComplete with aReason). Also possible we could send IPDL to
|
||||
// parent to cancel.
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSService::Resolve(const nsACString &hostname,
|
||||
uint32_t flags,
|
||||
nsIDNSRecord **result)
|
||||
{
|
||||
// not planning to ever support this, since sync IPDL is evil.
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSService::GetDNSCacheEntries(nsTArray<mozilla::net::DNSCacheEntries> *args)
|
||||
{
|
||||
// Only used by networking dashboard, so may not ever need this in child.
|
||||
// (and would provide a way to spy on what hosts other apps are connecting to,
|
||||
// unless we start keeping per-app DNS caches).
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSService::GetMyHostName(nsACString &result)
|
||||
{
|
||||
// TODO: get value from parent during PNecko construction?
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ChildDNSService::nsPIDNSService
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsresult
|
||||
ChildDNSService::Init()
|
||||
{
|
||||
// Disable prefetching either by explicit preference or if a manual proxy
|
||||
// is configured
|
||||
bool disablePrefetch = false;
|
||||
int proxyType = nsIProtocolProxyService::PROXYCONFIG_DIRECT;
|
||||
|
||||
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
prefs->GetBoolPref(kPrefNameDisablePrefetch, &disablePrefetch);
|
||||
if (prefs) {
|
||||
prefs->GetIntPref("network.proxy.type", &proxyType);
|
||||
prefs->GetBoolPref(kPrefNameDisablePrefetch, &disablePrefetch);
|
||||
}
|
||||
|
||||
if (mFirstTime) {
|
||||
mFirstTime = false;
|
||||
if (prefs) {
|
||||
prefs->AddObserver(kPrefNameDisablePrefetch, this, false);
|
||||
|
||||
// Monitor these to see if there is a change in proxy configuration
|
||||
// If a manual proxy is in use, disable prefetch implicitly
|
||||
prefs->AddObserver("network.proxy.type", this, false);
|
||||
}
|
||||
}
|
||||
|
||||
mDisablePrefetch = disablePrefetch ||
|
||||
(proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ChildDNSService::Shutdown()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSService::GetPrefetchEnabled(bool *outVal)
|
||||
{
|
||||
*outVal = !mDisablePrefetch;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSService::SetPrefetchEnabled(bool inVal)
|
||||
{
|
||||
mDisablePrefetch = !inVal;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSService::GetOffline(bool* aResult)
|
||||
{
|
||||
*aResult = mOffline;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSService::SetOffline(bool value)
|
||||
{
|
||||
mOffline = value;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ChildDNSService::nsIObserver
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSService::Observe(nsISupports *subject, const char *topic,
|
||||
const PRUnichar *data)
|
||||
{
|
||||
// we are only getting called if a preference has changed.
|
||||
NS_ASSERTION(strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
|
||||
"unexpected observe call");
|
||||
|
||||
// Reread prefs
|
||||
Init();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,42 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_net_ChildDNSService_h
|
||||
#define mozilla_net_ChildDNSService_h
|
||||
|
||||
|
||||
#include "nsPIDNSService.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class ChildDNSService MOZ_FINAL
|
||||
: public nsPIDNSService
|
||||
, public nsIObserver
|
||||
{
|
||||
public:
|
||||
// AsyncResolve (and CancelAsyncResolve) can be called off-main
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSPIDNSSERVICE
|
||||
NS_DECL_NSIDNSSERVICE
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
ChildDNSService();
|
||||
virtual ~ChildDNSService();
|
||||
|
||||
static ChildDNSService* GetSingleton();
|
||||
|
||||
private:
|
||||
bool mFirstTime;
|
||||
bool mOffline;
|
||||
bool mDisablePrefetch;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
#endif // mozilla_net_ChildDNSService_h
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/mozalloc.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include <string.h>
|
||||
|
||||
#ifdef XP_WIN
|
||||
|
@ -203,6 +204,31 @@ bool IsIPAddrLocal(const NetAddr *addr)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
NetAddr::operator == (const NetAddr& other) const
|
||||
{
|
||||
if (this->raw.family != other.raw.family) {
|
||||
return false;
|
||||
} else if (this->raw.family == AF_INET) {
|
||||
return (this->inet.port == other.inet.port) &&
|
||||
(this->inet.ip == other.inet.ip);
|
||||
} else if (this->raw.family == AF_INET6) {
|
||||
return (this->inet6.port == other.inet6.port) &&
|
||||
(this->inet6.flowinfo == other.inet6.flowinfo) &&
|
||||
(memcmp(&this->inet6.ip, &other.inet6.ip,
|
||||
sizeof(this->inet6.ip)) == 0) &&
|
||||
(this->inet6.scope_id == other.inet6.scope_id);
|
||||
#if defined(XP_UNIX) || defined(XP_OS2)
|
||||
} else if (this->raw.family == AF_LOCAL) {
|
||||
return PL_strncmp(this->local.path, other.local.path,
|
||||
ArrayLength(this->local.path));
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
NetAddrElement::NetAddrElement(const PRNetAddr *prNetAddr)
|
||||
{
|
||||
PRNetAddrToNetAddr(prNetAddr, &mAddress);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "nscore.h"
|
||||
#include "prio.h"
|
||||
#include "prnetdb.h"
|
||||
#include "plstr.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
|
@ -112,6 +113,8 @@ union NetAddr {
|
|||
#endif
|
||||
} local;
|
||||
#endif
|
||||
// introduced to support nsTArray<NetAddr> (for DNSRequestParent.cpp)
|
||||
bool operator == (const NetAddr& other) const;
|
||||
};
|
||||
|
||||
// This class wraps a NetAddr union to provide C++ linked list
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 : */
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/net/DNSListenerProxy.h"
|
||||
#include "nsICancelable.h"
|
||||
#include "nsIEventTarget.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
NS_IMPL_ISUPPORTS1(DNSListenerProxy, nsIDNSListener)
|
||||
|
||||
NS_IMETHODIMP
|
||||
DNSListenerProxy::OnLookupComplete(nsICancelable* aRequest,
|
||||
nsIDNSRecord* aRecord,
|
||||
nsresult aStatus)
|
||||
{
|
||||
nsRefPtr<OnLookupCompleteRunnable> r =
|
||||
new OnLookupCompleteRunnable(mListener, aRequest, aRecord, aStatus);
|
||||
return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DNSListenerProxy::OnLookupCompleteRunnable::Run()
|
||||
{
|
||||
mListener->OnLookupComplete(mRequest, mRecord, mStatus);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,66 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 : */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef DNSListenerProxy_h__
|
||||
#define DNSListenerProxy_h__
|
||||
|
||||
#include "nsIDNSListener.h"
|
||||
#include "nsIDNSRecord.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
class nsIEventTarget;
|
||||
class nsICancelable;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class DNSListenerProxy MOZ_FINAL : public nsIDNSListener
|
||||
{
|
||||
public:
|
||||
DNSListenerProxy(nsIDNSListener* aListener, nsIEventTarget* aTargetThread)
|
||||
// Sometimes aListener is a main-thread only object like XPCWrappedJS, and
|
||||
// sometimes it's a threadsafe object like nsSOCKSSocketInfo. Use a main-
|
||||
// thread pointer holder, but disable strict enforcement of thread invariants.
|
||||
// The AddRef implementation of XPCWrappedJS will assert if we go wrong here.
|
||||
: mListener(new nsMainThreadPtrHolder<nsIDNSListener>(aListener, false))
|
||||
, mTargetThread(aTargetThread)
|
||||
{ }
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIDNSLISTENER
|
||||
|
||||
class OnLookupCompleteRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
OnLookupCompleteRunnable(const nsMainThreadPtrHandle<nsIDNSListener>& aListener,
|
||||
nsICancelable* aRequest,
|
||||
nsIDNSRecord* aRecord,
|
||||
nsresult aStatus)
|
||||
: mListener(aListener)
|
||||
, mRequest(aRequest)
|
||||
, mRecord(aRecord)
|
||||
, mStatus(aStatus)
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
private:
|
||||
nsMainThreadPtrHandle<nsIDNSListener> mListener;
|
||||
nsCOMPtr<nsICancelable> mRequest;
|
||||
nsCOMPtr<nsIDNSRecord> mRecord;
|
||||
nsresult mStatus;
|
||||
};
|
||||
|
||||
private:
|
||||
nsMainThreadPtrHandle<nsIDNSListener> mListener;
|
||||
nsCOMPtr<nsIEventTarget> mTargetThread;
|
||||
};
|
||||
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
#endif // DNSListenerProxy_h__
|
|
@ -0,0 +1,247 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/net/DNSRequestChild.h"
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
#include "nsIDNSRecord.h"
|
||||
#include "nsHostResolver.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsNetAddr.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ChildDNSRecord:
|
||||
// A simple class to provide nsIDNSRecord on the child
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class ChildDNSRecord : public nsIDNSRecord
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIDNSRECORD
|
||||
|
||||
ChildDNSRecord(const DNSRecord& reply, uint16_t flags);
|
||||
virtual ~ChildDNSRecord();
|
||||
|
||||
private:
|
||||
nsCString mCanonicalName;
|
||||
nsTArray<NetAddr> mAddresses;
|
||||
uint32_t mCurrent; // addr iterator
|
||||
uint32_t mLength; // number of addrs
|
||||
uint16_t mFlags;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(ChildDNSRecord, nsIDNSRecord)
|
||||
|
||||
ChildDNSRecord::ChildDNSRecord(const DNSRecord& reply, uint16_t flags)
|
||||
: mCurrent(0)
|
||||
, mFlags(flags)
|
||||
{
|
||||
mCanonicalName = reply.canonicalName();
|
||||
|
||||
// A shame IPDL gives us no way to grab ownership of array: so copy it.
|
||||
const nsTArray<NetAddr>& addrs = reply.addrs();
|
||||
uint32_t i = 0;
|
||||
mLength = addrs.Length();
|
||||
for (; i < mLength; i++) {
|
||||
mAddresses.AppendElement(addrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
ChildDNSRecord::~ChildDNSRecord()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ChildDNSRecord::nsIDNSRecord
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSRecord::GetCanonicalName(nsACString &result)
|
||||
{
|
||||
if (!(mFlags & nsHostResolver::RES_CANON_NAME)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
result = mCanonicalName;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSRecord::GetNextAddr(uint16_t port, NetAddr *addr)
|
||||
{
|
||||
if (mCurrent >= mLength) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
memcpy(addr, &mAddresses[mCurrent++], sizeof(NetAddr));
|
||||
|
||||
// both Ipv4/6 use same bits for port, so safe to just use ipv4's field
|
||||
addr->inet.port = port;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// shamelessly copied from nsDNSRecord
|
||||
NS_IMETHODIMP
|
||||
ChildDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr **result)
|
||||
{
|
||||
NetAddr addr;
|
||||
nsresult rv = GetNextAddr(port, &addr);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
NS_ADDREF(*result = new nsNetAddr(&addr));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// also copied from nsDNSRecord
|
||||
NS_IMETHODIMP
|
||||
ChildDNSRecord::GetNextAddrAsString(nsACString &result)
|
||||
{
|
||||
NetAddr addr;
|
||||
nsresult rv = GetNextAddr(0, &addr);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
char buf[kIPv6CStrBufSize];
|
||||
if (NetAddrToString(&addr, buf, sizeof(buf))) {
|
||||
result.Assign(buf);
|
||||
return NS_OK;
|
||||
}
|
||||
NS_ERROR("NetAddrToString failed unexpectedly");
|
||||
return NS_ERROR_FAILURE; // conversion failed for some reason
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSRecord::HasMore(bool *result)
|
||||
{
|
||||
*result = mCurrent < mLength;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSRecord::Rewind()
|
||||
{
|
||||
mCurrent = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSRecord::ReportUnusable(uint16_t aPort)
|
||||
{
|
||||
// "We thank you for your feedback" == >/dev/null
|
||||
// TODO: we could send info back to parent.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// DNSRequestChild
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DNSRequestChild::DNSRequestChild(const nsCString& aHost,
|
||||
const uint32_t& aFlags,
|
||||
nsIDNSListener *aListener,
|
||||
nsIEventTarget *target)
|
||||
: mListener(aListener)
|
||||
, mTarget(target)
|
||||
, mResultStatus(NS_OK)
|
||||
, mHost(aHost)
|
||||
, mFlags(aFlags)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
DNSRequestChild::StartRequest()
|
||||
{
|
||||
// we can only do IPDL on the main thread
|
||||
if (!NS_IsMainThread()) {
|
||||
NS_DispatchToMainThread(
|
||||
NS_NewRunnableMethod(this, &DNSRequestChild::StartRequest));
|
||||
return;
|
||||
}
|
||||
|
||||
// Send request to Parent process.
|
||||
gNeckoChild->SendPDNSRequestConstructor(this, mHost, mFlags);
|
||||
|
||||
// IPDL holds a reference until IPDL channel gets destroyed
|
||||
AddIPDLReference();
|
||||
}
|
||||
|
||||
void
|
||||
DNSRequestChild::CallOnLookupComplete()
|
||||
{
|
||||
MOZ_ASSERT(mListener);
|
||||
|
||||
mListener->OnLookupComplete(this, mResultRecord, mResultStatus);
|
||||
}
|
||||
|
||||
bool
|
||||
DNSRequestChild::Recv__delete__(const DNSRequestResponse& reply)
|
||||
{
|
||||
MOZ_ASSERT(mListener);
|
||||
|
||||
switch (reply.type()) {
|
||||
case DNSRequestResponse::TDNSRecord: {
|
||||
mResultRecord = new ChildDNSRecord(reply.get_DNSRecord(), mFlags);
|
||||
break;
|
||||
}
|
||||
case DNSRequestResponse::Tnsresult: {
|
||||
mResultStatus = reply.get_nsresult();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
NS_NOTREACHED("unknown type");
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
bool targetIsMain = false;
|
||||
if (!mTarget) {
|
||||
targetIsMain = true;
|
||||
} else {
|
||||
mTarget->IsOnCurrentThread(&targetIsMain);
|
||||
}
|
||||
|
||||
if (targetIsMain) {
|
||||
CallOnLookupComplete();
|
||||
} else {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(this, &DNSRequestChild::CallOnLookupComplete);
|
||||
mTarget->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// DNSRequestChild::nsISupports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_ISUPPORTS1(DNSRequestChild,
|
||||
nsICancelable)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// DNSRequestChild::nsICancelable
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
DNSRequestChild::Cancel(nsresult reason)
|
||||
{
|
||||
// for now Cancel is a no-op
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
}} // mozilla::net
|
|
@ -0,0 +1,58 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_net_DNSRequestChild_h
|
||||
#define mozilla_net_DNSRequestChild_h
|
||||
|
||||
#include "mozilla/net/PDNSRequestChild.h"
|
||||
#include "nsICancelable.h"
|
||||
#include "nsIDNSRecord.h"
|
||||
#include "nsIDNSListener.h"
|
||||
#include "nsIEventTarget.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class DNSRequestChild
|
||||
: public PDNSRequestChild
|
||||
, public nsICancelable
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSICANCELABLE
|
||||
|
||||
DNSRequestChild(const nsCString& aHost, const uint32_t& aFlags,
|
||||
nsIDNSListener *aListener, nsIEventTarget *target);
|
||||
virtual ~DNSRequestChild() {}
|
||||
|
||||
void AddIPDLReference() {
|
||||
AddRef();
|
||||
}
|
||||
void ReleaseIPDLReference() {
|
||||
// we don't need an 'mIPCOpen' variable until/unless we add calls that might
|
||||
// try to send IPDL msgs to parent after ReleaseIPDLReference is called
|
||||
// (when IPDL channel torn down).
|
||||
Release();
|
||||
}
|
||||
|
||||
// Sends IPDL request to parent
|
||||
void StartRequest();
|
||||
void CallOnLookupComplete();
|
||||
|
||||
private:
|
||||
virtual bool Recv__delete__(const DNSRequestResponse& reply) MOZ_OVERRIDE;
|
||||
|
||||
nsCOMPtr<nsIDNSListener> mListener;
|
||||
nsCOMPtr<nsIEventTarget> mTarget;
|
||||
nsCOMPtr<nsIDNSRecord> mResultRecord;
|
||||
nsresult mResultStatus;
|
||||
nsCString mHost;
|
||||
uint16_t mFlags;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
#endif // mozilla_net_DNSRequestChild_h
|
|
@ -0,0 +1,105 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/net/DNSRequestParent.h"
|
||||
#include "nsIDNSService.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsICancelable.h"
|
||||
#include "nsIDNSRecord.h"
|
||||
#include "nsHostResolver.h"
|
||||
#include "mozilla/unused.h"
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
DNSRequestParent::DNSRequestParent()
|
||||
: mIPCClosed(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DNSRequestParent::~DNSRequestParent()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
DNSRequestParent::DoAsyncResolve(const nsACString &hostname, uint32_t flags)
|
||||
{
|
||||
nsresult rv;
|
||||
mFlags = flags;
|
||||
nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
|
||||
nsCOMPtr<nsICancelable> unused;
|
||||
rv = dns->AsyncResolve(hostname, flags, this, mainThread,
|
||||
getter_AddRefs(unused));
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv) && !mIPCClosed) {
|
||||
unused << Send__delete__(this, DNSRequestResponse(rv));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DNSRequestParent::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
// We may still have refcount>0 if DNS hasn't called our OnLookupComplete
|
||||
// yet, but child process has crashed. We must not send any more msgs
|
||||
// to child, or IPDL will kill chrome process, too.
|
||||
mIPCClosed = true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// DNSRequestParent::nsISupports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_ISUPPORTS1(DNSRequestParent,
|
||||
nsIDNSListener)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIDNSListener functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
DNSRequestParent::OnLookupComplete(nsICancelable *request,
|
||||
nsIDNSRecord *rec,
|
||||
nsresult status)
|
||||
{
|
||||
if (mIPCClosed) {
|
||||
// nothing to do: child probably crashed
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(status)) {
|
||||
MOZ_ASSERT(rec);
|
||||
|
||||
nsAutoCString cname;
|
||||
if (mFlags & nsHostResolver::RES_CANON_NAME) {
|
||||
rec->GetCanonicalName(cname);
|
||||
}
|
||||
|
||||
// Get IP addresses for hostname (use port 80 as dummy value for NetAddr)
|
||||
NetAddrArray array;
|
||||
NetAddr addr;
|
||||
while (NS_SUCCEEDED(rec->GetNextAddr(80, &addr))) {
|
||||
array.AppendElement(addr);
|
||||
}
|
||||
|
||||
unused << Send__delete__(this, DNSRequestResponse(DNSRecord(cname, array)));
|
||||
} else {
|
||||
unused << Send__delete__(this, DNSRequestResponse(status));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}} // mozilla::net
|
|
@ -0,0 +1,39 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_net_DNSRequestParent_h
|
||||
#define mozilla_net_DNSRequestParent_h
|
||||
|
||||
#include "mozilla/net/PDNSRequestParent.h"
|
||||
#include "nsIDNSService.h"
|
||||
#include "nsIDNSListener.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class DNSRequestParent
|
||||
: public PDNSRequestParent
|
||||
, public nsIDNSListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDNSLISTENER
|
||||
|
||||
DNSRequestParent();
|
||||
virtual ~DNSRequestParent();
|
||||
|
||||
void DoAsyncResolve(const nsACString &hostname, uint32_t flags);
|
||||
|
||||
protected:
|
||||
virtual void ActorDestroy(ActorDestroyReason why);
|
||||
private:
|
||||
uint32_t mFlags;
|
||||
bool mIPCClosed; // true if IPDL channel has been closed (child crash)
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
#endif // mozilla_net_DNSRequestParent_h
|
|
@ -0,0 +1,23 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 ft=c: */
|
||||
|
||||
/* 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/. */
|
||||
|
||||
#ifndef PDNSParams_h
|
||||
#define PDNSParams_h
|
||||
|
||||
#include "DNS.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
// Need to define typedef in .h file--can't seem to in ipdl.h file?
|
||||
typedef nsTArray<NetAddr> NetAddrArray;
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // PDNSParams_h
|
|
@ -0,0 +1,31 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
|
||||
|
||||
/* 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/. */
|
||||
|
||||
include protocol PNecko;
|
||||
|
||||
include PDNSRequestParams;
|
||||
|
||||
include "mozilla/net/NeckoMessageUtils.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
async protocol PDNSRequest
|
||||
{
|
||||
manager PNecko;
|
||||
|
||||
//parent:
|
||||
// constructor in PNecko takes AsyncResolve args that initialize request
|
||||
|
||||
|
||||
child:
|
||||
__delete__(DNSRequestResponse reply);
|
||||
|
||||
};
|
||||
|
||||
} //namespace net
|
||||
} //namespace mozilla
|
|
@ -0,0 +1,31 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
|
||||
|
||||
/* 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/. */
|
||||
|
||||
using NetAddrArray from "mozilla/net/PDNSParams.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// DNS IPDL structs
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
struct DNSRecord
|
||||
{
|
||||
nsCString canonicalName;
|
||||
NetAddrArray addrs;
|
||||
};
|
||||
|
||||
union DNSRequestResponse
|
||||
{
|
||||
DNSRecord;
|
||||
nsresult; // if error
|
||||
};
|
||||
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
|
@ -16,7 +16,12 @@ XPIDL_SOURCES += [
|
|||
XPIDL_MODULE = 'necko_dns'
|
||||
|
||||
EXPORTS.mozilla.net += [
|
||||
'ChildDNSService.h',
|
||||
'DNS.h',
|
||||
'DNSListenerProxy.h',
|
||||
'DNSRequestChild.h',
|
||||
'DNSRequestParent.h',
|
||||
'PDNSParams.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
|
@ -25,7 +30,11 @@ SOURCES += [
|
|||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'ChildDNSService.cpp',
|
||||
'DNS.cpp',
|
||||
'DNSListenerProxy.cpp',
|
||||
'DNSRequestChild.cpp',
|
||||
'DNSRequestParent.cpp',
|
||||
'nameprep.c',
|
||||
'nsDNSService2.cpp',
|
||||
'nsIDNService.cpp',
|
||||
|
@ -33,10 +42,17 @@ UNIFIED_SOURCES += [
|
|||
'race.c',
|
||||
]
|
||||
|
||||
IPDL_SOURCES = [
|
||||
'PDNSRequest.ipdl',
|
||||
'PDNSRequestParams.ipdlh',
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
||||
MSVC_ENABLE_PGO = True
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'necko'
|
||||
|
||||
GENERATED_FILES = [
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* vim:set ts=4 sw=4 sts=4 et cin: */
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 : */
|
||||
/* 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/. */
|
||||
|
@ -33,6 +34,9 @@
|
|||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/VisualEventTracer.h"
|
||||
#include "mozilla/net/NeckoCommon.h"
|
||||
#include "mozilla/net/ChildDNSService.h"
|
||||
#include "mozilla/net/DNSListenerProxy.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::net;
|
||||
|
@ -410,6 +414,43 @@ nsDNSService::~nsDNSService()
|
|||
NS_IMPL_ISUPPORTS_INHERITED3(nsDNSService, MemoryUniReporter, nsIDNSService,
|
||||
nsPIDNSService, nsIObserver)
|
||||
|
||||
/******************************************************************************
|
||||
* nsDNSService impl:
|
||||
* singleton instance ctor/dtor methods
|
||||
******************************************************************************/
|
||||
static nsDNSService *gDNSService;
|
||||
|
||||
nsIDNSService*
|
||||
nsDNSService::GetXPCOMSingleton()
|
||||
{
|
||||
if (IsNeckoChild()) {
|
||||
return ChildDNSService::GetSingleton();
|
||||
}
|
||||
|
||||
return GetSingleton();
|
||||
}
|
||||
|
||||
nsDNSService*
|
||||
nsDNSService::GetSingleton()
|
||||
{
|
||||
NS_ASSERTION(!IsNeckoChild(), "not a parent process");
|
||||
|
||||
if (gDNSService) {
|
||||
NS_ADDREF(gDNSService);
|
||||
return gDNSService;
|
||||
}
|
||||
|
||||
gDNSService = new nsDNSService();
|
||||
if (gDNSService) {
|
||||
NS_ADDREF(gDNSService);
|
||||
if (NS_FAILED(gDNSService->Init())) {
|
||||
NS_RELEASE(gDNSService);
|
||||
}
|
||||
}
|
||||
|
||||
return gDNSService;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSService::Init()
|
||||
{
|
||||
|
@ -563,70 +604,6 @@ nsDNSService::SetPrefetchEnabled(bool inVal)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class DNSListenerProxy MOZ_FINAL : public nsIDNSListener
|
||||
{
|
||||
public:
|
||||
DNSListenerProxy(nsIDNSListener* aListener, nsIEventTarget* aTargetThread)
|
||||
// Sometimes aListener is a main-thread only object like XPCWrappedJS, and
|
||||
// sometimes it's a threadsafe object like nsSOCKSSocketInfo. Use a main-
|
||||
// thread pointer holder, but disable strict enforcement of thread invariants.
|
||||
// The AddRef implementation of XPCWrappedJS will assert if we go wrong here.
|
||||
: mListener(new nsMainThreadPtrHolder<nsIDNSListener>(aListener, false))
|
||||
, mTargetThread(aTargetThread)
|
||||
{ }
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIDNSLISTENER
|
||||
|
||||
class OnLookupCompleteRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
OnLookupCompleteRunnable(const nsMainThreadPtrHandle<nsIDNSListener>& aListener,
|
||||
nsICancelable* aRequest,
|
||||
nsIDNSRecord* aRecord,
|
||||
nsresult aStatus)
|
||||
: mListener(aListener)
|
||||
, mRequest(aRequest)
|
||||
, mRecord(aRecord)
|
||||
, mStatus(aStatus)
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
private:
|
||||
nsMainThreadPtrHandle<nsIDNSListener> mListener;
|
||||
nsCOMPtr<nsICancelable> mRequest;
|
||||
nsCOMPtr<nsIDNSRecord> mRecord;
|
||||
nsresult mStatus;
|
||||
};
|
||||
|
||||
private:
|
||||
nsMainThreadPtrHandle<nsIDNSListener> mListener;
|
||||
nsCOMPtr<nsIEventTarget> mTargetThread;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(DNSListenerProxy, nsIDNSListener)
|
||||
|
||||
NS_IMETHODIMP
|
||||
DNSListenerProxy::OnLookupComplete(nsICancelable* aRequest,
|
||||
nsIDNSRecord* aRecord,
|
||||
nsresult aStatus)
|
||||
{
|
||||
nsRefPtr<OnLookupCompleteRunnable> r =
|
||||
new OnLookupCompleteRunnable(mListener, aRequest, aRecord, aStatus);
|
||||
return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DNSListenerProxy::OnLookupCompleteRunnable::Run()
|
||||
{
|
||||
mListener->OnLookupComplete(mRequest, mRecord, mStatus);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSService::AsyncResolve(const nsACString &hostname,
|
||||
|
@ -670,6 +647,7 @@ nsDNSService::AsyncResolve(const nsACString &hostname,
|
|||
hostPtr = &hostACE;
|
||||
}
|
||||
|
||||
// make sure JS callers get notification on the main thread
|
||||
nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener);
|
||||
if (wrappedListener && !target) {
|
||||
nsCOMPtr<nsIThread> mainThread;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 : */
|
||||
/* 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/. */
|
||||
|
@ -30,6 +32,8 @@ public:
|
|||
nsDNSService();
|
||||
~nsDNSService();
|
||||
|
||||
static nsIDNSService* GetXPCOMSingleton();
|
||||
|
||||
int64_t Amount() MOZ_OVERRIDE
|
||||
{
|
||||
return SizeOfIncludingThis(MallocSizeOf);
|
||||
|
@ -37,6 +41,8 @@ public:
|
|||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||
|
||||
private:
|
||||
static nsDNSService* GetSingleton();
|
||||
|
||||
uint16_t GetAFForLookup(const nsACString &host, uint32_t flags);
|
||||
|
||||
nsRefPtr<nsHostResolver> mResolver;
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
* 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/. */
|
||||
|
||||
#ifndef EffectiveTLDService_h
|
||||
#define EffectiveTLDService_h
|
||||
|
||||
#include "nsIEffectiveTLDService.h"
|
||||
|
||||
#include "nsIMemoryReporter.h"
|
||||
|
@ -124,3 +127,5 @@ private:
|
|||
nsTHashtable<nsDomainEntry> mHash;
|
||||
nsCOMPtr<nsIIDNService> mIDNService;
|
||||
};
|
||||
|
||||
#endif // EffectiveTLDService_h
|
||||
|
|
|
@ -878,9 +878,9 @@ nsHostResolver::ConditionallyCreateThread(nsHostRecord *rec)
|
|||
#include "windns.h"
|
||||
#include "windows.h"
|
||||
|
||||
typedef DNS_STATUS (__stdcall * DnsQueryFunc) (PCTSTR lpstrName, WORD wType,
|
||||
typedef DNS_STATUS (__stdcall * DnsQueryFunc) (LPCSTR lpstrName, WORD wType,
|
||||
DWORD Options, PVOID pExtra,
|
||||
PDNS_RECORD *ppQueryResultsSet,
|
||||
PDNS_RECORDA *ppQueryResultsSet,
|
||||
PVOID *pReserved);
|
||||
|
||||
class ExperimentFinishedRunner : public nsRunnable
|
||||
|
@ -1055,7 +1055,7 @@ public:
|
|||
}
|
||||
|
||||
if (srvStatus == DNS_RCODE_NOERROR) {
|
||||
DNS_SRV_DATA *srvData = &srvResults->Data.Srv;
|
||||
DNS_SRV_DATAA *srvData = &srvResults->Data.Srv;
|
||||
if (_stricmp(srvData->pNameTarget, "success.http2test.mozilla.org") ||
|
||||
srvData->wPort != 443 ||
|
||||
srvData->wPriority != 100 ||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "mozilla/net/WyciwygChannelChild.h"
|
||||
#include "mozilla/net/FTPChannelChild.h"
|
||||
#include "mozilla/net/WebSocketChannelChild.h"
|
||||
#include "mozilla/net/DNSRequestChild.h"
|
||||
#include "mozilla/net/RemoteOpenFileChild.h"
|
||||
#include "mozilla/dom/network/TCPSocketChild.h"
|
||||
#include "mozilla/dom/network/TCPServerSocketChild.h"
|
||||
|
@ -230,6 +231,24 @@ NeckoChild::DeallocPUDPSocketChild(PUDPSocketChild* child)
|
|||
return true;
|
||||
}
|
||||
|
||||
PDNSRequestChild*
|
||||
NeckoChild::AllocPDNSRequestChild(const nsCString& aHost,
|
||||
const uint32_t& aFlags)
|
||||
{
|
||||
// We don't allocate here: instead we always use IPDL constructor that takes
|
||||
// an existing object
|
||||
NS_NOTREACHED("AllocPDNSRequestChild should not be called on child");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
NeckoChild::DeallocPDNSRequestChild(PDNSRequestChild* aChild)
|
||||
{
|
||||
DNSRequestChild *p = static_cast<DNSRequestChild*>(aChild);
|
||||
p->ReleaseIPDLReference();
|
||||
return true;
|
||||
}
|
||||
|
||||
PRemoteOpenFileChild*
|
||||
NeckoChild::AllocPRemoteOpenFileChild(const URIParams&, const OptionalURIParams&)
|
||||
{
|
||||
|
|
|
@ -51,6 +51,9 @@ protected:
|
|||
const uint16_t& aPort,
|
||||
const nsCString& aFilter);
|
||||
virtual bool DeallocPUDPSocketChild(PUDPSocketChild*);
|
||||
virtual PDNSRequestChild* AllocPDNSRequestChild(const nsCString& aHost,
|
||||
const uint32_t& aFlags);
|
||||
virtual bool DeallocPDNSRequestChild(PDNSRequestChild*);
|
||||
virtual PRemoteOpenFileChild* AllocPRemoteOpenFileChild(const URIParams&,
|
||||
const OptionalURIParams&);
|
||||
virtual bool DeallocPRemoteOpenFileChild(PRemoteOpenFileChild*);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#ifdef NECKO_PROTOCOL_rtsp
|
||||
#include "mozilla/net/RtspControllerParent.h"
|
||||
#endif
|
||||
#include "mozilla/net/DNSRequestParent.h"
|
||||
#include "mozilla/net/RemoteOpenFileParent.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/TabParent.h"
|
||||
|
@ -428,6 +429,32 @@ NeckoParent::DeallocPUDPSocketParent(PUDPSocketParent* actor)
|
|||
return true;
|
||||
}
|
||||
|
||||
PDNSRequestParent*
|
||||
NeckoParent::AllocPDNSRequestParent(const nsCString& aHost,
|
||||
const uint32_t& aFlags)
|
||||
{
|
||||
DNSRequestParent *p = new DNSRequestParent();
|
||||
p->AddRef();
|
||||
return p;
|
||||
}
|
||||
|
||||
bool
|
||||
NeckoParent::RecvPDNSRequestConstructor(PDNSRequestParent* aActor,
|
||||
const nsCString& aHost,
|
||||
const uint32_t& aFlags)
|
||||
{
|
||||
static_cast<DNSRequestParent*>(aActor)->DoAsyncResolve(aHost, aFlags);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
NeckoParent::DeallocPDNSRequestParent(PDNSRequestParent* aParent)
|
||||
{
|
||||
DNSRequestParent *p = static_cast<DNSRequestParent*>(aParent);
|
||||
p->Release();
|
||||
return true;
|
||||
}
|
||||
|
||||
PRemoteOpenFileParent*
|
||||
NeckoParent::AllocPRemoteOpenFileParent(const URIParams& aURI,
|
||||
const OptionalURIParams& aAppURI)
|
||||
|
|
|
@ -124,6 +124,12 @@ protected:
|
|||
const uint16_t& aPort,
|
||||
const nsCString& aFilter);
|
||||
virtual bool DeallocPUDPSocketParent(PUDPSocketParent*);
|
||||
virtual PDNSRequestParent* AllocPDNSRequestParent(const nsCString& aHost,
|
||||
const uint32_t& aFlags);
|
||||
virtual bool RecvPDNSRequestConstructor(PDNSRequestParent* actor,
|
||||
const nsCString& hostName,
|
||||
const uint32_t& flags);
|
||||
virtual bool DeallocPDNSRequestParent(PDNSRequestParent*);
|
||||
virtual bool RecvHTMLDNSPrefetch(const nsString& hostname,
|
||||
const uint16_t& flags);
|
||||
virtual bool RecvCancelHTMLDNSPrefetch(const nsString& hostname,
|
||||
|
|
|
@ -16,6 +16,7 @@ include protocol PTCPSocket;
|
|||
include protocol PTCPServerSocket;
|
||||
include protocol PUDPSocket;
|
||||
include protocol PRemoteOpenFile;
|
||||
include protocol PDNSRequest;
|
||||
include protocol PBlob; //FIXME: bug #792908
|
||||
|
||||
include protocol PRtspController;
|
||||
|
@ -41,6 +42,7 @@ sync protocol PNecko
|
|||
manages PTCPSocket;
|
||||
manages PTCPServerSocket;
|
||||
manages PUDPSocket;
|
||||
manages PDNSRequest;
|
||||
manages PRemoteOpenFile;
|
||||
manages PRtspController;
|
||||
|
||||
|
@ -59,6 +61,8 @@ parent:
|
|||
PTCPServerSocket(uint16_t localPort, uint16_t backlog, nsString binaryType);
|
||||
PUDPSocket(nsCString host, uint16_t port, nsCString filter);
|
||||
|
||||
PDNSRequest(nsCString hostName, uint32_t flags);
|
||||
|
||||
PRemoteOpenFile(URIParams fileuri, OptionalURIParams appuri);
|
||||
|
||||
HTMLDNSPrefetch(nsString hostname, uint16_t flags);
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
//
|
||||
// Run test script in content process instead of chrome (xpcshell's default)
|
||||
//
|
||||
|
||||
function run_test() {
|
||||
run_test_in_child("../unit/test_dns_service.js");
|
||||
}
|
|
@ -9,6 +9,7 @@ support-files = disabled_test_bug528292_wrap.js
|
|||
[test_channel_close_wrap.js]
|
||||
[test_cookie_header_wrap.js]
|
||||
[test_cookiejars_wrap.js]
|
||||
[test_dns_service_wrap.js]
|
||||
[test_duplicate_headers_wrap.js]
|
||||
[test_event_sink_wrap.js]
|
||||
[test_head_wrap.js]
|
||||
|
|
|
@ -239,6 +239,7 @@ public:
|
|||
enum TapGestureType
|
||||
{
|
||||
TAPGESTURE_LONG,
|
||||
TAPGESTURE_LONG_UP,
|
||||
TAPGESTURE_UP,
|
||||
TAPGESTURE_CONFIRMED,
|
||||
TAPGESTURE_DOUBLE,
|
||||
|
|
|
@ -2002,6 +2002,11 @@ AndroidBridge::HandleLongTap(const CSSIntPoint& aPoint, int32_t aModifiers)
|
|||
NS_LITERAL_CSTRING("Gesture:LongPress"), data));
|
||||
}
|
||||
|
||||
void
|
||||
AndroidBridge::HandleLongTapUp(const CSSIntPoint& aPoint, int32_t aModifiers)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
AndroidBridge::SendAsyncScrollDOMEvent(bool aIsRoot,
|
||||
const CSSRect& aContentRect,
|
||||
|
|
|
@ -409,6 +409,7 @@ public:
|
|||
void HandleDoubleTap(const CSSIntPoint& aPoint, int32_t aModifiers) MOZ_OVERRIDE;
|
||||
void HandleSingleTap(const CSSIntPoint& aPoint, int32_t aModifiers) MOZ_OVERRIDE;
|
||||
void HandleLongTap(const CSSIntPoint& aPoint, int32_t aModifiers) MOZ_OVERRIDE;
|
||||
void HandleLongTapUp(const CSSIntPoint& aPoint, int32_t aModifiers) MOZ_OVERRIDE;
|
||||
void SendAsyncScrollDOMEvent(bool aIsRoot,
|
||||
const CSSRect& aContentRect,
|
||||
const CSSSize& aScrollableSize) MOZ_OVERRIDE;
|
||||
|
|
|
@ -397,6 +397,10 @@ nsLookAndFeel::GetIntImpl(IntID aID, int32_t &aResult)
|
|||
aResult = 1;
|
||||
break;
|
||||
|
||||
case eIntID_ColorPickerAvailable:
|
||||
aResult = 1;
|
||||
break;
|
||||
|
||||
case eIntID_WindowsDefaultTheme:
|
||||
case eIntID_WindowsThemeIdentifier:
|
||||
case eIntID_OperatingSystemVersionIdentifier:
|
||||
|
|
|
@ -23,6 +23,8 @@ public:
|
|||
virtual void HandleDoubleTap(const CSSIntPoint& aPoint, int32_t aModifiers) MOZ_OVERRIDE {}
|
||||
virtual void HandleSingleTap(const CSSIntPoint& aPoint, int32_t aModifiers) MOZ_OVERRIDE {}
|
||||
virtual void HandleLongTap(const CSSIntPoint& aPoint, int32_t aModifiers) MOZ_OVERRIDE {}
|
||||
virtual void HandleLongTapUp(const CSSIntPoint& aPoint, int32_t aModifiers) MOZ_OVERRIDE {}
|
||||
|
||||
virtual void SendAsyncScrollDOMEvent(bool aIsRoot,
|
||||
const CSSRect &aContentRect,
|
||||
const CSSSize &aScrollableSize) MOZ_OVERRIDE {}
|
||||
|
|
|
@ -301,6 +301,11 @@ APZController::HandleLongTap(const CSSIntPoint& aPoint, int32_t aModifiers)
|
|||
{
|
||||
}
|
||||
|
||||
void
|
||||
APZController::HandleLongTapUp(const CSSIntPoint& aPoint, int32_t aModifiers)
|
||||
{
|
||||
}
|
||||
|
||||
// requests that we send a mozbrowserasyncscroll domevent. not in use.
|
||||
void
|
||||
APZController::SendAsyncScrollDOMEvent(bool aIsRoot,
|
||||
|
|
|
@ -35,6 +35,7 @@ public:
|
|||
virtual void HandleDoubleTap(const mozilla::CSSIntPoint& aPoint, int32_t aModifiers);
|
||||
virtual void HandleSingleTap(const mozilla::CSSIntPoint& aPoint, int32_t aModifiers);
|
||||
virtual void HandleLongTap(const mozilla::CSSIntPoint& aPoint, int32_t aModifiers);
|
||||
virtual void HandleLongTapUp(const mozilla::CSSIntPoint& aPoint, int32_t aModifiers);
|
||||
virtual void SendAsyncScrollDOMEvent(bool aIsRoot, const mozilla::CSSRect &aContentRect, const mozilla::CSSSize &aScrollableSize);
|
||||
virtual void PostDelayedTask(Task* aTask, int aDelayMs);
|
||||
virtual void NotifyTransformBegin(const ScrollableLayerGuid& aGuid);
|
||||
|
|
Загрузка…
Ссылка в новой задаче