mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge birch to m-c.
This commit is contained in:
commit
6f0cba2d2f
@ -576,12 +576,20 @@ pref("hal.processPriorityManager.gonk.backgroundKillUnderMB", 20);
|
||||
pref("hal.processPriorityManager.gonk.notifyLowMemUnderMB", 10);
|
||||
|
||||
// Niceness values (i.e., CPU priorities) for B2G processes.
|
||||
//
|
||||
// Note: The maximum nice value on Linux is 19, but the max value you should
|
||||
// use here is 18. NSPR adds 1 to some threads' nice values, to mark
|
||||
// low-priority threads. If the process priority manager were to renice a
|
||||
// process (and all its threads) to 19, all threads would have the same
|
||||
// niceness. Then when we reniced the process to (say) 10, all threads would
|
||||
// /still/ have the same niceness; we'd effectively have erased NSPR's thread
|
||||
// priorities.
|
||||
pref("hal.processPriorityManager.gonk.masterNice", 0);
|
||||
pref("hal.processPriorityManager.gonk.foregroundHighNice", 0);
|
||||
pref("hal.processPriorityManager.gonk.foregroundNice", 1);
|
||||
pref("hal.processPriorityManager.gonk.backgroundPerceivableNice", 10);
|
||||
pref("hal.processPriorityManager.gonk.backgroundHomescreenNice", 20);
|
||||
pref("hal.processPriorityManager.gonk.backgroundNice", 20);
|
||||
pref("hal.processPriorityManager.gonk.backgroundHomescreenNice", 18);
|
||||
pref("hal.processPriorityManager.gonk.backgroundNice", 18);
|
||||
|
||||
#ifndef DEBUG
|
||||
// Enable pre-launching content processes for improved startup time
|
||||
@ -665,6 +673,9 @@ pref("memory_info_dumper.watch_fifo.directory", "/data/local");
|
||||
|
||||
pref("general.useragent.enable_overrides", true);
|
||||
|
||||
// Make <audio> and <video> talk to the AudioChannelService.
|
||||
pref("media.useAudioChannelService", true);
|
||||
|
||||
pref("b2g.version", @MOZ_B2G_VERSION@);
|
||||
|
||||
// Disable console buffering to save memory.
|
||||
@ -673,4 +684,4 @@ pref("consoleservice.buffered", false);
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// Performance testing suggests 2k is a better page size for SQLite.
|
||||
pref("toolkit.storage.pageSize", 2048);
|
||||
#endif
|
||||
#endif
|
||||
|
@ -13,6 +13,7 @@ interface nsSubDocumentFrame;
|
||||
interface nsIMessageSender;
|
||||
interface nsIVariant;
|
||||
interface nsIDOMElement;
|
||||
interface nsITabParent;
|
||||
|
||||
typedef unsigned long long nsContentViewId;
|
||||
|
||||
@ -110,7 +111,7 @@ interface nsIContentViewManager : nsISupports
|
||||
readonly attribute nsIContentView rootContentView;
|
||||
};
|
||||
|
||||
[scriptable, uuid(a4db652e-e3b0-4345-8107-cf6a30486759)]
|
||||
[scriptable, builtinclass, uuid(e4333e51-f2fa-4fdd-becd-75d000703355)]
|
||||
interface nsIFrameLoader : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -118,6 +119,12 @@ interface nsIFrameLoader : nsISupports
|
||||
*/
|
||||
readonly attribute nsIDocShell docShell;
|
||||
|
||||
/**
|
||||
* Get this frame loader's TabParent, if it has a remote frame. Otherwise,
|
||||
* returns null.
|
||||
*/
|
||||
readonly attribute nsITabParent tabParent;
|
||||
|
||||
/**
|
||||
* Start loading the frame. This method figures out what to load
|
||||
* from the owner content in the frame loader.
|
||||
@ -249,6 +256,15 @@ interface nsIFrameLoader : nsISupports
|
||||
* returns the iframe element.
|
||||
*/
|
||||
readonly attribute nsIDOMElement ownerElement;
|
||||
|
||||
/**
|
||||
* Get or set this frame loader's visibility.
|
||||
*
|
||||
* The notion of "visibility" here is separate from the notion of a
|
||||
* window/docshell's visibility. This field is mostly here so that we can
|
||||
* have a notion of visibility in the parent process when frames are OOP.
|
||||
*/
|
||||
[infallible] attribute boolean visible;
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
@ -285,6 +285,7 @@ nsFrameLoader::nsFrameLoader(Element* aOwner, bool aNetworkCreated)
|
||||
, mClampScrollPosition(true)
|
||||
, mRemoteBrowserInitialized(false)
|
||||
, mObservingOwnerContent(false)
|
||||
, mVisible(true)
|
||||
, mCurrentRemoteFrame(nullptr)
|
||||
, mRemoteBrowser(nullptr)
|
||||
, mRenderMode(RENDER_MODE_DEFAULT)
|
||||
@ -2551,3 +2552,29 @@ nsFrameLoader::ResetPermissionManagerStatus()
|
||||
}
|
||||
}
|
||||
|
||||
/* [infallible] */ NS_IMETHODIMP
|
||||
nsFrameLoader::SetVisible(bool aVisible)
|
||||
{
|
||||
mVisible = aVisible;
|
||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
if (os) {
|
||||
os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
|
||||
"frameloader-visible-changed", nullptr);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* [infallible] */ NS_IMETHODIMP
|
||||
nsFrameLoader::GetVisible(bool* aVisible)
|
||||
{
|
||||
*aVisible = mVisible;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFrameLoader::GetTabParent(nsITabParent** aTabParent)
|
||||
{
|
||||
nsCOMPtr<nsITabParent> tp = mRemoteBrowser;
|
||||
tp.forget(aTabParent);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -428,6 +428,11 @@ private:
|
||||
bool mRemoteBrowserInitialized : 1;
|
||||
bool mObservingOwnerContent : 1;
|
||||
|
||||
// Backs nsIFrameLoader::{Get,Set}Visible. Visibility state here relates to
|
||||
// whether this frameloader's <iframe mozbrowser> is setVisible(true)'ed, and
|
||||
// doesn't necessarily correlate with docshell/document visibility.
|
||||
bool mVisible : 1;
|
||||
|
||||
// XXX leaking
|
||||
nsCOMPtr<nsIObserver> mChildHost;
|
||||
RenderFrameParent* mCurrentRemoteFrame;
|
||||
|
@ -45,13 +45,12 @@
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/dom/WebGLRenderingContextBinding.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/ipc/ProcessPriorityManager.h"
|
||||
#include "mozilla/ProcessPriorityManager.h"
|
||||
|
||||
#include "Layers.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::dom::ipc;
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::gl;
|
||||
using namespace mozilla::layers;
|
||||
@ -66,7 +65,8 @@ WebGLMemoryPressureObserver::Observe(nsISupports* aSubject,
|
||||
|
||||
bool wantToLoseContext = true;
|
||||
|
||||
if (!mContext->mCanLoseContextInForeground && CurrentProcessIsForeground())
|
||||
if (!mContext->mCanLoseContextInForeground &&
|
||||
ProcessPriorityManager::CurrentProcessIsForeground())
|
||||
wantToLoseContext = false;
|
||||
else if (!nsCRT::strcmp(aSomeData,
|
||||
NS_LITERAL_STRING("heap-minimize").get()))
|
||||
|
@ -970,6 +970,11 @@ static bool IsAutoplayEnabled()
|
||||
return Preferences::GetBool("media.autoplay.enabled");
|
||||
}
|
||||
|
||||
static bool UseAudioChannelService()
|
||||
{
|
||||
return Preferences::GetBool("media.useAudioChannelService");
|
||||
}
|
||||
|
||||
void HTMLMediaElement::UpdatePreloadAction()
|
||||
{
|
||||
PreloadAction nextAction = PRELOAD_UNDEFINED;
|
||||
@ -2163,7 +2168,10 @@ bool HTMLMediaElement::ParseAttribute(int32_t aNamespaceID,
|
||||
|
||||
bool HTMLMediaElement::CheckAudioChannelPermissions(const nsAString& aString)
|
||||
{
|
||||
#ifdef ANDROID
|
||||
if (!UseAudioChannelService()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Only normal channel doesn't need permission.
|
||||
if (!aString.EqualsASCII("normal")) {
|
||||
nsCOMPtr<nsIPermissionManager> permissionManager =
|
||||
@ -2179,7 +2187,7 @@ bool HTMLMediaElement::CheckAudioChannelPermissions(const nsAString& aString)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3218,17 +3226,17 @@ void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement, bool aSuspendE
|
||||
void HTMLMediaElement::NotifyOwnerDocumentActivityChanged()
|
||||
{
|
||||
nsIDocument* ownerDoc = OwnerDoc();
|
||||
#ifdef ANDROID
|
||||
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(OwnerDoc());
|
||||
if (domDoc) {
|
||||
bool hidden = false;
|
||||
domDoc->GetHidden(&hidden);
|
||||
// SetVisibilityState will update mChannelSuspended via the CanPlayChanged callback.
|
||||
if (mPlayingThroughTheAudioChannel && mAudioChannelAgent) {
|
||||
mAudioChannelAgent->SetVisibilityState(!hidden);
|
||||
if (UseAudioChannelService()) {
|
||||
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(OwnerDoc());
|
||||
if (domDoc) {
|
||||
bool hidden = false;
|
||||
domDoc->GetHidden(&hidden);
|
||||
// SetVisibilityState will update mChannelSuspended via the CanPlayChanged callback.
|
||||
if (mPlayingThroughTheAudioChannel && mAudioChannelAgent) {
|
||||
mAudioChannelAgent->SetVisibilityState(!hidden);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
bool suspendEvents = !ownerDoc->IsActive() || !ownerDoc->IsVisible();
|
||||
bool pauseElement = suspendEvents || mChannelSuspended;
|
||||
|
||||
@ -3656,9 +3664,10 @@ ImageContainer* HTMLMediaElement::GetImageContainer()
|
||||
|
||||
nsresult HTMLMediaElement::UpdateChannelMuteState(bool aCanPlay)
|
||||
{
|
||||
// Only on B2G we mute the HTMLMediaElement following the rules of
|
||||
// AudioChannelService.
|
||||
#ifdef ANDROID
|
||||
if (!UseAudioChannelService()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// We have to mute this channel:
|
||||
if (!aCanPlay && !mChannelSuspended) {
|
||||
mChannelSuspended = true;
|
||||
@ -3669,15 +3678,15 @@ nsresult HTMLMediaElement::UpdateChannelMuteState(bool aCanPlay)
|
||||
}
|
||||
|
||||
SuspendOrResumeElement(mChannelSuspended, false);
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void HTMLMediaElement::UpdateAudioChannelPlayingState()
|
||||
{
|
||||
// The HTMLMediaElement is registered to the AudioChannelService only on B2G.
|
||||
#ifdef ANDROID
|
||||
if (!UseAudioChannelService()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool playingThroughTheAudioChannel =
|
||||
(!mPaused &&
|
||||
(HasAttr(kNameSpaceID_None, nsGkAtoms::loop) ||
|
||||
@ -3711,7 +3720,6 @@ void HTMLMediaElement::UpdateAudioChannelPlayingState()
|
||||
mAudioChannelAgent = nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* void canPlayChanged (in boolean canPlay); */
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsHashPropertyBag.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include "nsIAudioManager.h"
|
||||
@ -104,7 +105,7 @@ AudioChannelService::RegisterType(AudioChannelType aType, uint64_t aChildID)
|
||||
// In order to avoid race conditions, it's safer to notify any existing
|
||||
// agent any time a new one is registered.
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Default) {
|
||||
SendAudioChannelChangedNotification();
|
||||
SendAudioChannelChangedNotification(aChildID);
|
||||
Notify();
|
||||
}
|
||||
}
|
||||
@ -142,7 +143,7 @@ AudioChannelService::UnregisterType(AudioChannelType aType,
|
||||
!mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].Contains(aChildID)) {
|
||||
mActiveContentChildIDs.RemoveElement(aChildID);
|
||||
}
|
||||
SendAudioChannelChangedNotification();
|
||||
SendAudioChannelChangedNotification(aChildID);
|
||||
Notify();
|
||||
}
|
||||
}
|
||||
@ -180,7 +181,6 @@ AudioChannelService::GetMuted(AudioChannelAgent* aAgent, bool aElementHidden)
|
||||
aElementHidden, oldElementHidden);
|
||||
data->mMuted = muted;
|
||||
|
||||
SendAudioChannelChangedNotification();
|
||||
return muted;
|
||||
}
|
||||
|
||||
@ -207,7 +207,6 @@ AudioChannelService::GetMutedInternal(AudioChannelType aType, uint64_t aChildID,
|
||||
mActiveContentChildIDs.AppendElement(aChildID);
|
||||
}
|
||||
}
|
||||
|
||||
else if (newType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN &&
|
||||
oldType == AUDIO_CHANNEL_INT_CONTENT &&
|
||||
!mActiveContentChildIDsFrozen) {
|
||||
@ -228,6 +227,8 @@ AudioChannelService::GetMutedInternal(AudioChannelType aType, uint64_t aChildID,
|
||||
Notify();
|
||||
}
|
||||
|
||||
SendAudioChannelChangedNotification(aChildID);
|
||||
|
||||
// Let play any visible audio channel.
|
||||
if (!aElementHidden) {
|
||||
return false;
|
||||
@ -258,13 +259,29 @@ AudioChannelService::ContentOrNormalChannelIsActive()
|
||||
!mChannelCounters[AUDIO_CHANNEL_INT_NORMAL].IsEmpty();
|
||||
}
|
||||
|
||||
bool
|
||||
AudioChannelService::ProcessContentOrNormalChannelIsActive(uint64_t aChildID)
|
||||
{
|
||||
return mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].Contains(aChildID) ||
|
||||
mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].Contains(aChildID) ||
|
||||
mChannelCounters[AUDIO_CHANNEL_INT_NORMAL].Contains(aChildID);
|
||||
}
|
||||
|
||||
void
|
||||
AudioChannelService::SendAudioChannelChangedNotification()
|
||||
AudioChannelService::SendAudioChannelChangedNotification(uint64_t aChildID)
|
||||
{
|
||||
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
|
||||
props->Init();
|
||||
props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), aChildID);
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
obs->NotifyObservers(static_cast<nsIWritablePropertyBag*>(props),
|
||||
"audio-channel-process-changed", nullptr);
|
||||
|
||||
// Calculating the most important active channel.
|
||||
AudioChannelType higher = AUDIO_CHANNEL_LAST;
|
||||
|
||||
@ -341,7 +358,6 @@ AudioChannelService::SendAudioChannelChangedNotification()
|
||||
channelName.AssignLiteral("none");
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
obs->NotifyObservers(nullptr, "audio-channel-changed", channelName.get());
|
||||
}
|
||||
|
||||
@ -355,7 +371,6 @@ AudioChannelService::SendAudioChannelChangedNotification()
|
||||
channelName.AssignLiteral("none");
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
obs->NotifyObservers(nullptr, "visible-audio-channel-changed", channelName.get());
|
||||
}
|
||||
}
|
||||
@ -461,7 +476,7 @@ AudioChannelService::Observe(nsISupports* aSubject, const char* aTopic, const PR
|
||||
// We don't have to remove the agents from the mAgents hashtable because if
|
||||
// that table contains only agents running on the same process.
|
||||
|
||||
SendAudioChannelChangedNotification();
|
||||
SendAudioChannelChangedNotification(childID);
|
||||
Notify();
|
||||
} else {
|
||||
NS_WARNING("ipc:content-shutdown message without childID property");
|
||||
|
@ -60,13 +60,20 @@ public:
|
||||
*/
|
||||
virtual bool ContentOrNormalChannelIsActive();
|
||||
|
||||
/**
|
||||
* Return true iff a normal or content channel is active for the given process
|
||||
* ID.
|
||||
*/
|
||||
virtual bool ProcessContentOrNormalChannelIsActive(uint64_t aChildID);
|
||||
|
||||
protected:
|
||||
void Notify();
|
||||
|
||||
/**
|
||||
* Send the audio-channel-changed notification if needed.
|
||||
* Send the audio-channel-changed notification for the given process ID if
|
||||
* needed.
|
||||
*/
|
||||
void SendAudioChannelChangedNotification();
|
||||
void SendAudioChannelChangedNotification(uint64_t aChildID);
|
||||
|
||||
/* Register/Unregister IPC types: */
|
||||
void RegisterType(AudioChannelType aType, uint64_t aChildID);
|
||||
|
@ -646,13 +646,7 @@ BrowserElementChild.prototype = {
|
||||
}
|
||||
|
||||
this._forcedVisible = data.json.visible;
|
||||
this._updateDocShellVisibility();
|
||||
|
||||
// Fire a notification to the ProcessPriorityManager to reset this
|
||||
// process's priority now (as opposed to after a brief delay).
|
||||
var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
|
||||
os.notifyObservers(/* subject */ null, 'process-priority:reset-now',
|
||||
/* data */ null);
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_recvVisible: function(data) {
|
||||
@ -669,13 +663,14 @@ BrowserElementChild.prototype = {
|
||||
_recvOwnerVisibilityChange: function(data) {
|
||||
debug("Received ownerVisibilityChange: (" + data.json.visible + ")");
|
||||
this._ownerVisible = data.json.visible;
|
||||
this._updateDocShellVisibility();
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_updateDocShellVisibility: function() {
|
||||
_updateVisibility: function() {
|
||||
var visible = this._forcedVisible && this._ownerVisible;
|
||||
if (docShell.isActive !== visible) {
|
||||
docShell.isActive = visible;
|
||||
sendAsyncMsg('visibility-change', {visibility: visible});
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -118,7 +118,8 @@ function BrowserElementParent(frameLoader, hasRemoteFrame) {
|
||||
"fullscreen-origin-change": this._remoteFullscreenOriginChange,
|
||||
"rollback-fullscreen": this._remoteFrameFullscreenReverted,
|
||||
"exit-fullscreen": this._exitFullscreen,
|
||||
"got-visible": this._gotDOMRequestResult
|
||||
"got-visible": this._gotDOMRequestResult,
|
||||
"visibility-change": this._childVisibilityChange,
|
||||
}
|
||||
|
||||
this._mm.addMessageListener('browser-element-api:call', function(aMsg) {
|
||||
@ -447,6 +448,7 @@ BrowserElementParent.prototype = {
|
||||
|
||||
_setVisible: function(visible) {
|
||||
this._sendAsyncMsg('set-visible', {visible: visible});
|
||||
this._frameLoader.visible = visible;
|
||||
},
|
||||
|
||||
_sendMouseEvent: function(type, x, y, button, clickCount, modifiers) {
|
||||
@ -562,6 +564,19 @@ BrowserElementParent.prototype = {
|
||||
{visible: !this._window.document.hidden});
|
||||
},
|
||||
|
||||
/*
|
||||
* Called when the child notices that its visibility has changed.
|
||||
*
|
||||
* This is sometimes redundant; for example, the child's visibility may
|
||||
* change in response to a setVisible request that we made here! But it's
|
||||
* not always redundant; for example, the child's visibility may change in
|
||||
* response to its parent docshell being hidden.
|
||||
*/
|
||||
_childVisibilityChange: function(data) {
|
||||
debug("_childVisibilityChange(" + data.json.visible + ")");
|
||||
this._frameLoader.visible = data.json.visible;
|
||||
},
|
||||
|
||||
_exitFullscreen: function() {
|
||||
this._windowUtils.exitFullscreen();
|
||||
},
|
||||
|
@ -61,18 +61,42 @@ const browserElementTestHelpers = {
|
||||
this.tempPermissions.push(location.href)
|
||||
},
|
||||
|
||||
removeAllTempPermissions: function() {
|
||||
for(var i = 0; i < this.tempPermissions.length; i++) {
|
||||
SpecialPowers.removePermission("browser", this.tempPermissions[i]);
|
||||
}
|
||||
},
|
||||
|
||||
'tempPermissions': [],
|
||||
addPermissionForUrl: function(url) {
|
||||
SpecialPowers.addPermission("browser", true, url);
|
||||
this.tempPermissions.push(url);
|
||||
},
|
||||
|
||||
'tempPermissions': [],
|
||||
_observers: [],
|
||||
|
||||
// This function is a wrapper which lets you register an observer to one of
|
||||
// the process priority manager's test-only topics. observerFn should be a
|
||||
// function which takes (subject, topic, data).
|
||||
//
|
||||
// We'll clean up any observers you add at the end of the test.
|
||||
addProcessPriorityObserver: function(processPriorityTopic, observerFn) {
|
||||
var topic = "process-priority-manager:TEST-ONLY:" + processPriorityTopic;
|
||||
|
||||
// SpecialPowers appears to require that the observer be an object, not a
|
||||
// function.
|
||||
var observer = {
|
||||
observe: observerFn
|
||||
};
|
||||
|
||||
SpecialPowers.addObserver(observer, topic, /* weak = */ false);
|
||||
this._observers.push([observer, topic]);
|
||||
},
|
||||
|
||||
cleanUp: function() {
|
||||
for (var i = 0; i < this.tempPermissions.length; i++) {
|
||||
SpecialPowers.removePermission("browser", this.tempPermissions[i]);
|
||||
}
|
||||
|
||||
for (var i = 0; i < this._observers.length; i++) {
|
||||
SpecialPowers.removeObserver(this._observers[i][0],
|
||||
this._observers[i][1]);
|
||||
}
|
||||
},
|
||||
|
||||
// Some basically-empty pages from different domains you can load.
|
||||
'emptyPage1': 'http://example.com' + _getPath() + '/file_empty.html',
|
||||
@ -81,6 +105,92 @@ const browserElementTestHelpers = {
|
||||
'focusPage': 'http://example.org' + _getPath() + '/file_focus.html',
|
||||
};
|
||||
|
||||
// Returns a promise which is resolved when a subprocess is created. The
|
||||
// argument to resolve() is the childID of the subprocess.
|
||||
function expectProcessCreated() {
|
||||
var deferred = Promise.defer();
|
||||
|
||||
var observed = false;
|
||||
browserElementTestHelpers.addProcessPriorityObserver(
|
||||
"process-created",
|
||||
function(subject, topic, data) {
|
||||
// Don't run this observer twice, so we don't ok(true) twice. (It's fine
|
||||
// to resolve a promise twice; the second resolve() call does nothing.)
|
||||
if (observed) {
|
||||
return;
|
||||
}
|
||||
observed = true;
|
||||
|
||||
var childID = parseInt(data);
|
||||
ok(true, 'Got new process, id=' + childID);
|
||||
deferred.resolve(childID);
|
||||
}
|
||||
);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Just like expectProcessCreated(), except we'll call ok(false) if a second
|
||||
// process is created.
|
||||
function expectOnlyOneProcessCreated() {
|
||||
var p = expectProcessCreated();
|
||||
p.then(function() {
|
||||
expectProcessCreated().then(function(childID) {
|
||||
ok(false, 'Got unexpected process creation, childID=' + childID);
|
||||
});
|
||||
});
|
||||
return p;
|
||||
}
|
||||
|
||||
// Returns a promise which is resolved or rejected the next time the process
|
||||
// childID changes its priority. We resolve if the priority matches
|
||||
// expectedPriority, and we reject otherwise.
|
||||
function expectPriorityChange(childID, expectedPriority) {
|
||||
var deferred = Promise.defer();
|
||||
|
||||
var observed = false;
|
||||
browserElementTestHelpers.addProcessPriorityObserver(
|
||||
'process-priority-set',
|
||||
function(subject, topic, data) {
|
||||
if (observed) {
|
||||
return;
|
||||
}
|
||||
|
||||
[id, priority] = data.split(":");
|
||||
if (id != childID) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we run the is() calls in this observer only once, otherwise
|
||||
// we'll expect /every/ priority change to match expectedPriority.
|
||||
observed = true;
|
||||
|
||||
is(priority, expectedPriority,
|
||||
'Expected priority of childID ' + childID +
|
||||
' to change to ' + expectedPriority);
|
||||
|
||||
if (priority == expectedPriority) {
|
||||
deferred.resolve(priority);
|
||||
} else {
|
||||
deferred.reject(priority);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Returns a promise which is resolved the first time the given iframe fires
|
||||
// the mozbrowser##eventName event.
|
||||
function expectMozbrowserEvent(iframe, eventName) {
|
||||
var deferred = Promise.defer();
|
||||
iframe.addEventListener('mozbrowser' + eventName, function handler(e) {
|
||||
iframe.removeEventListener('mozbrowser' + eventName, handler);
|
||||
deferred.resolve(e);
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Set some prefs:
|
||||
//
|
||||
// * browser.pageThumbs.enabled: false
|
||||
@ -129,7 +239,7 @@ const browserElementTestHelpers = {
|
||||
})();
|
||||
|
||||
addEventListener('unload', function() {
|
||||
browserElementTestHelpers.removeAllTempPermissions();
|
||||
browserElementTestHelpers.cleanUp();
|
||||
});
|
||||
|
||||
// Wait for the load event before unlocking the test-ready event.
|
||||
@ -137,3 +247,276 @@ browserElementTestHelpers.lockTestReady();
|
||||
addEventListener('load', function() {
|
||||
SimpleTest.executeSoon(browserElementTestHelpers.unlockTestReady.bind(browserElementTestHelpers));
|
||||
});
|
||||
|
||||
//////////////////////////////////
|
||||
// promise.js from the addon SDK with some modifications to the module
|
||||
// boilerplate.
|
||||
//////////////////////////////////
|
||||
|
||||
;(function(id, factory) { // Module boilerplate :(
|
||||
var globals = this;
|
||||
factory(function require(id) {
|
||||
return globals[id];
|
||||
}, (globals[id] = {}), { uri: document.location.href + '#' + id, id: id });
|
||||
}).call(this, 'Promise', function Promise(require, exports, module) {
|
||||
|
||||
'use strict';
|
||||
|
||||
module.metadata = {
|
||||
"stability": "unstable"
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal utility: Wraps given `value` into simplified promise, successfully
|
||||
* fulfilled to a given `value`. Note the result is not a complete promise
|
||||
* implementation, as its method `then` does not returns anything.
|
||||
*/
|
||||
function fulfilled(value) {
|
||||
return { then: function then(fulfill) { fulfill(value); } };
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal utility: Wraps given input into simplified promise, pre-rejected
|
||||
* with a given `reason`. Note the result is not a complete promise
|
||||
* implementation, as its method `then` does not returns anything.
|
||||
*/
|
||||
function rejected(reason) {
|
||||
return { then: function then(fulfill, reject) { reject(reason); } };
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal utility: Returns `true` if given `value` is a promise. Value is
|
||||
* assumed to be a promise if it implements method `then`.
|
||||
*/
|
||||
function isPromise(value) {
|
||||
return value && typeof(value.then) === 'function';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates deferred object containing fresh promise & methods to either resolve
|
||||
* or reject it. The result is an object with the following properties:
|
||||
* - `promise` Eventual value representation implementing CommonJS [Promises/A]
|
||||
* (http://wiki.commonjs.org/wiki/Promises/A) API.
|
||||
* - `resolve` Single shot function that resolves enclosed `promise` with a
|
||||
* given `value`.
|
||||
* - `reject` Single shot function that rejects enclosed `promise` with a given
|
||||
* `reason`.
|
||||
*
|
||||
* An optional `prototype` argument is used as a prototype of the returned
|
||||
* `promise` allowing one to implement additional API. If prototype is not
|
||||
* passed then it falls back to `Object.prototype`.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* function fetchURI(uri, type) {
|
||||
* var deferred = defer();
|
||||
* var request = new XMLHttpRequest();
|
||||
* request.open("GET", uri, true);
|
||||
* request.responseType = type;
|
||||
* request.onload = function onload() {
|
||||
* deferred.resolve(request.response);
|
||||
* }
|
||||
* request.onerror = function(event) {
|
||||
* deferred.reject(event);
|
||||
* }
|
||||
* request.send();
|
||||
*
|
||||
* return deferred.promise;
|
||||
* }
|
||||
*/
|
||||
function defer(prototype) {
|
||||
// Define FIFO queue of observer pairs. Once promise is resolved & all queued
|
||||
// observers are forwarded to `result` and variable is set to `null`.
|
||||
var observers = [];
|
||||
|
||||
// Promise `result`, which will be assigned a resolution value once promise
|
||||
// is resolved. Note that result will always be assigned promise (or alike)
|
||||
// object to take care of propagation through promise chains. If result is
|
||||
// `null` promise is not resolved yet.
|
||||
var result = null;
|
||||
|
||||
prototype = (prototype || prototype === null) ? prototype : Object.prototype;
|
||||
|
||||
// Create an object implementing promise API.
|
||||
var promise = Object.create(prototype, {
|
||||
then: { value: function then(onFulfill, onError) {
|
||||
var deferred = defer(prototype);
|
||||
|
||||
function resolve(value) {
|
||||
// If `onFulfill` handler is provided resolve `deferred.promise` with
|
||||
// result of invoking it with a resolution value. If handler is not
|
||||
// provided propagate value through.
|
||||
try {
|
||||
deferred.resolve(onFulfill ? onFulfill(value) : value);
|
||||
}
|
||||
// `onFulfill` may throw exception in which case resulting promise
|
||||
// is rejected with thrown exception.
|
||||
catch(error) {
|
||||
if (exports._reportErrors && typeof(console) === 'object')
|
||||
console.error(error);
|
||||
// Note: Following is equivalent of `deferred.reject(error)`,
|
||||
// we use this shortcut to reduce a stack.
|
||||
deferred.resolve(rejected(error));
|
||||
}
|
||||
}
|
||||
|
||||
function reject(reason) {
|
||||
try {
|
||||
if (onError) deferred.resolve(onError(reason));
|
||||
else deferred.resolve(rejected(reason));
|
||||
}
|
||||
catch(error) {
|
||||
if (exports._reportErrors && typeof(console) === 'object')
|
||||
console.error(error)
|
||||
deferred.resolve(rejected(error));
|
||||
}
|
||||
}
|
||||
|
||||
// If enclosed promise (`this.promise`) observers queue is still alive
|
||||
// enqueue a new observer pair into it. Note that this does not
|
||||
// necessary means that promise is pending, it may already be resolved,
|
||||
// but we still have to queue observers to guarantee an order of
|
||||
// propagation.
|
||||
if (observers) {
|
||||
observers.push({ resolve: resolve, reject: reject });
|
||||
}
|
||||
// Otherwise just forward observer pair right to a `result` promise.
|
||||
else {
|
||||
result.then(resolve, reject);
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
}}
|
||||
})
|
||||
|
||||
var deferred = {
|
||||
promise: promise,
|
||||
/**
|
||||
* Resolves associated `promise` to a given `value`, unless it's already
|
||||
* resolved or rejected. Note that resolved promise is not necessary a
|
||||
* successfully fulfilled. Promise may be resolved with a promise `value`
|
||||
* in which case `value` promise's fulfillment / rejection will propagate
|
||||
* up to a promise resolved with `value`.
|
||||
*/
|
||||
resolve: function resolve(value) {
|
||||
if (!result) {
|
||||
// Store resolution `value` in a `result` as a promise, so that all
|
||||
// the subsequent handlers can be simply forwarded to it. Since
|
||||
// `result` will be a promise all the value / error propagation will
|
||||
// be uniformly taken care of.
|
||||
result = isPromise(value) ? value : fulfilled(value);
|
||||
|
||||
// Forward already registered observers to a `result` promise in the
|
||||
// order they were registered. Note that we intentionally dequeue
|
||||
// observer at a time until queue is exhausted. This makes sure that
|
||||
// handlers registered as side effect of observer forwarding are
|
||||
// queued instead of being invoked immediately, guaranteeing FIFO
|
||||
// order.
|
||||
while (observers.length) {
|
||||
var observer = observers.shift();
|
||||
result.then(observer.resolve, observer.reject);
|
||||
}
|
||||
|
||||
// Once `observers` queue is exhausted we `null`-ify it, so that
|
||||
// new handlers are forwarded straight to the `result`.
|
||||
observers = null;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Rejects associated `promise` with a given `reason`, unless it's already
|
||||
* resolved / rejected. This is just a (better performing) convenience
|
||||
* shortcut for `deferred.resolve(reject(reason))`.
|
||||
*/
|
||||
reject: function reject(reason) {
|
||||
// Note that if promise is resolved that does not necessary means that it
|
||||
// is successfully fulfilled. Resolution value may be a promise in which
|
||||
// case its result propagates. In other words if promise `a` is resolved
|
||||
// with promise `b`, `a` is either fulfilled or rejected depending
|
||||
// on weather `b` is fulfilled or rejected. Here `deferred.promise` is
|
||||
// resolved with a promise pre-rejected with a given `reason`, there for
|
||||
// `deferred.promise` is rejected with a given `reason`. This may feel
|
||||
// little awkward first, but doing it this way greatly simplifies
|
||||
// propagation through promise chains.
|
||||
deferred.resolve(rejected(reason));
|
||||
}
|
||||
};
|
||||
|
||||
return deferred;
|
||||
}
|
||||
exports.defer = defer;
|
||||
|
||||
/**
|
||||
* Returns a promise resolved to a given `value`. Optionally a second
|
||||
* `prototype` argument may be provided to be used as a prototype for the
|
||||
* returned promise.
|
||||
*/
|
||||
function resolve(value, prototype) {
|
||||
var deferred = defer(prototype);
|
||||
deferred.resolve(value);
|
||||
return deferred.promise;
|
||||
}
|
||||
exports.resolve = resolve;
|
||||
|
||||
/**
|
||||
* Returns a promise rejected with a given `reason`. Optionally a second
|
||||
* `prototype` argument may be provided to be used as a prototype for the
|
||||
* returned promise.
|
||||
*/
|
||||
function reject(reason, prototype) {
|
||||
var deferred = defer(prototype);
|
||||
deferred.reject(reason);
|
||||
return deferred.promise;
|
||||
}
|
||||
exports.reject = reject;
|
||||
|
||||
var promised = (function() {
|
||||
// Note: Define shortcuts and utility functions here in order to avoid
|
||||
// slower property accesses and unnecessary closure creations on each
|
||||
// call of this popular function.
|
||||
|
||||
var call = Function.call;
|
||||
var concat = Array.prototype.concat;
|
||||
|
||||
// Utility function that does following:
|
||||
// execute([ f, self, args...]) => f.apply(self, args)
|
||||
function execute(args) { return call.apply(call, args) }
|
||||
|
||||
// Utility function that takes promise of `a` array and maybe promise `b`
|
||||
// as arguments and returns promise for `a.concat(b)`.
|
||||
function promisedConcat(promises, unknown) {
|
||||
return promises.then(function(values) {
|
||||
return resolve(unknown).then(function(value) {
|
||||
return values.concat([ value ])
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return function promised(f, prototype) {
|
||||
/**
|
||||
Returns a wrapped `f`, which when called returns a promise that resolves to
|
||||
`f(...)` passing all the given arguments to it, which by the way may be
|
||||
promises. Optionally second `prototype` argument may be provided to be used
|
||||
a prototype for a returned promise.
|
||||
|
||||
## Example
|
||||
|
||||
var promise = promised(Array)(1, promise(2), promise(3))
|
||||
promise.then(console.log) // => [ 1, 2, 3 ]
|
||||
**/
|
||||
|
||||
return function promised() {
|
||||
// create array of [ f, this, args... ]
|
||||
return concat.apply([ f, this ], arguments).
|
||||
// reduce it via `promisedConcat` to get promised array of fulfillments
|
||||
reduce(promisedConcat, resolve([], prototype)).
|
||||
// finally map that to promise of `f.apply(this, args...)`
|
||||
then(execute);
|
||||
}
|
||||
}
|
||||
})();
|
||||
exports.promised = promised;
|
||||
|
||||
var all = promised(Array);
|
||||
exports.all = all;
|
||||
|
||||
});
|
||||
|
@ -4,3 +4,4 @@
|
||||
# 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/.
|
||||
|
||||
DIRS = ['priority']
|
||||
|
46
dom/browser-element/mochitest/priority/Makefile.in
Normal file
46
dom/browser-element/mochitest/priority/Makefile.in
Normal file
@ -0,0 +1,46 @@
|
||||
# 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/.
|
||||
|
||||
DEPTH = @DEPTH@
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
relativesrcdir = @relativesrcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
# Note: ../browserElementTestHelpers.js makes all tests in this directory OOP,
|
||||
# because testing the process-priority manager without OOP frames doesn't make
|
||||
# much sense.
|
||||
#
|
||||
# Good luck running these tests on anything but desktop Linux.
|
||||
|
||||
ifeq ($(MOZ_WIDGET_TOOLKIT),gtk2)
|
||||
|
||||
# This test disabled due to bug 865844. In fact, it was never enabled!
|
||||
#
|
||||
# test_WebGLContextLost.html \
|
||||
# file_WebGLContextLost.html \
|
||||
|
||||
MOCHITEST_FILES = \
|
||||
test_Simple.html \
|
||||
test_Visibility.html \
|
||||
test_HighPriority.html \
|
||||
file_HighPriority.html \
|
||||
test_Background.html \
|
||||
test_Audio.html \
|
||||
file_Audio.html \
|
||||
silence.ogg \
|
||||
test_MultipleFrames.html \
|
||||
file_MultipleFrames.html \
|
||||
test_Preallocated.html \
|
||||
test_ExpectingSystemMessage.html \
|
||||
test_ExpectingSystemMessage2.html \
|
||||
test_NestedFrames.html \
|
||||
file_NestedFramesOuter.html \
|
||||
$(NULL)
|
||||
|
||||
endif # MOZ_WIDGET_TOOLKIT == gtk2
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
19
dom/browser-element/mochitest/priority/file_Audio.html
Normal file
19
dom/browser-element/mochitest/priority/file_Audio.html
Normal file
@ -0,0 +1,19 @@
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
addEventListener('load', function() {
|
||||
setTimeout(function() {
|
||||
var a = document.getElementById('audio');
|
||||
a.onplay = function() {
|
||||
alert('onplay');
|
||||
};
|
||||
a.play();
|
||||
}, 0);
|
||||
});
|
||||
</script>
|
||||
|
||||
<audio id='audio' loop src='silence.ogg'>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
var lock = navigator.requestWakeLock('high-priority');
|
||||
alert('step0');
|
||||
|
||||
lock.unlock();
|
||||
alert('step1');
|
||||
|
||||
lock = navigator.requestWakeLock('cpu');
|
||||
alert('step2');
|
||||
|
||||
lock.unlock();
|
||||
alert('step3');
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,14 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>file_MultipleFrames.html</p>
|
||||
|
||||
<script>
|
||||
addEventListener('load', function() {
|
||||
setTimeout(function() {
|
||||
window.open('../file_empty.html');
|
||||
}, 0);
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,20 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>file_NestedFramesOuter.html</p>
|
||||
<script>
|
||||
|
||||
addEventListener('load', function() {
|
||||
setTimeout(createIframe, 0);
|
||||
});
|
||||
|
||||
function createIframe()
|
||||
{
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('mozbrowser', true);
|
||||
iframe.src = location.hash.substr(1);
|
||||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,22 @@
|
||||
<html>
|
||||
<body>
|
||||
file_WebGLContextLost.html
|
||||
<canvas id='canvas'></canvas>
|
||||
|
||||
<script>
|
||||
function runTest()
|
||||
{
|
||||
var canvas = document.getElementById('canvas');
|
||||
canvas.addEventListener('webglcontextlost', function() {
|
||||
alert('webglcontextlost');
|
||||
});
|
||||
|
||||
var context = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
|
||||
context.viewport(0, 0, 10, 10);
|
||||
alert('ready');
|
||||
}
|
||||
|
||||
addEventListener('load', function() { setTimeout(runTest, 0) });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
0
dom/browser-element/mochitest/priority/moz.build
Normal file
0
dom/browser-element/mochitest/priority/moz.build
Normal file
BIN
dom/browser-element/mochitest/priority/silence.ogg
Normal file
BIN
dom/browser-element/mochitest/priority/silence.ogg
Normal file
Binary file not shown.
60
dom/browser-element/mochitest/priority/test_Audio.html
Normal file
60
dom/browser-element/mochitest/priority/test_Audio.html
Normal file
@ -0,0 +1,60 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test that frames playing audio get BACKGROUND_PERCEIVABLE priority.
|
||||
-->
|
||||
<head>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="../browserElementTestHelpers.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
browserElementTestHelpers.setEnabledPref(true);
|
||||
browserElementTestHelpers.addPermission();
|
||||
browserElementTestHelpers.enableProcessPriorityManager();
|
||||
|
||||
function runTest() {
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('mozbrowser', true);
|
||||
iframe.src = 'file_Audio.html';
|
||||
|
||||
var childID = null;
|
||||
expectOnlyOneProcessCreated().then(function(chid) {
|
||||
childID = chid;
|
||||
return Promise.all(
|
||||
[expectPriorityChange(childID, 'FOREGROUND'),
|
||||
expectMozbrowserEvent(iframe, 'loadend'),
|
||||
expectMozbrowserEvent(iframe, 'showmodalprompt').then(function(e) {
|
||||
is(e.detail.message, 'onplay', 'showmodalprompt message');
|
||||
})]
|
||||
);
|
||||
}).then(function() {
|
||||
// Send the child process into the background. Because it's playing audio,
|
||||
// it should get priority BACKGROUND_PERCEIVABLE, not vanilla BACKGROUND.
|
||||
var p = expectPriorityChange(childID, 'BACKGROUND_PERCEIVABLE');
|
||||
iframe.setVisible(false);
|
||||
return p;
|
||||
}).then(function() {
|
||||
var p = expectPriorityChange(childID, 'FOREGROUND');
|
||||
iframe.setVisible(true);
|
||||
return p;
|
||||
}).then(SimpleTest.finish);
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
// This test relies on <audio> elements interacting with the audio channel
|
||||
// service. This is controled by the media.useAudioChannelService pref.
|
||||
addEventListener('testready', function() {
|
||||
SpecialPowers.pushPrefEnv({set: [['media.useAudioChannelService', true]]},
|
||||
runTest);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
55
dom/browser-element/mochitest/priority/test_Background.html
Normal file
55
dom/browser-element/mochitest/priority/test_Background.html
Normal file
@ -0,0 +1,55 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test that calling setVisible('false') on an iframe causes its visibility to
|
||||
change.
|
||||
-->
|
||||
<head>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="../browserElementTestHelpers.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
browserElementTestHelpers.setEnabledPref(true);
|
||||
browserElementTestHelpers.addPermission();
|
||||
browserElementTestHelpers.enableProcessPriorityManager();
|
||||
|
||||
function runTest() {
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('mozbrowser', true);
|
||||
|
||||
iframe.src = browserElementTestHelpers.emptyPage1;
|
||||
|
||||
var childID = null;
|
||||
expectOnlyOneProcessCreated().then(function(chid) {
|
||||
childID = chid;
|
||||
}).then(function() {
|
||||
return expectPriorityChange(childID, 'FOREGROUND');
|
||||
}).then(function() {
|
||||
return expectMozbrowserEvent(iframe, 'loadend');
|
||||
}).then(function() {
|
||||
var p = expectPriorityChange(childID, 'BACKGROUND');
|
||||
|
||||
// We wait until mozbrowserloadend before calling setVisible, because
|
||||
// setVisible isn't available until mozbrowser has loaded. In practice, that
|
||||
// means we can call setVisible once we've gotten /any/ mozbrowser event.
|
||||
iframe.setVisible(false);
|
||||
return p;
|
||||
}).then(function() {
|
||||
var p = expectPriorityChange(childID, 'FOREGROUND');
|
||||
iframe.setVisible(true);
|
||||
}).then(SimpleTest.finish);
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
addEventListener('testready', runTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,55 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test that a high-priority frame that's expecting a system message initially
|
||||
gets priority FOREGROUND_HIGH.
|
||||
-->
|
||||
<head>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="../browserElementTestHelpers.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
browserElementTestHelpers.setEnabledPref(true);
|
||||
browserElementTestHelpers.addPermission();
|
||||
browserElementTestHelpers.enableProcessPriorityManager();
|
||||
SpecialPowers.addPermission("embed-apps", true, document);
|
||||
|
||||
function runTest() {
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('mozbrowser', true);
|
||||
iframe.setAttribute('expecting-system-message', true);
|
||||
iframe.setAttribute('mozapptype', 'critical');
|
||||
iframe.setAttribute('mozapp', 'http://example.org/manifest.webapp');
|
||||
|
||||
iframe.src = browserElementTestHelpers.emptyPage1;
|
||||
|
||||
var childID = null;
|
||||
expectOnlyOneProcessCreated().then(function(chid) {
|
||||
childID = chid;
|
||||
return expectPriorityChange(childID, 'FOREGROUND_HIGH');
|
||||
}).then(function() {
|
||||
// We go back to foreground when the wake lock taken on behalf of our new
|
||||
// process times out.
|
||||
return expectPriorityChange(childID, 'FOREGROUND');
|
||||
}).then(SimpleTest.finish);
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
addEventListener('testready', function() {
|
||||
// Cause the CPU wake lock taken on behalf of this new process to time out
|
||||
// after 1s.
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{set: [["dom.ipc.systemMessageCPULockTimeoutSec", 1]]},
|
||||
runTest);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,70 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test that a regular (not mozapptype=critical) frame that's expecting a system
|
||||
message gets priority BACKGROUND_PERCEIVABLE when it's in the background.
|
||||
-->
|
||||
<head>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="../browserElementTestHelpers.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
browserElementTestHelpers.setEnabledPref(true);
|
||||
browserElementTestHelpers.addPermission();
|
||||
browserElementTestHelpers.enableProcessPriorityManager();
|
||||
SpecialPowers.addPermission("embed-apps", true, document);
|
||||
|
||||
// Give our origin permission to open browsers, and remove it when the test is complete.
|
||||
var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
|
||||
SpecialPowers.addPermission("browser", true, { url: SpecialPowers.wrap(principal.URI).spec,
|
||||
appId: principal.appId,
|
||||
isInBrowserElement: true });
|
||||
|
||||
addEventListener('unload', function() {
|
||||
var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
|
||||
SpecialPowers.removePermission("browser", { url: SpecialPowers.wrap(principal.URI).spec,
|
||||
appId: principal.appId,
|
||||
isInBrowserElement: true });
|
||||
});
|
||||
|
||||
function runTest() {
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('mozbrowser', true);
|
||||
iframe.setAttribute('expecting-system-message', true);
|
||||
iframe.setAttribute('mozapp', 'http://example.org/manifest.webapp');
|
||||
|
||||
iframe.src = browserElementTestHelpers.emptyPage1;
|
||||
|
||||
var childID = null;
|
||||
expectOnlyOneProcessCreated().then(function(chid) {
|
||||
childID = chid;
|
||||
return Promise.all(
|
||||
[expectPriorityChange(childID, 'FOREGROUND'),
|
||||
expectMozbrowserEvent(iframe, 'loadend')]);
|
||||
}).then(function() {
|
||||
var p = expectPriorityChange(childID, 'BACKGROUND_PERCEIVABLE');
|
||||
iframe.setVisible(false);
|
||||
return p;
|
||||
}).then(SimpleTest.finish);
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
addEventListener('testready', function() {
|
||||
// We don't want this wake lock to time out during the test; if it did, then
|
||||
// we might see BACKGROUND priority instead of BACKGROUND_PERCEIVABLE. So
|
||||
// set the timeout to a large value.
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{set: [["dom.ipc.systemMessageCPULockTimeoutSec", 99999]]},
|
||||
runTest);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
121
dom/browser-element/mochitest/priority/test_HighPriority.html
Normal file
121
dom/browser-element/mochitest/priority/test_HighPriority.html
Normal file
@ -0,0 +1,121 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test that frames with mozapptype=critical which hold the "high-priority" or
|
||||
"cpu" wake locks get elevated process priority.
|
||||
-->
|
||||
<head>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="../browserElementTestHelpers.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
browserElementTestHelpers.setEnabledPref(true);
|
||||
browserElementTestHelpers.addPermission();
|
||||
browserElementTestHelpers.enableProcessPriorityManager();
|
||||
|
||||
function runTest() {
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('mozbrowser', true);
|
||||
iframe.setAttribute('mozapptype', 'critical');
|
||||
iframe.src = 'file_HighPriority.html';
|
||||
|
||||
// We expect the following to happen:
|
||||
//
|
||||
// - Process is created.
|
||||
// - Its priority is set to FOREGROUND (when the process starts).
|
||||
// - wait_alert('step0', FOREGROUND_HIGH)
|
||||
// - wait_alert('step1', FOREGROUND)
|
||||
// - wait_alert('step2', FOREGROUND_HIGH)
|
||||
//
|
||||
// Where wait_alert(M, P) means that we expect the subprocess to
|
||||
// * do alert(M) and
|
||||
// * be set to priority P
|
||||
// in some order. If the alert occurs before the priority change, we block
|
||||
// the alert until we observe the priority change. So the subprocess only
|
||||
// has to do
|
||||
//
|
||||
// // set priority to FOREGROUND_HIGH
|
||||
// alert('step0');
|
||||
// // set priority to FOREGROUND
|
||||
// alert('step1');
|
||||
//
|
||||
// etc.
|
||||
|
||||
var childID = null;
|
||||
var alertTimes = [];
|
||||
|
||||
// Return a promise that's resolved once the child process calls alert() and
|
||||
// we get a priority change, in some order.
|
||||
//
|
||||
// We check that the text of the alert is |"step" + index|.
|
||||
//
|
||||
// If gracePeriod is given, we check that the priority change occurred at
|
||||
// least gracePeriod ms since the alert from the previous step (with a fudge
|
||||
// factor to account for inaccurate timers).
|
||||
function expectAlertAndPriorityChange(index, priority, /* optional */ gracePeriod) {
|
||||
function checkAlertInfo(e) {
|
||||
is(e.detail.message, 'step' + index, 'alert() number ' + index);
|
||||
alertTimes.push(new Date());
|
||||
|
||||
// Block the alert; we'll unblock it by calling e.detail.unblock() later.
|
||||
e.preventDefault();
|
||||
return Promise.resolve(e.detail.unblock);
|
||||
}
|
||||
|
||||
function checkGracePeriod() {
|
||||
if (gracePeriod) {
|
||||
var msSinceLastAlert = (new Date()) - alertTimes[index - 1];
|
||||
|
||||
// 50ms fudge factor. This test is set up so that, if nsITimers are
|
||||
// accurate, we don't need any fudge factor. Unfortunately our timers
|
||||
// are not accurate! There's little we can do here except fudge.
|
||||
// Thankfully all we're trying to test is that we get /some/ delay; the
|
||||
// exact amount of delay isn't so important.
|
||||
ok(msSinceLastAlert + 50 >= gracePeriod,
|
||||
msSinceLastAlert + "ms since last alert >= (" + gracePeriod + " - 50ms)");
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.all(
|
||||
[expectMozbrowserEvent(iframe, 'showmodalprompt').then(checkAlertInfo),
|
||||
expectPriorityChange(childID, priority).then(checkGracePeriod)]
|
||||
).then(function(results) {
|
||||
// expectMozbrowserEvent returns the function to call to unblock the
|
||||
// alert. It comes to us as the first element of the results array.
|
||||
results[0]();
|
||||
});
|
||||
}
|
||||
|
||||
expectProcessCreated().then(function(chid) {
|
||||
childID = chid;
|
||||
return expectPriorityChange(childID, 'FOREGROUND');
|
||||
}).then(function() {
|
||||
return expectAlertAndPriorityChange(0, 'FOREGROUND_HIGH');
|
||||
}).then(function() {
|
||||
return expectAlertAndPriorityChange(1, 'FOREGROUND', priorityChangeGracePeriod);
|
||||
}).then(function() {
|
||||
return expectAlertAndPriorityChange(2, 'FOREGROUND_HIGH');
|
||||
}).then(function() {
|
||||
return expectAlertAndPriorityChange(3, 'FOREGROUND', priorityChangeGracePeriod);
|
||||
}).then(SimpleTest.finish);
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
const priorityChangeGracePeriod = 100;
|
||||
addEventListener('testready', function() {
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{set: [['dom.ipc.processPriorityManager.backgroundGracePeriodMS',
|
||||
priorityChangeGracePeriod]]},
|
||||
runTest);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,56 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test that when we remove one of a process's frames from the DOM, the process's
|
||||
priority is recomputed.
|
||||
-->
|
||||
<head>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="../browserElementTestHelpers.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
browserElementTestHelpers.setEnabledPref(true);
|
||||
browserElementTestHelpers.addPermission();
|
||||
browserElementTestHelpers.enableProcessPriorityManager();
|
||||
|
||||
function runTest() {
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('mozbrowser', true);
|
||||
iframe.src = 'file_MultipleFrames.html';
|
||||
|
||||
var childID = null;
|
||||
var iframe2;
|
||||
expectProcessCreated().then(function(chid) {
|
||||
childID = chid;
|
||||
return expectPriorityChange(childID, 'FOREGROUND');
|
||||
}).then(function() {
|
||||
return expectMozbrowserEvent(iframe, 'openwindow');
|
||||
}).then(function(e) {
|
||||
iframe2 = e.detail.frameElement;
|
||||
document.body.appendChild(iframe2);
|
||||
return expectMozbrowserEvent(iframe2, 'loadend');
|
||||
}).then(function() {
|
||||
// At this point, the child process has been set to FOREGROUND, and the popup
|
||||
// opened by file_MultipleFrames has finished loading.
|
||||
//
|
||||
// Now setVisible(false) the popup frame and remove the popup frame from the
|
||||
// DOM. This should cause the process to take on BACKGROUND priority.
|
||||
var p = expectPriorityChange(childID, 'BACKGROUND');
|
||||
iframe.setVisible(false);
|
||||
document.body.removeChild(iframe2);
|
||||
return p;
|
||||
}).then(SimpleTest.finish);
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
addEventListener('testready', runTest);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,73 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test changing the visibility of an <iframe mozbrowser> changes the visibility
|
||||
(and thus the priority) of any <iframe mozbrowser>s it contains.
|
||||
-->
|
||||
<head>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="../browserElementTestHelpers.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
browserElementTestHelpers.setEnabledPref(true);
|
||||
browserElementTestHelpers.addPermission();
|
||||
browserElementTestHelpers.enableProcessPriorityManager();
|
||||
|
||||
// Give our origin permission to open browsers, and remove it when the test is complete.
|
||||
var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
|
||||
SpecialPowers.addPermission("browser", true, { url: SpecialPowers.wrap(principal.URI).spec,
|
||||
appId: principal.appId,
|
||||
isInBrowserElement: true });
|
||||
|
||||
addEventListener('unload', function() {
|
||||
var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
|
||||
SpecialPowers.removePermission("browser", { url: SpecialPowers.wrap(principal.URI).spec,
|
||||
appId: principal.appId,
|
||||
isInBrowserElement: true });
|
||||
});
|
||||
|
||||
function runTest() {
|
||||
// Set up the following hierarchy of frames:
|
||||
//
|
||||
// <iframe mozbrowser remote=false src='file_NestedFramesOuter.html'>
|
||||
// <iframe mozbrowser remote=true src='file_empty.html'>
|
||||
//
|
||||
// When we change the visibility of the outer iframe, it should change the
|
||||
// priority of the inner one.
|
||||
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('mozbrowser', true);
|
||||
iframe.setAttribute('remote', false);
|
||||
iframe.src = 'file_NestedFramesOuter.html#' + browserElementTestHelpers.emptyPage1;
|
||||
|
||||
// Note that this is the process corresponding to the /inner/ iframe. The
|
||||
// outer iframe runs in-process (because it has remote=false).
|
||||
var childID = null;
|
||||
expectOnlyOneProcessCreated().then(function(chid) {
|
||||
childID = chid;
|
||||
return Promise.all(
|
||||
[expectPriorityChange(childID, 'FOREGROUND'),
|
||||
expectMozbrowserEvent(iframe, 'loadend')]
|
||||
);
|
||||
}).then(function() {
|
||||
// Send the outer iframe into the background. This should change the
|
||||
// priority of the inner frame's process to BACKGROUND.
|
||||
var p = expectPriorityChange(childID, 'BACKGROUND');
|
||||
iframe.setVisible(false);
|
||||
return p;
|
||||
}).then(SimpleTest.finish);
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
addEventListener('testready', runTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,77 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test that the preallocated process starts up with priority BACKGROUND.
|
||||
-->
|
||||
<head>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="../browserElementTestHelpers.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
browserElementTestHelpers.setEnabledPref(true);
|
||||
browserElementTestHelpers.addPermission();
|
||||
browserElementTestHelpers.enableProcessPriorityManager();
|
||||
|
||||
var preallocationEnabledPref = null;
|
||||
try {
|
||||
preallocationEnabledPref = SpecialPowers.getBoolPref('dom.ipc.processPrelaunch.enabled');
|
||||
}
|
||||
catch(e) {
|
||||
preallocationEnabledPref = null;
|
||||
}
|
||||
|
||||
var childID = null;
|
||||
|
||||
var cleanedUp = false;
|
||||
function cleanUp()
|
||||
{
|
||||
if (cleanedUp) {
|
||||
return;
|
||||
}
|
||||
|
||||
cleanedUp = true;
|
||||
|
||||
if (preallocationEnabledPref === null) {
|
||||
SpecialPowers.clearUserPref('dom.ipc.processPrelaunch.enabled');
|
||||
} else {
|
||||
SpecialPowers.setBoolPref('dom.ipc.processPrelaunch.enabled',
|
||||
preallocationEnabledPref);
|
||||
}
|
||||
}
|
||||
|
||||
// Even if this test times out, we still want to run cleanUp so as to set the
|
||||
// pref back.
|
||||
addEventListener('unload', cleanUp);
|
||||
|
||||
function runTest()
|
||||
{
|
||||
if (preallocationEnabledPref) {
|
||||
ok(false, "dom.ipc.processPrelaunch.enabled must be " +
|
||||
"false for this test to work.");
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure that the preallocated process initially gets BACKGROUND priority.
|
||||
// That's it.
|
||||
expectProcessCreated().then(function(childID) {
|
||||
return expectPriorityChange(childID, 'BACKGROUND');
|
||||
}).then(function() {
|
||||
cleanUp();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
// Setting this pref to true should cause us to prelaunch a process.
|
||||
SpecialPowers.setBoolPref('dom.ipc.processPrelaunch.enabled', true);
|
||||
}
|
||||
|
||||
addEventListener('testready', runTest);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
50
dom/browser-element/mochitest/priority/test_Simple.html
Normal file
50
dom/browser-element/mochitest/priority/test_Simple.html
Normal file
@ -0,0 +1,50 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
A simple test of the process priority manager.
|
||||
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=844323
|
||||
|
||||
Note: If you run this test alone (i.e. not as part of the larger mochitest
|
||||
suite), you may see some IPC assertions, e.g. "Can't allocate graphics
|
||||
resources."
|
||||
|
||||
What appears to be happening is that we close the Firefox window before the
|
||||
frame we create in this tab finishes starting up. Then the frame finishes
|
||||
loading, and it tries to show itself. But it's too late to show a remote frame
|
||||
at that point, so we kill the child process.
|
||||
|
||||
In other words, I think these errors are nothing to worry about.
|
||||
-->
|
||||
<head>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="../browserElementTestHelpers.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
browserElementTestHelpers.setEnabledPref(true);
|
||||
browserElementTestHelpers.addPermission();
|
||||
browserElementTestHelpers.enableProcessPriorityManager();
|
||||
|
||||
function runTest() {
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('mozbrowser', true);
|
||||
iframe.src = browserElementTestHelpers.emptyPage1;
|
||||
|
||||
expectProcessCreated().then(function(childID) {
|
||||
return expectPriorityChange(childID, 'FOREGROUND');
|
||||
}).then(SimpleTest.finish);
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
addEventListener('testready', runTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
52
dom/browser-element/mochitest/priority/test_Visibility.html
Normal file
52
dom/browser-element/mochitest/priority/test_Visibility.html
Normal file
@ -0,0 +1,52 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test that setVisible() changes a process's priority.
|
||||
-->
|
||||
<head>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="../browserElementTestHelpers.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
browserElementTestHelpers.setEnabledPref(true);
|
||||
browserElementTestHelpers.addPermission();
|
||||
browserElementTestHelpers.enableProcessPriorityManager();
|
||||
|
||||
function runTest() {
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('mozbrowser', true);
|
||||
iframe.src = browserElementTestHelpers.emptyPage1;
|
||||
|
||||
var childID = null;
|
||||
expectOnlyOneProcessCreated().then(function(chid) {
|
||||
childID = chid;
|
||||
return Promise.all(
|
||||
[expectPriorityChange(childID, 'FOREGROUND'),
|
||||
expectMozbrowserEvent(iframe, 'loadend')]);
|
||||
}).then(function() {
|
||||
// Mark the frame as not visible. This should cause its priority to drop
|
||||
// to BACKGROUND.
|
||||
var p = expectPriorityChange(childID, 'BACKGROUND');
|
||||
iframe.setVisible(false);
|
||||
return p;
|
||||
}).then(function() {
|
||||
// Mark the frame as visible again. This should cause its priority change
|
||||
// back to FOREGROUND.
|
||||
var p = expectPriorityChange(childID, 'FOREGROUND');
|
||||
iframe.setVisible(true);
|
||||
return p;
|
||||
}).then(SimpleTest.finish);
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
addEventListener('testready', runTest);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,100 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test that calling setVisible('false') and then sending a low-memory
|
||||
notification causes a WebGL context loss event.
|
||||
-->
|
||||
<head>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="../browserElementTestHelpers.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
browserElementTestHelpers.setEnabledPref(true);
|
||||
browserElementTestHelpers.addPermission();
|
||||
browserElementTestHelpers.enableProcessPriorityManager();
|
||||
|
||||
function runTest() {
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('mozbrowser', true);
|
||||
iframe.src = 'file_WebGLContextLost.html';
|
||||
|
||||
// We use this to ensure that we don't call SimpleTest.finish() twice.
|
||||
var finished = false;
|
||||
function finishOnce() {
|
||||
if (!finished) {
|
||||
SimpleTest.finish();
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
expectMozbrowserEvent(iframe, 'error').then(function(e) {
|
||||
if (finished) {
|
||||
// We don't care if the frame dies after the test finishes.
|
||||
return;
|
||||
}
|
||||
todo(false, "child process is crashing; this probably indicates that " +
|
||||
"something is wrong with WebGL in child processes on your machine.");
|
||||
is(e.detail.type, 'fatal');
|
||||
}).then(finishOnce);
|
||||
|
||||
var childID = null;
|
||||
expectOnlyOneProcessCreated().then(function(chid) {
|
||||
childID = chid;
|
||||
return Promise.all(
|
||||
[expectPriorityChange(childID, 'FOREGROUND'),
|
||||
expectMozbrowserEvent(iframe, 'loadend'),
|
||||
expectMozbrowserEvent(iframe, 'showmodalprompt').then(function(e) {
|
||||
is(e.detail.message, 'ready');
|
||||
})
|
||||
]);
|
||||
}).then(function() {
|
||||
// Fire a low-memory notification once the process goes into the background
|
||||
// due to the setVisible(false) call below.
|
||||
expectPriorityChange(childID, 'BACKGROUND').then(function() {
|
||||
SimpleTest.executeSoon(function() {
|
||||
var os = SpecialPowers.Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(SpecialPowers.Ci.nsIObserverService);
|
||||
os.notifyObservers(null, "memory-pressure", "low-memory");
|
||||
ok(true, 'Successfully notified observers.');
|
||||
});
|
||||
});
|
||||
|
||||
// This test isn't the only possible source of a low-memory notification; the
|
||||
// browser can fire one whenever it likes. So it's fine if we lose the
|
||||
// WebGL context before we fire the low-memory notification ourself.
|
||||
|
||||
var p = expectMozbrowserEvent(iframe, 'showmodalprompt').then(function(e) {
|
||||
is(e.detail.message, 'webglcontextlost');
|
||||
});
|
||||
|
||||
iframe.setVisible(false);
|
||||
return p;
|
||||
}).then(finishOnce);
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
addEventListener('testready', function() {
|
||||
// At the time this test was written, webgl was blocklisted inside child
|
||||
// processes on desktop Linux. The issue is that we spawn a child process to
|
||||
// read driver info, but we only did this on the main prrocess. Child
|
||||
// processes never read the driver info themselves, nor do they get it from
|
||||
// their parent, so they refuse to start up WebGL.
|
||||
//
|
||||
// This isn't a problem on B2G because we force WebGL on there. But it
|
||||
// obviously makes this test difficult. bjacob says forcing WebGL on here
|
||||
// shouldn't hurt things, and anyway this setting mirrors what we do on B2G,
|
||||
// which is what we're trying to test!
|
||||
SpecialPowers.pushPrefEnv({set: [["webgl.force-enabled", true]]},
|
||||
runTest);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -19,7 +19,7 @@ Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Timer.jsm");
|
||||
|
||||
const DB_NAME = "contacts";
|
||||
const DB_VERSION = 8;
|
||||
const DB_VERSION = 10;
|
||||
const STORE_NAME = "contacts";
|
||||
const SAVED_GETALL_STORE_NAME = "getallcache";
|
||||
const CHUNK_SIZE = 20;
|
||||
@ -315,6 +315,50 @@ ContactDB.prototype = {
|
||||
} else if (currVersion == 7) {
|
||||
if (DEBUG) debug("Adding object store for cached searches");
|
||||
db.createObjectStore(SAVED_GETALL_STORE_NAME);
|
||||
} else if (currVersion == 8) {
|
||||
if (DEBUG) debug("Make exactTel only contain the value entered by the user");
|
||||
if (!objectStore) {
|
||||
objectStore = aTransaction.objectStore(STORE_NAME);
|
||||
}
|
||||
|
||||
objectStore.openCursor().onsuccess = function(event) {
|
||||
let cursor = event.target.result;
|
||||
if (cursor) {
|
||||
if (cursor.value.properties.tel) {
|
||||
cursor.value.search.exactTel = [];
|
||||
cursor.value.properties.tel.forEach(
|
||||
function(tel) {
|
||||
let normalized = PhoneNumberUtils.normalize(tel.value.toString());
|
||||
cursor.value.search.exactTel.push(normalized);
|
||||
}
|
||||
);
|
||||
cursor.update(cursor.value);
|
||||
}
|
||||
cursor.continue();
|
||||
}
|
||||
};
|
||||
} else if (currVersion == 9) {
|
||||
if (DEBUG) debug("Add a telMatch index with national and international numbers");
|
||||
objectStore.createIndex("telMatch", "search.parsedTel", {multiEntry: true});
|
||||
objectStore.openCursor().onsuccess = function(event) {
|
||||
let cursor = event.target.result;
|
||||
if (cursor) {
|
||||
if (cursor.value.properties.tel) {
|
||||
cursor.value.search.parsedTel = [];
|
||||
cursor.value.properties.tel.forEach(
|
||||
function(tel) {
|
||||
cursor.value.search.parsedTel.push(parsed.nationalNumber);
|
||||
cursor.value.search.parsedTel.push(PhoneNumberUtils.normalize(parsed.nationalFormat));
|
||||
cursor.value.search.parsedTel.push(parsed.internationalNumber);
|
||||
cursor.value.search.parsedTel.push(PhoneNumberUtils.normalize(parsed.internationalFormat));
|
||||
cursor.value.search.parsedTel.push(PhoneNumberUtils.normalize(tel.value.toString()));
|
||||
}
|
||||
);
|
||||
cursor.update(cursor.value);
|
||||
}
|
||||
cursor.continue();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -402,7 +446,8 @@ ContactDB.prototype = {
|
||||
email: [],
|
||||
category: [],
|
||||
tel: [],
|
||||
exactTel: []
|
||||
exactTel: [],
|
||||
parsedTel: []
|
||||
};
|
||||
|
||||
for (let field in aContact.properties) {
|
||||
@ -417,9 +462,11 @@ ContactDB.prototype = {
|
||||
|
||||
// Chop off the first characters
|
||||
let number = aContact.properties[field][i].value;
|
||||
contact.search.exactTel.push(number);
|
||||
let search = {};
|
||||
if (number) {
|
||||
number = number.toString();
|
||||
contact.search.exactTel.push(PhoneNumberUtils.normalize(number));
|
||||
contact.search.parsedTel.push(PhoneNumberUtils.normalize(number));
|
||||
for (let i = 0; i < number.length; i++) {
|
||||
search[number.substring(i, number.length)] = 1;
|
||||
}
|
||||
@ -440,9 +487,14 @@ ContactDB.prototype = {
|
||||
debug("NationalNumber: " + parsedNumber.nationalNumber);
|
||||
debug("NationalFormat: " + parsedNumber.nationalFormat);
|
||||
}
|
||||
|
||||
contact.search.parsedTel.push(parsedNumber.nationalNumber);
|
||||
contact.search.parsedTel.push(PhoneNumberUtils.normalize(parsedNumber.nationalFormat));
|
||||
contact.search.parsedTel.push(parsedNumber.internationalNumber);
|
||||
contact.search.parsedTel.push(PhoneNumberUtils.normalize(parsedNumber.internationalFormat));
|
||||
|
||||
if (parsedNumber.internationalNumber &&
|
||||
number.toString() !== parsedNumber.internationalNumber) {
|
||||
contact.search.exactTel.push(parsedNumber.internationalNumber);
|
||||
number !== parsedNumber.internationalNumber) {
|
||||
let digits = parsedNumber.internationalNumber.match(/\d/g);
|
||||
if (digits) {
|
||||
digits = digits.join('');
|
||||
@ -731,7 +783,7 @@ ContactDB.prototype = {
|
||||
if (DEBUG) debug("ContactDB:find val:" + aOptions.filterValue + " by: " + aOptions.filterBy + " op: " + aOptions.filterOp);
|
||||
let self = this;
|
||||
this.newTxn("readonly", STORE_NAME, function (txn, store) {
|
||||
if (aOptions && (aOptions.filterOp == "equals" || aOptions.filterOp == "contains")) {
|
||||
if (aOptions && (["equals", "contains", "match"].indexOf(aOptions.filterOp) >= 0)) {
|
||||
self._findWithIndex(txn, store, aOptions);
|
||||
} else {
|
||||
self._findAll(txn, store, aOptions);
|
||||
@ -775,12 +827,24 @@ ContactDB.prototype = {
|
||||
if (DEBUG) debug("Getting index: " + key);
|
||||
// case sensitive
|
||||
let index = store.index(key);
|
||||
request = index.mozGetAll(options.filterValue, limit);
|
||||
let filterValue = options.filterValue;
|
||||
if (key == "tel") {
|
||||
filterValue = PhoneNumberUtils.normalize(filterValue);
|
||||
}
|
||||
request = index.mozGetAll(filterValue, limit);
|
||||
} else if (options.filterOp == "match") {
|
||||
if (DEBUG) debug("match");
|
||||
if (key != "tel") {
|
||||
dump("ContactDB: 'match' filterOp only works on tel\n");
|
||||
return txn.abort();
|
||||
}
|
||||
|
||||
let index = store.index("telMatch");
|
||||
let normalized = PhoneNumberUtils.normalize(options.filterValue)
|
||||
request = index.mozGetAll(normalized, limit);
|
||||
} else {
|
||||
// not case sensitive
|
||||
let tmp = typeof options.filterValue == "string"
|
||||
? options.filterValue.toLowerCase()
|
||||
: options.filterValue.toString().toLowerCase();
|
||||
let tmp = options.filterValue.toString().toLowerCase();
|
||||
if (key === 'tel') {
|
||||
let digits = tmp.match(/\d/g);
|
||||
if (digits) {
|
||||
|
@ -87,7 +87,7 @@ var properties1 = {
|
||||
familyName: ["TestFamilyName","Wagner"],
|
||||
givenName: ["Test1","Test2"],
|
||||
nickname: "nicktest",
|
||||
tel: [{type: ["work"], value: "123456", carrier: "testCarrier"} , {type: ["home", "fax"], value: "+9-876-5432"}],
|
||||
tel: [{type: ["work"], value: "123456", carrier: "testCarrier"} , {type: ["home", "fax"], value: "+55 (31) 9876-3456"}, {type: ["home"], value: "+49 451 491934"}],
|
||||
adr: adr1,
|
||||
email: [{type: ["work"], value: "x@y.com"}]
|
||||
};
|
||||
@ -333,12 +333,12 @@ var steps = [
|
||||
checkContacts(createResult1, properties1);
|
||||
dump("findResult: " + JSON.stringify(findResult1) + "\n");
|
||||
// Some manual testing. Testint the testfunctions
|
||||
// tel: [{type: ["work"], value: "123456", carrier: "testCarrier"} , {type: ["home", "fax"], value: "+9-876-5432"}],
|
||||
// tel: [{type: ["work"], value: "123456", carrier: "testCarrier"} , {type: ["home", "fax"], value: "+55 (31) 9876-3456"}],
|
||||
is(findResult1.tel[0].carrier, "testCarrier", "Same Carrier");
|
||||
is(findResult1.tel[0].type, "work", "Same type");
|
||||
is(findResult1.tel[0].value, "123456", "Same Value");
|
||||
is(findResult1.tel[1].type[1], "fax", "Same type");
|
||||
is(findResult1.tel[1].value, "+9-876-5432", "Same Value");
|
||||
is(findResult1.tel[1].value, "+55 (31) 9876-3456", "Same Value");
|
||||
|
||||
is(findResult1.adr[0].countryName, "country 1", "Same country");
|
||||
|
||||
@ -503,6 +503,117 @@ var steps = [
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Retrieving by tel exact");
|
||||
var options = {filterBy: ["tel"],
|
||||
filterOp: "equals",
|
||||
filterValue: "+55 319 8 7 6 3456"};
|
||||
req = mozContacts.find(options);
|
||||
req.onsuccess = function () {
|
||||
is(req.result.length, 1, "Found exactly 1 contact.");
|
||||
findResult1 = req.result[0];
|
||||
ok(findResult1.id == sample_id1, "Same ID");
|
||||
checkContacts(createResult1, properties1);
|
||||
next();
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Retrieving by tel exact with substring");
|
||||
var options = {filterBy: ["tel"],
|
||||
filterOp: "equals",
|
||||
filterValue: "3456"};
|
||||
req = mozContacts.find(options);
|
||||
req.onsuccess = function () {
|
||||
is(req.result.length, 0, "Found no contacts.");
|
||||
next();
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Retrieving by tel exact with substring");
|
||||
var options = {filterBy: ["tel"],
|
||||
filterOp: "equals",
|
||||
filterValue: "+55 (31)"};
|
||||
req = mozContacts.find(options);
|
||||
req.onsuccess = function () {
|
||||
is(req.result.length, 0, "Found no contacts.");
|
||||
next();
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Retrieving by tel match national number");
|
||||
var options = {filterBy: ["tel"],
|
||||
filterOp: "match",
|
||||
filterValue: "3198763456"};
|
||||
req = mozContacts.find(options);
|
||||
req.onsuccess = function () {
|
||||
is(req.result.length, 1, "Found exactly 1 contact.");
|
||||
findResult1 = req.result[0];
|
||||
ok(findResult1.id == sample_id1, "Same ID");
|
||||
checkContacts(createResult1, properties1);
|
||||
next();
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Retrieving by tel match national format");
|
||||
var options = {filterBy: ["tel"],
|
||||
filterOp: "match",
|
||||
filterValue: "0451 491934"};
|
||||
req = mozContacts.find(options);
|
||||
req.onsuccess = function () {
|
||||
is(req.result.length, 1, "Found exactly 1 contact.");
|
||||
findResult1 = req.result[0];
|
||||
ok(findResult1.id == sample_id1, "Same ID");
|
||||
checkContacts(createResult1, properties1);
|
||||
next();
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Retrieving by tel match entered number");
|
||||
var options = {filterBy: ["tel"],
|
||||
filterOp: "match",
|
||||
filterValue: "123456"};
|
||||
req = mozContacts.find(options);
|
||||
req.onsuccess = function () {
|
||||
is(req.result.length, 1, "Found exactly 1 contact.");
|
||||
findResult1 = req.result[0];
|
||||
ok(findResult1.id == sample_id1, "Same ID");
|
||||
checkContacts(createResult1, properties1);
|
||||
next();
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Retrieving by tel match international number");
|
||||
var options = {filterBy: ["tel"],
|
||||
filterOp: "match",
|
||||
filterValue: "+55 31 98763456"};
|
||||
req = mozContacts.find(options);
|
||||
req.onsuccess = function () {
|
||||
is(req.result.length, 1, "Found exactly 1 contact.");
|
||||
findResult1 = req.result[0];
|
||||
ok(findResult1.id == sample_id1, "Same ID");
|
||||
checkContacts(createResult1, properties1);
|
||||
next();
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Retrieving by match with field other than tel");
|
||||
var options = {filterBy: ["givenName"],
|
||||
filterOp: "match",
|
||||
filterValue: "my friends call me 555-4040"};
|
||||
req = mozContacts.find(options);
|
||||
req.onsuccess = onUnwantedSuccess;
|
||||
req.onerror = function() {
|
||||
ok(true, "Failed");
|
||||
next();
|
||||
}
|
||||
},
|
||||
function () {
|
||||
ok(true, "Retrieving by substring tel2");
|
||||
var options = {filterBy: ["tel"],
|
||||
@ -522,7 +633,7 @@ var steps = [
|
||||
ok(true, "Retrieving by substring tel3");
|
||||
var options = {filterBy: ["tel"],
|
||||
filterOp: "contains",
|
||||
filterValue: "98765432"};
|
||||
filterValue: "98763456"};
|
||||
req = mozContacts.find(options);
|
||||
req.onsuccess = function () {
|
||||
is(req.result.length, 1, "Found exactly 1 contact.");
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include "nsJSEnvironment.h"
|
||||
#include "SandboxHal.h"
|
||||
#include "nsDebugImpl.h"
|
||||
#include "nsHashPropertyBag.h"
|
||||
#include "nsLayoutStylesheetCache.h"
|
||||
|
||||
#include "IHistory.h"
|
||||
@ -571,12 +572,6 @@ ContentChild::RecvPBrowserConstructor(PBrowserChild* actor,
|
||||
{
|
||||
// This runs after AllocPBrowser() returns and the IPC machinery for this
|
||||
// PBrowserChild has been set up.
|
||||
//
|
||||
// We have to NotifyObservers("tab-child-created") before we
|
||||
// TemporarilyLockProcessPriority because the NotifyObservers call may cause
|
||||
// us to initialize the ProcessPriorityManager, and
|
||||
// TemporarilyLockProcessPriority only works after the
|
||||
// ProcessPriorityManager has been initialized.
|
||||
|
||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
if (os) {
|
||||
@ -592,13 +587,6 @@ ContentChild::RecvPBrowserConstructor(PBrowserChild* actor,
|
||||
MOZ_ASSERT(!sFirstIdleTask);
|
||||
sFirstIdleTask = NewRunnableFunction(FirstIdle);
|
||||
MessageLoop::current()->PostIdleTask(FROM_HERE, sFirstIdleTask);
|
||||
|
||||
// We are either a brand-new process loading its first PBrowser, or we
|
||||
// are the preallocated process transforming into a particular
|
||||
// app/browser. Either way, our parent has already set our process
|
||||
// priority, and we want to leave it there for a few seconds while we
|
||||
// start up.
|
||||
TemporarilyLockProcessPriority();
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -1208,5 +1196,57 @@ ContentChild::RecvFileSystemUpdate(const nsString& aFsName,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvNotifyProcessPriorityChanged(
|
||||
const hal::ProcessPriority& aPriority)
|
||||
{
|
||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
NS_ENSURE_TRUE(os, true);
|
||||
|
||||
nsRefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
|
||||
props->Init();
|
||||
props->SetPropertyAsInt32(NS_LITERAL_STRING("priority"),
|
||||
static_cast<int32_t>(aPriority));
|
||||
|
||||
os->NotifyObservers(static_cast<nsIPropertyBag2*>(props),
|
||||
"ipc:process-priority-changed", nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvMinimizeMemoryUsage()
|
||||
{
|
||||
nsCOMPtr<nsIMemoryReporterManager> mgr =
|
||||
do_GetService("@mozilla.org/memory-reporter-manager;1");
|
||||
NS_ENSURE_TRUE(mgr, true);
|
||||
|
||||
nsCOMPtr<nsICancelableRunnable> runnable =
|
||||
do_QueryReferent(mMemoryMinimizerRunnable);
|
||||
|
||||
// Cancel the previous task if it's still pending.
|
||||
if (runnable) {
|
||||
runnable->Cancel();
|
||||
runnable = nullptr;
|
||||
}
|
||||
|
||||
mgr->MinimizeMemoryUsage(/* callback = */ nullptr,
|
||||
getter_AddRefs(runnable));
|
||||
mMemoryMinimizerRunnable = do_GetWeakReference(runnable);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvCancelMinimizeMemoryUsage()
|
||||
{
|
||||
nsCOMPtr<nsICancelableRunnable> runnable =
|
||||
do_QueryReferent(mMemoryMinimizerRunnable);
|
||||
if (runnable) {
|
||||
runnable->Cancel();
|
||||
mMemoryMinimizerRunnable = nullptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -192,6 +192,10 @@ public:
|
||||
const int32_t& aState,
|
||||
const int32_t& aMountGeneration);
|
||||
|
||||
virtual bool RecvNotifyProcessPriorityChanged(const hal::ProcessPriority& aPriority);
|
||||
virtual bool RecvMinimizeMemoryUsage();
|
||||
virtual bool RecvCancelMinimizeMemoryUsage();
|
||||
|
||||
#ifdef ANDROID
|
||||
gfxIntSize GetScreenSize() { return mScreenSize; }
|
||||
#endif
|
||||
@ -244,6 +248,7 @@ private:
|
||||
bool mIsForApp;
|
||||
bool mIsForBrowser;
|
||||
nsString mProcessName;
|
||||
nsWeakPtr mMemoryMinimizerRunnable;
|
||||
|
||||
static ContentChild* sSingleton;
|
||||
|
||||
|
@ -59,6 +59,7 @@
|
||||
#include "nsFrameMessageManager.h"
|
||||
#include "nsHashPropertyBag.h"
|
||||
#include "nsIAlertsService.h"
|
||||
#include "nsIAppsService.h"
|
||||
#include "nsIClipboard.h"
|
||||
#include "nsIDOMApplicationRegistry.h"
|
||||
#include "nsIDOMGeoGeolocation.h"
|
||||
@ -80,6 +81,8 @@
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsToolkitCompsCID.h"
|
||||
#include "nsWidgetsCID.h"
|
||||
#include "PreallocatedProcessManager.h"
|
||||
#include "ProcessPriorityManager.h"
|
||||
#include "SandboxHal.h"
|
||||
#include "StructuredCloneUtils.h"
|
||||
#include "TabParent.h"
|
||||
@ -194,9 +197,10 @@ MemoryReportRequestParent::~MemoryReportRequestParent()
|
||||
MOZ_COUNT_DTOR(MemoryReportRequestParent);
|
||||
}
|
||||
|
||||
nsDataHashtable<nsStringHashKey, ContentParent*>* ContentParent::gAppContentParents;
|
||||
nsTArray<ContentParent*>* ContentParent::gNonAppContentParents;
|
||||
nsTArray<ContentParent*>* ContentParent::gPrivateContent;
|
||||
nsDataHashtable<nsStringHashKey, ContentParent*>* ContentParent::sAppContentParents;
|
||||
nsTArray<ContentParent*>* ContentParent::sNonAppContentParents;
|
||||
nsTArray<ContentParent*>* ContentParent::sPrivateContent;
|
||||
LinkedList<ContentParent> ContentParent::sContentParents;
|
||||
|
||||
// This is true when subprocess launching is enabled. This is the
|
||||
// case between StartUp() and ShutDown() or JoinAllSubprocesses().
|
||||
@ -205,60 +209,28 @@ static bool sCanLaunchSubprocesses;
|
||||
// The first content child has ID 1, so the chrome process can have ID 0.
|
||||
static uint64_t gContentChildID = 1;
|
||||
|
||||
// Try to keep an app process always preallocated, to get
|
||||
// initialization off the critical path of app startup.
|
||||
static bool sKeepAppProcessPreallocated;
|
||||
static StaticRefPtr<ContentParent> sPreallocatedAppProcess;
|
||||
static CancelableTask* sPreallocateAppProcessTask;
|
||||
// This number is fairly arbitrary ... the intention is to put off
|
||||
// launching another app process until the last one has finished
|
||||
// loading its content, to reduce CPU/memory/IO contention.
|
||||
static int sPreallocateDelayMs;
|
||||
// We want the prelaunched process to know that it's for apps, but not
|
||||
// actually for any app in particular. Use a magic manifest URL.
|
||||
// Can't be a static constant.
|
||||
#define MAGIC_PREALLOCATED_APP_MANIFEST_URL NS_LITERAL_STRING("{{template}}")
|
||||
|
||||
/*static*/ void
|
||||
// PreallocateAppProcess is called by the PreallocatedProcessManager.
|
||||
// ContentParent then takes this process back within
|
||||
// MaybeTakePreallocatedAppProcess.
|
||||
|
||||
/*static*/ already_AddRefed<ContentParent>
|
||||
ContentParent::PreallocateAppProcess()
|
||||
{
|
||||
MOZ_ASSERT(!sPreallocatedAppProcess);
|
||||
|
||||
if (sPreallocateAppProcessTask) {
|
||||
// We were called directly while a delayed task was scheduled.
|
||||
sPreallocateAppProcessTask->Cancel();
|
||||
sPreallocateAppProcessTask = nullptr;
|
||||
}
|
||||
|
||||
sPreallocatedAppProcess =
|
||||
new ContentParent(MAGIC_PREALLOCATED_APP_MANIFEST_URL,
|
||||
/*isBrowserElement=*/false,
|
||||
nsRefPtr<ContentParent> process =
|
||||
new ContentParent(/* app = */ nullptr,
|
||||
/* isForBrowserElement = */ false,
|
||||
/* isForPreallocated = */ true,
|
||||
// Final privileges are set when we
|
||||
// transform into our app.
|
||||
base::PRIVILEGES_INHERIT,
|
||||
PROCESS_PRIORITY_BACKGROUND);
|
||||
sPreallocatedAppProcess->Init();
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
ContentParent::DelayedPreallocateAppProcess()
|
||||
{
|
||||
sPreallocateAppProcessTask = nullptr;
|
||||
if (!sPreallocatedAppProcess) {
|
||||
PreallocateAppProcess();
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
ContentParent::ScheduleDelayedPreallocateAppProcess()
|
||||
{
|
||||
if (!sKeepAppProcessPreallocated || sPreallocateAppProcessTask) {
|
||||
return;
|
||||
}
|
||||
sPreallocateAppProcessTask =
|
||||
NewRunnableFunction(DelayedPreallocateAppProcess);
|
||||
MessageLoop::current()->PostDelayedTask(
|
||||
FROM_HERE, sPreallocateAppProcessTask, sPreallocateDelayMs);
|
||||
process->Init();
|
||||
return process.forget();
|
||||
}
|
||||
|
||||
/*static*/ already_AddRefed<ContentParent>
|
||||
@ -266,9 +238,7 @@ ContentParent::MaybeTakePreallocatedAppProcess(const nsAString& aAppManifestURL,
|
||||
ChildPrivileges aPrivs,
|
||||
ProcessPriority aInitialPriority)
|
||||
{
|
||||
nsRefPtr<ContentParent> process = sPreallocatedAppProcess.get();
|
||||
sPreallocatedAppProcess = nullptr;
|
||||
|
||||
nsRefPtr<ContentParent> process = PreallocatedProcessManager::Take();
|
||||
if (!process) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -284,14 +254,6 @@ ContentParent::MaybeTakePreallocatedAppProcess(const nsAString& aAppManifestURL,
|
||||
return process.forget();
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
ContentParent::FirstIdle(void)
|
||||
{
|
||||
// The parent has gone idle for the first time. This would be a good
|
||||
// time to preallocate an app process.
|
||||
ScheduleDelayedPreallocateAppProcess();
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
ContentParent::StartUp()
|
||||
{
|
||||
@ -299,22 +261,10 @@ ContentParent::StartUp()
|
||||
return;
|
||||
}
|
||||
|
||||
sKeepAppProcessPreallocated =
|
||||
Preferences::GetBool("dom.ipc.processPrelaunch.enabled", false);
|
||||
if (sKeepAppProcessPreallocated) {
|
||||
ClearOnShutdown(&sPreallocatedAppProcess);
|
||||
|
||||
sPreallocateDelayMs = Preferences::GetUint(
|
||||
"dom.ipc.processPrelaunch.delayMs", 1000);
|
||||
|
||||
MOZ_ASSERT(!sPreallocateAppProcessTask);
|
||||
|
||||
// Let's not slow down the main process initialization. Wait until
|
||||
// the main process goes idle before we preallocate a process
|
||||
MessageLoop::current()->PostIdleTask(FROM_HERE, NewRunnableFunction(FirstIdle));
|
||||
}
|
||||
|
||||
sCanLaunchSubprocesses = true;
|
||||
|
||||
// Try to preallocate a process that we can transform into an app later.
|
||||
PreallocatedProcessManager::AllocateAfterDelay();
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
@ -376,27 +326,28 @@ ContentParent::JoinAllSubprocesses()
|
||||
/*static*/ already_AddRefed<ContentParent>
|
||||
ContentParent::GetNewOrUsed(bool aForBrowserElement)
|
||||
{
|
||||
if (!gNonAppContentParents)
|
||||
gNonAppContentParents = new nsTArray<ContentParent*>();
|
||||
if (!sNonAppContentParents)
|
||||
sNonAppContentParents = new nsTArray<ContentParent*>();
|
||||
|
||||
int32_t maxContentProcesses = Preferences::GetInt("dom.ipc.processCount", 1);
|
||||
if (maxContentProcesses < 1)
|
||||
maxContentProcesses = 1;
|
||||
|
||||
if (gNonAppContentParents->Length() >= uint32_t(maxContentProcesses)) {
|
||||
uint32_t idx = rand() % gNonAppContentParents->Length();
|
||||
nsRefPtr<ContentParent> p = (*gNonAppContentParents)[idx];
|
||||
NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in gNonAppContentParents?");
|
||||
if (sNonAppContentParents->Length() >= uint32_t(maxContentProcesses)) {
|
||||
uint32_t idx = rand() % sNonAppContentParents->Length();
|
||||
nsRefPtr<ContentParent> p = (*sNonAppContentParents)[idx];
|
||||
NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sNonAppContentParents?");
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
nsRefPtr<ContentParent> p =
|
||||
new ContentParent(/* appManifestURL = */ EmptyString(),
|
||||
new ContentParent(/* app = */ nullptr,
|
||||
aForBrowserElement,
|
||||
/* isForPreallocated = */ false,
|
||||
base::PRIVILEGES_DEFAULT,
|
||||
PROCESS_PRIORITY_FOREGROUND);
|
||||
p->Init();
|
||||
gNonAppContentParents->AppendElement(p);
|
||||
sNonAppContentParents->AppendElement(p);
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
@ -482,10 +433,10 @@ ContentParent::CreateBrowserOrApp(const TabContext& aContext,
|
||||
// !HasOwnApp() branch above.
|
||||
nsCOMPtr<mozIApplication> ownApp = aContext.GetOwnApp();
|
||||
|
||||
if (!gAppContentParents) {
|
||||
gAppContentParents =
|
||||
if (!sAppContentParents) {
|
||||
sAppContentParents =
|
||||
new nsDataHashtable<nsStringHashKey, ContentParent*>();
|
||||
gAppContentParents->Init();
|
||||
sAppContentParents->Init();
|
||||
}
|
||||
|
||||
// Each app gets its own ContentParent instance.
|
||||
@ -497,7 +448,7 @@ ContentParent::CreateBrowserOrApp(const TabContext& aContext,
|
||||
|
||||
ProcessPriority initialPriority = GetInitialProcessPriority(aFrameElement);
|
||||
|
||||
nsRefPtr<ContentParent> p = gAppContentParents->Get(manifestURL);
|
||||
nsRefPtr<ContentParent> p = sAppContentParents->Get(manifestURL);
|
||||
if (p) {
|
||||
// Check that the process is still alive and set its priority.
|
||||
// Hopefully the process won't die after this point, if this call
|
||||
@ -513,61 +464,36 @@ ContentParent::CreateBrowserOrApp(const TabContext& aContext,
|
||||
initialPriority);
|
||||
if (!p) {
|
||||
NS_WARNING("Unable to use pre-allocated app process");
|
||||
p = new ContentParent(manifestURL, /* isBrowserElement = */ false,
|
||||
privs, initialPriority);
|
||||
p = new ContentParent(ownApp,
|
||||
/* isForBrowserElement = */ false,
|
||||
/* isForPreallocated = */ false,
|
||||
privs,
|
||||
initialPriority);
|
||||
p->Init();
|
||||
}
|
||||
gAppContentParents->Put(manifestURL, p);
|
||||
sAppContentParents->Put(manifestURL, p);
|
||||
}
|
||||
|
||||
nsRefPtr<TabParent> tp = new TabParent(aContext);
|
||||
tp->SetOwnerElement(aFrameElement);
|
||||
PBrowserParent* browser = p->SendPBrowserConstructor(
|
||||
tp.forget().get(), // DeallocPBrowserParent() releases this ref.
|
||||
nsRefPtr<TabParent>(tp).forget().get(), // DeallocPBrowserParent() releases this ref.
|
||||
aContext.AsIPCTabContext(),
|
||||
/* chromeFlags */ 0);
|
||||
|
||||
// Send the frame element's mozapptype down to the child process. This ends
|
||||
// up in TabChild::GetAppType(). We have to do this /before/ we acquire the
|
||||
// CPU wake lock for this process, because if the child sees that it has a
|
||||
// CPU wake lock but its TabChild doesn't have the right mozapptype, it
|
||||
// might downgrade its process priority.
|
||||
nsCOMPtr<Element> frameElement = do_QueryInterface(aFrameElement);
|
||||
if (frameElement) {
|
||||
nsAutoString appType;
|
||||
frameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::mozapptype, appType);
|
||||
unused << browser->SendSetAppType(appType);
|
||||
}
|
||||
|
||||
p->MaybeTakeCPUWakeLock(aFrameElement);
|
||||
|
||||
return static_cast<TabParent*>(browser);
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
AppendToTArray(const nsAString& aKey, ContentParent* aValue, void* aArray)
|
||||
{
|
||||
nsTArray<ContentParent*> *array =
|
||||
static_cast<nsTArray<ContentParent*>*>(aArray);
|
||||
array->AppendElement(aValue);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
ContentParent::GetAll(nsTArray<ContentParent*>& aArray)
|
||||
{
|
||||
aArray.Clear();
|
||||
|
||||
if (gNonAppContentParents) {
|
||||
aArray.AppendElements(*gNonAppContentParents);
|
||||
}
|
||||
|
||||
if (gAppContentParents) {
|
||||
gAppContentParents->EnumerateRead(&AppendToTArray, &aArray);
|
||||
}
|
||||
|
||||
if (sPreallocatedAppProcess) {
|
||||
aArray.AppendElement(sPreallocatedAppProcess);
|
||||
for (ContentParent* cp = sContentParents.getFirst(); cp;
|
||||
cp = cp->getNext()) {
|
||||
aArray.AppendElement(cp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -697,17 +623,6 @@ NS_IMPL_ISUPPORTS1(SystemMessageHandledListener,
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void
|
||||
ContentParent::SetProcessPriority(ProcessPriority aPriority)
|
||||
{
|
||||
if (!Preferences::GetBool("dom.ipc.processPriorityManager.enabled")) {
|
||||
return;
|
||||
}
|
||||
|
||||
hal::SetProcessPriority(base::GetProcId(mSubprocess->GetChildProcessHandle()),
|
||||
aPriority);
|
||||
}
|
||||
|
||||
void
|
||||
ContentParent::MaybeTakeCPUWakeLock(nsIDOMElement* aFrameElement)
|
||||
{
|
||||
@ -735,7 +650,7 @@ ContentParent::MaybeTakeCPUWakeLock(nsIDOMElement* aFrameElement)
|
||||
bool
|
||||
ContentParent::SetPriorityAndCheckIsAlive(ProcessPriority aPriority)
|
||||
{
|
||||
SetProcessPriority(aPriority);
|
||||
ProcessPriorityManager::SetProcessPriority(this, aPriority);
|
||||
|
||||
// Now that we've set this process's priority, check whether the process is
|
||||
// still alive. Hopefully we've set the priority to FOREGROUND*, so the
|
||||
@ -754,14 +669,38 @@ ContentParent::SetPriorityAndCheckIsAlive(ProcessPriority aPriority)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Helper for ContentParent::TransformPreallocatedIntoApp.
|
||||
static void
|
||||
TryGetNameFromManifestURL(const nsAString& aManifestURL,
|
||||
nsAString& aName)
|
||||
{
|
||||
aName.Truncate();
|
||||
if (aManifestURL.IsEmpty() ||
|
||||
aManifestURL == MAGIC_PREALLOCATED_APP_MANIFEST_URL) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
|
||||
NS_ENSURE_TRUE_VOID(appsService);
|
||||
|
||||
nsCOMPtr<mozIDOMApplication> domApp;
|
||||
appsService->GetAppByManifestURL(aManifestURL, getter_AddRefs(domApp));
|
||||
|
||||
nsCOMPtr<mozIApplication> app = do_QueryInterface(domApp);
|
||||
if (!app) {
|
||||
return;
|
||||
}
|
||||
|
||||
app->GetName(aName);
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::TransformPreallocatedIntoApp(const nsAString& aAppManifestURL,
|
||||
ChildPrivileges aPrivs)
|
||||
{
|
||||
MOZ_ASSERT(mAppManifestURL == MAGIC_PREALLOCATED_APP_MANIFEST_URL);
|
||||
// Clients should think of mAppManifestURL as const ... we're
|
||||
// bending the rules here just for the preallocation hack.
|
||||
const_cast<nsString&>(mAppManifestURL) = aAppManifestURL;
|
||||
MOZ_ASSERT(IsPreallocated());
|
||||
mAppManifestURL = aAppManifestURL;
|
||||
TryGetNameFromManifestURL(aAppManifestURL, mAppName);
|
||||
|
||||
return SendSetProcessPrivileges(aPrivs);
|
||||
}
|
||||
@ -790,30 +729,35 @@ void
|
||||
ContentParent::MarkAsDead()
|
||||
{
|
||||
if (!mAppManifestURL.IsEmpty()) {
|
||||
if (gAppContentParents) {
|
||||
gAppContentParents->Remove(mAppManifestURL);
|
||||
if (!gAppContentParents->Count()) {
|
||||
delete gAppContentParents;
|
||||
gAppContentParents = NULL;
|
||||
if (sAppContentParents) {
|
||||
sAppContentParents->Remove(mAppManifestURL);
|
||||
if (!sAppContentParents->Count()) {
|
||||
delete sAppContentParents;
|
||||
sAppContentParents = NULL;
|
||||
}
|
||||
}
|
||||
} else if (gNonAppContentParents) {
|
||||
gNonAppContentParents->RemoveElement(this);
|
||||
if (!gNonAppContentParents->Length()) {
|
||||
delete gNonAppContentParents;
|
||||
gNonAppContentParents = NULL;
|
||||
} else if (sNonAppContentParents) {
|
||||
sNonAppContentParents->RemoveElement(this);
|
||||
if (!sNonAppContentParents->Length()) {
|
||||
delete sNonAppContentParents;
|
||||
sNonAppContentParents = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (gPrivateContent) {
|
||||
gPrivateContent->RemoveElement(this);
|
||||
if (!gPrivateContent->Length()) {
|
||||
delete gPrivateContent;
|
||||
gPrivateContent = NULL;
|
||||
if (sPrivateContent) {
|
||||
sPrivateContent->RemoveElement(this);
|
||||
if (!sPrivateContent->Length()) {
|
||||
delete sPrivateContent;
|
||||
sPrivateContent = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
mIsAlive = false;
|
||||
|
||||
// Remove from sContentParents.
|
||||
if (isInList()) {
|
||||
remove();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -922,10 +866,6 @@ ContentParent::ActorDestroy(ActorDestroyReason why)
|
||||
#endif
|
||||
}
|
||||
|
||||
if (sPreallocatedAppProcess == this) {
|
||||
sPreallocatedAppProcess = nullptr;
|
||||
}
|
||||
|
||||
mMessageManager->Disconnect();
|
||||
|
||||
// clear the child memory reporters
|
||||
@ -1068,8 +1008,9 @@ ContentParent::GetTestShellSingleton()
|
||||
return static_cast<TestShellParent*>(ManagedPTestShellParent()[0]);
|
||||
}
|
||||
|
||||
ContentParent::ContentParent(const nsAString& aAppManifestURL,
|
||||
ContentParent::ContentParent(mozIApplication* aApp,
|
||||
bool aIsForBrowser,
|
||||
bool aIsForPreallocated,
|
||||
ChildPrivileges aOSPrivileges,
|
||||
ProcessPriority aInitialPriority /* = PROCESS_PRIORITY_FOREGROUND */)
|
||||
: mSubprocess(nullptr)
|
||||
@ -1078,7 +1019,6 @@ ContentParent::ContentParent(const nsAString& aAppManifestURL,
|
||||
, mGeolocationWatchID(-1)
|
||||
, mRunToCompletionDepth(0)
|
||||
, mShouldCallUnblockChild(false)
|
||||
, mAppManifestURL(aAppManifestURL)
|
||||
, mForceKillTask(nullptr)
|
||||
, mNumDestroyingTabs(0)
|
||||
, mIsAlive(true)
|
||||
@ -1086,6 +1026,20 @@ ContentParent::ContentParent(const nsAString& aAppManifestURL,
|
||||
, mSendPermissionUpdates(false)
|
||||
, mIsForBrowser(aIsForBrowser)
|
||||
{
|
||||
// No more than one of !!aApp, aIsForBrowser, and aIsForPreallocated should
|
||||
// be true.
|
||||
MOZ_ASSERT(!!aApp + aIsForBrowser + aIsForPreallocated <= 1);
|
||||
|
||||
// Insert ourselves into the global linked list of ContentParent objects.
|
||||
sContentParents.insertBack(this);
|
||||
|
||||
if (aApp) {
|
||||
aApp->GetManifestURL(mAppManifestURL);
|
||||
aApp->GetName(mAppName);
|
||||
} else if (aIsForPreallocated) {
|
||||
mAppManifestURL = MAGIC_PREALLOCATED_APP_MANIFEST_URL;
|
||||
}
|
||||
|
||||
// From this point on, NS_WARNING, NS_ASSERTION, etc. should print out the
|
||||
// PID along with the warning.
|
||||
nsDebugImpl::SetMultiprocessMode("Parent");
|
||||
@ -1096,13 +1050,16 @@ ContentParent::ContentParent(const nsAString& aAppManifestURL,
|
||||
|
||||
mSubprocess->LaunchAndWaitForProcessHandle();
|
||||
|
||||
// Set the subprocess's priority. We do this first because we're likely
|
||||
// /lowering/ its CPU and memory priority, which it has inherited from this
|
||||
// process.
|
||||
SetProcessPriority(aInitialPriority);
|
||||
|
||||
Open(mSubprocess->GetChannel(), mSubprocess->GetChildProcessHandle());
|
||||
|
||||
// Set the subprocess's priority. We do this early on because we're likely
|
||||
// /lowering/ the process's CPU and memory priority, which it has inherited
|
||||
// from this process.
|
||||
//
|
||||
// This call can cause us to send IPC messages to the child process, so it
|
||||
// must come after the Open() call above.
|
||||
ProcessPriorityManager::SetProcessPriority(this, aInitialPriority);
|
||||
|
||||
// NB: internally, this will send an IPC message to the child
|
||||
// process to get it to create the CompositorChild. This
|
||||
// message goes through the regular IPC queue for this
|
||||
@ -1149,17 +1106,17 @@ ContentParent::~ContentParent()
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
// We should be removed from all these lists in ActorDestroy.
|
||||
MOZ_ASSERT(!gPrivateContent || !gPrivateContent->Contains(this));
|
||||
MOZ_ASSERT(!sPrivateContent || !sPrivateContent->Contains(this));
|
||||
if (mAppManifestURL.IsEmpty()) {
|
||||
MOZ_ASSERT(!gNonAppContentParents ||
|
||||
!gNonAppContentParents->Contains(this));
|
||||
MOZ_ASSERT(!sNonAppContentParents ||
|
||||
!sNonAppContentParents->Contains(this));
|
||||
} else {
|
||||
// In general, we expect gAppContentParents->Get(mAppManifestURL) to be
|
||||
// In general, we expect sAppContentParents->Get(mAppManifestURL) to be
|
||||
// NULL. But it could be that we created another ContentParent for this
|
||||
// app after we did this->ActorDestroy(), so the right check is that
|
||||
// gAppContentParent->Get(mAppManifestURL) != this.
|
||||
MOZ_ASSERT(!gAppContentParents ||
|
||||
gAppContentParents->Get(mAppManifestURL) != this);
|
||||
MOZ_ASSERT(!sAppContentParents ||
|
||||
sAppContentParents->Get(mAppManifestURL) != this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1380,11 +1337,11 @@ ContentParent::RecvGetShowPasswordSetting(bool* showPassword)
|
||||
bool
|
||||
ContentParent::RecvFirstIdle()
|
||||
{
|
||||
// When the ContentChild goes idle, it sends us a FirstIdle message
|
||||
// which we use as a good time to prelaunch another process. If we
|
||||
// prelaunch any sooner than this, then we'll be competing with the
|
||||
// When the ContentChild goes idle, it sends us a FirstIdle message which we
|
||||
// use as an indicator that it's a good time to prelaunch another process.
|
||||
// If we prelaunch any sooner than this, then we'll be competing with the
|
||||
// child process and slowing it down.
|
||||
ScheduleDelayedPreallocateAppProcess();
|
||||
PreallocatedProcessManager::AllocateOnIdle();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1433,7 +1390,7 @@ ContentParent::RecvAudioChannelChangedNotification()
|
||||
nsRefPtr<AudioChannelService> service =
|
||||
AudioChannelService::GetAudioChannelService();
|
||||
if (service) {
|
||||
service->SendAudioChannelChangedNotification();
|
||||
service->SendAudioChannelChangedNotification(ChildID());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1778,6 +1735,30 @@ ContentParent::KillHard()
|
||||
OtherProcess(), /*force=*/true));
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::IsPreallocated()
|
||||
{
|
||||
return mAppManifestURL == MAGIC_PREALLOCATED_APP_MANIFEST_URL;
|
||||
}
|
||||
|
||||
void
|
||||
ContentParent::FriendlyName(nsAString& aName)
|
||||
{
|
||||
aName.Truncate();
|
||||
if (IsPreallocated()) {
|
||||
aName.AssignLiteral("(Preallocated)");
|
||||
} else if (mIsForBrowser) {
|
||||
aName.AssignLiteral("Browser");
|
||||
} else if (!mAppName.IsEmpty()) {
|
||||
aName = mAppName;
|
||||
} else if (!mAppManifestURL.IsEmpty()) {
|
||||
aName.AssignLiteral("Unknown app: ");
|
||||
aName.Append(mAppManifestURL);
|
||||
} else {
|
||||
aName.AssignLiteral("???");
|
||||
}
|
||||
}
|
||||
|
||||
PCrashReporterParent*
|
||||
ContentParent::AllocPCrashReporter(const NativeThreadId& tid,
|
||||
const uint32_t& processType)
|
||||
@ -2523,17 +2504,17 @@ ContentParent::RecvScriptError(const nsString& aMessage,
|
||||
bool
|
||||
ContentParent::RecvPrivateDocShellsExist(const bool& aExist)
|
||||
{
|
||||
if (!gPrivateContent)
|
||||
gPrivateContent = new nsTArray<ContentParent*>();
|
||||
if (!sPrivateContent)
|
||||
sPrivateContent = new nsTArray<ContentParent*>();
|
||||
if (aExist) {
|
||||
gPrivateContent->AppendElement(this);
|
||||
sPrivateContent->AppendElement(this);
|
||||
} else {
|
||||
gPrivateContent->RemoveElement(this);
|
||||
if (!gPrivateContent->Length()) {
|
||||
sPrivateContent->RemoveElement(this);
|
||||
if (!sPrivateContent->Length()) {
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
|
||||
delete gPrivateContent;
|
||||
gPrivateContent = NULL;
|
||||
delete sPrivateContent;
|
||||
sPrivateContent = NULL;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "mozilla/dom/ipc/Blob.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/HalTypes.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
|
||||
#include "nsFrameMessageManager.h"
|
||||
#include "nsIObserver.h"
|
||||
@ -58,6 +59,7 @@ class ContentParent : public PContentParent
|
||||
, public nsIThreadObserver
|
||||
, public nsIDOMGeoPositionCallback
|
||||
, public mozilla::dom::ipc::MessageManagerCallback
|
||||
, public mozilla::LinkedListElement<ContentParent>
|
||||
{
|
||||
typedef mozilla::ipc::GeckoChildProcessHost GeckoChildProcessHost;
|
||||
typedef mozilla::ipc::OptionalURIParams OptionalURIParams;
|
||||
@ -84,6 +86,11 @@ public:
|
||||
static already_AddRefed<ContentParent>
|
||||
GetNewOrUsed(bool aForBrowserElement = false);
|
||||
|
||||
/**
|
||||
* Create a subprocess suitable for use as a preallocated app process.
|
||||
*/
|
||||
static already_AddRefed<ContentParent> PreallocateAppProcess();
|
||||
|
||||
/**
|
||||
* Get or create a content process for the given TabContext. aFrameElement
|
||||
* should be the frame/iframe element with which this process will
|
||||
@ -131,6 +138,10 @@ public:
|
||||
return mSubprocess;
|
||||
}
|
||||
|
||||
int32_t Pid() {
|
||||
return base::GetProcId(mSubprocess->GetChildProcessHandle());
|
||||
}
|
||||
|
||||
bool NeedsPermissionsUpdate() {
|
||||
return mSendPermissionUpdates;
|
||||
}
|
||||
@ -145,23 +156,29 @@ public:
|
||||
void KillHard();
|
||||
|
||||
uint64_t ChildID() { return mChildID; }
|
||||
bool IsPreallocated();
|
||||
|
||||
/**
|
||||
* Get a user-friendly name for this ContentParent. We make no guarantees
|
||||
* about this name: It might not be unique, apps can spoof special names,
|
||||
* etc. So please don't use this name to make any decisions about the
|
||||
* ContentParent based on the value returned here.
|
||||
*/
|
||||
void FriendlyName(nsAString& aName);
|
||||
|
||||
protected:
|
||||
void OnChannelConnected(int32_t pid);
|
||||
virtual void ActorDestroy(ActorDestroyReason why);
|
||||
|
||||
private:
|
||||
static nsDataHashtable<nsStringHashKey, ContentParent*> *gAppContentParents;
|
||||
static nsTArray<ContentParent*>* gNonAppContentParents;
|
||||
static nsTArray<ContentParent*>* gPrivateContent;
|
||||
static nsDataHashtable<nsStringHashKey, ContentParent*> *sAppContentParents;
|
||||
static nsTArray<ContentParent*>* sNonAppContentParents;
|
||||
static nsTArray<ContentParent*>* sPrivateContent;
|
||||
static LinkedList<ContentParent> sContentParents;
|
||||
|
||||
static void JoinProcessesIOThread(const nsTArray<ContentParent*>* aProcesses,
|
||||
Monitor* aMonitor, bool* aDone);
|
||||
|
||||
static void PreallocateAppProcess();
|
||||
static void DelayedPreallocateAppProcess();
|
||||
static void ScheduleDelayedPreallocateAppProcess();
|
||||
|
||||
// Take the preallocated process and transform it into a "real" app process,
|
||||
// for the specified manifest URL. If there is no preallocated process (or
|
||||
// if it's dead), this returns false.
|
||||
@ -172,24 +189,23 @@ private:
|
||||
|
||||
static hal::ProcessPriority GetInitialProcessPriority(nsIDOMElement* aFrameElement);
|
||||
|
||||
static void FirstIdle();
|
||||
|
||||
// Hide the raw constructor methods since we don't want client code
|
||||
// using them.
|
||||
using PContentParent::SendPBrowserConstructor;
|
||||
using PContentParent::SendPTestShellConstructor;
|
||||
|
||||
ContentParent(const nsAString& aAppManifestURL, bool aIsForBrowser,
|
||||
// No more than one of !!aApp, aIsForBrowser, and aIsForPreallocated may be
|
||||
// true.
|
||||
ContentParent(mozIApplication* aApp,
|
||||
bool aIsForBrowser,
|
||||
bool aIsForPreallocated,
|
||||
ChildPrivileges aOSPrivileges = base::PRIVILEGES_DEFAULT,
|
||||
hal::ProcessPriority aInitialPriority = hal::PROCESS_PRIORITY_FOREGROUND);
|
||||
|
||||
virtual ~ContentParent();
|
||||
|
||||
void Init();
|
||||
|
||||
// Set the child process's priority. Once the child starts up, it will
|
||||
// manage its own priority via the ProcessPriorityManager.
|
||||
void SetProcessPriority(hal::ProcessPriority aInitialPriority);
|
||||
|
||||
// If the frame element indicates that the child process is "critical" and
|
||||
// has a pending system message, this function acquires the CPU wake lock on
|
||||
// behalf of the child. We'll release the lock when the system message is
|
||||
@ -405,7 +421,15 @@ private:
|
||||
// the nsIObserverService.
|
||||
nsCOMArray<nsIMemoryReporter> mMemoryReporters;
|
||||
|
||||
const nsString mAppManifestURL;
|
||||
nsString mAppManifestURL;
|
||||
|
||||
/**
|
||||
* We cache mAppName instead of looking it up using mAppManifestURL when we
|
||||
* need it because it turns out that getting an app from the apps service is
|
||||
* expensive.
|
||||
*/
|
||||
nsString mAppName;
|
||||
|
||||
nsRefPtr<nsFrameMessageManager> mMessageManager;
|
||||
|
||||
// After we initiate shutdown, we also start a timer to ensure
|
||||
|
@ -24,6 +24,7 @@ CPPSRCS = \
|
||||
CrashReporterParent.cpp \
|
||||
CrashReporterChild.cpp \
|
||||
PermissionMessageUtils.cpp \
|
||||
PreallocatedProcessManager.cpp \
|
||||
ProcessPriorityManager.cpp \
|
||||
StructuredCloneUtils.cpp \
|
||||
TabParent.cpp \
|
||||
|
@ -384,20 +384,6 @@ child:
|
||||
uint32_t renderFlags, bool flushLayout,
|
||||
nsIntSize renderSize);
|
||||
|
||||
/**
|
||||
* Send the child its app type. The app type identifies the kind of app
|
||||
* shown in this PBrowser. Currently, the only recognized app type is
|
||||
* "homescreen".
|
||||
*
|
||||
* The value here corresponds to the "mozapptype" attribute on iframes. For
|
||||
* example, <iframe mozbrowser mozapp="..." mozapptype="homescreen">.
|
||||
*
|
||||
* Only app frames (i.e., frames with an app-id) should have a non-empty app
|
||||
* type. If you try to SetAppType() with a non-empty app type on a non-app
|
||||
* PBrowserChild, we may assert.
|
||||
*/
|
||||
SetAppType(nsString appType);
|
||||
|
||||
/**
|
||||
* Sent by the chrome process when it no longer wants this remote
|
||||
* <browser>. The child side cleans up in response, then
|
||||
|
@ -27,6 +27,7 @@ include URIParams;
|
||||
include "mozilla/chrome/RegistryMessageUtils.h";
|
||||
include "mozilla/dom/PermissionMessageUtils.h";
|
||||
include "mozilla/dom/TabMessageUtils.h";
|
||||
include "mozilla/HalTypes.h";
|
||||
include "mozilla/layout/RenderFrameUtils.h";
|
||||
include "mozilla/net/NeckoMessageUtils.h";
|
||||
include "nsGeoPositionIPCSerialiser.h";
|
||||
@ -44,6 +45,7 @@ using mozilla::null_t;
|
||||
using mozilla::void_t;
|
||||
using mozilla::dom::AudioChannelType;
|
||||
using mozilla::dom::NativeThreadId;
|
||||
using mozilla::hal::ProcessPriority;
|
||||
using mozilla::layout::ScrollingBehavior;
|
||||
using gfxIntSize;
|
||||
|
||||
@ -349,6 +351,10 @@ child:
|
||||
FileSystemUpdate(nsString fsName, nsString mountPoint, int32_t fsState,
|
||||
int32_t mountGeneration);
|
||||
|
||||
NotifyProcessPriorityChanged(ProcessPriority priority);
|
||||
MinimizeMemoryUsage();
|
||||
CancelMinimizeMemoryUsage();
|
||||
|
||||
parent:
|
||||
/**
|
||||
* Tell the content process some attributes of itself. This is
|
||||
|
229
dom/ipc/PreallocatedProcessManager.cpp
Normal file
229
dom/ipc/PreallocatedProcessManager.cpp
Normal file
@ -0,0 +1,229 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et ft=cpp : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/PreallocatedProcessManager.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::hal;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* This singleton class implements the static methods on
|
||||
* PreallocatedProcessManager.
|
||||
*/
|
||||
class PreallocatedProcessManagerImpl MOZ_FINAL
|
||||
: public nsIObserver
|
||||
{
|
||||
public:
|
||||
static PreallocatedProcessManagerImpl* Singleton();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
// See comments on PreallocatedProcessManager for these methods.
|
||||
void AllocateAfterDelay();
|
||||
void AllocateOnIdle();
|
||||
void AllocateNow();
|
||||
already_AddRefed<ContentParent> Take();
|
||||
|
||||
private:
|
||||
static mozilla::StaticRefPtr<PreallocatedProcessManagerImpl> sSingleton;
|
||||
|
||||
PreallocatedProcessManagerImpl();
|
||||
DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManagerImpl);
|
||||
|
||||
void Init();
|
||||
|
||||
void RereadPrefs();
|
||||
void Enable();
|
||||
void Disable();
|
||||
|
||||
void ObserveProcessShutdown(nsISupports* aSubject);
|
||||
|
||||
bool mEnabled;
|
||||
nsRefPtr<ContentParent> mPreallocatedAppProcess;
|
||||
};
|
||||
|
||||
/* static */ StaticRefPtr<PreallocatedProcessManagerImpl>
|
||||
PreallocatedProcessManagerImpl::sSingleton;
|
||||
|
||||
/* static */ PreallocatedProcessManagerImpl*
|
||||
PreallocatedProcessManagerImpl::Singleton()
|
||||
{
|
||||
if (!sSingleton) {
|
||||
sSingleton = new PreallocatedProcessManagerImpl();
|
||||
sSingleton->Init();
|
||||
ClearOnShutdown(&sSingleton);
|
||||
}
|
||||
|
||||
return sSingleton;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(PreallocatedProcessManagerImpl, nsIObserver)
|
||||
|
||||
PreallocatedProcessManagerImpl::PreallocatedProcessManagerImpl()
|
||||
: mEnabled(false)
|
||||
{}
|
||||
|
||||
void
|
||||
PreallocatedProcessManagerImpl::Init()
|
||||
{
|
||||
Preferences::AddStrongObserver(this, "dom.ipc.processPrelaunch.enabled");
|
||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
if (os) {
|
||||
os->AddObserver(this, "ipc:content-shutdown",
|
||||
/* weakRef = */ false);
|
||||
}
|
||||
RereadPrefs();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PreallocatedProcessManagerImpl::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
const PRUnichar* aData)
|
||||
{
|
||||
if (!strcmp("ipc:content-shutdown", aTopic)) {
|
||||
ObserveProcessShutdown(aSubject);
|
||||
} else if (!strcmp("nsPref:changed", aTopic)) {
|
||||
// The only other observer we registered was for our prefs.
|
||||
RereadPrefs();
|
||||
} else {
|
||||
MOZ_ASSERT(false);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
PreallocatedProcessManagerImpl::RereadPrefs()
|
||||
{
|
||||
if (Preferences::GetBool("dom.ipc.processPrelaunch.enabled")) {
|
||||
Enable();
|
||||
} else {
|
||||
Disable();
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<ContentParent>
|
||||
PreallocatedProcessManagerImpl::Take()
|
||||
{
|
||||
return mPreallocatedAppProcess.forget();
|
||||
}
|
||||
|
||||
void
|
||||
PreallocatedProcessManagerImpl::Enable()
|
||||
{
|
||||
if (mEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
mEnabled = true;
|
||||
AllocateAfterDelay();
|
||||
}
|
||||
|
||||
void
|
||||
PreallocatedProcessManagerImpl::AllocateAfterDelay()
|
||||
{
|
||||
if (!mEnabled || mPreallocatedAppProcess) {
|
||||
return;
|
||||
}
|
||||
|
||||
MessageLoop::current()->PostDelayedTask(
|
||||
FROM_HERE,
|
||||
NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateOnIdle),
|
||||
Preferences::GetUint("dom.ipc.processPrelaunch.delayMs", 1000));
|
||||
}
|
||||
|
||||
void
|
||||
PreallocatedProcessManagerImpl::AllocateOnIdle()
|
||||
{
|
||||
if (!mEnabled || mPreallocatedAppProcess) {
|
||||
return;
|
||||
}
|
||||
|
||||
MessageLoop::current()->PostIdleTask(
|
||||
FROM_HERE,
|
||||
NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateNow));
|
||||
}
|
||||
|
||||
void
|
||||
PreallocatedProcessManagerImpl::AllocateNow()
|
||||
{
|
||||
if (!mEnabled || mPreallocatedAppProcess) {
|
||||
return;
|
||||
}
|
||||
|
||||
mPreallocatedAppProcess = ContentParent::PreallocateAppProcess();
|
||||
}
|
||||
|
||||
void
|
||||
PreallocatedProcessManagerImpl::Disable()
|
||||
{
|
||||
if (!mEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
mEnabled = false;
|
||||
|
||||
if (mPreallocatedAppProcess) {
|
||||
mPreallocatedAppProcess->ShutDown();
|
||||
mPreallocatedAppProcess = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PreallocatedProcessManagerImpl::ObserveProcessShutdown(nsISupports* aSubject)
|
||||
{
|
||||
if (!mPreallocatedAppProcess) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
|
||||
NS_ENSURE_TRUE_VOID(props);
|
||||
|
||||
uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
|
||||
props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID);
|
||||
NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN);
|
||||
|
||||
if (childID == mPreallocatedAppProcess->ChildID()) {
|
||||
mPreallocatedAppProcess = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/* static */ void
|
||||
PreallocatedProcessManager::AllocateAfterDelay()
|
||||
{
|
||||
PreallocatedProcessManagerImpl::Singleton()->AllocateAfterDelay();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
PreallocatedProcessManager::AllocateOnIdle()
|
||||
{
|
||||
PreallocatedProcessManagerImpl::Singleton()->AllocateOnIdle();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
PreallocatedProcessManager::AllocateNow()
|
||||
{
|
||||
PreallocatedProcessManagerImpl::Singleton()->AllocateNow();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<ContentParent>
|
||||
PreallocatedProcessManager::Take()
|
||||
{
|
||||
return PreallocatedProcessManagerImpl::Singleton()->Take();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
90
dom/ipc/PreallocatedProcessManager.h
Normal file
90
dom/ipc/PreallocatedProcessManager.h
Normal file
@ -0,0 +1,90 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et ft=cpp : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_PreallocatedProcessManager_h
|
||||
#define mozilla_PreallocatedProcessManager_h
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class ContentParent;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class manages a ContentParent that it starts up ahead of any particular
|
||||
* need. You can then call Take() to get this process and use it. Since we
|
||||
* already started it up, it should be ready for use faster than if you'd
|
||||
* created the process when you needed it.
|
||||
*
|
||||
* This class watches the dom.ipc.processPrelaunch.enabled pref. If it changes
|
||||
* from false to true, it preallocates a process. If it changes from true to
|
||||
* false, it kills the preallocated process, if any.
|
||||
*
|
||||
* We don't expect this pref to flip between true and false in production, but
|
||||
* flipping the pref is important for tests.
|
||||
*
|
||||
* The static methods here are implemented by forwarding calls on to a
|
||||
* PreallocatedProcessManagerImpl singleton class, so if you add a new static
|
||||
* method here, you'll need to write a corresponding public method on the
|
||||
* singleton.
|
||||
*/
|
||||
class PreallocatedProcessManager MOZ_FINAL
|
||||
{
|
||||
typedef mozilla::dom::ContentParent ContentParent;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a process after a delay. We wait for a period of time (specified
|
||||
* by the dom.ipc.processPrelaunch.delayMs pref), then wait for this process
|
||||
* to go idle, then allocate the new process.
|
||||
*
|
||||
* If the dom.ipc.processPrelaunch.enabled pref is false, or if we already
|
||||
* have a preallocated process, this function does nothing.
|
||||
*/
|
||||
static void AllocateAfterDelay();
|
||||
|
||||
/**
|
||||
* Create a process once this process goes idle.
|
||||
*
|
||||
* If the dom.ipc.processPrelaunch.enabled pref is false, or if we already
|
||||
* have a preallocated process, this function does nothing.
|
||||
*/
|
||||
static void AllocateOnIdle();
|
||||
|
||||
/**
|
||||
* Create a process right now.
|
||||
*
|
||||
* If the dom.ipc.processPrelaunch.enabled pref is false, or if we already
|
||||
* have a preallocated process, this function does nothing.
|
||||
*/
|
||||
static void AllocateNow();
|
||||
|
||||
/**
|
||||
* Take the preallocated process, if we have one. If we don't have one, this
|
||||
* returns null.
|
||||
*
|
||||
* If you call Take() twice in a row, the second call is guaranteed to return
|
||||
* null.
|
||||
*
|
||||
* After you Take() the preallocated process, you need to call one of the
|
||||
* Allocate* functions (or change the dom.ipc.processPrelaunch pref from
|
||||
* false to true) before we'll create a new process.
|
||||
*/
|
||||
static already_AddRefed<ContentParent> Take();
|
||||
|
||||
private:
|
||||
PreallocatedProcessManager();
|
||||
DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManager);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // defined mozilla_PreallocatedProcessManager_h
|
File diff suppressed because it is too large
Load Diff
@ -7,46 +7,75 @@
|
||||
#ifndef mozilla_ProcessPriorityManager_h_
|
||||
#define mozilla_ProcessPriorityManager_h_
|
||||
|
||||
#include "mozilla/HalTypes.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace ipc {
|
||||
class ContentParent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the ProcessPriorityManager.
|
||||
* This class sets the priority of subprocesses in response to explicit
|
||||
* requests and events in the system.
|
||||
*
|
||||
* The ProcessPriorityManager informs the hal back-end whether this is the root
|
||||
* Gecko process, and, if we're not the root, informs hal when this process
|
||||
* transitions between having no visible top-level windows, and having at least
|
||||
* one visible top-level window.
|
||||
* A process's priority changes e.g. when it goes into the background via
|
||||
* mozbrowser's setVisible(false). Process priority affects CPU scheduling and
|
||||
* also which processes get killed when we run out of memory.
|
||||
*
|
||||
* Hal may adjust this process's operating system priority (e.g. niceness, on
|
||||
* *nix) according to these notificaitons.
|
||||
*
|
||||
* This function call does nothing if the pref for OOP tabs is not set.
|
||||
* After you call Initialize(), the only thing you probably have to do is call
|
||||
* SetProcessPriority on processes immediately after creating them in order to
|
||||
* set their initial priority. The ProcessPriorityManager takes care of the
|
||||
* rest.
|
||||
*/
|
||||
void InitProcessPriorityManager();
|
||||
class ProcessPriorityManager MOZ_FINAL
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Initialize the ProcessPriorityManager machinery, causing the
|
||||
* ProcessPriorityManager to actively manage the priorities of all
|
||||
* subprocesses. You should call this before creating any subprocesses.
|
||||
*
|
||||
* You should also call this function even if you're in a child process,
|
||||
* since it will initialize ProcessPriorityManagerChild.
|
||||
*/
|
||||
static void Init();
|
||||
|
||||
/**
|
||||
* True iff the current process has foreground or higher priority as
|
||||
* computed by DOM visibility. The returned answer may not match the
|
||||
* actual OS process priority, for short intervals.
|
||||
*/
|
||||
bool CurrentProcessIsForeground();
|
||||
/**
|
||||
* Set the process priority of a given ContentParent's process.
|
||||
*
|
||||
* Note that because this method takes a ContentParent*, you can only set the
|
||||
* priority of your subprocesses. In fact, because we don't support nested
|
||||
* content processes (bug 761935), you can only call this method from the
|
||||
* main process.
|
||||
*
|
||||
* It probably only makes sense to call this function immediately after a
|
||||
* process is created. At this point, the process priority manager doesn't
|
||||
* have enough context about the processs to know what its priority should
|
||||
* be.
|
||||
*
|
||||
* Eventually whatever priority you set here can and probably will be
|
||||
* overwritten by the process priority manager.
|
||||
*/
|
||||
static void SetProcessPriority(dom::ContentParent* aContentParent,
|
||||
hal::ProcessPriority aPriority);
|
||||
|
||||
/**
|
||||
* Calling this function prevents us from changing this process's priority
|
||||
* for a few seconds, if that change in priority would not have taken effect
|
||||
* immediately to begin with.
|
||||
*
|
||||
* In practice, this prevents foreground --> background transitions, but not
|
||||
* background --> foreground transitions. It also does not prevent
|
||||
* transitions from an unknown priority (as happens immediately after we're
|
||||
* constructed) to a foreground priority.
|
||||
*/
|
||||
void TemporarilyLockProcessPriority();
|
||||
/**
|
||||
* Returns true iff this process's priority is FOREGROUND*.
|
||||
*
|
||||
* Note that because process priorities are set in the main process, it's
|
||||
* possible for this method to return a stale value. So be careful about
|
||||
* what you use this for.
|
||||
*/
|
||||
static bool CurrentProcessIsForeground();
|
||||
|
||||
private:
|
||||
ProcessPriorityManager();
|
||||
DISALLOW_EVIL_CONSTRUCTORS(ProcessPriorityManager);
|
||||
};
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
@ -2018,14 +2018,6 @@ TabChild::RecvDestroy()
|
||||
return Send__delete__(this);
|
||||
}
|
||||
|
||||
/* virtual */ bool
|
||||
TabChild::RecvSetAppType(const nsString& aAppType)
|
||||
{
|
||||
MOZ_ASSERT_IF(!aAppType.IsEmpty(), HasOwnApp());
|
||||
mAppType = aAppType;
|
||||
return true;
|
||||
}
|
||||
|
||||
PRenderFrameChild*
|
||||
TabChild::AllocPRenderFrame(ScrollingBehavior* aScrolling,
|
||||
TextureFactoryIdentifier* aTextureFactoryIdentifier,
|
||||
|
@ -315,16 +315,6 @@ public:
|
||||
void MakeVisible();
|
||||
void MakeHidden();
|
||||
|
||||
virtual bool RecvSetAppType(const nsString& aAppType);
|
||||
|
||||
/**
|
||||
* Get this object's app type.
|
||||
*
|
||||
* A TabChild's app type corresponds to the value of its frame element's
|
||||
* "mozapptype" attribute.
|
||||
*/
|
||||
void GetAppType(nsAString& aAppType) const { aAppType = mAppType; }
|
||||
|
||||
// Returns true if the file descriptor was found in the cache, false
|
||||
// otherwise.
|
||||
bool GetCachedFileDescriptor(const nsAString& aPath,
|
||||
@ -459,7 +449,6 @@ private:
|
||||
bool mNotified;
|
||||
bool mContentDocumentIsDisplayed;
|
||||
bool mTriedBrowserInit;
|
||||
nsString mAppType;
|
||||
ScreenOrientation mOrientation;
|
||||
|
||||
DISALLOW_EVIL_CONSTRUCTORS(TabChild);
|
||||
|
@ -219,6 +219,31 @@ TabParent::SetOwnerElement(nsIDOMElement* aElement)
|
||||
TryCacheDPI();
|
||||
}
|
||||
|
||||
void
|
||||
TabParent::GetAppType(nsAString& aOut)
|
||||
{
|
||||
aOut.Truncate();
|
||||
nsCOMPtr<Element> elem = do_QueryInterface(mFrameElement);
|
||||
if (!elem) {
|
||||
return;
|
||||
}
|
||||
|
||||
elem->GetAttr(kNameSpaceID_None, nsGkAtoms::mozapptype, aOut);
|
||||
}
|
||||
|
||||
bool
|
||||
TabParent::IsVisible()
|
||||
{
|
||||
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
|
||||
if (!frameLoader) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool visible = false;
|
||||
frameLoader->GetVisible(&visible);
|
||||
return visible;
|
||||
}
|
||||
|
||||
void
|
||||
TabParent::Destroy()
|
||||
{
|
||||
@ -265,18 +290,20 @@ TabParent::ActorDestroy(ActorDestroyReason why)
|
||||
mIMETabParent = nullptr;
|
||||
}
|
||||
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
|
||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
if (frameLoader) {
|
||||
ReceiveMessage(CHILD_PROCESS_SHUTDOWN_MESSAGE, false, nullptr, nullptr);
|
||||
frameLoader->DestroyChild();
|
||||
|
||||
if (why == AbnormalShutdown) {
|
||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
if (os) {
|
||||
os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, frameLoader),
|
||||
"oop-frameloader-crashed", nullptr);
|
||||
}
|
||||
if (why == AbnormalShutdown && os) {
|
||||
os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, frameLoader),
|
||||
"oop-frameloader-crashed", nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
if (os) {
|
||||
os->NotifyObservers(NS_ISUPPORTS_CAST(nsITabParent*, this), "ipc:browser-destroyed", nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -62,6 +62,20 @@ public:
|
||||
virtual ~TabParent();
|
||||
nsIDOMElement* GetOwnerElement() { return mFrameElement; }
|
||||
void SetOwnerElement(nsIDOMElement* aElement);
|
||||
|
||||
/**
|
||||
* Get the mozapptype attribute from this TabParent's owner DOM element.
|
||||
*/
|
||||
void GetAppType(nsAString& aOut);
|
||||
|
||||
/**
|
||||
* Returns true iff this TabParent's nsIFrameLoader is visible.
|
||||
*
|
||||
* The frameloader's visibility can be independent of e.g. its docshell's
|
||||
* visibility.
|
||||
*/
|
||||
bool IsVisible();
|
||||
|
||||
nsIBrowserDOMWindow *GetBrowserDOMWindow() { return mBrowserDOMWindow; }
|
||||
void SetBrowserDOMWindow(nsIBrowserDOMWindow* aBrowserDOMWindow) {
|
||||
mBrowserDOMWindow = aBrowserDOMWindow;
|
||||
|
@ -16,7 +16,6 @@ EXPORTS += [
|
||||
|
||||
EXPORTS.mozilla.dom.ipc += [
|
||||
'Blob.h',
|
||||
'ProcessPriorityManager.h',
|
||||
'nsIRemoteBlob.h',
|
||||
]
|
||||
|
||||
@ -36,5 +35,7 @@ EXPORTS.mozilla.dom += [
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
'AppProcessChecker.h',
|
||||
'PreallocatedProcessManager.h',
|
||||
'ProcessPriorityManager.h',
|
||||
]
|
||||
|
||||
|
@ -1061,6 +1061,83 @@ EnsureKernelLowMemKillerParamsSet()
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
SetNiceForPid(int aPid, int aNice)
|
||||
{
|
||||
errno = 0;
|
||||
int origProcPriority = getpriority(PRIO_PROCESS, aPid);
|
||||
if (errno) {
|
||||
LOG("Unable to get nice for pid=%d; error %d. SetNiceForPid bailing.",
|
||||
aPid, errno);
|
||||
return;
|
||||
}
|
||||
|
||||
int rv = setpriority(PRIO_PROCESS, aPid, aNice);
|
||||
if (rv) {
|
||||
LOG("Unable to set nice for pid=%d; error %d. SetNiceForPid bailing.",
|
||||
aPid, errno);
|
||||
return;
|
||||
}
|
||||
|
||||
// On Linux, setpriority(aPid) modifies the priority only of the main
|
||||
// thread of that process. We have to modify the priorities of all of the
|
||||
// process's threads as well, so iterate over all the threads and increase
|
||||
// each of their priorites by aNice - origProcPriority (and also ensure that
|
||||
// none of the tasks has a lower priority than the main thread).
|
||||
//
|
||||
// This is horribly racy.
|
||||
|
||||
DIR* tasksDir = opendir(nsPrintfCString("/proc/%d/task/", aPid).get());
|
||||
if (!tasksDir) {
|
||||
LOG("Unable to open /proc/%d/task. SetNiceForPid bailing.", aPid);
|
||||
return;
|
||||
}
|
||||
|
||||
// Be careful not to leak tasksDir; after this point, we must call closedir().
|
||||
|
||||
while (struct dirent* de = readdir(tasksDir)) {
|
||||
char* endptr = nullptr;
|
||||
long tidlong = strtol(de->d_name, &endptr, /* base */ 10);
|
||||
if (*endptr || tidlong < 0 || tidlong > INT32_MAX || tidlong == aPid) {
|
||||
// if dp->d_name was not an integer, was negative (?!) or too large, or
|
||||
// was the same as aPid, we're not interested.
|
||||
//
|
||||
// (The |tidlong == aPid| check is very important; without it, we'll
|
||||
// renice aPid twice, and the second renice will be relative to the
|
||||
// priority set by the first renice.)
|
||||
continue;
|
||||
}
|
||||
|
||||
int tid = static_cast<int>(tidlong);
|
||||
|
||||
errno = 0;
|
||||
// Get and set the task's new priority.
|
||||
int origtaskpriority = getpriority(PRIO_PROCESS, tid);
|
||||
if (errno) {
|
||||
LOG("Unable to get nice for tid=%d (pid=%d); error %d. This isn't "
|
||||
"necessarily a problem; it could be a benign race condition.",
|
||||
tid, aPid, errno);
|
||||
continue;
|
||||
}
|
||||
|
||||
int newtaskpriority =
|
||||
std::max(origtaskpriority + aNice - origProcPriority, origProcPriority);
|
||||
rv = setpriority(PRIO_PROCESS, tid, newtaskpriority);
|
||||
|
||||
if (rv) {
|
||||
LOG("Unable to set nice for tid=%d (pid=%d); error %d. This isn't "
|
||||
"necessarily a problem; it could be a benign race condition.",
|
||||
tid, aPid, errno);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
LOG("Changed nice for pid %d from %d to %d.",
|
||||
aPid, origProcPriority, aNice);
|
||||
|
||||
closedir(tasksDir);
|
||||
}
|
||||
|
||||
void
|
||||
SetProcessPriority(int aPid, ProcessPriority aPriority)
|
||||
{
|
||||
@ -1141,10 +1218,7 @@ SetProcessPriority(int aPid, ProcessPriority aPriority)
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
HAL_LOG(("Setting nice for pid %d to %d", aPid, nice));
|
||||
|
||||
int success = setpriority(PRIO_PROCESS, aPid, nice);
|
||||
if (success != 0) {
|
||||
HAL_LOG(("Failed to set nice for pid %d to %d", aPid, nice));
|
||||
}
|
||||
SetNiceForPid(aPid, nice);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,7 @@ using namespace mozilla::system;
|
||||
#include "nsEditorSpellCheck.h"
|
||||
#include "nsWindowMemoryReporter.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/ipc/ProcessPriorityManager.h"
|
||||
#include "mozilla/ProcessPriorityManager.h"
|
||||
#include "nsPermissionManager.h"
|
||||
#include "nsCookieService.h"
|
||||
#include "nsApplicationCacheService.h"
|
||||
@ -265,7 +265,7 @@ nsLayoutStatics::Initialize()
|
||||
SVGElementFactory::Init();
|
||||
nsSVGUtils::Init();
|
||||
|
||||
InitProcessPriorityManager();
|
||||
ProcessPriorityManager::Init();
|
||||
|
||||
nsPermissionManager::AppClearDataObserverInit();
|
||||
nsCookieService::AppClearDataObserverInit();
|
||||
|
@ -712,3 +712,6 @@ pref("media.webaudio.enabled", true);
|
||||
// This needs more tests and stability fixes first, as well as UI.
|
||||
pref("media.navigator.enabled", false);
|
||||
pref("media.peerconnection.enabled", false);
|
||||
|
||||
// Make <audio> and <video> talk to the AudioChannelService.
|
||||
pref("media.useAudioChannelService", true);
|
||||
|
@ -7,6 +7,9 @@
|
||||
#ifndef mozilla_StaticPtr_h
|
||||
#define mozilla_StaticPtr_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/NullPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
@ -216,7 +219,7 @@ REFLEXIVE_EQUALITY_OPERATORS(const StaticAutoPtr<T>&, U*,
|
||||
|
||||
// Let us compare StaticAutoPtr to 0.
|
||||
REFLEXIVE_EQUALITY_OPERATORS(const StaticAutoPtr<T>&, StaticPtr_internal::Zero*,
|
||||
lhs.get() == NULL, class T)
|
||||
lhs.get() == nullptr, class T)
|
||||
|
||||
// StaticRefPtr (in)equality operators
|
||||
|
||||
@ -242,7 +245,7 @@ REFLEXIVE_EQUALITY_OPERATORS(const StaticRefPtr<T>&, U*,
|
||||
|
||||
// Let us compare StaticRefPtr to 0.
|
||||
REFLEXIVE_EQUALITY_OPERATORS(const StaticRefPtr<T>&, StaticPtr_internal::Zero*,
|
||||
lhs.get() == NULL, class T)
|
||||
lhs.get() == nullptr, class T)
|
||||
|
||||
#undef REFLEXIVE_EQUALITY_OPERATORS
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user