mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 03:45:46 +00:00
Merge mozilla-central to mozilla-inbound
This commit is contained in:
commit
247a47fac9
1
.flake8
1
.flake8
@ -2,4 +2,3 @@
|
||||
# See http://pep8.readthedocs.io/en/latest/intro.html#configuration
|
||||
ignore = E121, E123, E126, E129, E133, E226, E241, E242, E704, W503, E402
|
||||
max-line-length = 99
|
||||
filename = *.py, +.lint
|
||||
|
@ -1,5 +1,5 @@
|
||||
python
|
||||
import sys
|
||||
sys.path.append('python/gdbpp/')
|
||||
sys.path.append('third_party/python/gdbpp/')
|
||||
import gdbpp
|
||||
end
|
||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -70,9 +70,9 @@ parser/html/java/javaparser/
|
||||
/local.properties
|
||||
|
||||
# Python virtualenv artifacts.
|
||||
python/psutil/**/*.so
|
||||
python/psutil/**/*.pyd
|
||||
python/psutil/build/
|
||||
third_party/python/psutil/**/*.so
|
||||
third_party/python/psutil/**/*.pyd
|
||||
third_party/python/psutil/build/
|
||||
|
||||
# Ignore chrome.manifest files from the devtools loader
|
||||
devtools/client/chrome.manifest
|
||||
|
@ -72,9 +72,9 @@ _OPT\.OBJ/
|
||||
^local.properties$
|
||||
|
||||
# Python stuff installed at build time.
|
||||
^python/psutil/.*\.so
|
||||
^python/psutil/.*\.pyd
|
||||
^python/psutil/build/
|
||||
^third_party/python/psutil/.*\.so
|
||||
^third_party/python/psutil/.*\.pyd
|
||||
^third_party/python/psutil/build/
|
||||
|
||||
# Git repositories
|
||||
.git/
|
||||
|
@ -1,15 +1,15 @@
|
||||
# .lldbinit file for debugging Mozilla
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# For documentation on all of the commands and type summaries defined here
|
||||
# and in the accompanying Python scripts, see python/lldbutils/README.txt.
|
||||
# For documentation on all of the commands and type summaries defined here and
|
||||
# in the accompanying Python scripts, see third_party/python/lldbutils/README.txt.
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# Import the module that defines complex Gecko debugging commands. This assumes
|
||||
# you are either running lldb from the top level source directory, the objdir,
|
||||
# or the dist/bin directory. (.lldbinit files in the objdir and dist/bin set
|
||||
# topsrcdir appropriately.)
|
||||
script topsrcdir = topsrcdir if locals().has_key("topsrcdir") else os.getcwd(); sys.path.append(os.path.join(topsrcdir, "python/lldbutils")); import lldbutils; lldbutils.init()
|
||||
script topsrcdir = topsrcdir if locals().has_key("topsrcdir") else os.getcwd(); sys.path.append(os.path.join(topsrcdir, "third_party/python/lldbutils")); import lldbutils; lldbutils.init()
|
||||
|
||||
# Mozilla's use of UNIFIED_SOURCES to include multiple source files into a
|
||||
# single compiled file breaks lldb breakpoint setting. This works around that.
|
||||
|
2
CLOBBER
2
CLOBBER
@ -22,5 +22,5 @@
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Bug 1361661 - Update Telemetry build and headers.
|
||||
Bug 1346025 - Move vendored python modules to /third_party/python (need to clobber virtualenv)
|
||||
|
||||
|
@ -997,10 +997,6 @@ pref("browser.flash-protected-mode-flip.done", false);
|
||||
|
||||
pref("dom.ipc.shims.enabledWarnings", false);
|
||||
|
||||
// Start the browser in e10s mode
|
||||
pref("browser.tabs.remote.autostart", false);
|
||||
pref("browser.tabs.remote.desktopbehavior", true);
|
||||
|
||||
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
|
||||
// Controls whether and how the Windows NPAPI plugin process is sandboxed.
|
||||
// To get a different setting for a particular plugin replace "default", with
|
||||
@ -1517,7 +1513,11 @@ pref("privacy.usercontext.about_newtab_segregation.enabled", false);
|
||||
pref("privacy.userContext.longPressBehavior", 0);
|
||||
#endif
|
||||
|
||||
#ifndef RELEASE_OR_BETA
|
||||
// Start the browser in e10s mode
|
||||
pref("browser.tabs.remote.autostart", false);
|
||||
pref("browser.tabs.remote.desktopbehavior", true);
|
||||
|
||||
#if !defined(RELEASE_OR_BETA) || defined(MOZ_DEV_EDITION)
|
||||
// At the moment, autostart.2 is used, while autostart.1 is unused.
|
||||
// We leave it here set to false to reset users' defaults and allow
|
||||
// us to change everybody to true in the future, when desired.
|
||||
@ -1663,7 +1663,7 @@ pref("extensions.formautofill.experimental", true);
|
||||
pref("extensions.formautofill.experimental", false);
|
||||
#endif
|
||||
pref("extensions.formautofill.addresses.enabled", true);
|
||||
pref("extensions.formautofill.heuristics.enabled", false);
|
||||
pref("extensions.formautofill.heuristics.enabled", true);
|
||||
pref("extensions.formautofill.loglevel", "Warn");
|
||||
|
||||
// Whether or not to restore a session with lazy-browser tabs.
|
||||
|
@ -138,16 +138,6 @@ tabbrowser {
|
||||
visibility: hidden; /* temporary space to keep a tab's close button under the cursor */
|
||||
}
|
||||
|
||||
.tabs-newtab-button > .toolbarbutton-menu-dropmarker,
|
||||
#new-tab-button > .toolbarbutton-menu-dropmarker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* override drop marker image padding */
|
||||
.tabs-newtab-button > .toolbarbutton-icon {
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
|
||||
.tabbrowser-tab {
|
||||
-moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tab");
|
||||
}
|
||||
|
@ -169,7 +169,6 @@
|
||||
consumeoutsideclicks="false"
|
||||
level="parent"
|
||||
tabspecific="true">
|
||||
<iframe id="dateTimePopupFrame"/>
|
||||
</panel>
|
||||
|
||||
<!-- for select dropdowns. The menupopup is what shows the list of options,
|
||||
|
@ -28,6 +28,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "setTimeout",
|
||||
"resource://gre/modules/Timer.jsm");
|
||||
|
||||
var {Cc: classes, Ci: interfaces} = Components;
|
||||
|
||||
/**
|
||||
* A number of iterations after which to yield time back
|
||||
* to the system.
|
||||
|
@ -2876,7 +2876,7 @@
|
||||
}
|
||||
|
||||
// Mute audio immediately to improve perceived speed of tab closure.
|
||||
if (aTab.hasAttribute("soundplaying")) {
|
||||
if (!aAdoptedByTab && aTab.hasAttribute("soundplaying")) {
|
||||
// Don't persist the muted state as this wasn't a user action.
|
||||
// This lets undo-close-tab return it to an unmuted state.
|
||||
aTab.linkedBrowser.mute(true);
|
||||
|
@ -63,7 +63,9 @@ add_task(async function test_setup_html() {
|
||||
let video = videoIframe.contentDocument.querySelector("video");
|
||||
|
||||
audio.loop = true;
|
||||
audio.src = "audio.ogg";
|
||||
video.loop = true;
|
||||
video.src = "video.ogg";
|
||||
|
||||
let awaitPause = ContentTaskUtils.waitForEvent(audio, "pause");
|
||||
await ContentTaskUtils.waitForCondition(() => !audio.paused, "Making sure audio is playing before calling pause");
|
||||
|
@ -3,8 +3,17 @@
|
||||
|
||||
const PREF_NEWTAB_ENABLED = "browser.newtabpage.enabled";
|
||||
const PREF_NEWTAB_DIRECTORYSOURCE = "browser.newtabpage.directory.source";
|
||||
const PREF_NEWTAB_ACTIVITY_STREAM = "browser.newtabpage.activity-stream.enabled";
|
||||
|
||||
Services.prefs.setBoolPref(PREF_NEWTAB_ENABLED, true);
|
||||
Services.prefs.setBoolPref(PREF_NEWTAB_ACTIVITY_STREAM, false);
|
||||
|
||||
// Opens and closes a new tab to clear any existing preloaded ones. This is
|
||||
// necessary to prevent any left-over activity-stream preloaded new tabs from
|
||||
// affecting these tests.
|
||||
BrowserOpenTab();
|
||||
const initialTab = gBrowser.selectedTab;
|
||||
gBrowser.removeTab(initialTab);
|
||||
|
||||
var tmp = {};
|
||||
Cu.import("resource://gre/modules/NewTabUtils.jsm", tmp);
|
||||
@ -78,6 +87,7 @@ registerCleanupFunction(function() {
|
||||
});
|
||||
|
||||
Services.prefs.clearUserPref(PREF_NEWTAB_ENABLED);
|
||||
Services.prefs.clearUserPref(PREF_NEWTAB_ACTIVITY_STREAM);
|
||||
Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, gOrigDirectorySource);
|
||||
|
||||
return watchLinksChangeOnce();
|
||||
|
@ -1012,22 +1012,27 @@ this.PanelMultiView = class {
|
||||
// Take the label for toolbarbuttons; it only exists on those elements.
|
||||
element = element.labelElement || element;
|
||||
|
||||
let bounds = this._dwu.getBoundsWithoutFlushing(element);
|
||||
let bounds = element.getBoundingClientRect();
|
||||
let previous = this._multiLineElementsMap.get(element);
|
||||
// Only remove the 'height' property, which will cause a layout flush, when
|
||||
// absolutely necessary.
|
||||
// We don't need to (re-)apply the workaround for invisible elements or
|
||||
// on elements we've seen before and haven't changed in the meantime.
|
||||
if (!bounds.width || !bounds.height ||
|
||||
(previous && element.textContent == previous.textContent &&
|
||||
bounds.width == previous.bounds.width)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
element.style.removeProperty("height");
|
||||
items.push({ element });
|
||||
}
|
||||
|
||||
// Removing the 'height' property will only cause a layout flush in the next
|
||||
// loop below if it was set.
|
||||
for (let item of items) {
|
||||
item.element.style.removeProperty("height");
|
||||
}
|
||||
|
||||
// We now read the computed style to store the height of any element that
|
||||
// may contain wrapping text, which will be zero if the element is hidden.
|
||||
// may contain wrapping text.
|
||||
for (let item of items) {
|
||||
item.bounds = item.element.getBoundingClientRect();
|
||||
}
|
||||
|
@ -60,7 +60,33 @@ const PanelUI = {
|
||||
Services.obs.addObserver(this, "fullscreen-nav-toolbox");
|
||||
Services.obs.addObserver(this, "appMenu-notifications");
|
||||
|
||||
window.addEventListener("fullscreen", this);
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "autoHideToolbarInFullScreen",
|
||||
"browser.fullscreen.autohide", false, (pref, previousValue, newValue) => {
|
||||
// On OSX, or with autohide preffed off, MozDOMFullscreen is the only
|
||||
// event we care about, since fullscreen should behave just like non
|
||||
// fullscreen. Otherwise, we don't want to listen to these because
|
||||
// we'd just be spamming ourselves with both of them whenever a user
|
||||
// opened a video.
|
||||
if (newValue) {
|
||||
window.removeEventListener("MozDOMFullscreen:Entered", this);
|
||||
window.removeEventListener("MozDOMFullscreen:Exited", this);
|
||||
window.addEventListener("fullscreen", this);
|
||||
} else {
|
||||
window.addEventListener("MozDOMFullscreen:Entered", this);
|
||||
window.addEventListener("MozDOMFullscreen:Exited", this);
|
||||
window.removeEventListener("fullscreen", this);
|
||||
}
|
||||
|
||||
this._updateNotifications(false);
|
||||
}, autoHidePref => autoHidePref && Services.appinfo.OS !== "Darwin");
|
||||
|
||||
if (this.autoHideToolbarInFullScreen) {
|
||||
window.addEventListener("fullscreen", this);
|
||||
} else {
|
||||
window.addEventListener("MozDOMFullscreen:Entered", this);
|
||||
window.addEventListener("MozDOMFullscreen:Exited", this);
|
||||
}
|
||||
|
||||
window.addEventListener("activate", this);
|
||||
window.matchMedia("(-moz-overlay-scrollbars)").addListener(this._overlayScrollListenerBoundFn);
|
||||
CustomizableUI.addListener(this);
|
||||
@ -175,6 +201,8 @@ const PanelUI = {
|
||||
Services.obs.removeObserver(this, "fullscreen-nav-toolbox");
|
||||
Services.obs.removeObserver(this, "appMenu-notifications");
|
||||
|
||||
window.removeEventListener("MozDOMFullscreen:Entered", this);
|
||||
window.removeEventListener("MozDOMFullscreen:Exited", this);
|
||||
window.removeEventListener("fullscreen", this);
|
||||
window.removeEventListener("activate", this);
|
||||
this.menuButton.removeEventListener("mousedown", this);
|
||||
@ -326,6 +354,8 @@ const PanelUI = {
|
||||
case "keypress":
|
||||
this.toggle(aEvent);
|
||||
break;
|
||||
case "MozDOMFullscreen:Entered":
|
||||
case "MozDOMFullscreen:Exited":
|
||||
case "fullscreen":
|
||||
case "activate":
|
||||
this._updateNotifications();
|
||||
@ -744,10 +774,8 @@ const PanelUI = {
|
||||
this._showBannerItem(notifications[0]);
|
||||
}
|
||||
} else if (doorhangers.length > 0) {
|
||||
let autoHideFullScreen = Services.prefs.getBoolPref("browser.fullscreen.autohide", false) &&
|
||||
Services.appinfo.OS !== "Darwin";
|
||||
// Only show the doorhanger if the window is focused and not fullscreen
|
||||
if ((window.fullScreen && autoHideFullScreen) || Services.focus.activeWindow !== window) {
|
||||
if ((window.fullScreen && this.autoHideToolbarInFullScreen) || Services.focus.activeWindow !== window) {
|
||||
this._hidePopup();
|
||||
this._showBadge(doorhangers[0]);
|
||||
this._showBannerItem(doorhangers[0]);
|
||||
|
@ -156,6 +156,8 @@ skip-if = os == "mac"
|
||||
[browser_panelUINotifications_fullscreen.js]
|
||||
tags = fullscreen
|
||||
skip-if = os == "mac"
|
||||
[browser_panelUINotifications_fullscreen_noAutoHideToolbar.js]
|
||||
tags = fullscreen
|
||||
[browser_panelUINotifications_multiWindow.js]
|
||||
[browser_switch_to_customize_mode.js]
|
||||
[browser_synced_tabs_menu.js]
|
||||
|
@ -0,0 +1,57 @@
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/AppMenuNotifications.jsm");
|
||||
|
||||
add_task(async function testFullscreen() {
|
||||
if (Services.appinfo.OS !== "Darwin") {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.fullscreen.autohide", false],
|
||||
]});
|
||||
}
|
||||
|
||||
is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
|
||||
let mainActionCalled = false;
|
||||
let mainAction = {
|
||||
callback: () => { mainActionCalled = true; }
|
||||
};
|
||||
AppMenuNotifications.showNotification("update-manual", mainAction);
|
||||
|
||||
isnot(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is showing.");
|
||||
let notifications = [...PanelUI.notificationPanel.children].filter(n => !n.hidden);
|
||||
is(notifications.length, 1, "PanelUI doorhanger is only displaying one notification.");
|
||||
let doorhanger = notifications[0];
|
||||
is(doorhanger.id, "appMenu-update-manual-notification", "PanelUI is displaying the update-manual notification.");
|
||||
|
||||
let fullscreenPromise = BrowserTestUtils.waitForEvent(window, "fullscreen");
|
||||
EventUtils.synthesizeKey("VK_F11", {});
|
||||
await fullscreenPromise;
|
||||
isnot(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is still showing after entering fullscreen.");
|
||||
|
||||
let popuphiddenPromise = BrowserTestUtils.waitForEvent(PanelUI.notificationPanel, "popuphidden");
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, {}, async () => {
|
||||
content.document.documentElement.requestFullscreen();
|
||||
});
|
||||
await popuphiddenPromise;
|
||||
await new Promise(executeSoon);
|
||||
is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is hidden after entering DOM fullscreen.");
|
||||
|
||||
let popupshownPromise = BrowserTestUtils.waitForEvent(PanelUI.notificationPanel, "popupshown");
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, {}, async () => {
|
||||
content.document.exitFullscreen();
|
||||
});
|
||||
await popupshownPromise;
|
||||
await new Promise(executeSoon);
|
||||
isnot(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is shown after exiting DOM fullscreen.");
|
||||
isnot(PanelUI.menuButton.getAttribute("badge-status"), "update-manual", "Badge is not displaying on PanelUI button.");
|
||||
|
||||
let mainActionButton = document.getAnonymousElementByAttribute(doorhanger, "anonid", "button");
|
||||
mainActionButton.click();
|
||||
ok(mainActionCalled, "Main action callback was called");
|
||||
is(PanelUI.notificationPanel.state, "closed", "update-manual doorhanger is closed.");
|
||||
is(PanelUI.menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
|
||||
|
||||
fullscreenPromise = BrowserTestUtils.waitForEvent(window, "fullscreen");
|
||||
EventUtils.synthesizeKey("VK_F11", {});
|
||||
await fullscreenPromise;
|
||||
});
|
@ -25,17 +25,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "HttpServer",
|
||||
var gTestTargetFile = FileUtils.getFile("TmpD", ["dm-ui-test.file"]);
|
||||
gTestTargetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
|
||||
|
||||
// Load mocking/stubbing library, sinon
|
||||
// docs: http://sinonjs.org/docs/
|
||||
/* global sinon:false */
|
||||
Services.scriptloader.loadSubScript("resource://testing-common/sinon-1.16.1.js");
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gTestTargetFile.remove(false);
|
||||
|
||||
delete window.sinon;
|
||||
delete window.setImmediate;
|
||||
delete window.clearImmediate;
|
||||
});
|
||||
|
||||
// Asynchronous support subroutines
|
||||
|
@ -4,13 +4,16 @@ let Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
const PREF_NEWTAB_ACTIVITY_STREAM = "browser.newtabpage.activity-stream.enabled";
|
||||
|
||||
Services.prefs.setBoolPref(PREF_NEWTAB_ACTIVITY_STREAM, false);
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
|
||||
"@mozilla.org/browser/aboutnewtab-service;1",
|
||||
"nsIAboutNewTabService");
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
Services.prefs.setBoolPref("browser.newtabpage.activity-stream.enabled", false);
|
||||
Services.prefs.clearUserPref(PREF_NEWTAB_ACTIVITY_STREAM);
|
||||
aboutNewTabService.resetNewTabURL();
|
||||
});
|
||||
|
||||
|
@ -95,6 +95,7 @@ var gSyncPane = {
|
||||
"weave:service:logout:finish",
|
||||
FxAccountsCommon.ONVERIFIED_NOTIFICATION,
|
||||
FxAccountsCommon.ONLOGIN_NOTIFICATION,
|
||||
FxAccountsCommon.ON_ACCOUNT_STATE_CHANGE_NOTIFICATION,
|
||||
FxAccountsCommon.ON_PROFILE_CHANGE_NOTIFICATION,
|
||||
];
|
||||
// Add the observers now and remove them on unload
|
||||
|
@ -51,6 +51,7 @@ skip-if = true || !healthreport # Bug 1185403 for the "true"
|
||||
[browser_security.js]
|
||||
[browser_siteData.js]
|
||||
[browser_siteData2.js]
|
||||
[browser_site_login_exceptions.js]
|
||||
[browser_subdialogs.js]
|
||||
support-files =
|
||||
subdialog.xul
|
||||
|
@ -6,8 +6,8 @@
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
/* import-globals-from ../../../../../testing/modules/sinon-1.16.1.js */
|
||||
Services.scriptloader.loadSubScript("resource://testing-common/sinon-1.16.1.js");
|
||||
/* global sinon */
|
||||
Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
|
||||
|
||||
const TEST_QUOTA_USAGE_HOST = "example.com";
|
||||
const TEST_QUOTA_USAGE_ORIGIN = "https://" + TEST_QUOTA_USAGE_HOST;
|
||||
@ -89,8 +89,6 @@ function promiseCookiesCleared() {
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
delete window.sinon;
|
||||
delete window.setImmediate;
|
||||
delete window.clearImmediate;
|
||||
mockOfflineAppCacheHelper.unregister();
|
||||
});
|
||||
|
||||
|
@ -0,0 +1,76 @@
|
||||
"use strict";
|
||||
const PERMISSIONS_URL = "chrome://browser/content/preferences/permissions.xul";
|
||||
|
||||
var exceptionsDialog;
|
||||
|
||||
add_task(async function openLoginExceptionsSubDialog() {
|
||||
// Undo the save password change.
|
||||
registerCleanupFunction(async function() {
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
|
||||
let doc = content.document;
|
||||
let savePasswordCheckBox = doc.getElementById("savePasswords");
|
||||
if (savePasswordCheckBox.checked) {
|
||||
savePasswordCheckBox.click();
|
||||
}
|
||||
});
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
await openPreferencesViaOpenPreferencesAPI("privacy", {leaveOpen: true});
|
||||
|
||||
let dialogOpened = promiseLoadSubDialog(PERMISSIONS_URL);
|
||||
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
|
||||
let doc = content.document;
|
||||
let savePasswordCheckBox = doc.getElementById("savePasswords");
|
||||
Assert.ok(!savePasswordCheckBox.checked,
|
||||
"Save Password CheckBox should be unchecked by default");
|
||||
savePasswordCheckBox.click();
|
||||
|
||||
let loginExceptionsButton = doc.getElementById("passwordExceptions");
|
||||
loginExceptionsButton.click();
|
||||
});
|
||||
|
||||
exceptionsDialog = await dialogOpened;
|
||||
});
|
||||
|
||||
add_task(async function addALoginException() {
|
||||
let doc = exceptionsDialog.document;
|
||||
|
||||
let tree = doc.getElementById("permissionsTree");
|
||||
Assert.equal(tree.view.rowCount, 0, "Row count should initially be 0");
|
||||
|
||||
let inputBox = doc.getElementById("url");
|
||||
inputBox.focus();
|
||||
|
||||
EventUtils.sendString("www.example.com", exceptionsDialog);
|
||||
|
||||
let btnBlock = doc.getElementById("btnBlock");
|
||||
btnBlock.click();
|
||||
|
||||
await waitForCondition(() => tree.view.rowCount == 1);
|
||||
|
||||
Assert.equal(tree.view.getCellText(0, tree.treeBoxObject.columns.getColumnAt(0)),
|
||||
"http://www.example.com");
|
||||
});
|
||||
|
||||
add_task(async function deleteALoginException() {
|
||||
let doc = exceptionsDialog.document;
|
||||
|
||||
let tree = doc.getElementById("permissionsTree");
|
||||
Assert.equal(tree.view.rowCount, 1, "Row count should initially be 1");
|
||||
tree.focus();
|
||||
tree.view.selection.select(0);
|
||||
|
||||
if (AppConstants.platform == "macosx") {
|
||||
EventUtils.synthesizeKey("VK_BACK_SPACE", {});
|
||||
} else {
|
||||
EventUtils.synthesizeKey("VK_DELETE", {});
|
||||
}
|
||||
|
||||
await waitForCondition(() => tree.view.rowCount == 0);
|
||||
|
||||
is_element_visible(content.gSubDialog._dialogs[0]._box,
|
||||
"Subdialog is visible after deleting an element");
|
||||
});
|
@ -95,6 +95,7 @@ var gSyncPane = {
|
||||
"weave:service:logout:finish",
|
||||
FxAccountsCommon.ONVERIFIED_NOTIFICATION,
|
||||
FxAccountsCommon.ONLOGIN_NOTIFICATION,
|
||||
FxAccountsCommon.ON_ACCOUNT_STATE_CHANGE_NOTIFICATION,
|
||||
FxAccountsCommon.ON_PROFILE_CHANGE_NOTIFICATION,
|
||||
];
|
||||
// Add the observers now and remove them on unload
|
||||
|
@ -41,6 +41,7 @@ skip-if = true || !healthreport # Bug 1185403 for the "true"
|
||||
[browser_sanitizeOnShutdown_prefLocked.js]
|
||||
[browser_searchsuggestions.js]
|
||||
[browser_security.js]
|
||||
[browser_site_login_exceptions.js]
|
||||
[browser_subdialogs.js]
|
||||
support-files =
|
||||
subdialog.xul
|
||||
|
@ -6,8 +6,8 @@
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
/* import-globals-from ../../../../../testing/modules/sinon-1.16.1.js */
|
||||
Services.scriptloader.loadSubScript("resource://testing-common/sinon-1.16.1.js");
|
||||
/* global sinon */
|
||||
Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
|
||||
|
||||
const TEST_QUOTA_USAGE_HOST = "example.com";
|
||||
const TEST_QUOTA_USAGE_ORIGIN = "https://" + TEST_QUOTA_USAGE_HOST;
|
||||
@ -182,8 +182,6 @@ function assertSitesListed(doc, hosts) {
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
delete window.sinon;
|
||||
delete window.setImmediate;
|
||||
delete window.clearImmediate;
|
||||
mockOfflineAppCacheHelper.unregister();
|
||||
});
|
||||
|
||||
|
@ -0,0 +1,76 @@
|
||||
"use strict";
|
||||
const PERMISSIONS_URL = "chrome://browser/content/preferences/permissions.xul";
|
||||
|
||||
var exceptionsDialog;
|
||||
|
||||
add_task(async function openLoginExceptionsSubDialog() {
|
||||
// Undo the save password change.
|
||||
registerCleanupFunction(async function() {
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
|
||||
let doc = content.document;
|
||||
let savePasswordCheckBox = doc.getElementById("savePasswords");
|
||||
if (savePasswordCheckBox.checked) {
|
||||
savePasswordCheckBox.click();
|
||||
}
|
||||
});
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
await openPreferencesViaOpenPreferencesAPI("security", null, {leaveOpen: true});
|
||||
|
||||
let dialogOpened = promiseLoadSubDialog(PERMISSIONS_URL);
|
||||
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
|
||||
let doc = content.document;
|
||||
let savePasswordCheckBox = doc.getElementById("savePasswords");
|
||||
Assert.ok(!savePasswordCheckBox.checked,
|
||||
"Save Password CheckBox should be unchecked by default");
|
||||
savePasswordCheckBox.click();
|
||||
|
||||
let loginExceptionsButton = doc.getElementById("passwordExceptions");
|
||||
loginExceptionsButton.click();
|
||||
});
|
||||
|
||||
exceptionsDialog = await dialogOpened;
|
||||
});
|
||||
|
||||
add_task(async function addALoginException() {
|
||||
let doc = exceptionsDialog.document;
|
||||
|
||||
let tree = doc.getElementById("permissionsTree");
|
||||
Assert.equal(tree.view.rowCount, 0, "Row count should initially be 0");
|
||||
|
||||
let inputBox = doc.getElementById("url");
|
||||
inputBox.focus();
|
||||
|
||||
EventUtils.sendString("www.example.com", exceptionsDialog);
|
||||
|
||||
let btnBlock = doc.getElementById("btnBlock");
|
||||
btnBlock.click();
|
||||
|
||||
await waitForCondition(() => tree.view.rowCount == 1);
|
||||
|
||||
Assert.equal(tree.view.getCellText(0, tree.treeBoxObject.columns.getColumnAt(0)),
|
||||
"http://www.example.com");
|
||||
});
|
||||
|
||||
add_task(async function deleteALoginException() {
|
||||
let doc = exceptionsDialog.document;
|
||||
|
||||
let tree = doc.getElementById("permissionsTree");
|
||||
Assert.equal(tree.view.rowCount, 1, "Row count should initially be 1");
|
||||
tree.focus();
|
||||
tree.view.selection.select(0);
|
||||
|
||||
if (AppConstants.platform == "macosx") {
|
||||
EventUtils.synthesizeKey("VK_BACK_SPACE", {});
|
||||
} else {
|
||||
EventUtils.synthesizeKey("VK_DELETE", {});
|
||||
}
|
||||
|
||||
await waitForCondition(() => tree.view.rowCount == 0);
|
||||
|
||||
is_element_visible(content.gSubDialog._dialogs[0]._box,
|
||||
"Subdialog is visible after deleting an element");
|
||||
});
|
@ -9,11 +9,9 @@ Cu.import("resource://gre/modules/Promise.jsm");
|
||||
// docs: http://sinonjs.org/docs/
|
||||
/* global sinon */
|
||||
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
|
||||
loader.loadSubScript("resource://testing-common/sinon-1.16.1.js");
|
||||
loader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
|
||||
|
||||
registerCleanupFunction(function*() {
|
||||
// Cleanup window or the test runner will throw an error
|
||||
delete window.sinon;
|
||||
delete window.setImmediate;
|
||||
delete window.clearImmediate;
|
||||
});
|
||||
|
@ -7,23 +7,24 @@ XPCOMUtils.defineLazyGetter(this, "FxAccountsCommon", function() {
|
||||
return Components.utils.import("resource://gre/modules/FxAccountsCommon.js", {});
|
||||
});
|
||||
|
||||
Cu.import("resource://gre/modules/Timer.jsm");
|
||||
|
||||
do_get_profile(); // fxa needs a profile directory for storage.
|
||||
|
||||
// Create a window polyfill so sinon can load
|
||||
let window = {
|
||||
document: {},
|
||||
location: {},
|
||||
setTimeout,
|
||||
setInterval,
|
||||
clearTimeout,
|
||||
clearInterval,
|
||||
};
|
||||
let self = window;
|
||||
|
||||
// ================================================
|
||||
// Load mocking/stubbing library, sinon
|
||||
// docs: http://sinonjs.org/docs/
|
||||
/* global sinon */
|
||||
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
|
||||
loader.loadSubScript("resource://testing-common/sinon-1.16.1.js");
|
||||
// docs: http://sinonjs.org/releases/v2.3.2/
|
||||
Cu.import("resource://gre/modules/Timer.jsm");
|
||||
const {Loader} = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {});
|
||||
const loader = new Loader.Loader({
|
||||
paths: {
|
||||
"": "resource://testing-common/",
|
||||
},
|
||||
globals: {
|
||||
setTimeout,
|
||||
setInterval,
|
||||
clearTimeout,
|
||||
clearInterval,
|
||||
},
|
||||
});
|
||||
const require = Loader.Require(loader, {id: ""});
|
||||
const sinon = require("sinon-2.3.2");
|
||||
// ================================================
|
||||
|
@ -12,6 +12,7 @@ let { DeckView } = Cu.import("resource:///modules/syncedtabs/SyncedTabsDeckView.
|
||||
add_task(async function testInitUninit() {
|
||||
let deckStore = new SyncedTabsDeckStore();
|
||||
let listComponent = {};
|
||||
let mockWindow = {};
|
||||
|
||||
let ViewMock = sinon.stub();
|
||||
let view = {render: sinon.spy(), destroy: sinon.spy(), container: {}};
|
||||
@ -23,7 +24,7 @@ add_task(async function testInitUninit() {
|
||||
sinon.stub(deckStore, "setPanels");
|
||||
|
||||
let component = new SyncedTabsDeckComponent({
|
||||
window,
|
||||
window: mockWindow,
|
||||
deckStore,
|
||||
listComponent,
|
||||
SyncedTabs,
|
||||
@ -38,7 +39,7 @@ add_task(async function testInitUninit() {
|
||||
SyncedTabs.syncTabs.restore();
|
||||
|
||||
Assert.ok(ViewMock.calledWithNew(), "view is instantiated");
|
||||
Assert.equal(ViewMock.args[0][0], window);
|
||||
Assert.equal(ViewMock.args[0][0], mockWindow);
|
||||
Assert.equal(ViewMock.args[0][1], listComponent);
|
||||
Assert.ok(ViewMock.args[0][2].onAndroidClick,
|
||||
"view is passed onAndroidClick prop");
|
||||
@ -81,6 +82,7 @@ add_task(async function testObserver() {
|
||||
let deckStore = new SyncedTabsDeckStore();
|
||||
let listStore = new SyncedTabsListStore(SyncedTabs);
|
||||
let listComponent = {};
|
||||
let mockWindow = {};
|
||||
|
||||
let ViewMock = sinon.stub();
|
||||
let view = {render: sinon.spy(), destroy: sinon.spy(), container: {}};
|
||||
@ -94,7 +96,7 @@ add_task(async function testObserver() {
|
||||
sinon.stub(listStore, "getData");
|
||||
|
||||
let component = new SyncedTabsDeckComponent({
|
||||
window,
|
||||
mockWindow,
|
||||
deckStore,
|
||||
listStore,
|
||||
listComponent,
|
||||
|
@ -25,6 +25,7 @@ add_task(function* testInitUninit() {
|
||||
let store = new SyncedTabsListStore();
|
||||
let ViewMock = sinon.stub();
|
||||
let view = {render() {}, destroy() {}};
|
||||
let mockWindow = {};
|
||||
|
||||
ViewMock.returns(view);
|
||||
|
||||
@ -35,7 +36,7 @@ add_task(function* testInitUninit() {
|
||||
sinon.stub(store, "getData");
|
||||
sinon.stub(store, "focusInput");
|
||||
|
||||
let component = new TabListComponent({window, store, View: ViewMock, SyncedTabs});
|
||||
let component = new TabListComponent({window: mockWindow, store, View: ViewMock, SyncedTabs});
|
||||
|
||||
for (let action of ACTION_METHODS) {
|
||||
sinon.stub(component, action);
|
||||
|
@ -23,11 +23,25 @@ XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
|
||||
|
||||
do_get_profile();
|
||||
|
||||
// Setup the environment for sinon.
|
||||
// ================================================
|
||||
// Load mocking/stubbing library, sinon
|
||||
// docs: http://sinonjs.org/releases/v2.3.2/
|
||||
Cu.import("resource://gre/modules/Timer.jsm");
|
||||
let self = {}; // eslint-disable-line no-unused-vars
|
||||
var sinon;
|
||||
Services.scriptloader.loadSubScript("resource://testing-common/sinon-1.16.1.js");
|
||||
const {Loader} = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {});
|
||||
const loader = new Loader.Loader({
|
||||
paths: {
|
||||
"": "resource://testing-common/",
|
||||
},
|
||||
globals: {
|
||||
setTimeout,
|
||||
setInterval,
|
||||
clearTimeout,
|
||||
clearInterval,
|
||||
},
|
||||
});
|
||||
const require = Loader.Require(loader, {id: ""});
|
||||
const sinon = require("sinon-2.3.2");
|
||||
// ================================================
|
||||
|
||||
// Load our bootstrap extension manifest so we can access our chrome/resource URIs.
|
||||
const EXTENSION_ID = "formautofill@mozilla.org";
|
||||
|
@ -102,14 +102,20 @@ class Onboarding {
|
||||
}
|
||||
}
|
||||
|
||||
addEventListener("load", function(evt) {
|
||||
addEventListener("load", function onLoad(evt) {
|
||||
if (!content || evt.target != content.document) {
|
||||
return;
|
||||
}
|
||||
removeEventListener("load", onLoad);
|
||||
|
||||
let window = evt.target.defaultView;
|
||||
// Load onboarding module only when we enable it.
|
||||
if ((content.location.href == ABOUT_NEWTAB_URL ||
|
||||
content.location.href == ABOUT_HOME_URL) &&
|
||||
if ((window.location.href == ABOUT_NEWTAB_URL ||
|
||||
window.location.href == ABOUT_HOME_URL) &&
|
||||
Services.prefs.getBoolPref("browser.onboarding.enabled", false)) {
|
||||
|
||||
content.requestIdleCallback(() => {
|
||||
new Onboarding(content);
|
||||
window.requestIdleCallback(() => {
|
||||
new Onboarding(window);
|
||||
});
|
||||
}
|
||||
}, true);
|
||||
|
@ -11,7 +11,7 @@ Cu.import("resource://shield-recipe-client/lib/Utils.jsm", this);
|
||||
// docs: http://sinonjs.org/docs/
|
||||
const loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
|
||||
/* global sinon */
|
||||
loader.loadSubScript("resource://testing-common/sinon-1.16.1.js");
|
||||
loader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
|
||||
|
||||
// Make sinon assertions fail in a way that mochitest understands
|
||||
sinon.assert.fail = function(message) {
|
||||
@ -21,8 +21,6 @@ sinon.assert.fail = function(message) {
|
||||
registerCleanupFunction(async function() {
|
||||
// Cleanup window or the test runner will throw an error
|
||||
delete window.sinon;
|
||||
delete window.setImmediate;
|
||||
delete window.clearImmediate;
|
||||
});
|
||||
|
||||
|
||||
|
@ -20,13 +20,22 @@ if (!extensionDir.exists()) {
|
||||
}
|
||||
Components.manager.addBootstrappedManifestLocation(extensionDir);
|
||||
|
||||
// Load Sinon for mocking/stubbing during tests.
|
||||
// Sinon assumes that setTimeout and friends are available, and looks for a
|
||||
// global object named self during initialization.
|
||||
// ================================================
|
||||
// Load mocking/stubbing library, sinon
|
||||
// docs: http://sinonjs.org/releases/v2.3.2/
|
||||
Cu.import("resource://gre/modules/Timer.jsm");
|
||||
const self = {}; // eslint-disable-line no-unused-vars
|
||||
|
||||
/* global sinon */
|
||||
/* exported sinon */
|
||||
const loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
|
||||
loader.loadSubScript("resource://testing-common/sinon-1.16.1.js");
|
||||
const {Loader} = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {});
|
||||
const loader = new Loader.Loader({
|
||||
paths: {
|
||||
"": "resource://testing-common/",
|
||||
},
|
||||
globals: {
|
||||
setTimeout,
|
||||
setInterval,
|
||||
clearTimeout,
|
||||
clearInterval,
|
||||
},
|
||||
});
|
||||
const require = Loader.Require(loader, {id: ""});
|
||||
const sinon = require("sinon-2.3.2");
|
||||
// ================================================
|
||||
|
@ -3,15 +3,10 @@
|
||||
* 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/. */
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Stop updating jumplists after some idle time.
|
||||
const IDLE_TIMEOUT_SECONDS = 5 * 60;
|
||||
@ -52,28 +47,18 @@ XPCOMUtils.defineLazyGetter(this, "_stringBundle", function() {
|
||||
.createBundle("chrome://browser/locale/taskbar.properties");
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "PlacesUtils", function() {
|
||||
Components.utils.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
return PlacesUtils;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
|
||||
Components.utils.import("resource://gre/modules/NetUtil.jsm");
|
||||
return NetUtil;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "_idle",
|
||||
"@mozilla.org/widget/idleservice;1",
|
||||
"nsIIdleService");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "_taskbarService",
|
||||
"@mozilla.org/windows-taskbar;1",
|
||||
"nsIWinTaskbar");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "_winShellService",
|
||||
"@mozilla.org/browser/shell-service;1",
|
||||
"nsIWindowsShellService");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
|
||||
@ -437,7 +422,7 @@ this.WinTaskbarJumpList =
|
||||
}
|
||||
},
|
||||
handleError(aError) {
|
||||
Components.utils.reportError(
|
||||
Cu.reportError(
|
||||
"Async execution error (" + aError.result + "): " + aError.message);
|
||||
},
|
||||
handleCompletion(aReason) {
|
||||
@ -456,12 +441,12 @@ this.WinTaskbarJumpList =
|
||||
if (oldItem) {
|
||||
try { // in case we get a bad uri
|
||||
let uriSpec = oldItem.app.getParameter(0);
|
||||
URIsToRemove.push(NetUtil.newURI(uriSpec));
|
||||
URIsToRemove.push(Services.io.newURI(uriSpec));
|
||||
} catch (err) { }
|
||||
}
|
||||
}
|
||||
if (URIsToRemove.length > 0) {
|
||||
PlacesUtils.bhistory.removePages(URIsToRemove, URIsToRemove.length, true);
|
||||
PlacesUtils.history.remove(URIsToRemove).catch(Cu.reportError);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1055,10 +1055,6 @@ html|span.ac-emphasize-text-url {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/alltabs-inverted.png");
|
||||
}
|
||||
|
||||
#alltabs-button > .toolbarbutton-menu-dropmarker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* All tabs menupopup */
|
||||
.alltabs-item > .menu-iconic-left > .menu-iconic-icon {
|
||||
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
|
||||
|
@ -1786,10 +1786,6 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
|
||||
}
|
||||
}
|
||||
|
||||
#alltabs-button > .toolbarbutton-menu-dropmarker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* All Tabs Menupopup */
|
||||
.alltabs-item > .menu-iconic-left > .menu-iconic-icon {
|
||||
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
|
||||
|
@ -526,6 +526,15 @@
|
||||
width: calc(36px + var(--tab-curve-width));
|
||||
}
|
||||
|
||||
.tabs-newtab-button > .toolbarbutton-menu-dropmarker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tabs-newtab-button > .toolbarbutton-icon {
|
||||
/* override drop marker image padding */
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
|
||||
@media (min-resolution: 1.1dppx) {
|
||||
/* image preloading hack from like lowdpi styles */
|
||||
#tabbrowser-tabs::before {
|
||||
|
@ -61,19 +61,16 @@ toolbar[brighttext] {
|
||||
#nav-bar-overflow-button[disabled=true] > .toolbarbutton-icon,
|
||||
#PanelUI-menu-button[disabled=true] > .toolbarbutton-badge-stack > .toolbarbutton-icon,
|
||||
#main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-icon,
|
||||
#main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-menu-dropmarker,
|
||||
#main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-menubutton-dropmarker,
|
||||
#main-window:not([customizing]) .toolbarbutton-1 > .toolbarbutton-menubutton-button[disabled=true] > .toolbarbutton-icon,
|
||||
#main-window:not([customizing]) .toolbarbutton-1[disabled=true] > :-moz-any(.toolbarbutton-menubutton-button, .toolbarbutton-badge-stack) > .toolbarbutton-icon {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.toolbarbutton-1 > .toolbarbutton-menu-dropmarker,
|
||||
.toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
|
||||
list-style-image: url("chrome://browser/skin/toolbarbutton-dropdown-arrow.png");
|
||||
}
|
||||
|
||||
toolbar[brighttext] .toolbarbutton-1 > .toolbarbutton-menu-dropmarker,
|
||||
toolbar[brighttext] .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
|
||||
list-style-image: url("chrome://browser/skin/toolbarbutton-dropdown-arrow-inverted.png");
|
||||
}
|
||||
@ -209,6 +206,8 @@ toolbarbutton.bookmark-item:not(.subviewbutton),
|
||||
border-inline-end-style: none;
|
||||
}
|
||||
|
||||
.bookmark-item > .toolbarbutton-menu-dropmarker,
|
||||
#TabsToolbar .toolbarbutton-1 > .toolbarbutton-menu-dropmarker,
|
||||
#nav-bar .toolbarbutton-1 > .toolbarbutton-menu-dropmarker {
|
||||
display: none;
|
||||
}
|
||||
@ -464,6 +463,3 @@ toolbarbutton.bookmark-item:not(.subviewbutton) {
|
||||
margin-inline-end: 5px;
|
||||
}
|
||||
|
||||
.bookmark-item > .toolbarbutton-menu-dropmarker {
|
||||
display: none;
|
||||
}
|
||||
|
@ -1505,10 +1505,6 @@ treechildren.searchbar-treebody::-moz-tree-row(selected) {
|
||||
list-style-image: url("chrome://browser/skin/toolbarbutton-dropdown-arrow-inverted.png");
|
||||
}
|
||||
|
||||
#alltabs-button > .toolbarbutton-menu-dropmarker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* All tabs menupopup */
|
||||
.alltabs-item > .menu-iconic-left > .menu-iconic-icon {
|
||||
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
|
||||
|
@ -1,6 +1,6 @@
|
||||
#filter substitution
|
||||
python
|
||||
import sys
|
||||
sys.path.append('@topsrcdir@/python/gdbpp')
|
||||
sys.path.append('@topsrcdir@/third_party/python/gdbpp')
|
||||
import gdbpp
|
||||
end
|
||||
|
@ -43,7 +43,6 @@ MACH_MODULES = [
|
||||
'python/mach_commands.py',
|
||||
'python/mach/mach/commands/commandinfo.py',
|
||||
'python/mach/mach/commands/settings.py',
|
||||
'python/compare-locales/mach_commands.py',
|
||||
'python/mozboot/mozboot/mach_commands.py',
|
||||
'python/mozbuild/mozbuild/mach_commands.py',
|
||||
'python/mozbuild/mozbuild/backend/mach_commands.py',
|
||||
@ -60,6 +59,7 @@ MACH_MODULES = [
|
||||
'testing/talos/mach_commands.py',
|
||||
'testing/web-platform/mach_commands.py',
|
||||
'testing/xpcshell/mach_commands.py',
|
||||
'tools/compare-locales/mach_commands.py',
|
||||
'tools/docs/mach_commands.py',
|
||||
'tools/lint/mach_commands.py',
|
||||
'tools/mach_commands.py',
|
||||
|
@ -193,7 +193,7 @@ def virtualenv_python(env_python, build_env, mozconfig, help):
|
||||
# If we're not in the virtualenv, we need the which module for
|
||||
# find_program.
|
||||
if normsep(sys.executable) != normsep(manager.python_path):
|
||||
sys.path.append(os.path.join(topsrcdir, 'python', 'which'))
|
||||
sys.path.append(os.path.join(topsrcdir, 'third_party', 'python', 'which'))
|
||||
found_python = find_program(python)
|
||||
if not found_python:
|
||||
die('The PYTHON environment variable does not contain '
|
||||
|
@ -9,7 +9,7 @@ import sys
|
||||
import time
|
||||
|
||||
HERE = os.path.abspath(os.path.dirname(__file__))
|
||||
sys.path.append(os.path.join(HERE, '..', 'python', 'requests'))
|
||||
sys.path.append(os.path.join(HERE, '..', 'third_party', 'python', 'requests'))
|
||||
import requests
|
||||
|
||||
|
||||
|
@ -3,25 +3,25 @@ mozilla.pth:python/mozboot
|
||||
mozilla.pth:python/mozbuild
|
||||
mozilla.pth:python/mozlint
|
||||
mozilla.pth:python/mozversioncontrol
|
||||
mozilla.pth:python/blessings
|
||||
mozilla.pth:python/compare-locales
|
||||
mozilla.pth:python/configobj
|
||||
mozilla.pth:python/dlmanager
|
||||
mozilla.pth:python/futures
|
||||
mozilla.pth:python/jsmin
|
||||
optional:setup.py:python/psutil:build_ext:--inplace
|
||||
mozilla.pth:python/psutil
|
||||
mozilla.pth:python/pylru
|
||||
mozilla.pth:python/which
|
||||
mozilla.pth:python/pystache
|
||||
mozilla.pth:python/pyyaml/lib
|
||||
mozilla.pth:python/requests
|
||||
mozilla.pth:python/slugid
|
||||
mozilla.pth:python/py
|
||||
mozilla.pth:python/pytest
|
||||
mozilla.pth:python/pytoml
|
||||
mozilla.pth:python/redo
|
||||
mozilla.pth:python/voluptuous
|
||||
mozilla.pth:third_party/python/blessings
|
||||
mozilla.pth:third_party/python/compare-locales
|
||||
mozilla.pth:third_party/python/configobj
|
||||
mozilla.pth:third_party/python/dlmanager
|
||||
mozilla.pth:third_party/python/futures
|
||||
mozilla.pth:third_party/python/jsmin
|
||||
optional:setup.py:third_party/python/psutil:build_ext:--inplace
|
||||
mozilla.pth:third_party/python/psutil
|
||||
mozilla.pth:third_party/python/pylru
|
||||
mozilla.pth:third_party/python/which
|
||||
mozilla.pth:third_party/python/pystache
|
||||
mozilla.pth:third_party/python/pyyaml/lib
|
||||
mozilla.pth:third_party/python/requests
|
||||
mozilla.pth:third_party/python/slugid
|
||||
mozilla.pth:third_party/python/py
|
||||
mozilla.pth:third_party/python/pytest
|
||||
mozilla.pth:third_party/python/pytoml
|
||||
mozilla.pth:third_party/python/redo
|
||||
mozilla.pth:third_party/python/voluptuous
|
||||
mozilla.pth:build
|
||||
objdir:build
|
||||
mozilla.pth:build/pymake
|
||||
@ -46,11 +46,11 @@ mozilla.pth:testing/web-platform/tests/tools/wptrunner
|
||||
mozilla.pth:testing/web-platform/tests/tools/wptserve
|
||||
mozilla.pth:testing/web-platform/tests/tools/six
|
||||
mozilla.pth:testing/xpcshell
|
||||
mozilla.pth:python/mock-1.0.0
|
||||
mozilla.pth:third_party/python/mock-1.0.0
|
||||
mozilla.pth:xpcom/typelib/xpt/tools
|
||||
mozilla.pth:tools/docs
|
||||
mozilla.pth:media/webrtc/trunk/tools/gyp/pylib
|
||||
mozilla.pth:python/pyasn1
|
||||
mozilla.pth:python/pyasn1-modules
|
||||
mozilla.pth:python/rsa
|
||||
mozilla.pth:python/PyECC
|
||||
mozilla.pth:third_party/python/pyasn1
|
||||
mozilla.pth:third_party/python/pyasn1-modules
|
||||
mozilla.pth:third_party/python/rsa
|
||||
mozilla.pth:third_party/python/PyECC
|
||||
|
11
config/external/icu/defs.mozbuild
vendored
11
config/external/icu/defs.mozbuild
vendored
@ -47,3 +47,14 @@ if CONFIG['CLANG_CL']:
|
||||
'-Wno-macro-redefined',
|
||||
'-Wno-microsoft-include',
|
||||
]
|
||||
|
||||
if CONFIG['_MSC_VER'] and not CONFIG['CLANG_CL']:
|
||||
CFLAGS += [
|
||||
'-wd4005', # 'WIN32_LEAN_AND_MEAN' : macro redefinition
|
||||
'-wd4996', # The compiler encountered a deprecated declaration.
|
||||
]
|
||||
CXXFLAGS += [
|
||||
'-wd4005', # 'WIN32_LEAN_AND_MEAN' : macro redefinition
|
||||
'-wd4333', # '>>' : right shift by too large amount, data loss
|
||||
'-wd4996', # The compiler encountered a deprecated declaration.
|
||||
]
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
ifndef NO_DIST_INSTALL
|
||||
|
||||
ifneq (,$(strip $(PROGRAM)$(SIMPLE_PROGRAMS)))
|
||||
ifneq (,$(strip $(PROGRAM)$(SIMPLE_PROGRAMS)$(RUST_PROGRAMS)))
|
||||
PROGRAMS_EXECUTABLES = $(SIMPLE_PROGRAMS) $(PROGRAM) $(RUST_PROGRAMS)
|
||||
PROGRAMS_DEST ?= $(FINAL_TARGET)
|
||||
PROGRAMS_TARGET := target
|
||||
|
47
devtools/bootstrap.js
vendored
47
devtools/bootstrap.js
vendored
@ -11,6 +11,7 @@ const Cu = Components.utils;
|
||||
const Ci = Components.interfaces;
|
||||
const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||
const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
|
||||
const {AppConstants} = Cu.import("resource://gre/modules/AppConstants.jsm", {});
|
||||
|
||||
// MultiWindowKeyListener instance for Ctrl+Alt+R key
|
||||
let listener;
|
||||
@ -40,10 +41,56 @@ function readURI(uri) {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpret the processing instructions contained in a preferences file, based on a
|
||||
* limited set of supported #if statements. After we ship as an addon, we don't want to
|
||||
* introduce anymore processing instructions, so all unrecognized preprocessing
|
||||
* instructions will be treated as an error.
|
||||
*
|
||||
* This function is mostly copied from devtools/client/inspector/webpack/prefs-loader.js
|
||||
*
|
||||
* @param {String} content
|
||||
* The string content of a preferences file.
|
||||
* @return {String} the content stripped of preprocessing instructions.
|
||||
*/
|
||||
function interpretPreprocessingInstructions(content) {
|
||||
const ifMap = {
|
||||
"#if MOZ_UPDATE_CHANNEL == beta": AppConstants.MOZ_UPDATE_CHANNEL === "beta",
|
||||
"#if defined(NIGHTLY_BUILD)": AppConstants.NIGHTLY_BUILD,
|
||||
"#ifdef MOZ_DEV_EDITION": AppConstants.MOZ_DEV_EDITION,
|
||||
"#ifdef RELEASE_OR_BETA": AppConstants.RELEASE_OR_BETA,
|
||||
};
|
||||
|
||||
let lines = content.split("\n");
|
||||
let ignoring = false;
|
||||
let newLines = [];
|
||||
let continuation = false;
|
||||
for (let line of lines) {
|
||||
if (line.startsWith("#if")) {
|
||||
if (!(line in ifMap)) {
|
||||
throw new Error("missing line in ifMap: " + line);
|
||||
}
|
||||
ignoring = !ifMap[line];
|
||||
} else if (line.startsWith("#else")) {
|
||||
ignoring = !ignoring;
|
||||
}
|
||||
|
||||
let isPrefLine = /^ *pref\("([^"]+)"/.test(line);
|
||||
if (continuation || (!ignoring && isPrefLine)) {
|
||||
newLines.push(line);
|
||||
|
||||
// The call to pref(...); might span more than one line.
|
||||
continuation = !/\);/.test(line);
|
||||
}
|
||||
}
|
||||
return newLines.join("\n");
|
||||
}
|
||||
|
||||
// Read a preference file and set all of its defined pref as default values
|
||||
// (This replicates the behavior of preferences files from mozilla-central)
|
||||
function processPrefFile(url) {
|
||||
let content = readURI(url);
|
||||
content = interpretPreprocessingInstructions(content);
|
||||
content.match(/pref\("[^"]+",\s*.+\s*\)/g).forEach(item => {
|
||||
let m = item.match(/pref\("([^"]+)",\s*(.+)\s*\)/);
|
||||
let name = m[1];
|
||||
|
@ -516,9 +516,15 @@ function appendPathElement(parentEl, pathSegments, cls, isClosePathNeeded = true
|
||||
}
|
||||
|
||||
const nextPathSegment = pathSegments[i + 1];
|
||||
path += pathSegment.easing.startsWith("steps")
|
||||
? createStepsPathString(pathSegment, nextPathSegment)
|
||||
: createCubicBezierPathString(pathSegment, nextPathSegment);
|
||||
let createPathFunction;
|
||||
if (pathSegment.easing.startsWith("steps")) {
|
||||
createPathFunction = createStepsPathString;
|
||||
} else if (pathSegment.easing.startsWith("frames")) {
|
||||
createPathFunction = createFramesPathString;
|
||||
} else {
|
||||
createPathFunction = createCubicBezierPathString;
|
||||
}
|
||||
path += createPathFunction(pathSegment, nextPathSegment);
|
||||
}
|
||||
path += ` L${ pathSegments[pathSegments.length - 1].x },0`;
|
||||
if (isClosePathNeeded) {
|
||||
@ -593,6 +599,28 @@ function createStepsPathString(currentSegment, nextSegment) {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a path string to represents a frames function.
|
||||
* @param {Object} currentSegment - e.g. { x: 0, y: 0, easing: "frames(2)" }
|
||||
* @param {Object} nextSegment - e.g. { x: 1, y: 1 }
|
||||
* @return {String} path string - e.g. "C 0.25 0.1, 0.25 1, 1 1"
|
||||
*/
|
||||
function createFramesPathString(currentSegment, nextSegment) {
|
||||
const matches =
|
||||
currentSegment.easing.match(/^frames\((\d+)\)/);
|
||||
const framesNumber = parseInt(matches[1], 10);
|
||||
const oneFrameX = (nextSegment.x - currentSegment.x) / framesNumber;
|
||||
const oneFrameY = (nextSegment.y - currentSegment.y) / (framesNumber - 1);
|
||||
let path = "";
|
||||
for (let frame = 0; frame < framesNumber; frame++) {
|
||||
const sx = currentSegment.x + frame * oneFrameX;
|
||||
const ex = sx + oneFrameX;
|
||||
const y = currentSegment.y + frame * oneFrameY;
|
||||
path += ` L${ sx },${ y } L${ ex },${ y }`;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a path string to represents a bezier curve.
|
||||
* @param {Object} currentSegment - e.g. { x: 0, y: 0, easing: "ease" }
|
||||
@ -684,9 +712,9 @@ exports.getPreferredKeyframesProgressThreshold = getPreferredKeyframesProgressTh
|
||||
* @return {float} - preferred threshold.
|
||||
*/
|
||||
function getPreferredProgressThreshold(easing) {
|
||||
const stepFunction = easing.match(/steps\((\d+)/);
|
||||
return stepFunction
|
||||
? 1 / (parseInt(stepFunction[1], 10) + 1)
|
||||
const stepOrFramesFunction = easing.match(/(steps|frames)\((\d+)/);
|
||||
return stepOrFramesFunction
|
||||
? 1 / (parseInt(stepOrFramesFunction[2], 10) + 1)
|
||||
: DEFAULT_MIN_PROGRESS_THRESHOLD;
|
||||
}
|
||||
exports.getPreferredProgressThreshold = getPreferredProgressThreshold;
|
||||
|
@ -239,7 +239,26 @@ const TEST_CASES = [
|
||||
expectedClass: "opacity",
|
||||
expectedValues: [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: 250, y: 0.25 },
|
||||
{ x: 500, y: 0.5 },
|
||||
{ x: 750, y: 0.75 },
|
||||
{ x: 1000, y: 1 },
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"opacity": {
|
||||
expectedClass: "opacity",
|
||||
expectedValues: [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: 199, y: 0 },
|
||||
{ x: 200, y: 0.25 },
|
||||
{ x: 399, y: 0.25 },
|
||||
{ x: 400, y: 0.5 },
|
||||
{ x: 599, y: 0.5 },
|
||||
{ x: 600, y: 0.75 },
|
||||
{ x: 799, y: 0.75 },
|
||||
{ x: 800, y: 1 },
|
||||
{ x: 1000, y: 1 },
|
||||
]
|
||||
}
|
||||
|
@ -30,18 +30,29 @@ const TEST_CASES = {
|
||||
"effect-easing": {
|
||||
expectedEffectEasingGraph: [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: 49999, y: 0.0 },
|
||||
{ x: 50000, y: 0.5 },
|
||||
{ x: 99999, y: 0.5 },
|
||||
{ x: 19999, y: 0.0 },
|
||||
{ x: 20000, y: 0.25 },
|
||||
{ x: 39999, y: 0.25 },
|
||||
{ x: 40000, y: 0.5 },
|
||||
{ x: 59999, y: 0.5 },
|
||||
{ x: 60000, y: 0.75 },
|
||||
{ x: 79999, y: 0.75 },
|
||||
{ x: 80000, y: 1 },
|
||||
{ x: 99999, y: 1 },
|
||||
{ x: 100000, y: 0 },
|
||||
],
|
||||
expectedKeyframeEasingGraphs: [
|
||||
[
|
||||
{ x: 0, y: 0 },
|
||||
{ x: 49999, y: 0.0 },
|
||||
{ x: 50000, y: 0.5 },
|
||||
{ x: 99999, y: 0.5 },
|
||||
{ x: 100000, y: 1 },
|
||||
{ x: 19999, y: 0.0 },
|
||||
{ x: 20000, y: 0.25 },
|
||||
{ x: 39999, y: 0.25 },
|
||||
{ x: 40000, y: 0.5 },
|
||||
{ x: 59999, y: 0.5 },
|
||||
{ x: 60000, y: 0.75 },
|
||||
{ x: 79999, y: 0.75 },
|
||||
{ x: 80000, y: 1 },
|
||||
{ x: 99999, y: 1 },
|
||||
{ x: 100000, y: 0 },
|
||||
]
|
||||
]
|
||||
|
@ -25,7 +25,7 @@
|
||||
id: "effect-easing",
|
||||
frames: { opacity: [1, 0] },
|
||||
timing: {
|
||||
easing: "steps(2)",
|
||||
easing: "frames(5)",
|
||||
duration: DURATION
|
||||
}
|
||||
},
|
||||
|
@ -15,6 +15,7 @@
|
||||
<div id=target3>3</div>
|
||||
<div id=target4>4</div>
|
||||
<div id=target5>5</div>
|
||||
<div id=target6>6</div>
|
||||
|
||||
<script>
|
||||
"use strict";
|
||||
@ -90,6 +91,10 @@
|
||||
timing.easing = "steps(2)";
|
||||
document.querySelector("#target5").animate(
|
||||
[{ opacity: 0 }, { opacity: 1 }], timing).pause();
|
||||
|
||||
timing.easing = "linear";
|
||||
document.querySelector("#target6").animate(
|
||||
[{ opacity: 0, easing: "frames(5)" }, { opacity: 1 }], timing).pause();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -8,6 +8,14 @@
|
||||
const TEST_URL = "data:text/html,<html><head><title>Test for the " +
|
||||
"highlighter keybindings</title></head><body>" +
|
||||
"<h1>Keybindings!</h1></body></html>"
|
||||
|
||||
// Use the new debugger frontend because the old one swallows the netmonitor shortcut:
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1370442#c7
|
||||
Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", true);
|
||||
registerCleanupFunction(function* () {
|
||||
Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
|
||||
});
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
@ -6,7 +6,7 @@
|
||||
const global = require("devtools/client/performance/modules/global");
|
||||
const demangle = require("devtools/client/shared/demangle");
|
||||
const { assert } = require("devtools/shared/DevToolsUtils");
|
||||
const { isChromeScheme, isContentScheme, parseURL } =
|
||||
const { isChromeScheme, isContentScheme, isWASM, parseURL } =
|
||||
require("devtools/client/shared/source-utils");
|
||||
|
||||
const { CATEGORY_MASK, CATEGORY_MAPPINGS } = require("devtools/client/performance/modules/categories");
|
||||
@ -221,7 +221,9 @@ function computeIsContentAndCategory(frame) {
|
||||
schemeStartIndex = 0;
|
||||
}
|
||||
|
||||
if (isContentScheme(location, schemeStartIndex)) {
|
||||
// We can't know if WASM frames are content or not at the time of this writing, so label
|
||||
// them all as content.
|
||||
if (isContentScheme(location, schemeStartIndex) || isWASM(location)) {
|
||||
frame.isContent = true;
|
||||
return;
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ const UNKNOWN_SOURCE_STRING = l10n.getStr("frame.unknownSource");
|
||||
|
||||
// Character codes used in various parsing helper functions.
|
||||
const CHAR_CODE_A = "a".charCodeAt(0);
|
||||
const CHAR_CODE_B = "b".charCodeAt(0);
|
||||
const CHAR_CODE_C = "c".charCodeAt(0);
|
||||
const CHAR_CODE_D = "d".charCodeAt(0);
|
||||
const CHAR_CODE_E = "e".charCodeAt(0);
|
||||
@ -19,13 +20,17 @@ const CHAR_CODE_I = "i".charCodeAt(0);
|
||||
const CHAR_CODE_J = "j".charCodeAt(0);
|
||||
const CHAR_CODE_L = "l".charCodeAt(0);
|
||||
const CHAR_CODE_M = "m".charCodeAt(0);
|
||||
const CHAR_CODE_N = "n".charCodeAt(0);
|
||||
const CHAR_CODE_O = "o".charCodeAt(0);
|
||||
const CHAR_CODE_P = "p".charCodeAt(0);
|
||||
const CHAR_CODE_R = "r".charCodeAt(0);
|
||||
const CHAR_CODE_S = "s".charCodeAt(0);
|
||||
const CHAR_CODE_T = "t".charCodeAt(0);
|
||||
const CHAR_CODE_U = "u".charCodeAt(0);
|
||||
const CHAR_CODE_W = "w".charCodeAt(0);
|
||||
const CHAR_CODE_COLON = ":".charCodeAt(0);
|
||||
const CHAR_CODE_DASH = "-".charCodeAt(0);
|
||||
const CHAR_CODE_L_SQUARE_BRACKET = "[".charCodeAt(0);
|
||||
const CHAR_CODE_SLASH = "/".charCodeAt(0);
|
||||
const CHAR_CODE_CAP_S = "S".charCodeAt(0);
|
||||
|
||||
@ -248,6 +253,18 @@ function isContentScheme(location, i = 0) {
|
||||
}
|
||||
return false;
|
||||
|
||||
// "blob:"
|
||||
case CHAR_CODE_B:
|
||||
if (
|
||||
location.charCodeAt(++i) == CHAR_CODE_L &&
|
||||
location.charCodeAt(++i) == CHAR_CODE_O &&
|
||||
location.charCodeAt(++i) == CHAR_CODE_B &&
|
||||
location.charCodeAt(++i) == CHAR_CODE_COLON
|
||||
) {
|
||||
return isContentScheme(location, i + 1);
|
||||
}
|
||||
return false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -299,6 +316,26 @@ function isChromeScheme(location, i = 0) {
|
||||
}
|
||||
}
|
||||
|
||||
function isWASM(location, i = 0) {
|
||||
return (
|
||||
// "wasm-function["
|
||||
location.charCodeAt(i) === CHAR_CODE_W &&
|
||||
location.charCodeAt(++i) === CHAR_CODE_A &&
|
||||
location.charCodeAt(++i) === CHAR_CODE_S &&
|
||||
location.charCodeAt(++i) === CHAR_CODE_M &&
|
||||
location.charCodeAt(++i) === CHAR_CODE_DASH &&
|
||||
location.charCodeAt(++i) === CHAR_CODE_F &&
|
||||
location.charCodeAt(++i) === CHAR_CODE_U &&
|
||||
location.charCodeAt(++i) === CHAR_CODE_N &&
|
||||
location.charCodeAt(++i) === CHAR_CODE_C &&
|
||||
location.charCodeAt(++i) === CHAR_CODE_T &&
|
||||
location.charCodeAt(++i) === CHAR_CODE_I &&
|
||||
location.charCodeAt(++i) === CHAR_CODE_O &&
|
||||
location.charCodeAt(++i) === CHAR_CODE_N &&
|
||||
location.charCodeAt(++i) === CHAR_CODE_L_SQUARE_BRACKET
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility method to get the file name from a sourcemapped location
|
||||
* The sourcemap location can be in any form. This method returns a
|
||||
@ -324,5 +361,6 @@ exports.getSourceNames = getSourceNames;
|
||||
exports.isScratchpadScheme = isScratchpadScheme;
|
||||
exports.isChromeScheme = isChromeScheme;
|
||||
exports.isContentScheme = isContentScheme;
|
||||
exports.isWASM = isWASM;
|
||||
exports.isDataScheme = isDataScheme;
|
||||
exports.getSourceMappedFile = getSourceMappedFile;
|
||||
|
@ -19,7 +19,8 @@ const CHROME_URLS = [
|
||||
];
|
||||
|
||||
const CONTENT_URLS = [
|
||||
"http://mozilla.org", "https://mozilla.org", "file:///Users/root", "app://fxosapp"
|
||||
"http://mozilla.org", "https://mozilla.org", "file:///Users/root", "app://fxosapp",
|
||||
"blob:http://mozilla.org", "blob:https://mozilla.org"
|
||||
];
|
||||
|
||||
// Test `sourceUtils.parseURL`
|
||||
@ -61,6 +62,13 @@ add_task(function* () {
|
||||
}
|
||||
});
|
||||
|
||||
// Test `sourceUtils.isWASM`.
|
||||
add_task(function* () {
|
||||
ok(sourceUtils.isWASM("wasm-function[66240] (?:13870536)"),
|
||||
"wasm function correctly identified");
|
||||
ok(!sourceUtils.isWASM(CHROME_URLS[0]), `A chrome url does not identify as wasm.`);
|
||||
});
|
||||
|
||||
// Test `sourceUtils.isDataScheme`.
|
||||
add_task(function* () {
|
||||
let dataURI = "data:text/html;charset=utf-8,<!DOCTYPE html></html>";
|
||||
|
@ -14,6 +14,7 @@ const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||
const {
|
||||
getAllMessagesUiById,
|
||||
getAllMessagesTableDataById,
|
||||
getAllNetworkMessagesUpdateById,
|
||||
getVisibleMessages,
|
||||
getAllRepeatById,
|
||||
} = require("devtools/client/webconsole/new-console-output/selectors/messages");
|
||||
@ -34,6 +35,7 @@ const ConsoleOutput = createClass({
|
||||
timestampsVisible: PropTypes.bool,
|
||||
messagesTableData: PropTypes.object.isRequired,
|
||||
messagesRepeat: PropTypes.object.isRequired,
|
||||
networkMessagesUpdate: PropTypes.object.isRequired,
|
||||
visibleMessages: PropTypes.array.isRequired,
|
||||
},
|
||||
|
||||
@ -78,6 +80,7 @@ const ConsoleOutput = createClass({
|
||||
messagesUi,
|
||||
messagesTableData,
|
||||
messagesRepeat,
|
||||
networkMessagesUpdate,
|
||||
serviceContainer,
|
||||
timestampsVisible,
|
||||
} = this.props;
|
||||
@ -93,7 +96,8 @@ const ConsoleOutput = createClass({
|
||||
tableData: messagesTableData.get(message.id),
|
||||
indent: message.indent,
|
||||
timestampsVisible,
|
||||
repeat: messagesRepeat[message.id]
|
||||
repeat: messagesRepeat[message.id],
|
||||
networkMessageUpdate: networkMessagesUpdate[message.id],
|
||||
})
|
||||
);
|
||||
});
|
||||
@ -128,6 +132,7 @@ function mapStateToProps(state, props) {
|
||||
messagesUi: getAllMessagesUiById(state),
|
||||
messagesTableData: getAllMessagesTableDataById(state),
|
||||
messagesRepeat: getAllRepeatById(state),
|
||||
networkMessagesUpdate: getAllNetworkMessagesUpdateById(state),
|
||||
timestampsVisible: state.ui.timestampsVisible,
|
||||
};
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ const MessageContainer = createClass({
|
||||
tableData: PropTypes.object,
|
||||
timestampsVisible: PropTypes.bool.isRequired,
|
||||
repeat: PropTypes.object,
|
||||
networkMessageUpdate: PropTypes.object.isRequired,
|
||||
},
|
||||
|
||||
getDefaultProps: function () {
|
||||
@ -55,13 +56,16 @@ const MessageContainer = createClass({
|
||||
const totalTimeChanged = this.props.message.totalTime !== nextProps.message.totalTime;
|
||||
const timestampVisibleChanged =
|
||||
this.props.timestampsVisible !== nextProps.timestampsVisible;
|
||||
const networkMessageUpdateChanged =
|
||||
this.props.networkMessageUpdate !== nextProps.networkMessageUpdate;
|
||||
|
||||
return repeatChanged
|
||||
|| openChanged
|
||||
|| tableDataChanged
|
||||
|| responseChanged
|
||||
|| totalTimeChanged
|
||||
|| timestampVisibleChanged;
|
||||
|| timestampVisibleChanged
|
||||
|| networkMessageUpdateChanged;
|
||||
},
|
||||
|
||||
render() {
|
||||
|
@ -24,6 +24,7 @@ NetworkEventMessage.propTypes = {
|
||||
}),
|
||||
indent: PropTypes.number.isRequired,
|
||||
timestampsVisible: PropTypes.bool.isRequired,
|
||||
networkMessageUpdate: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
NetworkEventMessage.defaultProps = {
|
||||
@ -35,6 +36,7 @@ function NetworkEventMessage({
|
||||
message = {},
|
||||
serviceContainer,
|
||||
timestampsVisible,
|
||||
networkMessageUpdate = {},
|
||||
}) {
|
||||
const {
|
||||
actor,
|
||||
@ -42,20 +44,25 @@ function NetworkEventMessage({
|
||||
type,
|
||||
level,
|
||||
request,
|
||||
response: {
|
||||
httpVersion,
|
||||
status,
|
||||
statusText,
|
||||
},
|
||||
isXHR,
|
||||
timeStamp,
|
||||
totalTime,
|
||||
} = message;
|
||||
|
||||
const {
|
||||
response = {},
|
||||
totalTime,
|
||||
} = networkMessageUpdate;
|
||||
|
||||
const {
|
||||
httpVersion,
|
||||
status,
|
||||
statusText,
|
||||
} = response;
|
||||
|
||||
const topLevelClasses = [ "cm-s-mozilla" ];
|
||||
let statusInfo;
|
||||
|
||||
if (httpVersion && status && statusText && totalTime !== undefined) {
|
||||
if (httpVersion && status && statusText !== undefined && totalTime !== undefined) {
|
||||
statusInfo = `[${httpVersion} ${status} ${statusText} ${totalTime}ms]`;
|
||||
}
|
||||
|
||||
|
@ -184,11 +184,10 @@ NewConsoleOutputWrapper.prototype = {
|
||||
},
|
||||
|
||||
dispatchMessageUpdate: function (message, res) {
|
||||
batchedMessageAdd(actions.networkMessageUpdate(message));
|
||||
|
||||
// network-message-updated will emit when eventTimings message arrives
|
||||
// which is the last one of 8 updates happening on network message update.
|
||||
if (res.packet.updateType === "eventTimings") {
|
||||
batchedMessageAdd(actions.networkMessageUpdate(message));
|
||||
this.jsterm.hud.emit("network-message-updated", res);
|
||||
}
|
||||
},
|
||||
|
@ -36,7 +36,10 @@ const MessageState = Immutable.Record({
|
||||
// This array is not supposed to be consumed by any UI component.
|
||||
removedMessages: [],
|
||||
// Map of the form {messageId : numberOfRepeat}
|
||||
repeatById: {}
|
||||
repeatById: {},
|
||||
// Map of the form {messageId : networkInformation}
|
||||
// `networkInformation` holds request, response, totalTime, ...
|
||||
networkMessagesUpdateById: {},
|
||||
});
|
||||
|
||||
function messages(state = new MessageState(), action, filtersState, prefsState) {
|
||||
@ -44,6 +47,7 @@ function messages(state = new MessageState(), action, filtersState, prefsState)
|
||||
messagesById,
|
||||
messagesUiById,
|
||||
messagesTableDataById,
|
||||
networkMessagesUpdateById,
|
||||
groupsById,
|
||||
currentGroup,
|
||||
repeatById,
|
||||
@ -189,11 +193,12 @@ function messages(state = new MessageState(), action, filtersState, prefsState)
|
||||
const {id, data} = action;
|
||||
return state.set("messagesTableDataById", messagesTableDataById.set(id, data));
|
||||
case constants.NETWORK_MESSAGE_UPDATE:
|
||||
let updateMessage = action.message;
|
||||
return state.set("messagesById", messagesById.set(
|
||||
updateMessage.id,
|
||||
updateMessage
|
||||
));
|
||||
return state.set(
|
||||
"networkMessagesUpdateById",
|
||||
Object.assign({}, networkMessagesUpdateById, {
|
||||
[action.message.id]: action.message
|
||||
})
|
||||
);
|
||||
|
||||
case constants.REMOVED_MESSAGES_CLEAR:
|
||||
return state.set("removedMessages", []);
|
||||
@ -312,6 +317,13 @@ function limitTopLevelMessageCount(state, record, logLimit) {
|
||||
const isInRemovedId = id => removedMessagesId.includes(id);
|
||||
const mapHasRemovedIdKey = map => map.findKey((value, id) => isInRemovedId(id));
|
||||
const cleanUpCollection = map => removedMessagesId.forEach(id => map.remove(id));
|
||||
const cleanUpObject = object => [...Object.entries(object)]
|
||||
.reduce((res, [id, value]) => {
|
||||
if (!isInRemovedId(id)) {
|
||||
res[id] = value;
|
||||
}
|
||||
return res;
|
||||
}, {});
|
||||
|
||||
record.set("messagesById", record.messagesById.withMutations(cleanUpCollection));
|
||||
|
||||
@ -327,14 +339,12 @@ function limitTopLevelMessageCount(state, record, logLimit) {
|
||||
}
|
||||
|
||||
if (Object.keys(record.repeatById).includes(removedMessagesId)) {
|
||||
record.set("repeatById",
|
||||
[...Object.entries(record.repeatById)].reduce((res, [id, repeat]) => {
|
||||
if (!isInRemovedId(id)) {
|
||||
res[id] = repeat;
|
||||
}
|
||||
return res;
|
||||
}, {})
|
||||
);
|
||||
record.set("repeatById", cleanUpObject(record.repeatById));
|
||||
}
|
||||
|
||||
if (Object.keys(record.networkMessagesUpdateById).includes(removedMessagesId)) {
|
||||
record.set("networkMessagesUpdateById",
|
||||
cleanUpObject(record.networkMessagesUpdateById));
|
||||
}
|
||||
|
||||
return record;
|
||||
|
@ -37,6 +37,10 @@ function getAllRepeatById(state) {
|
||||
return state.messages.repeatById;
|
||||
}
|
||||
|
||||
function getAllNetworkMessagesUpdateById(state) {
|
||||
return state.messages.networkMessagesUpdateById;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getMessage,
|
||||
getAllMessagesById,
|
||||
@ -46,4 +50,5 @@ module.exports = {
|
||||
getCurrentGroup,
|
||||
getVisibleMessages,
|
||||
getAllRepeatById,
|
||||
getAllNetworkMessagesUpdateById,
|
||||
};
|
||||
|
@ -14,8 +14,22 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
"use strict";
|
||||
const numMessages = 4000;
|
||||
const testPackets = Array.from({length: numMessages}).map((el, id) => ({
|
||||
|
||||
// To analyze the profile results:
|
||||
// > ./mach mochitest test_render_perf.html
|
||||
// Then open https://perf-html.io and drag the json file printed at the end of this test
|
||||
|
||||
const NUM_MESSAGES = 4000;
|
||||
const NUM_STREAMING = 100;
|
||||
Components.utils.import("resource://gre/modules/FileUtils.jsm");
|
||||
const Services = browserRequire("Services");
|
||||
Services.prefs.setIntPref("devtools.hud.loglimit", NUM_MESSAGES);
|
||||
const NewConsoleOutputWrapper = browserRequire(
|
||||
"devtools/client/webconsole/new-console-output/new-console-output-wrapper");
|
||||
const actions =
|
||||
browserRequire("devtools/client/webconsole/new-console-output/actions/index");
|
||||
const EventEmitter = browserRequire("devtools/shared/event-emitter");
|
||||
const testPackets = Array.from({length: NUM_MESSAGES}).map((el, id) => ({
|
||||
"from": "server1.conn4.child1/consoleActor2",
|
||||
"type": "consoleAPICall",
|
||||
"message": {
|
||||
@ -39,6 +53,7 @@ const testPackets = Array.from({length: numMessages}).map((el, id) => ({
|
||||
"category": "webdev"
|
||||
}
|
||||
}));
|
||||
const lastPacket = testPackets.pop();
|
||||
|
||||
async function timeit(cb) {
|
||||
// Return a Promise that resolves the number of seconds cb takes.
|
||||
@ -48,59 +63,126 @@ async function timeit(cb) {
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
async function addAllMessages(wrapper) {
|
||||
let time = await timeit(async () => {
|
||||
testPackets.forEach((packet) => wrapper.dispatchMessageAdd(packet));
|
||||
// Only wait for the last packet to minimize work.
|
||||
await wrapper.dispatchMessageAdd(lastPacket, true);
|
||||
await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
});
|
||||
return time;
|
||||
}
|
||||
|
||||
async function addMessage(wrapper, message) {
|
||||
return timeit(async () => {
|
||||
await wrapper.dispatchMessageAdd(message, true);
|
||||
});
|
||||
}
|
||||
|
||||
function getTimes(times) {
|
||||
times = times.sort();
|
||||
let totalTime = times.reduce((sum, t) => sum + t);
|
||||
let avg = totalTime / times.length;
|
||||
let median = times.length % 2 !== 0
|
||||
? times[Math.floor(times.length / 2)]
|
||||
: (times[(times.length / 2) - 1] + times[times.length / 2]) / 2;
|
||||
return {avg, median};
|
||||
}
|
||||
|
||||
async function clearMessages(wrapper) {
|
||||
wrapper.dispatchMessagesClear();
|
||||
await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
}
|
||||
|
||||
async function testStreamLogging(wrapper) {
|
||||
await clearMessages(wrapper);
|
||||
let streamTimes = [];
|
||||
for (let i = 0; i < NUM_STREAMING; i++) {
|
||||
streamTimes.push(addMessage(wrapper, testPackets[i]));
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
}
|
||||
let {avg, median} = getTimes(await Promise.all(streamTimes));
|
||||
info(`STREAMING: On average, it took ${avg} ms (median ${median} ms) ` +
|
||||
`for each message`);
|
||||
}
|
||||
|
||||
async function testBulkLogging(wrapper) {
|
||||
await clearMessages(wrapper);
|
||||
let bulkTimes = [];
|
||||
const iterations = 5;
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
let time = await addAllMessages(wrapper);
|
||||
info(`took ${time} ms to render bulk messages (iteration ${i})`);
|
||||
bulkTimes.push(time);
|
||||
|
||||
await clearMessages(wrapper);
|
||||
}
|
||||
let {avg, median} = getTimes(bulkTimes);
|
||||
|
||||
info(`BULK: On average, it took ${avg} ms (median ${median} ms) ` +
|
||||
`to render ${NUM_MESSAGES} messages`);
|
||||
}
|
||||
|
||||
async function testFiltering(wrapper) {
|
||||
await clearMessages(wrapper);
|
||||
await addAllMessages(wrapper);
|
||||
let filterToggleTimeOff = await timeit(() => {
|
||||
wrapper.getStore().dispatch(actions.filterToggle("log"));
|
||||
});
|
||||
info(`Filter toggle time (off): ${filterToggleTimeOff}`);
|
||||
|
||||
let filterToggleTimeOn = await timeit(() => {
|
||||
wrapper.getStore().dispatch(actions.filterToggle("log"));
|
||||
});
|
||||
info(`Filter toggle time (on): ${filterToggleTimeOn}`);
|
||||
}
|
||||
|
||||
window.onload = async function () {
|
||||
// This test does costly work multiple times to have better performance data.
|
||||
// It doesn't run in automation
|
||||
SimpleTest.requestLongerTimeout(3);
|
||||
|
||||
try {
|
||||
const Services = browserRequire("Services");
|
||||
Services.prefs.setIntPref("devtools.hud.loglimit", numMessages);
|
||||
const NewConsoleOutputWrapper = browserRequire(
|
||||
"devtools/client/webconsole/new-console-output/new-console-output-wrapper");
|
||||
const EventEmitter = browserRequire("devtools/shared/event-emitter");
|
||||
const wrapper = new NewConsoleOutputWrapper(
|
||||
document.getElementById("output"),
|
||||
{hud: EventEmitter.decorate({proxy: {}})},
|
||||
{},
|
||||
null,
|
||||
document,
|
||||
);
|
||||
wrapper.init();
|
||||
|
||||
const wrapper = new NewConsoleOutputWrapper(
|
||||
document.getElementById("output"),
|
||||
{hud: EventEmitter.decorate({proxy: {}})},
|
||||
{},
|
||||
null,
|
||||
document,
|
||||
);
|
||||
wrapper.init();
|
||||
// From https://github.com/devtools-html/perf.html/blob/b73eb73df04c7df51464fa50eeadef3dc7f5d4e2/docs/gecko-profile-format.md#L21
|
||||
const settings = {
|
||||
entries: 100000000,
|
||||
interval: 1,
|
||||
features: ["js"],
|
||||
threads: ["GeckoMain"]
|
||||
};
|
||||
Services.profiler.StartProfiler(
|
||||
settings.entries,
|
||||
settings.interval,
|
||||
settings.features,
|
||||
settings.features.length,
|
||||
settings.threads,
|
||||
settings.threads.length
|
||||
);
|
||||
info("Profiler has started");
|
||||
|
||||
let times = [];
|
||||
const iterations = 25;
|
||||
const lastPacket = testPackets.pop();
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
let time = await timeit(async () => {
|
||||
testPackets.forEach((packet) => wrapper.dispatchMessageAdd(packet));
|
||||
// Only wait for the last packet to minimize work.
|
||||
await wrapper.dispatchMessageAdd(lastPacket, true);
|
||||
await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
});
|
||||
info(`took ${time} ms to render messages`);
|
||||
times.push(time);
|
||||
Services.profiler.AddMarker("Stream Logging");
|
||||
await testStreamLogging(wrapper);
|
||||
|
||||
// Clear the console
|
||||
wrapper.dispatchMessagesClear();
|
||||
await new Promise(resolve => requestAnimationFrame(resolve));
|
||||
}
|
||||
Services.profiler.AddMarker("Bulk Logging");
|
||||
await testBulkLogging(wrapper);
|
||||
|
||||
times.sort();
|
||||
let totalTime = times.reduce((sum, t) => sum + t);
|
||||
let avg = totalTime / times.length;
|
||||
let median = times.length % 2 !== 0
|
||||
? times[Math.floor(times.length / 2)]
|
||||
: (times[(times.length / 2) - 1] + times[times.length / 2]) / 2;
|
||||
info(`On average, it took ${avg} ms (median ${median} ms) ` +
|
||||
`to render ${numMessages} messages`);
|
||||
Services.profiler.AddMarker("Filtering Logging");
|
||||
await testFiltering(wrapper);
|
||||
|
||||
ok(true, "Yay, it didn't time out!");
|
||||
} catch (e) {
|
||||
ok(false, `Error : ${e.message}
|
||||
${e.stack}
|
||||
`);
|
||||
}
|
||||
ok(true, "Tests finished");
|
||||
|
||||
let file = FileUtils.getFile("TmpD", [`test_render_perf_${Date.now()}.json`]);
|
||||
info("PROFILE:\n\n\n\n\nSaving profile " + file.path + "\n\n\n\n\n");
|
||||
Services.profiler.dumpProfileToFile(file.path);
|
||||
Services.profiler.StopProfiler();
|
||||
|
||||
SimpleTest.finish();
|
||||
};
|
||||
|
@ -23,11 +23,13 @@ const EXPECTED_STATUS = /\[HTTP\/\d\.\d \d+ [A-Za-z ]+ \d+ms\]/;
|
||||
describe("NetworkEventMessage component:", () => {
|
||||
describe("GET request", () => {
|
||||
it("renders as expected", () => {
|
||||
const message = stubPreparedMessages.get("GET request eventTimings");
|
||||
const message = stubPreparedMessages.get("GET request");
|
||||
const update = stubPreparedMessages.get("GET request eventTimings");
|
||||
const wrapper = render(NetworkEventMessage({
|
||||
message,
|
||||
serviceContainer,
|
||||
timestampsVisible: true,
|
||||
networkMessageUpdate: update,
|
||||
}));
|
||||
const { timestampString } = require("devtools/client/webconsole/webconsole-l10n");
|
||||
|
||||
@ -66,8 +68,13 @@ describe("NetworkEventMessage component:", () => {
|
||||
|
||||
describe("XHR GET request", () => {
|
||||
it("renders as expected", () => {
|
||||
const message = stubPreparedMessages.get("XHR GET request eventTimings");
|
||||
const wrapper = render(NetworkEventMessage({ message, serviceContainer }));
|
||||
const message = stubPreparedMessages.get("XHR GET request");
|
||||
const update = stubPreparedMessages.get("XHR GET request eventTimings");
|
||||
const wrapper = render(NetworkEventMessage({
|
||||
message,
|
||||
serviceContainer,
|
||||
networkMessageUpdate: update,
|
||||
}));
|
||||
|
||||
expect(wrapper.find(".message-body .method").text()).toBe("GET");
|
||||
expect(wrapper.find(".message-body .xhr").length).toBe(1);
|
||||
@ -79,8 +86,13 @@ describe("NetworkEventMessage component:", () => {
|
||||
|
||||
describe("XHR POST request", () => {
|
||||
it("renders as expected", () => {
|
||||
const message = stubPreparedMessages.get("XHR POST request eventTimings");
|
||||
const wrapper = render(NetworkEventMessage({ message, serviceContainer }));
|
||||
const message = stubPreparedMessages.get("XHR POST request");
|
||||
const update = stubPreparedMessages.get("XHR POST request eventTimings");
|
||||
const wrapper = render(NetworkEventMessage({
|
||||
message,
|
||||
serviceContainer,
|
||||
networkMessageUpdate: update,
|
||||
}));
|
||||
|
||||
expect(wrapper.find(".message-body .method").text()).toBe("POST");
|
||||
expect(wrapper.find(".message-body .xhr").length).toBe(1);
|
||||
|
@ -7,6 +7,7 @@ const {
|
||||
getAllMessagesById,
|
||||
getAllMessagesTableDataById,
|
||||
getAllMessagesUiById,
|
||||
getAllNetworkMessagesUpdateById,
|
||||
getAllRepeatById,
|
||||
getCurrentGroup,
|
||||
getVisibleMessages,
|
||||
@ -477,4 +478,45 @@ describe("Message reducer:", () => {
|
||||
expect(groupsById.size).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("networkMessagesUpdateById", () => {
|
||||
it("adds the network update message when network update action is called", () => {
|
||||
const { dispatch, getState } = setupStore([
|
||||
"GET request",
|
||||
"XHR GET request"
|
||||
]);
|
||||
|
||||
let networkUpdates = getAllNetworkMessagesUpdateById(getState());
|
||||
expect(Object.keys(networkUpdates).length).toBe(0);
|
||||
|
||||
let updatePacket = stubPackets.get("GET request eventTimings");
|
||||
dispatch(actions.networkMessageUpdate(updatePacket));
|
||||
|
||||
networkUpdates = getAllNetworkMessagesUpdateById(getState());
|
||||
expect(Object.keys(networkUpdates).length).toBe(1);
|
||||
|
||||
let xhrUpdatePacket = stubPackets.get("XHR GET request eventTimings");
|
||||
dispatch(actions.networkMessageUpdate(xhrUpdatePacket));
|
||||
|
||||
networkUpdates = getAllNetworkMessagesUpdateById(getState());
|
||||
expect(Object.keys(networkUpdates).length).toBe(2);
|
||||
});
|
||||
|
||||
it("resets networkMessagesUpdateById in response to MESSAGES_CLEAR action", () => {
|
||||
const { dispatch, getState } = setupStore([
|
||||
"XHR GET request"
|
||||
]);
|
||||
|
||||
const updatePacket = stubPackets.get("XHR GET request eventTimings");
|
||||
dispatch(actions.networkMessageUpdate(updatePacket));
|
||||
|
||||
let networkUpdates = getAllNetworkMessagesUpdateById(getState());
|
||||
expect(Object.keys(networkUpdates).length).toBe(1);
|
||||
|
||||
dispatch(actions.messagesClear());
|
||||
|
||||
networkUpdates = getAllNetworkMessagesUpdateById(getState());
|
||||
expect(Object.keys(networkUpdates).length).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1382,6 +1382,20 @@ var DebuggerServer = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when DevTools are unloaded to remove the contend process server script for the
|
||||
* list of scripts loaded for each new content process. Will also remove message
|
||||
* listeners from already loaded scripts.
|
||||
*/
|
||||
removeContentServerScript() {
|
||||
Services.ppmm.removeDelayedProcessScript(CONTENT_PROCESS_DBG_SERVER_SCRIPT);
|
||||
try {
|
||||
Services.ppmm.broadcastAsyncMessage("debug:close-content-server");
|
||||
} catch (e) {
|
||||
// Nothing to do
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Searches all active connections for an actor matching an ID.
|
||||
*
|
||||
|
@ -47,59 +47,67 @@ const filenameParam = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Both commands have the same set of standard optional parameters
|
||||
* Both commands have almost the same set of standard optional parameters, except for the
|
||||
* type of the --selector option, which can be a node only on the server.
|
||||
*/
|
||||
const standardParams = {
|
||||
group: l10n.lookup("screenshotGroupOptions"),
|
||||
params: [
|
||||
{
|
||||
name: "clipboard",
|
||||
type: "boolean",
|
||||
description: l10n.lookup("screenshotClipboardDesc"),
|
||||
manual: l10n.lookup("screenshotClipboardManual")
|
||||
},
|
||||
{
|
||||
name: "imgur",
|
||||
type: "boolean",
|
||||
description: l10n.lookup("screenshotImgurDesc"),
|
||||
manual: l10n.lookup("screenshotImgurManual")
|
||||
},
|
||||
{
|
||||
name: "delay",
|
||||
type: { name: "number", min: 0 },
|
||||
defaultValue: 0,
|
||||
description: l10n.lookup("screenshotDelayDesc"),
|
||||
manual: l10n.lookup("screenshotDelayManual")
|
||||
},
|
||||
{
|
||||
name: "dpr",
|
||||
type: { name: "number", min: 0, allowFloat: true },
|
||||
defaultValue: 0,
|
||||
description: l10n.lookup("screenshotDPRDesc"),
|
||||
manual: l10n.lookup("screenshotDPRManual")
|
||||
},
|
||||
{
|
||||
name: "fullpage",
|
||||
type: "boolean",
|
||||
description: l10n.lookup("screenshotFullPageDesc"),
|
||||
manual: l10n.lookup("screenshotFullPageManual")
|
||||
},
|
||||
{
|
||||
name: "selector",
|
||||
type: "node",
|
||||
defaultValue: null,
|
||||
description: l10n.lookup("inspectNodeDesc"),
|
||||
manual: l10n.lookup("inspectNodeManual")
|
||||
},
|
||||
{
|
||||
name: "file",
|
||||
type: "boolean",
|
||||
description: l10n.lookup("screenshotFileDesc"),
|
||||
manual: l10n.lookup("screenshotFileManual"),
|
||||
},
|
||||
]
|
||||
const getScreenshotCommandParams = function (isClient) {
|
||||
return {
|
||||
group: l10n.lookup("screenshotGroupOptions"),
|
||||
params: [
|
||||
{
|
||||
name: "clipboard",
|
||||
type: "boolean",
|
||||
description: l10n.lookup("screenshotClipboardDesc"),
|
||||
manual: l10n.lookup("screenshotClipboardManual")
|
||||
},
|
||||
{
|
||||
name: "imgur",
|
||||
type: "boolean",
|
||||
description: l10n.lookup("screenshotImgurDesc"),
|
||||
manual: l10n.lookup("screenshotImgurManual")
|
||||
},
|
||||
{
|
||||
name: "delay",
|
||||
type: { name: "number", min: 0 },
|
||||
defaultValue: 0,
|
||||
description: l10n.lookup("screenshotDelayDesc"),
|
||||
manual: l10n.lookup("screenshotDelayManual")
|
||||
},
|
||||
{
|
||||
name: "dpr",
|
||||
type: { name: "number", min: 0, allowFloat: true },
|
||||
defaultValue: 0,
|
||||
description: l10n.lookup("screenshotDPRDesc"),
|
||||
manual: l10n.lookup("screenshotDPRManual")
|
||||
},
|
||||
{
|
||||
name: "fullpage",
|
||||
type: "boolean",
|
||||
description: l10n.lookup("screenshotFullPageDesc"),
|
||||
manual: l10n.lookup("screenshotFullPageManual")
|
||||
},
|
||||
{
|
||||
name: "selector",
|
||||
// On the client side, don't try to parse the selector as a node as it will
|
||||
// trigger an unsafe CPOW.
|
||||
type: isClient ? "string" : "node",
|
||||
defaultValue: null,
|
||||
description: l10n.lookup("inspectNodeDesc"),
|
||||
manual: l10n.lookup("inspectNodeManual")
|
||||
},
|
||||
{
|
||||
name: "file",
|
||||
type: "boolean",
|
||||
description: l10n.lookup("screenshotFileDesc"),
|
||||
manual: l10n.lookup("screenshotFileManual"),
|
||||
},
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
const clientScreenshotParams = getScreenshotCommandParams(true);
|
||||
const serverScreenshotParams = getScreenshotCommandParams(false);
|
||||
|
||||
exports.items = [
|
||||
{
|
||||
/**
|
||||
@ -180,7 +188,7 @@ exports.items = [
|
||||
tooltipText: l10n.lookup("screenshotTooltipPage"),
|
||||
params: [
|
||||
filenameParam,
|
||||
standardParams,
|
||||
clientScreenshotParams,
|
||||
],
|
||||
exec: function (args, context) {
|
||||
// Re-execute the command on the server
|
||||
@ -199,7 +207,10 @@ exports.items = [
|
||||
name: "screenshot_server",
|
||||
hidden: true,
|
||||
returnType: "imageSummary",
|
||||
params: [ filenameParam, standardParams ],
|
||||
params: [
|
||||
filenameParam,
|
||||
serverScreenshotParams,
|
||||
],
|
||||
exec: function (args, context) {
|
||||
return captureScreenshot(args, context.environment.document);
|
||||
},
|
||||
|
@ -1042,6 +1042,7 @@ EffectCompositor::PreTraverseInSubtree(Element* aRoot,
|
||||
// middle of the servo traversal.
|
||||
mPresContext->RestyleManager()->AsServo()->
|
||||
PostRestyleEventForAnimations(target.mElement,
|
||||
target.mPseudoType,
|
||||
cascadeLevel == CascadeLevel::Transitions
|
||||
? eRestyle_CSSTransitions
|
||||
: eRestyle_CSSAnimations);
|
||||
@ -1108,6 +1109,7 @@ EffectCompositor::PreTraverse(dom::Element* aElement,
|
||||
|
||||
mPresContext->RestyleManager()->AsServo()->
|
||||
PostRestyleEventForAnimations(aElement,
|
||||
aPseudoType,
|
||||
cascadeLevel == CascadeLevel::Transitions
|
||||
? eRestyle_CSSTransitions
|
||||
: eRestyle_CSSAnimations);
|
||||
|
@ -249,6 +249,15 @@ public:
|
||||
bool PreTraverseInSubtree(dom::Element* aElement,
|
||||
AnimationRestyleType aRestyleType);
|
||||
|
||||
// Returns the target element for restyling.
|
||||
//
|
||||
// If |aPseudoType| is ::after or ::before, returns the generated content
|
||||
// element of which |aElement| is the parent. If |aPseudoType| is any other
|
||||
// pseudo type (other thant CSSPseudoElementType::NotPseudo) returns nullptr.
|
||||
// Otherwise, returns |aElement|.
|
||||
static dom::Element* GetElementToRestyle(dom::Element* aElement,
|
||||
CSSPseudoElementType aPseudoType);
|
||||
|
||||
private:
|
||||
~EffectCompositor() = default;
|
||||
|
||||
@ -258,10 +267,6 @@ private:
|
||||
CSSPseudoElementType aPseudoType,
|
||||
CascadeLevel aCascadeLevel);
|
||||
|
||||
static dom::Element* GetElementToRestyle(dom::Element* aElement,
|
||||
CSSPseudoElementType
|
||||
aPseudoType);
|
||||
|
||||
// Get the properties in |aEffectSet| that we are able to animate on the
|
||||
// compositor but which are also specified at a higher level in the cascade
|
||||
// than the animations level.
|
||||
|
28
dom/animation/test/crashtests/1335998-1.html
Normal file
28
dom/animation/test/crashtests/1335998-1.html
Normal file
@ -0,0 +1,28 @@
|
||||
<!doctype html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<title>
|
||||
Bug 1335998 - Handle {Interpolate, Accumulate}Matrix of mismatched transform lists
|
||||
</title>
|
||||
<style>
|
||||
#target {
|
||||
width: 100px; height: 100px;
|
||||
background: blue;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="target"></div>
|
||||
</body>
|
||||
<script>
|
||||
var div = document.getElementById("target");
|
||||
var animation = div.animate([ { transform: 'translateX(200px) scale(2.0)',
|
||||
composite: 'accumulate' },
|
||||
{ transform: 'rotate(-45deg)' } ],
|
||||
2000);
|
||||
animation.finished.then(function() {
|
||||
document.documentElement.className = "";
|
||||
});
|
||||
</script>
|
||||
</html>
|
@ -26,5 +26,6 @@ pref(dom.animations-api.core.enabled,true) load 1333539-1.html
|
||||
pref(dom.animations-api.core.enabled,true) load 1333539-2.html
|
||||
pref(dom.animations-api.core.enabled,true) load 1333418-1.html
|
||||
pref(dom.animations-api.core.enabled,true) load 1334583-1.html
|
||||
pref(dom.animations-api.core.enabled,true) load 1335998-1.html
|
||||
pref(dom.animations-api.core.enabled,true) load 1343589-1.html
|
||||
pref(dom.animations-api.core.enabled,true) load 1359658-1.html
|
||||
|
@ -14,6 +14,12 @@
|
||||
@keyframes animRight {
|
||||
to { right: 100px }
|
||||
}
|
||||
::before {
|
||||
content: ''
|
||||
}
|
||||
::after {
|
||||
content: ''
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<script>
|
||||
|
@ -3,6 +3,12 @@
|
||||
<script src="../testcommon.js"></script>
|
||||
<style>
|
||||
@keyframes anim { }
|
||||
::before {
|
||||
content: ''
|
||||
}
|
||||
::after {
|
||||
content: ''
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<script>
|
||||
|
@ -11,6 +11,12 @@
|
||||
@keyframes multiPropAnim {
|
||||
to { background: green, opacity: 0.5, left: 100px, top: 100px }
|
||||
}
|
||||
::before {
|
||||
content: ''
|
||||
}
|
||||
::after {
|
||||
content: ''
|
||||
}
|
||||
@keyframes empty { }
|
||||
</style>
|
||||
<body>
|
||||
|
@ -6,6 +6,7 @@
|
||||
@keyframes anim2 { }
|
||||
.before::before {
|
||||
animation: anim1 10s;
|
||||
content: '';
|
||||
}
|
||||
.after-with-mix-anims-trans::after {
|
||||
content: '';
|
||||
@ -17,6 +18,7 @@
|
||||
.after-change::after {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
content: '';
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
|
@ -921,6 +921,10 @@ public:
|
||||
*/
|
||||
mozilla::dom::Element* GetEditingHost();
|
||||
|
||||
bool SupportsLangAttr() const {
|
||||
return IsHTMLElement() || IsSVGElement() || IsXULElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determining language. Look at the nearest ancestor element that has a lang
|
||||
* attribute in the XML namespace or is an HTML/SVG element and has a lang in
|
||||
@ -933,8 +937,7 @@ public:
|
||||
// XHTML1 section C.7).
|
||||
bool hasAttr = content->GetAttr(kNameSpaceID_XML, nsGkAtoms::lang,
|
||||
aResult);
|
||||
if (!hasAttr && (content->IsHTMLElement() || content->IsSVGElement() ||
|
||||
content->IsXULElement())) {
|
||||
if (!hasAttr && content->SupportsLangAttr()) {
|
||||
hasAttr = content->GetAttr(kNameSpaceID_None, nsGkAtoms::lang,
|
||||
aResult);
|
||||
}
|
||||
|
@ -121,6 +121,10 @@ public:
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(IMEContentObserver)
|
||||
|
||||
// Note that we don't need to add mFirstAddedNodeContainer nor
|
||||
// mLastAddedNodeContainer to cycle collection because they are non-null only
|
||||
// during short time and shouldn't be touched while they are non-null.
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver)
|
||||
nsAutoScriptBlocker scriptBlocker;
|
||||
|
||||
@ -132,6 +136,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditableNode)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditor)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentObserver)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndOfAddedTextCache.mContainerNode)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartOfRemovingTextRangeCache.mContainerNode)
|
||||
|
||||
@ -147,6 +152,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IMEContentObserver)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditableNode)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditor)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentObserver)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndOfAddedTextCache.mContainerNode)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
|
||||
mStartOfRemovingTextRangeCache.mContainerNode)
|
||||
@ -166,7 +172,9 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver)
|
||||
|
||||
IMEContentObserver::IMEContentObserver()
|
||||
: mESM(nullptr)
|
||||
: mFirstAddedNodeOffset(0)
|
||||
, mLastAddedNodeOffset(0)
|
||||
, mESM(nullptr)
|
||||
, mIMENotificationRequests(nullptr)
|
||||
, mSuppressNotifications(0)
|
||||
, mPreCharacterDataChangeLength(-1)
|
||||
@ -201,7 +209,6 @@ IMEContentObserver::Init(nsIWidget* aWidget,
|
||||
// If this is now trying to initialize with new contents, all observers
|
||||
// should be registered again for simpler implementation.
|
||||
UnregisterObservers();
|
||||
// Clear members which may not be initialized again.
|
||||
Clear();
|
||||
}
|
||||
|
||||
@ -352,6 +359,8 @@ IMEContentObserver::InitWithEditor(nsPresContext* aPresContext,
|
||||
return false;
|
||||
}
|
||||
|
||||
mDocumentObserver = new DocumentObserver(*this);
|
||||
|
||||
MOZ_ASSERT(!WasInitializedWithPlugin());
|
||||
|
||||
return true;
|
||||
@ -383,6 +392,11 @@ IMEContentObserver::InitWithPlugin(nsPresContext* aPresContext,
|
||||
mEditor = nullptr;
|
||||
mEditableNode = aContent;
|
||||
mRootContent = aContent;
|
||||
// Should be safe to clear mDocumentObserver here even though it *might*
|
||||
// grab this instance because this is called by Init() and the callers of
|
||||
// it and MaybeReinitialize() grabs this instance with local RefPtr.
|
||||
// So, this won't cause refcount of this instance become 0.
|
||||
mDocumentObserver = nullptr;
|
||||
|
||||
mDocShell = aPresContext->GetDocShell();
|
||||
if (NS_WARN_IF(!mDocShell)) {
|
||||
@ -408,6 +422,12 @@ IMEContentObserver::Clear()
|
||||
mEditableNode = nullptr;
|
||||
mRootContent = nullptr;
|
||||
mDocShell = nullptr;
|
||||
// Should be safe to clear mDocumentObserver here even though it grabs
|
||||
// this instance in most cases because this is called by Init() or Destroy().
|
||||
// The callers of Init() grab this instance with local RefPtr.
|
||||
// The caller of Destroy() also grabs this instance with local RefPtr.
|
||||
// So, this won't cause refcount of this instance become 0.
|
||||
mDocumentObserver = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
@ -446,6 +466,13 @@ IMEContentObserver::ObserveEditableNode()
|
||||
// non-plugin content since we cannot detect text changes in
|
||||
// plugins.
|
||||
mRootContent->AddMutationObserver(this);
|
||||
// If it's in a document (should be so), we can use document observer to
|
||||
// reduce redundant computation of text change offsets.
|
||||
nsIDocument* doc = mRootContent->GetComposedDoc();
|
||||
if (doc) {
|
||||
RefPtr<DocumentObserver> documentObserver = mDocumentObserver;
|
||||
documentObserver->Observe(doc);
|
||||
}
|
||||
}
|
||||
|
||||
if (mDocShell) {
|
||||
@ -519,6 +546,11 @@ IMEContentObserver::UnregisterObservers()
|
||||
mRootContent->RemoveMutationObserver(this);
|
||||
}
|
||||
|
||||
if (mDocumentObserver) {
|
||||
RefPtr<DocumentObserver> documentObserver = mDocumentObserver;
|
||||
documentObserver->StopObserving();
|
||||
}
|
||||
|
||||
if (mDocShell) {
|
||||
mDocShell->RemoveWeakScrollObserver(this);
|
||||
mDocShell->RemoveWeakReflowObserver(this);
|
||||
@ -907,6 +939,13 @@ IMEContentObserver::CharacterDataWillChange(nsIDocument* aDocument,
|
||||
|
||||
mEndOfAddedTextCache.Clear();
|
||||
mStartOfRemovingTextRangeCache.Clear();
|
||||
|
||||
// Although we don't assume this change occurs while this is storing
|
||||
// the range of added consecutive nodes, if it actually happens, we need to
|
||||
// flush them since this change may occur before or in the range. So, it's
|
||||
// safe to flush pending computation of mTextChangeData before handling this.
|
||||
MaybeNotifyIMEOfAddedTextDuringDocumentChange();
|
||||
|
||||
mPreCharacterDataChangeLength =
|
||||
ContentEventHandler::GetNativeTextLength(aContent, aInfo->mChangeStart,
|
||||
aInfo->mChangeEnd);
|
||||
@ -929,6 +968,8 @@ IMEContentObserver::CharacterDataChanged(nsIDocument* aDocument,
|
||||
|
||||
mEndOfAddedTextCache.Clear();
|
||||
mStartOfRemovingTextRangeCache.Clear();
|
||||
MOZ_ASSERT(!HasAddedNodesDuringDocumentChange(),
|
||||
"The stored range should be flushed before actually the data is changed");
|
||||
|
||||
int64_t removedLength = mPreCharacterDataChangeLength;
|
||||
mPreCharacterDataChangeLength = -1;
|
||||
@ -973,6 +1014,42 @@ IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
|
||||
|
||||
mStartOfRemovingTextRangeCache.Clear();
|
||||
|
||||
// If it's in a document change, nodes are added consecutively. Therefore,
|
||||
// if we cache the first node and the last node, we need to compute the
|
||||
// range once.
|
||||
// FYI: This is not true if the change caused by an operation in the editor.
|
||||
if (IsInDocumentChange()) {
|
||||
// Now, mEndOfAddedTextCache may be invalid if node is added before
|
||||
// the last node in mEndOfAddedTextCache. Clear it.
|
||||
mEndOfAddedTextCache.Clear();
|
||||
if (!HasAddedNodesDuringDocumentChange()) {
|
||||
mFirstAddedNodeContainer = mLastAddedNodeContainer = aContainer;
|
||||
mFirstAddedNodeOffset = aStartIndex;
|
||||
mLastAddedNodeOffset = aEndIndex;
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Debug,
|
||||
("0x%p IMEContentObserver::NotifyContentAdded(), starts to store "
|
||||
"consecutive added nodes", this));
|
||||
return;
|
||||
}
|
||||
// If first node being added is not next node of the last node,
|
||||
// notify IME of the previous range first, then, restart to cache the
|
||||
// range.
|
||||
if (NS_WARN_IF(!IsNextNodeOfLastAddedNode(aContainer, aStartIndex))) {
|
||||
// Flush the old range first.
|
||||
MaybeNotifyIMEOfAddedTextDuringDocumentChange();
|
||||
mFirstAddedNodeContainer = aContainer;
|
||||
mFirstAddedNodeOffset = aStartIndex;
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Debug,
|
||||
("0x%p IMEContentObserver::NotifyContentAdded(), starts to store "
|
||||
"consecutive added nodes", this));
|
||||
}
|
||||
mLastAddedNodeContainer = aContainer;
|
||||
mLastAddedNodeOffset = aEndIndex;
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(!HasAddedNodesDuringDocumentChange(),
|
||||
"The cache should be cleared when document change finished");
|
||||
|
||||
uint32_t offset = 0;
|
||||
nsresult rv = NS_OK;
|
||||
if (!mEndOfAddedTextCache.Match(aContainer, aStartIndex)) {
|
||||
@ -1048,6 +1125,7 @@ IMEContentObserver::ContentRemoved(nsIDocument* aDocument,
|
||||
}
|
||||
|
||||
mEndOfAddedTextCache.Clear();
|
||||
MaybeNotifyIMEOfAddedTextDuringDocumentChange();
|
||||
|
||||
nsINode* containerNode = NODE_FROM(aContainer, aDocument);
|
||||
|
||||
@ -1133,6 +1211,9 @@ IMEContentObserver::AttributeChanged(nsIDocument* aDocument,
|
||||
if (postAttrChangeLength == mPreAttrChangeLength) {
|
||||
return;
|
||||
}
|
||||
// First, compute text range which were added during a document change.
|
||||
MaybeNotifyIMEOfAddedTextDuringDocumentChange();
|
||||
// Then, compute the new text changed caused by this attribute change.
|
||||
uint32_t start;
|
||||
nsresult rv =
|
||||
ContentEventHandler::GetFlatTextLengthInRange(
|
||||
@ -1150,6 +1231,169 @@ IMEContentObserver::AttributeChanged(nsIDocument* aDocument,
|
||||
MaybeNotifyIMEOfTextChange(data);
|
||||
}
|
||||
|
||||
void
|
||||
IMEContentObserver::ClearAddedNodesDuringDocumentChange()
|
||||
{
|
||||
mFirstAddedNodeContainer = mLastAddedNodeContainer = nullptr;
|
||||
mFirstAddedNodeOffset = mLastAddedNodeOffset = 0;
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Debug,
|
||||
("0x%p IMEContentObserver::ClearAddedNodesDuringDocumentChange()"
|
||||
", finished storing consecutive nodes", this));
|
||||
}
|
||||
|
||||
// static
|
||||
nsIContent*
|
||||
IMEContentObserver::GetChildNode(nsINode* aParent, int32_t aOffset)
|
||||
{
|
||||
if (!aParent->HasChildren() || aOffset < 0 ||
|
||||
aOffset >= static_cast<int32_t>(aParent->Length())) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!aOffset) {
|
||||
return aParent->GetFirstChild();
|
||||
}
|
||||
if (aOffset == static_cast<int32_t>(aParent->Length() - 1)) {
|
||||
return aParent->GetLastChild();
|
||||
}
|
||||
return aParent->GetChildAt(aOffset);
|
||||
}
|
||||
|
||||
bool
|
||||
IMEContentObserver::IsNextNodeOfLastAddedNode(nsINode* aParent,
|
||||
int32_t aOffset) const
|
||||
{
|
||||
MOZ_ASSERT(aParent);
|
||||
MOZ_ASSERT(aOffset >= 0 &&
|
||||
aOffset <= static_cast<int32_t>(aParent->Length()));
|
||||
MOZ_ASSERT(mRootContent);
|
||||
MOZ_ASSERT(HasAddedNodesDuringDocumentChange());
|
||||
|
||||
// If the parent node isn't changed, we can check it only with offset.
|
||||
if (aParent == mLastAddedNodeContainer) {
|
||||
if (NS_WARN_IF(mLastAddedNodeOffset != aOffset)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the parent node is changed, that means that given offset should be the
|
||||
// last added node not having next sibling.
|
||||
if (NS_WARN_IF(mLastAddedNodeOffset !=
|
||||
static_cast<int32_t>(mLastAddedNodeContainer->Length()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the node is aParent is a descendant of mLastAddedNodeContainer,
|
||||
// aOffset should be 0.
|
||||
if (mLastAddedNodeContainer == aParent->GetParent()) {
|
||||
if (NS_WARN_IF(aOffset)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, we need to check it even with slow path.
|
||||
nsIContent* lastAddedContent =
|
||||
GetChildNode(mLastAddedNodeContainer, mLastAddedNodeOffset - 1);
|
||||
if (NS_WARN_IF(!lastAddedContent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIContent* nextContentOfLastAddedContent =
|
||||
lastAddedContent->GetNextNode(mRootContent->GetParentNode());
|
||||
if (NS_WARN_IF(!nextContentOfLastAddedContent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIContent* startContent = GetChildNode(aParent, aOffset);
|
||||
if (NS_WARN_IF(!startContent) ||
|
||||
NS_WARN_IF(nextContentOfLastAddedContent != startContent)) {
|
||||
return false;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
NS_WARNING_ASSERTION(
|
||||
!aOffset || aOffset == static_cast<int32_t>(aParent->Length() - 1),
|
||||
"Used slow path for aParent");
|
||||
NS_WARNING_ASSERTION(
|
||||
!(mLastAddedNodeOffset - 1) ||
|
||||
mLastAddedNodeOffset ==
|
||||
static_cast<int32_t>(mLastAddedNodeContainer->Length()),
|
||||
"Used slow path for mLastAddedNodeContainer");
|
||||
#endif // #ifdef DEBUG
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
IMEContentObserver::MaybeNotifyIMEOfAddedTextDuringDocumentChange()
|
||||
{
|
||||
if (!HasAddedNodesDuringDocumentChange()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Debug,
|
||||
("0x%p IMEContentObserver::MaybeNotifyIMEOfAddedTextDuringDocumentChange()"
|
||||
", flushing stored consecutive nodes", this));
|
||||
|
||||
// Notify IME of text change which is caused by added nodes now.
|
||||
|
||||
// First, compute offset of start of first added node from start of the
|
||||
// editor.
|
||||
uint32_t offset;
|
||||
nsresult rv =
|
||||
ContentEventHandler::GetFlatTextLengthInRange(
|
||||
NodePosition(mRootContent, 0),
|
||||
NodePosition(mFirstAddedNodeContainer,
|
||||
mFirstAddedNodeOffset),
|
||||
mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
ClearAddedNodesDuringDocumentChange();
|
||||
return;
|
||||
}
|
||||
|
||||
// Next, compute the text length of added nodes.
|
||||
uint32_t length;
|
||||
rv =
|
||||
ContentEventHandler::GetFlatTextLengthInRange(
|
||||
NodePosition(mFirstAddedNodeContainer,
|
||||
mFirstAddedNodeOffset),
|
||||
NodePosition(mLastAddedNodeContainer,
|
||||
mLastAddedNodeOffset),
|
||||
mRootContent, &length, LINE_BREAK_TYPE_NATIVE);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
ClearAddedNodesDuringDocumentChange();
|
||||
return;
|
||||
}
|
||||
|
||||
// Finally, try to notify IME of the range.
|
||||
TextChangeData data(offset, offset, offset + length,
|
||||
IsEditorHandlingEventForComposition(),
|
||||
IsEditorComposing());
|
||||
MaybeNotifyIMEOfTextChange(data);
|
||||
ClearAddedNodesDuringDocumentChange();
|
||||
}
|
||||
|
||||
void
|
||||
IMEContentObserver::BeginDocumentUpdate()
|
||||
{
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Debug,
|
||||
("0x%p IMEContentObserver::BeginDocumentUpdate(), "
|
||||
"HasAddedNodesDuringDocumentChange()=%s",
|
||||
this, ToChar(HasAddedNodesDuringDocumentChange())));
|
||||
|
||||
MOZ_ASSERT(!HasAddedNodesDuringDocumentChange());
|
||||
}
|
||||
|
||||
void
|
||||
IMEContentObserver::EndDocumentUpdate()
|
||||
{
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Debug,
|
||||
("0x%p IMEContentObserver::EndDocumentUpdate(), "
|
||||
"HasAddedNodesDuringDocumentChange()=%s",
|
||||
this, ToChar(HasAddedNodesDuringDocumentChange())));
|
||||
|
||||
MaybeNotifyIMEOfAddedTextDuringDocumentChange();
|
||||
}
|
||||
|
||||
void
|
||||
IMEContentObserver::SuppressNotifyingIME()
|
||||
{
|
||||
@ -1975,4 +2219,105 @@ IMEContentObserver::IMENotificationSender::SendCompositionEventHandled()
|
||||
"NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED", this));
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* mozilla::IMEContentObserver::DocumentObservingHelper
|
||||
******************************************************************************/
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(IMEContentObserver::DocumentObserver)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver::DocumentObserver)
|
||||
// StopObserving() releases mIMEContentObserver and mDocument.
|
||||
tmp->StopObserving();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IMEContentObserver::DocumentObserver)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIMEContentObserver)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMEContentObserver::DocumentObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver::DocumentObserver)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver::DocumentObserver)
|
||||
|
||||
void
|
||||
IMEContentObserver::DocumentObserver::Observe(nsIDocument* aDocument)
|
||||
{
|
||||
MOZ_ASSERT(aDocument);
|
||||
|
||||
// Guarantee that aDocument won't be destroyed during a call of
|
||||
// StopObserving().
|
||||
RefPtr<nsIDocument> newDocument = aDocument;
|
||||
|
||||
StopObserving();
|
||||
|
||||
mDocument = newDocument.forget();
|
||||
mDocument->AddObserver(this);
|
||||
}
|
||||
|
||||
void
|
||||
IMEContentObserver::DocumentObserver::StopObserving()
|
||||
{
|
||||
if (!IsObserving()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Grab IMEContentObserver which could be destroyed during method calls.
|
||||
RefPtr<IMEContentObserver> observer = mIMEContentObserver.forget();
|
||||
|
||||
// Stop observing the document first.
|
||||
RefPtr<nsIDocument> document = mDocument.forget();
|
||||
document->RemoveObserver(this);
|
||||
|
||||
// Notify IMEContentObserver of ending of document updates if this already
|
||||
// notified it of beginning of document updates.
|
||||
for (; IsUpdating(); --mDocumentUpdating) {
|
||||
// FYI: IsUpdating() returns true until mDocumentUpdating becomes 0.
|
||||
// However, IsObserving() returns false now because mDocument was
|
||||
// already cleared above. Therefore, this method won't be called
|
||||
// recursively.
|
||||
observer->EndDocumentUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
IMEContentObserver::DocumentObserver::Destroy()
|
||||
{
|
||||
StopObserving();
|
||||
mIMEContentObserver = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
IMEContentObserver::DocumentObserver::BeginUpdate(nsIDocument* aDocument,
|
||||
nsUpdateType aUpdateType)
|
||||
{
|
||||
if (NS_WARN_IF(Destroyed()) || NS_WARN_IF(!IsObserving())) {
|
||||
return;
|
||||
}
|
||||
if (!(aUpdateType & UPDATE_CONTENT_MODEL)) {
|
||||
return;
|
||||
}
|
||||
mDocumentUpdating++;
|
||||
mIMEContentObserver->BeginDocumentUpdate();
|
||||
}
|
||||
|
||||
void
|
||||
IMEContentObserver::DocumentObserver::EndUpdate(nsIDocument* aDocument,
|
||||
nsUpdateType aUpdateType)
|
||||
{
|
||||
if (NS_WARN_IF(Destroyed()) || NS_WARN_IF(!IsObserving()) ||
|
||||
NS_WARN_IF(!IsUpdating())) {
|
||||
return;
|
||||
}
|
||||
if (!(aUpdateType & UPDATE_CONTENT_MODEL)) {
|
||||
return;
|
||||
}
|
||||
mDocumentUpdating--;
|
||||
mIMEContentObserver->EndDocumentUpdate();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "nsISelectionListener.h"
|
||||
#include "nsIScrollObserver.h"
|
||||
#include "nsIWidget.h"
|
||||
#include "nsStubDocumentObserver.h"
|
||||
#include "nsStubMutationObserver.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsWeakReference.h"
|
||||
@ -73,9 +74,39 @@ public:
|
||||
|
||||
nsresult HandleQueryContentEvent(WidgetQueryContentEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Init() initializes the instance, i.e., retrieving necessary objects and
|
||||
* starts to observe something.
|
||||
* Be aware, callers of this method need to guarantee that the instance
|
||||
* won't be released during calling this.
|
||||
*
|
||||
* @param aWidget The widget which can access native IME.
|
||||
* @param aPresContext The PresContext which has aContent.
|
||||
* @param aContent An editable element or a plugin host element which
|
||||
* user may use IME in.
|
||||
* Or nullptr if this will observe design mode
|
||||
* document.
|
||||
* @param aEditor When aContent is an editable element or nullptr,
|
||||
* non-nullptr referring an editor instance which
|
||||
* manages aContent.
|
||||
* Otherwise, i.e., this will observe a plugin content,
|
||||
* should be nullptr.
|
||||
*/
|
||||
void Init(nsIWidget* aWidget, nsPresContext* aPresContext,
|
||||
nsIContent* aContent, nsIEditor* aEditor);
|
||||
|
||||
/**
|
||||
* Destroy() finalizes the instance, i.e., stops observing contents and
|
||||
* clearing the members.
|
||||
* Be aware, callers of this method need to guarantee that the instance
|
||||
* won't be released during calling this.
|
||||
*/
|
||||
void Destroy();
|
||||
|
||||
/**
|
||||
* Returns false if the instance refers some objects and observing them.
|
||||
* Otherwise, true.
|
||||
*/
|
||||
bool Destroyed() const;
|
||||
|
||||
/**
|
||||
@ -84,10 +115,14 @@ public:
|
||||
* storing the instance.
|
||||
*/
|
||||
void DisconnectFromEventStateManager();
|
||||
|
||||
/**
|
||||
* MaybeReinitialize() tries to restart to observe the editor's root node.
|
||||
* This is useful when the editor is reframed and all children are replaced
|
||||
* with new node instances.
|
||||
* Be aware, callers of this method need to guarantee that the instance
|
||||
* won't be released during calling this.
|
||||
*
|
||||
* @return Returns true if the instance is managing the content.
|
||||
* Otherwise, false.
|
||||
*/
|
||||
@ -95,6 +130,7 @@ public:
|
||||
nsPresContext* aPresContext,
|
||||
nsIContent* aContent,
|
||||
nsIEditor* aEditor);
|
||||
|
||||
bool IsManaging(nsPresContext* aPresContext, nsIContent* aContent) const;
|
||||
bool IsManaging(const TextComposition* aTextComposition) const;
|
||||
bool WasInitializedWithPlugin() const;
|
||||
@ -146,6 +182,61 @@ private:
|
||||
bool IsSafeToNotifyIME() const;
|
||||
bool IsEditorComposing() const;
|
||||
|
||||
/**
|
||||
* nsINode::GetChildAt() is slow. So, this avoids to use it if it's
|
||||
* first child or last child of aParent.
|
||||
*/
|
||||
static nsIContent* GetChildNode(nsINode* aParent, int32_t aOffset);
|
||||
|
||||
// Following methods are called by DocumentObserver when
|
||||
// beginning to update the contents and ending updating the contents.
|
||||
void BeginDocumentUpdate();
|
||||
void EndDocumentUpdate();
|
||||
|
||||
// Following methods manages added nodes during a document change.
|
||||
|
||||
/**
|
||||
* MaybeNotifyIMEOfAddedTextDuringDocumentChange() may send text change
|
||||
* notification caused by the nodes added between mFirstAddedNodeOffset in
|
||||
* mFirstAddedNodeContainer and mLastAddedNodeOffset in
|
||||
* mLastAddedNodeContainer and forgets the range.
|
||||
*/
|
||||
void MaybeNotifyIMEOfAddedTextDuringDocumentChange();
|
||||
|
||||
/**
|
||||
* IsInDocumentChange() returns true while the DOM tree is being modified
|
||||
* with mozAutoDocUpdate. E.g., it's being modified by setting innerHTML or
|
||||
* insertAdjacentHTML(). This returns false when user types something in
|
||||
* the focused editor editor.
|
||||
*/
|
||||
bool IsInDocumentChange() const
|
||||
{
|
||||
return mDocumentObserver && mDocumentObserver->IsUpdating();
|
||||
}
|
||||
|
||||
/**
|
||||
* Forget the range of added nodes during a document change.
|
||||
*/
|
||||
void ClearAddedNodesDuringDocumentChange();
|
||||
|
||||
/**
|
||||
* HasAddedNodesDuringDocumentChange() returns true when this stores range
|
||||
* of nodes which were added into the DOM tree during a document change but
|
||||
* have not been sent to IME. Note that this should always return false when
|
||||
* IsInDocumentChange() returns false.
|
||||
*/
|
||||
bool HasAddedNodesDuringDocumentChange() const
|
||||
{
|
||||
return mFirstAddedNodeContainer && mLastAddedNodeContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the node at aOffset in aParent is next node of the node at
|
||||
* mLastAddedNodeOffset in mLastAddedNodeContainer in pre-order tree
|
||||
* traversal of the DOM.
|
||||
*/
|
||||
bool IsNextNodeOfLastAddedNode(nsINode* aParent, int32_t aOffset) const;
|
||||
|
||||
void PostFocusSetNotification();
|
||||
void MaybeNotifyIMEOfFocusSet();
|
||||
void PostTextChangeNotification();
|
||||
@ -278,6 +369,47 @@ private:
|
||||
// mQueuedSender is, it was put into the event queue but not run yet.
|
||||
RefPtr<IMENotificationSender> mQueuedSender;
|
||||
|
||||
/**
|
||||
* IMEContentObserver is a mutation observer of mRootContent. However,
|
||||
* it needs to know the beginning of content changes and end of it too for
|
||||
* reducing redundant computation of text offset with ContentEventHandler.
|
||||
* Therefore, it needs helper class to listen only them since if
|
||||
* both mutations were observed by IMEContentObserver directly, each
|
||||
* methods need to check if the changing node is in mRootContent but it's
|
||||
* too expensive.
|
||||
*/
|
||||
class DocumentObserver final : public nsStubDocumentObserver
|
||||
{
|
||||
public:
|
||||
explicit DocumentObserver(IMEContentObserver& aIMEContentObserver)
|
||||
: mIMEContentObserver(&aIMEContentObserver)
|
||||
, mDocumentUpdating(0)
|
||||
{
|
||||
}
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(DocumentObserver)
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE
|
||||
NS_DECL_NSIDOCUMENTOBSERVER_ENDUPDATE
|
||||
|
||||
void Observe(nsIDocument* aDocument);
|
||||
void StopObserving();
|
||||
void Destroy();
|
||||
|
||||
bool Destroyed() const { return !mIMEContentObserver; }
|
||||
bool IsObserving() const { return mDocument != nullptr; }
|
||||
bool IsUpdating() const { return mDocumentUpdating != 0; }
|
||||
|
||||
private:
|
||||
DocumentObserver() = delete;
|
||||
virtual ~DocumentObserver() { Destroy(); }
|
||||
|
||||
RefPtr<IMEContentObserver> mIMEContentObserver;
|
||||
nsCOMPtr<nsIDocument> mDocument;
|
||||
uint32_t mDocumentUpdating;
|
||||
};
|
||||
RefPtr<DocumentObserver> mDocumentObserver;
|
||||
|
||||
/**
|
||||
* FlatTextCache stores flat text length from start of the content to
|
||||
* mNodeOffset of mContainerNode.
|
||||
@ -332,6 +464,27 @@ private:
|
||||
// handled by the editor and no other mutation (e.g., adding node) occur.
|
||||
FlatTextCache mStartOfRemovingTextRangeCache;
|
||||
|
||||
// mFirstAddedNodeContainer is parent node of first added node in current
|
||||
// document change. So, this is not nullptr only when a node was added
|
||||
// during a document change and the change has not been included into
|
||||
// mTextChangeData yet.
|
||||
// Note that this shouldn't be in cycle collection since this is not nullptr
|
||||
// only during a document change.
|
||||
nsCOMPtr<nsINode> mFirstAddedNodeContainer;
|
||||
// mLastAddedNodeContainer is parent node of last added node in current
|
||||
// document change. So, this is not nullptr only when a node was added
|
||||
// during a document change and the change has not been included into
|
||||
// mTextChangeData yet.
|
||||
// Note that this shouldn't be in cycle collection since this is not nullptr
|
||||
// only during a document change.
|
||||
nsCOMPtr<nsINode> mLastAddedNodeContainer;
|
||||
// mFirstAddedNodeOffset is offset of first added node in
|
||||
// mFirstAddedNodeContainer.
|
||||
int32_t mFirstAddedNodeOffset;
|
||||
// mLastAddedNodeOffset is offset of *after* last added node in
|
||||
// mLastAddedNodeContainer. I.e., the index of last added node + 1.
|
||||
int32_t mLastAddedNodeOffset;
|
||||
|
||||
TextChangeData mTextChangeData;
|
||||
|
||||
// mSelectionData is the last selection data which was notified. The
|
||||
|
@ -863,8 +863,9 @@ IMEStateManager::UpdateIMEState(const IMEState& aNewIMEState,
|
||||
MOZ_LOG(sISMLog, LogLevel::Debug,
|
||||
(" UpdateIMEState(), try to reinitialize the "
|
||||
"active IMEContentObserver"));
|
||||
if (!sActiveIMEContentObserver->MaybeReinitialize(widget, sPresContext,
|
||||
aContent, &aEditorBase)) {
|
||||
RefPtr<IMEContentObserver> contentObserver = sActiveIMEContentObserver;
|
||||
if (!contentObserver->MaybeReinitialize(widget, sPresContext,
|
||||
aContent, &aEditorBase)) {
|
||||
MOZ_LOG(sISMLog, LogLevel::Error,
|
||||
(" UpdateIMEState(), failed to reinitialize the "
|
||||
"active IMEContentObserver"));
|
||||
|
@ -1287,10 +1287,11 @@ private:
|
||||
|
||||
if (aReject.mError == NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA) {
|
||||
SLOG("OnSeekRejected reason=WAITING_FOR_DATA type=%d", aReject.mType);
|
||||
MOZ_ASSERT(!mMaster->IsRequestingAudioData());
|
||||
MOZ_ASSERT(!mMaster->IsRequestingVideoData());
|
||||
MOZ_ASSERT(!mMaster->IsWaitingAudioData());
|
||||
MOZ_ASSERT(!mMaster->IsWaitingVideoData());
|
||||
MOZ_ASSERT_IF(aReject.mType == MediaData::AUDIO_DATA, !mMaster->IsRequestingAudioData());
|
||||
MOZ_ASSERT_IF(aReject.mType == MediaData::VIDEO_DATA, !mMaster->IsRequestingVideoData());
|
||||
MOZ_ASSERT_IF(aReject.mType == MediaData::AUDIO_DATA, !mMaster->IsWaitingAudioData());
|
||||
MOZ_ASSERT_IF(aReject.mType == MediaData::VIDEO_DATA, !mMaster->IsWaitingVideoData());
|
||||
|
||||
// Fire 'waiting' to notify the player that we are waiting for data.
|
||||
mMaster->UpdateNextFrameStatus(
|
||||
MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING);
|
||||
|
@ -112,6 +112,9 @@ public:
|
||||
int32_t offset;
|
||||
ok &= NS_SUCCEEDED(info->Offset(&offset));
|
||||
|
||||
int32_t size;
|
||||
ok &= NS_SUCCEEDED(info->Size(&size));
|
||||
|
||||
int64_t presentationTimeUs;
|
||||
ok &= NS_SUCCEEDED(info->PresentationTimeUs(&presentationTimeUs));
|
||||
|
||||
@ -130,7 +133,7 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
if (ok && presentationTimeUs >= 0) {
|
||||
if (ok && (size > 0 || presentationTimeUs >= 0)) {
|
||||
RefPtr<layers::Image> img = new SurfaceTextureImage(
|
||||
mDecoder->mSurfaceHandle, inputInfo.mImageSize, false /* NOT continuous */,
|
||||
gl::OriginPos::BottomLeft);
|
||||
|
@ -385,11 +385,12 @@ H264Converter::CheckForSPSChange(MediaRawData* aSample)
|
||||
// We now check if the out of band one has changed.
|
||||
// This scenario can only occur on Android with devices that can recycle a
|
||||
// decoder.
|
||||
if (mp4_demuxer::AnnexB::HasSPS(aSample->mExtraData) &&
|
||||
!mp4_demuxer::AnnexB::CompareExtraData(aSample->mExtraData,
|
||||
mOriginalExtraData)) {
|
||||
extra_data = mOriginalExtraData = aSample->mExtraData;
|
||||
if (!mp4_demuxer::AnnexB::HasSPS(aSample->mExtraData) ||
|
||||
mp4_demuxer::AnnexB::CompareExtraData(aSample->mExtraData,
|
||||
mOriginalExtraData)) {
|
||||
return NS_OK;
|
||||
}
|
||||
extra_data = mOriginalExtraData = aSample->mExtraData;
|
||||
}
|
||||
if (mp4_demuxer::AnnexB::CompareExtraData(extra_data,
|
||||
mCurrentConfig.mExtraData)) {
|
||||
|
@ -13,10 +13,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1323324
|
||||
/** Test for Bug 1323324 **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var globalWrapper;
|
||||
function verifyPromiseGlobal(p, global, msg) {
|
||||
// SpecialPowers.Cu.getGlobalForObject returns a SpecialPowers wrapper for
|
||||
// the actual global. We want to grab the underlying object.
|
||||
var globalWrapper = SpecialPowers.Cu.getGlobalForObject(p);
|
||||
globalWrapper = SpecialPowers.Cu.getGlobalForObject(p);
|
||||
is(SpecialPowers.unwrap(globalWrapper), global,
|
||||
msg + " should come from " + global.label);
|
||||
}
|
||||
|
@ -13,18 +13,20 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1323324
|
||||
/** Test for Bug 1323324 **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var globalWrapper;
|
||||
function verifyPromiseGlobal(p, global, msg) {
|
||||
// SpecialPowers.Cu.getGlobalForObject returns a SpecialPowers wrapper for
|
||||
// the actual global. We want to grab the underlying object.
|
||||
var globalWrapper = SpecialPowers.Cu.getGlobalForObject(p);
|
||||
globalWrapper = SpecialPowers.Cu.getGlobalForObject(p);
|
||||
is(SpecialPowers.unwrap(globalWrapper), global,
|
||||
msg + " should come from " + global.label);
|
||||
}
|
||||
|
||||
const isXrayArgumentTest = false;
|
||||
|
||||
var func;
|
||||
function getPromise(global, arg) {
|
||||
var func = new global.Function("x", "return x").bind(undefined, arg);
|
||||
func = new global.Function("x", "return x").bind(undefined, arg);
|
||||
return TestFunctions.passThroughCallbackPromise(func);
|
||||
}
|
||||
|
||||
|
@ -767,6 +767,7 @@ nsSMILAnimationController::PreTraverseInSubtree(Element* aRoot)
|
||||
|
||||
context->RestyleManager()->AsServo()->
|
||||
PostRestyleEventForAnimations(key.mElement,
|
||||
CSSPseudoElementType::NotPseudo,
|
||||
eRestyle_StyleAttribute_Animations);
|
||||
|
||||
foundElementsNeedingRestyle = true;
|
||||
|
@ -617,6 +617,17 @@ EditorBase::GetSelectionController(nsISelectionController** aSel)
|
||||
{
|
||||
NS_ENSURE_TRUE(aSel, NS_ERROR_NULL_POINTER);
|
||||
*aSel = nullptr; // init out param
|
||||
nsCOMPtr<nsISelectionController> selCon = GetSelectionController();
|
||||
if (NS_WARN_IF(!selCon)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
selCon.forget(aSel);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsISelectionController>
|
||||
EditorBase::GetSelectionController()
|
||||
{
|
||||
nsCOMPtr<nsISelectionController> selCon;
|
||||
if (mSelConWeak) {
|
||||
selCon = do_QueryReferent(mSelConWeak);
|
||||
@ -624,11 +635,7 @@ EditorBase::GetSelectionController(nsISelectionController** aSel)
|
||||
nsCOMPtr<nsIPresShell> presShell = GetPresShell();
|
||||
selCon = do_QueryInterface(presShell);
|
||||
}
|
||||
if (!selCon) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
NS_ADDREF(*aSel = selCon);
|
||||
return NS_OK;
|
||||
return selCon.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -651,8 +658,7 @@ EditorBase::GetSelection(SelectionType aSelectionType,
|
||||
{
|
||||
NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
|
||||
*aSelection = nullptr;
|
||||
nsCOMPtr<nsISelectionController> selcon;
|
||||
GetSelectionController(getter_AddRefs(selcon));
|
||||
nsCOMPtr<nsISelectionController> selcon = GetSelectionController();
|
||||
if (!selcon) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
@ -493,6 +493,7 @@ protected:
|
||||
*/
|
||||
bool EnsureComposition(WidgetCompositionEvent* aCompositionEvent);
|
||||
|
||||
already_AddRefed<nsISelectionController> GetSelectionController();
|
||||
nsresult GetSelection(SelectionType aSelectionType,
|
||||
nsISelection** aSelection);
|
||||
|
||||
|
@ -525,7 +525,7 @@ nsHttpNegotiateAuth::GenerateCredentials(nsIHttpAuthenticableChannel *authChanne
|
||||
//
|
||||
unsigned int len = strlen(challenge);
|
||||
|
||||
void *inToken, *outToken;
|
||||
void *inToken = nullptr, *outToken;
|
||||
uint32_t inTokenLen, outTokenLen;
|
||||
|
||||
if (len > kNegotiateLen) {
|
||||
@ -545,6 +545,7 @@ nsHttpNegotiateAuth::GenerateCredentials(nsIHttpAuthenticableChannel *authChanne
|
||||
Base64Decode(challenge, len, (char**)&inToken, &inTokenLen);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
free(inToken);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
@ -552,7 +553,6 @@ nsHttpNegotiateAuth::GenerateCredentials(nsIHttpAuthenticableChannel *authChanne
|
||||
//
|
||||
// Initializing, don't use an input token.
|
||||
//
|
||||
inToken = nullptr;
|
||||
inTokenLen = 0;
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,8 @@ function* test(testDriver) {
|
||||
// scroll over the scrollbar, and make sure the subframe scrolls
|
||||
var scrollPos = subframe.scrollTop;
|
||||
var scrollbarX = (200 + subframe.clientWidth) / 2;
|
||||
yield moveMouseAndScrollWheelOver(subframe, scrollbarX, 100, testDriver);
|
||||
yield synthesizeNativeWheelAndWaitForScrollEvent(subframe, scrollbarX, 100,
|
||||
0, -10, testDriver);
|
||||
ok(subframe.scrollTop > scrollPos, "subframe scrolled after wheeling over scrollbar");
|
||||
}
|
||||
|
||||
|
@ -460,7 +460,7 @@ private:
|
||||
DECL_GFX_PREF(Live, "gfx.vsync.collect-scroll-transforms", CollectScrollTransforms, bool, false);
|
||||
DECL_GFX_PREF(Once, "gfx.vsync.compositor.unobserve-count", CompositorUnobserveCount, int32_t, 10);
|
||||
DECL_OVERRIDE_PREF(Live, "gfx.webrender.omta.enabled", WebRenderOMTAEnabled, gfxPrefs::OverrideBase_WebRender());
|
||||
DECL_GFX_PREF(Live, "gfx.webrender.profiler.enable", WebRenderProfilerEnabled, bool, false);
|
||||
DECL_GFX_PREF(Live, "gfx.webrender.profiler.enabled", WebRenderProfilerEnabled, bool, false);
|
||||
DECL_GFX_PREF(Live, "gfx.webrendest.enabled", WebRendestEnabled, bool, false);
|
||||
// Use vsync events generated by hardware
|
||||
DECL_GFX_PREF(Once, "gfx.work-around-driver-bugs", WorkAroundDriverBugs, bool, true);
|
||||
@ -501,7 +501,7 @@ private:
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.boxshadow-outer-layers", LayersAllowOuterBoxShadow, gfxPrefs::OverrideBase_WebRender());
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.bullet-layers", LayersAllowBulletLayers, gfxPrefs::OverrideBase_WebRender());
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.button-foreground-layers", LayersAllowButtonForegroundLayers, gfxPrefs::OverrideBase_WebRender());
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.canvas-background-color", LayersAllowCanvasBackgroundColorLayers, gfxPrefs::OverrideBase_WebRendest());
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.canvas-background-color", LayersAllowCanvasBackgroundColorLayers, gfxPrefs::OverrideBase_WebRender());
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.caret-layers", LayersAllowCaretLayers, gfxPrefs::OverrideBase_WebRender());
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.columnRule-layers", LayersAllowColumnRuleLayers, gfxPrefs::OverrideBase_WebRender());
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.displaybuttonborder-layers", LayersAllowDisplayButtonBorder, gfxPrefs::OverrideBase_WebRender());
|
||||
|
@ -129,6 +129,7 @@ NS_IMETHODIMP nsTextToSubURI::UnEscapeAndConvert(
|
||||
nsDependentCString label(charset);
|
||||
nsAutoCString encoding;
|
||||
if (!EncodingUtils::FindEncodingForLabelNoReplacement(label, encoding)) {
|
||||
free(unescaped);
|
||||
return NS_ERROR_UCONV_NOCONV;
|
||||
}
|
||||
nsCOMPtr<nsIUnicodeDecoder> decoder =
|
||||
|
@ -217,6 +217,7 @@ function treatAsSafeArgument(entry, varName, csuName)
|
||||
["Gecko_AddPropertyToSet", "aPropertySet", null],
|
||||
["Gecko_CalcStyleDifference", "aAnyStyleChanged", null],
|
||||
["Gecko_nsStyleSVG_CopyContextProperties", "aDst", null],
|
||||
["Gecko_nsStyleFont_PrefillDefaultForGeneric", "aFont", null],
|
||||
];
|
||||
for (var [entryMatch, varMatch, csuMatch] of whitelist) {
|
||||
assert(entryMatch || varMatch || csuMatch);
|
||||
@ -331,8 +332,7 @@ function ignoreCallEdge(entry, callee)
|
||||
// We manually lock here
|
||||
if (name == "Gecko_nsFont_InitSystem" ||
|
||||
name == "Gecko_GetFontMetrics" ||
|
||||
name == "Gecko_nsStyleFont_FixupNoneGeneric" ||
|
||||
name == "Gecko_nsStyleFont_FixupMinFontSize")
|
||||
name == "ThreadSafeGetDefaultFontHelper")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -390,6 +390,7 @@ function ignoreContents(entry)
|
||||
/CSSValueSerializeCalcOps::Append/,
|
||||
"Gecko_CSSValue_SetFunction",
|
||||
"Gecko_CSSValue_SetArray",
|
||||
"Gecko_CSSValue_InitSharedList",
|
||||
"Gecko_EnsureMozBorderColors",
|
||||
"Gecko_ClearMozBorderColors",
|
||||
"Gecko_AppendMozBorderColors",
|
||||
|
@ -10665,33 +10665,110 @@ BytecodeEmitter::emitClass(ParseNode* pn)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pseudocode for class declarations:
|
||||
//
|
||||
// class extends BaseExpression {
|
||||
// constructor() { ... }
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if defined <BaseExpression> {
|
||||
// let heritage = BaseExpression;
|
||||
//
|
||||
// if (heritage !== null) {
|
||||
// funProto = heritage;
|
||||
// objProto = heritage.prototype;
|
||||
// } else {
|
||||
// funProto = %FunctionPrototype%;
|
||||
// objProto = null;
|
||||
// }
|
||||
// } else {
|
||||
// objProto = %ObjectPrototype%;
|
||||
// }
|
||||
//
|
||||
// let homeObject = ObjectCreate(objProto);
|
||||
//
|
||||
// if defined <constructor> {
|
||||
// if defined <BaseExpression> {
|
||||
// cons = DefineMethod(<constructor>, proto=homeObject, funProto=funProto);
|
||||
// } else {
|
||||
// cons = DefineMethod(<constructor>, proto=homeObject);
|
||||
// }
|
||||
// } else {
|
||||
// if defined <BaseExpression> {
|
||||
// cons = DefaultDerivedConstructor(proto=homeObject, funProto=funProto);
|
||||
// } else {
|
||||
// cons = DefaultConstructor(proto=homeObject);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// cons.prototype = homeObject;
|
||||
// homeObject.constructor = cons;
|
||||
//
|
||||
// EmitPropertyList(...)
|
||||
|
||||
// This is kind of silly. In order to the get the home object defined on
|
||||
// the constructor, we have to make it second, but we want the prototype
|
||||
// on top for EmitPropertyList, because we expect static properties to be
|
||||
// rarer. The result is a few more swaps than we would like. Such is life.
|
||||
if (heritageExpression) {
|
||||
if (!emitTree(heritageExpression))
|
||||
return false;
|
||||
if (!emit1(JSOP_CLASSHERITAGE))
|
||||
return false;
|
||||
if (!emit1(JSOP_OBJWITHPROTO))
|
||||
IfThenElseEmitter ifThenElse(this);
|
||||
|
||||
if (!emitTree(heritageExpression)) // ... HERITAGE
|
||||
return false;
|
||||
|
||||
// JSOP_CLASSHERITAGE leaves both protos on the stack. After
|
||||
// creating the prototype, swap it to the bottom to make the
|
||||
// constructor.
|
||||
if (!emit1(JSOP_SWAP))
|
||||
// Heritage must be null or a non-generator constructor
|
||||
if (!emit1(JSOP_CHECKCLASSHERITAGE)) // ... HERITAGE
|
||||
return false;
|
||||
|
||||
// [IF] (heritage !== null)
|
||||
if (!emit1(JSOP_DUP)) // ... HERITAGE HERITAGE
|
||||
return false;
|
||||
if (!emit1(JSOP_NULL)) // ... HERITAGE HERITAGE NULL
|
||||
return false;
|
||||
if (!emit1(JSOP_STRICTNE)) // ... HERITAGE NE
|
||||
return false;
|
||||
|
||||
// [THEN] funProto = heritage, objProto = heritage.prototype
|
||||
if (!ifThenElse.emitIfElse())
|
||||
return false;
|
||||
if (!emit1(JSOP_DUP)) // ... HERITAGE HERITAGE
|
||||
return false;
|
||||
if (!emitAtomOp(cx->names().prototype, JSOP_GETPROP)) // ... HERITAGE PROTO
|
||||
return false;
|
||||
|
||||
// [ELSE] funProto = %FunctionPrototype%, objProto = null
|
||||
if (!ifThenElse.emitElse())
|
||||
return false;
|
||||
if (!emit1(JSOP_POP)) // ...
|
||||
return false;
|
||||
if (!emit2(JSOP_BUILTINPROTO, JSProto_Function)) // ... PROTO
|
||||
return false;
|
||||
if (!emit1(JSOP_NULL)) // ... PROTO NULL
|
||||
return false;
|
||||
|
||||
// [ENDIF]
|
||||
if (!ifThenElse.emitEnd())
|
||||
return false;
|
||||
|
||||
if (!emit1(JSOP_OBJWITHPROTO)) // ... HERITAGE HOMEOBJ
|
||||
return false;
|
||||
if (!emit1(JSOP_SWAP)) // ... HOMEOBJ HERITAGE
|
||||
return false;
|
||||
} else {
|
||||
if (!emitNewInit(JSProto_Object))
|
||||
if (!emitNewInit(JSProto_Object)) // ... HOMEOBJ
|
||||
return false;
|
||||
}
|
||||
|
||||
// Stack currently has HOMEOBJ followed by optional HERITAGE. When HERITAGE
|
||||
// is not used, an implicit value of %FunctionPrototype% is implied.
|
||||
|
||||
if (constructor) {
|
||||
if (!emitFunction(constructor, !!heritageExpression))
|
||||
if (!emitFunction(constructor, !!heritageExpression)) // ... HOMEOBJ CONSTRUCTOR
|
||||
return false;
|
||||
if (constructor->pn_funbox->needsHomeObject()) {
|
||||
if (!emit2(JSOP_INITHOMEOBJECT, 0))
|
||||
if (!emit2(JSOP_INITHOMEOBJECT, 0)) // ... HOMEOBJ CONSTRUCTOR
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
@ -10712,34 +10789,34 @@ BytecodeEmitter::emitClass(ParseNode* pn)
|
||||
|
||||
JSAtom *name = names ? names->innerBinding()->pn_atom : cx->names().empty;
|
||||
if (heritageExpression) {
|
||||
if (!emitAtomOp(name, JSOP_DERIVEDCONSTRUCTOR))
|
||||
if (!emitAtomOp(name, JSOP_DERIVEDCONSTRUCTOR)) // ... HOMEOBJ CONSTRUCTOR
|
||||
return false;
|
||||
} else {
|
||||
if (!emitAtomOp(name, JSOP_CLASSCONSTRUCTOR))
|
||||
if (!emitAtomOp(name, JSOP_CLASSCONSTRUCTOR)) // ... HOMEOBJ CONSTRUCTOR
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!emit1(JSOP_SWAP))
|
||||
if (!emit1(JSOP_SWAP)) // ... CONSTRUCTOR HOMEOBJ
|
||||
return false;
|
||||
|
||||
if (!emit1(JSOP_DUP2))
|
||||
if (!emit1(JSOP_DUP2)) // ... CONSTRUCTOR HOMEOBJ CONSTRUCTOR HOMEOBJ
|
||||
return false;
|
||||
if (!emitAtomOp(cx->names().prototype, JSOP_INITLOCKEDPROP))
|
||||
if (!emitAtomOp(cx->names().prototype, JSOP_INITLOCKEDPROP)) // ... CONSTRUCTOR HOMEOBJ CONSTRUCTOR
|
||||
return false;
|
||||
if (!emitAtomOp(cx->names().constructor, JSOP_INITHIDDENPROP))
|
||||
if (!emitAtomOp(cx->names().constructor, JSOP_INITHIDDENPROP)) // ... CONSTRUCTOR HOMEOBJ
|
||||
return false;
|
||||
|
||||
RootedPlainObject obj(cx);
|
||||
if (!emitPropertyList(classMethods, &obj, ClassBody))
|
||||
if (!emitPropertyList(classMethods, &obj, ClassBody)) // ... CONSTRUCTOR HOMEOBJ
|
||||
return false;
|
||||
|
||||
if (!emit1(JSOP_POP))
|
||||
if (!emit1(JSOP_POP)) // ... CONSTRUCTOR
|
||||
return false;
|
||||
|
||||
if (names) {
|
||||
ParseNode* innerName = names->innerBinding();
|
||||
if (!emitLexicalInitialization(innerName))
|
||||
if (!emitLexicalInitialization(innerName)) // ... CONSTRUCTOR
|
||||
return false;
|
||||
|
||||
// Pop the inner scope.
|
||||
@ -10749,15 +10826,17 @@ BytecodeEmitter::emitClass(ParseNode* pn)
|
||||
|
||||
ParseNode* outerName = names->outerBinding();
|
||||
if (outerName) {
|
||||
if (!emitLexicalInitialization(outerName))
|
||||
if (!emitLexicalInitialization(outerName)) // ... CONSTRUCTOR
|
||||
return false;
|
||||
// Only class statements make outer bindings, and they do not leave
|
||||
// themselves on the stack.
|
||||
if (!emit1(JSOP_POP))
|
||||
if (!emit1(JSOP_POP)) // ...
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// The CONSTRUCTOR is left on stack if this is an expression.
|
||||
|
||||
MOZ_ALWAYS_TRUE(sc->setLocalStrictMode(savedStrictness));
|
||||
|
||||
return true;
|
||||
|
@ -285,3 +285,9 @@ runTest(`(module
|
||||
end $outer
|
||||
)
|
||||
)`);
|
||||
|
||||
// Import as a start function.
|
||||
runTest(`(module
|
||||
(import "env" "test" (func))
|
||||
(start 0)
|
||||
)`);
|
||||
|
@ -4695,3 +4695,82 @@ BaselineCompiler::emit_JSOP_JUMPTARGET()
|
||||
masm.inc64(AbsoluteAddress(counterAddr));
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*CheckClassHeritageOperationFn)(JSContext*, HandleValue);
|
||||
static const VMFunction CheckClassHeritageOperationInfo =
|
||||
FunctionInfo<CheckClassHeritageOperationFn>(js::CheckClassHeritageOperation,
|
||||
"CheckClassHeritageOperation");
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_CHECKCLASSHERITAGE()
|
||||
{
|
||||
frame.syncStack(0);
|
||||
|
||||
// Leave the heritage value on the stack.
|
||||
masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
|
||||
|
||||
prepareVMCall();
|
||||
pushArg(R0);
|
||||
return callVM(CheckClassHeritageOperationInfo);
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_BUILTINPROTO()
|
||||
{
|
||||
// The builtin prototype is a constant for a given global.
|
||||
RootedObject builtin(cx);
|
||||
JSProtoKey key = static_cast<JSProtoKey>(GET_UINT8(pc));
|
||||
MOZ_ASSERT(key < JSProto_LIMIT);
|
||||
if (!GetBuiltinPrototype(cx, key, &builtin))
|
||||
return false;
|
||||
frame.push(ObjectValue(*builtin));
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef JSObject* (*ObjectWithProtoOperationFn)(JSContext*, HandleValue);
|
||||
static const VMFunction ObjectWithProtoOperationInfo =
|
||||
FunctionInfo<ObjectWithProtoOperationFn>(js::ObjectWithProtoOperation,
|
||||
"ObjectWithProtoOperationInfo");
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_OBJWITHPROTO()
|
||||
{
|
||||
frame.syncStack(0);
|
||||
|
||||
// Leave the proto value on the stack for the decompiler
|
||||
masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
|
||||
|
||||
prepareVMCall();
|
||||
pushArg(R0);
|
||||
if (!callVM(ObjectWithProtoOperationInfo))
|
||||
return false;
|
||||
|
||||
masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
|
||||
frame.pop();
|
||||
frame.push(R0);
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef JSObject* (*FunWithProtoFn)(JSContext*, HandleFunction, HandleObject, HandleObject);
|
||||
static const VMFunction FunWithProtoInfo =
|
||||
FunctionInfo<FunWithProtoFn>(js::FunWithProtoOperation, "FunWithProtoOperation");
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_FUNWITHPROTO()
|
||||
{
|
||||
frame.popRegsAndSync(1);
|
||||
|
||||
masm.unboxObject(R0, R0.scratchReg());
|
||||
masm.loadPtr(frame.addressOfEnvironmentChain(), R1.scratchReg());
|
||||
|
||||
prepareVMCall();
|
||||
pushArg(R0.scratchReg());
|
||||
pushArg(R1.scratchReg());
|
||||
pushArg(ImmGCPtr(script->getFunction(GET_UINT32_INDEX(pc))));
|
||||
if (!callVM(FunWithProtoInfo))
|
||||
return false;
|
||||
|
||||
masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
|
||||
frame.push(R0);
|
||||
return true;
|
||||
}
|
||||
|
@ -241,7 +241,11 @@ namespace jit {
|
||||
_(JSOP_DEBUGCHECKSELFHOSTED) \
|
||||
_(JSOP_JUMPTARGET) \
|
||||
_(JSOP_IS_CONSTRUCTING) \
|
||||
_(JSOP_TRY_DESTRUCTURING_ITERCLOSE)
|
||||
_(JSOP_TRY_DESTRUCTURING_ITERCLOSE) \
|
||||
_(JSOP_CHECKCLASSHERITAGE) \
|
||||
_(JSOP_BUILTINPROTO) \
|
||||
_(JSOP_OBJWITHPROTO) \
|
||||
_(JSOP_FUNWITHPROTO)
|
||||
|
||||
class BaselineCompiler : public BaselineCompilerSpecific
|
||||
{
|
||||
|
@ -102,11 +102,12 @@ MSG_DEF(JSMSG_CANT_SET_PROTO_OF, 1, JSEXN_TYPEERR, "can't set prototype of
|
||||
MSG_DEF(JSMSG_CANT_SET_PROTO_CYCLE, 0, JSEXN_TYPEERR, "can't set prototype: it would cause a prototype chain cycle")
|
||||
MSG_DEF(JSMSG_INVALID_ARG_TYPE, 3, JSEXN_TYPEERR, "Invalid type: {0} can't be a{1} {2}")
|
||||
MSG_DEF(JSMSG_TERMINATED, 1, JSEXN_ERR, "Script terminated by timeout at:\n{0}")
|
||||
MSG_DEF(JSMSG_PROTO_NOT_OBJORNULL, 1, JSEXN_TYPEERR, "{0}.prototype is not an object or null")
|
||||
MSG_DEF(JSMSG_CANT_CALL_CLASS_CONSTRUCTOR, 0, JSEXN_TYPEERR, "class constructors must be invoked with |new|")
|
||||
MSG_DEF(JSMSG_UNINITIALIZED_THIS, 1, JSEXN_REFERENCEERR, "|this| used uninitialized in {0} class constructor")
|
||||
MSG_DEF(JSMSG_UNINITIALIZED_THIS_ARROW, 0, JSEXN_REFERENCEERR, "|this| used uninitialized in arrow function in class constructor")
|
||||
MSG_DEF(JSMSG_BAD_DERIVED_RETURN, 1, JSEXN_TYPEERR, "derived class constructor returned invalid value {0}")
|
||||
MSG_DEF(JSMSG_BAD_HERITAGE, 2, JSEXN_TYPEERR, "class heritage {0} is {1}")
|
||||
MSG_DEF(JSMSG_NOT_OBJORNULL, 1, JSEXN_TYPEERR, "{0} is not an object or null")
|
||||
|
||||
// JSON
|
||||
MSG_DEF(JSMSG_JSON_BAD_PARSE, 3, JSEXN_SYNTAXERR, "JSON.parse: {0} at line {1} column {2} of the JSON data")
|
||||
|
@ -729,6 +729,7 @@ BytecodeParser::simulateOp(JSOp op, uint32_t offset, OffsetAndDefIndex* offsetSt
|
||||
case JSOP_CHECKOBJCOERCIBLE:
|
||||
case JSOP_CHECKTHIS:
|
||||
case JSOP_CHECKTHISREINIT:
|
||||
case JSOP_CHECKCLASSHERITAGE:
|
||||
case JSOP_DEBUGCHECKSELFHOSTED:
|
||||
case JSOP_INITGLEXICAL:
|
||||
case JSOP_INITLEXICAL:
|
||||
@ -1970,12 +1971,6 @@ ExpressionDecompiler::decompilePC(jsbytecode* pc, uint8_t defIndex)
|
||||
case JSOP_DERIVEDCONSTRUCTOR:
|
||||
return write("CONSTRUCTOR");
|
||||
|
||||
case JSOP_CLASSHERITAGE:
|
||||
if (defIndex == 0)
|
||||
return write("FUNCPROTO");
|
||||
MOZ_ASSERT(defIndex == 1);
|
||||
return write("OBJPROTO");
|
||||
|
||||
case JSOP_DOUBLE:
|
||||
return sprinter.printf("%lf", script->getConst(GET_UINT32_INDEX(pc)).toDouble());
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user