Merge mozilla-central and inbound

This commit is contained in:
Ed Morley 2014-08-19 15:18:22 +01:00
Родитель 987e99e6f8 7d22538f76
Коммит 3b0c8ce16a
152 изменённых файлов: 3146 добавлений и 799 удалений

Просмотреть файл

@ -8336,10 +8336,6 @@ if test "$MOZ_DEBUG" -o "$NS_TRACE_MALLOC" -o "$MOZ_DMD"; then
MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS=
fi
if test "$MOZ_APP_COMPONENT_INCLUDE"; then
AC_DEFINE_UNQUOTED(MOZ_APP_COMPONENT_INCLUDE, "$MOZ_APP_COMPONENT_INCLUDE")
fi
dnl ========================================================
dnl =
dnl = Maintainer debug option (no --enable equivalent)

Просмотреть файл

@ -112,7 +112,7 @@ interface nsIContentViewManager : nsISupports
readonly attribute nsIContentView rootContentView;
};
[scriptable, builtinclass, uuid(d52ca6a8-8237-4ae0-91d7-7be4f1db24ef)]
[scriptable, builtinclass, uuid(55a772b8-855a-4c5f-b4a1-284b6b3bec28)]
interface nsIFrameLoader : nsISupports
{
/**
@ -288,8 +288,16 @@ interface nsIFrameLoader : nsISupports
/**
* Find out whether the owner content really is a browser or app frame
* Especially, a widget frame is regarded as an app frame.
*/
readonly attribute boolean ownerIsBrowserOrAppFrame;
/**
* Find out whether the owner content really is a widget. If this attribute
* returns true, |ownerIsBrowserOrAppFrame| must return true.
*/
readonly attribute boolean ownerIsWidget;
};
%{C++

Просмотреть файл

@ -1474,6 +1474,22 @@ nsFrameLoader::GetOwnerIsBrowserOrAppFrame(bool* aResult)
return NS_OK;
}
bool
nsFrameLoader::OwnerIsWidget()
{
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
return browserFrame ? browserFrame->GetReallyIsWidget() : false;
}
// The xpcom getter version
NS_IMETHODIMP
nsFrameLoader::GetOwnerIsWidget(bool* aResult)
{
*aResult = OwnerIsWidget();
return NS_OK;
}
bool
nsFrameLoader::OwnerIsAppFrame()
{

Просмотреть файл

@ -333,6 +333,12 @@ private:
*/
bool OwnerIsBrowserOrAppFrame();
/**
* Is this a frameloader for a bona fide <iframe mozwidget>? (I.e., does the
* frame return true for nsIMozBrowserFrame::GetReallyIsWidget()?)
*/
bool OwnerIsWidget();
/**
* Is this a frameloader for a bona fide <iframe mozapp>? (I.e., does the
* frame return true for nsIMozBrowserFrame::GetReallyIsApp()?)

Просмотреть файл

@ -91,6 +91,7 @@ GK_ATOM(_and, "and")
GK_ATOM(anonid, "anonid")
GK_ATOM(any, "any")
GK_ATOM(mozapp, "mozapp")
GK_ATOM(mozwidget, "mozwidget")
GK_ATOM(applet, "applet")
GK_ATOM(applyImports, "apply-imports")
GK_ATOM(applyTemplates, "apply-templates")

Просмотреть файл

@ -319,6 +319,44 @@ nsGenericHTMLFrameElement::GetReallyIsApp(bool *aOut)
return NS_OK;
}
namespace {
bool WidgetsEnabled()
{
static bool sMozWidgetsEnabled = false;
static bool sBoolVarCacheInitialized = false;
if (!sBoolVarCacheInitialized) {
sBoolVarCacheInitialized = true;
Preferences::AddBoolVarCache(&sMozWidgetsEnabled,
"dom.enable_widgets");
}
return sMozWidgetsEnabled;
}
} // anonymous namespace
/* [infallible] */ NS_IMETHODIMP
nsGenericHTMLFrameElement::GetReallyIsWidget(bool *aOut)
{
*aOut = false;
if (!WidgetsEnabled()) {
return NS_OK;
}
nsAutoString appManifestURL;
GetManifestURLByType(nsGkAtoms::mozapp, appManifestURL);
bool isApp = !appManifestURL.IsEmpty();
nsAutoString widgetManifestURL;
GetManifestURLByType(nsGkAtoms::mozwidget, widgetManifestURL);
bool isWidget = !widgetManifestURL.IsEmpty();
*aOut = isWidget && !isApp;
return NS_OK;
}
/* [infallible] */ NS_IMETHODIMP
nsGenericHTMLFrameElement::GetIsExpectingSystemMessage(bool *aOut)
{
@ -332,6 +370,63 @@ nsGenericHTMLFrameElement::GetIsExpectingSystemMessage(bool *aOut)
return NS_OK;
}
/** Get manifest url of app or widget
* @param AppType: nsGkAtoms::mozapp or nsGkAtoms::mozwidget
*/
void nsGenericHTMLFrameElement::GetManifestURLByType(nsIAtom *aAppType,
nsAString& aManifestURL)
{
aManifestURL.Truncate();
if (aAppType != nsGkAtoms::mozapp && aAppType != nsGkAtoms::mozwidget) {
return;
}
nsAutoString manifestURL;
GetAttr(kNameSpaceID_None, aAppType, manifestURL);
if (manifestURL.IsEmpty()) {
return;
}
// Check permission.
nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
NS_ENSURE_TRUE_VOID(permMgr);
nsIPrincipal *principal = NodePrincipal();
const char* aPermissionType = (aAppType == nsGkAtoms::mozapp) ? "embed-apps"
: "embed-widgets";
uint32_t permission = nsIPermissionManager::DENY_ACTION;
nsresult rv = permMgr->TestPermissionFromPrincipal(principal,
aPermissionType,
&permission);
NS_ENSURE_SUCCESS_VOID(rv);
if (permission != nsIPermissionManager::ALLOW_ACTION) {
return;
}
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
NS_ENSURE_TRUE_VOID(appsService);
nsCOMPtr<mozIApplication> app;
appsService->GetAppByManifestURL(manifestURL, getter_AddRefs(app));
if (!app) {
return;
}
bool hasWidgetPage = false;
nsAutoString src;
if (aAppType == nsGkAtoms::mozwidget) {
GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
nsresult rv = app->HasWidgetPage(src, &hasWidgetPage);
if (!NS_SUCCEEDED(rv) || !hasWidgetPage) {
return;
}
}
aManifestURL.Assign(manifestURL);
}
NS_IMETHODIMP
nsGenericHTMLFrameElement::GetAppManifestURL(nsAString& aOut)
{
@ -342,35 +437,36 @@ nsGenericHTMLFrameElement::GetAppManifestURL(nsAString& aOut)
return NS_OK;
}
// Check permission.
nsIPrincipal *principal = NodePrincipal();
nsCOMPtr<nsIPermissionManager> permMgr =
services::GetPermissionManager();
NS_ENSURE_TRUE(permMgr, NS_OK);
nsAutoString appManifestURL;
nsAutoString widgetManifestURL;
uint32_t permission = nsIPermissionManager::DENY_ACTION;
nsresult rv = permMgr->TestPermissionFromPrincipal(principal,
"embed-apps",
&permission);
NS_ENSURE_SUCCESS(rv, NS_OK);
if (permission != nsIPermissionManager::ALLOW_ACTION) {
GetManifestURLByType(nsGkAtoms::mozapp, appManifestURL);
if (WidgetsEnabled()) {
GetManifestURLByType(nsGkAtoms::mozwidget, widgetManifestURL);
}
bool isApp = !appManifestURL.IsEmpty();
bool isWidget = !widgetManifestURL.IsEmpty();
if (!isApp && !isWidget) {
// No valid case to get manifest
return NS_OK;
}
if (isApp && isWidget) {
NS_WARNING("Can not simultaneously be mozapp and mozwidget");
return NS_OK;
}
nsAutoString manifestURL;
GetAttr(kNameSpaceID_None, nsGkAtoms::mozapp, manifestURL);
if (manifestURL.IsEmpty()) {
return NS_OK;
if (isApp) {
manifestURL.Assign(appManifestURL);
} else if (isWidget) {
manifestURL.Assign(widgetManifestURL);
}
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(appsService, NS_OK);
nsCOMPtr<mozIApplication> app;
appsService->GetAppByManifestURL(manifestURL, getter_AddRefs(app));
if (app) {
aOut.Assign(manifestURL);
}
aOut.Assign(manifestURL);
return NS_OK;
}

Просмотреть файл

@ -88,6 +88,9 @@ protected:
{
return this;
}
private:
void GetManifestURLByType(nsIAtom *aAppType, nsAString& aOut);
};
#endif // nsGenericHTMLFrameElement_h

Просмотреть файл

@ -1518,6 +1518,14 @@ nsTextEditorState::UnbindFromFrame(nsTextControlFrame* aFrame)
NS_ASSERTION(!aFrame || aFrame == mBoundFrame, "Unbinding from the wrong frame");
NS_ENSURE_TRUE_VOID(!aFrame || aFrame == mBoundFrame);
// If the editor is modified but nsIEditorObserver::EditAction() hasn't been
// called yet, we need to notify it here because editor may be destroyed
// before EditAction() is called if selection listener causes flushing layout.
if (mTextListener && mEditor && mEditorInitialized &&
mEditor->GetIsInEditAction()) {
mTextListener->EditAction();
}
// We need to start storing the value outside of the editor if we're not
// going to use it anymore, so retrieve it for now.
nsAutoString value;

Просмотреть файл

@ -17,46 +17,45 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=833386
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true]]},
function() {
reflectLimitedEnumerated({
element: document.createElement("track"),
attribute: "kind",
validValues: ["subtitles", "captions", "descriptions", "chapters", "metadata"],
invalidValues: ["foo", "bar", "\u0000", "null", "", "subtitle", "caption", "description", "chapter", "meta"],
defaultValue: "subtitles"
});
// Default attribute
reflectBoolean({
element: document.createElement("track"),
attribute: "default"
});
// Label attribute
reflectString({
element: document.createElement("track"),
attribute: "label",
otherValues: [ "foo", "BAR", "_FoO", "\u0000", "null", "white space" ]
});
// Source attribute
reflectURL({
element: document.createElement("track"),
attribute: "src",
otherValues: ["foo", "bar", "\u0000", "null", ""]
});
// Source Language attribute
reflectString({
element: document.createElement("track"),
attribute: "srclang",
otherValues: ["foo", "bar", "\u0000", "null", ""]
});
reflectLimitedEnumerated({
element: document.createElement("track"),
attribute: "kind",
validValues: ["subtitles", "captions", "descriptions", "chapters",
"metadata"],
invalidValues: ["foo", "bar", "\u0000", "null", "", "subtitle", "caption",
"description", "chapter", "meta"],
defaultValue: "subtitles"
});
var track = document.createElement("track");
is(track.readyState, 0, "Default ready state should be 0 (NONE).");
// Default attribute
reflectBoolean({
element: document.createElement("track"),
attribute: "default"
});
SimpleTest.finish();
}
);
// Label attribute
reflectString({
element: document.createElement("track"),
attribute: "label",
otherValues: [ "foo", "BAR", "_FoO", "\u0000", "null", "white space" ]
});
// Source attribute
reflectURL({
element: document.createElement("track"),
attribute: "src",
otherValues: ["foo", "bar", "\u0000", "null", ""]
});
// Source Language attribute
reflectString({
element: document.createElement("track"),
attribute: "srclang",
otherValues: ["foo", "bar", "\u0000", "null", ""]
});
var track = document.createElement("track");
is(track.readyState, 0, "Default ready state should be 0 (NONE).");
</script>
</pre>
</body>

Просмотреть файл

@ -324,7 +324,7 @@ AudioNodeExternalInputStream::ProcessInput(GraphTime aFrom, GraphTime aTo,
// GC stuff can result in our input stream being destroyed before this stream.
// Handle that.
if (!IsEnabled() || mInputs.IsEmpty()) {
if (!IsEnabled() || mInputs.IsEmpty() || mPassThrough) {
mLastChunks[0].SetNull(WEBAUDIO_BLOCK_SIZE);
AdvanceOutputSegment();
return;

Просмотреть файл

@ -70,10 +70,12 @@ NS_IMPL_RELEASE_INHERITED(MediaRecorder, DOMEventTargetHelper)
* Release session resource and remove associated streams from MSG.
*
* Lifetime of MediaRecorder and Session objects.
* 1) MediaRecorder creates a Session in MediaRecorder::Start function.
* And the Session registers itself to ShutdownObserver and also holds a
* MediaRecorder. Therefore, the reference dependency in gecko is:
* ShutdownObserver -> Session -> MediaRecorder
* 1) MediaRecorder creates a Session in MediaRecorder::Start function and holds
* a reference to Session. Then the Session registers itself to
* ShutdownObserver and also holds a reference to MediaRecorder.
* Therefore, the reference dependency in gecko is:
* ShutdownObserver -> Session <-> MediaRecorder, note that there is a cycle
* reference between Session and MediaRecorder.
* 2) A Session is destroyed in DestroyRunnable after MediaRecorder::Stop being called
* _and_ all encoded media data been passed to OnDataAvailable handler.
* 3) MediaRecorder::Stop is called by user or the document is going to
@ -150,18 +152,11 @@ class MediaRecorder::Session: public nsIObserver
class ExtractRunnable : public nsRunnable
{
public:
ExtractRunnable(already_AddRefed<Session>&& aSession)
ExtractRunnable(Session* aSession)
: mSession(aSession) {}
~ExtractRunnable()
{
if (mSession) {
NS_WARNING("~ExtractRunnable something wrong the mSession should null");
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!");
NS_ProxyRelease(mainThread, mSession);
}
}
{}
NS_IMETHODIMP Run()
{
@ -170,16 +165,15 @@ class MediaRecorder::Session: public nsIObserver
LOG(PR_LOG_DEBUG, ("Session.ExtractRunnable shutdown = %d", mSession->mEncoder->IsShutdown()));
if (!mSession->mEncoder->IsShutdown()) {
mSession->Extract(false);
nsRefPtr<nsIRunnable> event = new ExtractRunnable(mSession.forget());
nsRefPtr<nsIRunnable> event = new ExtractRunnable(mSession);
if (NS_FAILED(NS_DispatchToCurrentThread(event))) {
NS_WARNING("Failed to dispatch ExtractRunnable to encoder thread");
}
} else {
// Flush out remaining encoded data.
mSession->Extract(true);
// Destroy this Session object in main thread.
if (NS_FAILED(NS_DispatchToMainThread(
new DestroyRunnable(mSession.forget())))) {
new DestroyRunnable(mSession)))) {
MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
}
}
@ -224,7 +218,7 @@ class MediaRecorder::Session: public nsIObserver
class DestroyRunnable : public nsRunnable
{
public:
DestroyRunnable(already_AddRefed<Session>&& aSession)
DestroyRunnable(Session* aSession)
: mSession(aSession) {}
NS_IMETHODIMP Run()
@ -246,8 +240,7 @@ class MediaRecorder::Session: public nsIObserver
ErrorResult result;
mSession->mStopIssued = true;
recorder->Stop(result);
if (NS_FAILED(NS_DispatchToMainThread(
new DestroyRunnable(mSession.forget())))) {
if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(mSession)))) {
MOZ_ASSERT(false, "NS_DispatchToMainThread failed");
}
return NS_OK;
@ -257,8 +250,7 @@ class MediaRecorder::Session: public nsIObserver
mSession->mMimeType = NS_LITERAL_STRING("");
recorder->SetMimeType(mSession->mMimeType);
recorder->DispatchSimpleEvent(NS_LITERAL_STRING("stop"));
recorder->RemoveSession(mSession);
mSession->mRecorder = nullptr;
mSession->BreakCycle();
return NS_OK;
}
@ -454,8 +446,7 @@ private:
// shutdown notification and stop Read Thread.
nsContentUtils::RegisterShutdownObserver(this);
already_AddRefed<Session> session(this); // addref in MediaRecorder::Start
nsRefPtr<nsIRunnable> event = new ExtractRunnable(Move(session));
nsRefPtr<nsIRunnable> event = new ExtractRunnable(this);
if (NS_FAILED(mReadThread->Dispatch(event, NS_DISPATCH_NORMAL))) {
NS_WARNING("Failed to dispatch ExtractRunnable at beginning");
}
@ -472,9 +463,7 @@ private:
if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
}
// Destroy this session object in main thread.
already_AddRefed<Session> session(this); // addref in MediaRecorder::Start
if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(Move(session))))) {
if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(this)))) {
MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
}
}
@ -502,16 +491,23 @@ private:
mReadThread->Shutdown();
mReadThread = nullptr;
}
if (mRecorder) {
mRecorder->RemoveSession(this);
mRecorder = nullptr;
}
BreakCycle();
Stop();
}
return NS_OK;
}
// Break the cycle reference between Session and MediaRecorder.
void BreakCycle()
{
MOZ_ASSERT(NS_IsMainThread());
if (mRecorder) {
mRecorder->RemoveSession(this);
mRecorder = nullptr;
}
}
private:
// Hold reference to MediaRecoder that ensure MediaRecorder is alive
// if there is an active session. Access ONLY on main thread.
@ -640,16 +636,8 @@ MediaRecorder::Start(const Optional<int32_t>& aTimeSlice, ErrorResult& aResult)
mState = RecordingState::Recording;
// Start a session.
// Add session's reference here and pass to ExtractRunnable since the
// MediaRecorder doesn't hold any reference to Session. Also
// the DoSessionEndTask need this reference for DestroyRunnable.
// Note that the reference count is not balance here due to the
// DestroyRunnable will destroyed the last reference.
nsRefPtr<Session> session = new Session(this, timeSlice);
Session* rawPtr;
session.forget(&rawPtr);
mSessions.AppendElement();
mSessions.LastElement() = rawPtr;
mSessions.LastElement() = new Session(this, timeSlice);
mSessions.LastElement()->Start();
}

Просмотреть файл

@ -111,9 +111,9 @@ protected:
nsRefPtr<DOMMediaStream> mStream;
// The current state of the MediaRecorder object.
RecordingState mState;
// Hold the sessions pointer and clean it when the DestroyRunnable for a
// Hold the sessions reference and clean it when the DestroyRunnable for a
// session is running.
nsTArray<Session*> mSessions;
nsTArray<nsRefPtr<Session> > mSessions;
// It specifies the container format as well as the audio and video capture formats.
nsString mMimeType;

Просмотреть файл

@ -399,6 +399,32 @@ GMPChild::GetGMPTimers()
return mTimerChild;
}
PGMPStorageChild*
GMPChild::AllocPGMPStorageChild()
{
return new GMPStorageChild(this);
}
bool
GMPChild::DeallocPGMPStorageChild(PGMPStorageChild* aActor)
{
mStorage = nullptr;
return true;
}
GMPStorageChild*
GMPChild::GetGMPStorage()
{
if (!mStorage) {
PGMPStorageChild* sc = SendPGMPStorageConstructor();
if (!sc) {
return nullptr;
}
mStorage = static_cast<GMPStorageChild*>(sc);
}
return mStorage;
}
bool
GMPChild::RecvCrashPluginNow()
{

Просмотреть файл

@ -9,6 +9,7 @@
#include "mozilla/gmp/PGMPChild.h"
#include "GMPSharedMemManager.h"
#include "GMPTimerChild.h"
#include "GMPStorageChild.h"
#include "gmp-async-shutdown.h"
#include "gmp-entrypoints.h"
#include "prlink.h"
@ -37,6 +38,7 @@ public:
// Main thread only.
GMPTimerChild* GetGMPTimers();
GMPStorageChild* GetGMPStorage();
// GMPSharedMem
virtual void CheckThread() MOZ_OVERRIDE;
@ -67,6 +69,9 @@ private:
virtual PGMPTimerChild* AllocPGMPTimerChild() MOZ_OVERRIDE;
virtual bool DeallocPGMPTimerChild(PGMPTimerChild* aActor) MOZ_OVERRIDE;
virtual PGMPStorageChild* AllocPGMPStorageChild() MOZ_OVERRIDE;
virtual bool DeallocPGMPStorageChild(PGMPStorageChild* aActor) MOZ_OVERRIDE;
virtual bool RecvCrashPluginNow() MOZ_OVERRIDE;
virtual bool RecvBeginAsyncShutdown() MOZ_OVERRIDE;
@ -75,6 +80,7 @@ private:
GMPAsyncShutdown* mAsyncShutdown;
nsRefPtr<GMPTimerChild> mTimerChild;
nsRefPtr<GMPStorageChild> mStorage;
PRLibrary* mLib;
GMPGetAPIFunc mGetAPIFunc;

Просмотреть файл

@ -161,6 +161,12 @@ GMPParent::CloseIfUnused()
mVideoEncoders.IsEmpty() &&
mDecryptors.IsEmpty() &&
mAudioDecoders.IsEmpty()) {
// Ensure all timers are killed.
for (uint32_t i = mTimers.Length(); i > 0; i--) {
mTimers[i - 1]->Shutdown();
}
if (mAsyncShutdownRequired) {
if (!mAsyncShutdownInProgress) {
LOGD(("%s::%s: %p sending async shutdown notification", __CLASS__,
@ -171,6 +177,10 @@ GMPParent::CloseIfUnused()
}
}
} else {
// Any async shutdown must be complete. Shutdown GMPStorage.
for (size_t i = mStorage.Length(); i > 0; i--) {
mStorage[i - 1]->Shutdown();
}
Shutdown();
}
}
@ -218,24 +228,23 @@ GMPParent::CloseActive(bool aDieWhenUnloaded)
mVideoDecoders[i - 1]->Shutdown();
}
// Invalidate and remove any remaining API objects.
for (uint32_t i = mVideoEncoders.Length(); i > 0; i--) {
mVideoEncoders[i - 1]->Shutdown();
}
// Invalidate and remove any remaining API objects.
for (uint32_t i = mDecryptors.Length(); i > 0; i--) {
mDecryptors[i - 1]->Shutdown();
}
// Invalidate and remove any remaining API objects.
for (uint32_t i = mAudioDecoders.Length(); i > 0; i--) {
mAudioDecoders[i - 1]->Shutdown();
}
for (uint32_t i = mTimers.Length(); i > 0; i--) {
mTimers[i - 1]->Shutdown();
}
// Note: we don't shutdown timers here, we do that in CloseIfUnused(),
// as there are multiple entry points to CloseIfUnused().
// Note: We don't shutdown storage API objects here, as they need to
// work during async shutdown of GMPs.
// Note: the shutdown of the codecs is async! don't kill
// the plugin-container until they're all safely shut down via
@ -658,6 +667,29 @@ GMPParent::DeallocPGMPAudioDecoderParent(PGMPAudioDecoderParent* aActor)
return true;
}
PGMPStorageParent*
GMPParent::AllocPGMPStorageParent()
{
GMPStorageParent* p = new GMPStorageParent(mOrigin, this);
mStorage.AppendElement(p); // Addrefs, released in DeallocPGMPStorageParent.
return p;
}
bool
GMPParent::DeallocPGMPStorageParent(PGMPStorageParent* aActor)
{
GMPStorageParent* p = static_cast<GMPStorageParent*>(aActor);
p->Shutdown();
mStorage.RemoveElement(p);
return true;
}
bool
GMPParent::RecvPGMPStorageConstructor(PGMPStorageParent* actor)
{
return true;
}
bool
GMPParent::RecvPGMPTimerConstructor(PGMPTimerParent* actor)
{

Просмотреть файл

@ -13,6 +13,7 @@
#include "GMPVideoDecoderParent.h"
#include "GMPVideoEncoderParent.h"
#include "GMPTimerParent.h"
#include "GMPStorageParent.h"
#include "mozilla/gmp/PGMPParent.h"
#include "nsCOMPtr.h"
#include "nscore.h"
@ -154,6 +155,10 @@ private:
virtual PGMPAudioDecoderParent* AllocPGMPAudioDecoderParent() MOZ_OVERRIDE;
virtual bool DeallocPGMPAudioDecoderParent(PGMPAudioDecoderParent* aActor) MOZ_OVERRIDE;
virtual bool RecvPGMPStorageConstructor(PGMPStorageParent* actor) MOZ_OVERRIDE;
virtual PGMPStorageParent* AllocPGMPStorageParent() MOZ_OVERRIDE;
virtual bool DeallocPGMPStorageParent(PGMPStorageParent* aActor) MOZ_OVERRIDE;
virtual bool RecvPGMPTimerConstructor(PGMPTimerParent* actor) MOZ_OVERRIDE;
virtual PGMPTimerParent* AllocPGMPTimerParent() MOZ_OVERRIDE;
virtual bool DeallocPGMPTimerParent(PGMPTimerParent* aActor) MOZ_OVERRIDE;
@ -177,6 +182,7 @@ private:
nsTArray<nsRefPtr<GMPDecryptorParent>> mDecryptors;
nsTArray<nsRefPtr<GMPAudioDecoderParent>> mAudioDecoders;
nsTArray<nsRefPtr<GMPTimerParent>> mTimers;
nsTArray<nsRefPtr<GMPStorageParent>> mStorage;
nsCOMPtr<nsIThread> mGMPThread;
// Origin the plugin is assigned to, or empty if the the plugin is not
// assigned to an origin.

Просмотреть файл

@ -4,6 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GMPPlatform.h"
#include "GMPStorageChild.h"
#include "GMPTimerChild.h"
#include "mozilla/Monitor.h"
#include "nsAutoPtr.h"
@ -151,6 +152,30 @@ CreateMutex(GMPMutex** aMutex)
return GMPNoErr;
}
GMPErr
CreateRecord(const char* aRecordName,
uint32_t aRecordNameSize,
GMPRecord** aOutRecord,
GMPRecordClient* aClient)
{
if (sMainLoop != MessageLoop::current()) {
NS_WARNING("GMP called CreateRecord() on non-main thread!");
return GMPGenericErr;
}
if (aRecordNameSize > GMP_MAX_RECORD_NAME_SIZE) {
NS_WARNING("GMP tried to CreateRecord with too long record name");
return GMPGenericErr;
}
GMPStorageChild* storage = sChild->GetGMPStorage();
if (!storage) {
return GMPGenericErr;
}
MOZ_ASSERT(storage);
return storage->CreateRecord(nsDependentCString(aRecordName, aRecordNameSize),
aOutRecord,
aClient);
}
GMPErr
SetTimerOnMainThread(GMPTask* aTask, int64_t aTimeoutMS)
{
@ -184,7 +209,7 @@ InitPlatformAPI(GMPPlatformAPI& aPlatformAPI, GMPChild* aChild)
aPlatformAPI.runonmainthread = &RunOnMainThread;
aPlatformAPI.syncrunonmainthread = &SyncRunOnMainThread;
aPlatformAPI.createmutex = &CreateMutex;
aPlatformAPI.createrecord = nullptr;
aPlatformAPI.createrecord = &CreateRecord;
aPlatformAPI.settimer = &SetTimerOnMainThread;
aPlatformAPI.getcurrenttime = &GetClock;
}

Просмотреть файл

@ -0,0 +1,282 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GMPStorageChild.h"
#include "GMPChild.h"
#include "gmp-storage.h"
namespace mozilla {
namespace gmp {
GMPRecordImpl::GMPRecordImpl(GMPStorageChild* aOwner,
const nsCString& aName,
GMPRecordClient* aClient)
: mName(aName)
, mClient(aClient)
, mOwner(aOwner)
, mIsClosed(true)
{
}
GMPErr
GMPRecordImpl::Open()
{
if (!mIsClosed) {
return GMPRecordInUse;
}
return mOwner->Open(this);
}
void
GMPRecordImpl::OpenComplete(GMPErr aStatus)
{
mIsClosed = false;
mClient->OpenComplete(aStatus);
}
GMPErr
GMPRecordImpl::Read()
{
if (mIsClosed) {
return GMPClosedErr;
}
return mOwner->Read(this);
}
void
GMPRecordImpl::ReadComplete(GMPErr aStatus,
const uint8_t* aBytes,
uint32_t aLength)
{
mClient->ReadComplete(aStatus, aBytes, aLength);
}
GMPErr
GMPRecordImpl::Write(const uint8_t* aData, uint32_t aDataSize)
{
if (mIsClosed) {
return GMPClosedErr;
}
return mOwner->Write(this, aData, aDataSize);
}
void
GMPRecordImpl::WriteComplete(GMPErr aStatus)
{
mClient->WriteComplete(aStatus);
}
GMPErr
GMPRecordImpl::Close()
{
nsRefPtr<GMPRecordImpl> kungfuDeathGrip(this);
if (!mIsClosed) {
// Delete the storage child's reference to us.
mOwner->Close(this);
// Owner should callback MarkClosed().
MOZ_ASSERT(mIsClosed);
}
// Delete our self reference.
Release();
return GMPNoErr;
}
void
GMPRecordImpl::MarkClosed()
{
mIsClosed = true;
}
GMPStorageChild::GMPStorageChild(GMPChild* aPlugin)
: mPlugin(aPlugin)
, mShutdown(false)
{
MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
}
GMPErr
GMPStorageChild::CreateRecord(const nsCString& aRecordName,
GMPRecord** aOutRecord,
GMPRecordClient* aClient)
{
if (mPlugin->GMPMessageLoop() != MessageLoop::current()) {
NS_WARNING("GMP used GMPStorage on non-main thread.");
return GMPGenericErr;
}
if (mShutdown) {
NS_WARNING("GMPStorage used after it's been shutdown!");
return GMPClosedErr;
}
MOZ_ASSERT(aRecordName.Length() && aOutRecord);
nsRefPtr<GMPRecordImpl> record(new GMPRecordImpl(this, aRecordName, aClient));
mRecords.Put(aRecordName, record); // Addrefs
// The GMPRecord holds a self reference until the GMP calls Close() on
// it. This means the object is always valid (even if neutered) while
// the GMP expects it to be.
record.forget(aOutRecord);
return GMPNoErr;
}
GMPErr
GMPStorageChild::Open(GMPRecordImpl* aRecord)
{
if (mPlugin->GMPMessageLoop() != MessageLoop::current()) {
NS_WARNING("GMP used GMPStorage on non-main thread.");
return GMPGenericErr;
}
if (mShutdown) {
NS_WARNING("GMPStorage used after it's been shutdown!");
return GMPClosedErr;
}
if (!SendOpen(aRecord->Name())) {
Close(aRecord);
return GMPClosedErr;
}
return GMPNoErr;
}
GMPErr
GMPStorageChild::Read(GMPRecordImpl* aRecord)
{
if (mPlugin->GMPMessageLoop() != MessageLoop::current()) {
NS_WARNING("GMP used GMPStorage on non-main thread.");
return GMPGenericErr;
}
if (mShutdown) {
NS_WARNING("GMPStorage used after it's been shutdown!");
return GMPClosedErr;
}
if (!SendRead(aRecord->Name())) {
Close(aRecord);
return GMPClosedErr;
}
return GMPNoErr;
}
GMPErr
GMPStorageChild::Write(GMPRecordImpl* aRecord,
const uint8_t* aData,
uint32_t aDataSize)
{
if (mPlugin->GMPMessageLoop() != MessageLoop::current()) {
NS_WARNING("GMP used GMPStorage on non-main thread.");
return GMPGenericErr;
}
if (mShutdown) {
NS_WARNING("GMPStorage used after it's been shutdown!");
return GMPClosedErr;
}
if (aDataSize > GMP_MAX_RECORD_SIZE) {
return GMPQuotaExceededErr;
}
nsTArray<uint8_t> data;
data.AppendElements(aData, aDataSize);
if (!SendWrite(aRecord->Name(), data)) {
Close(aRecord);
return GMPClosedErr;
}
return GMPNoErr;
}
GMPErr
GMPStorageChild::Close(GMPRecordImpl* aRecord)
{
if (mPlugin->GMPMessageLoop() != MessageLoop::current()) {
NS_WARNING("GMP used GMPStorage on non-main thread.");
return GMPGenericErr;
}
if (!mRecords.Contains(aRecord->Name())) {
// Already closed.
return GMPClosedErr;
}
GMPErr rv = GMPNoErr;
if (!mShutdown && !SendClose(aRecord->Name())) {
rv = GMPGenericErr;
}
aRecord->MarkClosed();
mRecords.Remove(aRecord->Name());
return rv;
}
bool
GMPStorageChild::RecvOpenComplete(const nsCString& aRecordName,
const GMPErr& aStatus)
{
if (mShutdown) {
return true;
}
nsRefPtr<GMPRecordImpl> record;
if (!mRecords.Get(aRecordName, getter_AddRefs(record)) || !record) {
// Not fatal.
return true;
}
record->OpenComplete(aStatus);
if (GMP_FAILED(aStatus)) {
Close(record);
}
return true;
}
bool
GMPStorageChild::RecvReadComplete(const nsCString& aRecordName,
const GMPErr& aStatus,
const InfallibleTArray<uint8_t>& aBytes)
{
if (mShutdown) {
return true;
}
nsRefPtr<GMPRecordImpl> record;
if (!mRecords.Get(aRecordName, getter_AddRefs(record)) || !record) {
// Not fatal.
return true;
}
record->ReadComplete(aStatus,
aBytes.Elements(),
aBytes.Length());
if (GMP_FAILED(aStatus)) {
Close(record);
}
return true;
}
bool
GMPStorageChild::RecvWriteComplete(const nsCString& aRecordName,
const GMPErr& aStatus)
{
if (mShutdown) {
return true;
}
nsRefPtr<GMPRecordImpl> record;
if (!mRecords.Get(aRecordName, getter_AddRefs(record)) || !record) {
// Not fatal.
return true;
}
record->WriteComplete(aStatus);
if (GMP_FAILED(aStatus)) {
Close(record);
}
return true;
}
bool
GMPStorageChild::RecvShutdown()
{
// Block any new storage requests, and thus any messages back to the
// parent. We don't delete any objects here, as that may invalidate
// GMPRecord pointers held by the GMP.
mShutdown = true;
return true;
}
} // namespace gmp
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,95 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GMPStorageChild_h_
#define GMPStorageChild_h_
#include "mozilla/gmp/PGMPStorageChild.h"
#include "gmp-storage.h"
#include "nsTHashtable.h"
#include "nsRefPtrHashtable.h"
namespace mozilla {
namespace gmp {
class GMPChild;
class GMPStorageChild;
class GMPRecordImpl : public GMPRecord
{
public:
NS_INLINE_DECL_REFCOUNTING(GMPRecordImpl)
GMPRecordImpl(GMPStorageChild* aOwner,
const nsCString& aName,
GMPRecordClient* aClient);
// GMPRecord.
virtual GMPErr Open() MOZ_OVERRIDE;
virtual GMPErr Read() MOZ_OVERRIDE;
virtual GMPErr Write(const uint8_t* aData,
uint32_t aDataSize) MOZ_OVERRIDE;
virtual GMPErr Close() MOZ_OVERRIDE;
const nsCString& Name() const { return mName; }
void OpenComplete(GMPErr aStatus);
void ReadComplete(GMPErr aStatus, const uint8_t* aBytes, uint32_t aLength);
void WriteComplete(GMPErr aStatus);
void MarkClosed();
private:
~GMPRecordImpl() {}
const nsCString mName;
GMPRecordClient* const mClient;
GMPStorageChild* const mOwner;
bool mIsClosed;
};
class GMPStorageChild : public PGMPStorageChild
{
public:
NS_INLINE_DECL_REFCOUNTING(GMPStorageChild)
GMPStorageChild(GMPChild* aPlugin);
GMPErr CreateRecord(const nsCString& aRecordName,
GMPRecord** aOutRecord,
GMPRecordClient* aClient);
GMPErr Open(GMPRecordImpl* aRecord);
GMPErr Read(GMPRecordImpl* aRecord);
GMPErr Write(GMPRecordImpl* aRecord,
const uint8_t* aData,
uint32_t aDataSize);
GMPErr Close(GMPRecordImpl* aRecord);
protected:
~GMPStorageChild() {}
// PGMPStorageChild
virtual bool RecvOpenComplete(const nsCString& aRecordName,
const GMPErr& aStatus) MOZ_OVERRIDE;
virtual bool RecvReadComplete(const nsCString& aRecordName,
const GMPErr& aStatus,
const InfallibleTArray<uint8_t>& aBytes) MOZ_OVERRIDE;
virtual bool RecvWriteComplete(const nsCString& aRecordName,
const GMPErr& aStatus) MOZ_OVERRIDE;
virtual bool RecvShutdown() MOZ_OVERRIDE;
private:
nsRefPtrHashtable<nsCStringHashKey, GMPRecordImpl> mRecords;
GMPChild* mPlugin;
bool mShutdown;
};
} // namespace gmp
} // namespace mozilla
#endif // GMPStorageChild_h_

Просмотреть файл

@ -0,0 +1,287 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GMPStorageParent.h"
#include "mozilla/SyncRunnable.h"
#include "plhash.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "GMPParent.h"
#include "gmp-storage.h"
#include "mozilla/unused.h"
namespace mozilla {
#ifdef LOG
#undef LOG
#endif
#ifdef PR_LOGGING
extern PRLogModuleInfo* GetGMPLog();
#define LOGD(msg) PR_LOG(GetGMPLog(), PR_LOG_DEBUG, msg)
#define LOG(level, msg) PR_LOG(GetGMPLog(), (level), msg)
#else
#define LOGD(msg)
#define LOG(level, msg)
#endif
#ifdef __CLASS__
#undef __CLASS__
#endif
#define __CLASS__ "GMPParent"
namespace gmp {
class GetTempDirTask : public nsRunnable
{
public:
NS_IMETHOD Run() {
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIFile> tmpFile;
nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpFile));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
tmpFile->GetPath(mPath);
return NS_OK;
}
nsString mPath;
};
// We store the records in files in the system temp dir.
static nsresult
GetGMPStorageDir(nsIFile** aTempDir, const nsString& aOrigin)
{
if (NS_WARN_IF(!aTempDir)) {
return NS_ERROR_INVALID_ARG;
}
// Directory service is main thread only...
nsRefPtr<GetTempDirTask> task = new GetTempDirTask();
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
mozilla::SyncRunnable::DispatchToThread(mainThread, task);
nsCOMPtr<nsIFile> tmpFile;
nsresult rv = NS_NewLocalFile(task->mPath, false, getter_AddRefs(tmpFile));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = tmpFile->AppendNative(nsDependentCString("mozilla-gmp-storage"));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// TODO: When aOrigin is the same node-id as the GMP sees in the child
// process (a UUID or somesuch), we can just append it un-hashed here.
// This should reduce the chance of hash collsions exposing data.
nsAutoString nodeIdHash;
nodeIdHash.AppendInt(HashString(aOrigin.get()));
rv = tmpFile->Append(nodeIdHash);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = tmpFile->Create(nsIFile::DIRECTORY_TYPE, 0700);
if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
tmpFile.forget(aTempDir);
return NS_OK;
}
GMPStorageParent::GMPStorageParent(const nsString& aOrigin,
GMPParent* aPlugin)
: mOrigin(aOrigin)
, mPlugin(aPlugin)
, mShutdown(false)
{
}
enum OpenFileMode { ReadWrite, Truncate };
nsresult
OpenStorageFile(const nsCString& aRecordName,
const nsString& aNodeId,
const OpenFileMode aMode,
PRFileDesc** aOutFD)
{
MOZ_ASSERT(aOutFD);
nsCOMPtr<nsIFile> f;
nsresult rv = GetGMPStorageDir(getter_AddRefs(f), aNodeId);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsAutoString recordNameHash;
recordNameHash.AppendInt(HashString(aRecordName.get()));
f->Append(recordNameHash);
auto mode = PR_RDWR | PR_CREATE_FILE;
if (aMode == Truncate) {
mode |= PR_TRUNCATE;
}
return f->OpenNSPRFileDesc(mode, PR_IRWXU, aOutFD);
}
bool
GMPStorageParent::RecvOpen(const nsCString& aRecordName)
{
if (mShutdown) {
return true;
}
if (mOrigin.EqualsASCII("null")) {
// Refuse to open storage if the page is the "null" origin; if the page
// is opened from disk.
NS_WARNING("Refusing to open storage for null origin");
unused << SendOpenComplete(aRecordName, GMPGenericErr);
return true;
}
if (aRecordName.IsEmpty() || mFiles.Contains(aRecordName)) {
unused << SendOpenComplete(aRecordName, GMPRecordInUse);
return true;
}
PRFileDesc* fd = nullptr;
nsresult rv = OpenStorageFile(aRecordName, mOrigin, ReadWrite, &fd);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to open storage file.");
unused << SendOpenComplete(aRecordName, GMPGenericErr);
return true;
}
mFiles.Put(aRecordName, fd);
unused << SendOpenComplete(aRecordName, GMPNoErr);
return true;
}
bool
GMPStorageParent::RecvRead(const nsCString& aRecordName)
{
LOGD(("%s::%s: %p record=%s", __CLASS__, __FUNCTION__, this, aRecordName.get()));
if (mShutdown) {
return true;
}
PRFileDesc* fd = mFiles.Get(aRecordName);
nsTArray<uint8_t> data;
if (!fd) {
unused << SendReadComplete(aRecordName, GMPClosedErr, data);
return true;
}
int32_t len = PR_Seek(fd, 0, PR_SEEK_END);
PR_Seek(fd, 0, PR_SEEK_SET);
if (len > GMP_MAX_RECORD_SIZE) {
// Refuse to read big records.
unused << SendReadComplete(aRecordName, GMPQuotaExceededErr, data);
return true;
}
data.SetLength(len);
auto bytesRead = PR_Read(fd, data.Elements(), len);
auto res = (bytesRead == len) ? GMPNoErr : GMPGenericErr;
unused << SendReadComplete(aRecordName, res, data);
return true;
}
bool
GMPStorageParent::RecvWrite(const nsCString& aRecordName,
const InfallibleTArray<uint8_t>& aBytes)
{
LOGD(("%s::%s: %p record=%s", __CLASS__, __FUNCTION__, this, aRecordName.get()));
if (mShutdown) {
return true;
}
if (aBytes.Length() > GMP_MAX_RECORD_SIZE) {
unused << SendWriteComplete(aRecordName, GMPQuotaExceededErr);
return true;
}
PRFileDesc* fd = mFiles.Get(aRecordName);
if (!fd) {
unused << SendWriteComplete(aRecordName, GMPGenericErr);
return true;
}
// Write operations overwrite the entire record. So re-open the file
// in truncate mode, to clear its contents.
PR_Close(fd);
mFiles.Remove(aRecordName);
if (NS_FAILED(OpenStorageFile(aRecordName, mOrigin, Truncate, &fd))) {
unused << SendWriteComplete(aRecordName, GMPGenericErr);
return true;
}
mFiles.Put(aRecordName, fd);
int32_t bytesWritten = PR_Write(fd, aBytes.Elements(), aBytes.Length());
auto res = (bytesWritten == (int32_t)aBytes.Length()) ? GMPNoErr : GMPGenericErr;
unused << SendWriteComplete(aRecordName, res);
return true;
}
bool
GMPStorageParent::RecvClose(const nsCString& aRecordName)
{
LOGD(("%s::%s: %p record=%s", __CLASS__, __FUNCTION__, this, aRecordName.get()));
if (mShutdown) {
return true;
}
PRFileDesc* fd = mFiles.Get(aRecordName);
if (!fd) {
return true;
}
PR_Close(fd);
mFiles.Remove(aRecordName);
return true;
}
void
GMPStorageParent::ActorDestroy(ActorDestroyReason aWhy)
{
LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
Shutdown();
}
PLDHashOperator
CloseFile(const nsACString& key, PRFileDesc*& entry, void* cx)
{
PR_Close(entry);
return PL_DHASH_REMOVE;
}
void
GMPStorageParent::Shutdown()
{
LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
if (mShutdown) {
return;
}
mShutdown = true;
unused << SendShutdown();
mFiles.Enumerate(CloseFile, nullptr);
MOZ_ASSERT(!mFiles.Count());
}
} // namespace gmp
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,47 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GMPStorageParent_h_
#define GMPStorageParent_h_
#include "mozilla/gmp/PGMPStorageParent.h"
#include "gmp-storage.h"
#include "nsTHashtable.h"
#include "nsDataHashtable.h"
#include "prio.h"
namespace mozilla {
namespace gmp {
class GMPParent;
class GMPStorageParent : public PGMPStorageParent {
public:
NS_INLINE_DECL_REFCOUNTING(GMPStorageParent)
GMPStorageParent(const nsString& aOrigin, GMPParent* aPlugin);
void Shutdown();
protected:
virtual bool RecvOpen(const nsCString& aRecordName) MOZ_OVERRIDE;
virtual bool RecvRead(const nsCString& aRecordName) MOZ_OVERRIDE;
virtual bool RecvWrite(const nsCString& aRecordName,
const InfallibleTArray<uint8_t>& aBytes) MOZ_OVERRIDE;
virtual bool RecvClose(const nsCString& aRecordName) MOZ_OVERRIDE;
virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
private:
~GMPStorageParent() {}
nsDataHashtable<nsCStringHashKey, PRFileDesc*> mFiles;
const nsString mOrigin;
nsRefPtr<GMPParent> mPlugin;
bool mShutdown;
};
} // namespace gmp
} // namespace mozilla
#endif // GMPStorageParent_h_

Просмотреть файл

@ -9,6 +9,7 @@ include protocol PCrashReporter;
include protocol PGMPDecryptor;
include protocol PGMPAudioDecoder;
include protocol PGMPTimer;
include protocol PGMPStorage;
using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
@ -23,10 +24,12 @@ intr protocol PGMP
manages PGMPVideoEncoder;
manages PCrashReporter;
manages PGMPTimer;
manages PGMPStorage;
parent:
async PCrashReporter(NativeThreadId tid);
async PGMPTimer();
async PGMPStorage();
async AsyncShutdownComplete();
async AsyncShutdownRequired();

Просмотреть файл

@ -0,0 +1,34 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
include protocol PGMP;
include GMPTypes;
using GMPErr from "gmp-errors.h";
namespace mozilla {
namespace gmp {
async protocol PGMPStorage
{
manager PGMP;
child:
OpenComplete(nsCString aRecordName, GMPErr aStatus);
ReadComplete(nsCString aRecordName, GMPErr aStatus, uint8_t[] aBytes);
WriteComplete(nsCString aRecordName, GMPErr aStatus);
Shutdown();
parent:
Open(nsCString aRecordName);
Read(nsCString aRecordName);
Write(nsCString aRecordName, uint8_t[] aBytes);
Close(nsCString aRecordName);
__delete__();
};
} // namespace gmp
} // namespace mozilla

Просмотреть файл

@ -69,6 +69,8 @@ typedef GMPErr (*GMPCreateThreadPtr)(GMPThread** aThread);
typedef GMPErr (*GMPRunOnMainThreadPtr)(GMPTask* aTask);
typedef GMPErr (*GMPSyncRunOnMainThreadPtr)(GMPTask* aTask);
typedef GMPErr (*GMPCreateMutexPtr)(GMPMutex** aMutex);
// Call on main thread only.
typedef GMPErr (*GMPCreateRecordPtr)(const char* aRecordName,
uint32_t aRecordNameSize,
GMPRecord** aOutRecord,

Просмотреть файл

@ -20,16 +20,31 @@
#include "gmp-errors.h"
#include <stdint.h>
// Maximum size of a record, in bytes.
#define GMP_MAX_RECORD_SIZE (1024 * 1024 * 1024)
// Maximum length of a record name.
#define GMP_MAX_RECORD_NAME_SIZE 200
// Provides basic per-origin storage for CDMs. GMPRecord instances can be
// retrieved by calling GMPPlatformAPI->openstorage. Multiple GMPRecords
// with different names can be open at once, but a single record can only
// be opened by one client at a time. This interface is asynchronous, with
// results being returned via callbacks to the GMPRecordClient pointer
// provided to the GMPPlatformAPI->openstorage call, on the main thread.
//
// Lifecycle: Once opened, the GMPRecord object remains allocated until
// GMPRecord::Close() is called. If any GMPRecord function, either
// synchronously or asynchronously through a GMPRecordClient callback,
// returns an error, the GMP is responsible for calling Close() on the
// GMPRecord to delete the GMPRecord object's memory. If your GMP does not
// call Close(), the GMPRecord's memory will leak.
class GMPRecord {
public:
// Opens the record. Calls OpenComplete() once the record is open.
// Note: Only work when GMP is loading content from a webserver.
// Does not work for web pages on loaded from disk.
// Note: OpenComplete() is only called if this returns GMPNoErr.
virtual GMPErr Open() = 0;
@ -39,13 +54,15 @@ public:
virtual GMPErr Read() = 0;
// Writes aDataSize bytes of aData into the record, overwriting the
// contents of the record. Overwriting with 0 bytes "deletes" the file.
// contents of the record, truncating it to aDataSize length.
// Overwriting with 0 bytes "deletes" the record.
// Note: WriteComplete is only called if this returns GMPNoErr.
virtual GMPErr Write(const uint8_t* aData, uint32_t aDataSize) = 0;
// Closes a record. GMPRecord object must not be used after this is
// called, request a new one with GMPPlatformAPI->openstorage to re-open
// this record. Cancels all callbacks.
// Closes a record, deletes the GMPRecord object. The GMPRecord object
// must not be used after this is called, request a new one with
// GMPPlatformAPI->openstorage to re-open this record. Cancels all
// callbacks.
virtual GMPErr Close() = 0;
virtual ~GMPRecord() {}
@ -61,7 +78,8 @@ class GMPRecordClient {
// - GMPNoErr - Record opened successfully. Record may be empty.
// - GMPRecordInUse - This record is in use by another client.
// - GMPGenericErr - Unspecified error.
// Do not use the GMPRecord if aStatus is not GMPNoErr.
// If aStatus is not GMPNoErr, the GMPRecord is unusable, and you must
// call Close() on the GMPRecord to dispose of it.
virtual void OpenComplete(GMPErr aStatus) = 0;
// Response to a GMPRecord::Read() call, where aData is the record contents,
@ -74,7 +92,8 @@ class GMPRecordClient {
// - GMPRecordInUse - There are other operations or clients in use on
// this record.
// - GMPGenericErr - Unspecified error.
// Do not continue to use the GMPRecord if aStatus is not GMPNoErr.
// If aStatus is not GMPNoErr, the GMPRecord is unusable, and you must
// call Close() on the GMPRecord to dispose of it.
virtual void ReadComplete(GMPErr aStatus,
const uint8_t* aData,
uint32_t aDataSize) = 0;
@ -84,7 +103,8 @@ class GMPRecordClient {
// - GMPRecordInUse - There are other operations or clients in use on
// this record.
// - GMPGenericErr - Unspecified error.
// Do not continue to use the GMPRecord if aStatus is not GMPNoErr.
// If aStatus is not GMPNoErr, the GMPRecord is unusable, and you must
// call Close() on the GMPRecord to dispose of it.
virtual void WriteComplete(GMPErr aStatus) = 0;
virtual ~GMPRecordClient() {}

Просмотреть файл

@ -46,6 +46,8 @@ EXPORTS += [
'GMPProcessParent.h',
'GMPService.h',
'GMPSharedMemManager.h',
'GMPStorageChild.h',
'GMPStorageParent.h',
'GMPTimerChild.h',
'GMPTimerParent.h',
'GMPVideoDecoderChild.h',
@ -74,6 +76,8 @@ UNIFIED_SOURCES += [
'GMPProcessParent.cpp',
'GMPService.cpp',
'GMPSharedMemManager.cpp',
'GMPStorageChild.cpp',
'GMPStorageParent.cpp',
'GMPTimerChild.cpp',
'GMPTimerParent.cpp',
'GMPVideoDecoderChild.cpp',
@ -91,6 +95,7 @@ IPDL_SOURCES += [
'PGMP.ipdl',
'PGMPAudioDecoder.ipdl',
'PGMPDecryptor.ipdl',
'PGMPStorage.ipdl',
'PGMPTimer.ipdl',
'PGMPVideoDecoder.ipdl',
'PGMPVideoEncoder.ipdl',

Просмотреть файл

@ -46,11 +46,11 @@ load 880384.html
load 880404.html
load 880724.html
load 881775.html
test-pref(media.webvtt.enabled,true) load 882549.html
load 882549.html
load 882956.html
load 884459.html
load 889042.html
test-pref(media.webvtt.enabled,true) load 894104.html
load 894104.html
load 907986-1.html
load 907986-2.html
load 907986-3.html
@ -71,9 +71,9 @@ load 1015662.html
skip-if(Android||B2G) test-pref(media.navigator.permission.disabled,true) load 1028458.html # bug 1048863
load buffer-source-ended-1.html
HTTP load media-element-source-seek-1.html
load offline-buffer-source-ended-1.html
load oscillator-ended-1.html
load oscillator-ended-2.html
load offline-buffer-source-ended-1.html
include ../../mediasource/test/crashtests/crashtests.list
# This needs to run at the end to avoid leaking busted state into other tests.

Просмотреть файл

@ -17,33 +17,33 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1018933
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true]]},
function() {
var video = document.createElement("video");
video.src = "seek.webm";
video.preload = "auto";
var trackElement = document.createElement("track");
trackElement.src = "basic.vtt";
trackElement.kind = "subtitles";
document.getElementById("content").appendChild(video);
video.appendChild(trackElement);
// Accessing the track now would have caused the bug as the track element
// shouldn't have had time to bind to the tree yet.
trackElement.track.mode = 'showing';
var video = document.createElement("video");
video.src = "seek.webm";
video.preload = "auto";
video.addEventListener("loadedmetadata", function run_tests() {
// Re-que run_tests() at the end of the event loop until the track
// element has loaded its data.
if (trackElement.readyState == 1) {
setTimeout(run_tests, 0);
return;
}
is(video.textTracks.length, 1, "Video should have one TextTrack.");
SimpleTest.finish();
});
var trackElement = document.createElement("track");
trackElement.src = "basic.vtt";
trackElement.kind = "subtitles";
document.getElementById("content").appendChild(video);
video.appendChild(trackElement);
// Accessing the track now would have caused the bug as the track element
// shouldn't have had time to bind to the tree yet.
trackElement.track.mode = 'showing';
video.addEventListener("loadedmetadata", function run_tests() {
// Re-que run_tests() at the end of the event loop until the track
// element has loaded its data.
if (trackElement.readyState == 1) {
setTimeout(run_tests, 0);
return;
}
);
is(video.textTracks.length, 1, "Video should have one TextTrack.");
SimpleTest.finish();
});
</script>
</pre>
</body>

Просмотреть файл

@ -17,43 +17,42 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=883173
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true]]},
function() {
var video = document.createElement("video");
video.src = "seek.webm";
video.preload = "auto";
var trackElement = document.createElement("track");
trackElement.src = "bug883173.vtt";
trackElement.kind = "subtitles";
document.getElementById("content").appendChild(video);
video.appendChild(trackElement);
video.addEventListener("loadedmetadata",
function run_tests() {
// Re-queue run_tests() at the end of the event loop until the track
// element has loaded its data.
if (trackElement.readyState == 1) {
setTimeout(run_tests, 0);
return;
}
is(trackElement.readyState, 2, "Track::ReadyState should be set to LOADED.");
// Set mode to hidden so that the active cue lists are being updated.
trackElement.track.mode = "hidden";
var expected = [[1, 2], [1, 3], [2, 3], [2, 4], [3, 4]];
var cueList = trackElement.track.cues;
var video = document.createElement("video");
video.src = "seek.webm";
video.preload = "auto";
is(cueList.length, expected.length, "Cue list length should be 5.");
for (var i = 0; i < expected.length; i++) {
is(cueList[i].startTime, expected[i][0], "Cue's start time should be " + expected[i][0]);
is(cueList[i].endTime, expected[i][1], "Cue's end time should be " + expected[i][1]);
}
var trackElement = document.createElement("track");
trackElement.src = "bug883173.vtt";
trackElement.kind = "subtitles";
SimpleTest.finish();
}
);
document.getElementById("content").appendChild(video);
video.appendChild(trackElement);
video.addEventListener("loadedmetadata",
function run_tests() {
// Re-queue run_tests() at the end of the event loop until the track
// element has loaded its data.
if (trackElement.readyState == 1) {
setTimeout(run_tests, 0);
return;
}
is(trackElement.readyState, 2, "Track::ReadyState should be set to LOADED.");
// Set mode to hidden so that the active cue lists are being updated.
trackElement.track.mode = "hidden";
var expected = [[1, 2], [1, 3], [2, 3], [2, 4], [3, 4]];
var cueList = trackElement.track.cues;
is(cueList.length, expected.length, "Cue list length should be 5.");
for (var i = 0; i < expected.length; i++) {
is(cueList[i].startTime, expected[i][0], "Cue's start time should be " + expected[i][0]);
is(cueList[i].endTime, expected[i][1], "Cue's end time should be " + expected[i][1]);
}
SimpleTest.finish();
}
);
</script>
</pre>
</body>

Просмотреть файл

@ -17,47 +17,43 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=895091
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true]]},
function() {
var video = document.createElement("video");
video.src = "seek.webm";
video.preload = "auto";
var trackElement = document.createElement("track");
trackElement.src = "long.vtt";
trackElement.kind = "subtitles";
var video = document.createElement("video");
video.src = "seek.webm";
video.preload = "auto";
var trackElementTwo = document.createElement("track");
trackElementTwo.src = "long.vtt";
trackElementTwo.kind = "subtitles";
var trackElement = document.createElement("track");
trackElement.src = "long.vtt";
trackElement.kind = "subtitles";
document.getElementById("content").appendChild(video);
video.appendChild(trackElement);
video.appendChild(trackElementTwo);
var trackElementTwo = document.createElement("track");
trackElementTwo.src = "long.vtt";
trackElementTwo.kind = "subtitles";
video.addEventListener("loadedmetadata", function run_tests() {
// Re-que run_tests() at the end of the event loop until the track
// element has loaded its data.
if (trackElement.readyState == 1 || trackElementTwo.readyState == 1) {
setTimeout(run_tests, 0);
return;
}
document.getElementById("content").appendChild(video);
video.appendChild(trackElement);
video.appendChild(trackElementTwo);
// Set mode to hidden so that the active cue lists are being updated.
trackElement.track.mode = "hidden";
trackElementTwo.track.mode = "hidden";
is(trackElement.readyState, 2, "Track::ReadyState should be set to LOADED.");
is(trackElementTwo.readyState, 2, "Track::ReadyState should be set to LOADED.");
is(trackElement.track.cues.length, 2000, "Cue list length should be 2000.");
is(trackElementTwo.track.cues.length, 2000, "Cue list length should be 2000.");
SimpleTest.finish();
});
video.addEventListener("loadedmetadata", function run_tests() {
// Re-que run_tests() at the end of the event loop until the track
// element has loaded its data.
if (trackElement.readyState == 1 || trackElementTwo.readyState == 1) {
setTimeout(run_tests, 0);
return;
}
);
// Set mode to hidden so that the active cue lists are being updated.
trackElement.track.mode = "hidden";
trackElementTwo.track.mode = "hidden";
is(trackElement.readyState, 2, "Track::ReadyState should be set to LOADED.");
is(trackElementTwo.readyState, 2, "Track::ReadyState should be set to LOADED.");
is(trackElement.track.cues.length, 2000, "Cue list length should be 2000.");
is(trackElementTwo.track.cues.length, 2000, "Cue list length should be 2000.");
SimpleTest.finish();
});
</script>
</pre>
</body>

Просмотреть файл

@ -18,25 +18,23 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=905320
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true]]},
function() {
var audio = document.createElement("audio");
// Check leaking on TextTrackList objects.
window.ttl = audio.textTracks;
ttl.addEventListener("click", function(){}, false);
// Check leaking on VTTCue objects.
window.ttc = new VTTCue(3, 4, "Test.");
ttc.addEventListener("click", function() {}, false);
var audio = document.createElement("audio");
// Check leaking on TextTrack objects.
audio.addTextTrack("subtitles", "label", "en-CA");
ttl[0].addEventListener("click", function() {}, false);
// Check leaking on TextTrackList objects.
window.ttl = audio.textTracks;
ttl.addEventListener("click", function(){}, false);
ok(true); // Need to have at least one assertion for Mochitest to be happy.
SimpleTest.finish();
}
);
// Check leaking on VTTCue objects.
window.ttc = new VTTCue(3, 4, "Test.");
ttc.addEventListener("click", function() {}, false);
// Check leaking on TextTrack objects.
audio.addTextTrack("subtitles", "label", "en-CA");
ttl[0].addEventListener("click", function() {}, false);
ok(true); // Need to have at least one assertion for Mochitest to be happy.
SimpleTest.finish();
</script>
</pre>
</body>

Просмотреть файл

@ -17,15 +17,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=919265
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true]]},
function() {
// We shouldn't leak upon shutdown.
(new VTTCue(0, 0, "")).getCueAsHTML();
// We need to assert something for Mochitest to be happy.
ok(true);
SimpleTest.finish();
}
);
// We shouldn't leak upon shutdown.
(new VTTCue(0, 0, "")).getCueAsHTML();
// We need to assert something for Mochitest to be happy.
ok(true);
SimpleTest.finish();
</script>
</pre>
</body>

Просмотреть файл

@ -17,15 +17,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=957847
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true]]},
function() {
var trackElement = document.createElement('track');
trackElement.track.addCue(new VTTCue(0, 1, "A"));
// We need to assert something for Mochitest to be happy.
ok(true);
SimpleTest.finish();
}
);
var trackElement = document.createElement('track');
trackElement.track.addCue(new VTTCue(0, 1, "A"));
// We need to assert something for Mochitest to be happy.
ok(true);
SimpleTest.finish();
</script>
</pre>
</body>

Просмотреть файл

@ -17,8 +17,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=833386
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true],
["media.webvtt.regions.enabled", true]]},
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.regions.enabled", true]]},
function() {
var video = document.createElement("video");

Просмотреть файл

@ -16,8 +16,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=881976
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true],
["media.webvtt.regions.enabled", true]]},
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.regions.enabled", true]]},
function() {
var video = document.createElement("video");

Просмотреть файл

@ -17,15 +17,16 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=833386
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true],
["media.webvtt.regions.enabled", true]]},
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.regions.enabled", true]]},
function() {
var video = document.createElement("video");
video.src = "seek.webm";
video.preload = "auto";
var trackElement = document.createElement("track");
trackElement.src = "basic.vtt";
trackElement.kind = "subtitles";
document.getElementById("content").appendChild(video);
video.appendChild(trackElement);
video.addEventListener("loadedmetadata", function run_tests() {
@ -35,6 +36,7 @@ SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true],
setTimeout(run_tests, 0);
return;
}
is(trackElement.readyState, 2, "Track::ReadyState should be set to LOADED.");
// Set mode to hidden so that the active cue lists are being updated.
trackElement.track.mode = "hidden";

Просмотреть файл

@ -16,18 +16,15 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=967157
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true]]},
function() {
var cue = new VTTCue(0, 1, "Some text.");
is(cue.hasBeenReset, false, "Cue's hasBeenReset flag should be false.");
is(cue.displayState, null, "Cue's displayState should be null.");
cue.startTime = 0.5;
is(cue.hasBeenReset, true, "Cue's hasBeenReset flag should now be true.");
var cue = new VTTCue(0, 1, "Some text.");
is(cue.hasBeenReset, false, "Cue's hasBeenReset flag should be false.");
is(cue.displayState, null, "Cue's displayState should be null.");
cue.displayState = document.createElement("div");
is(cue.hasBeenReset, false, "Cue's hasBeenReset flag should now be false.");
SimpleTest.finish();
}
);
cue.startTime = 0.5;
is(cue.hasBeenReset, true, "Cue's hasBeenReset flag should now be true.");
cue.displayState = document.createElement("div");
is(cue.hasBeenReset, false, "Cue's hasBeenReset flag should now be false.");
SimpleTest.finish();
</script>

Просмотреть файл

@ -8,32 +8,37 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=882703
<title>Media test: TextTrackList change event</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
video = document.createElement("video");
isnot(video.textTracks, null, "Video should have a list of TextTracks.");
video.addTextTrack("subtitles", "", "");
track = video.textTracks[0];
video.textTracks.addEventListener("change", changed);
is(track.mode, "hidden", "New TextTrack's mode should be hidden.");
track.mode = "showing";
function changed(event) {
is(event.target, video.textTracks, "change event's target should be video.textTracks.");
ok(event instanceof window.Event, "change event should be a simple event.");
ok(!event.bubbles, "change event should not bubble.");
ok(event.isTrusted, "change event should be trusted.");
ok(!event.cancelable, "change event should not be cancelable.");
SimpleTest.finish();
}
</script>
</head>
<body>
<p id="display"></p>
<div id="content">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
video = document.createElement("video");
isnot(video.textTracks, null, "Video should have a list of TextTracks.");
video.addTextTrack("subtitles", "", "");
track = video.textTracks[0];
video.textTracks.addEventListener("change", changed);
is(track.mode, "hidden", "New TextTrack's mode should be hidden.");
track.mode = "showing";
function changed(event) {
is(event.target, video.textTracks, "change event's target should be video.textTracks.");
ok(event instanceof window.Event, "change event should be a simple event.");
ok(!event.bubbles, "change event should not bubble.");
ok(event.isTrusted, "change event should be trusted.");
ok(!event.cancelable, "change event should not be cancelable.");
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -16,14 +16,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=881976
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true]]},
function() {
var video = document.createElement("video");
is(video.textTracks.mediaElement, video, "Video's TextTrackList's MediaElement reference should be set to the video.");
SimpleTest.finish();
}
);
var video = document.createElement("video");
is(video.textTracks.mediaElement, video, "Video's TextTrackList's MediaElement reference should be set to the video.");
SimpleTest.finish();
</script>
</pre>
</body>

Просмотреть файл

@ -17,15 +17,16 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=917945
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true],
["media.webvtt.regions.enabled", true]]},
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.regions.enabled", true]]},
function() {
var video = document.createElement("video");
video.src = "seek.webm";
video.preload = "auto";
var trackElement = document.createElement("track");
trackElement.src = "region.vtt";
trackElement.kind = "subtitles";
document.getElementById("content").appendChild(video);
video.appendChild(trackElement);
video.addEventListener("loadedmetadata", function run_tests() {
@ -36,6 +37,7 @@ SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true],
return;
}
is(trackElement.readyState, 2, "Track::ReadyState should be set to LOADED.");
// Set mode to hidden so that the active cue lists are being updated.
trackElement.track.mode = "hidden";
@ -56,7 +58,6 @@ SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true],
});
}
);
</script>
</pre>
</body>

Просмотреть файл

@ -13,8 +13,7 @@
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true],
["media.webvtt.regions.enabled", true]]},
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.regions.enabled", true]]},
function() {
var video = document.createElement("video");
video.src = "seek.webm";

Просмотреть файл

@ -14,58 +14,55 @@
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true]]},
function() {
var video = document.createElement("video");
isnot(video.textTracks, undefined, "HTMLMediaElement::TextTrack() property should be available.")
ok(typeof video.addTextTrack == "function", "HTMLMediaElement::AddTextTrack() function should be available.")
var trackList = video.textTracks;
is(trackList.length, 0, "Length should be 0.");
var video = document.createElement("video");
isnot(video.textTracks, undefined, "HTMLMediaElement::TextTrack() property should be available.")
ok(typeof video.addTextTrack == "function", "HTMLMediaElement::AddTextTrack() function should be available.")
var evtTextTrack, numOfCalls = 0, tt;
trackList.onaddtrack = function(event) {
ok(event instanceof TrackEvent, "Fired event from onaddtrack should be a TrackEvent");
is(event.type, "addtrack", "Event type should be addtrack");
ok(event.isTrusted, "Event should be trusted!");
ok(!event.bubbles, "Event shouldn't bubble!");
ok(!event.cancelable, "Event shouldn't be cancelable!");
evtTextTrack = event.track;
tt = textTrack[numOfCalls].track || textTrack[numOfCalls];
var trackList = video.textTracks;
is(trackList.length, 0, "Length should be 0.");
ok(tt === evtTextTrack, "Text tracks should be the same");
is(evtTextTrack.label, label[numOfCalls], "Label should be set to "+ label[numOfCalls]);
is(evtTextTrack.language, language[numOfCalls], "Language should be " + language[numOfCalls]);
is(evtTextTrack.kind, kind[numOfCalls], "Kind should be " + kind[numOfCalls]);
var evtTextTrack, numOfCalls = 0, tt;
trackList.onaddtrack = function(event) {
ok(event instanceof TrackEvent, "Fired event from onaddtrack should be a TrackEvent");
is(event.type, "addtrack", "Event type should be addtrack");
ok(event.isTrusted, "Event should be trusted!");
ok(!event.bubbles, "Event shouldn't bubble!");
ok(!event.cancelable, "Event shouldn't be cancelable!");
if (++numOfCalls == 4) {
SimpleTest.finish();
}
};
evtTextTrack = event.track;
tt = textTrack[numOfCalls].track || textTrack[numOfCalls];
var label = ["Oasis", "Coldplay", "t.A.T.u", ""];
language = ["en-CA", "en-GB", "ru", ""];
kind = ["subtitles", "captions", "chapters", "subtitles"];
ok(tt === evtTextTrack, "Text tracks should be the same");
is(evtTextTrack.label, label[numOfCalls], "Label should be set to "+ label[numOfCalls]);
is(evtTextTrack.language, language[numOfCalls], "Language should be " + language[numOfCalls]);
is(evtTextTrack.kind, kind[numOfCalls], "Kind should be " + kind[numOfCalls]);
var textTrack = new Array(4);
for (var i = 0; i < 3; ++i) {
textTrack[i] = video.addTextTrack(kind[i], label[i], language[i]);
is(trackList.length, i + 1, "Length should be " + (i+1));
}
video.src = "seek.webm";
video.preload = "auto";
var trackElement = document.createElement("track");
trackElement.src = "basic.vtt";
textTrack[3] = trackElement;
document.getElementById("content").appendChild(video);
video.appendChild(trackElement);
//TODO: Tests for removetrack event to be added along with bug 882677
if (++numOfCalls == 4) {
SimpleTest.finish();
}
);
};
var label = ["Oasis", "Coldplay", "t.A.T.u", ""];
language = ["en-CA", "en-GB", "ru", ""];
kind = ["subtitles", "captions", "chapters", "subtitles"];
var textTrack = new Array(4);
for (var i = 0; i < 3; ++i) {
textTrack[i] = video.addTextTrack(kind[i], label[i], language[i]);
is(trackList.length, i + 1, "Length should be " + (i+1));
}
video.src = "seek.webm";
video.preload = "auto";
var trackElement = document.createElement("track");
trackElement.src = "basic.vtt";
textTrack[3] = trackElement;
document.getElementById("content").appendChild(video);
video.appendChild(trackElement);
//TODO: Tests for removetrack event to be added along with bug 882677
</script>
</pre>
</body>

Просмотреть файл

@ -14,30 +14,30 @@
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true]]},
function() {
var video = document.createElement("video");
video.src = "seek.webm";
video.preload = "auto";
var trackElement = document.createElement("track");
trackElement.src = "parser.vtt";
trackElement.kind = "subtitles";
trackElement.default = true;
document.getElementById("content").appendChild(video);
video.appendChild(trackElement);
video.addEventListener("loadedmetadata", function run_tests() {
// Re-que run_tests() at the end of the event loop until the track
// element has loaded its data.
if (trackElement.readyState == 1) {
setTimeout(run_tests, 0);
return;
}
is(trackElement.readyState, 2, "Track::ReadyState should be set to LOADED.");
is(trackElement.track.cues.length, 2, "Track should have two Cues.");
SimpleTest.finish();
});
});
var video = document.createElement("video");
video.src = "seek.webm";
video.preload = "auto";
var trackElement = document.createElement("track");
trackElement.src = "parser.vtt";
trackElement.kind = "subtitles";
trackElement.default = true;
document.getElementById("content").appendChild(video);
video.appendChild(trackElement);
video.addEventListener("loadedmetadata", function run_tests() {
// Re-que run_tests() at the end of the event loop until the track
// element has loaded its data.
if (trackElement.readyState == 1) {
setTimeout(run_tests, 0);
return;
}
is(trackElement.readyState, 2, "Track::ReadyState should be set to LOADED.");
is(trackElement.track.cues.length, 2, "Track should have two Cues.");
SimpleTest.finish();
});
</script>
</pre>
</body>

Просмотреть файл

@ -420,14 +420,14 @@ AudioNode::RemoveOutputParam(AudioParam* aParam)
bool
AudioNode::PassThrough() const
{
MOZ_ASSERT(NumberOfInputs() == 1 && NumberOfOutputs() == 1);
MOZ_ASSERT(NumberOfInputs() <= 1 && NumberOfOutputs() == 1);
return mPassThrough;
}
void
AudioNode::SetPassThrough(bool aPassThrough)
{
MOZ_ASSERT(NumberOfInputs() == 1 && NumberOfOutputs() == 1);
MOZ_ASSERT(NumberOfInputs() <= 1 && NumberOfOutputs() == 1);
mPassThrough = aPassThrough;
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
MOZ_ASSERT(ns, "How come we don't have a stream here?");

Просмотреть файл

@ -38,6 +38,7 @@ support-files =
[test_audioBufferSourceNodeNullBuffer.html]
[test_audioBufferSourceNodeOffset.html]
skip-if = (toolkit == 'gonk' && !debug) || (toolkit == 'android') #bug 906752
[test_audioBufferSourceNodePassThrough.html]
[test_AudioContext.html]
[test_audioDestinationNode.html]
[test_AudioListener.html]
@ -100,9 +101,11 @@ skip-if = (toolkit == 'gonk' && !debug) || (toolkit == 'android') #bug 906752
[test_maxChannelCount.html]
[test_mediaDecoding.html]
[test_mediaElementAudioSourceNode.html]
[test_mediaElementAudioSourceNodePassThrough.html]
[test_mediaStreamAudioDestinationNode.html]
[test_mediaStreamAudioSourceNode.html]
[test_mediaStreamAudioSourceNodeCrossOrigin.html]
[test_mediaStreamAudioSourceNodePassThrough.html]
[test_mediaStreamAudioSourceNodeResampling.html]
[test_mixingRules.html]
[test_mozaudiochannel.html]
@ -113,6 +116,7 @@ skip-if = (toolkit == 'gonk' && !debug)
[test_offlineDestinationChannelCountMore.html]
[test_oscillatorNode.html]
[test_oscillatorNode2.html]
[test_oscillatorNodePassThrough.html]
[test_oscillatorNodeStart.html]
[test_oscillatorTypeChange.html]
[test_pannerNode.html]

Просмотреть файл

@ -0,0 +1,45 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test AudioBufferSourceNode with passthrough</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="webaudio.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
var gTest = {
length: 2048,
numberOfChannels: 1,
createGraph: function(context) {
var buffer = context.createBuffer(1, 2048, context.sampleRate);
for (var i = 0; i < 2048; ++i) {
buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
}
var source = context.createBufferSource();
source.buffer = buffer;
var srcWrapped = SpecialPowers.wrap(source);
ok("passThrough" in srcWrapped, "AudioBufferSourceNode should support the passThrough API");
srcWrapped.passThrough = true;
source.start(0);
return source;
},
createExpectedBuffers: function(context) {
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
return [expectedBuffer];
},
};
runTest();
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,66 @@
<!DOCTYPE HTML>
<html>
<meta charset="utf-8">
<head>
<title>Test MediaElementAudioSourceNode with passthrough</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
function test() {
var audio = new Audio("small-shot.ogg");
var context = new AudioContext();
var node = context.createMediaElementSource(audio);
var sp = context.createScriptProcessor(2048, 1);
node.connect(sp);
var nonzeroSampleCount = 0;
var complete = false;
var iterationCount = 0;
var srcWrapped = SpecialPowers.wrap(node);
ok("passThrough" in srcWrapped, "MediaElementAudioSourceNode should support the passThrough API");
srcWrapped.passThrough = true;
// This test ensures we receive at least expectedSampleCount nonzero samples
function processSamples(e) {
if (complete) {
return;
}
if (iterationCount == 0) {
// Don't start playing the audio until the AudioContext stuff is connected
// and running.
audio.play();
}
++iterationCount;
var buf = e.inputBuffer.getChannelData(0);
var nonzeroSamplesThisBuffer = 0;
for (var i = 0; i < buf.length; ++i) {
if (buf[i] != 0) {
++nonzeroSamplesThisBuffer;
}
}
nonzeroSampleCount += nonzeroSamplesThisBuffer;
if (iterationCount == 10) {
is(nonzeroSampleCount, 0, "The input must be silence");
SimpleTest.finish();
complete = true;
}
}
audio.oncanplaythrough = function() {
sp.onaudioprocess = processSamples;
};
}
SpecialPowers.pushPrefEnv({"set": [["media.preload.default", 2], ["media.preload.auto", 3]]}, test);
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,55 @@
<!DOCTYPE HTML>
<html>
<meta charset="utf-8">
<head>
<title>Test MediaStreamAudioSourceNode passthrough</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="webaudio.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
function createBuffer(context, delay) {
var buffer = context.createBuffer(2, 2048, context.sampleRate);
for (var i = 0; i < 2048 - delay; ++i) {
buffer.getChannelData(0)[i + delay] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
buffer.getChannelData(1)[i + delay] = -buffer.getChannelData(0)[i + delay];
}
return buffer;
}
var gTest = {
length: 2048,
skipOfflineContextTests: true,
createGraph: function(context) {
var sourceGraph = new AudioContext();
var source = sourceGraph.createBufferSource();
source.buffer = createBuffer(context, 0);
var dest = sourceGraph.createMediaStreamDestination();
source.connect(dest);
source.start(0);
var mediaStreamSource = context.createMediaStreamSource(dest.stream);
// channelCount and channelCountMode should have no effect
mediaStreamSource.channelCount = 1;
mediaStreamSource.channelCountMode = "explicit";
var srcWrapped = SpecialPowers.wrap(mediaStreamSource);
ok("passThrough" in srcWrapped, "MediaStreamAudioSourceNode should support the passThrough API");
srcWrapped.passThrough = true;
return mediaStreamSource;
},
createExpectedBuffers: function(context) {
return context.createBuffer(2, 2048, context.sampleRate);
},
};
runTest();
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,43 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test Oscillator with passthrough</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="webaudio.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
var gTest = {
length: 2048,
numberOfChannels: 1,
createGraph: function(context) {
var buffer = context.createBuffer(1, 2048, context.sampleRate);
for (var i = 0; i < 2048; ++i) {
buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
}
var source = context.createOscillator();
var srcWrapped = SpecialPowers.wrap(source);
ok("passThrough" in srcWrapped, "OscillatorNode should support the passThrough API");
srcWrapped.passThrough = true;
source.start(0);
return source;
},
createExpectedBuffers: function(context) {
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
return [expectedBuffer];
},
};
runTest();
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -57,6 +57,10 @@ mozIApplication.prototype = {
return (perm === Ci.nsIPermissionManager.ALLOW_ACTION);
},
hasWidgetPage: function(aPageURL) {
return this.widgetPages.indexOf(aPageURL) != -1;
},
QueryInterface: function(aIID) {
if (aIID.equals(Ci.mozIApplication) ||
aIID.equals(Ci.nsISupports))
@ -100,6 +104,7 @@ function _setAppProperties(aObj, aApp) {
aObj.storeVersion = aApp.storeVersion || 0;
aObj.role = aApp.role || "";
aObj.redirects = aApp.redirects;
aObj.widgetPages = aApp.widgetPages || [];
aObj.kind = aApp.kind;
}
@ -678,6 +683,10 @@ ManifestHelper.prototype = {
return this._localeProp("package_path");
},
get widgetPages() {
return this._localeProp("widgetPages");
},
get size() {
return this._manifest["size"] || 0;
},

Просмотреть файл

@ -232,6 +232,11 @@ this.PermissionsTable = { geolocation: {
privileged: DENY_ACTION,
certified: ALLOW_ACTION
},
"embed-widgets": {
app: DENY_ACTION,
privileged: ALLOW_ACTION,
certified: ALLOW_ACTION
},
"storage": {
app: ALLOW_ACTION,
privileged: ALLOW_ACTION,

Просмотреть файл

@ -262,6 +262,10 @@ this.DOMApplicationRegistry = {
app.role = "";
}
if (app.widgetPages === undefined) {
app.widgetPages = [];
}
// At startup we can't be downloading, and the $TMP directory
// will be empty so we can't just apply a staged update.
app.downloading = false;
@ -321,6 +325,15 @@ this.DOMApplicationRegistry = {
return res.length > 0 ? res : null;
},
_saveWidgetsFullPath: function(aManifest, aDestApp) {
if (aManifest.widgetPages) {
aDestApp.widgetPages = aManifest.widgetPages.map(aManifest.resolveURL,
aManifest/* thisArg */);
} else {
aDestApp.widgetPages = [];
}
},
// Registers all the activities and system messages.
registerAppsHandlers: Task.async(function*(aRunUpdate) {
this.notifyAppsRegistryStart();
@ -345,6 +358,10 @@ this.DOMApplicationRegistry = {
let app = this.webapps[aResult.id];
app.csp = aResult.manifest.csp || "";
app.role = aResult.manifest.role || "";
let localeManifest = new ManifestHelper(aResult.manifest, app.origin, app.manifestURL);
this._saveWidgetsFullPath(localeManifest, app);
if (app.appStatus >= Ci.nsIPrincipal.APP_STATUS_PRIVILEGED) {
app.redirects = this.sanitizeRedirects(aResult.redirects);
}
@ -984,6 +1001,8 @@ this.DOMApplicationRegistry = {
app.name = manifest.name;
app.csp = manifest.csp || "";
app.role = localeManifest.role;
this._saveWidgetsFullPath(localeManifest, app);
if (app.appStatus >= Ci.nsIPrincipal.APP_STATUS_PRIVILEGED) {
app.redirects = this.sanitizeRedirects(manifest.redirects);
}
@ -2014,6 +2033,8 @@ this.DOMApplicationRegistry = {
aApp.downloadAvailable = true;
aApp.downloadSize = manifest.size;
aApp.updateManifest = aNewManifest;
this._saveWidgetsFullPath(manifest, aApp);
yield this._saveApps();
this.broadcastMessage("Webapps:UpdateState", {
@ -2072,6 +2093,7 @@ this.DOMApplicationRegistry = {
aApp.name = aNewManifest.name;
aApp.csp = manifest.csp || "";
aApp.role = manifest.role || "";
this._saveWidgetsFullPath(manifest, aApp);
aApp.updateTime = Date.now();
} else {
manifest =
@ -2471,6 +2493,7 @@ this.DOMApplicationRegistry = {
appObject.name = aManifest.name;
appObject.csp = aLocaleManifest.csp || "";
appObject.role = aLocaleManifest.role;
this._saveWidgetsFullPath(aLocaleManifest, appObject);
appObject.installerAppId = aData.appId;
appObject.installerIsBrowser = aData.isBrowser;
@ -2506,6 +2529,9 @@ this.DOMApplicationRegistry = {
app.csp = aManifest.csp || "";
let aLocaleManifest = new ManifestHelper(aManifest, app.origin, app.manifestURL);
this._saveWidgetsFullPath(aLocaleManifest, app);
app.appStatus = AppsUtils.getAppManifestStatus(aManifest);
app.removable = true;

Просмотреть файл

@ -1,6 +1,7 @@
var gBasePath = "tests/dom/apps/tests/";
var gAppTemplatePath = "tests/dom/apps/tests/file_app.template.html";
var gAppcacheTemplatePath = "tests/dom/apps/tests/file_cached_app.template.appcache";
var gWidgetTemplatePath = "tests/dom/apps/tests/file_widget_app.template.html";
var gDefaultIcon = "default_icon";
function makeResource(templatePath, version, apptype) {
@ -14,7 +15,6 @@ function makeResource(templatePath, version, apptype) {
if (templatePath == gAppTemplatePath && apptype == 'cached') {
res = res.replace('<html>', '<html manifest="file_app.sjs?apptype=cached&getappcache=true">');
}
return res;
}
@ -46,7 +46,7 @@ function handleRequest(request, response) {
// Get the app type.
var apptype = query.apptype;
if (apptype != 'hosted' && apptype != 'cached')
if (apptype != 'hosted' && apptype != 'cached' && apptype != 'widget' && apptype != 'invalidWidget')
throw "Invalid app type: " + apptype;
// Get the version from server state and handle the etag.
@ -83,7 +83,12 @@ function handleRequest(request, response) {
response.write(makeResource(gAppcacheTemplatePath, version, apptype));
return;
}
else if (apptype == 'widget' || apptype == 'invalidWidget')
{
response.setHeader("Content-Type", "text/html", false);
response.write(makeResource(gWidgetTemplatePath, version, apptype));
return;
}
// Generate the app.
response.setHeader("Content-Type", "text/html", false);
response.write(makeResource(gAppTemplatePath, version, apptype));

Просмотреть файл

@ -0,0 +1,11 @@
{
"name": "Really Rapid Release (invalid widget)",
"description": "Updated even faster than <a href='http://mozilla.org'>Firefox</a>, just to annoy slashdotters.",
"launch_path": "/tests/dom/apps/tests/file_app.sjs?apptype=invalidWidget",
"icons": {
"128": "ICONTOKEN"
},
"widgetPages": [
"/tests/dom/apps/tests/file_app.sjs?apptype=widget"
]
}

Просмотреть файл

@ -0,0 +1,8 @@
<html>
<head>
</head>
<body>
App Body. Version: VERSIONTOKEN
<br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
</body>
</html>

Просмотреть файл

@ -0,0 +1,11 @@
{
"name": "Really Rapid Release (widget)",
"description": "Updated even faster than <a href='http://mozilla.org'>Firefox</a>, just to annoy slashdotters.",
"launch_path": "/tests/dom/apps/tests/file_app.sjs?apptype=widget",
"icons": {
"128": "ICONTOKEN"
},
"widgetPages": [
"/tests/dom/apps/tests/file_app.sjs?apptype=widget"
]
}

Просмотреть файл

@ -7,9 +7,12 @@ support-files =
file_cached_app.template.appcache
file_cached_app.template.webapp
file_hosted_app.template.webapp
file_invalidWidget_app.template.webapp
file_packaged_app.sjs
file_packaged_app.template.html
file_packaged_app.template.webapp
file_widget_app.template.webapp
file_widget_app.template.html
signed_app.sjs
signed_app_template.webapp
signed/*
@ -28,3 +31,4 @@ skip-if = buildapp == "b2g" || toolkit == "android" # see bug 989806
[test_receipt_operations.html]
[test_signed_pkg_install.html]
[test_uninstall_errors.html]
[test_widget.html]

Просмотреть файл

@ -0,0 +1,231 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test for DataStore - basic operation on a readonly db</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<div id="container"></div>
<script type="application/javascript;version=1.7">
var gWidgetManifestURL = 'http://test/tests/dom/apps/tests/file_app.sjs?apptype=widget&getmanifest=true';
var gInvalidWidgetManifestURL = 'http://test/tests/dom/apps/tests/file_app.sjs?apptype=invalidWidget&getmanifest=true';
var gApp;
function onError() {
ok(false, "Error callback invoked");
finish();
}
function installApp(path) {
var request = navigator.mozApps.install(path);
request.onerror = onError;
request.onsuccess = function() {
gApp = request.result;
runTest();
}
}
function uninstallApp() {
// Uninstall the app.
var request = navigator.mozApps.mgmt.uninstall(gApp);
request.onerror = onError;
request.onsuccess = function() {
// All done.
info("All done");
runTest();
}
}
function testApp(isValidWidget) {
info("Test widget feature. IsValidWidget: " + isValidWidget);
var ifr = document.createElement('iframe');
ifr.setAttribute('mozbrowser', 'true');
ifr.setAttribute('mozwidget', gApp.manifestURL);
ifr.setAttribute('src', gApp.origin+gApp.manifest.launch_path);
var domParent = document.getElementById('container');
domParent.appendChild(ifr);
var mm = SpecialPowers.getBrowserFrameMessageManager(ifr);
mm.addMessageListener('OK', function(msg) {
ok(isValidWidget, "Message from widget: " + SpecialPowers.wrap(msg).json);
});
mm.addMessageListener('KO', function(msg) {
ok(!isValidWidget, "Message from widget: " + SpecialPowers.wrap(msg).json);
});
mm.addMessageListener('DONE', function(msg) {
ok(true, "Message from widget complete: "+SpecialPowers.wrap(msg).json);
domParent.removeChild(ifr);
runTest();
});
ifr.addEventListener('mozbrowserloadend', function() {
ok(true, "receive mozbrowserloadend");
// Test limited browser API feature only for valid widget case
if (isValidWidget) {
testLimitedBrowserAPI(ifr);
}
SimpleTest.executeSoon(()=>loadFrameScript(mm));
}, false);
// Test limited browser API feature only for valid widget case
if (!isValidWidget) {
return;
}
[
'mozbrowsertitlechange',
'mozbrowseropenwindow',
'mozbrowserscroll',
'mozbrowserasyncscroll'
].forEach( function(topic) {
ifr.addEventListener(topic, function() {
ok(false, topic + " should be hidden");
}, false);
});
}
function testLimitedBrowserAPI(ifr) {
var securitySensitiveCalls = [
'sendMouseEvent',
'sendTouchEvent',
'goBack',
'goForward',
'reload',
'stop',
'download',
'purgeHistory',
'getScreenshot',
'zoom',
'getCanGoBack',
'getCanGoForward'
];
securitySensitiveCalls.forEach( function(call) {
is(typeof ifr[call], "undefined", call + " should be hidden for widget");
});
}
function loadFrameScript(mm) {
var script = 'data:,\
function ok(p, msg) { \
if (p) { \
sendAsyncMessage("OK", msg); \
} else { \
sendAsyncMessage("KO", msg); \
} \
} \
\
function is(a, b, msg) { \
if (a == b) { \
sendAsyncMessage("OK", a + " == " + b + " - " + msg); \
} else { \
sendAsyncMessage("KO", a + " != " + b + " - " + msg); \
} \
} \
\
function finish() { \
sendAsyncMessage("DONE",""); \
} \
\
function onError() { \
ok(false, "Error callback invoked"); \
finish(); \
} \
\
function checkWidget(widget) { \
/*For invalid widget case, ignore the following check*/\
if (widget) { \
var widgetName = "Really Rapid Release (APPTYPETOKEN)"; \
is(widget.origin, "http://test", "Widget origin should be correct"); \
is(widget.installOrigin, "http://mochi.test:8888", "Install origin should be correct"); \
} \
finish(); \
} \
\
var request = content.window.navigator.mozApps.getSelf(); \
request.onsuccess = function() { \
var widget = request.result; \
ok(widget,"Should be a widget"); \
checkWidget(widget); \
}; \
request.onerror = onError; \
content.window.open("about:blank"); /*test mozbrowseropenwindow*/ \
content.window.scrollTo(4000, 4000); /*test mozbrowser(async)scroll*/ \
';
mm.loadFrameScript(script, /* allowDelayedLoad = */ false);
}
var tests = [
// Permissions
function() {
SpecialPowers.pushPermissions(
[{ "type": "browser", "allow": 1, "context": document },
{ "type": "embed-widgets", "allow": 1, "context": document },
{ "type": "webapps-manage", "allow": 1, "context": document }], runTest);
},
// Preferences
function() {
SpecialPowers.pushPrefEnv({"set": [["dom.mozBrowserFramesEnabled", true],
["dom.enable_widgets", true]]}, runTest);
},
function() {
SpecialPowers.setAllAppsLaunchable(true);
runTest();
},
// No confirmation needed when an app is installed
function() {
SpecialPowers.autoConfirmAppInstall(runTest);
},
// Installing the app
()=>installApp(gWidgetManifestURL),
// Run tests in app
()=>testApp(true),
// Uninstall the app
uninstallApp,
// Installing the app for invalid widget case
()=>installApp(gInvalidWidgetManifestURL),
// Run tests in app for invalid widget case
()=>testApp(false),
// Uninstall the app
uninstallApp
];
function runTest() {
if (!tests.length) {
finish();
return;
}
var test = tests.shift();
test();
}
function finish() {
SimpleTest.finish();
}
if (SpecialPowers.isMainProcess()) {
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
}
SimpleTest.waitForExplicitFinish();
runTest();
</script>
</body>
</html>

Просмотреть файл

@ -428,7 +428,6 @@ static const nsConstructorFuncMapData kConstructorFuncMap[] =
#undef NS_DEFINE_CONSTRUCTOR_FUNC_DATA
nsIXPConnect *nsDOMClassInfo::sXPConnect = nullptr;
nsIScriptSecurityManager *nsDOMClassInfo::sSecMan = nullptr;
bool nsDOMClassInfo::sIsInitialized = false;
@ -779,19 +778,11 @@ nsDOMClassInfo::Init()
nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager();
NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
nsresult rv = CallGetService(nsIXPConnect::GetCID(), &sXPConnect);
NS_ENSURE_SUCCESS(rv, rv);
NS_ADDREF(sXPConnect = nsContentUtils::XPConnect());
nsCOMPtr<nsIXPCFunctionThisTranslator> elt = new nsEventListenerThisTranslator();
sXPConnect->SetFunctionThisTranslator(NS_GET_IID(nsIDOMEventListener), elt);
nsCOMPtr<nsIScriptSecurityManager> sm =
do_GetService("@mozilla.org/scriptsecuritymanager;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
sSecMan = sm;
NS_ADDREF(sSecMan);
AutoSafeJSContext cx;
DOM_CLASSINFO_MAP_BEGIN(Window, nsIDOMWindow)
@ -1042,9 +1033,6 @@ nsDOMClassInfo::Init()
RegisterExternalClasses();
// Register new DOM bindings
mozilla::dom::Register(nameSpaceManager);
sIsInitialized = true;
return NS_OK;
@ -1523,11 +1511,8 @@ NS_GetDOMClassInfoInstance(nsDOMClassInfoID aID)
return nullptr;
}
if (!nsDOMClassInfo::sIsInitialized) {
nsresult rv = nsDOMClassInfo::Init();
NS_ENSURE_SUCCESS(rv, nullptr);
}
nsresult rv = RegisterDOMNames();
NS_ENSURE_SUCCESS(rv, nullptr);
if (!sClassInfoData[aID].mCachedClassInfo) {
nsDOMClassInfoData& data = sClassInfoData[aID];
@ -1588,7 +1573,6 @@ nsDOMClassInfo::ShutDown()
sWrappedJSObject_id = JSID_VOID;
NS_IF_RELEASE(sXPConnect);
NS_IF_RELEASE(sSecMan);
sIsInitialized = false;
}

Просмотреть файл

@ -96,6 +96,7 @@ public:
static nsIClassInfo* GetClassInfoInstance(nsDOMClassInfoData* aData);
static nsresult Init();
static void ShutDown();
static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
@ -127,10 +128,6 @@ public:
{
return sXPConnect;
}
static nsIScriptSecurityManager *ScriptSecurityManager()
{
return sSecMan;
}
protected:
friend nsIClassInfo* NS_GetDOMClassInfoInstance(nsDOMClassInfoID aID);
@ -146,14 +143,12 @@ protected:
return mData->mInterfacesBitmap;
}
static nsresult Init();
static nsresult RegisterClassProtos(int32_t aDOMClassInfoID);
static nsresult RegisterExternalClasses();
nsresult ResolveConstructor(JSContext *cx, JSObject *obj,
JSObject **objp);
static nsIXPConnect *sXPConnect;
static nsIScriptSecurityManager *sSecMan;
// nsIXPCScriptable code
static nsresult DefineStaticJSVals(JSContext *cx);

Просмотреть файл

@ -43,6 +43,7 @@
#include "mozilla/dom/HTMLAppletElementBinding.h"
#include "mozilla/dom/Promise.h"
#include "WorkerPrivate.h"
#include "nsDOMClassInfo.h"
namespace mozilla {
namespace dom {
@ -2561,11 +2562,44 @@ CreateGlobalOptions<nsGlobalWindow>::TraceGlobal(JSTracer* aTrc, JSObject* aObj)
xpc::TraceXPCGlobal(aTrc, aObj);
}
static bool sRegisteredDOMNames = false;
nsresult
RegisterDOMNames()
{
if (sRegisteredDOMNames) {
return NS_OK;
}
nsresult rv = nsDOMClassInfo::Init();
if (NS_FAILED(rv)) {
NS_ERROR("Could not initialize nsDOMClassInfo");
return rv;
}
// Register new DOM bindings
nsScriptNameSpaceManager* nameSpaceManager = GetNameSpaceManager();
if (!nameSpaceManager) {
NS_ERROR("Could not initialize nsScriptNameSpaceManager");
return NS_ERROR_FAILURE;
}
mozilla::dom::Register(nameSpaceManager);
sRegisteredDOMNames = true;
return NS_OK;
}
/* static */
bool
CreateGlobalOptions<nsGlobalWindow>::PostCreateGlobal(JSContext* aCx,
JS::Handle<JSObject*> aGlobal)
{
nsresult rv = RegisterDOMNames();
if (NS_FAILED(rv)) {
return Throw(aCx, rv);
}
// Invoking the XPCWrappedNativeScope constructor automatically hooks it
// up to the compartment of aGlobal.
(void) new XPCWrappedNativeScope(aCx, aGlobal);

Просмотреть файл

@ -2808,6 +2808,9 @@ struct CreateGlobalOptions<nsGlobalWindow>
static bool PostCreateGlobal(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
};
nsresult
RegisterDOMNames();
template <class T, ProtoGetter GetProto>
bool
CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache,

Просмотреть файл

@ -164,6 +164,13 @@ BrowserElementParent::DispatchOpenWindowEvent(Element* aOpenerFrameElement,
return BrowserElementParent::OPEN_WINDOW_IGNORED;
}
// Do not dispatch a mozbrowseropenwindow event of a widget to its embedder
nsCOMPtr<nsIMozBrowserFrame> browserFrame =
do_QueryInterface(aOpenerFrameElement);
if (browserFrame && browserFrame->GetReallyIsWidget()) {
return BrowserElementParent::OPEN_WINDOW_CANCELLED;
}
nsEventStatus status;
bool dispatchSucceeded =
DispatchCustomDOMEvent(aOpenerFrameElement,
@ -349,6 +356,14 @@ BrowserElementParent::DispatchAsyncScrollEvent(TabParent* aTabParent,
const CSSRect& aContentRect,
const CSSSize& aContentSize)
{
// Do not dispatch a mozbrowserasyncscroll event of a widget to its embedder
nsCOMPtr<Element> frameElement = aTabParent->GetOwnerElement();
NS_ENSURE_TRUE(frameElement, false);
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(frameElement);
if (browserFrame && browserFrame->GetReallyIsWidget()) {
return true;
}
nsRefPtr<DispatchAsyncScrollEventRunnable> runnable =
new DispatchAsyncScrollEventRunnable(aTabParent, aContentRect,
aContentSize);

Просмотреть файл

@ -116,24 +116,30 @@ function BrowserElementParent(frameLoader, hasRemoteFrame, isPendingFrame) {
// Define methods on the frame element.
defineNoReturnMethod('setVisible', this._setVisible);
defineDOMRequestMethod('getVisible', 'get-visible');
defineNoReturnMethod('sendMouseEvent', this._sendMouseEvent);
// 0 = disabled, 1 = enabled, 2 - auto detect
if (getIntPref(TOUCH_EVENTS_ENABLED_PREF, 0) != 0) {
defineNoReturnMethod('sendTouchEvent', this._sendTouchEvent);
// Not expose security sensitive browser API for widgets
if (!this._frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerIsWidget) {
defineNoReturnMethod('sendMouseEvent', this._sendMouseEvent);
// 0 = disabled, 1 = enabled, 2 - auto detect
if (getIntPref(TOUCH_EVENTS_ENABLED_PREF, 0) != 0) {
defineNoReturnMethod('sendTouchEvent', this._sendTouchEvent);
}
defineNoReturnMethod('goBack', this._goBack);
defineNoReturnMethod('goForward', this._goForward);
defineNoReturnMethod('reload', this._reload);
defineNoReturnMethod('stop', this._stop);
defineMethod('download', this._download);
defineDOMRequestMethod('purgeHistory', 'purge-history');
defineMethod('getScreenshot', this._getScreenshot);
defineNoReturnMethod('zoom', this._zoom);
defineDOMRequestMethod('getCanGoBack', 'get-can-go-back');
defineDOMRequestMethod('getCanGoForward', 'get-can-go-forward');
}
defineNoReturnMethod('goBack', this._goBack);
defineNoReturnMethod('goForward', this._goForward);
defineNoReturnMethod('reload', this._reload);
defineNoReturnMethod('stop', this._stop);
defineNoReturnMethod('zoom', this._zoom);
defineMethod('download', this._download);
defineDOMRequestMethod('purgeHistory', 'purge-history');
defineMethod('getScreenshot', this._getScreenshot);
defineMethod('addNextPaintListener', this._addNextPaintListener);
defineMethod('removeNextPaintListener', this._removeNextPaintListener);
defineDOMRequestMethod('getCanGoBack', 'get-can-go-back');
defineDOMRequestMethod('getCanGoForward', 'get-can-go-forward');
let principal = this._frameElement.ownerDocument.nodePrincipal;
let perm = Services.perms
@ -217,6 +223,9 @@ BrowserElementParent.prototype = {
_setupMessageListener: function() {
this._mm = this._frameLoader.messageManager;
let self = this;
let isWidget = this._frameLoader
.QueryInterface(Ci.nsIFrameLoader)
.ownerIsWidget;
// Messages we receive are handed to functions which take a (data) argument,
// where |data| is the message manager's data object.
@ -224,26 +233,14 @@ BrowserElementParent.prototype = {
// on data.msg_name
let mmCalls = {
"hello": this._recvHello,
"contextmenu": this._fireCtxMenuEvent,
"locationchange": this._fireEventFromMsg,
"loadstart": this._fireProfiledEventFromMsg,
"loadend": this._fireProfiledEventFromMsg,
"titlechange": this._fireProfiledEventFromMsg,
"iconchange": this._fireEventFromMsg,
"manifestchange": this._fireEventFromMsg,
"metachange": this._fireEventFromMsg,
"close": this._fireEventFromMsg,
"resize": this._fireEventFromMsg,
"activitydone": this._fireEventFromMsg,
"opensearch": this._fireEventFromMsg,
"securitychange": this._fireEventFromMsg,
"error": this._fireEventFromMsg,
"scroll": this._fireEventFromMsg,
"firstpaint": this._fireProfiledEventFromMsg,
"documentfirstpaint": this._fireProfiledEventFromMsg,
"nextpaint": this._recvNextPaint,
"keyevent": this._fireKeyEvent,
"showmodalprompt": this._handleShowModalPrompt,
"got-purge-history": this._gotDOMRequestResult,
"got-screenshot": this._gotDOMRequestResult,
"got-can-go-back": this._gotDOMRequestResult,
@ -257,9 +254,31 @@ BrowserElementParent.prototype = {
"selectionchange": this._handleSelectionChange
};
let mmSecuritySensitiveCalls = {
"showmodalprompt": this._handleShowModalPrompt,
"contextmenu": this._fireCtxMenuEvent,
"securitychange": this._fireEventFromMsg,
"locationchange": this._fireEventFromMsg,
"iconchange": this._fireEventFromMsg,
"titlechange": this._fireProfiledEventFromMsg,
"opensearch": this._fireEventFromMsg,
"manifestchange": this._fireEventFromMsg,
"metachange": this._fireEventFromMsg,
"resize": this._fireEventFromMsg,
"activitydone": this._fireEventFromMsg,
"scroll": this._fireEventFromMsg
};
this._mm.addMessageListener('browser-element-api:call', function(aMsg) {
if (self._isAlive() && (aMsg.data.msg_name in mmCalls)) {
if (!self._isAlive()) {
return;
}
if (aMsg.data.msg_name in mmCalls) {
return mmCalls[aMsg.data.msg_name].apply(self, arguments);
} else if (!isWidget && aMsg.data.msg_name in mmSecuritySensitiveCalls) {
return mmSecuritySensitiveCalls[aMsg.data.msg_name]
.apply(self, arguments);
}
});
},
@ -294,26 +313,29 @@ BrowserElementParent.prototype = {
}
};
if (authDetail.isOnlyPassword) {
// We don't handle password-only prompts, so just cancel it.
// 1. We don't handle password-only prompts.
// 2. We don't handle for widget case because of security concern.
if (authDetail.isOnlyPassword ||
this._frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerIsWidget) {
cancelCallback();
return;
} else { /* username and password */
let detail = {
host: authDetail.host,
realm: authDetail.realm
};
evt = this._createEvent('usernameandpasswordrequired', detail,
/* cancelable */ true);
Cu.exportFunction(function(username, password) {
if (callbackCalled)
return;
callbackCalled = true;
callback(true, username, password);
}, evt.detail, { defineAs: 'authenticate' });
}
/* username and password */
let detail = {
host: authDetail.host,
realm: authDetail.realm
};
evt = this._createEvent('usernameandpasswordrequired', detail,
/* cancelable */ true);
Cu.exportFunction(function(username, password) {
if (callbackCalled)
return;
callbackCalled = true;
callback(true, username, password);
}, evt.detail, { defineAs: 'authenticate' });
Cu.exportFunction(cancelCallback, evt.detail, { defineAs: 'cancel' });
this._frameElement.dispatchEvent(evt);

Просмотреть файл

@ -84,7 +84,6 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver)
IMEContentObserver::IMEContentObserver()
: mESM(nullptr)
, mPreCharacterDataChangeLength(-1)
, mIsEditorInTransaction(false)
, mIsSelectionChangeEventPending(false)
, mSelectionChangeCausedOnlyByComposition(false)
, mIsPositionChangeEventPending(false)
@ -911,7 +910,6 @@ IMEContentObserver::AttributeChanged(nsIDocument* aDocument,
NS_IMETHODIMP
IMEContentObserver::EditAction()
{
mIsEditorInTransaction = false;
mEndOfAddedTextCache.Clear();
mStartOfRemovingTextRangeCache.Clear();
FlushMergeableNotifications();
@ -921,7 +919,6 @@ IMEContentObserver::EditAction()
NS_IMETHODIMP
IMEContentObserver::BeforeEditAction()
{
mIsEditorInTransaction = true;
mEndOfAddedTextCache.Clear();
mStartOfRemovingTextRangeCache.Clear();
return NS_OK;
@ -930,7 +927,6 @@ IMEContentObserver::BeforeEditAction()
NS_IMETHODIMP
IMEContentObserver::CancelEditAction()
{
mIsEditorInTransaction = false;
mEndOfAddedTextCache.Clear();
mStartOfRemovingTextRangeCache.Clear();
FlushMergeableNotifications();
@ -988,10 +984,14 @@ private:
void
IMEContentObserver::FlushMergeableNotifications()
{
// If we're in handling an edit action, this method will be called later.
// If this is already detached from the widget, this doesn't need to notify
// anything.
if (mIsEditorInTransaction || !mWidget) {
if (!mWidget) {
return;
}
// If we're in handling an edit action, this method will be called later.
if (mEditor && mEditor->GetIsInEditAction()) {
return;
}

Просмотреть файл

@ -217,7 +217,6 @@ private:
uint32_t mPreAttrChangeLength;
int64_t mPreCharacterDataChangeLength;
bool mIsEditorInTransaction;
bool mIsSelectionChangeEventPending;
bool mSelectionChangeCausedOnlyByComposition;
bool mIsPositionChangeEventPending;

Просмотреть файл

@ -11,12 +11,18 @@
* We expose Gecko-internal helpers related to "web apps" through this
* sub-interface.
*/
[scriptable, uuid(7bd62430-c374-49eb-be1b-ce821a180360)]
[scriptable, uuid(1d856b11-ac29-47d3-bd52-a86e3d45fcf4)]
interface mozIApplication: nsISupports
{
/* Return true if this app has |permission|. */
boolean hasPermission(in string permission);
/**
* Return true if this app can be a widget and
* its |widgetPages| contains |page|
*/
boolean hasWidgetPage(in DOMString pageURL);
/* Application status as defined in nsIPrincipal. */
readonly attribute unsigned short appStatus;

Просмотреть файл

@ -9,7 +9,7 @@
interface nsITabParent;
[scriptable, builtinclass, uuid(929AED00-3E15-49B7-8CA2-75003715B7E7)]
[scriptable, builtinclass, uuid(0c0a862c-1a47-43c0-ae9e-d51835e3e1a6)]
interface nsIMozBrowserFrame : nsIDOMMozBrowserFrame
{
/**
@ -25,11 +25,24 @@ interface nsIMozBrowserFrame : nsIDOMMozBrowserFrame
* Gets whether this frame really is an app frame.
*
* In order to really be an app frame, this frame must really be a browser
* frame (this requirement will go away eventually), and the frame's mozapp
* attribute must point to the manifest of a valid app.
* frame (this requirement will go away eventually), and must satisfy one
* and only one of the following conditions:
* 1. the frame's mozapp attribute must point to the manifest of a valid app
* 2. the frame's mozwidget attribute must point to the manifest of a valid
* app, and the src should be in the |widgetPages| specified by the manifest.
*/
[infallible] readonly attribute boolean reallyIsApp;
/**
* Gets whether this frame really is a widget frame.
*
* In order to really be a frame, this frame must really be a browser
* frame (this requirement will go away eventually), the frame's mozwidget
* attribute must point to the manifest of a valid app, and the src should
* be in the |widgetPages| specified by the manifest.
*/
[infallible] readonly attribute boolean reallyIsWidget;
/**
* This corresponds to the expecting-system-message attribute, which tells us
* whether we should expect that this frame will receive a system message once
@ -41,7 +54,8 @@ interface nsIMozBrowserFrame : nsIDOMMozBrowserFrame
[infallible] readonly attribute boolean isExpectingSystemMessage;
/**
* Gets this frame's app manifest URL, if the frame really is an app frame.
* Gets this frame's app manifest URL or widget manifest URL, if the frame
* really is an app frame.
* Otherwise, returns the empty string.
*
* This method is guaranteed not to fail.

Просмотреть файл

@ -88,6 +88,7 @@ var ecmaGlobals =
"Uint8ClampedArray",
"URIError",
"WeakMap",
"WeakSet",
];
// IMPORTANT: Do not change the list above without review from
// a JavaScript Engine peer!

Просмотреть файл

@ -28,3 +28,6 @@ interface AudioBufferSourceNode : AudioNode {
attribute EventHandler onended;
};
// Mozilla extensions
AudioBufferSourceNode implements AudioNodePassThrough;

Просмотреть файл

@ -14,3 +14,6 @@ interface MediaElementAudioSourceNode : AudioNode {
};
// Mozilla extensions
MediaElementAudioSourceNode implements AudioNodePassThrough;

Просмотреть файл

@ -14,3 +14,6 @@ interface MediaStreamAudioSourceNode : AudioNode {
};
// Mozilla extensions
MediaStreamAudioSourceNode implements AudioNodePassThrough;

Просмотреть файл

@ -36,3 +36,6 @@ interface OscillatorNode : AudioNode {
};
// Mozilla extensions
OscillatorNode implements AudioNodePassThrough;

Просмотреть файл

@ -62,6 +62,7 @@ var ecmaGlobals =
"Uint8ClampedArray",
"URIError",
"WeakMap",
"WeakSet",
];
// IMPORTANT: Do not change the list above without review from
// a JavaScript Engine peer!

Просмотреть файл

@ -147,6 +147,7 @@ nsEditor::nsEditor()
, mDidPreDestroy(false)
, mDidPostCreate(false)
, mDispatchInputEvent(true)
, mIsInEditAction(false)
{
}
@ -1832,6 +1833,7 @@ nsEditor::NotifyEditorObservers(NotificationForEditorObservers aNotification)
{
switch (aNotification) {
case eNotifyEditorObserversOfEnd:
mIsInEditAction = false;
for (int32_t i = 0; i < mEditorObservers.Count(); i++) {
mEditorObservers[i]->EditAction();
}
@ -1843,11 +1845,13 @@ nsEditor::NotifyEditorObservers(NotificationForEditorObservers aNotification)
FireInputEvent();
break;
case eNotifyEditorObserversOfBefore:
mIsInEditAction = true;
for (int32_t i = 0; i < mEditorObservers.Count(); i++) {
mEditorObservers[i]->BeforeEditAction();
}
break;
case eNotifyEditorObserversOfCancel:
mIsInEditAction = false;
for (int32_t i = 0; i < mEditorObservers.Count(); i++) {
mEditorObservers[i]->CancelEditAction();
}
@ -5273,3 +5277,11 @@ nsEditor::SetSuppressDispatchingInputEvent(bool aSuppress)
mDispatchInputEvent = !aSuppress;
return NS_OK;
}
NS_IMETHODIMP
nsEditor::GetIsInEditAction(bool* aIsInEditAction)
{
MOZ_ASSERT(aIsInEditAction, "aIsInEditAction must not be null");
*aIsInEditAction = mIsInEditAction;
return NS_OK;
}

Просмотреть файл

@ -895,6 +895,7 @@ protected:
bool mDidPreDestroy; // whether PreDestroy has been called
bool mDidPostCreate; // whether PostCreate has been called
bool mDispatchInputEvent;
bool mIsInEditAction; // true while the instance is handling an edit action
friend bool NSCanUnload(nsISupports* serviceMgr);
friend class nsAutoTxnsConserveSelection;

Просмотреть файл

@ -18,6 +18,7 @@ skip-if = buildapp == 'mulet'
[test_bug646194.xul]
[test_bug780908.xul]
[test_bug830600.html]
[test_bug1053048.html]
[test_composition_event_created_in_chrome.html]
[test_contenteditable_text_input_handling.html]
[test_dragdrop.html]

Просмотреть файл

@ -0,0 +1,73 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1053048
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1053048</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
<script type="application/javascript">
/** Test for Bug 1053048 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(runTests);
const nsISelectionPrivate = Components.interfaces.nsISelectionPrivate;
const nsISelectionListener = Components.interfaces.nsISelectionListener;
const nsIDOMNSEditableElement = Components.interfaces.nsIDOMNSEditableElement;
function runTests()
{
var textarea = document.getElementById("textarea");
textarea.focus();
var editor = textarea.QueryInterface(nsIDOMNSEditableElement).editor;
var selectionPrivate = editor.selection.QueryInterface(nsISelectionPrivate);
var selectionListener = {
count: 0,
notifySelectionChanged: function (aDocument, aSelection, aReason)
{
ok(true, "selectionStart: " + textarea.selectionStart);
ok(true, "selectionEnd: " + textarea.selectionEnd);
this.count++;
}
};
// Move caret to the end of the textarea
synthesizeMouse(textarea, 290, 10, {});
is(textarea.selectionStart, 3, "selectionStart should be 3 (after \"foo\")");
is(textarea.selectionEnd, 3, "selectionEnd should be 3 (after \"foo\")");
selectionPrivate.addSelectionListener(selectionListener);
sendKey("RETURN");
is(selectionListener.count, 1, "nsISelectionListener.notifySelectionChanged() should be called");
is(textarea.selectionStart, 4, "selectionStart should be 4");
is(textarea.selectionEnd, 4, "selectionEnd should be 4");
is(textarea.value, "foo\n", "The line break should be appended");
selectionPrivate.removeSelectionListener(selectionListener);
SimpleTest.finish();
}
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1053048">Mozilla Bug 1053048</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<textarea id="textarea"
style="height: 100px; width: 300px; -moz-appearance: none"
spellcheck="false"
onkeydown="this.style.display='block'; this.style.height='200px';">foo</textarea>
<pre id="test">
</pre>
</body>
</html>

Просмотреть файл

@ -21,7 +21,7 @@ interface nsIEditActionListener;
interface nsIInlineSpellChecker;
interface nsITransferable;
[scriptable, uuid(65523eab-db1f-44aa-893e-dfe57ad306f0)]
[builtinclass, scriptable, uuid(c3b61bc9-ccdd-4bcd-acd8-1b8dcbe6a247)]
interface nsIEditor : nsISupports
{
@ -549,4 +549,11 @@ interface nsIEditor : nsISupports
/* Set true if you want to suppress dispatching input event. */
attribute boolean suppressDispatchingInputEvent;
/**
* True if an edit action is being handled (in other words, between calls of
* nsIEditorObserver::BeforeEditAction() and nsIEditorObserver::EditAction()
* or nsIEditorObserver::CancelEditAction(). Otherwise, false.
*/
[infallible, noscript] readonly attribute boolean isInEditAction;
};

Просмотреть файл

@ -22,7 +22,7 @@ class Element;
[ptr] native Element (mozilla::dom::Element);
[scriptable, uuid(833f30de-94c7-4630-a852-2300ef329d7b)]
[builtinclass, scriptable, uuid(9470bee7-cad3-4382-8fb4-6bdda9f0273a)]
interface nsIHTMLEditor : nsISupports
{

Просмотреть файл

@ -5,7 +5,7 @@
#include "nsISupports.idl"
[scriptable, uuid(07b6d070-ccea-4a00-84b4-f4b94dd9eb52)]
[builtinclass, scriptable, uuid(da8f244b-6ffc-4be1-8b1a-667abfe1d304)]
interface nsIPlaintextEditor : nsISupports
{

Просмотреть файл

@ -1321,7 +1321,8 @@ gfxDWriteFontList::GetStandardFamilyName(const nsAString& aFontName,
return false;
}
gfxFontFamily* gfxDWriteFontList::FindFamily(const nsAString& aFamily)
gfxFontFamily*
gfxDWriteFontList::FindFamily(const nsAString& aFamily, bool aUseSystemFonts)
{
if (!mInitialized) {
mInitialized = true;

Просмотреть файл

@ -360,7 +360,8 @@ public:
IDWriteGdiInterop *GetGDIInterop() { return mGDIInterop; }
bool UseGDIFontTableAccess() { return mGDIFontTableAccess; }
virtual gfxFontFamily* FindFamily(const nsAString& aFamily);
virtual gfxFontFamily* FindFamily(const nsAString& aFamily,
bool aUseSystemFonts = false);
virtual void GetFontFamilyList(nsTArray<nsRefPtr<gfxFontFamily> >& aFamilyArray);

Просмотреть файл

@ -5033,7 +5033,7 @@ gfxFontGroup::FindPlatformFont(const nsAString& aName,
// Not known in the user font set ==> check system fonts
if (!family) {
gfxPlatformFontList *fontList = gfxPlatformFontList::PlatformFontList();
family = fontList->FindFamily(aName);
family = fontList->FindFamily(aName, mStyle.systemFont);
if (family) {
fe = family->FindFontForStyle(mStyle, needsBold);
}

Просмотреть файл

@ -839,7 +839,7 @@ gfxGDIFontList::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
}
gfxFontFamily*
gfxGDIFontList::FindFamily(const nsAString& aFamily)
gfxGDIFontList::FindFamily(const nsAString& aFamily, bool aUseSystemFonts)
{
nsAutoString keyName(aFamily);
BuildKeyNameFromFontName(keyName);

Просмотреть файл

@ -307,7 +307,8 @@ public:
virtual gfxFontFamily* GetDefaultFont(const gfxFontStyle* aStyle);
virtual gfxFontFamily* FindFamily(const nsAString& aFamily);
virtual gfxFontFamily* FindFamily(const nsAString& aFamily,
bool aUseSystemFonts = false);
virtual gfxFontEntry* LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
const nsAString& aFontName);

Просмотреть файл

@ -647,6 +647,7 @@ gfxMacPlatformFontList::InitFontList()
// reset font lists
gfxPlatformFontList::InitFontList();
mSystemFontFamilies.Clear();
// iterate over available families
@ -662,12 +663,16 @@ gfxMacPlatformFontList::InitFontList()
// CTFontManager includes weird internal family names and
// LastResort, skip over those
if (!family ||
::CFStringHasPrefix(family, CFSTR(".")) ||
CFStringCompare(family, CFSTR("LastResort"),
kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
continue;
}
bool hiddenSystemFont = false;
if (::CFStringHasPrefix(family, CFSTR("."))) {
hiddenSystemFont = true;
}
nsAutoTArray<UniChar, 1024> buffer;
CFIndex len = ::CFStringGetLength(family);
buffer.SetLength(len+1);
@ -682,7 +687,11 @@ gfxMacPlatformFontList::InitFontList()
// add the family entry to the hash table
ToLowerCase(familyName);
mFontFamilies.Put(familyName, familyEntry);
if (!hiddenSystemFont) {
mFontFamilies.Put(familyName, familyEntry);
} else {
mSystemFontFamilies.Put(familyName, familyEntry);
}
// check the bad underline blacklist
if (mBadUnderlineFamilyNames.Contains(familyName))

Просмотреть файл

@ -729,7 +729,7 @@ gfxPlatformFontList::CheckFamily(gfxFontFamily *aFamily)
}
gfxFontFamily*
gfxPlatformFontList::FindFamily(const nsAString& aFamily)
gfxPlatformFontList::FindFamily(const nsAString& aFamily, bool aUseSystemFonts)
{
nsAutoString key;
gfxFontFamily *familyEntry;
@ -742,6 +742,15 @@ gfxPlatformFontList::FindFamily(const nsAString& aFamily)
return CheckFamily(familyEntry);
}
#if defined(XP_MACOSX)
// for system font types allow hidden system fonts to be referenced
if (aUseSystemFonts) {
if ((familyEntry = mSystemFontFamilies.GetWeak(key)) != nullptr) {
return CheckFamily(familyEntry);
}
}
#endif
// lookup in other family names list (mostly localized names)
if ((familyEntry = mOtherFamilyNames.GetWeak(key)) != nullptr) {
return CheckFamily(familyEntry);

Просмотреть файл

@ -125,8 +125,8 @@ public:
int32_t aRunScript,
const gfxFontStyle* aStyle);
// TODO: make this virtual, for lazily adding to the font list
virtual gfxFontFamily* FindFamily(const nsAString& aFamily);
virtual gfxFontFamily* FindFamily(const nsAString& aFamily,
bool aUseSystemFonts = false);
gfxFontEntry* FindFontForFamily(const nsAString& aFamily, const gfxFontStyle* aStyle, bool& aNeedsBold);
@ -292,6 +292,11 @@ protected:
// canonical family name ==> family entry (unique, one name per family entry)
nsRefPtrHashtable<nsStringHashKey, gfxFontFamily> mFontFamilies;
#if defined(XP_MACOSX)
// hidden system fonts used within UI elements
nsRefPtrHashtable<nsStringHashKey, gfxFontFamily> mSystemFontFamilies;
#endif
// other family name ==> family entry (not unique, can have multiple names per
// family entry, only names *other* than the canonical names are stored here)
nsRefPtrHashtable<nsStringHashKey, gfxFontFamily> mOtherFamilyNames;

Просмотреть файл

@ -7,4 +7,8 @@
# internal linkage. Components that use frozen (external) linkage should use
# unicharutil_external_s.
ifdef ENABLE_INTL_API
LOCAL_INCLUDES += $(MOZ_ICU_CFLAGS)
endif
DIST_INSTALL = 1

Просмотреть файл

@ -14,6 +14,3 @@ LOCAL_INCLUDES += [
'..',
'../../src',
]
if CONFIG['ENABLE_INTL_API']:
CXXFLAGS += CONFIG['MOZ_ICU_CFLAGS']

Просмотреть файл

@ -324,6 +324,7 @@ selfhosting_srcs := \
$(srcdir)/builtin/String.js \
$(srcdir)/builtin/Set.js \
$(srcdir)/builtin/TypedObject.js \
$(srcdir)/builtin/WeakSet.js \
$(NULL)
selfhosted_out_h_deps := \

Просмотреть файл

@ -760,11 +760,11 @@ AsmJSModule::initHeap(Handle<ArrayBufferObject*> heap, JSContext *cx)
for (unsigned i = 0; i < heapAccesses_.length(); i++) {
const jit::AsmJSHeapAccess &access = heapAccesses_[i];
if (access.hasLengthCheck())
JSC::X86Assembler::setPointer(access.patchLengthAt(code_), heapLength);
X86Assembler::setPointer(access.patchLengthAt(code_), heapLength);
void *addr = access.patchOffsetAt(code_);
uint32_t disp = reinterpret_cast<uint32_t>(JSC::X86Assembler::getPointer(addr));
uint32_t disp = reinterpret_cast<uint32_t>(X86Assembler::getPointer(addr));
JS_ASSERT(disp <= INT32_MAX);
JSC::X86Assembler::setPointer(addr, (void *)(heapOffset + disp));
X86Assembler::setPointer(addr, (void *)(heapOffset + disp));
}
#elif defined(JS_CODEGEN_X64)
int32_t heapLength = int32_t(intptr_t(heap->byteLength()));
@ -777,7 +777,7 @@ AsmJSModule::initHeap(Handle<ArrayBufferObject*> heap, JSContext *cx)
for (size_t i = 0; i < heapAccesses_.length(); i++) {
const jit::AsmJSHeapAccess &access = heapAccesses_[i];
if (access.hasLengthCheck())
JSC::X86Assembler::setInt32(access.patchLengthAt(code_), heapLength);
X86Assembler::setInt32(access.patchLengthAt(code_), heapLength);
}
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
uint32_t heapLength = heap->byteLength();
@ -825,9 +825,9 @@ AsmJSModule::restoreToInitialState(uint8_t *prevCode, ArrayBufferObject *maybePr
for (unsigned i = 0; i < heapAccesses_.length(); i++) {
const jit::AsmJSHeapAccess &access = heapAccesses_[i];
void *addr = access.patchOffsetAt(code_);
uint8_t *ptr = reinterpret_cast<uint8_t*>(JSC::X86Assembler::getPointer(addr));
uint8_t *ptr = reinterpret_cast<uint8_t*>(X86Assembler::getPointer(addr));
JS_ASSERT(ptr >= ptrBase);
JSC::X86Assembler::setPointer(addr, (void *)(ptr - ptrBase));
X86Assembler::setPointer(addr, (void *)(ptr - ptrBase));
}
#endif
}
@ -1601,7 +1601,7 @@ AsmJSModule::setProfilingEnabled(bool enabled, JSContext *cx)
uint8_t *callerRetAddr = code_ + cs.returnAddressOffset();
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
void *callee = JSC::X86Assembler::getRel32Target(callerRetAddr);
void *callee = X86Assembler::getRel32Target(callerRetAddr);
#elif defined(JS_CODEGEN_ARM)
uint8_t *caller = callerRetAddr - 4;
Instruction *callerInsn = reinterpret_cast<Instruction*>(caller);
@ -1629,7 +1629,7 @@ AsmJSModule::setProfilingEnabled(bool enabled, JSContext *cx)
uint8_t *newCallee = enabled ? profilingEntry : entry;
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
JSC::X86Assembler::setRel32(callerRetAddr, newCallee);
X86Assembler::setRel32(callerRetAddr, newCallee);
#elif defined(JS_CODEGEN_ARM)
new (caller) InstBLImm(BOffImm(newCallee - caller), Assembler::Always);
#elif defined(JS_CODEGEN_MIPS)

Просмотреть файл

@ -383,42 +383,42 @@ SetRegisterToCoercedUndefined(CONTEXT *context, bool isFloat32, AnyRegister reg)
{
if (reg.isFloat()) {
switch (reg.fpu().code()) {
case JSC::X86Registers::xmm0: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 0)); break;
case JSC::X86Registers::xmm1: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 1)); break;
case JSC::X86Registers::xmm2: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 2)); break;
case JSC::X86Registers::xmm3: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 3)); break;
case JSC::X86Registers::xmm4: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 4)); break;
case JSC::X86Registers::xmm5: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 5)); break;
case JSC::X86Registers::xmm6: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 6)); break;
case JSC::X86Registers::xmm7: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 7)); break;
case JSC::X86Registers::xmm8: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 8)); break;
case JSC::X86Registers::xmm9: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 9)); break;
case JSC::X86Registers::xmm10: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 10)); break;
case JSC::X86Registers::xmm11: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 11)); break;
case JSC::X86Registers::xmm12: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 12)); break;
case JSC::X86Registers::xmm13: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 13)); break;
case JSC::X86Registers::xmm14: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 14)); break;
case JSC::X86Registers::xmm15: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 15)); break;
case X86Registers::xmm0: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 0)); break;
case X86Registers::xmm1: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 1)); break;
case X86Registers::xmm2: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 2)); break;
case X86Registers::xmm3: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 3)); break;
case X86Registers::xmm4: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 4)); break;
case X86Registers::xmm5: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 5)); break;
case X86Registers::xmm6: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 6)); break;
case X86Registers::xmm7: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 7)); break;
case X86Registers::xmm8: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 8)); break;
case X86Registers::xmm9: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 9)); break;
case X86Registers::xmm10: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 10)); break;
case X86Registers::xmm11: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 11)); break;
case X86Registers::xmm12: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 12)); break;
case X86Registers::xmm13: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 13)); break;
case X86Registers::xmm14: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 14)); break;
case X86Registers::xmm15: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 15)); break;
default: MOZ_CRASH();
}
} else {
switch (reg.gpr().code()) {
case JSC::X86Registers::eax: RAX_sig(context) = 0; break;
case JSC::X86Registers::ecx: RCX_sig(context) = 0; break;
case JSC::X86Registers::edx: RDX_sig(context) = 0; break;
case JSC::X86Registers::ebx: RBX_sig(context) = 0; break;
case JSC::X86Registers::esp: RSP_sig(context) = 0; break;
case JSC::X86Registers::ebp: RBP_sig(context) = 0; break;
case JSC::X86Registers::esi: RSI_sig(context) = 0; break;
case JSC::X86Registers::edi: RDI_sig(context) = 0; break;
case JSC::X86Registers::r8: R8_sig(context) = 0; break;
case JSC::X86Registers::r9: R9_sig(context) = 0; break;
case JSC::X86Registers::r10: R10_sig(context) = 0; break;
case JSC::X86Registers::r11: R11_sig(context) = 0; break;
case JSC::X86Registers::r12: R12_sig(context) = 0; break;
case JSC::X86Registers::r13: R13_sig(context) = 0; break;
case JSC::X86Registers::r14: R14_sig(context) = 0; break;
case JSC::X86Registers::r15: R15_sig(context) = 0; break;
case X86Registers::eax: RAX_sig(context) = 0; break;
case X86Registers::ecx: RCX_sig(context) = 0; break;
case X86Registers::edx: RDX_sig(context) = 0; break;
case X86Registers::ebx: RBX_sig(context) = 0; break;
case X86Registers::esp: RSP_sig(context) = 0; break;
case X86Registers::ebp: RBP_sig(context) = 0; break;
case X86Registers::esi: RSI_sig(context) = 0; break;
case X86Registers::edi: RDI_sig(context) = 0; break;
case X86Registers::r8: R8_sig(context) = 0; break;
case X86Registers::r9: R9_sig(context) = 0; break;
case X86Registers::r10: R10_sig(context) = 0; break;
case X86Registers::r11: R11_sig(context) = 0; break;
case X86Registers::r12: R12_sig(context) = 0; break;
case X86Registers::r13: R13_sig(context) = 0; break;
case X86Registers::r14: R14_sig(context) = 0; break;
case X86Registers::r15: R15_sig(context) = 0; break;
default: MOZ_CRASH();
}
}
@ -554,22 +554,22 @@ SetRegisterToCoercedUndefined(mach_port_t rtThread, x86_thread_state64_t &state,
bool f32 = heapAccess.isFloat32Load();
switch (heapAccess.loadedReg().fpu().code()) {
case JSC::X86Registers::xmm0: SetXMMRegToNaN(f32, &fstate.__fpu_xmm0); break;
case JSC::X86Registers::xmm1: SetXMMRegToNaN(f32, &fstate.__fpu_xmm1); break;
case JSC::X86Registers::xmm2: SetXMMRegToNaN(f32, &fstate.__fpu_xmm2); break;
case JSC::X86Registers::xmm3: SetXMMRegToNaN(f32, &fstate.__fpu_xmm3); break;
case JSC::X86Registers::xmm4: SetXMMRegToNaN(f32, &fstate.__fpu_xmm4); break;
case JSC::X86Registers::xmm5: SetXMMRegToNaN(f32, &fstate.__fpu_xmm5); break;
case JSC::X86Registers::xmm6: SetXMMRegToNaN(f32, &fstate.__fpu_xmm6); break;
case JSC::X86Registers::xmm7: SetXMMRegToNaN(f32, &fstate.__fpu_xmm7); break;
case JSC::X86Registers::xmm8: SetXMMRegToNaN(f32, &fstate.__fpu_xmm8); break;
case JSC::X86Registers::xmm9: SetXMMRegToNaN(f32, &fstate.__fpu_xmm9); break;
case JSC::X86Registers::xmm10: SetXMMRegToNaN(f32, &fstate.__fpu_xmm10); break;
case JSC::X86Registers::xmm11: SetXMMRegToNaN(f32, &fstate.__fpu_xmm11); break;
case JSC::X86Registers::xmm12: SetXMMRegToNaN(f32, &fstate.__fpu_xmm12); break;
case JSC::X86Registers::xmm13: SetXMMRegToNaN(f32, &fstate.__fpu_xmm13); break;
case JSC::X86Registers::xmm14: SetXMMRegToNaN(f32, &fstate.__fpu_xmm14); break;
case JSC::X86Registers::xmm15: SetXMMRegToNaN(f32, &fstate.__fpu_xmm15); break;
case X86Registers::xmm0: SetXMMRegToNaN(f32, &fstate.__fpu_xmm0); break;
case X86Registers::xmm1: SetXMMRegToNaN(f32, &fstate.__fpu_xmm1); break;
case X86Registers::xmm2: SetXMMRegToNaN(f32, &fstate.__fpu_xmm2); break;
case X86Registers::xmm3: SetXMMRegToNaN(f32, &fstate.__fpu_xmm3); break;
case X86Registers::xmm4: SetXMMRegToNaN(f32, &fstate.__fpu_xmm4); break;
case X86Registers::xmm5: SetXMMRegToNaN(f32, &fstate.__fpu_xmm5); break;
case X86Registers::xmm6: SetXMMRegToNaN(f32, &fstate.__fpu_xmm6); break;
case X86Registers::xmm7: SetXMMRegToNaN(f32, &fstate.__fpu_xmm7); break;
case X86Registers::xmm8: SetXMMRegToNaN(f32, &fstate.__fpu_xmm8); break;
case X86Registers::xmm9: SetXMMRegToNaN(f32, &fstate.__fpu_xmm9); break;
case X86Registers::xmm10: SetXMMRegToNaN(f32, &fstate.__fpu_xmm10); break;
case X86Registers::xmm11: SetXMMRegToNaN(f32, &fstate.__fpu_xmm11); break;
case X86Registers::xmm12: SetXMMRegToNaN(f32, &fstate.__fpu_xmm12); break;
case X86Registers::xmm13: SetXMMRegToNaN(f32, &fstate.__fpu_xmm13); break;
case X86Registers::xmm14: SetXMMRegToNaN(f32, &fstate.__fpu_xmm14); break;
case X86Registers::xmm15: SetXMMRegToNaN(f32, &fstate.__fpu_xmm15); break;
default: MOZ_CRASH();
}
@ -578,22 +578,22 @@ SetRegisterToCoercedUndefined(mach_port_t rtThread, x86_thread_state64_t &state,
return false;
} else {
switch (heapAccess.loadedReg().gpr().code()) {
case JSC::X86Registers::eax: state.__rax = 0; break;
case JSC::X86Registers::ecx: state.__rcx = 0; break;
case JSC::X86Registers::edx: state.__rdx = 0; break;
case JSC::X86Registers::ebx: state.__rbx = 0; break;
case JSC::X86Registers::esp: state.__rsp = 0; break;
case JSC::X86Registers::ebp: state.__rbp = 0; break;
case JSC::X86Registers::esi: state.__rsi = 0; break;
case JSC::X86Registers::edi: state.__rdi = 0; break;
case JSC::X86Registers::r8: state.__r8 = 0; break;
case JSC::X86Registers::r9: state.__r9 = 0; break;
case JSC::X86Registers::r10: state.__r10 = 0; break;
case JSC::X86Registers::r11: state.__r11 = 0; break;
case JSC::X86Registers::r12: state.__r12 = 0; break;
case JSC::X86Registers::r13: state.__r13 = 0; break;
case JSC::X86Registers::r14: state.__r14 = 0; break;
case JSC::X86Registers::r15: state.__r15 = 0; break;
case X86Registers::eax: state.__rax = 0; break;
case X86Registers::ecx: state.__rcx = 0; break;
case X86Registers::edx: state.__rdx = 0; break;
case X86Registers::ebx: state.__rbx = 0; break;
case X86Registers::esp: state.__rsp = 0; break;
case X86Registers::ebp: state.__rbp = 0; break;
case X86Registers::esi: state.__rsi = 0; break;
case X86Registers::edi: state.__rdi = 0; break;
case X86Registers::r8: state.__r8 = 0; break;
case X86Registers::r9: state.__r9 = 0; break;
case X86Registers::r10: state.__r10 = 0; break;
case X86Registers::r11: state.__r11 = 0; break;
case X86Registers::r12: state.__r12 = 0; break;
case X86Registers::r13: state.__r13 = 0; break;
case X86Registers::r14: state.__r14 = 0; break;
case X86Registers::r15: state.__r15 = 0; break;
default: MOZ_CRASH();
}
}

Просмотреть файл

@ -30,7 +30,7 @@
#ifndef assembler_assembler_MacroAssemblerX86Common_h
#define assembler_assembler_MacroAssemblerX86Common_h
#include "assembler/assembler/X86Assembler.h"
#include "jit/shared/BaseAssembler-x86-shared.h"
namespace JSC {

Просмотреть файл

@ -36,4 +36,7 @@
#define ATTR_NONCONFIGURABLE 0x10
#define ATTR_NONWRITABLE 0x20
// Stores the private WeakMap slot used for WeakSets
#define WEAKSET_MAP_SLOT 0
#endif

Просмотреть файл

@ -81,6 +81,8 @@ var std_WeakMap = WeakMap;
var std_WeakMap_get = WeakMap.prototype.get;
var std_WeakMap_has = WeakMap.prototype.has;
var std_WeakMap_set = WeakMap.prototype.set;
var std_WeakMap_clear = WeakMap.prototype.clear;
var std_WeakMap_delete = WeakMap.prototype.delete;
var std_Map_has = Map.prototype.has;
var std_Set_has = Set.prototype.has;
var std_iterator = '@@iterator'; // FIXME: Change to be a symbol.

72
js/src/builtin/WeakSet.js Normal file
Просмотреть файл

@ -0,0 +1,72 @@
/* 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/. */
// 23.4.3.1
function WeakSet_add(value) {
// Steps 1-4.
var S = this;
if (!IsObject(S) || !IsWeakSet(S))
ThrowError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "add", typeof S);
// Step 5.
if (!IsObject(value))
ThrowError(JSMSG_NOT_NONNULL_OBJECT);
// Step 6.
let entries = UnsafeGetReservedSlot(this, WEAKSET_MAP_SLOT);
// Steps 7-8.
callFunction(std_WeakMap_set, entries, value, true);
// Step 8.
return S;
}
// 23.4.3.2
function WeakSet_clear() {
// Step 1-4.
var S = this;
if (!IsObject(S) || !IsWeakSet(S))
ThrowError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "clear", typeof S);
// Step 5.
let entries = UnsafeGetReservedSlot(this, WEAKSET_MAP_SLOT);
callFunction(std_WeakMap_clear, entries);
// Step 6.
return undefined;
}
// 23.4.3.4
function WeakSet_delete(value) {
// Steps 1-2.
var S = this;
if (!IsObject(S) || !IsWeakSet(S))
ThrowError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "delete", typeof S);
// Step 5.
if (!IsObject(value))
ThrowError(JSMSG_NOT_NONNULL_OBJECT);
// Step 6.
let entries = UnsafeGetReservedSlot(this, WEAKSET_MAP_SLOT);
// Steps 7-8.
return callFunction(std_WeakMap_delete, entries, value);
}
// 23.4.3.5
function WeakSet_has(value) {
// Steps 1-4.
var S = this;
if (!IsObject(S) || !IsWeakSet(S))
ThrowError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "has", typeof S);
// Step 5.
if (!IsObject(value))
ThrowError(JSMSG_NOT_NONNULL_OBJECT);
// Step 6.
let entries = UnsafeGetReservedSlot(this, WEAKSET_MAP_SLOT);
// Steps 7-8.
return callFunction(std_WeakMap_has, entries, value);
}

Просмотреть файл

@ -0,0 +1,134 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "builtin/WeakSetObject.h"
#include "jsapi.h"
#include "jscntxt.h"
#include "jsiter.h"
#include "builtin/SelfHostingDefines.h"
#include "vm/GlobalObject.h"
#include "jsobjinlines.h"
using namespace js;
using namespace JS;
const Class WeakSetObject::class_ = {
"WeakSet",
JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_CACHED_PROTO(JSProto_WeakSet) |
JSCLASS_HAS_RESERVED_SLOTS(WeakSetObject::RESERVED_SLOTS),
JS_PropertyStub, // addProperty
JS_DeletePropertyStub, // delProperty
JS_PropertyStub, // getProperty
JS_StrictPropertyStub, // setProperty
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub
};
const JSPropertySpec WeakSetObject::properties[] = {
JS_PS_END
};
const JSFunctionSpec WeakSetObject::methods[] = {
JS_SELF_HOSTED_FN("add", "WeakSet_add", 1, 0),
JS_SELF_HOSTED_FN("clear", "WeakSet_clear", 0, 0),
JS_SELF_HOSTED_FN("delete", "WeakSet_delete", 1, 0),
JS_SELF_HOSTED_FN("has", "WeakSet_has", 1, 0),
JS_FS_END
};
JSObject *
WeakSetObject::initClass(JSContext *cx, JSObject *obj)
{
Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
// Todo: WeakSet.prototype should not be a WeakSet!
Rooted<JSObject*> proto(cx, global->createBlankPrototype(cx, &class_));
if (!proto)
return nullptr;
proto->setReservedSlot(WEAKSET_MAP_SLOT, UndefinedValue());
Rooted<JSFunction*> ctor(cx, global->createConstructor(cx, construct, ClassName(JSProto_WeakSet, cx), 1));
if (!ctor ||
!LinkConstructorAndPrototype(cx, ctor, proto) ||
!DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
!GlobalObject::initBuiltinConstructor(cx, global, JSProto_WeakSet, ctor, proto))
{
return nullptr;
}
return proto;
}
WeakSetObject*
WeakSetObject::create(JSContext *cx)
{
RootedObject obj(cx, NewBuiltinClassInstance(cx, &class_));
if (!obj)
return nullptr;
RootedObject map(cx, NewWeakMapObject(cx));
if (!map)
return nullptr;
obj->setReservedSlot(WEAKSET_MAP_SLOT, ObjectValue(*map));
return &obj->as<WeakSetObject>();
}
bool
WeakSetObject::construct(JSContext *cx, unsigned argc, Value *vp)
{
Rooted<WeakSetObject*> obj(cx, WeakSetObject::create(cx));
if (!obj)
return false;
// Based on our "Set" implementation instead of the more general ES6 steps.
CallArgs args = CallArgsFromVp(argc, vp);
if (!args.isConstructing()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_FUNCTION, "WeakSet");
return false;
}
if (args.hasDefined(0)) {
RootedObject map(cx, &obj->getReservedSlot(WEAKSET_MAP_SLOT).toObject());
ForOfIterator iter(cx);
if (!iter.init(args[0]))
return false;
RootedValue keyVal(cx);
RootedObject keyObject(cx);
RootedValue placeholder(cx, BooleanValue(true));
while (true) {
bool done;
if (!iter.next(&keyVal, &done))
return false;
if (done)
break;
if (keyVal.isPrimitive()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
return false;
}
keyObject = &keyVal.toObject();
if (!SetWeakMapEntry(cx, map, keyObject, placeholder))
return false;
}
}
args.rval().setObject(*obj);
return true;
}
JSObject *
js_InitWeakSetClass(JSContext *cx, HandleObject obj)
{
return WeakSetObject::initClass(cx, obj);
}

Просмотреть файл

@ -0,0 +1,35 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef builtin_WeakSetObject_h
#define builtin_WeakSetObject_h
#include "jsobj.h"
namespace js {
class WeakSetObject : public JSObject
{
public:
static const unsigned RESERVED_SLOTS = 1;
static JSObject *initClass(JSContext *cx, JSObject *obj);
static const Class class_;
private:
static const JSPropertySpec properties[];
static const JSFunctionSpec methods[];
static WeakSetObject* create(JSContext *cx);
static bool construct(JSContext *cx, unsigned argc, Value *vp);
};
} // namespace js
extern JSObject *
js_InitWeakSetClass(JSContext *cx, js::HandleObject obj);
#endif /* builtin_WeakSetObject_h */

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше