mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
import change from rmottola/Arctic-Fox:
- Bug 1139453 - Send network UP/DOWN events on desktop Linux. (7466c1298) - Bug 1141488 - On non-APZ platforms, combine any async transform with the OMTA transform rather than clobbering it the OMTA transform. (fe48d6774) - Bug 1080752 - Hold wakelock when attempting to connect to push server. (b1e74295f) - Bug 1126089 - Allow messages to be sent after frame script unload event. (167ebbd9a) - Bug 1126089 - Add test for pageload event on browser element when tab closed (f2011e6e4) - Bug 1090921 - Logging to see which tasks remain alive too long (ebfc3c9bd) - Bug 1142544 - delete now-unnecessary |extern "C++"| block from nsISupportsUtils.h; r=mccr8 (07e6572f8) - Bug 1142503 - don't use QueryInterface when the compiler can do the cast for us; r=ehsan (b13219054) - Bug 1140161 - Sensibly handle patterns with absolute paths in TEST_HARNESS_FILES. (173e369a7) - Bug 1082510 - When we detect a chrome popup at the current event coordinates and remote content has the mouse captured, release capture. r=Enn (da9fe26a7) - Bug 1137722 - Ensure that system message cache is refreshed before openapp. r=fabrice (97dc41f50) - Bug 1139904 - Add initial test runtime files for mochitest browser-chrome and devtools (9837c79b1) - Bug 1061604 part.1 nsTextStore::GetTextExt() should rReturn previous character rect instead of TS_E_NOLAYOUT when Google Japanese Input retrieves first character of selected clause at composing r=emk (54779d30e) - Bug 1061604 part.2 nsTextStore::GetTextExt() should rReturn previous character rect of modified range instead of TS_E_NOLAYOUT when Google Japanese Input retrieves caret rect during composition r=emk (ba7f93879) - Bug 1136051 - component_insert_pair statrs checks only while we are still checking or haven't started pairing; (ae1ae1d4f) - Bug 1137007 - Detect namespace and SECCOMP_FILTER_FLAG_TSYNC support in SandboxInfo. r=kang, r=Unfocused (6d9e32a8f) - Bug 1142263 - Specify all syscall parameters when doing CLONE_NEWUSER detection; f=bwc r=kang (8bcba1831) - Bug 1142567 - Remove FAIL_ON_WARNINGS_DEBUG; It is not used. (bf22f1ccf) - No bug - Add an explanatory message to the multiple-inheritance assertion in DOM bindings; # (ffd94ef07) - Bug 1141252. Don't mark the TablePainter display item as having background-attachment:fixed content if the background-attachment:fixed image will be painted by a dedicated nsDisplayTableCellBackground. r=mats (98287b37c) - Bug 1140084 - delete list of script counts in tail-recursive fashion. (90a2b7c45) - Bug 1048741 - [WebGL2] texParameter: Fix silly bug in TEXTURE_COMPARE_FUNC handling. (ccc90dae3) - Bug 1142478. Fix integer attribute parsing to not lose track of leading zeroes. (d596a1ce1) - Bug 1139388 - Remove superfluous locking in the OS X profiler implementation to fix deadlock. (r=mstange) (a13088958) - Bug 1128603: Remove findReferences and the tests that use it. (bccc64734) - Bug 1140683: Fix async plugin init using null plugin funcs on MacOSX (47b591d54) - Bug 1142494 - Fix OSX packaging mistake. (15555f4ee) - Bug 1136784 - Add screenshot to LogShake produced data. (7ff264243) - Bug 1142550 - Fix screenshot. (d684b6469) - Bug 1142006 - Set MACOSX_DEPLOYMENT_TARGET earlier. r=mshal (c333b954e) - Bug 1100966 - Remember all ranges for all selections when joining nodes in the editor transactions; r=roc (bf34bd0c8) - Bug 1136507 - L8, LA8, and A8 should not be renderable. (96e807795) - Bug 1128019 - Init SCISSOR_BOX with initial width and height. (7258cc44c) - Bug 1142211 - Add layerization testing mechanisms to reftest. r=roc (4c6aa11f8) - Bug 1106905 - Modify mobile desktop mode implementation to use a desktop viewport. (644ede0e7) - Bug 1139675 - Simplify the APIs for getting and setting the pres shell resolution. r=mstange,mattwoodrow (c520a8c98) - Bug 1141563 - Don't update Shapes in parallel after compacting GC (098af79c9)
This commit is contained in:
@@ -33,6 +33,9 @@ Cu.import('resource://gre/modules/PresentationDeviceInfoManager.jsm');
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
|
||||
"resource://gre/modules/SystemAppProxy.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Screenshot",
|
||||
"resource://gre/modules/Screenshot.jsm");
|
||||
|
||||
Cu.import('resource://gre/modules/Webapps.jsm');
|
||||
DOMApplicationRegistry.allAppsLaunchable = true;
|
||||
|
||||
@@ -818,30 +821,9 @@ window.addEventListener('ContentStart', function ss_onContentStart() {
|
||||
return;
|
||||
|
||||
try {
|
||||
var canvas = document.createElementNS('http://www.w3.org/1999/xhtml',
|
||||
'canvas');
|
||||
var docRect = document.body.getBoundingClientRect();
|
||||
var width = docRect.width;
|
||||
var height = docRect.height;
|
||||
|
||||
// Convert width and height from CSS pixels (potentially fractional)
|
||||
// to device pixels (integer).
|
||||
var scale = window.devicePixelRatio;
|
||||
canvas.setAttribute('width', Math.round(width * scale));
|
||||
canvas.setAttribute('height', Math.round(height * scale));
|
||||
|
||||
var context = canvas.getContext('2d');
|
||||
var flags =
|
||||
context.DRAWWINDOW_DRAW_CARET |
|
||||
context.DRAWWINDOW_DRAW_VIEW |
|
||||
context.DRAWWINDOW_USE_WIDGET_LAYERS;
|
||||
context.scale(scale, scale);
|
||||
context.drawWindow(window, 0, 0, width, height,
|
||||
'rgb(255,255,255)', flags);
|
||||
|
||||
shell.sendChromeEvent({
|
||||
type: 'take-screenshot-success',
|
||||
file: canvas.mozGetAsFile('screenshot', 'image/png')
|
||||
file: Screenshot.get()
|
||||
});
|
||||
} catch (e) {
|
||||
dump('exception while creating screenshot: ' + e + '\n');
|
||||
|
||||
@@ -25,7 +25,7 @@ const Observer = {
|
||||
start: function () {
|
||||
Services.obs.addObserver(this, 'remote-browser-shown', false);
|
||||
Services.obs.addObserver(this, 'inprocess-browser-shown', false);
|
||||
Services.obs.addObserver(this, 'message-manager-disconnect', false);
|
||||
Services.obs.addObserver(this, 'message-manager-close', false);
|
||||
|
||||
SystemAppProxy.getFrames().forEach(frame => {
|
||||
let mm = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager;
|
||||
@@ -40,7 +40,7 @@ const Observer = {
|
||||
stop: function () {
|
||||
Services.obs.removeObserver(this, 'remote-browser-shown');
|
||||
Services.obs.removeObserver(this, 'inprocess-browser-shown');
|
||||
Services.obs.removeObserver(this, 'message-manager-disconnect');
|
||||
Services.obs.removeObserver(this, 'message-manager-close');
|
||||
this._frames.clear();
|
||||
this._apps.clear();
|
||||
},
|
||||
@@ -61,7 +61,7 @@ const Observer = {
|
||||
break;
|
||||
|
||||
// Every time an iframe is destroyed, its message manager also is
|
||||
case 'message-manager-disconnect':
|
||||
case 'message-manager-close':
|
||||
this.onMessageManagerDestroyed(subject);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ const Cc = Components.classes;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Screenshot", "resource://gre/modules/Screenshot.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["LogCapture"];
|
||||
|
||||
@@ -185,6 +186,26 @@ let LogCapture = {
|
||||
deferred.resolve(file);
|
||||
}, null, false);
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Dumping screenshot, returning a Promise. Will be resolved with the content
|
||||
* as an ArrayBuffer.
|
||||
*/
|
||||
getScreenshot: function() {
|
||||
this.ensureLoaded();
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let fr = Cc["@mozilla.org/files/filereader;1"]
|
||||
.createInstance(Ci.nsIDOMFileReader);
|
||||
|
||||
fr.onload = function(evt) {
|
||||
deferred.resolve(new Uint8Array(evt.target.result));
|
||||
};
|
||||
|
||||
fr.readAsArrayBuffer(Screenshot.get());
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -240,6 +240,14 @@ let LogShake = {
|
||||
Cu.reportError("Unable to get about:memory dump: " + ex);
|
||||
}
|
||||
|
||||
try {
|
||||
LogCapture.getScreenshot().then(screenshot => {
|
||||
logArrays["logshake-screenshot.png"] = screenshot;
|
||||
});
|
||||
} catch (ex) {
|
||||
Cu.reportError("Unable to get screenshot dump: " + ex);
|
||||
}
|
||||
|
||||
for (let loc in this.LOGS_WITH_PARSERS) {
|
||||
let logArray;
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
'use strict';
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy", "resource://gre/modules/SystemAppProxy.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ['Screenshot'];
|
||||
|
||||
let Screenshot = {
|
||||
get: function screenshot_get() {
|
||||
let systemAppFrame = SystemAppProxy.getFrame();
|
||||
let window = systemAppFrame.ownerDocument.defaultView;
|
||||
let document = window.document;
|
||||
|
||||
var canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
|
||||
var docRect = document.body.getBoundingClientRect();
|
||||
var width = docRect.width;
|
||||
var height = docRect.height;
|
||||
|
||||
// Convert width and height from CSS pixels (potentially fractional)
|
||||
// to device pixels (integer).
|
||||
var scale = window.devicePixelRatio;
|
||||
canvas.setAttribute('width', Math.round(width * scale));
|
||||
canvas.setAttribute('height', Math.round(height * scale));
|
||||
|
||||
var context = canvas.getContext('2d');
|
||||
var flags =
|
||||
context.DRAWWINDOW_DRAW_CARET |
|
||||
context.DRAWWINDOW_DRAW_VIEW |
|
||||
context.DRAWWINDOW_USE_WIDGET_LAYERS;
|
||||
context.scale(scale, scale);
|
||||
context.drawWindow(window, 0, 0, width, height, 'rgb(255,255,255)', flags);
|
||||
|
||||
return canvas.mozGetAsFile('screenshot', 'image/png');
|
||||
}
|
||||
};
|
||||
this.Screenshot = Screenshot;
|
||||
@@ -29,6 +29,11 @@ let SystemAppProxy = {
|
||||
this._pendingListeners = [];
|
||||
},
|
||||
|
||||
// Get the system app frame
|
||||
getFrame: function () {
|
||||
return this._frame;
|
||||
},
|
||||
|
||||
// To call when it is ready to receive events
|
||||
setIsReady: function () {
|
||||
if (this._isReady) {
|
||||
|
||||
@@ -60,6 +60,7 @@ EXTRA_JS_MODULES += [
|
||||
'LogParser.jsm',
|
||||
'LogShake.jsm',
|
||||
'OrientationChangeHandler.jsm',
|
||||
'Screenshot.jsm',
|
||||
'SignInToWebsite.jsm',
|
||||
'SystemAppProxy.jsm',
|
||||
'TelURIParser.jsm',
|
||||
|
||||
@@ -4,6 +4,7 @@ support-files =
|
||||
permission_handler_chrome.js
|
||||
SandboxPromptTest.html
|
||||
filepicker_path_handler_chrome.js
|
||||
screenshot_helper.js
|
||||
systemapp_helper.js
|
||||
presentation_prompt_handler_chrome.js
|
||||
|
||||
@@ -12,5 +13,6 @@ support-files =
|
||||
[test_permission_gum_remember.html]
|
||||
skip-if = true # Bug 1019572 - frequent timeouts
|
||||
[test_sandbox_permission.html]
|
||||
[test_screenshot.html]
|
||||
[test_systemapp.html]
|
||||
[test_presentation_device_prompt.html]
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
const Cu = Components.utils;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
const { Services } = Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Load a duplicated copy of the jsm to prevent messing with the currently running one
|
||||
let scope = {};
|
||||
Services.scriptloader.loadSubScript("resource://gre/modules/Screenshot.jsm", scope);
|
||||
const { Screenshot } = scope;
|
||||
|
||||
let index = -1;
|
||||
function next() {
|
||||
index++;
|
||||
if (index >= steps.length) {
|
||||
assert.ok(false, "Shouldn't get here!");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
steps[index]();
|
||||
} catch(ex) {
|
||||
assert.ok(false, "Caught exception: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
let steps = [
|
||||
function getScreenshot() {
|
||||
let screenshot = Screenshot.get();
|
||||
assert.ok(screenshot instanceof Ci.nsIDOMFile,
|
||||
"Screenshot.get() returns a File");
|
||||
next();
|
||||
},
|
||||
|
||||
function endOfTest() {
|
||||
sendAsyncMessage("finish");
|
||||
}
|
||||
];
|
||||
|
||||
next();
|
||||
@@ -113,6 +113,9 @@ let steps = [
|
||||
SystemAppProxy.setIsReady();
|
||||
assert.ok(true, "Frame declared as loaded");
|
||||
|
||||
let gotFrame = SystemAppProxy.getFrame();
|
||||
assert.equal(gotFrame, frame, "getFrame returns the frame we passed");
|
||||
|
||||
// Once pending events are received,
|
||||
// we will run checkEventDispatching from `listener` function
|
||||
});
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1136784
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Screenshot Test</title>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1136784">Screenshot.jsm</a>
|
||||
<script type="application/javascript">
|
||||
|
||||
"use strict";
|
||||
|
||||
var gUrl = SimpleTest.getTestFileURL("screenshot_helper.js");
|
||||
var gScript = SpecialPowers.loadChromeScript(gUrl);
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
gScript.addMessageListener("finish", function () {
|
||||
SimpleTest.ok(true, "chrome test script finished");
|
||||
gScript.destroy();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -45,7 +45,6 @@ add_test(function test_readAppIni() {
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
|
||||
add_test(function test_get_about_memory() {
|
||||
let memLog = LogCapture.readAboutMemory();
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
|
||||
#distribution,
|
||||
#distributionId {
|
||||
font-weight: bold;
|
||||
display: none;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
|
||||
+2
-10
@@ -387,24 +387,16 @@ endif # CROSS_COMPILE
|
||||
CFLAGS += $(MOZ_FRAMEPTR_FLAGS)
|
||||
CXXFLAGS += $(MOZ_FRAMEPTR_FLAGS)
|
||||
|
||||
# Check for FAIL_ON_WARNINGS & FAIL_ON_WARNINGS_DEBUG (Shorthand for Makefiles
|
||||
# to request that we use the 'warnings as errors' compile flags)
|
||||
# Check for FAIL_ON_WARNINGS (Shorthand for Makefiles to request that we use
|
||||
# the 'warnings as errors' compile flags)
|
||||
|
||||
# NOTE: First, we clear FAIL_ON_WARNINGS[_DEBUG] if we're doing a Windows PGO
|
||||
# build, since WARNINGS_AS_ERRORS has been suspected of causing isuses in that
|
||||
# situation. (See bug 437002.)
|
||||
ifeq (WINNT_1,$(OS_ARCH)_$(MOZ_PROFILE_GENERATE)$(MOZ_PROFILE_USE))
|
||||
FAIL_ON_WARNINGS_DEBUG=
|
||||
FAIL_ON_WARNINGS=
|
||||
endif # WINNT && (MOS_PROFILE_GENERATE ^ MOZ_PROFILE_USE)
|
||||
|
||||
# Now, check for debug version of flag; it turns on normal flag in debug builds.
|
||||
ifdef FAIL_ON_WARNINGS_DEBUG
|
||||
ifdef MOZ_DEBUG
|
||||
FAIL_ON_WARNINGS = 1
|
||||
endif # MOZ_DEBUG
|
||||
endif # FAIL_ON_WARNINGS_DEBUG
|
||||
|
||||
# Check for normal version of flag, and add WARNINGS_AS_ERRORS if it's set to 1.
|
||||
ifdef FAIL_ON_WARNINGS
|
||||
# Never treat warnings as errors in clang-cl, because it warns about many more
|
||||
|
||||
+24
-24
@@ -418,6 +418,30 @@ MOZ_TOOL_VARIABLES
|
||||
|
||||
MOZ_CHECK_COMPILER_WRAPPER
|
||||
|
||||
dnl ========================================================
|
||||
dnl Check for MacOS deployment target version
|
||||
dnl ========================================================
|
||||
|
||||
MOZ_ARG_ENABLE_STRING(macos-target,
|
||||
[ --enable-macos-target=VER (default=10.6)
|
||||
Set the minimum MacOS version needed at runtime],
|
||||
[_MACOSX_DEPLOYMENT_TARGET=$enableval])
|
||||
|
||||
case "$target" in
|
||||
*-darwin*)
|
||||
if test -n "$_MACOSX_DEPLOYMENT_TARGET" ; then
|
||||
dnl Use the specified value
|
||||
export MACOSX_DEPLOYMENT_TARGET=$_MACOSX_DEPLOYMENT_TARGET
|
||||
else
|
||||
dnl No value specified on the command line or in the environment,
|
||||
dnl use architecture minimum.
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.6
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
AC_SUBST(MACOSX_DEPLOYMENT_TARGET)
|
||||
|
||||
dnl ========================================================
|
||||
dnl Special win32 checks
|
||||
dnl ========================================================
|
||||
@@ -839,30 +863,6 @@ if test -n "$UNIVERSAL_BINARY"; then
|
||||
fi
|
||||
AC_SUBST(UNIFY_DIST)
|
||||
|
||||
dnl ========================================================
|
||||
dnl Check for MacOS deployment target version
|
||||
dnl ========================================================
|
||||
|
||||
MOZ_ARG_ENABLE_STRING(macos-target,
|
||||
[ --enable-macos-target=VER (default=10.6)
|
||||
Set the minimum MacOS version needed at runtime],
|
||||
[_MACOSX_DEPLOYMENT_TARGET=$enableval])
|
||||
|
||||
case "$target" in
|
||||
*-darwin*)
|
||||
if test -n "$_MACOSX_DEPLOYMENT_TARGET" ; then
|
||||
dnl Use the specified value
|
||||
export MACOSX_DEPLOYMENT_TARGET=$_MACOSX_DEPLOYMENT_TARGET
|
||||
else
|
||||
dnl No value specified on the command line or in the environment,
|
||||
dnl use architecture minimum.
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.6
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
AC_SUBST(MACOSX_DEPLOYMENT_TARGET)
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Mac OS X SDK support
|
||||
dnl ========================================================
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/LoadInfo.h"
|
||||
#include "mozilla/dom/DocumentFragment.h"
|
||||
@@ -994,19 +995,29 @@ nsContentUtils::ParseHTMLInteger(const nsAString& aValue,
|
||||
}
|
||||
|
||||
bool foundValue = false;
|
||||
int32_t value = 0;
|
||||
int32_t pValue = 0; // Previous value, used to check integer overflow
|
||||
CheckedInt32 value = 0;
|
||||
|
||||
// Check for leading zeros first.
|
||||
uint64_t leadingZeros = 0;
|
||||
while (iter != end) {
|
||||
if (*iter != char16_t('0')) {
|
||||
break;
|
||||
}
|
||||
|
||||
++leadingZeros;
|
||||
foundValue = true;
|
||||
++iter;
|
||||
}
|
||||
|
||||
while (iter != end) {
|
||||
if (*iter >= char16_t('0') && *iter <= char16_t('9')) {
|
||||
value = (value * 10) + (*iter - char16_t('0'));
|
||||
++iter;
|
||||
// Checking for integer overflow.
|
||||
if (pValue > value) {
|
||||
if (!value.isValid()) {
|
||||
result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorOverflow;
|
||||
break;
|
||||
} else {
|
||||
foundValue = true;
|
||||
pValue = value;
|
||||
}
|
||||
} else if (*iter == char16_t('%')) {
|
||||
++iter;
|
||||
@@ -1024,17 +1035,21 @@ nsContentUtils::ParseHTMLInteger(const nsAString& aValue,
|
||||
if (negate) {
|
||||
value = -value;
|
||||
// Checking the special case of -0.
|
||||
if (!value) {
|
||||
if (value == 0) {
|
||||
result |= eParseHTMLInteger_NonStandard;
|
||||
}
|
||||
}
|
||||
|
||||
if (leadingZeros > 1 || (leadingZeros == 1 && !(value == 0))) {
|
||||
result |= eParseHTMLInteger_NonStandard;
|
||||
}
|
||||
|
||||
if (iter != end) {
|
||||
result |= eParseHTMLInteger_DidNotConsumeAllInput;
|
||||
}
|
||||
|
||||
*aResult = (ParseHTMLIntegerResultFlags)result;
|
||||
return value;
|
||||
return value.value();
|
||||
}
|
||||
|
||||
#define SKIP_WHITESPACE(iter, end_iter, end_res) \
|
||||
|
||||
@@ -366,6 +366,8 @@ public:
|
||||
enum ParseHTMLIntegerResultFlags {
|
||||
eParseHTMLInteger_NoFlags = 0,
|
||||
eParseHTMLInteger_IsPercent = 1 << 0,
|
||||
// eParseHTMLInteger_NonStandard is set if the string representation of the
|
||||
// integer was not the canonical one (e.g. had extra leading '+' or '0').
|
||||
eParseHTMLInteger_NonStandard = 1 << 1,
|
||||
eParseHTMLInteger_DidNotConsumeAllInput = 1 << 2,
|
||||
// Set if one or more error flags were set.
|
||||
|
||||
@@ -490,7 +490,7 @@ nsDOMWindowUtils::SetDisplayPortBaseForElement(int32_t aX,
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::SetResolution(float aXResolution, float aYResolution)
|
||||
nsDOMWindowUtils::SetResolution(float aResolution)
|
||||
{
|
||||
if (!nsContentUtils::IsCallerChrome()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
@@ -503,15 +503,15 @@ nsDOMWindowUtils::SetResolution(float aXResolution, float aYResolution)
|
||||
|
||||
nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
|
||||
if (sf) {
|
||||
sf->SetResolution(gfxSize(aXResolution, aYResolution));
|
||||
presShell->SetResolution(aXResolution, aYResolution);
|
||||
sf->SetResolution(aResolution);
|
||||
presShell->SetResolution(aResolution);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::SetResolutionAndScaleTo(float aXResolution, float aYResolution)
|
||||
nsDOMWindowUtils::SetResolutionAndScaleTo(float aResolution)
|
||||
{
|
||||
if (!nsContentUtils::IsCallerChrome()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
@@ -524,15 +524,15 @@ nsDOMWindowUtils::SetResolutionAndScaleTo(float aXResolution, float aYResolution
|
||||
|
||||
nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
|
||||
if (sf) {
|
||||
sf->SetResolutionAndScaleTo(gfxSize(aXResolution, aYResolution));
|
||||
presShell->SetResolutionAndScaleTo(aXResolution, aYResolution);
|
||||
sf->SetResolutionAndScaleTo(aResolution);
|
||||
presShell->SetResolutionAndScaleTo(aResolution);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::GetResolution(float* aXResolution, float* aYResolution)
|
||||
nsDOMWindowUtils::GetResolution(float* aResolution)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
||||
|
||||
@@ -543,12 +543,9 @@ nsDOMWindowUtils::GetResolution(float* aXResolution, float* aYResolution)
|
||||
|
||||
nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
|
||||
if (sf) {
|
||||
const gfxSize& res = sf->GetResolution();
|
||||
*aXResolution = res.width;
|
||||
*aYResolution = res.height;
|
||||
*aResolution = sf->GetResolution();
|
||||
} else {
|
||||
*aXResolution = presShell->GetXResolution();
|
||||
*aYResolution = presShell->GetYResolution();
|
||||
*aResolution = presShell->GetResolution();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@@ -2375,6 +2372,18 @@ nsDOMWindowUtils::IsInModalState(bool *retval)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::SetDesktopModeViewport(bool aDesktopMode)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
|
||||
NS_ENSURE_STATE(window);
|
||||
|
||||
static_cast<nsGlobalWindow*>(window.get())->SetDesktopModeViewport(aDesktopMode);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::GetOuterWindowID(uint64_t *aWindowID)
|
||||
{
|
||||
@@ -2928,6 +2937,67 @@ nsDOMWindowUtils::CheckAndClearPaintedState(nsIDOMElement* aElement, bool* aResu
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::IsPartOfOpaqueLayer(nsIDOMElement* aElement, bool* aResult)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
||||
|
||||
if (!aElement) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(aElement, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsIFrame* frame = content->GetPrimaryFrame();
|
||||
if (!frame) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
Layer* layer = FrameLayerBuilder::GetDebugSingleOldLayerForFrame(frame);
|
||||
if (!layer || !layer->AsPaintedLayer()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
*aResult = (layer->GetContentFlags() & Layer::CONTENT_OPAQUE);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::NumberOfAssignedPaintedLayers(nsIDOMElement** aElements,
|
||||
uint32_t aCount,
|
||||
uint32_t* aResult)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
||||
|
||||
if (!aElements) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsTHashtable<nsPtrHashKey<PaintedLayer>> layers;
|
||||
nsresult rv;
|
||||
for (uint32_t i = 0; i < aCount; i++) {
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(aElements[i], &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsIFrame* frame = content->GetPrimaryFrame();
|
||||
if (!frame) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
Layer* layer = FrameLayerBuilder::GetDebugSingleOldLayerForFrame(frame);
|
||||
if (!layer || !layer->AsPaintedLayer()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
layers.PutEntry(layer->AsPaintedLayer());
|
||||
}
|
||||
|
||||
*aResult = layers.Count();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::EnableDialogs()
|
||||
{
|
||||
|
||||
+16
-21
@@ -7311,14 +7311,14 @@ nsDocument::InitializeFrameLoader(nsFrameLoader* aLoader)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDocument::FinalizeFrameLoader(nsFrameLoader* aLoader)
|
||||
nsDocument::FinalizeFrameLoader(nsFrameLoader* aLoader, nsIRunnable* aFinalizer)
|
||||
{
|
||||
mInitializableFrameLoaders.RemoveElement(aLoader);
|
||||
if (mInDestructor) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mFinalizableFrameLoaders.AppendElement(aLoader);
|
||||
mFrameLoaderFinalizers.AppendElement(aFinalizer);
|
||||
if (!mFrameLoaderRunner) {
|
||||
mFrameLoaderRunner =
|
||||
NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
|
||||
@@ -7343,7 +7343,7 @@ nsDocument::MaybeInitializeFinalizeFrameLoaders()
|
||||
if (!nsContentUtils::IsSafeToRunScript()) {
|
||||
if (!mInDestructor && !mFrameLoaderRunner &&
|
||||
(mInitializableFrameLoaders.Length() ||
|
||||
mFinalizableFrameLoaders.Length())) {
|
||||
mFrameLoaderFinalizers.Length())) {
|
||||
mFrameLoaderRunner =
|
||||
NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
|
||||
nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
|
||||
@@ -7362,12 +7362,12 @@ nsDocument::MaybeInitializeFinalizeFrameLoaders()
|
||||
loader->ReallyStartLoading();
|
||||
}
|
||||
|
||||
uint32_t length = mFinalizableFrameLoaders.Length();
|
||||
uint32_t length = mFrameLoaderFinalizers.Length();
|
||||
if (length > 0) {
|
||||
nsTArray<nsRefPtr<nsFrameLoader> > loaders;
|
||||
mFinalizableFrameLoaders.SwapElements(loaders);
|
||||
nsTArray<nsCOMPtr<nsIRunnable> > finalizers;
|
||||
mFrameLoaderFinalizers.SwapElements(finalizers);
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
loaders[i]->Finalize();
|
||||
finalizers[i]->Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7384,20 +7384,6 @@ nsDocument::TryCancelFrameLoaderInitialization(nsIDocShell* aShell)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsDocument::FrameLoaderScheduledToBeFinalized(nsIDocShell* aShell)
|
||||
{
|
||||
if (aShell) {
|
||||
uint32_t length = mFinalizableFrameLoaders.Length();
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
if (mFinalizableFrameLoaders[i]->GetExistingDocShell() == aShell) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIDocument*
|
||||
nsDocument::RequestExternalResource(nsIURI* aURI,
|
||||
nsINode* aRequestingNode,
|
||||
@@ -7871,6 +7857,15 @@ nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
|
||||
|
||||
CSSToScreenScale defaultScale = layoutDeviceScale
|
||||
* LayoutDeviceToScreenScale(1.0);
|
||||
// Get requested Desktopmode
|
||||
nsPIDOMWindow* win = GetWindow();
|
||||
if (win && win->IsDesktopModeViewport())
|
||||
{
|
||||
return nsViewportInfo(aDisplaySize,
|
||||
defaultScale,
|
||||
/*allowZoom*/false,
|
||||
/*allowDoubleTapZoom*/ true);
|
||||
}
|
||||
|
||||
if (!Preferences::GetBool("dom.meta-viewport.enabled", false)) {
|
||||
return nsViewportInfo(aDisplaySize,
|
||||
|
||||
@@ -1034,9 +1034,8 @@ public:
|
||||
virtual void FlushSkinBindings() override;
|
||||
|
||||
virtual nsresult InitializeFrameLoader(nsFrameLoader* aLoader) override;
|
||||
virtual nsresult FinalizeFrameLoader(nsFrameLoader* aLoader) override;
|
||||
virtual nsresult FinalizeFrameLoader(nsFrameLoader* aLoader, nsIRunnable* aFinalizer) override;
|
||||
virtual void TryCancelFrameLoaderInitialization(nsIDocShell* aShell) override;
|
||||
virtual bool FrameLoaderScheduledToBeFinalized(nsIDocShell* aShell) override;
|
||||
virtual nsIDocument*
|
||||
RequestExternalResource(nsIURI* aURI,
|
||||
nsINode* aRequestingNode,
|
||||
@@ -1765,7 +1764,7 @@ private:
|
||||
nsString mLastStyleSheetSet;
|
||||
|
||||
nsTArray<nsRefPtr<nsFrameLoader> > mInitializableFrameLoaders;
|
||||
nsTArray<nsRefPtr<nsFrameLoader> > mFinalizableFrameLoaders;
|
||||
nsTArray<nsCOMPtr<nsIRunnable> > mFrameLoaderFinalizers;
|
||||
nsRefPtr<nsRunnableMethod<nsDocument> > mFrameLoaderRunner;
|
||||
|
||||
nsRevocableEventPtr<nsRunnableMethod<nsDocument, void, false> >
|
||||
|
||||
+154
-56
@@ -107,25 +107,6 @@ using namespace mozilla::layers;
|
||||
using namespace mozilla::layout;
|
||||
typedef FrameMetrics::ViewID ViewID;
|
||||
|
||||
class nsAsyncDocShellDestroyer : public nsRunnable
|
||||
{
|
||||
public:
|
||||
explicit nsAsyncDocShellDestroyer(nsIDocShell* aDocShell)
|
||||
: mDocShell(aDocShell)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell));
|
||||
if (base_win) {
|
||||
base_win->Destroy();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
nsRefPtr<nsIDocShell> mDocShell;
|
||||
};
|
||||
|
||||
// Bug 136580: Limit to the number of nested content frames that can have the
|
||||
// same URL. This is to stop content that is recursively loading
|
||||
// itself. Note that "#foo" on the end of URL doesn't affect
|
||||
@@ -181,7 +162,6 @@ nsFrameLoader::nsFrameLoader(Element* aOwner, bool aNetworkCreated)
|
||||
|
||||
nsFrameLoader::~nsFrameLoader()
|
||||
{
|
||||
mNeedsAsyncDestroy = true;
|
||||
if (mMessageManager) {
|
||||
mMessageManager->Disconnect();
|
||||
}
|
||||
@@ -523,16 +503,6 @@ nsFrameLoader::GetDocShell(nsIDocShell **aDocShell)
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
nsFrameLoader::Finalize()
|
||||
{
|
||||
nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell));
|
||||
if (base_win) {
|
||||
base_win->Destroy();
|
||||
}
|
||||
mDocShell = nullptr;
|
||||
}
|
||||
|
||||
static void
|
||||
FirePageHideEvent(nsIDocShellTreeItem* aItem,
|
||||
EventTarget* aChromeEventHandler)
|
||||
@@ -1358,29 +1328,59 @@ nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsFrameLoader::DestroyChild()
|
||||
{
|
||||
if (mRemoteBrowser) {
|
||||
mRemoteBrowser->SetOwnerElement(nullptr);
|
||||
mRemoteBrowser->Destroy();
|
||||
mRemoteBrowser = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFrameLoader::Destroy()
|
||||
{
|
||||
StartDestroy();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class nsFrameLoaderDestroyRunnable : public nsRunnable
|
||||
{
|
||||
enum DestroyPhase
|
||||
{
|
||||
// See the implementation of Run for an explanation of these phases.
|
||||
eDestroyDocShell,
|
||||
eWaitForUnloadMessage,
|
||||
eDestroyComplete
|
||||
};
|
||||
|
||||
nsRefPtr<nsFrameLoader> mFrameLoader;
|
||||
DestroyPhase mPhase;
|
||||
|
||||
public:
|
||||
explicit nsFrameLoaderDestroyRunnable(nsFrameLoader* aFrameLoader)
|
||||
: mFrameLoader(aFrameLoader), mPhase(eDestroyDocShell) {}
|
||||
|
||||
NS_IMETHODIMP Run() override;
|
||||
};
|
||||
|
||||
void
|
||||
nsFrameLoader::StartDestroy()
|
||||
{
|
||||
// nsFrameLoader::StartDestroy is called just before the frameloader is
|
||||
// detached from the <browser> element. Destruction continues in phases via
|
||||
// the nsFrameLoaderDestroyRunnable.
|
||||
|
||||
if (mDestroyCalled) {
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
mDestroyCalled = true;
|
||||
|
||||
// After this point, we return an error when trying to send a message using
|
||||
// the message manager on the frame.
|
||||
if (mMessageManager) {
|
||||
mMessageManager->Disconnect();
|
||||
mMessageManager->Close();
|
||||
}
|
||||
if (mChildMessageManager) {
|
||||
static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->Disconnect();
|
||||
|
||||
// Retain references to the <browser> element and the frameloader in case we
|
||||
// receive any messages from the message manager on the frame. These
|
||||
// references are dropped in DestroyComplete.
|
||||
if (mChildMessageManager || mRemoteBrowser) {
|
||||
mOwnerContentStrong = mOwnerContent;
|
||||
if (mRemoteBrowser) {
|
||||
mRemoteBrowser->CacheFrameLoader(this);
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc;
|
||||
@@ -1392,7 +1392,6 @@ nsFrameLoader::Destroy()
|
||||
|
||||
SetOwnerContent(nullptr);
|
||||
}
|
||||
DestroyChild();
|
||||
|
||||
// Seems like this is a dynamic frame removal.
|
||||
if (dynamicSubframeRemoval) {
|
||||
@@ -1412,7 +1411,7 @@ nsFrameLoader::Destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Let our window know that we are gone
|
||||
if (mDocShell) {
|
||||
nsCOMPtr<nsPIDOMWindow> win_private(mDocShell->GetWindow());
|
||||
@@ -1421,23 +1420,122 @@ nsFrameLoader::Destroy()
|
||||
}
|
||||
}
|
||||
|
||||
if ((mNeedsAsyncDestroy || !doc ||
|
||||
NS_FAILED(doc->FinalizeFrameLoader(this))) && mDocShell) {
|
||||
nsCOMPtr<nsIRunnable> event = new nsAsyncDocShellDestroyer(mDocShell);
|
||||
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
|
||||
NS_DispatchToCurrentThread(event);
|
||||
nsCOMPtr<nsIRunnable> destroyRunnable = new nsFrameLoaderDestroyRunnable(this);
|
||||
if (mNeedsAsyncDestroy || !doc ||
|
||||
NS_FAILED(doc->FinalizeFrameLoader(this, destroyRunnable))) {
|
||||
NS_DispatchToCurrentThread(destroyRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
// Let go of our docshell now that the async destroyer holds on to
|
||||
// the docshell.
|
||||
nsresult
|
||||
nsFrameLoaderDestroyRunnable::Run()
|
||||
{
|
||||
switch (mPhase) {
|
||||
case eDestroyDocShell:
|
||||
mFrameLoader->DestroyDocShell();
|
||||
|
||||
mDocShell = nullptr;
|
||||
// In the out-of-process case, TabParent will eventually call
|
||||
// DestroyComplete once it receives a __delete__ message from the child. In
|
||||
// the in-process case, we dispatch a series of runnables to ensure that
|
||||
// DestroyComplete gets called at the right time. The frame loader is kept
|
||||
// alive by mFrameLoader during this time.
|
||||
if (mFrameLoader->mChildMessageManager) {
|
||||
// When the docshell is destroyed, NotifyWindowIDDestroyed is called to
|
||||
// asynchronously notify {outer,inner}-window-destroyed via a runnable. We
|
||||
// don't want DestroyComplete to run until after those runnables have
|
||||
// run. Since we're enqueueing ourselves after the window-destroyed
|
||||
// runnables are enqueued, we're guaranteed to run after.
|
||||
mPhase = eWaitForUnloadMessage;
|
||||
NS_DispatchToCurrentThread(this);
|
||||
}
|
||||
break;
|
||||
|
||||
case eWaitForUnloadMessage:
|
||||
// The *-window-destroyed observers have finished running at this
|
||||
// point. However, it's possible that a *-window-destroyed observer might
|
||||
// have sent a message using the message manager. These messages might not
|
||||
// have been processed yet. So we enqueue ourselves again to ensure that
|
||||
// DestroyComplete runs after all messages sent by *-window-destroyed
|
||||
// observers have been processed.
|
||||
mPhase = eDestroyComplete;
|
||||
NS_DispatchToCurrentThread(this);
|
||||
break;
|
||||
|
||||
case eDestroyComplete:
|
||||
// Now that all messages sent by unload listeners and window destroyed
|
||||
// observers have been processed, we disconnect the message manager and
|
||||
// finish destruction.
|
||||
mFrameLoader->DestroyComplete();
|
||||
break;
|
||||
}
|
||||
|
||||
// NOTE: 'this' may very well be gone by now.
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsFrameLoader::DestroyDocShell()
|
||||
{
|
||||
// This code runs after the frameloader has been detached from the <browser>
|
||||
// element. We postpone this work because we may not be allowed to run
|
||||
// script at that time.
|
||||
|
||||
// Ask the TabChild to fire the frame script "unload" event, destroy its
|
||||
// docshell, and finally destroy the PBrowser actor. This eventually leads to
|
||||
// nsFrameLoader::DestroyComplete being called.
|
||||
if (mRemoteBrowser) {
|
||||
mRemoteBrowser->Destroy();
|
||||
}
|
||||
|
||||
// Fire the "unload" event if we're in-process.
|
||||
if (mChildMessageManager) {
|
||||
static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->FireUnloadEvent();
|
||||
}
|
||||
|
||||
// Destroy the docshell.
|
||||
nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell));
|
||||
if (base_win) {
|
||||
base_win->Destroy();
|
||||
}
|
||||
mDocShell = nullptr;
|
||||
|
||||
if (mChildMessageManager) {
|
||||
// Stop handling events in the in-process frame script.
|
||||
static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->DisconnectEventListeners();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsFrameLoader::DestroyComplete()
|
||||
{
|
||||
// We get here, as part of StartDestroy, after the docshell has been destroyed
|
||||
// and all message manager messages sent during docshell destruction have been
|
||||
// dispatched. We also get here if the child process crashes. In the latter
|
||||
// case, StartDestroy might not have been called.
|
||||
|
||||
// Drop the strong references created in StartDestroy.
|
||||
if (mChildMessageManager || mRemoteBrowser) {
|
||||
mOwnerContentStrong = nullptr;
|
||||
if (mRemoteBrowser) {
|
||||
mRemoteBrowser->CacheFrameLoader(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Call TabParent::Destroy if we haven't already (in case of a crash).
|
||||
if (mRemoteBrowser) {
|
||||
mRemoteBrowser->SetOwnerElement(nullptr);
|
||||
mRemoteBrowser->Destroy();
|
||||
mRemoteBrowser = nullptr;
|
||||
}
|
||||
|
||||
if (mMessageManager) {
|
||||
mMessageManager->Disconnect();
|
||||
}
|
||||
|
||||
if (mChildMessageManager) {
|
||||
static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFrameLoader::GetDepthTooGreat(bool* aDepthTooGreat)
|
||||
{
|
||||
|
||||
@@ -79,7 +79,9 @@ public:
|
||||
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
|
||||
nsresult CheckForRecursiveLoad(nsIURI* aURI);
|
||||
nsresult ReallyStartLoading();
|
||||
void Finalize();
|
||||
void StartDestroy();
|
||||
void DestroyDocShell();
|
||||
void DestroyComplete();
|
||||
nsIDocShell* GetExistingDocShell() { return mDocShell; }
|
||||
mozilla::dom::EventTarget* GetTabChildGlobalAsEventTarget();
|
||||
nsresult CreateStaticClone(nsIFrameLoader* aDest);
|
||||
@@ -320,6 +322,11 @@ private:
|
||||
nsCOMPtr<nsIURI> mURIToLoad;
|
||||
mozilla::dom::Element* mOwnerContent; // WEAK
|
||||
|
||||
// After the frameloader has been removed from the DOM but before all of the
|
||||
// messages from the frame have been received, we keep a strong reference to
|
||||
// our <browser> element.
|
||||
nsRefPtr<mozilla::dom::Element> mOwnerContentStrong;
|
||||
|
||||
// Note: this variable must be modified only by ResetPermissionManagerStatus()
|
||||
uint32_t mAppIdSentToPermissionManager;
|
||||
|
||||
|
||||
@@ -301,7 +301,8 @@ SameProcessCpowHolder::ToObject(JSContext* aCx,
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFrameMessageManager::AddMessageListener(const nsAString& aMessage,
|
||||
nsIMessageListener* aListener)
|
||||
nsIMessageListener* aListener,
|
||||
bool aListenWhenClosed)
|
||||
{
|
||||
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
|
||||
mListeners.Get(aMessage);
|
||||
@@ -320,6 +321,7 @@ nsFrameMessageManager::AddMessageListener(const nsAString& aMessage,
|
||||
nsMessageListenerInfo* entry = listeners->AppendElement();
|
||||
NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
|
||||
entry->mStrongListener = aListener;
|
||||
entry->mListenWhenClosed = aListenWhenClosed;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -407,6 +409,7 @@ nsFrameMessageManager::AddWeakMessageListener(const nsAString& aMessage,
|
||||
|
||||
nsMessageListenerInfo* entry = listeners->AppendElement();
|
||||
entry->mWeakListener = weak;
|
||||
entry->mListenWhenClosed = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -983,6 +986,20 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
|
||||
mozilla::jsipc::CpowHolder* aCpows,
|
||||
nsIPrincipal* aPrincipal,
|
||||
InfallibleTArray<nsString>* aJSONRetVal)
|
||||
{
|
||||
return ReceiveMessage(aTarget, mClosed, aMessage, aIsSync,
|
||||
aCloneData, aCpows, aPrincipal, aJSONRetVal);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
|
||||
bool aTargetClosed,
|
||||
const nsAString& aMessage,
|
||||
bool aIsSync,
|
||||
const StructuredCloneData* aCloneData,
|
||||
mozilla::jsipc::CpowHolder* aCpows,
|
||||
nsIPrincipal* aPrincipal,
|
||||
InfallibleTArray<nsString>* aJSONRetVal)
|
||||
{
|
||||
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
|
||||
mListeners.Get(aMessage);
|
||||
@@ -1004,6 +1021,10 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
|
||||
}
|
||||
}
|
||||
|
||||
if (!listener.mListenWhenClosed && aTargetClosed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS;
|
||||
if (weakListener) {
|
||||
wrappedJS = do_QueryInterface(weakListener);
|
||||
@@ -1154,7 +1175,7 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
|
||||
}
|
||||
}
|
||||
nsRefPtr<nsFrameMessageManager> kungfuDeathGrip = mParentManager;
|
||||
return mParentManager ? mParentManager->ReceiveMessage(aTarget, aMessage,
|
||||
return mParentManager ? mParentManager->ReceiveMessage(aTarget, aTargetClosed, aMessage,
|
||||
aIsSync, aCloneData,
|
||||
aCpows, aPrincipal,
|
||||
aJSONRetVal) : NS_OK;
|
||||
@@ -1234,9 +1255,27 @@ nsFrameMessageManager::RemoveFromParent()
|
||||
mOwnedCallback = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
nsFrameMessageManager::Close()
|
||||
{
|
||||
if (!mClosed) {
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->NotifyObservers(NS_ISUPPORTS_CAST(nsIContentFrameMessageManager*, this),
|
||||
"message-manager-close", nullptr);
|
||||
}
|
||||
}
|
||||
mClosed = true;
|
||||
mCallback = nullptr;
|
||||
mOwnedCallback = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
nsFrameMessageManager::Disconnect(bool aRemoveFromParent)
|
||||
{
|
||||
// Notify message-manager-close if we haven't already.
|
||||
Close();
|
||||
|
||||
if (!mDisconnected) {
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
@@ -1249,8 +1288,6 @@ nsFrameMessageManager::Disconnect(bool aRemoveFromParent)
|
||||
}
|
||||
mDisconnected = true;
|
||||
mParentManager = nullptr;
|
||||
mCallback = nullptr;
|
||||
mOwnedCallback = nullptr;
|
||||
if (!mHandlingMessage) {
|
||||
mListeners.Clear();
|
||||
}
|
||||
|
||||
@@ -133,6 +133,7 @@ struct nsMessageListenerInfo
|
||||
// Exactly one of mStrongListener and mWeakListener must be non-null.
|
||||
nsCOMPtr<nsIMessageListener> mStrongListener;
|
||||
nsWeakPtr mWeakListener;
|
||||
bool mListenWhenClosed;
|
||||
};
|
||||
|
||||
|
||||
@@ -169,6 +170,7 @@ public:
|
||||
mIsBroadcaster(!!(aFlags & mozilla::dom::ipc::MM_BROADCASTER)),
|
||||
mOwnsCallback(!!(aFlags & mozilla::dom::ipc::MM_OWNSCALLBACK)),
|
||||
mHandlingMessage(false),
|
||||
mClosed(false),
|
||||
mDisconnected(false),
|
||||
mCallback(aCallback),
|
||||
mParentManager(aParentManager)
|
||||
@@ -238,6 +240,7 @@ public:
|
||||
mChildManagers.RemoveObject(aManager);
|
||||
}
|
||||
void Disconnect(bool aRemoveFromParent = true);
|
||||
void Close();
|
||||
|
||||
void InitWithCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback);
|
||||
void SetCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback);
|
||||
@@ -290,6 +293,11 @@ private:
|
||||
JS::MutableHandle<JS::Value> aRetval,
|
||||
bool aIsSync);
|
||||
|
||||
nsresult ReceiveMessage(nsISupports* aTarget, bool aTargetClosed, const nsAString& aMessage,
|
||||
bool aIsSync, const StructuredCloneData* aCloneData,
|
||||
mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal,
|
||||
InfallibleTArray<nsString>* aJSONRetVal);
|
||||
|
||||
NS_IMETHOD LoadScript(const nsAString& aURL,
|
||||
bool aAllowDelayedLoad,
|
||||
bool aRunInGlobalScope);
|
||||
@@ -309,6 +317,7 @@ protected:
|
||||
bool mIsBroadcaster; // true if the message manager is a broadcaster
|
||||
bool mOwnsCallback;
|
||||
bool mHandlingMessage;
|
||||
bool mClosed; // true if we can no longer send messages
|
||||
bool mDisconnected;
|
||||
mozilla::dom::ipc::MessageManagerCallback* mCallback;
|
||||
nsAutoPtr<mozilla::dom::ipc::MessageManagerCallback> mOwnedCallback;
|
||||
|
||||
@@ -582,7 +582,8 @@ nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWindow *aOuterWindow)
|
||||
mIsModalContentWindow(false),
|
||||
mIsActive(false), mIsBackground(false),
|
||||
mAudioMuted(false), mAudioVolume(1.0),
|
||||
mInnerWindow(nullptr), mOuterWindow(aOuterWindow),
|
||||
mDesktopModeViewport(false), mInnerWindow(nullptr),
|
||||
mOuterWindow(aOuterWindow),
|
||||
// Make sure no actual window ends up with mWindowID == 0
|
||||
mWindowID(NextWindowID()), mHasNotifiedGlobalCreated(false),
|
||||
mMarkedCCGeneration(0)
|
||||
|
||||
@@ -65,6 +65,7 @@ class nsIObserver;
|
||||
class nsIPresShell;
|
||||
class nsIPrincipal;
|
||||
class nsIRequest;
|
||||
class nsIRunnable;
|
||||
class nsIStreamListener;
|
||||
class nsIStructuredCloneContainer;
|
||||
class nsIStyleRule;
|
||||
@@ -1707,11 +1708,9 @@ public:
|
||||
virtual nsresult InitializeFrameLoader(nsFrameLoader* aLoader) = 0;
|
||||
// In case of failure, the caller must handle the error, for example by
|
||||
// finalizing frame loader asynchronously.
|
||||
virtual nsresult FinalizeFrameLoader(nsFrameLoader* aLoader) = 0;
|
||||
virtual nsresult FinalizeFrameLoader(nsFrameLoader* aLoader, nsIRunnable* aFinalizer) = 0;
|
||||
// Removes the frame loader of aShell from the initialization list.
|
||||
virtual void TryCancelFrameLoaderInitialization(nsIDocShell* aShell) = 0;
|
||||
// Returns true if the frame loader of aShell is in the finalization list.
|
||||
virtual bool FrameLoaderScheduledToBeFinalized(nsIDocShell* aShell) = 0;
|
||||
|
||||
/**
|
||||
* Check whether this document is a root document that is not an
|
||||
|
||||
@@ -201,7 +201,7 @@ interface nsIMessageListener : nsISupports
|
||||
void receiveMessage();
|
||||
};
|
||||
|
||||
[scriptable, builtinclass, uuid(aae827bd-acf1-45fe-a556-ea545d4c0804)]
|
||||
[scriptable, builtinclass, uuid(b949bfec-bb7d-47bc-b387-ac6a9b655072)]
|
||||
interface nsIMessageListenerManager : nsISupports
|
||||
{
|
||||
/**
|
||||
@@ -213,9 +213,16 @@ interface nsIMessageListenerManager : nsISupports
|
||||
*
|
||||
* If the same listener registers twice for the same message, the
|
||||
* second registration is ignored.
|
||||
*
|
||||
* Pass true for listenWhenClosed if you want to receive messages
|
||||
* during the short period after a frame has been removed from the
|
||||
* DOM and before its frame script has finished unloading. This
|
||||
* parameter only has an effect for frame message managers in
|
||||
* the main process. Default is false.
|
||||
*/
|
||||
void addMessageListener(in AString messageName,
|
||||
in nsIMessageListener listener);
|
||||
in nsIMessageListener listener,
|
||||
[optional] in boolean listenWhenClosed);
|
||||
|
||||
/**
|
||||
* Undo an |addMessageListener| call -- that is, calling this causes us to no
|
||||
@@ -252,7 +259,7 @@ interface nsIMessageListenerManager : nsISupports
|
||||
* messages that are only delivered to its one parent-process message
|
||||
* manager.
|
||||
*/
|
||||
[scriptable, builtinclass, uuid(d6b0d851-43e6-426d-9f13-054bc0198175)]
|
||||
[scriptable, builtinclass, uuid(bb5d79e4-e73c-45e7-9651-4d718f4b994c)]
|
||||
interface nsIMessageSender : nsIMessageListenerManager
|
||||
{
|
||||
/**
|
||||
@@ -284,7 +291,7 @@ interface nsIMessageSender : nsIMessageListenerManager
|
||||
* manager will broadcast the message to all frame message managers
|
||||
* within its window.
|
||||
*/
|
||||
[scriptable, builtinclass, uuid(d36346b9-5d3b-497d-9c28-ffbc3e4f6d0d)]
|
||||
[scriptable, builtinclass, uuid(4d7d62ad-4725-4f39-86cf-8fb22bf9c1d8)]
|
||||
interface nsIMessageBroadcaster : nsIMessageListenerManager
|
||||
{
|
||||
/**
|
||||
@@ -311,7 +318,7 @@ interface nsIMessageBroadcaster : nsIMessageListenerManager
|
||||
nsIMessageListenerManager getChildAt(in unsigned long aIndex);
|
||||
};
|
||||
|
||||
[scriptable, builtinclass, uuid(7fda0941-9dcc-448b-bd39-16373c5b4003)]
|
||||
[scriptable, builtinclass, uuid(0e602c9e-1977-422a-a8e4-fe0d4a4f78d0)]
|
||||
interface nsISyncMessageSender : nsIMessageSender
|
||||
{
|
||||
/**
|
||||
@@ -341,7 +348,7 @@ interface nsISyncMessageSender : nsIMessageSender
|
||||
[optional] in nsIPrincipal principal);
|
||||
};
|
||||
|
||||
[scriptable, builtinclass, uuid(e04a7ade-c61a-46ec-9f13-efeabedd9d3d)]
|
||||
[scriptable, builtinclass, uuid(13f3555f-769e-44ea-b607-5239230c3162)]
|
||||
interface nsIMessageManagerGlobal : nsISyncMessageSender
|
||||
{
|
||||
/**
|
||||
@@ -376,13 +383,13 @@ interface nsIContentFrameMessageManager : nsIMessageManagerGlobal
|
||||
readonly attribute nsIDocShell docShell;
|
||||
};
|
||||
|
||||
[uuid(a2325927-9c0c-437d-9215-749c79235031)]
|
||||
[uuid(a9e07e89-7125-48e3-bf73-2cbae7fc5b1c)]
|
||||
interface nsIInProcessContentFrameMessageManager : nsIContentFrameMessageManager
|
||||
{
|
||||
[notxpcom] nsIContent getOwnerContent();
|
||||
};
|
||||
|
||||
[scriptable, builtinclass, uuid(9ca95410-b253-11e4-ab27-0800200c9a66)]
|
||||
[scriptable, builtinclass, uuid(d0c799a2-d5ff-4a75-acbb-b8c8347944a6)]
|
||||
interface nsIContentProcessMessageManager : nsIMessageManagerGlobal
|
||||
{
|
||||
};
|
||||
|
||||
@@ -101,6 +101,7 @@ nsInProcessTabChildGlobal::nsInProcessTabChildGlobal(nsIDocShell* aShell,
|
||||
nsIContent* aOwner,
|
||||
nsFrameMessageManager* aChrome)
|
||||
: mDocShell(aShell), mInitialized(false), mLoadingScript(false),
|
||||
mPreventEventsEscaping(false),
|
||||
mOwner(aOwner), mChromeMessageManager(aChrome)
|
||||
{
|
||||
SetIsNotDOMBinding();
|
||||
@@ -206,25 +207,24 @@ nsInProcessTabChildGlobal::GetDocShell(nsIDocShell** aDocShell)
|
||||
}
|
||||
|
||||
void
|
||||
nsInProcessTabChildGlobal::Disconnect()
|
||||
nsInProcessTabChildGlobal::FireUnloadEvent()
|
||||
{
|
||||
// Let the frame scripts know the child is being closed. We do any other
|
||||
// cleanup after the event has been fired. See DelayedDisconnect
|
||||
nsContentUtils::AddScriptRunner(
|
||||
NS_NewRunnableMethod(this, &nsInProcessTabChildGlobal::DelayedDisconnect)
|
||||
);
|
||||
// We're called from nsDocument::MaybeInitializeFinalizeFrameLoaders, so it
|
||||
// should be safe to run script.
|
||||
MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
|
||||
|
||||
// Don't let the unload event propagate to chrome event handlers.
|
||||
mPreventEventsEscaping = true;
|
||||
DOMEventTargetHelper::DispatchTrustedEvent(NS_LITERAL_STRING("unload"));
|
||||
|
||||
// Allow events fired during docshell destruction (pagehide, unload) to
|
||||
// propagate to the <browser> element since chrome code depends on this.
|
||||
mPreventEventsEscaping = false;
|
||||
}
|
||||
|
||||
void
|
||||
nsInProcessTabChildGlobal::DelayedDisconnect()
|
||||
nsInProcessTabChildGlobal::DisconnectEventListeners()
|
||||
{
|
||||
// Don't let the event escape
|
||||
mOwner = nullptr;
|
||||
|
||||
// Fire the "unload" event
|
||||
DOMEventTargetHelper::DispatchTrustedEvent(NS_LITERAL_STRING("unload"));
|
||||
|
||||
// Continue with the Disconnect cleanup
|
||||
if (mDocShell) {
|
||||
nsCOMPtr<nsPIDOMWindow> win = mDocShell->GetWindow();
|
||||
if (win) {
|
||||
@@ -232,15 +232,22 @@ nsInProcessTabChildGlobal::DelayedDisconnect()
|
||||
win->SetChromeEventHandler(win->GetChromeEventHandler());
|
||||
}
|
||||
}
|
||||
if (mListenerManager) {
|
||||
mListenerManager->Disconnect();
|
||||
}
|
||||
|
||||
mDocShell = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
nsInProcessTabChildGlobal::Disconnect()
|
||||
{
|
||||
mChromeMessageManager = nullptr;
|
||||
mOwner = nullptr;
|
||||
if (mMessageManager) {
|
||||
static_cast<nsFrameMessageManager*>(mMessageManager.get())->Disconnect();
|
||||
mMessageManager = nullptr;
|
||||
}
|
||||
if (mListenerManager) {
|
||||
mListenerManager->Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(nsIContent *)
|
||||
@@ -254,18 +261,6 @@ nsInProcessTabChildGlobal::PreHandleEvent(EventChainPreVisitor& aVisitor)
|
||||
{
|
||||
aVisitor.mCanHandle = true;
|
||||
|
||||
if (mIsBrowserOrAppFrame &&
|
||||
(!mOwner || !nsContentUtils::IsInChromeDocshell(mOwner->OwnerDoc()))) {
|
||||
if (mOwner) {
|
||||
nsPIDOMWindow* innerWindow = mOwner->OwnerDoc()->GetInnerWindow();
|
||||
if (innerWindow) {
|
||||
aVisitor.mParentTarget = innerWindow->GetParentTarget();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
aVisitor.mParentTarget = mOwner;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (mOwner) {
|
||||
nsCOMPtr<nsIFrameLoaderOwner> owner = do_QueryInterface(mOwner);
|
||||
@@ -279,6 +274,23 @@ nsInProcessTabChildGlobal::PreHandleEvent(EventChainPreVisitor& aVisitor)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mPreventEventsEscaping) {
|
||||
aVisitor.mParentTarget = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mIsBrowserOrAppFrame &&
|
||||
(!mOwner || !nsContentUtils::IsInChromeDocshell(mOwner->OwnerDoc()))) {
|
||||
if (mOwner) {
|
||||
nsPIDOMWindow* innerWindow = mOwner->OwnerDoc()->GetInnerWindow();
|
||||
if (innerWindow) {
|
||||
aVisitor.mParentTarget = innerWindow->GetParentTarget();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
aVisitor.mParentTarget = mOwner;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- */
|
||||
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8; -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@@ -118,6 +118,8 @@ public:
|
||||
virtual JSContext* GetJSContextForEventHandlers() override { return nsContentUtils::GetSafeJSContext(); }
|
||||
virtual nsIPrincipal* GetPrincipal() override { return mPrincipal; }
|
||||
void LoadFrameScript(const nsAString& aURL, bool aRunInGlobalScope);
|
||||
void FireUnloadEvent();
|
||||
void DisconnectEventListeners();
|
||||
void Disconnect();
|
||||
void SendMessageToParent(const nsString& aMessage, bool aSync,
|
||||
const nsString& aJSON,
|
||||
@@ -137,8 +139,6 @@ public:
|
||||
mChromeMessageManager = aParent;
|
||||
}
|
||||
|
||||
void DelayedDisconnect();
|
||||
|
||||
virtual JSObject* GetGlobalJSObject() override {
|
||||
if (!mGlobal) {
|
||||
return nullptr;
|
||||
@@ -164,6 +164,7 @@ protected:
|
||||
// <iframe mozapp>? This affects where events get sent, so it affects
|
||||
// PreHandleEvent.
|
||||
bool mIsBrowserOrAppFrame;
|
||||
bool mPreventEventsEscaping;
|
||||
public:
|
||||
nsIContent* mOwner;
|
||||
nsFrameMessageManager* mChromeMessageManager;
|
||||
|
||||
@@ -114,6 +114,17 @@ public:
|
||||
}
|
||||
|
||||
// Outer windows only.
|
||||
void SetDesktopModeViewport(bool aDesktopModeViewport)
|
||||
{
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
mDesktopModeViewport = aDesktopModeViewport;
|
||||
}
|
||||
bool IsDesktopModeViewport() const
|
||||
{
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
return mDesktopModeViewport;
|
||||
}
|
||||
|
||||
virtual void SetIsBackground(bool aIsBackground)
|
||||
{
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
@@ -814,6 +825,9 @@ protected:
|
||||
bool mAudioMuted;
|
||||
float mAudioVolume;
|
||||
|
||||
// current desktop mode flag.
|
||||
bool mDesktopModeViewport;
|
||||
|
||||
// And these are the references between inner and outer windows.
|
||||
nsPIDOMWindow* MOZ_NON_OWNING_REF mInnerWindow;
|
||||
nsCOMPtr<nsPIDOMWindow> mOuterWindow;
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
file_messagemanager_unload.html
|
||||
|
||||
[browser_bug593387.js]
|
||||
skip-if = e10s # Bug ?????? - test directly touches content (contentWindow.iframe.addEventListener)
|
||||
[browser_bug902350.js]
|
||||
skip-if = e10s # Bug ?????? - test e10s utils don't support load events from iframe etc, which this test relies on.
|
||||
[browser_messagemanager_loadprocessscript.js]
|
||||
[browser_pagehide_on_tab_close.js]
|
||||
skip-if = e10s # this tests non-e10s behavior. it's not expected to work in e10s.
|
||||
[browser_messagemanager_unload.js]
|
||||
[browser_state_notifications.js]
|
||||
# skip-if = e10s # Bug ?????? - content-document-* notifications come while document's URI is still about:blank, but test expects real URL.
|
||||
skip-if = true # Intermittent failures - bug 987493. Restore the skip-if above once fixed
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
function frameScript()
|
||||
{
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function eventHandler(e) {
|
||||
if (!docShell) {
|
||||
sendAsyncMessage("Test:Fail", "docShell is null");
|
||||
}
|
||||
|
||||
sendAsyncMessage("Test:Event", [e.type, e.target === content.document, e.eventPhase]);
|
||||
}
|
||||
|
||||
let outerID = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIDOMWindowUtils).outerWindowID;
|
||||
function onOuterWindowDestroyed(subject, topic, data) {
|
||||
if (docShell) {
|
||||
sendAsyncMessage("Test:Fail", "docShell is non-null");
|
||||
}
|
||||
|
||||
let id = subject.QueryInterface(Components.interfaces.nsISupportsPRUint64).data;
|
||||
sendAsyncMessage("Test:Event", ["outer-window-destroyed", id == outerID]);
|
||||
if (id == outerID) {
|
||||
Services.obs.removeObserver(onOuterWindowDestroyed, "outer-window-destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
let url = "https://example.com/browser/dom/base/test/file_messagemanager_unload.html";
|
||||
|
||||
content.location = url;
|
||||
addEventListener("load", (e) => {
|
||||
if (e.target.location != url) {
|
||||
return;
|
||||
}
|
||||
|
||||
addEventListener("unload", eventHandler, false);
|
||||
addEventListener("unload", eventHandler, true);
|
||||
addEventListener("pagehide", eventHandler, false);
|
||||
addEventListener("pagehide", eventHandler, true);
|
||||
Services.obs.addObserver(onOuterWindowDestroyed, "outer-window-destroyed", false);
|
||||
|
||||
sendAsyncMessage("Test:Ready");
|
||||
}, true);
|
||||
}
|
||||
|
||||
const EXPECTED = [
|
||||
// Unload events on the TabChildGlobal. These come first so that the
|
||||
// docshell is available.
|
||||
["unload", false, 2],
|
||||
["unload", false, 2],
|
||||
|
||||
// pagehide and unload events for the top-level page.
|
||||
["pagehide", true, 1],
|
||||
["pagehide", true, 3],
|
||||
["unload", true, 1],
|
||||
|
||||
// pagehide and unload events for the iframe.
|
||||
["pagehide", false, 1],
|
||||
["pagehide", false, 3],
|
||||
["unload", false, 1],
|
||||
|
||||
// outer-window-destroyed for both pages.
|
||||
["outer-window-destroyed", false],
|
||||
["outer-window-destroyed", true],
|
||||
];
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
var newTab = gBrowser.addTab("about:blank");
|
||||
gBrowser.selectedTab = newTab;
|
||||
|
||||
let browser = newTab.linkedBrowser;
|
||||
|
||||
browser.messageManager.loadFrameScript("data:,(" + frameScript.toString() + ")()", false);
|
||||
|
||||
browser.messageManager.addMessageListener("Test:Fail", (msg) => {
|
||||
ok(false, msg.data);
|
||||
}, true);
|
||||
|
||||
let index = 0;
|
||||
browser.messageManager.addMessageListener("Test:Event", (msg) => {
|
||||
ok(msg.target === browser, "<browser> is correct");
|
||||
|
||||
info(JSON.stringify(msg.data));
|
||||
|
||||
is(JSON.stringify(msg.data), JSON.stringify(EXPECTED[index]), "results match");
|
||||
index++;
|
||||
|
||||
if (index == EXPECTED.length) {
|
||||
finish();
|
||||
}
|
||||
}, true);
|
||||
|
||||
browser.messageManager.addMessageListener("Test:Ready", () => {
|
||||
info("Got ready message");
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
}
|
||||
function frameScript()
|
||||
{
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function eventHandler(e) {
|
||||
if (!docShell) {
|
||||
sendAsyncMessage("Test:Fail", "docShell is null");
|
||||
}
|
||||
|
||||
sendAsyncMessage("Test:Event", [e.type, e.target === content.document, e.eventPhase]);
|
||||
}
|
||||
|
||||
let outerID = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIDOMWindowUtils).outerWindowID;
|
||||
function onOuterWindowDestroyed(subject, topic, data) {
|
||||
if (docShell) {
|
||||
sendAsyncMessage("Test:Fail", "docShell is non-null");
|
||||
}
|
||||
|
||||
let id = subject.QueryInterface(Components.interfaces.nsISupportsPRUint64).data;
|
||||
sendAsyncMessage("Test:Event", ["outer-window-destroyed", id == outerID]);
|
||||
if (id == outerID) {
|
||||
Services.obs.removeObserver(onOuterWindowDestroyed, "outer-window-destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
let url = "https://example.com/browser/dom/base/test/file_messagemanager_unload.html";
|
||||
|
||||
content.location = url;
|
||||
addEventListener("load", (e) => {
|
||||
if (e.target.location != url) {
|
||||
return;
|
||||
}
|
||||
|
||||
addEventListener("unload", eventHandler, false);
|
||||
addEventListener("unload", eventHandler, true);
|
||||
addEventListener("pagehide", eventHandler, false);
|
||||
addEventListener("pagehide", eventHandler, true);
|
||||
Services.obs.addObserver(onOuterWindowDestroyed, "outer-window-destroyed", false);
|
||||
|
||||
sendAsyncMessage("Test:Ready");
|
||||
}, true);
|
||||
}
|
||||
|
||||
const EXPECTED = [
|
||||
// Unload events on the TabChildGlobal. These come first so that the
|
||||
// docshell is available.
|
||||
["unload", false, 2],
|
||||
["unload", false, 2],
|
||||
|
||||
// pagehide and unload events for the top-level page.
|
||||
["pagehide", true, 1],
|
||||
["pagehide", true, 3],
|
||||
["unload", true, 1],
|
||||
|
||||
// pagehide and unload events for the iframe.
|
||||
["pagehide", false, 1],
|
||||
["pagehide", false, 3],
|
||||
["unload", false, 1],
|
||||
|
||||
// outer-window-destroyed for both pages.
|
||||
["outer-window-destroyed", false],
|
||||
["outer-window-destroyed", true],
|
||||
];
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
var newTab = gBrowser.addTab("about:blank");
|
||||
gBrowser.selectedTab = newTab;
|
||||
|
||||
let browser = newTab.linkedBrowser;
|
||||
|
||||
browser.messageManager.loadFrameScript("data:,(" + frameScript.toString() + ")()", false);
|
||||
|
||||
browser.messageManager.addMessageListener("Test:Fail", (msg) => {
|
||||
ok(false, msg.data);
|
||||
}, true);
|
||||
|
||||
let index = 0;
|
||||
browser.messageManager.addMessageListener("Test:Event", (msg) => {
|
||||
ok(msg.target === browser, "<browser> is correct");
|
||||
|
||||
info(JSON.stringify(msg.data));
|
||||
|
||||
is(JSON.stringify(msg.data), JSON.stringify(EXPECTED[index]), "results match");
|
||||
index++;
|
||||
|
||||
if (index == EXPECTED.length) {
|
||||
finish();
|
||||
}
|
||||
}, true);
|
||||
|
||||
browser.messageManager.addMessageListener("Test:Ready", () => {
|
||||
info("Got ready message");
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
var tab = gBrowser.addTab();
|
||||
gBrowser.selectedTab = tab;
|
||||
|
||||
tab.linkedBrowser.addEventListener("load", function onload() {
|
||||
tab.linkedBrowser.removeEventListener("load", onload);
|
||||
|
||||
tab.linkedBrowser.addEventListener("pagehide", function() {
|
||||
ok(true, "got page hide event");
|
||||
finish();
|
||||
});
|
||||
|
||||
executeSoon(() => { gBrowser.removeTab(tab); });
|
||||
}, true);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<iframe id="frame" src="about:robots"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<iframe id="frame" src="about:robots"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
@@ -773,6 +773,7 @@ skip-if = true # bug 1107443 - code for newly-added test was disabled
|
||||
skip-if = buildapp == 'mulet' || buildapp == 'b2g'
|
||||
[test_bug1118689.html]
|
||||
skip-if = buildapp == 'mulet' || buildapp == 'b2g'
|
||||
[test_integer_attr_with_leading_zero.html]
|
||||
[test_getAttribute_after_createAttribute.html]
|
||||
[test_script_loader_crossorigin_data_url.html]
|
||||
[test_unknown_url_origin.html]
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<title>Test for parsing of integer attributes with leading zero</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
var td = document.createElement("td");
|
||||
var li = document.createElement("li");
|
||||
// Array of tests: "values" are the values to set, "tdreflection" is the
|
||||
// corresponding td.rowspan value, "lireflection" is the corresponding li.value
|
||||
// value.
|
||||
var testData = [
|
||||
{
|
||||
values: [
|
||||
"2",
|
||||
"02",
|
||||
"002",
|
||||
"00002",
|
||||
],
|
||||
tdreflection: 2,
|
||||
lireflection: 2,
|
||||
},
|
||||
{
|
||||
values: [
|
||||
"-2",
|
||||
"-02",
|
||||
"-002",
|
||||
"-00002",
|
||||
],
|
||||
tdreflection: 1,
|
||||
lireflection: -2,
|
||||
},
|
||||
{
|
||||
values: [
|
||||
"-0",
|
||||
"-00",
|
||||
"0",
|
||||
"00",
|
||||
],
|
||||
tdreflection: 0,
|
||||
lireflection: 0,
|
||||
},
|
||||
];
|
||||
|
||||
for (var data of testData) {
|
||||
for (var value of data.values) {
|
||||
td.setAttribute("rowspan", value);
|
||||
li.setAttribute("value", value);
|
||||
test(function() {
|
||||
assert_equals(td.rowSpan, data.tdreflection);
|
||||
}, `<td> reflection for ${value}`);
|
||||
test(function() {
|
||||
assert_equals(td.getAttribute("rowspan"), value);
|
||||
}, `<td> setAttribute roundtripping for ${value}`);
|
||||
test(function() {
|
||||
assert_equals(li.value, data.lireflection);
|
||||
}, `<li> reflection for ${value}`);
|
||||
test(function() {
|
||||
assert_equals(li.getAttribute("value"), value);
|
||||
}, `<li> setAttribute roundtripping for ${value}`);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -3203,8 +3203,9 @@ def AssertInheritanceChain(descriptor):
|
||||
desc = descriptor.getDescriptor(iface.identifier.name)
|
||||
asserts += (
|
||||
"MOZ_ASSERT(static_cast<%s*>(aObject) == \n"
|
||||
" reinterpret_cast<%s*>(aObject));\n" %
|
||||
(desc.nativeType, desc.nativeType))
|
||||
" reinterpret_cast<%s*>(aObject),\n"
|
||||
" \"Multiple inheritance for %s is broken.\");\n" %
|
||||
(desc.nativeType, desc.nativeType, desc.nativeType))
|
||||
iface = iface.parent
|
||||
asserts += "MOZ_ASSERT(ToSupportsIsCorrect(aObject));\n"
|
||||
return asserts
|
||||
|
||||
@@ -911,6 +911,8 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
|
||||
mViewportWidth = mWidth;
|
||||
mViewportHeight = mHeight;
|
||||
|
||||
gl->fScissor(0, 0, mWidth, mHeight);
|
||||
|
||||
// Make sure that we clear this out, otherwise
|
||||
// we'll end up displaying random memory
|
||||
gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
|
||||
|
||||
@@ -1502,10 +1502,10 @@ void WebGLContext::TexParameter_base(GLenum rawTarget, GLenum pname,
|
||||
case LOCAL_GL_NOTEQUAL:
|
||||
case LOCAL_GL_ALWAYS:
|
||||
case LOCAL_GL_NEVER:
|
||||
paramValueInvalid = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
paramValueInvalid = true;
|
||||
pnameAndParamAreIncompatible = true;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -263,9 +263,6 @@ bool
|
||||
WebGLContext::IsFormatValidForFB(GLenum sizedFormat) const
|
||||
{
|
||||
switch (sizedFormat) {
|
||||
case LOCAL_GL_ALPHA8:
|
||||
case LOCAL_GL_LUMINANCE8:
|
||||
case LOCAL_GL_LUMINANCE8_ALPHA8:
|
||||
case LOCAL_GL_RGB8:
|
||||
case LOCAL_GL_RGBA8:
|
||||
case LOCAL_GL_RGB565:
|
||||
|
||||
@@ -390,7 +390,7 @@ HTMLTableCellElement::ParseAttribute(int32_t aNamespaceID,
|
||||
// quirks mode does not honor the special html 4 value of 0
|
||||
if (val > MAX_COLSPAN || val < 0 ||
|
||||
(0 == val && InNavQuirksMode(OwnerDoc()))) {
|
||||
aResult.SetTo(1);
|
||||
aResult.SetTo(1, &aValue);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
@@ -401,7 +401,7 @@ HTMLTableCellElement::ParseAttribute(int32_t aNamespaceID,
|
||||
int32_t val = aResult.GetIntegerValue();
|
||||
// quirks mode does not honor the special html 4 value of 0
|
||||
if (val < 0 || (0 == val && InNavQuirksMode(OwnerDoc()))) {
|
||||
aResult.SetTo(1);
|
||||
aResult.SetTo(1, &aValue);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
|
||||
@@ -50,7 +50,7 @@ interface nsITranslationNodeList;
|
||||
interface nsIJSRAIIHelper;
|
||||
interface nsIContentPermissionRequest;
|
||||
|
||||
[scriptable, uuid(6eaf87a1-b252-4c4e-a2fc-318120680335)]
|
||||
[scriptable, uuid(dde97573-f4cf-45ce-bbb0-5af4e5f77440)]
|
||||
interface nsIDOMWindowUtils : nsISupports {
|
||||
|
||||
/**
|
||||
@@ -205,15 +205,14 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
*
|
||||
* The effect of this API is for gfx code to allocate more or fewer
|
||||
* pixels for rescalable content by a factor of |resolution| in
|
||||
* either or both dimensions. The scale at which the content is
|
||||
* displayed does not change; if that is desired, use
|
||||
* setResolutionAndScaleTo() instead.
|
||||
* both dimensions. The scale at which the content is displayed does
|
||||
* not change; if that is desired, use setResolutionAndScaleTo() instead.
|
||||
*
|
||||
* The caller of this method must have chrome privileges.
|
||||
*/
|
||||
void setResolution(in float aXResolution, in float aYResolution);
|
||||
void setResolution(in float aResolution);
|
||||
|
||||
void getResolution(out float aXResolution, out float aYResolution);
|
||||
void getResolution(out float aResolution);
|
||||
|
||||
/**
|
||||
* Similar to setResolution(), but also scales the content by the
|
||||
@@ -226,7 +225,7 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
*
|
||||
* The caller of this method must have chrome privileges.
|
||||
*/
|
||||
void setResolutionAndScaleTo(in float aXResolution, in float aYResolution);
|
||||
void setResolutionAndScaleTo(in float aResolution);
|
||||
|
||||
/**
|
||||
* Whether the resolution has been set by the user.
|
||||
@@ -1275,6 +1274,11 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
*/
|
||||
[noscript] boolean isInModalState();
|
||||
|
||||
/**
|
||||
* Request set internal desktopMode flag change.
|
||||
*/
|
||||
void setDesktopModeViewport(in boolean aDesktopModeViewport);
|
||||
|
||||
/**
|
||||
* Suspend/resume timeouts on this window and its descendant windows.
|
||||
*/
|
||||
@@ -1442,6 +1446,23 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
*/
|
||||
boolean checkAndClearPaintedState(in nsIDOMElement aElement);
|
||||
|
||||
/**
|
||||
* Check whether all display items of the primary frame of aElement have been
|
||||
* assigned to the same single PaintedLayer in the last paint. If that is the
|
||||
* case, returns whether that PaintedLayer is opaque; if it's not the case, an
|
||||
* exception is thrown.
|
||||
*/
|
||||
boolean isPartOfOpaqueLayer(in nsIDOMElement aElement);
|
||||
|
||||
/**
|
||||
* Count the number of different PaintedLayers that the supplied elements have
|
||||
* been assigned to in the last paint. Throws an exception if any of the
|
||||
* elements doesn't have a primary frame, or if that frame's display items are
|
||||
* assigned to any other layers than just a single PaintedLayer per element.
|
||||
*/
|
||||
unsigned long numberOfAssignedPaintedLayers([array, size_is(count)] in nsIDOMElement aElements,
|
||||
in uint32_t count);
|
||||
|
||||
/**
|
||||
* Get internal id of the stored blob, file or file handle.
|
||||
*/
|
||||
|
||||
+7
-34
@@ -387,7 +387,7 @@ TabChildBase::HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize)
|
||||
// This is the root layer, so the cumulative resolution is the same
|
||||
// as the resolution.
|
||||
metrics.SetPresShellResolution(metrics.GetCumulativeResolution().ToScaleFactor().scale);
|
||||
utils->SetResolutionAndScaleTo(metrics.GetPresShellResolution(), metrics.GetPresShellResolution());
|
||||
utils->SetResolutionAndScaleTo(metrics.GetPresShellResolution());
|
||||
|
||||
CSSSize scrollPort = metrics.CalculateCompositedSizeInCssPixels();
|
||||
utils->SetScrollPositionClampingScrollPortSize(scrollPort.width, scrollPort.height);
|
||||
@@ -910,8 +910,7 @@ TabChild::Observe(nsISupports *aSubject,
|
||||
// until we we get an inner size.
|
||||
if (HasValidInnerSize()) {
|
||||
InitializeRootMetrics();
|
||||
utils->SetResolutionAndScaleTo(mLastRootMetrics.GetPresShellResolution(),
|
||||
mLastRootMetrics.GetPresShellResolution());
|
||||
utils->SetResolutionAndScaleTo(mLastRootMetrics.GetPresShellResolution());
|
||||
HandlePossibleViewportChange(mInnerSize);
|
||||
}
|
||||
}
|
||||
@@ -2331,7 +2330,7 @@ TabChild::GetPresShellResolution() const
|
||||
if (!shell) {
|
||||
return 1.0f;
|
||||
}
|
||||
return shell->GetXResolution();
|
||||
return shell->GetResolution();
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -2637,32 +2636,6 @@ TabChild::RecvAppOfflineStatus(const uint32_t& aId, const bool& aOffline)
|
||||
return true;
|
||||
}
|
||||
|
||||
class UnloadScriptEvent : public nsRunnable
|
||||
{
|
||||
public:
|
||||
UnloadScriptEvent(TabChild* aTabChild, TabChildGlobal* aTabChildGlobal)
|
||||
: mTabChild(aTabChild), mTabChildGlobal(aTabChildGlobal)
|
||||
{ }
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
nsCOMPtr<nsIDOMEvent> event;
|
||||
NS_NewDOMEvent(getter_AddRefs(event), mTabChildGlobal, nullptr, nullptr);
|
||||
if (event) {
|
||||
event->InitEvent(NS_LITERAL_STRING("unload"), false, false);
|
||||
event->SetTrusted(true);
|
||||
|
||||
bool dummy;
|
||||
mTabChildGlobal->DispatchEvent(event, &dummy);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<TabChild> mTabChild;
|
||||
TabChildGlobal* mTabChildGlobal;
|
||||
};
|
||||
|
||||
bool
|
||||
TabChild::RecvDestroy()
|
||||
{
|
||||
@@ -2670,10 +2643,10 @@ TabChild::RecvDestroy()
|
||||
mDestroyed = true;
|
||||
|
||||
if (mTabChildGlobal) {
|
||||
// Let the frame scripts know the child is being closed
|
||||
nsContentUtils::AddScriptRunner(
|
||||
new UnloadScriptEvent(this, mTabChildGlobal)
|
||||
);
|
||||
// Message handlers are called from the event loop, so it better be safe to
|
||||
// run script.
|
||||
MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
|
||||
mTabChildGlobal->DispatchTrustedEvent(NS_LITERAL_STRING("unload"));
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
|
||||
+20
-9
@@ -315,6 +315,12 @@ TabParent::RemoveTabParentFromTable(uint64_t aLayersId)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TabParent::CacheFrameLoader(nsFrameLoader* aFrameLoader)
|
||||
{
|
||||
mFrameLoader = aFrameLoader;
|
||||
}
|
||||
|
||||
void
|
||||
TabParent::SetOwnerElement(Element* aElement)
|
||||
{
|
||||
@@ -401,15 +407,13 @@ TabParent::ActorDestroy(ActorDestroyReason why)
|
||||
if (mIMETabParent == this) {
|
||||
mIMETabParent = nullptr;
|
||||
}
|
||||
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
|
||||
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader(true);
|
||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
nsRefPtr<nsFrameMessageManager> fmm;
|
||||
if (frameLoader) {
|
||||
fmm = frameLoader->GetFrameMessageManager();
|
||||
nsCOMPtr<Element> frameElement(mFrameElement);
|
||||
ReceiveMessage(CHILD_PROCESS_SHUTDOWN_MESSAGE, false, nullptr, nullptr,
|
||||
nullptr);
|
||||
frameLoader->DestroyChild();
|
||||
frameLoader->DestroyComplete();
|
||||
|
||||
if (why == AbnormalShutdown && os) {
|
||||
os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, frameLoader),
|
||||
@@ -418,14 +422,13 @@ TabParent::ActorDestroy(ActorDestroyReason why)
|
||||
NS_LITERAL_STRING("oop-browser-crashed"),
|
||||
true, true);
|
||||
}
|
||||
|
||||
mFrameLoader = nullptr;
|
||||
}
|
||||
|
||||
if (os) {
|
||||
os->NotifyObservers(NS_ISUPPORTS_CAST(nsITabParent*, this), "ipc:browser-destroyed", nullptr);
|
||||
}
|
||||
if (fmm) {
|
||||
fmm->Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -2230,7 +2233,7 @@ TabParent::ReceiveMessage(const nsString& aMessage,
|
||||
nsIPrincipal* aPrincipal,
|
||||
InfallibleTArray<nsString>* aJSONRetVal)
|
||||
{
|
||||
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
|
||||
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader(true);
|
||||
if (frameLoader && frameLoader->GetFrameMessageManager()) {
|
||||
nsRefPtr<nsFrameMessageManager> manager =
|
||||
frameLoader->GetFrameMessageManager();
|
||||
@@ -2348,8 +2351,16 @@ TabParent::AllowContentIME()
|
||||
}
|
||||
|
||||
already_AddRefed<nsFrameLoader>
|
||||
TabParent::GetFrameLoader() const
|
||||
TabParent::GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy) const
|
||||
{
|
||||
if (mIsDestroyed && !aUseCachedFrameLoaderAfterDestroy) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mFrameLoader) {
|
||||
nsRefPtr<nsFrameLoader> fl = mFrameLoader;
|
||||
return fl.forget();
|
||||
}
|
||||
nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner = do_QueryInterface(mFrameElement);
|
||||
return frameLoaderOwner ? frameLoaderOwner->GetFrameLoader() : nullptr;
|
||||
}
|
||||
|
||||
+8
-1
@@ -81,6 +81,8 @@ public:
|
||||
Element* GetOwnerElement() const { return mFrameElement; }
|
||||
void SetOwnerElement(Element* aElement);
|
||||
|
||||
void CacheFrameLoader(nsFrameLoader* aFrameLoader);
|
||||
|
||||
/**
|
||||
* Get the mozapptype attribute from this TabParent's owner DOM element.
|
||||
*/
|
||||
@@ -426,7 +428,7 @@ protected:
|
||||
bool mUpdatedDimensions;
|
||||
|
||||
private:
|
||||
already_AddRefed<nsFrameLoader> GetFrameLoader() const;
|
||||
already_AddRefed<nsFrameLoader> GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy = false) const;
|
||||
layout::RenderFrameParent* GetRenderFrame();
|
||||
nsRefPtr<nsIContentParent> mManager;
|
||||
void TryCacheDPIAndScale();
|
||||
@@ -468,6 +470,11 @@ private:
|
||||
|
||||
nsCOMPtr<nsILoadContext> mLoadContext;
|
||||
|
||||
// We keep a strong reference to the frameloader after we've sent the
|
||||
// Destroy message and before we've received __delete__. This allows us to
|
||||
// dispatch message manager messages during this time.
|
||||
nsRefPtr<nsFrameLoader> mFrameLoader;
|
||||
|
||||
TabId mTabId;
|
||||
|
||||
// Helper class for RecvCreateWindow.
|
||||
|
||||
@@ -25,7 +25,6 @@ function SystemMessageCache() {
|
||||
|
||||
this._pagesCache = [];
|
||||
|
||||
dump("SystemMessageCache: init");
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
kMessages.forEach(function(aMessage) {
|
||||
cpmm.addMessageListener(aMessage, this);
|
||||
|
||||
@@ -250,8 +250,6 @@ SystemMessageInternal.prototype = {
|
||||
aExtra);
|
||||
debug("Returned status of sending message: " + result);
|
||||
|
||||
// Don't need to open the pages and queue the system message
|
||||
// which was not allowed to be sent.
|
||||
if (result === MSG_SENT_FAILURE_PERM_DENIED) {
|
||||
return;
|
||||
}
|
||||
@@ -259,14 +257,6 @@ SystemMessageInternal.prototype = {
|
||||
// For each page we must receive a confirm.
|
||||
++pendingPromise.counter;
|
||||
|
||||
let page = this._findPage(aType, aPageURL, manifestURL);
|
||||
if (page) {
|
||||
// Queue this message in the corresponding pages.
|
||||
this._queueMessage(page, aMessage, messageID);
|
||||
|
||||
this._openAppPage(page, aMessage, aExtra, result);
|
||||
}
|
||||
|
||||
}, this);
|
||||
|
||||
if (pendingPromise.counter) {
|
||||
@@ -308,18 +298,6 @@ SystemMessageInternal.prototype = {
|
||||
aPage.manifestURL,
|
||||
aExtra);
|
||||
debug("Returned status of sending message: " + result);
|
||||
|
||||
|
||||
// Don't need to open the pages and queue the system message
|
||||
// which was not allowed to be sent.
|
||||
if (result === MSG_SENT_FAILURE_PERM_DENIED) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Queue this message in the corresponding pages.
|
||||
this._queueMessage(aPage, aMessage, messageID);
|
||||
|
||||
this._openAppPage(aPage, aMessage, aExtra, result);
|
||||
};
|
||||
|
||||
if ('function' !== typeof shouldDispatchFunc) {
|
||||
@@ -738,15 +716,32 @@ SystemMessageInternal.prototype = {
|
||||
return MSG_SENT_FAILURE_PERM_DENIED;
|
||||
}
|
||||
|
||||
// Queue this message in the corresponding pages.
|
||||
let page = this._findPage(aType, aPageURL, aManifestURL);
|
||||
if (!page) {
|
||||
debug("Message " + aType + " is not registered for " +
|
||||
aPageURL + " @ " + aManifestURL);
|
||||
// FIXME bug 1140275 should only send message to page registered in manifest
|
||||
// return MSG_SENT_FAILURE_PERM_DENIED;
|
||||
}
|
||||
if (page)
|
||||
this._queueMessage(page, aMessage, aMessageID);
|
||||
|
||||
let appPageIsRunning = false;
|
||||
let pageKey = this._createKeyForPage({ type: aType,
|
||||
manifestURL: aManifestURL,
|
||||
pageURL: aPageURL });
|
||||
|
||||
let cache = this._findCacheForApp(aManifestURL);
|
||||
let targets = this._listeners[aManifestURL];
|
||||
if (targets) {
|
||||
for (let index = 0; index < targets.length; ++index) {
|
||||
let target = targets[index];
|
||||
let manager = target.target;
|
||||
|
||||
// Ensure hasPendingMessage cache is refreshed before we open app
|
||||
manager.sendAsyncMessage("SystemMessageCache:RefreshCache", cache);
|
||||
|
||||
// We only need to send the system message to the targets (processes)
|
||||
// which contain the window page that matches the manifest/page URL of
|
||||
// the destination of system message.
|
||||
@@ -765,7 +760,6 @@ SystemMessageInternal.prototype = {
|
||||
// Multiple windows can share the same target (process), the content
|
||||
// window needs to check if the manifest/page URL is matched. Only
|
||||
// *one* window should handle the system message.
|
||||
let manager = target.target;
|
||||
manager.sendAsyncMessage("SystemMessageManager:Message",
|
||||
{ type: aType,
|
||||
msg: aMessage,
|
||||
@@ -775,6 +769,7 @@ SystemMessageInternal.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
let result = MSG_SENT_SUCCESS;
|
||||
if (!appPageIsRunning) {
|
||||
// The app page isn't running and relies on the 'open-app' chrome event to
|
||||
// wake it up. We still need to acquire a CPU wake lock for that page and
|
||||
@@ -782,12 +777,12 @@ SystemMessageInternal.prototype = {
|
||||
// or a "SystemMessageManager:HandleMessageDone" message when the page
|
||||
// finishes handling the system message with other pending messages. At
|
||||
// that point, we'll release the lock we acquired.
|
||||
result = MSG_SENT_FAILURE_APP_NOT_RUNNING;
|
||||
this._acquireCpuWakeLock(pageKey);
|
||||
return MSG_SENT_FAILURE_APP_NOT_RUNNING;
|
||||
} else {
|
||||
return MSG_SENT_SUCCESS;
|
||||
}
|
||||
|
||||
if (page)
|
||||
this._openAppPage(page, aMessage, aExtra, result);
|
||||
return result;
|
||||
},
|
||||
|
||||
_resolvePendingPromises: function(aMessageID) {
|
||||
|
||||
@@ -231,8 +231,8 @@ nsPluginInstanceOwner::GetImageContainer()
|
||||
|
||||
// NotifySize() causes Flash to do a bunch of stuff like ask for surfaces to render
|
||||
// into, set y-flip flags, etc, so we do this at the beginning.
|
||||
gfxSize resolution = mPluginFrame->PresContext()->PresShell()->GetCumulativeResolution();
|
||||
ScreenSize screenSize = (r * LayoutDeviceToScreenScale2D(resolution.width, resolution.height)).Size();
|
||||
float resolution = mPluginFrame->PresContext()->PresShell()->GetCumulativeResolution();
|
||||
ScreenSize screenSize = (r * LayoutDeviceToScreenScale(resolution)).Size();
|
||||
mInstance->NotifySize(nsIntSize(screenSize.width, screenSize.height));
|
||||
|
||||
container = LayerManager::CreateImageContainer();
|
||||
|
||||
@@ -1049,6 +1049,8 @@ PluginModuleParent::DeallocPPluginInstanceParent(PPluginInstanceParent* aActor)
|
||||
void
|
||||
PluginModuleParent::SetPluginFuncs(NPPluginFuncs* aFuncs)
|
||||
{
|
||||
MOZ_ASSERT(aFuncs);
|
||||
|
||||
aFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
|
||||
aFuncs->javaClass = nullptr;
|
||||
|
||||
@@ -1684,7 +1686,7 @@ PluginModuleParent::RecvNP_InitializeResult(const NPError& aError)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mIsStartingAsync) {
|
||||
if (mIsStartingAsync && mNPPIface) {
|
||||
SetPluginFuncs(mNPPIface);
|
||||
InitAsyncSurrogates();
|
||||
}
|
||||
@@ -1803,8 +1805,17 @@ PluginModuleParent::NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error)
|
||||
|
||||
*error = NPERR_NO_ERROR;
|
||||
if (mIsStartingAsync && !IsChrome()) {
|
||||
PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
|
||||
mNPPIface = pFuncs;
|
||||
#if defined(XP_MACOSX)
|
||||
if (mNPInitialized) {
|
||||
SetPluginFuncs(pFuncs);
|
||||
InitAsyncSurrogates();
|
||||
} else {
|
||||
PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
|
||||
}
|
||||
#else
|
||||
PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
|
||||
#endif
|
||||
} else {
|
||||
SetPluginFuncs(pFuncs);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "gDNSService",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AlarmService",
|
||||
"resource://gre/modules/AlarmService.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gPowerManagerService",
|
||||
"@mozilla.org/power/powermanagerservice;1",
|
||||
"nsIPowerManagerService");
|
||||
|
||||
var threadManager = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager);
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["PushService"];
|
||||
@@ -873,8 +877,18 @@ this.PushService = {
|
||||
debug("serverURL: " + uri.spec);
|
||||
this._wsListener = new PushWebSocketListener(this);
|
||||
this._ws.protocol = "push-notification";
|
||||
this._ws.asyncOpen(uri, serverURL, this._wsListener, null);
|
||||
this._currentState = STATE_WAITING_FOR_WS_START;
|
||||
|
||||
try {
|
||||
// Grab a wakelock before we open the socket to ensure we don't go to sleep
|
||||
// before connection the is opened.
|
||||
this._ws.asyncOpen(uri, serverURL, this._wsListener, null);
|
||||
this._acquireWakeLock();
|
||||
this._currentState = STATE_WAITING_FOR_WS_START;
|
||||
} catch(e) {
|
||||
debug("Error opening websocket. asyncOpen failed!");
|
||||
this._shutdownWS();
|
||||
this._reconnectAfterBackoff();
|
||||
}
|
||||
},
|
||||
|
||||
_startListeningIfChannelsPresent: function() {
|
||||
@@ -997,6 +1011,38 @@ this.PushService = {
|
||||
}
|
||||
},
|
||||
|
||||
_acquireWakeLock: function() {
|
||||
if (!this._socketWakeLock) {
|
||||
debug("Acquiring Socket Wakelock");
|
||||
this._socketWakeLock = gPowerManagerService.newWakeLock("cpu");
|
||||
}
|
||||
if (!this._socketWakeLockTimer) {
|
||||
debug("Creating Socket WakeLock Timer");
|
||||
this._socketWakeLockTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
}
|
||||
|
||||
debug("Setting Socket WakeLock Timer");
|
||||
this._socketWakeLockTimer
|
||||
.initWithCallback(this._releaseWakeLock.bind(this),
|
||||
// Allow the same time for socket setup as we do for
|
||||
// requests after the setup. Fudge it a bit since
|
||||
// timers can be a little off and we don't want to go
|
||||
// to sleep just as the socket connected.
|
||||
this._requestTimeout + 1000,
|
||||
Ci.nsITimer.ONE_SHOT);
|
||||
},
|
||||
|
||||
_releaseWakeLock: function() {
|
||||
debug("Releasing Socket WakeLock");
|
||||
if (this._socketWakeLockTimer) {
|
||||
this._socketWakeLockTimer.cancel();
|
||||
}
|
||||
if (this._socketWakeLock) {
|
||||
this._socketWakeLock.unlock();
|
||||
this._socketWakeLock = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Protocol handler invoked by server message.
|
||||
*/
|
||||
@@ -1512,6 +1558,8 @@ this.PushService = {
|
||||
// begin Push protocol handshake
|
||||
_wsOnStart: function(context) {
|
||||
debug("wsOnStart()");
|
||||
this._releaseWakeLock();
|
||||
|
||||
if (this._currentState != STATE_WAITING_FOR_WS_START) {
|
||||
debug("NOT in STATE_WAITING_FOR_WS_START. Current state " +
|
||||
this._currentState + ". Skipping");
|
||||
@@ -1568,6 +1616,7 @@ this.PushService = {
|
||||
*/
|
||||
_wsOnStop: function(context, statusCode) {
|
||||
debug("wsOnStop()");
|
||||
this._releaseWakeLock();
|
||||
|
||||
if (statusCode != Cr.NS_OK &&
|
||||
!(statusCode == Cr.NS_BASE_STREAM_CLOSED && this._willBeWokenUpByUDP)) {
|
||||
|
||||
@@ -385,9 +385,7 @@ nsComposerCommandsUpdater::Notify(nsITimer *timer)
|
||||
nsresult
|
||||
NS_NewComposerCommandsUpdater(nsISelectionListener** aInstancePtrResult)
|
||||
{
|
||||
nsComposerCommandsUpdater* newThang = new nsComposerCommandsUpdater;
|
||||
NS_ENSURE_TRUE(newThang, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
return newThang->QueryInterface(NS_GET_IID(nsISelectionListener),
|
||||
(void **)aInstancePtrResult);
|
||||
nsRefPtr<nsComposerCommandsUpdater> newThang = new nsComposerCommandsUpdater;
|
||||
newThang.forget(aInstancePtrResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
+115
-73
@@ -625,23 +625,28 @@ nsEditor::DeleteSelection(EDirection aAction, EStripWrappers aStripWrappers)
|
||||
}
|
||||
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsEditor::GetSelection(nsISelection **aSelection)
|
||||
nsEditor::GetSelection(nsISelection** aSelection)
|
||||
{
|
||||
return GetSelection(nsISelectionController::SELECTION_NORMAL, aSelection);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsEditor::GetSelection(int16_t aSelectionType, nsISelection** aSelection)
|
||||
{
|
||||
NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
|
||||
*aSelection = nullptr;
|
||||
nsCOMPtr<nsISelectionController> selcon;
|
||||
GetSelectionController(getter_AddRefs(selcon));
|
||||
NS_ENSURE_TRUE(selcon, NS_ERROR_NOT_INITIALIZED);
|
||||
return selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, aSelection); // does an addref
|
||||
return selcon->GetSelection(aSelectionType, aSelection); // does an addref
|
||||
}
|
||||
|
||||
Selection*
|
||||
nsEditor::GetSelection()
|
||||
nsEditor::GetSelection(int16_t aSelectionType)
|
||||
{
|
||||
nsCOMPtr<nsISelection> sel;
|
||||
nsresult res = GetSelection(getter_AddRefs(sel));
|
||||
nsresult res = GetSelection(aSelectionType, getter_AddRefs(sel));
|
||||
NS_ENSURE_SUCCESS(res, nullptr);
|
||||
|
||||
return static_cast<Selection*>(sel.get());
|
||||
@@ -2689,6 +2694,14 @@ nsEditor::SplitNodeImpl(nsIContent& aExistingRightNode,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
struct SavedRange {
|
||||
nsRefPtr<Selection> mSelection;
|
||||
nsCOMPtr<nsINode> mStartNode;
|
||||
nsCOMPtr<nsINode> mEndNode;
|
||||
int32_t mStartOffset;
|
||||
int32_t mEndOffset;
|
||||
};
|
||||
|
||||
nsresult
|
||||
nsEditor::JoinNodesImpl(nsINode* aNodeToKeep,
|
||||
nsINode* aNodeToJoin,
|
||||
@@ -2698,25 +2711,6 @@ nsEditor::JoinNodesImpl(nsINode* aNodeToKeep,
|
||||
MOZ_ASSERT(aNodeToJoin);
|
||||
MOZ_ASSERT(aParent);
|
||||
|
||||
nsRefPtr<Selection> selection = GetSelection();
|
||||
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
|
||||
|
||||
// remember some selection points
|
||||
nsCOMPtr<nsINode> selStartNode;
|
||||
int32_t selStartOffset;
|
||||
nsresult result = GetStartNodeAndOffset(selection, getter_AddRefs(selStartNode), &selStartOffset);
|
||||
if (NS_FAILED(result)) {
|
||||
selStartNode = nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsINode> selEndNode;
|
||||
int32_t selEndOffset;
|
||||
result = GetEndNodeAndOffset(selection, getter_AddRefs(selEndNode), &selEndOffset);
|
||||
// Joe or Kin should comment here on why the following line is not a copy/paste error
|
||||
if (NS_FAILED(result)) {
|
||||
selStartNode = nullptr;
|
||||
}
|
||||
|
||||
uint32_t firstNodeLength = aNodeToJoin->Length();
|
||||
|
||||
int32_t joinOffset;
|
||||
@@ -2724,24 +2718,53 @@ nsEditor::JoinNodesImpl(nsINode* aNodeToKeep,
|
||||
int32_t keepOffset;
|
||||
nsINode* parent = GetNodeLocation(aNodeToKeep, &keepOffset);
|
||||
|
||||
// if selection endpoint is between the nodes, remember it as being
|
||||
// in the one that is going away instead. This simplifies later selection
|
||||
// adjustment logic at end of this method.
|
||||
if (selStartNode) {
|
||||
if (selStartNode == parent &&
|
||||
joinOffset < selStartOffset && selStartOffset <= keepOffset) {
|
||||
selStartNode = aNodeToJoin;
|
||||
selStartOffset = firstNodeLength;
|
||||
// Remember all selection points.
|
||||
nsAutoTArray<SavedRange, 10> savedRanges;
|
||||
for (size_t i = 0; i < nsISelectionController::NUM_SELECTIONTYPES - 1; ++i) {
|
||||
SelectionType type(1 << i);
|
||||
SavedRange range;
|
||||
range.mSelection = GetSelection(type);
|
||||
if (type == nsISelectionController::SELECTION_NORMAL) {
|
||||
NS_ENSURE_TRUE(range.mSelection, NS_ERROR_NULL_POINTER);
|
||||
} else if (!range.mSelection) {
|
||||
// For non-normal selections, skip over the non-existing ones.
|
||||
continue;
|
||||
}
|
||||
if (selEndNode == parent &&
|
||||
joinOffset < selEndOffset && selEndOffset <= keepOffset) {
|
||||
selEndNode = aNodeToJoin;
|
||||
selEndOffset = firstNodeLength;
|
||||
|
||||
for (uint32_t j = 0; j < range.mSelection->RangeCount(); ++j) {
|
||||
nsRefPtr<nsRange> r = range.mSelection->GetRangeAt(j);
|
||||
if (!r->IsPositioned()) {
|
||||
continue;
|
||||
}
|
||||
range.mStartNode = r->GetStartParent();
|
||||
range.mStartOffset = r->StartOffset();
|
||||
range.mEndNode = r->GetEndParent();
|
||||
range.mEndOffset = r->EndOffset();
|
||||
|
||||
// If selection endpoint is between the nodes, remember it as being
|
||||
// in the one that is going away instead. This simplifies later selection
|
||||
// adjustment logic at end of this method.
|
||||
if (range.mStartNode) {
|
||||
if (range.mStartNode == parent &&
|
||||
joinOffset < range.mStartOffset &&
|
||||
range.mStartOffset <= keepOffset) {
|
||||
range.mStartNode = aNodeToJoin;
|
||||
range.mStartOffset = firstNodeLength;
|
||||
}
|
||||
if (range.mEndNode == parent &&
|
||||
joinOffset < range.mEndOffset &&
|
||||
range.mEndOffset <= keepOffset) {
|
||||
range.mEndNode = aNodeToJoin;
|
||||
range.mEndOffset = firstNodeLength;
|
||||
}
|
||||
}
|
||||
|
||||
savedRanges.AppendElement(range);
|
||||
}
|
||||
}
|
||||
|
||||
// ok, ready to do join now.
|
||||
// if it's a text node, just shuffle around some text
|
||||
// OK, ready to do join now.
|
||||
// If it's a text node, just shuffle around some text.
|
||||
nsCOMPtr<nsIDOMCharacterData> keepNodeAsText( do_QueryInterface(aNodeToKeep) );
|
||||
nsCOMPtr<nsIDOMCharacterData> joinNodeAsText( do_QueryInterface(aNodeToJoin) );
|
||||
if (keepNodeAsText && joinNodeAsText) {
|
||||
@@ -2752,15 +2775,15 @@ nsEditor::JoinNodesImpl(nsINode* aNodeToKeep,
|
||||
leftText += rightText;
|
||||
keepNodeAsText->SetData(leftText);
|
||||
} else {
|
||||
// otherwise it's an interior node, so shuffle around the children
|
||||
// Otherwise it's an interior node, so shuffle around the children.
|
||||
nsCOMPtr<nsINodeList> childNodes = aNodeToJoin->ChildNodes();
|
||||
MOZ_ASSERT(childNodes);
|
||||
|
||||
// remember the first child in aNodeToKeep, we'll insert all the children of aNodeToJoin in front of it
|
||||
// GetFirstChild returns nullptr firstNode if aNodeToKeep has no children, that's ok.
|
||||
// Remember the first child in aNodeToKeep, we'll insert all the children of aNodeToJoin in front of it
|
||||
// GetFirstChild returns nullptr firstNode if aNodeToKeep has no children, that's OK.
|
||||
nsCOMPtr<nsIContent> firstNode = aNodeToKeep->GetFirstChild();
|
||||
|
||||
// have to go through the list backwards to keep deletes from interfering with iteration
|
||||
// Have to go through the list backwards to keep deletes from interfering with iteration.
|
||||
for (uint32_t i = childNodes->Length(); i > 0; --i) {
|
||||
nsCOMPtr<nsIContent> childNode = childNodes->Item(i - 1);
|
||||
if (childNode) {
|
||||
@@ -2773,41 +2796,60 @@ nsEditor::JoinNodesImpl(nsINode* aNodeToKeep,
|
||||
}
|
||||
}
|
||||
|
||||
// delete the extra node
|
||||
// Delete the extra node.
|
||||
ErrorResult err;
|
||||
aParent->RemoveChild(*aNodeToJoin, err);
|
||||
|
||||
if (GetShouldTxnSetSelection()) {
|
||||
// editor wants us to set selection at join point
|
||||
bool shouldSetSelection = GetShouldTxnSetSelection();
|
||||
|
||||
nsRefPtr<Selection> previousSelection;
|
||||
for (size_t i = 0; i < savedRanges.Length(); ++i) {
|
||||
// And adjust the selection if needed.
|
||||
SavedRange& range = savedRanges[i];
|
||||
|
||||
// If we have not seen the selection yet, clear all of its ranges.
|
||||
if (range.mSelection != previousSelection) {
|
||||
nsresult rv = range.mSelection->RemoveAllRanges();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
previousSelection = range.mSelection;
|
||||
}
|
||||
|
||||
if (shouldSetSelection &&
|
||||
range.mSelection->Type() ==
|
||||
nsISelectionController::SELECTION_NORMAL) {
|
||||
// If the editor should adjust the selection, don't bother restoring
|
||||
// the ranges for the normal selection here.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check to see if we joined nodes where selection starts.
|
||||
if (range.mStartNode == aNodeToJoin) {
|
||||
range.mStartNode = aNodeToKeep;
|
||||
} else if (range.mStartNode == aNodeToKeep) {
|
||||
range.mStartOffset += firstNodeLength;
|
||||
}
|
||||
|
||||
// Check to see if we joined nodes where selection ends.
|
||||
if (range.mEndNode == aNodeToJoin) {
|
||||
range.mEndNode = aNodeToKeep;
|
||||
} else if (range.mEndNode == aNodeToKeep) {
|
||||
range.mEndOffset += firstNodeLength;
|
||||
}
|
||||
|
||||
nsRefPtr<nsRange> newRange;
|
||||
nsresult rv = nsRange::CreateRange(range.mStartNode, range.mStartOffset,
|
||||
range.mEndNode, range.mEndOffset,
|
||||
getter_AddRefs(newRange));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = range.mSelection->AddRange(newRange);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (shouldSetSelection) {
|
||||
// Editor wants us to set selection at join point.
|
||||
nsRefPtr<Selection> selection = GetSelection();
|
||||
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
|
||||
selection->Collapse(aNodeToKeep, AssertedCast<int32_t>(firstNodeLength));
|
||||
} else if (selStartNode) {
|
||||
// and adjust the selection if needed
|
||||
// HACK: this is overly simplified - multi-range selections need more work than this
|
||||
bool bNeedToAdjust = false;
|
||||
|
||||
// check to see if we joined nodes where selection starts
|
||||
if (selStartNode == aNodeToJoin) {
|
||||
bNeedToAdjust = true;
|
||||
selStartNode = aNodeToKeep;
|
||||
} else if (selStartNode == aNodeToKeep) {
|
||||
bNeedToAdjust = true;
|
||||
selStartOffset += firstNodeLength;
|
||||
}
|
||||
|
||||
// check to see if we joined nodes where selection ends
|
||||
if (selEndNode == aNodeToJoin) {
|
||||
bNeedToAdjust = true;
|
||||
selEndNode = aNodeToKeep;
|
||||
} else if (selEndNode == aNodeToKeep) {
|
||||
bNeedToAdjust = true;
|
||||
selEndOffset += firstNodeLength;
|
||||
}
|
||||
|
||||
// adjust selection if needed
|
||||
if (bNeedToAdjust) {
|
||||
selection->Collapse(selStartNode, selStartOffset);
|
||||
selection->Extend(selEndNode, selEndOffset);
|
||||
}
|
||||
}
|
||||
|
||||
return err.ErrorCode();
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "nsIObserver.h" // for NS_DECL_NSIOBSERVER, etc
|
||||
#include "nsIPhonetic.h" // for NS_DECL_NSIPHONETIC, etc
|
||||
#include "nsIPlaintextEditor.h" // for nsIPlaintextEditor, etc
|
||||
#include "nsISelectionController.h" // for nsISelectionController constants
|
||||
#include "nsISupportsImpl.h" // for nsEditor::Release, etc
|
||||
#include "nsIWeakReferenceUtils.h" // for nsWeakPtr
|
||||
#include "nsLiteralString.h" // for NS_LITERAL_STRING
|
||||
@@ -421,6 +422,8 @@ protected:
|
||||
*/
|
||||
void EnsureComposition(mozilla::WidgetGUIEvent* aEvent);
|
||||
|
||||
nsresult GetSelection(int16_t aSelectionType, nsISelection** aSelection);
|
||||
|
||||
public:
|
||||
|
||||
/** All editor operations which alter the doc should be prefaced
|
||||
@@ -618,7 +621,8 @@ public:
|
||||
#if DEBUG_JOE
|
||||
static void DumpNode(nsIDOMNode *aNode, int32_t indent=0);
|
||||
#endif
|
||||
mozilla::dom::Selection* GetSelection();
|
||||
mozilla::dom::Selection* GetSelection(int16_t aSelectionType =
|
||||
nsISelectionController::SELECTION_NORMAL);
|
||||
|
||||
// Helpers to add a node to the selection.
|
||||
// Used by table cell selection methods
|
||||
|
||||
@@ -254,7 +254,7 @@ nsresult NS_NewHTMLURIRefObject(nsIURIRefObject** aResult, nsIDOMNode* aNode)
|
||||
*aResult = 0;
|
||||
return rv;
|
||||
}
|
||||
return refObject->QueryInterface(NS_GET_IID(nsIURIRefObject),
|
||||
(void**)aResult);
|
||||
refObject.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ skip-if = buildapp == 'mulet'
|
||||
[test_bug780908.xul]
|
||||
[test_bug830600.html]
|
||||
[test_bug1053048.html]
|
||||
[test_bug1100966.html]
|
||||
[test_bug1102906.html]
|
||||
[test_bug1101392.html]
|
||||
[test_bug1140617.xul]
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<!DOCTYPE>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1100966
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 1100966</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="display">
|
||||
</div>
|
||||
<div id="content" contenteditable>
|
||||
=====<br>
|
||||
correct<br>
|
||||
fivee sixx<br>
|
||||
====
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<script class="testbody" type="application/javascript">
|
||||
|
||||
/** Test for Bug 1100966 **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.waitForFocus(function() {
|
||||
var div = document.getElementById("content");
|
||||
div.focus();
|
||||
synthesizeMouseAtCenter(div, {});
|
||||
|
||||
synthesizeKey(" ", {});
|
||||
setTimeout(function() {
|
||||
synthesizeKey("a", {});
|
||||
setTimeout(function() {
|
||||
synthesizeKey("VK_BACK_SPACE", {});
|
||||
|
||||
var sel = getSpellCheckSelection();
|
||||
is(sel.rangeCount, 2, "We should have two misspelled words");
|
||||
is(sel.getRangeAt(0), "fivee", "Correct misspelled word");
|
||||
is(sel.getRangeAt(1), "sixx", "Correct misspelled word");
|
||||
|
||||
SimpleTest.finish();
|
||||
},0);
|
||||
},0);
|
||||
|
||||
});
|
||||
|
||||
function getSpellCheckSelection() {
|
||||
var Ci = Components.interfaces;
|
||||
var editingSession = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIEditingSession);
|
||||
var editor = editingSession.getEditorForWindow(window);
|
||||
var selcon = editor.selectionController;
|
||||
return selcon.getSelection(selcon.SELECTION_SPELLCHECK);
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -2000,7 +2000,7 @@ nsTextServicesDocument::GetDocumentContentRootNode(nsIDOMNode **aNode)
|
||||
|
||||
NS_ENSURE_TRUE(bodyElement, NS_ERROR_FAILURE);
|
||||
|
||||
result = bodyElement->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aNode);
|
||||
bodyElement.forget(aNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2014,7 +2014,7 @@ nsTextServicesDocument::GetDocumentContentRootNode(nsIDOMNode **aNode)
|
||||
|
||||
NS_ENSURE_TRUE(docElement, NS_ERROR_FAILURE);
|
||||
|
||||
result = docElement->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aNode);
|
||||
docElement.forget(aNode);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -288,10 +288,10 @@ nsControllerCommandGroup::IsCommandInGroup(const char *aCommand, const char *aGr
|
||||
NS_IMETHODIMP
|
||||
nsControllerCommandGroup::GetGroupsEnumerator(nsISimpleEnumerator **_retval)
|
||||
{
|
||||
nsGroupsEnumerator *groupsEnum = new nsGroupsEnumerator(mGroupsHash);
|
||||
if (!groupsEnum) return NS_ERROR_OUT_OF_MEMORY;
|
||||
nsRefPtr<nsGroupsEnumerator> groupsEnum = new nsGroupsEnumerator(mGroupsHash);
|
||||
|
||||
return groupsEnum->QueryInterface(NS_GET_IID(nsISimpleEnumerator), (void **)_retval);
|
||||
groupsEnum.forget(_retval);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* nsISimpleEnumerator getEnumeratorForGroup (in DOMString aGroup); */
|
||||
@@ -301,10 +301,10 @@ nsControllerCommandGroup::GetEnumeratorForGroup(const char *aGroup, nsISimpleEnu
|
||||
nsDependentCString groupKey(aGroup);
|
||||
nsTArray<nsCString> *commandList = mGroupsHash.Get(groupKey); // may be null
|
||||
|
||||
nsNamedGroupEnumerator *theGroupEnum = new nsNamedGroupEnumerator(commandList);
|
||||
if (!theGroupEnum) return NS_ERROR_OUT_OF_MEMORY;
|
||||
nsRefPtr<nsNamedGroupEnumerator> theGroupEnum = new nsNamedGroupEnumerator(commandList);
|
||||
|
||||
return theGroupEnum->QueryInterface(NS_GET_IID(nsISimpleEnumerator), (void **)_retval);
|
||||
theGroupEnum.forget(_retval);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
@@ -409,8 +409,8 @@ nsresult nsWebBrowserFind::GetRootNode(nsIDOMDocument* aDomDoc,
|
||||
rv = htmlDoc->GetBody(getter_AddRefs(bodyElement));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_ARG_POINTER(bodyElement);
|
||||
return bodyElement->QueryInterface(NS_GET_IID(nsIDOMNode),
|
||||
(void **)aNode);
|
||||
bodyElement.forget(aNode);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// For non-HTML documents, the content root node will be the doc element.
|
||||
@@ -418,7 +418,8 @@ nsresult nsWebBrowserFind::GetRootNode(nsIDOMDocument* aDomDoc,
|
||||
rv = aDomDoc->GetDocumentElement(getter_AddRefs(docElement));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_ARG_POINTER(docElement);
|
||||
return docElement->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aNode);
|
||||
docElement.forget(aNode);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsWebBrowserFind::SetRangeAroundDocument(nsIDOMRange* aSearchRange,
|
||||
|
||||
@@ -208,7 +208,7 @@ APZCCallbackHelper::UpdateRootFrame(nsIDOMWindowUtils* aUtils,
|
||||
// last paint.
|
||||
float presShellResolution = aMetrics.GetPresShellResolution()
|
||||
* aMetrics.GetAsyncZoom().scale;
|
||||
aUtils->SetResolutionAndScaleTo(presShellResolution, presShellResolution);
|
||||
aUtils->SetResolutionAndScaleTo(presShellResolution);
|
||||
|
||||
SetDisplayPortMargins(aUtils, content, aMetrics);
|
||||
}
|
||||
|
||||
@@ -979,9 +979,11 @@ AsyncCompositionManager::TransformScrollableLayer(Layer* aLayer)
|
||||
ParentLayerPoint translation = userScroll - goannaScroll;
|
||||
Matrix4x4 treeTransform = ViewTransform(asyncZoom, -translation);
|
||||
|
||||
SetShadowTransform(aLayer, oldTransform * treeTransform);
|
||||
NS_ASSERTION(!aLayer->AsLayerComposite()->GetShadowTransformSetByAnimation(),
|
||||
"overwriting animated transform!");
|
||||
// Apply the tree transform on top of GetLocalTransform() here (rather than
|
||||
// GetTransform()) in case the OMTA code in SampleAnimations already set a
|
||||
// shadow transform; in that case we want to apply ours on top of that one
|
||||
// rather than clobber it.
|
||||
SetShadowTransform(aLayer, aLayer->GetLocalTransform() * treeTransform);
|
||||
|
||||
// Make sure that overscroll and under-zoom are represented in the old
|
||||
// transform so that fixed position content moves and scales accordingly.
|
||||
|
||||
@@ -651,9 +651,8 @@ nsStringBundleService::CreateExtensibleBundle(const char* aCategory,
|
||||
return res;
|
||||
}
|
||||
|
||||
res = bundle->QueryInterface(NS_GET_IID(nsIStringBundle), (void**) aResult);
|
||||
|
||||
return res;
|
||||
bundle.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#define GLOBAL_PROPERTIES "chrome://global/locale/global-strres.properties"
|
||||
|
||||
@@ -407,6 +407,15 @@ void MessageLoop::ReloadWorkQueue() {
|
||||
}
|
||||
|
||||
bool MessageLoop::DeletePendingTasks() {
|
||||
#ifdef DEBUG
|
||||
if (!work_queue_.empty()) {
|
||||
Task* task = work_queue_.front().task;
|
||||
tracked_objects::Location loc = task->GetBirthPlace();
|
||||
printf("Unexpected task! %s:%s:%d\n",
|
||||
loc.function_name(), loc.file_name(), loc.line_number());
|
||||
}
|
||||
#endif
|
||||
|
||||
MOZ_ASSERT(work_queue_.empty());
|
||||
bool did_work = !deferred_non_nestable_work_queue_.empty();
|
||||
while (!deferred_non_nestable_work_queue_.empty()) {
|
||||
|
||||
@@ -81,6 +81,13 @@ void Tracked::SetBirthPlace(const Location& from_here) {
|
||||
tracked_births_->RecordBirth();
|
||||
}
|
||||
|
||||
Location Tracked::GetBirthPlace() const {
|
||||
if (tracked_births_) {
|
||||
return tracked_births_->location();
|
||||
}
|
||||
return Location();
|
||||
}
|
||||
|
||||
void Tracked::ResetBirthTime() {
|
||||
tracked_birth_time_ = Time::Now();
|
||||
}
|
||||
|
||||
@@ -99,6 +99,7 @@ class Tracked {
|
||||
|
||||
// Used to record the FROM_HERE location of a caller.
|
||||
void SetBirthPlace(const Location& from_here);
|
||||
Location GetBirthPlace() const;
|
||||
|
||||
// When a task sits around a long time, such as in a timer, or object watcher,
|
||||
// this method should be called when the task becomes active, and its
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
function referencesVia(from, edge, to) {
|
||||
if (typeof findReferences !== 'function')
|
||||
return true;
|
||||
|
||||
edge = "edge: " + edge;
|
||||
var edges = findReferences(to);
|
||||
if (edge in edges && edges[edge].indexOf(from) != -1)
|
||||
return true;
|
||||
|
||||
// Be nice: make it easy to fix if the edge name has just changed.
|
||||
var alternatives = [];
|
||||
for (var e in edges) {
|
||||
if (edges[e].indexOf(from) != -1)
|
||||
alternatives.push(e);
|
||||
}
|
||||
if (alternatives.length == 0) {
|
||||
print("referent not referred to by referrer after all");
|
||||
} else {
|
||||
print("referent is not referenced via: " + uneval(edge));
|
||||
print("but it is referenced via: " + uneval(alternatives));
|
||||
}
|
||||
print("all incoming edges, from any object:");
|
||||
for (var e in edges)
|
||||
print(e);
|
||||
return false;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// Binary: cache/js-dbg-64-06445f55f009-linux
|
||||
// Flags:
|
||||
//
|
||||
|
||||
var handler = { "\u0039" : function() {} };
|
||||
var g = newGlobal();
|
||||
if (typeof findReferences == 'function') {
|
||||
findReferences(g);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// Binary: cache/js-dbg-64-ff51ddfdf5d1-linux
|
||||
// Flags:
|
||||
//
|
||||
|
||||
function makeGenerator() {
|
||||
yield function generatorClosure() {};
|
||||
}
|
||||
var generator = makeGenerator();
|
||||
if (typeof findReferences == 'function') {
|
||||
findReferences(generator);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// Clearing a Map removes any strong references to its keys and values.
|
||||
|
||||
load(libdir + "referencesVia.js");
|
||||
|
||||
var m = new Map();
|
||||
var k = {}, v = {};
|
||||
m.set(k, v);
|
||||
assertEq(referencesVia(m, "key", k), true);
|
||||
assertEq(referencesVia(m, "value", v), true);
|
||||
m.clear();
|
||||
if (typeof findReferences == 'function') {
|
||||
assertEq(referencesVia(m, "key", k), false);
|
||||
assertEq(referencesVia(m, "value", v), false);
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// Check marking through the keys of a Map.
|
||||
|
||||
load(libdir + "referencesVia.js");
|
||||
|
||||
var m = new Map;
|
||||
for (var i = 0; i < 20; i++) {
|
||||
var n = new Map;
|
||||
n.set(m, i);
|
||||
assertEq(referencesVia(n, 'key', m), true);
|
||||
m = n;
|
||||
}
|
||||
|
||||
gc();
|
||||
gc();
|
||||
|
||||
// TODO: walk the chain using for-of to make sure everything is still there
|
||||
@@ -1,14 +0,0 @@
|
||||
// Check marking through the values of a Map.
|
||||
|
||||
load(libdir + "referencesVia.js");
|
||||
|
||||
var m = new Map;
|
||||
for (var i = 0; i < 20; i++) {
|
||||
var n = new Map;
|
||||
n.set(i, m);
|
||||
assertEq(referencesVia(n, 'value', m), true);
|
||||
m = n;
|
||||
}
|
||||
|
||||
gc();
|
||||
gc();
|
||||
@@ -1,12 +0,0 @@
|
||||
// Maps do not keep deleted keys or values alive.
|
||||
|
||||
load(libdir + "referencesVia.js");
|
||||
|
||||
var m = new Map;
|
||||
var k = {}, v = {};
|
||||
m.set(k, v);
|
||||
m.delete(k);
|
||||
if (typeof findReferences == 'function') {
|
||||
assertEq(referencesVia(m, 'key', k), false);
|
||||
assertEq(referencesVia(m, 'value', v), false);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// Clearing a Set removes any strong references to its elements.
|
||||
|
||||
load(libdir + "referencesVia.js");
|
||||
|
||||
var s = new Set();
|
||||
var obj = {};
|
||||
s.add(obj);
|
||||
assertEq(referencesVia(s, "key", obj), true);
|
||||
s.clear();
|
||||
if (typeof findReferences == 'function')
|
||||
assertEq(referencesVia(s, "key", obj), false);
|
||||
@@ -1,16 +0,0 @@
|
||||
// Check marking through the elements of a Set.
|
||||
|
||||
load(libdir + "referencesVia.js");
|
||||
|
||||
var s = new Set;
|
||||
for (var i = 0; i < 20; i++) {
|
||||
var t = new Set;
|
||||
t.add(s);
|
||||
assertEq(referencesVia(t, 'key', s), true);
|
||||
s = t;
|
||||
}
|
||||
|
||||
gc();
|
||||
gc();
|
||||
|
||||
// TODO: walk the chain and make sure it's still intact
|
||||
@@ -1,10 +0,0 @@
|
||||
// A Set iterator keeps the data alive.
|
||||
|
||||
load(libdir + "referencesVia.js");
|
||||
load(libdir + "iteration.js");
|
||||
|
||||
var key = {};
|
||||
var set = new Set([key]);
|
||||
var iter = set[Symbol.iterator]();
|
||||
referencesVia(iter, "**UNKNOWN SLOT 0**", set);
|
||||
referencesVia(set, "key", key);
|
||||
@@ -1,16 +0,0 @@
|
||||
// An iterator keeps its data alive.
|
||||
|
||||
load(libdir + "iteration.js");
|
||||
|
||||
load(libdir + "referencesVia.js");
|
||||
var key = {};
|
||||
|
||||
function test(obj, edgeName) {
|
||||
var iter = obj[Symbol.iterator]();
|
||||
referencesVia(iter, "**UNKNOWN SLOT 0**", obj);
|
||||
referencesVia(obj, edgeName, key);
|
||||
}
|
||||
|
||||
test([key], "element[0]");
|
||||
test(new Map([[key, 'value']]), "key");
|
||||
test(new Set([key]), "key");
|
||||
@@ -1,13 +0,0 @@
|
||||
// Array iterators keep the underlying array, arraylike object, or string alive.
|
||||
|
||||
load(libdir + "referencesVia.js");
|
||||
load(libdir + "iteration.js");
|
||||
|
||||
function test(obj) {
|
||||
var it = Array.prototype[Symbol.iterator].call(obj);
|
||||
assertEq(referencesVia(it, "**UNKNOWN SLOT 0**", obj), true);
|
||||
}
|
||||
|
||||
test([]);
|
||||
test([1, 2, 3, 4]);
|
||||
test({});
|
||||
@@ -1,3 +0,0 @@
|
||||
if (typeof findReferences == 'function') {
|
||||
findReferences([schedulegc(1)])
|
||||
}
|
||||
@@ -691,7 +691,15 @@ struct IonScriptCounts
|
||||
for (size_t i = 0; i < numBlocks_; i++)
|
||||
blocks_[i].destroy();
|
||||
js_free(blocks_);
|
||||
js_delete(previous_);
|
||||
// The list can be long in some corner cases (bug 1140084), so
|
||||
// unroll the recursion.
|
||||
IonScriptCounts *victims = previous_;
|
||||
while (victims) {
|
||||
IonScriptCounts *victim = victims;
|
||||
victims = victim->previous_;
|
||||
victim->previous_ = nullptr;
|
||||
js_delete(victim);
|
||||
}
|
||||
}
|
||||
|
||||
bool init(size_t numBlocks) {
|
||||
|
||||
+13
-2
@@ -2398,6 +2398,8 @@ struct ArenasToUpdate
|
||||
bool ArenasToUpdate::shouldProcessKind(AllocKind kind)
|
||||
{
|
||||
MOZ_ASSERT(kind < AllocKind::LIMIT);
|
||||
|
||||
// GC things that do not contain JSObject pointers don't need updating.
|
||||
if (kind == AllocKind::FAT_INLINE_STRING ||
|
||||
kind == AllocKind::STRING ||
|
||||
kind == AllocKind::EXTERNAL_STRING ||
|
||||
@@ -2406,10 +2408,19 @@ bool ArenasToUpdate::shouldProcessKind(AllocKind kind)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (js::gc::IsBackgroundFinalized(kind))
|
||||
// We try to update as many GC things in parallel as we can, but there are
|
||||
// kinds for which this might not be safe:
|
||||
// - we assume JSObjects that are foreground finalized are not safe to
|
||||
// update in parallel
|
||||
// - updating a shape touches child shapes in fixupShapeTreeAfterMovingGC()
|
||||
if (js::gc::IsBackgroundFinalized(kind) &&
|
||||
kind != AllocKind::SHAPE &&
|
||||
kind != AllocKind::ACCESSOR_SHAPE)
|
||||
{
|
||||
return (kinds & BACKGROUND) != 0;
|
||||
else
|
||||
} else {
|
||||
return (kinds & FOREGROUND) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
ArenasToUpdate::ArenasToUpdate(JSRuntime *rt, KindsToUpdate kinds)
|
||||
|
||||
@@ -68,7 +68,6 @@
|
||||
#include "js/StructuredClone.h"
|
||||
#include "js/TrackedOptimizationInfo.h"
|
||||
#include "perf/jsperf.h"
|
||||
#include "shell/jsheaptools.h"
|
||||
#include "shell/jsoptparse.h"
|
||||
#include "shell/OSObject.h"
|
||||
#include "vm/ArgumentsObject.h"
|
||||
@@ -4618,31 +4617,6 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
|
||||
" Tries to print a lot of information about the current stack. \n"
|
||||
" Similar to the DumpJSStack() function in the browser."),
|
||||
|
||||
JS_FN_HELP("findReferences", FindReferences, 1, 0,
|
||||
"findReferences(target)",
|
||||
" Walk the entire heap, looking for references to |target|, and return a\n"
|
||||
" \"references object\" describing what we found.\n"
|
||||
"\n"
|
||||
" Each property of the references object describes one kind of reference. The\n"
|
||||
" property's name is the label supplied to MarkObject, JS_CALL_TRACER, or what\n"
|
||||
" have you, prefixed with \"edge: \" to avoid collisions with system properties\n"
|
||||
" (like \"toString\" and \"__proto__\"). The property's value is an array of things\n"
|
||||
" that refer to |thing| via that kind of reference. Ordinary references from\n"
|
||||
" one object to another are named after the property name (with the \"edge: \"\n"
|
||||
" prefix).\n"
|
||||
"\n"
|
||||
" Garbage collection roots appear as references from 'null'. We use the name\n"
|
||||
" given to the root (with the \"edge: \" prefix) as the name of the reference.\n"
|
||||
"\n"
|
||||
" Note that the references object does record references from objects that are\n"
|
||||
" only reachable via |thing| itself, not just the references reachable\n"
|
||||
" themselves from roots that keep |thing| from being collected. (We could make\n"
|
||||
" this distinction if it is useful.)\n"
|
||||
"\n"
|
||||
" If there are any references on the native stack, the references\n"
|
||||
" object will have properties named like \"edge: exact-value \"; the referrers\n"
|
||||
" will be 'null', because they are roots."),
|
||||
|
||||
#endif
|
||||
JS_FN_HELP("build", BuildDate, 0, 0,
|
||||
"build()",
|
||||
|
||||
@@ -1,577 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "shell/jsheaptools.h"
|
||||
|
||||
#include "mozilla/Move.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "jsalloc.h"
|
||||
#include "jsapi.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jscompartment.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsprf.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
using mozilla::Move;
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
|
||||
/*** class HeapReverser **************************************************************************/
|
||||
|
||||
/*
|
||||
* A class for constructing a map of the JavaScript heap, with all
|
||||
* reference edges reversed.
|
||||
*
|
||||
* Unfortunately, it's not possible to build the results for findReferences
|
||||
* while visiting things solely in the order that js::TraceRuntime and
|
||||
* JS_TraceChildren reaches them. For example, as you work outward from the
|
||||
* roots, suppose an edge from thing T reaches a "gray" thing G --- G being gray
|
||||
* because you're still in the midst of traversing its descendants. At this
|
||||
* point, you don't know yet whether G will be a referrer or not, and so you
|
||||
* can't tell whether T should be a referrer either. And you won't visit T
|
||||
* again.
|
||||
*
|
||||
* So we take a brute-force approach. We reverse the entire graph, and then walk
|
||||
* outward from |target| to the representable objects that refer to it, stopping
|
||||
* at such objects.
|
||||
*/
|
||||
|
||||
/*
|
||||
* A JSTracer that produces a map of the heap with edges reversed.
|
||||
*
|
||||
* HeapReversers must be allocated in a stack frame. (They are derived from
|
||||
* CustomAutoRooter, and those must be allocated and destroyed in a stack-like
|
||||
* order.)
|
||||
*
|
||||
* HeapReversers keep all the roots they find in their traversal alive until
|
||||
* they are destroyed. So you don't need to worry about nodes going away while
|
||||
* you're using them.
|
||||
*/
|
||||
class HeapReverser : public JSTracer, public JS::CustomAutoRooter
|
||||
{
|
||||
public:
|
||||
struct Edge;
|
||||
|
||||
/* Metadata for a given Cell we have visited. */
|
||||
class Node {
|
||||
public:
|
||||
Node() { }
|
||||
explicit Node(JSGCTraceKind kind)
|
||||
: kind(kind), incoming(), marked(false) { }
|
||||
|
||||
/*
|
||||
* Move constructor and move assignment. These allow us to store our
|
||||
* incoming edge Vector in the hash table: Vectors support moves, but
|
||||
* not assignments or copy construction.
|
||||
*/
|
||||
Node(Node&& rhs)
|
||||
: kind(rhs.kind), incoming(Move(rhs.incoming)), marked(rhs.marked) { }
|
||||
Node& operator=(Node&& rhs) {
|
||||
MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
|
||||
this->~Node();
|
||||
new(this) Node(Move(rhs));
|
||||
return *this;
|
||||
}
|
||||
|
||||
void trace(JSTracer* trc) {
|
||||
for (Edge* e = incoming.begin(); e != incoming.end(); e++)
|
||||
e->trace(trc);
|
||||
}
|
||||
|
||||
/* What kind of Cell this is. */
|
||||
JSGCTraceKind kind;
|
||||
|
||||
/*
|
||||
* A vector of this Cell's incoming edges.
|
||||
* This must use SystemAllocPolicy because HashMap requires its elements to
|
||||
* be constructible with no arguments.
|
||||
*/
|
||||
Vector<Edge, 0, SystemAllocPolicy> incoming;
|
||||
|
||||
/* A mark bit, for other traversals. */
|
||||
bool marked;
|
||||
|
||||
private:
|
||||
Node(const Node&) = delete;
|
||||
Node& operator=(const Node&) = delete;
|
||||
};
|
||||
|
||||
/* Metadata for a heap edge we have traversed. */
|
||||
struct Edge {
|
||||
public:
|
||||
Edge(char* name, void* origin) : name(name), origin(origin) { }
|
||||
~Edge() { js_free(name); }
|
||||
|
||||
/*
|
||||
* Move constructor and move assignment. These allow us to live in
|
||||
* Vectors without needing to copy our name string when the vector is
|
||||
* resized.
|
||||
*/
|
||||
Edge(Edge&& rhs) : name(rhs.name), origin(rhs.origin) {
|
||||
rhs.name = nullptr;
|
||||
}
|
||||
Edge& operator=(Edge&& rhs) {
|
||||
MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
|
||||
this->~Edge();
|
||||
new(this) Edge(Move(rhs));
|
||||
return *this;
|
||||
}
|
||||
|
||||
void trace(JSTracer* trc) {
|
||||
if (origin)
|
||||
gc::MarkGCThingRoot(trc, &origin, "HeapReverser::Edge");
|
||||
}
|
||||
|
||||
/* The name of this heap edge. Owned by this Edge. */
|
||||
char* name;
|
||||
|
||||
/*
|
||||
* The Cell from which this edge originates. nullptr means a root. This
|
||||
* is a cell address instead of a Node * because Nodes live in HashMap
|
||||
* table entries; if the HashMap reallocates its table, all pointers to
|
||||
* the Nodes it contains would become invalid. You should look up the
|
||||
* address here in |map| to find its Node.
|
||||
*/
|
||||
void* origin;
|
||||
};
|
||||
|
||||
/*
|
||||
* The result of a reversal is a map from Cells' addresses to Node
|
||||
* structures describing their incoming edges.
|
||||
*/
|
||||
typedef HashMap<void*, Node, DefaultHasher<void*>, SystemAllocPolicy> Map;
|
||||
Map map;
|
||||
|
||||
/* Construct a HeapReverser for |context|'s heap. */
|
||||
explicit HeapReverser(JSContext* cx)
|
||||
: JSTracer(cx->runtime(), traverseEdgeWithThis),
|
||||
JS::CustomAutoRooter(cx),
|
||||
noggc(JS_GetRuntime(cx)),
|
||||
nocgc(JS_GetRuntime(cx)),
|
||||
runtime(JS_GetRuntime(cx)),
|
||||
parent(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
bool init() { return map.init(); }
|
||||
|
||||
/* Build a reversed map of the heap in |map|. */
|
||||
bool reverseHeap();
|
||||
|
||||
private:
|
||||
JS::AutoDisableGenerationalGC noggc;
|
||||
js::AutoDisableCompactingGC nocgc;
|
||||
|
||||
/* A runtime pointer for use by the destructor. */
|
||||
JSRuntime* runtime;
|
||||
|
||||
/*
|
||||
* Return the name of the most recent edge this JSTracer has traversed. The
|
||||
* result is allocated with malloc; if we run out of memory, raise an error
|
||||
* in this HeapReverser's context and return nullptr.
|
||||
*
|
||||
* This may not be called after that edge's call to traverseEdge has
|
||||
* returned.
|
||||
*/
|
||||
char* getEdgeDescription();
|
||||
|
||||
/* Class for setting new parent, and then restoring the original. */
|
||||
class AutoParent {
|
||||
public:
|
||||
AutoParent(HeapReverser* reverser, void* newParent) : reverser(reverser) {
|
||||
savedParent = reverser->parent;
|
||||
reverser->parent = newParent;
|
||||
}
|
||||
~AutoParent() {
|
||||
reverser->parent = savedParent;
|
||||
}
|
||||
private:
|
||||
HeapReverser* reverser;
|
||||
void* savedParent;
|
||||
};
|
||||
|
||||
/* A work item in the stack of nodes whose children we need to traverse. */
|
||||
struct Child {
|
||||
Child(void* cell, JSGCTraceKind kind) : cell(cell), kind(kind) { }
|
||||
void* cell;
|
||||
JSGCTraceKind kind;
|
||||
};
|
||||
|
||||
/*
|
||||
* A stack of work items. We represent the stack explicitly to avoid
|
||||
* overflowing the C++ stack when traversing long chains of objects.
|
||||
*/
|
||||
Vector<Child, 0, SystemAllocPolicy> work;
|
||||
|
||||
/* When traverseEdge is called, the Cell and kind at which the edge originated. */
|
||||
void* parent;
|
||||
|
||||
/* Traverse an edge. */
|
||||
bool traverseEdge(void* cell, JSGCTraceKind kind);
|
||||
|
||||
/*
|
||||
* js::TraceRuntime and JS_TraceChildren don't propagate error returns,
|
||||
* and out-of-memory errors, by design, don't establish an exception in
|
||||
* |context|, so traverseEdgeWithThis uses this to communicate the
|
||||
* result of the traversal to reverseHeap.
|
||||
*/
|
||||
bool traversalStatus;
|
||||
|
||||
/* Static member function wrapping 'traverseEdge'. */
|
||||
static void traverseEdgeWithThis(JSTracer* tracer, void** thingp, JSGCTraceKind kind) {
|
||||
HeapReverser* reverser = static_cast<HeapReverser*>(tracer);
|
||||
if (!reverser->traverseEdge(*thingp, kind))
|
||||
reverser->traversalStatus = false;
|
||||
}
|
||||
|
||||
/* Return a jsval representing a node, if possible; otherwise, return JSVAL_VOID. */
|
||||
jsval nodeToValue(void* cell, int kind) {
|
||||
if (kind != JSTRACE_OBJECT)
|
||||
return JSVAL_VOID;
|
||||
JSObject* object = static_cast<JSObject*>(cell);
|
||||
return OBJECT_TO_JSVAL(object);
|
||||
}
|
||||
|
||||
/* Keep all tracked objects live across GC. */
|
||||
virtual void trace(JSTracer* trc) override {
|
||||
if (!map.initialized())
|
||||
return;
|
||||
for (Map::Enum e(map); !e.empty(); e.popFront()) {
|
||||
gc::MarkGCThingRoot(trc, const_cast<void**>(&e.front().key()), "HeapReverser::map::key");
|
||||
e.front().value().trace(trc);
|
||||
}
|
||||
for (Child* c = work.begin(); c != work.end(); ++c)
|
||||
gc::MarkGCThingRoot(trc, &c->cell, "HeapReverser::Child");
|
||||
}
|
||||
};
|
||||
|
||||
bool
|
||||
HeapReverser::traverseEdge(void* cell, JSGCTraceKind kind)
|
||||
{
|
||||
/* Capture this edge before the JSTracer members get overwritten. */
|
||||
char* edgeDescription = getEdgeDescription();
|
||||
if (!edgeDescription)
|
||||
return false;
|
||||
Edge e(edgeDescription, parent);
|
||||
|
||||
Map::AddPtr a = map.lookupForAdd(cell);
|
||||
if (!a) {
|
||||
/*
|
||||
* We've never visited this cell before. Add it to the map (thus
|
||||
* marking it as visited), and put it on the work stack, to be
|
||||
* visited from the main loop.
|
||||
*/
|
||||
Node n(kind);
|
||||
Generation generation = map.generation();
|
||||
if (!map.add(a, cell, Move(n)) ||
|
||||
!work.append(Child(cell, kind)))
|
||||
return false;
|
||||
/* If the map has been resized, re-check the pointer. */
|
||||
if (map.generation() != generation)
|
||||
a = map.lookupForAdd(cell);
|
||||
}
|
||||
|
||||
/* Add this edge to the reversed map. */
|
||||
return a->value().incoming.append(Move(e));
|
||||
}
|
||||
|
||||
bool
|
||||
HeapReverser::reverseHeap()
|
||||
{
|
||||
traversalStatus = true;
|
||||
|
||||
/* Prime the work stack with the roots of collection. */
|
||||
js::TraceRuntime(this);
|
||||
if (!traversalStatus)
|
||||
return false;
|
||||
|
||||
/* Traverse children until the stack is empty. */
|
||||
while (!work.empty()) {
|
||||
const Child child = work.popCopy();
|
||||
AutoParent autoParent(this, child.cell);
|
||||
JS_TraceChildren(this, child.cell, child.kind);
|
||||
if (!traversalStatus)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
char*
|
||||
HeapReverser::getEdgeDescription()
|
||||
{
|
||||
if (!debugPrinter() && debugPrintIndex() == (size_t) -1) {
|
||||
const char* arg = static_cast<const char*>(debugPrintArg());
|
||||
char* name = js_pod_malloc<char>(strlen(arg) + 1);
|
||||
if (!name)
|
||||
return nullptr;
|
||||
strcpy(name, arg);
|
||||
return name;
|
||||
}
|
||||
|
||||
/* Lovely; but a fixed size is required by JSTraceNamePrinter. */
|
||||
static const int nameSize = 200;
|
||||
char* name = js_pod_malloc<char>(nameSize);
|
||||
if (!name)
|
||||
return nullptr;
|
||||
if (debugPrinter())
|
||||
debugPrinter()(this, name, nameSize);
|
||||
else
|
||||
JS_snprintf(name, nameSize, "%s[%lu]",
|
||||
static_cast<const char*>(debugPrintArg()), debugPrintIndex());
|
||||
|
||||
/* Shrink storage to fit. */
|
||||
return static_cast<char*>(js_realloc(name, strlen(name) + 1));
|
||||
}
|
||||
|
||||
|
||||
/*** class ReferenceFinder ***********************************************************************/
|
||||
|
||||
/* A class for finding an object's referrers, given a reversed heap map. */
|
||||
class ReferenceFinder {
|
||||
public:
|
||||
ReferenceFinder(JSContext* cx, const HeapReverser& reverser)
|
||||
: context(cx), reverser(reverser), result(cx) { }
|
||||
|
||||
/* Produce an object describing all references to |target|. */
|
||||
JSObject* findReferences(HandleObject target);
|
||||
|
||||
private:
|
||||
/* The context in which to do allocation and error-handling. */
|
||||
JSContext* context;
|
||||
|
||||
/* A reversed map of the current heap. */
|
||||
const HeapReverser& reverser;
|
||||
|
||||
/* The results object we're currently building. */
|
||||
RootedObject result;
|
||||
|
||||
/* A list of edges we've traversed to get to a certain point. */
|
||||
class Path {
|
||||
public:
|
||||
Path(const HeapReverser::Edge& edge, Path* next) : edge(edge), next(next) { }
|
||||
|
||||
/*
|
||||
* Compute the full path represented by this Path. The result is
|
||||
* owned by the caller.
|
||||
*/
|
||||
char* computeName(JSContext* cx);
|
||||
|
||||
private:
|
||||
const HeapReverser::Edge& edge;
|
||||
Path* next;
|
||||
};
|
||||
|
||||
struct AutoNodeMarker {
|
||||
explicit AutoNodeMarker(HeapReverser::Node* node) : node(node) { node->marked = true; }
|
||||
~AutoNodeMarker() { node->marked = false; }
|
||||
private:
|
||||
HeapReverser::Node* node;
|
||||
};
|
||||
|
||||
/*
|
||||
* Given that we've reached |cell| via |path|, with all Nodes along that
|
||||
* path marked, add paths from all reportable objects reachable from cell
|
||||
* to |result|.
|
||||
*/
|
||||
bool visit(void* cell, Path* path);
|
||||
|
||||
/*
|
||||
* If |cell|, of |kind|, is representable as a JavaScript value, return that
|
||||
* value; otherwise, return JSVAL_VOID.
|
||||
*/
|
||||
jsval representable(void* cell, int kind) {
|
||||
if (kind == JSTRACE_OBJECT) {
|
||||
JSObject* object = static_cast<JSObject*>(cell);
|
||||
|
||||
/* Certain classes of object are for internal use only. */
|
||||
if (object->is<BlockObject>() ||
|
||||
object->is<CallObject>() ||
|
||||
object->is<StaticWithObject>() ||
|
||||
object->is<DynamicWithObject>() ||
|
||||
object->is<DeclEnvObject>()) {
|
||||
return JSVAL_VOID;
|
||||
}
|
||||
|
||||
/* Internal function objects should also not be revealed. */
|
||||
if (JS_ObjectIsFunction(context, object) && IsInternalFunctionObject(object))
|
||||
return JSVAL_VOID;
|
||||
|
||||
return OBJECT_TO_JSVAL(object);
|
||||
}
|
||||
|
||||
return JSVAL_VOID;
|
||||
}
|
||||
|
||||
/* Add |referrer| as something that refers to |target| via |path|. */
|
||||
bool addReferrer(jsval referrer, Path* path);
|
||||
};
|
||||
|
||||
bool
|
||||
ReferenceFinder::visit(void* cell, Path* path)
|
||||
{
|
||||
/* In ReferenceFinder, paths will almost certainly fit on the C++ stack. */
|
||||
JS_CHECK_RECURSION(context, return false);
|
||||
|
||||
/* Have we reached a root? Always report that. */
|
||||
if (!cell)
|
||||
return addReferrer(JSVAL_NULL, path);
|
||||
|
||||
HeapReverser::Map::Ptr p = reverser.map.lookup(cell);
|
||||
MOZ_ASSERT(p);
|
||||
HeapReverser::Node* node = &p->value();
|
||||
|
||||
/* Is |cell| a representable cell, reached via a non-empty path? */
|
||||
if (path != nullptr) {
|
||||
jsval representation = representable(cell, node->kind);
|
||||
if (!representation.isUndefined())
|
||||
return addReferrer(representation, path);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we've made a cycle, don't traverse further. We *do* want to include
|
||||
* paths from the target to itself, so we don't want to do this check until
|
||||
* after we've possibly reported this cell as a referrer.
|
||||
*/
|
||||
if (node->marked)
|
||||
return true;
|
||||
AutoNodeMarker marker(node);
|
||||
|
||||
/* Visit the origins of all |cell|'s incoming edges. */
|
||||
for (size_t i = 0; i < node->incoming.length(); i++) {
|
||||
const HeapReverser::Edge& edge = node->incoming[i];
|
||||
Path extendedPath(edge, path);
|
||||
if (!visit(edge.origin, &extendedPath))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
char*
|
||||
ReferenceFinder::Path::computeName(JSContext* cx)
|
||||
{
|
||||
/* Walk the edge list and compute the total size of the path. */
|
||||
size_t size = 6;
|
||||
for (Path* l = this; l; l = l->next)
|
||||
size += strlen(l->edge.name) + (l->next ? 2 : 0);
|
||||
size += 1;
|
||||
|
||||
char* path = cx->pod_malloc<char>(size);
|
||||
if (!path)
|
||||
return nullptr;
|
||||
|
||||
/*
|
||||
* Walk the edge list again, and copy the edge names into place, with
|
||||
* appropriate separators. Note that we constructed the edge list from
|
||||
* target to referrer, which means that the list links point *towards* the
|
||||
* target, so we can walk the list and build the path from left to right.
|
||||
*/
|
||||
strcpy(path, "edge: ");
|
||||
char* next = path + 6;
|
||||
for (Path* l = this; l; l = l->next) {
|
||||
strcpy(next, l->edge.name);
|
||||
next += strlen(next);
|
||||
if (l->next) {
|
||||
strcpy(next, "; ");
|
||||
next += 2;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(next + 1 == path + size);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
bool
|
||||
ReferenceFinder::addReferrer(jsval referrerArg, Path* path)
|
||||
{
|
||||
RootedValue referrer(context, referrerArg);
|
||||
|
||||
if (!context->compartment()->wrap(context, &referrer))
|
||||
return false;
|
||||
|
||||
ScopedJSFreePtr<char> pathName(path->computeName(context));
|
||||
if (!pathName)
|
||||
return false;
|
||||
|
||||
/* Find the property of the results object named |pathName|. */
|
||||
RootedValue v(context);
|
||||
|
||||
if (!JS_GetProperty(context, result, pathName, &v))
|
||||
return false;
|
||||
if (v.isUndefined()) {
|
||||
/* Create an array to accumulate referents under this path. */
|
||||
JSObject* array = JS_NewArrayObject(context, HandleValueArray(referrer));
|
||||
if (!array)
|
||||
return false;
|
||||
v.setObject(*array);
|
||||
return !!JS_SetProperty(context, result, pathName, v);
|
||||
}
|
||||
|
||||
/* The property's value had better be an array. */
|
||||
RootedObject array(context, &v.toObject());
|
||||
MOZ_ASSERT(JS_IsArrayObject(context, array));
|
||||
|
||||
/* Append our referrer to this array. */
|
||||
uint32_t length;
|
||||
return JS_GetArrayLength(context, array, &length) &&
|
||||
JS_SetElement(context, array, length, referrer);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
ReferenceFinder::findReferences(HandleObject target)
|
||||
{
|
||||
result = JS_NewPlainObject(context);
|
||||
if (!result)
|
||||
return nullptr;
|
||||
if (!visit(target, nullptr))
|
||||
return nullptr;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* See help(findReferences). */
|
||||
bool
|
||||
FindReferences(JSContext* cx, unsigned argc, jsval* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (args.length() < 1) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
|
||||
"findReferences", "0", "s");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedValue target(cx, args[0]);
|
||||
if (!target.isObject()) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
|
||||
"argument", "not an object");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Walk the JSRuntime, producing a reversed map of the heap. */
|
||||
HeapReverser reverser(cx);
|
||||
if (!reverser.init() || !reverser.reverseHeap())
|
||||
return false;
|
||||
|
||||
/* Given the reversed map, find the referents of target. */
|
||||
ReferenceFinder finder(cx, reverser);
|
||||
Rooted<JSObject*> targetObj(cx, &target.toObject());
|
||||
JSObject* references = finder.findReferences(targetObj);
|
||||
if (!references)
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*references);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* DEBUG */
|
||||
@@ -1,18 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef shell_jsheaptools_h
|
||||
#define shell_jsheaptools_h
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
bool FindReferences(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
||||
#endif /* shell_jsheaptools_h */
|
||||
@@ -12,7 +12,6 @@ if CONFIG['JS_SHELL_NAME']:
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'js.cpp',
|
||||
'jsheaptools.cpp',
|
||||
'jsoptparse.cpp',
|
||||
'OSObject.cpp'
|
||||
]
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 422269;
|
||||
var summary = 'Compile-time let block should not capture runtime references';
|
||||
var actual = 'referenced only by stack and closure';
|
||||
var expect = 'referenced only by stack and closure';
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
test();
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function test()
|
||||
{
|
||||
enterFunc ('test');
|
||||
printBugNumber(BUGNUMBER);
|
||||
printStatus (summary);
|
||||
|
||||
function f()
|
||||
{
|
||||
let m = {sin: Math.sin};
|
||||
(function holder() { m.sin(1); })();
|
||||
return m;
|
||||
}
|
||||
|
||||
if (typeof findReferences == 'undefined')
|
||||
{
|
||||
expect = actual = 'Test skipped';
|
||||
print('Test skipped. Requires findReferences function.');
|
||||
}
|
||||
else
|
||||
{
|
||||
var x = f();
|
||||
var refs = findReferences(x);
|
||||
|
||||
// At this point, x should only be referenced from the stack --- the
|
||||
// variable 'x' itself, and any random things the conservative scanner
|
||||
// finds --- and possibly from the 'holder' closure, which could itself
|
||||
// be held alive for random reasons. Remove those from the refs list, and
|
||||
// then complain if anything is left.
|
||||
for (var edge in refs) {
|
||||
// Remove references from roots, like the stack.
|
||||
if (refs[edge].every(function (r) r === null))
|
||||
delete refs[edge];
|
||||
// Remove references from the closure, which could be held alive for
|
||||
// random reasons.
|
||||
else if (refs[edge].length === 1 &&
|
||||
typeof refs[edge][0] === "function" &&
|
||||
refs[edge][0].name === "holder")
|
||||
delete refs[edge];
|
||||
}
|
||||
|
||||
if (Object.keys(refs).length != 0)
|
||||
actual = "unexpected references to the result of f: " + Object.keys(refs).join(", ");
|
||||
}
|
||||
reportCompare(expect, actual, summary);
|
||||
|
||||
exitFunc ('test');
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/licenses/publicdomain/
|
||||
// Contributor: Jim Blandy
|
||||
|
||||
if (typeof findReferences == "function") {
|
||||
function C() {}
|
||||
var o = new C;
|
||||
o.x = {}; // via ordinary property
|
||||
o[42] = {}; // via numeric property
|
||||
o[123456789] = {}; // via ridiculous numeric property
|
||||
o.myself = o; // self-references should be reported
|
||||
o.alsoMyself = o; // multiple self-references should all be reported
|
||||
|
||||
assertEq(referencesVia(o, 'group; group_proto', C.prototype), true);
|
||||
assertEq(referencesVia(o, 'shape; base; parent', this), true);
|
||||
assertEq(referencesVia(o, 'x', o.x), true);
|
||||
assertEq(referencesVia(o, 'objectElements[42]', o[42]), true);
|
||||
assertEq(referencesVia(o, '123456789', o[123456789]), true);
|
||||
assertEq(referencesVia(o, 'myself', o), true);
|
||||
assertEq(referencesVia(o, 'alsoMyself', o), true);
|
||||
|
||||
function g() { return 42; }
|
||||
function s(v) { }
|
||||
var p = Object.defineProperty({}, 'a', { get:g, set:s });
|
||||
assertEq(referencesVia(p, 'shape; getter', g), true);
|
||||
assertEq(referencesVia(p, 'shape; setter', s), true);
|
||||
|
||||
// If there are multiple objects with the same shape referring to a getter
|
||||
// or setter, findReferences should get all of them, even though the shape
|
||||
// gets 'marked' the first time we visit it.
|
||||
var q = Object.defineProperty({}, 'a', { get:g, set:s });
|
||||
assertEq(referencesVia(p, 'shape; getter', g), true);
|
||||
assertEq(referencesVia(q, 'shape; getter', g), true);
|
||||
|
||||
// If we extend each object's shape chain, both should still be able to
|
||||
// reach the getter, even though the two shapes are each traversed twice.
|
||||
p.b = 9;
|
||||
q.b = 9;
|
||||
assertEq(referencesVia(p, 'shape; parent; getter', g), true);
|
||||
assertEq(referencesVia(q, 'shape; parent; getter', g), true);
|
||||
|
||||
// These are really just ordinary own property references.
|
||||
assertEq(referencesVia(C, 'prototype', Object.getPrototypeOf(o)), true);
|
||||
assertEq(referencesVia(Object.getPrototypeOf(o), 'constructor', C), true);
|
||||
|
||||
// Dense arrays should work, too.
|
||||
a = [];
|
||||
a[1] = o;
|
||||
assertEq(referencesVia(a, 'objectElements[1]', o), true);
|
||||
|
||||
reportCompare(true, true);
|
||||
} else {
|
||||
reportCompare(true, true, "test skipped: findReferences is not a function");
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/licenses/publicdomain/
|
||||
// Contributor: Jim Blandy
|
||||
|
||||
if (typeof findReferences == "function") {
|
||||
(function f() {
|
||||
assertEq(referencesVia(arguments, 'callee', f), true);
|
||||
})();
|
||||
|
||||
var o = ({});
|
||||
|
||||
function returnHeavy(y) { eval(''); Math.sin(); return function heavy() { return y; }; }
|
||||
assertEq(referencesVia(returnHeavy(o), 'fun_environment; y', o), true);
|
||||
assertEq(referencesVia(returnHeavy(o), 'fun_environment; enclosing_environment', this), true);
|
||||
|
||||
function returnBlock(z) { eval(''); let(w = z) { return function block() { return w; }; }; }
|
||||
assertEq(referencesVia(returnBlock(o), 'fun_environment; w', o), true);
|
||||
|
||||
function returnWithObj(v) { with(v) return function withObj() { return u; }; }
|
||||
assertEq(referencesVia(returnWithObj(o), 'fun_environment; with_object', o), true);
|
||||
|
||||
reportCompare(true, true);
|
||||
} else {
|
||||
reportCompare(true, true, "test skipped: findReferences is not a function");
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/licenses/publicdomain/
|
||||
// Contributor: Jim Blandy
|
||||
|
||||
if (typeof findReferences == "function") {
|
||||
|
||||
function makeGenerator(c) { eval(c); yield function generatorClosure() { return x; }; }
|
||||
var generator = makeGenerator('var x = 42');
|
||||
var closure = generator.next();
|
||||
referencesVia(closure, 'parent; generator object', generator);
|
||||
|
||||
var o = {};
|
||||
|
||||
assertEq(function f() {
|
||||
return referencesVia(null, 'arguments', arguments) ||
|
||||
referencesVia(null, 'baseline-args-obj', arguments);
|
||||
}(), true);
|
||||
|
||||
var rvalueCorrect;
|
||||
|
||||
function finallyHoldsRval() {
|
||||
try {
|
||||
return o;
|
||||
} finally {
|
||||
rvalueCorrect = referencesVia(null, 'rval', o) ||
|
||||
referencesVia(null, 'baseline-rval', o);
|
||||
}
|
||||
}
|
||||
rvalueCorrect = false;
|
||||
finallyHoldsRval();
|
||||
assertEq(rvalueCorrect, true);
|
||||
|
||||
// Because we don't distinguish between JavaScript stack marking and C++
|
||||
// stack marking (both use the conservative scanner), we can't really write
|
||||
// the following tests meaningfully:
|
||||
// generator frame -> generator object
|
||||
// stack frame -> local variables
|
||||
// stack frame -> this
|
||||
// stack frame -> callee
|
||||
// for(... in x) loop's reference to x
|
||||
|
||||
reportCompare(true, true);
|
||||
} else {
|
||||
reportCompare(true, true, "test skipped: findReferences is not a function");
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/licenses/publicdomain/
|
||||
// Contributor: Jim Blandy
|
||||
|
||||
if (typeof findReferences == "function") {
|
||||
|
||||
var global = newGlobal();
|
||||
var o = ({});
|
||||
global.o = o;
|
||||
|
||||
// Don't trip a cross-compartment reference assertion.
|
||||
findReferences(o);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
||||
} else {
|
||||
reportCompare(true, true, "test skipped: findReferences is not a function");
|
||||
}
|
||||
@@ -190,30 +190,6 @@ var Match =
|
||||
|
||||
})();
|
||||
|
||||
function referencesVia(from, edge, to) {
|
||||
edge = "edge: " + edge;
|
||||
var edges = findReferences(to);
|
||||
if (edge in edges && edges[edge].indexOf(from) != -1)
|
||||
return true;
|
||||
|
||||
// Be nice: make it easy to fix if the edge name has just changed.
|
||||
var alternatives = [];
|
||||
for (var e in edges) {
|
||||
if (edges[e].indexOf(from) != -1)
|
||||
alternatives.push(e);
|
||||
}
|
||||
if (alternatives.length == 0) {
|
||||
print("referent not referred to by referrer after all");
|
||||
} else {
|
||||
print("referent is not referenced via: " + uneval(edge));
|
||||
print("but it is referenced via: " + uneval(alternatives));
|
||||
}
|
||||
print("all incoming edges, from any object:");
|
||||
for (var e in edges)
|
||||
print(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note that AsmJS ArrayBuffers have a minimum size, currently 4096 bytes. If a
|
||||
// smaller size is given, a regular ArrayBuffer will be returned instead.
|
||||
function AsmJSArrayBuffer(size) {
|
||||
|
||||
@@ -1523,6 +1523,27 @@ FrameLayerBuilder::GetDebugOldLayerFor(nsIFrame* aFrame, uint32_t aDisplayItemKe
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* static */ Layer*
|
||||
FrameLayerBuilder::GetDebugSingleOldLayerForFrame(nsIFrame* aFrame)
|
||||
{
|
||||
nsTArray<DisplayItemData*>* array =
|
||||
reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty()));
|
||||
|
||||
if (!array) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Layer* layer = nullptr;
|
||||
for (DisplayItemData* data : *array) {
|
||||
if (layer && layer != data->mLayer) {
|
||||
// More than one layer assigned, bail.
|
||||
return nullptr;
|
||||
}
|
||||
layer = data->mLayer;
|
||||
}
|
||||
return layer;
|
||||
}
|
||||
|
||||
already_AddRefed<ColorLayer>
|
||||
ContainerState::CreateOrRecycleColorLayer(PaintedLayer *aPainted)
|
||||
{
|
||||
@@ -4264,8 +4285,9 @@ FrameLayerBuilder::GetPaintedLayerScaleForFrame(nsIFrame* aFrame)
|
||||
}
|
||||
}
|
||||
|
||||
float presShellResolution = last->PresContext()->PresShell()->GetResolution();
|
||||
return PredictScaleForContent(aFrame, last,
|
||||
last->PresContext()->PresShell()->GetResolution());
|
||||
gfxSize(presShellResolution, presShellResolution));
|
||||
}
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
|
||||
@@ -335,6 +335,14 @@ public:
|
||||
|
||||
static Layer* GetDebugOldLayerFor(nsIFrame* aFrame, uint32_t aDisplayItemKey);
|
||||
|
||||
/**
|
||||
* Return the layer that all display items of aFrame were assigned to in the
|
||||
* last paint, or nullptr if there was no single layer assigned to all of the
|
||||
* frame's display items (i.e. zero, or more than one).
|
||||
* This function is for testing purposes and not performance sensitive.
|
||||
*/
|
||||
static Layer* GetDebugSingleOldLayerForFrame(nsIFrame* aFrame);
|
||||
|
||||
/**
|
||||
* Destroy any stored LayerManagerDataProperty and the associated data for
|
||||
* aFrame.
|
||||
|
||||
@@ -408,16 +408,16 @@ IsElementClickableAndReadable(nsIFrame* aFrame, WidgetGUIEvent* aEvent, const Ev
|
||||
nsSize frameSize = aFrame->GetSize();
|
||||
nsPresContext* pc = aFrame->PresContext();
|
||||
nsIPresShell* presShell = pc->PresShell();
|
||||
gfxSize cumulativeResolution = presShell->GetCumulativeResolution();
|
||||
if ((pc->AppUnitsToGfxUnits(frameSize.height) * cumulativeResolution.height) < limitReadableSize ||
|
||||
(pc->AppUnitsToGfxUnits(frameSize.width) * cumulativeResolution.width) < limitReadableSize) {
|
||||
float cumulativeResolution = presShell->GetCumulativeResolution();
|
||||
if ((pc->AppUnitsToGfxUnits(frameSize.height) * cumulativeResolution) < limitReadableSize ||
|
||||
(pc->AppUnitsToGfxUnits(frameSize.width) * cumulativeResolution) < limitReadableSize) {
|
||||
return false;
|
||||
}
|
||||
nsRefPtr<nsFontMetrics> fm;
|
||||
nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm),
|
||||
nsLayoutUtils::FontSizeInflationFor(aFrame));
|
||||
if (fm) {
|
||||
if ((pc->AppUnitsToGfxUnits(fm->EmHeight()) * cumulativeResolution.height) < limitReadableSize) {
|
||||
if ((pc->AppUnitsToGfxUnits(fm->EmHeight()) * cumulativeResolution) < limitReadableSize) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -800,7 +800,7 @@ nsDisplayScrollLayer::ComputeFrameMetrics(nsIFrame* aForFrame,
|
||||
// Only the root scrollable frame for a given presShell should pick up
|
||||
// the presShell's resolution. All the other frames are 1.0.
|
||||
if (aScrollFrame == presShell->GetRootScrollFrame()) {
|
||||
metrics.SetPresShellResolution(presShell->GetXResolution());
|
||||
metrics.SetPresShellResolution(presShell->GetResolution());
|
||||
} else {
|
||||
metrics.SetPresShellResolution(1.0f);
|
||||
}
|
||||
@@ -886,10 +886,10 @@ nsDisplayScrollLayer::ComputeFrameMetrics(nsIFrame* aForFrame,
|
||||
} else {
|
||||
LayoutDeviceIntSize contentSize;
|
||||
if (nsLayoutUtils::GetContentViewerSize(presContext, contentSize)) {
|
||||
LayoutDeviceToParentLayerScale2D scale;
|
||||
LayoutDeviceToParentLayerScale scale;
|
||||
if (presContext->GetParentPresContext()) {
|
||||
gfxSize res = presContext->GetParentPresContext()->PresShell()->GetCumulativeResolution();
|
||||
scale = LayoutDeviceToParentLayerScale2D(res.width, res.height);
|
||||
float res = presContext->GetParentPresContext()->PresShell()->GetCumulativeResolution();
|
||||
scale = LayoutDeviceToParentLayerScale(res);
|
||||
}
|
||||
metrics.mCompositionBounds.SizeTo(contentSize * scale);
|
||||
}
|
||||
@@ -1639,7 +1639,7 @@ already_AddRefed<LayerManager> nsDisplayList::PaintRoot(nsDisplayListBuilder* aB
|
||||
}
|
||||
|
||||
ContainerLayerParameters containerParameters
|
||||
(presShell->GetXResolution(), presShell->GetYResolution());
|
||||
(presShell->GetResolution(), presShell->GetResolution());
|
||||
nsRefPtr<ContainerLayer> root = layerBuilder->
|
||||
BuildContainerLayerFor(aBuilder, layerManager, frame, nullptr, this,
|
||||
containerParameters, nullptr);
|
||||
@@ -4222,8 +4222,8 @@ nsDisplaySubDocument::ComputeFrameMetrics(Layer* aLayer,
|
||||
bool isRootContentDocument = presContext->IsRootContentDocument();
|
||||
nsIPresShell* presShell = presContext->PresShell();
|
||||
ContainerLayerParameters params(
|
||||
aContainerParameters.mXScale * presShell->GetXResolution(),
|
||||
aContainerParameters.mYScale * presShell->GetYResolution(),
|
||||
aContainerParameters.mXScale * presShell->GetResolution(),
|
||||
aContainerParameters.mYScale * presShell->GetResolution(),
|
||||
nsIntPoint(), aContainerParameters);
|
||||
if ((mFlags & GENERATE_SCROLLABLE_LAYER) &&
|
||||
rootScrollFrame->GetContent() &&
|
||||
@@ -4345,15 +4345,15 @@ nsDisplayResolution::BuildLayer(nsDisplayListBuilder* aBuilder,
|
||||
const ContainerLayerParameters& aContainerParameters) {
|
||||
nsIPresShell* presShell = mFrame->PresContext()->PresShell();
|
||||
ContainerLayerParameters containerParameters(
|
||||
presShell->GetXResolution(), presShell->GetYResolution(), nsIntPoint(),
|
||||
presShell->GetResolution(), presShell->GetResolution(), nsIntPoint(),
|
||||
aContainerParameters);
|
||||
|
||||
nsRefPtr<Layer> layer = nsDisplaySubDocument::BuildLayer(
|
||||
aBuilder, aManager, containerParameters);
|
||||
layer->SetPostScale(1.0f / presShell->GetXResolution(),
|
||||
1.0f / presShell->GetYResolution());
|
||||
layer->SetPostScale(1.0f / presShell->GetResolution(),
|
||||
1.0f / presShell->GetResolution());
|
||||
layer->AsContainerLayer()->SetScaleToResolution(
|
||||
presShell->ScaleToResolution(), presShell->GetXResolution());
|
||||
presShell->ScaleToResolution(), presShell->GetResolution());
|
||||
return layer.forget();
|
||||
}
|
||||
|
||||
|
||||
@@ -1381,17 +1381,15 @@ public:
|
||||
*
|
||||
* The resolution defaults to 1.0.
|
||||
*/
|
||||
virtual nsresult SetResolution(float aXResolution, float aYResolution) = 0;
|
||||
gfxSize GetResolution() { return gfxSize(mXResolution, mYResolution); }
|
||||
float GetXResolution() { return mXResolution; }
|
||||
float GetYResolution() { return mYResolution; }
|
||||
virtual gfxSize GetCumulativeResolution() = 0;
|
||||
virtual nsresult SetResolution(float aResolution) = 0;
|
||||
float GetResolution() { return mResolution; }
|
||||
virtual float GetCumulativeResolution() = 0;
|
||||
|
||||
/**
|
||||
* Similar to SetResolution() but also increases the scale of the content
|
||||
* by the same amount.
|
||||
*/
|
||||
virtual nsresult SetResolutionAndScaleTo(float aXResolution, float aYResolution) = 0;
|
||||
virtual nsresult SetResolutionAndScaleTo(float aResolution) = 0;
|
||||
|
||||
/**
|
||||
* Return whether we are scaling to the set resolution.
|
||||
@@ -1719,9 +1717,8 @@ protected:
|
||||
nscolor mCanvasBackgroundColor;
|
||||
|
||||
// Used to force allocation and rendering of proportionally more or
|
||||
// less pixels in the given dimension.
|
||||
float mXResolution;
|
||||
float mYResolution;
|
||||
// less pixels in both dimensions.
|
||||
float mResolution;
|
||||
|
||||
int16_t mSelectionFlags;
|
||||
|
||||
|
||||
@@ -892,9 +892,9 @@ GetDisplayPortFromMarginsData(nsIContent* aContent,
|
||||
if (isRoot) {
|
||||
// the base rect for root scroll frames is specified in the parent document
|
||||
// coordinate space, so it doesn't include the local resolution.
|
||||
gfxSize localRes = presContext->PresShell()->GetResolution();
|
||||
parentRes.xScale /= localRes.width;
|
||||
parentRes.yScale /= localRes.height;
|
||||
float localRes = presContext->PresShell()->GetResolution();
|
||||
parentRes.xScale /= localRes;
|
||||
parentRes.yScale /= localRes;
|
||||
}
|
||||
ScreenRect screenRect = LayoutDeviceRect::FromAppUnits(base, auPerDevPixel)
|
||||
* parentRes;
|
||||
@@ -2818,7 +2818,7 @@ CalculateFrameMetricsForDisplayPort(nsIScrollableFrame* aScrollFrame) {
|
||||
if (frame == presShell->GetRootScrollFrame()) {
|
||||
// Only the root scrollable frame for a given presShell should pick up
|
||||
// the presShell's resolution. All the other frames are 1.0.
|
||||
resolution = presShell->GetXResolution();
|
||||
resolution = presShell->GetResolution();
|
||||
}
|
||||
// Note: unlike in ComputeFrameMetrics(), we don't know the full cumulative
|
||||
// resolution including FrameMetrics::mExtraResolution, because layout hasn't
|
||||
@@ -2842,8 +2842,9 @@ CalculateFrameMetricsForDisplayPort(nsIScrollableFrame* aScrollFrame) {
|
||||
LayoutDeviceToParentLayerScale2D compBoundsScale;
|
||||
if (frame == presShell->GetRootScrollFrame() && presContext->IsRootContentDocument()) {
|
||||
if (presContext->GetParentPresContext()) {
|
||||
gfxSize res = presContext->GetParentPresContext()->PresShell()->GetCumulativeResolution();
|
||||
compBoundsScale = LayoutDeviceToParentLayerScale2D(res.width, res.height);
|
||||
float res = presContext->GetParentPresContext()->PresShell()->GetCumulativeResolution();
|
||||
compBoundsScale = LayoutDeviceToParentLayerScale2D(
|
||||
LayoutDeviceToParentLayerScale(res));
|
||||
}
|
||||
} else {
|
||||
compBoundsScale = cumulativeResolution * layerToParentLayerScale;
|
||||
@@ -7638,12 +7639,12 @@ nsLayoutUtils::CalculateCompositionSizeForFrame(nsIFrame* aFrame)
|
||||
widgetBounds.height * auPerDevPixel);
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
nsRect frameRect = aFrame->GetRect();
|
||||
gfxSize cumulativeResolution = presShell->GetCumulativeResolution();
|
||||
LayoutDeviceToParentLayerScale2D layoutToParentLayerScale =
|
||||
float cumulativeResolution = presShell->GetCumulativeResolution();
|
||||
LayoutDeviceToParentLayerScale layoutToParentLayerScale =
|
||||
// The ScreenToParentLayerScale should be mTransformScale which is
|
||||
// not calculated yet, but we don't yet handle CSS transforms, so we
|
||||
// assume it's 1 here.
|
||||
LayoutDeviceToLayerScale2D(cumulativeResolution.width, cumulativeResolution.height) *
|
||||
LayoutDeviceToLayerScale(cumulativeResolution) *
|
||||
LayerToScreenScale(1.0) * ScreenToParentLayerScale(1.0);
|
||||
ParentLayerRect frameRectPixels =
|
||||
LayoutDeviceRect::FromAppUnits(frameRect, auPerDevPixel)
|
||||
@@ -7652,7 +7653,7 @@ nsLayoutUtils::CalculateCompositionSizeForFrame(nsIFrame* aFrame)
|
||||
// Our return value is in appunits of the parent, so we need to
|
||||
// include the resolution.
|
||||
size.height =
|
||||
NSToCoordRound(frameRect.height * cumulativeResolution.height);
|
||||
NSToCoordRound(frameRect.height * cumulativeResolution);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
@@ -7699,9 +7700,9 @@ nsLayoutUtils::CalculateRootCompositionSize(nsIFrame* aFrame,
|
||||
// TODO: Reuse that code here.
|
||||
nsIPresShell* rootPresShell = rootPresContext->PresShell();
|
||||
if (nsIFrame* rootFrame = rootPresShell->GetRootFrame()) {
|
||||
LayoutDeviceToLayerScale cumulativeResolution(
|
||||
rootPresShell->GetCumulativeResolution().width
|
||||
* nsLayoutUtils::GetTransformToAncestorScale(rootFrame).width);
|
||||
LayoutDeviceToLayerScale2D cumulativeResolution(
|
||||
rootPresShell->GetCumulativeResolution()
|
||||
* nsLayoutUtils::GetTransformToAncestorScale(rootFrame));
|
||||
int32_t rootAUPerDevPixel = rootPresContext->AppUnitsPerDevPixel();
|
||||
LayerSize frameSize =
|
||||
(LayoutDeviceRect::FromAppUnits(rootFrame->GetRect(), rootAUPerDevPixel)
|
||||
@@ -7725,10 +7726,10 @@ nsLayoutUtils::CalculateRootCompositionSize(nsIFrame* aFrame,
|
||||
} else {
|
||||
LayoutDeviceIntSize contentSize;
|
||||
if (nsLayoutUtils::GetContentViewerSize(rootPresContext, contentSize)) {
|
||||
LayoutDeviceToLayerScale2D scale;
|
||||
LayoutDeviceToLayerScale scale;
|
||||
if (rootPresContext->GetParentPresContext()) {
|
||||
gfxSize res = rootPresContext->GetParentPresContext()->PresShell()->GetCumulativeResolution();
|
||||
scale = LayoutDeviceToLayerScale2D(res.width, res.height);
|
||||
float res = rootPresContext->GetParentPresContext()->PresShell()->GetCumulativeResolution();
|
||||
scale = LayoutDeviceToLayerScale(res);
|
||||
}
|
||||
rootCompositionSize = contentSize * scale * LayerToScreenScale(1.0f);
|
||||
}
|
||||
@@ -7800,9 +7801,9 @@ nsLayoutUtils::CalculateExpandedScrollableRect(nsIFrame* aFrame)
|
||||
if (aFrame == aFrame->PresContext()->PresShell()->GetRootScrollFrame()) {
|
||||
// the composition size for the root scroll frame does not include the
|
||||
// local resolution, so we adjust.
|
||||
gfxSize res = aFrame->PresContext()->PresShell()->GetResolution();
|
||||
compSize.width = NSToCoordRound(compSize.width / ((float) res.width));
|
||||
compSize.height = NSToCoordRound(compSize.height / ((float) res.height));
|
||||
float res = aFrame->PresContext()->PresShell()->GetResolution();
|
||||
compSize.width = NSToCoordRound(compSize.width / res);
|
||||
compSize.height = NSToCoordRound(compSize.height / res);
|
||||
}
|
||||
|
||||
if (scrollableRect.width < compSize.width) {
|
||||
|
||||
+15
-12
@@ -777,8 +777,7 @@ PresShell::PresShell()
|
||||
mPresArenaAllocCount = 0;
|
||||
#endif
|
||||
mRenderFlags = 0;
|
||||
mXResolution = 1.0;
|
||||
mYResolution = 1.0;
|
||||
mResolution = 1.0;
|
||||
mViewportOverridden = false;
|
||||
|
||||
mScrollPositionClampingScrollPortSizeSet = false;
|
||||
@@ -5544,17 +5543,16 @@ void PresShell::SetIgnoreViewportScrolling(bool aIgnore)
|
||||
SetRenderingState(state);
|
||||
}
|
||||
|
||||
nsresult PresShell::SetResolutionImpl(float aXResolution, float aYResolution, bool aScaleToResolution)
|
||||
nsresult PresShell::SetResolutionImpl(float aResolution, bool aScaleToResolution)
|
||||
{
|
||||
if (!(aXResolution > 0.0 && aYResolution > 0.0)) {
|
||||
if (!(aResolution > 0.0)) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
if (aXResolution == mXResolution && aYResolution == mYResolution) {
|
||||
if (aResolution == mResolution) {
|
||||
return NS_OK;
|
||||
}
|
||||
RenderingState state(this);
|
||||
state.mXResolution = aXResolution;
|
||||
state.mYResolution = aYResolution;
|
||||
state.mResolution = aResolution;
|
||||
SetRenderingState(state);
|
||||
mScaleToResolution = aScaleToResolution;
|
||||
|
||||
@@ -5566,12 +5564,12 @@ bool PresShell::ScaleToResolution() const
|
||||
return mScaleToResolution;
|
||||
}
|
||||
|
||||
gfxSize PresShell::GetCumulativeResolution()
|
||||
float PresShell::GetCumulativeResolution()
|
||||
{
|
||||
gfxSize resolution = GetResolution();
|
||||
float resolution = GetResolution();
|
||||
nsPresContext* parentCtx = GetPresContext()->GetParentPresContext();
|
||||
if (parentCtx) {
|
||||
resolution = resolution * parentCtx->PresShell()->GetCumulativeResolution();
|
||||
resolution *= parentCtx->PresShell()->GetCumulativeResolution();
|
||||
}
|
||||
return resolution;
|
||||
}
|
||||
@@ -5588,8 +5586,7 @@ void PresShell::SetRenderingState(const RenderingState& aState)
|
||||
}
|
||||
|
||||
mRenderFlags = aState.mRenderFlags;
|
||||
mXResolution = aState.mXResolution;
|
||||
mYResolution = aState.mYResolution;
|
||||
mResolution = aState.mResolution;
|
||||
}
|
||||
|
||||
void PresShell::SynthesizeMouseMove(bool aFromScroll)
|
||||
@@ -7414,6 +7411,12 @@ PresShell::HandleEvent(nsIFrame* aFrame,
|
||||
frame == mFrameConstructor->GetRootFrame()) {
|
||||
nsIFrame* popupFrame =
|
||||
nsLayoutUtils::GetPopupFrameForEventCoordinates(rootPresContext, aEvent);
|
||||
// If a remote browser is currently capturing input break out if we
|
||||
// detect a chrome generated popup.
|
||||
if (popupFrame && capturingContent &&
|
||||
EventStateManager::IsRemoteTarget(capturingContent)) {
|
||||
capturingContent = nullptr;
|
||||
}
|
||||
// If the popupFrame is an ancestor of the 'frame', the frame should
|
||||
// handle the event, otherwise, the popup should handle it.
|
||||
if (popupFrame &&
|
||||
|
||||
@@ -199,14 +199,14 @@ public:
|
||||
|
||||
virtual void SetIgnoreViewportScrolling(bool aIgnore) override;
|
||||
|
||||
virtual nsresult SetResolution(float aXResolution, float aYResolution) override {
|
||||
return SetResolutionImpl(aXResolution, aYResolution, /* aScaleToResolution = */ false);
|
||||
virtual nsresult SetResolution(float aResolution) override {
|
||||
return SetResolutionImpl(aResolution, /* aScaleToResolution = */ false);
|
||||
}
|
||||
virtual nsresult SetResolutionAndScaleTo(float aXResolution, float aYResolution) override {
|
||||
return SetResolutionImpl(aXResolution, aYResolution, /* aScaleToResolution = */ true);
|
||||
virtual nsresult SetResolutionAndScaleTo(float aResolution) override {
|
||||
return SetResolutionImpl(aResolution, /* aScaleToResolution = */ true);
|
||||
}
|
||||
virtual bool ScaleToResolution() const override;
|
||||
virtual gfxSize GetCumulativeResolution() override;
|
||||
virtual float GetCumulativeResolution() override;
|
||||
|
||||
//nsIViewObserver interface
|
||||
|
||||
@@ -464,12 +464,10 @@ protected:
|
||||
|
||||
struct RenderingState {
|
||||
explicit RenderingState(PresShell* aPresShell)
|
||||
: mXResolution(aPresShell->mXResolution)
|
||||
, mYResolution(aPresShell->mYResolution)
|
||||
: mResolution(aPresShell->mResolution)
|
||||
, mRenderFlags(aPresShell->mRenderFlags)
|
||||
{ }
|
||||
float mXResolution;
|
||||
float mYResolution;
|
||||
float mResolution;
|
||||
RenderFlags mRenderFlags;
|
||||
};
|
||||
|
||||
@@ -482,8 +480,7 @@ protected:
|
||||
~AutoSaveRestoreRenderingState()
|
||||
{
|
||||
mPresShell->mRenderFlags = mOldState.mRenderFlags;
|
||||
mPresShell->mXResolution = mOldState.mXResolution;
|
||||
mPresShell->mYResolution = mOldState.mYResolution;
|
||||
mPresShell->mResolution = mOldState.mResolution;
|
||||
}
|
||||
|
||||
PresShell* mPresShell;
|
||||
@@ -762,7 +759,7 @@ protected:
|
||||
// A list of images that are visible or almost visible.
|
||||
nsTHashtable< nsRefPtrHashKey<nsIImageLoadingContent> > mVisibleImages;
|
||||
|
||||
nsresult SetResolutionImpl(float aXResolution, float aYResolution, bool aScaleToResolution);
|
||||
nsresult SetResolutionImpl(float aResolution, bool aScaleToResolution);
|
||||
|
||||
#ifdef DEBUG
|
||||
// The reflow root under which we're currently reflowing. Null when
|
||||
|
||||
@@ -21,7 +21,7 @@ public:
|
||||
nsPresState()
|
||||
: mContentData(nullptr)
|
||||
, mScrollState(0, 0)
|
||||
, mResolution(1.0, 1.0)
|
||||
, mResolution(1.0)
|
||||
, mScaleToResolution(false)
|
||||
, mDisabledSet(false)
|
||||
, mDisabled(false)
|
||||
@@ -37,12 +37,12 @@ public:
|
||||
return mScrollState;
|
||||
}
|
||||
|
||||
void SetResolution(const gfxSize& aSize)
|
||||
void SetResolution(float aSize)
|
||||
{
|
||||
mResolution = aSize;
|
||||
}
|
||||
|
||||
gfxSize GetResolution() const
|
||||
float GetResolution() const
|
||||
{
|
||||
return mResolution;
|
||||
}
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
protected:
|
||||
nsCOMPtr<nsISupports> mContentData;
|
||||
nsPoint mScrollState;
|
||||
gfxSize mResolution;
|
||||
float mResolution;
|
||||
bool mScaleToResolution;
|
||||
bool mDisabledSet;
|
||||
bool mDisabled;
|
||||
|
||||
@@ -1895,7 +1895,7 @@ ScrollFrameHelper::ScrollFrameHelper(nsContainerFrame* aOuter,
|
||||
, mScrollPosAtLastPaint(0, 0)
|
||||
, mRestorePos(-1, -1)
|
||||
, mLastPos(-1, -1)
|
||||
, mResolution(1.0, 1.0)
|
||||
, mResolution(1.0)
|
||||
, mScrollPosForLayerPixelAlignment(-1, -1)
|
||||
, mLastUpdateImagesPos(-1, -1)
|
||||
, mNeverHasVerticalScrollbar(false)
|
||||
@@ -3305,14 +3305,14 @@ ScrollFrameHelper::GetScrollPositionClampingScrollPortSize() const
|
||||
return mScrollPort.Size();
|
||||
}
|
||||
|
||||
gfxSize
|
||||
float
|
||||
ScrollFrameHelper::GetResolution() const
|
||||
{
|
||||
return mResolution;
|
||||
}
|
||||
|
||||
void
|
||||
ScrollFrameHelper::SetResolution(const gfxSize& aResolution)
|
||||
ScrollFrameHelper::SetResolution(float aResolution)
|
||||
{
|
||||
mResolution = aResolution;
|
||||
mIsResolutionSet = true;
|
||||
@@ -3320,7 +3320,7 @@ ScrollFrameHelper::SetResolution(const gfxSize& aResolution)
|
||||
}
|
||||
|
||||
void
|
||||
ScrollFrameHelper::SetResolutionAndScaleTo(const gfxSize& aResolution)
|
||||
ScrollFrameHelper::SetResolutionAndScaleTo(float aResolution)
|
||||
{
|
||||
MOZ_ASSERT(mIsRoot); // This API should only be called on root scroll frames.
|
||||
mResolution = aResolution;
|
||||
@@ -5250,9 +5250,9 @@ ScrollFrameHelper::RestoreState(nsPresState* aState)
|
||||
if (mIsRoot) {
|
||||
nsIPresShell* presShell = mOuter->PresContext()->PresShell();
|
||||
if (mScaleToResolution) {
|
||||
presShell->SetResolutionAndScaleTo(mResolution.width, mResolution.height);
|
||||
presShell->SetResolutionAndScaleTo(mResolution);
|
||||
} else {
|
||||
presShell->SetResolution(mResolution.width, mResolution.height);
|
||||
presShell->SetResolution(mResolution);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user