mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Merge m-c to m-i
This commit is contained in:
commit
abe2443efb
@ -136,7 +136,8 @@ function handleGUMStop(aSubject, aTopic, aData) {
|
||||
};
|
||||
|
||||
let mm = getMessageManagerForWindow(contentWindow);
|
||||
mm.sendAsyncMessage("webrtc:StopRecording", request);
|
||||
if (mm)
|
||||
mm.sendAsyncMessage("webrtc:StopRecording", request);
|
||||
}
|
||||
|
||||
function handleGUMRequest(aSubject, aTopic, aData) {
|
||||
|
@ -15,5 +15,6 @@ support-files =
|
||||
[chrome/test_generated_content_getAnimations.html]
|
||||
[chrome/test_observers_for_sync_api.html]
|
||||
[chrome/test_restyles.html]
|
||||
skip-if = os == 'android' && processor == 'x86' # bug 1335986
|
||||
[chrome/test_running_on_compositor.html]
|
||||
[chrome/test_simulate_compute_values_failure.html]
|
||||
|
@ -3158,6 +3158,11 @@ nsDocument::GetAllowPlugins(bool * aAllowPlugins)
|
||||
*aAllowPlugins = !(mSandboxFlags & SANDBOXED_PLUGINS);
|
||||
}
|
||||
|
||||
if (*aAllowPlugins) {
|
||||
FlashClassification classification = DocumentFlashClassification();
|
||||
*aAllowPlugins = (classification != FlashClassification::Denied);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ from mach.decorators import (
|
||||
)
|
||||
|
||||
from mozbuild.base import MachCommandBase
|
||||
from mozbuild.util import mkdir
|
||||
|
||||
def get_test_parser():
|
||||
import runtests
|
||||
@ -38,6 +39,10 @@ class WebIDLProvider(MachCommandBase):
|
||||
sys.path.insert(0, os.path.join(self.topsrcdir, 'other-licenses',
|
||||
'ply'))
|
||||
|
||||
# Ensure the topobjdir exists. On a Taskcluster test run there won't be
|
||||
# an objdir yet.
|
||||
mkdir(self.topobjdir)
|
||||
|
||||
# Make sure we drop our cached grammar bits in the objdir, not
|
||||
# wherever we happen to be running from.
|
||||
os.chdir(self.topobjdir)
|
||||
|
@ -88,6 +88,7 @@ def run_tests(tests, verbose):
|
||||
print '%s:' % test
|
||||
for failure in failures:
|
||||
print 'TEST-UNEXPECTED-FAIL | %s' % failure
|
||||
return 1 if failed_tests else 0
|
||||
|
||||
def get_parser():
|
||||
usage = """%(prog)s [OPTIONS] [TESTS]
|
||||
@ -105,4 +106,9 @@ if __name__ == '__main__':
|
||||
args = parser.parse_args()
|
||||
if args.verbose is None:
|
||||
args.verbose = True
|
||||
run_tests(args.tests, verbose=args.verbose)
|
||||
|
||||
# Make sure the current directory is in the python path so we can cache the
|
||||
# result of the webidlyacc.py generation.
|
||||
sys.path.append('.')
|
||||
|
||||
sys.exit(run_tests(args.tests, verbose=args.verbose))
|
||||
|
@ -15,7 +15,3 @@ CPPSRCS += $(addprefix ../,$(test_sources))
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
endif
|
||||
|
||||
check::
|
||||
PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
|
||||
$(PLY_INCLUDE) $(srcdir)/../parser/runtests.py
|
||||
|
@ -356,11 +356,17 @@ MP4TrackDemuxer::GetNextSample()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sample->mCrypto.mValid) {
|
||||
nsAutoPtr<MediaRawDataWriter> writer(sample->CreateWriter());
|
||||
writer->mCrypto.mMode = mInfo->mCrypto.mMode;
|
||||
writer->mCrypto.mIVSize = mInfo->mCrypto.mIVSize;
|
||||
writer->mCrypto.mKeyId.AppendElements(mInfo->mCrypto.mKeyId);
|
||||
|
||||
// Only use the default key parsed from the moov if we haven't already got
|
||||
// one from the sample group description.
|
||||
if (writer->mCrypto.mKeyId.Length() == 0) {
|
||||
writer->mCrypto.mIVSize = mInfo->mCrypto.mIVSize;
|
||||
writer->mCrypto.mKeyId.AppendElements(mInfo->mCrypto.mKeyId);
|
||||
}
|
||||
}
|
||||
return sample.forget();
|
||||
}
|
||||
|
@ -524,6 +524,8 @@ support-files =
|
||||
resolution-change.webm^headers^
|
||||
sample.3gp
|
||||
sample.3g2
|
||||
sample-encrypted-sgpdstbl-sbgptraf.mp4
|
||||
sample-encrypted-sgpdstbl-sbgptraf.mp4^headers^
|
||||
sample-fisbone-skeleton4.ogv
|
||||
sample-fisbone-skeleton4.ogv^headers^
|
||||
sample-fisbone-wrong-header.ogv
|
||||
@ -700,6 +702,8 @@ skip-if = toolkit == 'android' # bug 1149374
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
[test_eme_requestKeySystemAccess.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
[test_eme_sample_groups_playback.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
[test_eme_setMediaKeys_before_attach_MediaSource.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
[test_eme_stream_capture_blocked_case1.html]
|
||||
|
BIN
dom/media/test/sample-encrypted-sgpdstbl-sbgptraf.mp4
Normal file
BIN
dom/media/test/sample-encrypted-sgpdstbl-sbgptraf.mp4
Normal file
Binary file not shown.
@ -0,0 +1 @@
|
||||
Cache-Control: no-store
|
146
dom/media/test/test_eme_sample_groups_playback.html
Normal file
146
dom/media/test/test_eme_sample_groups_playback.html
Normal file
@ -0,0 +1,146 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Test Encrypted Media Extensions</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
<script type="text/javascript" src="eme.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<video controls id="video"></video>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
// Tests that files with a default key and a seperate sample keyids in the
|
||||
// sgpd box play correctly (if the keyid from the sgpd box is not parsed
|
||||
// or assigned to the sample we will wait indefinitely for the default
|
||||
// key).
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Test files for samples encrypted with different media keys.
|
||||
var gEMESampleGoupTests = [
|
||||
{
|
||||
name:"video with 4 keys in sgpd (sbgp in traf sgpd in stbl)",
|
||||
track: {
|
||||
name:"video",
|
||||
type:"video/mp4; codecs=\"avc1.64000d\"",
|
||||
fragments:[ "sample-encrypted-sgpdstbl-sbgptraf.mp4"
|
||||
]
|
||||
},
|
||||
keys: {
|
||||
// "keyid" : "key"
|
||||
"279926496a7f5d25da69f2b3b2799a7f": "5544694d47473326622665665a396b36",
|
||||
"597669572e55547e656b56586e2f6f68": "7959493a764556786527517849756635",
|
||||
"205b2b293a342f3d3268293e6f6f4e29": "3a4f3674376d6c48675a273464447b40",
|
||||
"32783e367c2e4d4d6b46467b3e6b5478": "3e213f6d45584f51713d534f4b417855",
|
||||
},
|
||||
sessionType:"temporary",
|
||||
sessionCount:1,
|
||||
duration:2,
|
||||
},
|
||||
],
|
||||
test = gEMESampleGoupTests[0];
|
||||
|
||||
var video = document.getElementById("video");
|
||||
video.addEventListener("encrypted", () => {
|
||||
Log(test.name, "Recieved encrypted event");
|
||||
});
|
||||
|
||||
video.addEventListener("waitingforkey", () => {
|
||||
Log(test.name, "waitingforkey");
|
||||
ok(false, test.name + " Video is waitingforkey, indicating that the samples are not being assigned the correct id from the sgpd box!");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
function LoadEME() {
|
||||
var options = [{
|
||||
initDataType: "cenc",
|
||||
videoType: test.track.type,
|
||||
}];
|
||||
|
||||
return navigator.requestMediaKeySystemAccess("org.w3.clearkey", options)
|
||||
.then((keySystemAccess) => {
|
||||
return keySystemAccess.createMediaKeys();
|
||||
}, bail("Failed to request key system access."))
|
||||
|
||||
.then((mediaKeys) => {
|
||||
video.setMediaKeys(mediaKeys);
|
||||
var session = mediaKeys.createSession();
|
||||
once(session, "message", (ev) => {
|
||||
is(ev.messageType, "license-request", "Expected a license-request");
|
||||
|
||||
var keys = [];
|
||||
for (var keyid in test.keys) {
|
||||
keys.push({
|
||||
"kty":"oct",
|
||||
"kid":HexToBase64(keyid),
|
||||
"k":HexToBase64(test.keys[keyid])
|
||||
});
|
||||
}
|
||||
|
||||
var license = new TextEncoder().encode(JSON.stringify({
|
||||
"keys": keys,
|
||||
"type": test.sessionType || "temporary"
|
||||
}));
|
||||
session.update(license);
|
||||
});
|
||||
var json = JSON.stringify({
|
||||
"kids":Object.keys(test.keys).map(HexToBase64)
|
||||
});
|
||||
|
||||
var request = new TextEncoder().encode(json);
|
||||
session.generateRequest("keyids", request)
|
||||
.then(e => {
|
||||
Log(test.name, "Request license success");
|
||||
}, reason => {
|
||||
Log("Request license failed! " + reason);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function DownloadMedia(url, type, mediaSource) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var sourceBuffer = mediaSource.addSourceBuffer(type);
|
||||
fetchWithXHR(url, (response) => {
|
||||
once(sourceBuffer, "updateend", resolve);
|
||||
sourceBuffer.appendBuffer(new Uint8Array(response));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function LoadMSE() {
|
||||
// Only set the source of the video and download the tracks after we
|
||||
// have set the license keys, so we don't hit the waitingforkey event
|
||||
// unless samples are being incorrectly assigned the default key
|
||||
// (and we can safely fail).
|
||||
LoadEME()
|
||||
.then(() => {
|
||||
var ms = new MediaSource();
|
||||
video.src = URL.createObjectURL(ms);
|
||||
|
||||
once(ms, "sourceopen", () => {
|
||||
Promise.all(test.track.fragments.map(fragment => DownloadMedia(fragment, test.track.type, ms)))
|
||||
.then(() => {
|
||||
ms.endOfStream();
|
||||
LoadEME();
|
||||
})
|
||||
.then(() => {
|
||||
video.play();
|
||||
});
|
||||
});
|
||||
|
||||
video.addEventListener("ended", SimpleTest.finish);
|
||||
});
|
||||
}
|
||||
|
||||
SetupEMEPref(LoadMSE);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -77,6 +77,9 @@ interface AddonManager : EventTarget {
|
||||
*/
|
||||
Promise<AddonInstall> createInstall(optional addonInstallOptions options);
|
||||
|
||||
// Indicator to content whether permissions prompts are enabled
|
||||
readonly attribute boolean permissionPromptsEnabled;
|
||||
|
||||
/* Hooks for managing event listeners */
|
||||
[ChromeOnly]
|
||||
void eventListenerWasAdded(DOMString type);
|
||||
|
@ -137,6 +137,11 @@ already_AddRefed<MediaRawData> SampleIterator::GetNext()
|
||||
writer->mCrypto.mValid = true;
|
||||
writer->mCrypto.mIVSize = ivSize;
|
||||
|
||||
CencSampleEncryptionInfoEntry* sampleInfo = GetSampleEncryptionEntry();
|
||||
if (sampleInfo) {
|
||||
writer->mCrypto.mKeyId.AppendElements(sampleInfo->mKeyId);
|
||||
}
|
||||
|
||||
if (!reader.ReadArray(writer->mCrypto.mIV, ivSize)) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -164,6 +169,65 @@ already_AddRefed<MediaRawData> SampleIterator::GetNext()
|
||||
return sample.forget();
|
||||
}
|
||||
|
||||
CencSampleEncryptionInfoEntry* SampleIterator::GetSampleEncryptionEntry()
|
||||
{
|
||||
nsTArray<Moof>& moofs = mIndex->mMoofParser->Moofs();
|
||||
Moof* currentMoof = &moofs[mCurrentMoof];
|
||||
SampleToGroupEntry* sampleToGroupEntry = nullptr;
|
||||
|
||||
// Default to using the sample to group entries for the fragment, otherwise
|
||||
// fall back to the sample to group entries for the track.
|
||||
nsTArray<SampleToGroupEntry>* sampleToGroupEntries =
|
||||
currentMoof->mFragmentSampleToGroupEntries.Length() != 0
|
||||
? ¤tMoof->mFragmentSampleToGroupEntries
|
||||
: &mIndex->mMoofParser->mTrackSampleToGroupEntries;
|
||||
|
||||
uint32_t seen = 0;
|
||||
|
||||
for (SampleToGroupEntry& entry : *sampleToGroupEntries) {
|
||||
if (seen + entry.mSampleCount > mCurrentSample) {
|
||||
sampleToGroupEntry = &entry;
|
||||
break;
|
||||
}
|
||||
seen += entry.mSampleCount;
|
||||
}
|
||||
|
||||
// ISO-14496-12 Section 8.9.2.3 and 8.9.4 : group description index
|
||||
// (1) ranges from 1 to the number of sample group entries in the track
|
||||
// level SampleGroupDescription Box, or (2) takes the value 0 to
|
||||
// indicate that this sample is a member of no group, in this case, the
|
||||
// sample is associated with the default values specified in
|
||||
// TrackEncryption Box, or (3) starts at 0x10001, i.e. the index value
|
||||
// 1, with the value 1 in the top 16 bits, to reference fragment-local
|
||||
// SampleGroupDescription Box.
|
||||
|
||||
// According to the spec, ISO-14496-12, the sum of the sample counts in this
|
||||
// box should be equal to the total number of samples, and, if less, the
|
||||
// reader should behave as if an extra SampleToGroupEntry existed, with
|
||||
// groupDescriptionIndex 0.
|
||||
|
||||
if (!sampleToGroupEntry || sampleToGroupEntry->mGroupDescriptionIndex == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsTArray<CencSampleEncryptionInfoEntry>* entries =
|
||||
&mIndex->mMoofParser->mTrackSampleEncryptionInfoEntries;
|
||||
|
||||
uint32_t groupIndex = sampleToGroupEntry->mGroupDescriptionIndex;
|
||||
|
||||
// If the first bit is set to a one, then we should use the sample group
|
||||
// descriptions from the fragment.
|
||||
if (groupIndex > SampleToGroupEntry::kFragmentGroupDescriptionIndexBase) {
|
||||
groupIndex -= SampleToGroupEntry::kFragmentGroupDescriptionIndexBase;
|
||||
entries = ¤tMoof->mFragmentSampleEncryptionInfoEntries;
|
||||
}
|
||||
|
||||
// The group_index is one based.
|
||||
return groupIndex > entries->Length()
|
||||
? nullptr
|
||||
: &entries->ElementAt(groupIndex - 1);
|
||||
}
|
||||
|
||||
Sample* SampleIterator::Get()
|
||||
{
|
||||
if (!mIndex->mMoofParser) {
|
||||
|
@ -27,6 +27,8 @@ namespace mp4_demuxer
|
||||
using namespace stagefright;
|
||||
using namespace mozilla;
|
||||
|
||||
const uint32_t kKeyIdSize = 16;
|
||||
|
||||
bool
|
||||
MoofParser::RebuildFragmentedIndex(
|
||||
const MediaByteRangeSet& aByteRanges)
|
||||
@ -315,6 +317,18 @@ MoofParser::ParseStbl(Box& aBox)
|
||||
for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
|
||||
if (box.IsType("stsd")) {
|
||||
ParseStsd(box);
|
||||
} else if (box.IsType("sgpd")) {
|
||||
Sgpd sgpd(box);
|
||||
if (sgpd.IsValid() && sgpd.mGroupingType == "seig") {
|
||||
mTrackSampleEncryptionInfoEntries.Clear();
|
||||
mTrackSampleEncryptionInfoEntries.AppendElements(sgpd.mEntries);
|
||||
}
|
||||
} else if (box.IsType("sbgp")) {
|
||||
Sbgp sbgp(box);
|
||||
if (sbgp.IsValid() && sbgp.mGroupingType == "seig") {
|
||||
mTrackSampleToGroupEntries.Clear();
|
||||
mTrackSampleToGroupEntries.AppendElements(sbgp.mEntries);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -480,12 +494,25 @@ Moof::ParseTraf(Box& aBox, Trex& aTrex, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, S
|
||||
MOZ_ASSERT(aDecodeTime);
|
||||
Tfhd tfhd(aTrex);
|
||||
Tfdt tfdt;
|
||||
|
||||
for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
|
||||
if (box.IsType("tfhd")) {
|
||||
tfhd = Tfhd(box, aTrex);
|
||||
} else if (!aTrex.mTrackId || tfhd.mTrackId == aTrex.mTrackId) {
|
||||
if (box.IsType("tfdt")) {
|
||||
tfdt = Tfdt(box);
|
||||
} else if (box.IsType("sgpd")) {
|
||||
Sgpd sgpd(box);
|
||||
if (sgpd.IsValid() && sgpd.mGroupingType == "seig") {
|
||||
mFragmentSampleEncryptionInfoEntries.Clear();
|
||||
mFragmentSampleEncryptionInfoEntries.AppendElements(sgpd.mEntries);
|
||||
}
|
||||
} else if (box.IsType("sbgp")) {
|
||||
Sbgp sbgp(box);
|
||||
if (sbgp.IsValid() && sbgp.mGroupingType == "seig") {
|
||||
mFragmentSampleToGroupEntries.Clear();
|
||||
mFragmentSampleToGroupEntries.AppendElements(sbgp.mEntries);
|
||||
}
|
||||
} else if (box.IsType("saiz")) {
|
||||
mSaizs.AppendElement(Saiz(box, aSinf.mDefaultEncryptionType));
|
||||
} else if (box.IsType("saio")) {
|
||||
@ -906,5 +933,143 @@ Saio::Saio(Box& aBox, AtomType aDefaultType)
|
||||
mValid = true;
|
||||
}
|
||||
|
||||
Sbgp::Sbgp(Box& aBox)
|
||||
{
|
||||
BoxReader reader(aBox);
|
||||
|
||||
if (!reader->CanReadType<uint32_t>()) {
|
||||
LOG(Sbgp, "Incomplete Box (missing flags)");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t flags = reader->ReadU32();
|
||||
const uint8_t version = flags >> 24;
|
||||
flags = flags & 0xffffff;
|
||||
|
||||
// Make sure we have enough bytes to read as far as the count.
|
||||
uint32_t need = (version == 1 ? sizeof(uint32_t) : 0) + sizeof(uint32_t) * 2;
|
||||
if (reader->Remaining() < need) {
|
||||
LOG(Sbgp, "Incomplete Box (have:%lld, need:%lld)",
|
||||
(uint64_t)reader->Remaining(), (uint64_t)need);
|
||||
return;
|
||||
}
|
||||
|
||||
mGroupingType = reader->ReadU32();
|
||||
|
||||
if (version == 1) {
|
||||
mGroupingTypeParam = reader->Read32();
|
||||
}
|
||||
|
||||
uint32_t count = reader->ReadU32();
|
||||
|
||||
// Make sure we can read all the entries.
|
||||
need = sizeof(uint32_t) * 2 * count;
|
||||
if (reader->Remaining() < need) {
|
||||
LOG(Sbgp, "Incomplete Box (have:%lld, need:%lld). Failed to read entries",
|
||||
(uint64_t)reader->Remaining(), (uint64_t)need);
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
uint32_t sampleCount = reader->ReadU32();
|
||||
uint32_t groupDescriptionIndex = reader->ReadU32();
|
||||
|
||||
SampleToGroupEntry entry(sampleCount, groupDescriptionIndex);
|
||||
mEntries.AppendElement(entry);
|
||||
}
|
||||
|
||||
mValid = true;
|
||||
}
|
||||
|
||||
Sgpd::Sgpd(Box& aBox)
|
||||
{
|
||||
BoxReader reader(aBox);
|
||||
|
||||
if (!reader->CanReadType<uint32_t>()) {
|
||||
LOG(Sgpd, "Incomplete Box (missing flags)");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t flags = reader->ReadU32();
|
||||
const uint8_t version = flags >> 24;
|
||||
flags = flags & 0xffffff;
|
||||
|
||||
uint32_t need = ((flags & 1) ? sizeof(uint32_t) : 0) + sizeof(uint32_t) * 2;
|
||||
if (reader->Remaining() < need) {
|
||||
LOG(Sgpd, "Incomplete Box (have:%lld need:%lld)",
|
||||
(uint64_t)reader->Remaining(), (uint64_t)need);
|
||||
return;
|
||||
}
|
||||
|
||||
mGroupingType = reader->ReadU32();
|
||||
|
||||
const uint32_t entrySize = sizeof(uint32_t) + kKeyIdSize;
|
||||
uint32_t defaultLength = 0;
|
||||
|
||||
if (version == 1) {
|
||||
defaultLength = reader->ReadU32();
|
||||
if (defaultLength < entrySize && defaultLength != 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t count = reader->ReadU32();
|
||||
|
||||
// Make sure we have sufficient remaining bytes to read the entries.
|
||||
need =
|
||||
count * (sizeof(uint32_t) * (version == 1 && defaultLength == 0 ? 2 : 1) +
|
||||
kKeyIdSize * sizeof(uint8_t));
|
||||
if (reader->Remaining() < need) {
|
||||
LOG(Sgpd, "Incomplete Box (have:%lld need:%lld). Failed to read entries",
|
||||
(uint64_t)reader->Remaining(), (uint64_t)need);
|
||||
return;
|
||||
}
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
if (version == 1 && defaultLength == 0) {
|
||||
uint32_t descriptionLength = reader->ReadU32();
|
||||
if (descriptionLength < entrySize) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CencSampleEncryptionInfoEntry entry;
|
||||
bool valid = entry.Init(reader);
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
mEntries.AppendElement(entry);
|
||||
}
|
||||
|
||||
mValid = true;
|
||||
}
|
||||
|
||||
bool CencSampleEncryptionInfoEntry::Init(BoxReader& aReader)
|
||||
{
|
||||
// Skip a reserved byte.
|
||||
aReader->ReadU8();
|
||||
|
||||
uint8_t possiblePatternInfo = aReader->ReadU8();
|
||||
uint8_t flag = aReader->ReadU8();
|
||||
|
||||
mIVSize = aReader->ReadU8();
|
||||
|
||||
// Read the key id.
|
||||
for (uint32_t i = 0; i < kKeyIdSize; ++i) {
|
||||
mKeyId.AppendElement(aReader->ReadU8());
|
||||
}
|
||||
|
||||
mIsEncrypted = flag != 0;
|
||||
|
||||
if (mIsEncrypted) {
|
||||
if (mIVSize != 8 && mIVSize != 16) {
|
||||
return false;
|
||||
}
|
||||
} else if (mIVSize != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#undef LOG
|
||||
}
|
||||
|
@ -32,6 +32,9 @@ public:
|
||||
|
||||
private:
|
||||
Sample* Get();
|
||||
|
||||
CencSampleEncryptionInfoEntry* GetSampleEncryptionEntry();
|
||||
|
||||
void Next();
|
||||
RefPtr<Index> mIndex;
|
||||
size_t mCurrentMoof;
|
||||
|
@ -17,6 +17,7 @@ typedef int64_t Microseconds;
|
||||
|
||||
class Box;
|
||||
class BoxContext;
|
||||
class BoxReader;
|
||||
class Moof;
|
||||
|
||||
class Mvhd : public Atom
|
||||
@ -160,6 +161,53 @@ public:
|
||||
FallibleTArray<uint64_t> mOffsets;
|
||||
};
|
||||
|
||||
struct SampleToGroupEntry
|
||||
{
|
||||
public:
|
||||
static const uint32_t kTrackGroupDescriptionIndexBase = 0;
|
||||
static const uint32_t kFragmentGroupDescriptionIndexBase = 0x10000;
|
||||
|
||||
SampleToGroupEntry(uint32_t aSampleCount, uint32_t aGroupDescriptionIndex)
|
||||
: mSampleCount(aSampleCount)
|
||||
, mGroupDescriptionIndex(aGroupDescriptionIndex)
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t mSampleCount;
|
||||
uint32_t mGroupDescriptionIndex;
|
||||
};
|
||||
|
||||
class Sbgp final : public Atom // SampleToGroup box.
|
||||
{
|
||||
public:
|
||||
explicit Sbgp(Box& aBox);
|
||||
|
||||
AtomType mGroupingType;
|
||||
uint32_t mGroupingTypeParam;
|
||||
nsTArray<SampleToGroupEntry> mEntries;
|
||||
};
|
||||
|
||||
struct CencSampleEncryptionInfoEntry final
|
||||
{
|
||||
public:
|
||||
CencSampleEncryptionInfoEntry() { }
|
||||
|
||||
bool Init(BoxReader& aReader);
|
||||
|
||||
bool mIsEncrypted = false;
|
||||
uint8_t mIVSize = 0;
|
||||
nsTArray<uint8_t> mKeyId;
|
||||
};
|
||||
|
||||
class Sgpd final : public Atom // SampleGroupDescription box.
|
||||
{
|
||||
public:
|
||||
explicit Sgpd(Box& aBox);
|
||||
|
||||
AtomType mGroupingType;
|
||||
nsTArray<CencSampleEncryptionInfoEntry> mEntries;
|
||||
};
|
||||
|
||||
class AuxInfo {
|
||||
public:
|
||||
AuxInfo(int64_t aMoofOffset, Saiz& aSaiz, Saio& aSaio);
|
||||
@ -182,6 +230,9 @@ public:
|
||||
Interval<Microseconds> mTimeRange;
|
||||
FallibleTArray<Sample> mIndex;
|
||||
|
||||
nsTArray<CencSampleEncryptionInfoEntry> mFragmentSampleEncryptionInfoEntries;
|
||||
nsTArray<SampleToGroupEntry> mFragmentSampleToGroupEntries;
|
||||
|
||||
nsTArray<Saiz> mSaizs;
|
||||
nsTArray<Saio> mSaios;
|
||||
|
||||
@ -242,6 +293,10 @@ public:
|
||||
Tfdt mTfdt;
|
||||
Edts mEdts;
|
||||
Sinf mSinf;
|
||||
|
||||
nsTArray<CencSampleEncryptionInfoEntry> mTrackSampleEncryptionInfoEntries;
|
||||
nsTArray<SampleToGroupEntry> mTrackSampleToGroupEntries;
|
||||
|
||||
nsTArray<Moof>& Moofs() { return mMoofs; }
|
||||
private:
|
||||
void ScanForMetadata(mozilla::MediaByteRange& aFtyp,
|
||||
|
@ -44,6 +44,9 @@ public class CustomTabsActivity extends GeckoApp implements Tabs.OnTabsChangedLi
|
||||
private int toolbarColor;
|
||||
private String toolbarTitle;
|
||||
|
||||
// A state to indicate whether this activity is finishing with customize animation
|
||||
private boolean usingCustomAnimation = false;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@ -103,6 +106,32 @@ public class CustomTabsActivity extends GeckoApp implements Tabs.OnTabsChangedLi
|
||||
|
||||
}
|
||||
|
||||
// Bug 1329145: 3rd party app could specify customized exit-animation to this activity.
|
||||
// Activity.overridePendingTransition will invoke getPackageName to retrieve that animation resource.
|
||||
// In that case, to return different package name to get customized animation resource.
|
||||
@Override
|
||||
public String getPackageName() {
|
||||
if (usingCustomAnimation) {
|
||||
// Use its package name to retrieve animation resource
|
||||
return IntentUtil.getAnimationPackageName(getIntent());
|
||||
} else {
|
||||
return super.getPackageName();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
super.finish();
|
||||
|
||||
// When 3rd party app launch this Activity, it could also specify custom exit-animation.
|
||||
if (IntentUtil.hasExitAnimation(getIntent())) {
|
||||
usingCustomAnimation = true;
|
||||
overridePendingTransition(IntentUtil.getEnterAnimationRes(getIntent()),
|
||||
IntentUtil.getExitAnimationRes(getIntent()));
|
||||
usingCustomAnimation = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getNewTabFlags() {
|
||||
return Tabs.LOADURL_CUSTOMTAB | super.getNewTabFlags();
|
||||
@ -162,7 +191,7 @@ public class CustomTabsActivity extends GeckoApp implements Tabs.OnTabsChangedLi
|
||||
public void onResume() {
|
||||
if (lastSelectedTabId >= 0) {
|
||||
final Tabs tabs = Tabs.getInstance();
|
||||
final Tab tab = tabs.getTab(lastSelectedTabId);
|
||||
final Tab tab = tabs.getTab(lastSelectedTabId);
|
||||
if (tab == null) {
|
||||
finish();
|
||||
}
|
||||
@ -189,7 +218,7 @@ public class CustomTabsActivity extends GeckoApp implements Tabs.OnTabsChangedLi
|
||||
|
||||
private void updateToolbarColor(final Toolbar toolbar) {
|
||||
if (toolbarColor == NO_COLOR) {
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
toolbar.setBackgroundColor(toolbarColor);
|
||||
|
@ -0,0 +1,96 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.customtabs;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.customtabs.CustomTabsIntent;
|
||||
|
||||
/**
|
||||
* A utility class for CustomTabsActivity to extract information from intent.
|
||||
* For example, this class helps to extract exit-animation resource id.
|
||||
*/
|
||||
class IntentUtil {
|
||||
|
||||
public static final int NO_ANIMATION_RESOURCE = -1;
|
||||
|
||||
// Hidden constant values from ActivityOptions.java
|
||||
private static final String PREFIX = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||
? "android:activity."
|
||||
: "android:";
|
||||
private static final String KEY_PACKAGE_NAME = PREFIX + "packageName";
|
||||
private static final String KEY_ANIM_ENTER_RES_ID = PREFIX + "animEnterRes";
|
||||
private static final String KEY_ANIM_EXIT_RES_ID = PREFIX + "animExitRes";
|
||||
|
||||
/**
|
||||
* To get package name of 3rd-party-app from an intent.
|
||||
* If the app defined extra exit-animation to use, it should also provide its package name
|
||||
* to get correct animation resource.
|
||||
*
|
||||
* @param intent which to launch a Custom-Tabs-Activity
|
||||
* @return package name, if the intent defined extra exit-animation bundle. Otherwise, null.
|
||||
*/
|
||||
static String getAnimationPackageName(@NonNull Intent intent) {
|
||||
final Bundle bundle = getAnimationBundle(intent);
|
||||
return (bundle == null) ? null : bundle.getString(KEY_PACKAGE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* To check whether the intent has necessary information to apply customize exit-animation.
|
||||
*
|
||||
* @param intent which to launch a Custom-Tabs-Activity
|
||||
* @return true, if the intent has necessary information.
|
||||
*/
|
||||
static boolean hasExitAnimation(@NonNull Intent intent) {
|
||||
final Bundle bundle = getAnimationBundle(intent);
|
||||
return (bundle != null)
|
||||
&& (getAnimationPackageName(intent) != null)
|
||||
&& (getEnterAnimationRes(intent) != NO_ANIMATION_RESOURCE)
|
||||
&& (getExitAnimationRes(intent) != NO_ANIMATION_RESOURCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* To get enter-animation resource id of 3rd-party-app from an intent.
|
||||
* If the app defined extra exit-animation to use, it should also provide its animation resource
|
||||
* id.
|
||||
*
|
||||
* @param intent which to launch a Custom-Tabs-Activity
|
||||
* @return animation resource id if any; otherwise, NO_ANIMATION_RESOURCE;
|
||||
*/
|
||||
static int getEnterAnimationRes(@NonNull Intent intent) {
|
||||
final Bundle bundle = getAnimationBundle(intent);
|
||||
return (bundle == null)
|
||||
? NO_ANIMATION_RESOURCE
|
||||
: bundle.getInt(KEY_ANIM_ENTER_RES_ID, NO_ANIMATION_RESOURCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* To get exit-animation resource id of 3rd-party-app from an intent.
|
||||
* If the app defined extra exit-animation to use, it should also provide its animation resource
|
||||
* id.
|
||||
*
|
||||
* @param intent which to launch a Custom-Tabs-Activity
|
||||
* @return animation resource id if any; otherwise, NO_ANIMATION_RESOURCE.
|
||||
*/
|
||||
static int getExitAnimationRes(@NonNull Intent intent) {
|
||||
final Bundle bundle = getAnimationBundle(intent);
|
||||
return (bundle == null)
|
||||
? NO_ANIMATION_RESOURCE
|
||||
: bundle.getInt(KEY_ANIM_EXIT_RES_ID, NO_ANIMATION_RESOURCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* To extract extra exit-animation bundle from an intent.
|
||||
*
|
||||
* @param intent which to launch a Custom-Tabs-Activity
|
||||
* @return bundle for extra exit-animation, if any. Otherwise, null.
|
||||
*/
|
||||
private static Bundle getAnimationBundle(@NonNull Intent intent) {
|
||||
return intent.getBundleExtra(CustomTabsIntent.EXTRA_EXIT_ANIMATION_BUNDLE);
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -31,6 +32,9 @@ import java.util.zip.CRC32;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONArray;
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.util.HardwareUtils;
|
||||
import org.mozilla.gecko.util.ProxySelector;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
@ -375,7 +379,10 @@ public class SwitchBoard {
|
||||
*/
|
||||
@Nullable private static String readFromUrlGET(URL url) {
|
||||
try {
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
HttpURLConnection connection = (HttpURLConnection) ProxySelector.openConnectionWithProxy(url.toURI());
|
||||
connection.setRequestProperty("User-Agent", HardwareUtils.isTablet() ?
|
||||
AppConstants.USER_AGENT_FENNEC_TABLET :
|
||||
AppConstants.USER_AGENT_FENNEC_MOBILE);
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setUseCaches(false);
|
||||
|
||||
@ -390,7 +397,7 @@ public class SwitchBoard {
|
||||
bufferReader.close();
|
||||
|
||||
return resultContent.toString();
|
||||
} catch (IOException e) {
|
||||
} catch (IOException | URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
@ -352,6 +352,7 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
|
||||
'CustomEditText.java',
|
||||
'customtabs/CustomTabsActivity.java',
|
||||
'customtabs/GeckoCustomTabsService.java',
|
||||
'customtabs/IntentUtil.java',
|
||||
'DataReportingNotification.java',
|
||||
'db/AbstractPerProfileDatabaseProvider.java',
|
||||
'db/AbstractTransactionalProvider.java',
|
||||
|
@ -0,0 +1,94 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
package org.mozilla.gecko.customtabs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.support.annotation.AnimRes;
|
||||
import android.support.customtabs.CustomTabsIntent;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.internal.util.reflection.Whitebox;
|
||||
import org.mozilla.gecko.background.testhelpers.TestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@RunWith(TestRunner.class)
|
||||
public class TestCustomTabsActivity {
|
||||
|
||||
private static final String THIRD_PARTY_PACKAGE_NAME = "mozilla.unit.test";
|
||||
private Context spyContext; // 3rd party app context
|
||||
private CustomTabsActivity spyActivity;
|
||||
|
||||
@AnimRes
|
||||
private final int enterRes = 0x123; // arbitrary number as animation resource id
|
||||
@AnimRes
|
||||
private final int exitRes = 0x456; // arbitrary number as animation resource id
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
spyContext = spy(RuntimeEnvironment.application);
|
||||
doReturn(THIRD_PARTY_PACKAGE_NAME).when(spyContext).getPackageName();
|
||||
|
||||
spyActivity = spy(new CustomTabsActivity());
|
||||
|
||||
final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
|
||||
builder.setExitAnimations(spyContext, enterRes, exitRes);
|
||||
final Intent i = builder.build().intent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activity should not call overridePendingTransition if custom animation does not exist.
|
||||
*/
|
||||
@Test
|
||||
public void testFinishWithoutCustomAnimation() {
|
||||
final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
|
||||
final Intent i = builder.build().intent;
|
||||
|
||||
doReturn(i).when(spyActivity).getIntent();
|
||||
|
||||
spyActivity.finish();
|
||||
verify(spyActivity, times(0)).overridePendingTransition(anyInt(), anyInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* Activity should call overridePendingTransition if custom animation exists.
|
||||
*/
|
||||
@Test
|
||||
public void testFinish() {
|
||||
final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
|
||||
builder.setExitAnimations(spyContext, enterRes, exitRes);
|
||||
final Intent i = builder.build().intent;
|
||||
|
||||
doReturn(i).when(spyActivity).getIntent();
|
||||
|
||||
spyActivity.finish();
|
||||
verify(spyActivity, times(1)).overridePendingTransition(eq(enterRes), eq(exitRes));
|
||||
}
|
||||
|
||||
/**
|
||||
* To get 3rd party app's package name, if custom animation exists.
|
||||
*/
|
||||
@Test
|
||||
public void testGetPackageName() {
|
||||
final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
|
||||
builder.setExitAnimations(spyContext, enterRes, exitRes);
|
||||
final Intent i = builder.build().intent;
|
||||
|
||||
doReturn(i).when(spyActivity).getIntent();
|
||||
Whitebox.setInternalState(spyActivity, "usingCustomAnimation", true);
|
||||
|
||||
Assert.assertEquals(THIRD_PARTY_PACKAGE_NAME, spyActivity.getPackageName());
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
package org.mozilla.gecko.customtabs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.support.annotation.AnimRes;
|
||||
import android.support.customtabs.CustomTabsIntent;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mozilla.gecko.background.testhelpers.TestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
@RunWith(TestRunner.class)
|
||||
public class TestIntentUtil {
|
||||
|
||||
private static final String THIRD_PARTY_PACKAGE_NAME = "mozilla.unit.test";
|
||||
private Context spyContext; // 3rd party app context
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
spyContext = spy(RuntimeEnvironment.application);
|
||||
doReturn(THIRD_PARTY_PACKAGE_NAME).when(spyContext).getPackageName();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntentWithCustomAnimation() {
|
||||
@AnimRes final int enterRes = 0x123; // arbitrary number as animation resource id
|
||||
@AnimRes final int exitRes = 0x456; // arbitrary number as animation resource id
|
||||
|
||||
final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
|
||||
builder.setExitAnimations(spyContext, enterRes, exitRes);
|
||||
final Intent i = builder.build().intent;
|
||||
|
||||
Assert.assertEquals(true, IntentUtil.hasExitAnimation(i));
|
||||
Assert.assertEquals(THIRD_PARTY_PACKAGE_NAME, IntentUtil.getAnimationPackageName(i));
|
||||
Assert.assertEquals(enterRes, IntentUtil.getEnterAnimationRes(i));
|
||||
Assert.assertEquals(exitRes, IntentUtil.getExitAnimationRes(i));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntentWithoutCustomAnimation() {
|
||||
final CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
|
||||
final Intent i = builder.build().intent;
|
||||
|
||||
Assert.assertEquals(false, IntentUtil.hasExitAnimation(i));
|
||||
Assert.assertEquals(null, IntentUtil.getAnimationPackageName(i));
|
||||
Assert.assertEquals(IntentUtil.NO_ANIMATION_RESOURCE,
|
||||
IntentUtil.getEnterAnimationRes(i));
|
||||
Assert.assertEquals(IntentUtil.NO_ANIMATION_RESOURCE,
|
||||
IntentUtil.getExitAnimationRes(i));
|
||||
}
|
||||
}
|
@ -13,3 +13,4 @@ jobs-from:
|
||||
- python-tests.yml
|
||||
- mozlint.yml
|
||||
- doc.yml
|
||||
- webidl.yml
|
||||
|
24
taskcluster/ci/source-check/webidl.yml
Normal file
24
taskcluster/ci/source-check/webidl.yml
Normal file
@ -0,0 +1,24 @@
|
||||
webidl-test/opt:
|
||||
description: WebIDL parser tests
|
||||
treeherder:
|
||||
symbol: Wp
|
||||
kind: test
|
||||
tier: 1
|
||||
platform: lint/opt
|
||||
worker-type: aws-provisioner-v1/b2gtest
|
||||
worker:
|
||||
implementation: docker-worker
|
||||
docker-image: {in-tree: "lint"}
|
||||
max-run-time: 1800
|
||||
run:
|
||||
using: mach
|
||||
mach: webidl-parser-test --verbose
|
||||
run-on-projects:
|
||||
- integration
|
||||
- release
|
||||
when:
|
||||
files-changed:
|
||||
- 'dom/bindings/parser/runtests.py'
|
||||
- 'dom/bindings/parser/WebIDL.py'
|
||||
- 'dom/bindings/parser/tests/**'
|
||||
- 'other-licenses/ply/**'
|
@ -32,6 +32,18 @@ class SETA(object):
|
||||
# cached push_ids that failed to retrieve datetime for
|
||||
self.failed_json_push_calls = []
|
||||
|
||||
def _get_task_string(self, task_tuple):
|
||||
# convert task tuple to single task string, so the task label sent in can match
|
||||
# remove any empty parts of the tuple
|
||||
task_tuple = [x for x in task_tuple if len(x) != 0]
|
||||
|
||||
if len(task_tuple) == 0:
|
||||
return ''
|
||||
if len(task_tuple) != 3:
|
||||
return ' '.join(task_tuple)
|
||||
|
||||
return 'test-%s/%s-%s' % (task_tuple[0], task_tuple[1], task_tuple[2])
|
||||
|
||||
def query_low_value_tasks(self, project):
|
||||
# Request the set of low value tasks from the SETA service. Low value tasks will be
|
||||
# optimized out of the task graph.
|
||||
@ -47,12 +59,17 @@ class SETA(object):
|
||||
args=(url, ),
|
||||
kwargs={'timeout': 5, 'headers': headers})
|
||||
task_list = json.loads(response.content).get('jobtypes', '')
|
||||
if len(task_list) > 0:
|
||||
low_value_tasks = task_list.values()[0]
|
||||
|
||||
# Bug 1315145, disable SETA for tier-1 platforms until backfill is implemented.
|
||||
low_value_tasks = [x for x in low_value_tasks if x.find('debug') == -1]
|
||||
low_value_tasks = [x for x in low_value_tasks if x.find('asan') == -1]
|
||||
if type(task_list) == dict and len(task_list) > 0:
|
||||
if type(task_list.values()[0]) == list and len(task_list.values()[0]) > 0:
|
||||
low_value_tasks = task_list.values()[0]
|
||||
# bb job types return a list instead of a single string,
|
||||
# convert to a single string to match tc tasks format
|
||||
if type(low_value_tasks[0]) == list:
|
||||
low_value_tasks = [self._get_task_string(x) for x in low_value_tasks]
|
||||
|
||||
# ensure no build tasks slipped in, we never want to optimize out those
|
||||
low_value_tasks = [x for x in low_value_tasks if 'build' not in x.lower()]
|
||||
|
||||
# In the event of request times out, requests will raise a TimeoutError.
|
||||
except exceptions.Timeout:
|
||||
|
@ -910,33 +910,39 @@ var insertSyncLivemark = Task.async(function* (insertInfo) {
|
||||
// If we don't handle those cases by removing the conflicting keywords first,
|
||||
// the insertion will fail, and the keywords will either be wrong, or missing.
|
||||
// This function handles those cases.
|
||||
var removeConflictingKeywords = Task.async(function* (bookmarkURL, newKeyword) {
|
||||
let entryForURL = yield PlacesUtils.keywords.fetch({
|
||||
url: bookmarkURL
|
||||
});
|
||||
if (entryForURL && entryForURL.keyword !== newKeyword) {
|
||||
yield PlacesUtils.keywords.remove({
|
||||
keyword: entryForURL.keyword,
|
||||
source: SOURCE_SYNC,
|
||||
});
|
||||
// This will cause us to reupload this record for this sync, but without it,
|
||||
// we will risk data corruption.
|
||||
yield BookmarkSyncUtils.addSyncChangesForBookmarksWithURL(entryForURL.url.href);
|
||||
}
|
||||
if (!newKeyword) {
|
||||
return;
|
||||
}
|
||||
let entryForNewKeyword = yield PlacesUtils.keywords.fetch({
|
||||
keyword: newKeyword
|
||||
});
|
||||
if (entryForNewKeyword) {
|
||||
yield PlacesUtils.keywords.remove({
|
||||
keyword: entryForNewKeyword.keyword,
|
||||
source: SOURCE_SYNC,
|
||||
});
|
||||
yield BookmarkSyncUtils.addSyncChangesForBookmarksWithURL(entryForNewKeyword.url.href);
|
||||
}
|
||||
});
|
||||
function removeConflictingKeywords(bookmarkURL, newKeyword) {
|
||||
return PlacesUtils.withConnectionWrapper(
|
||||
"BookmarkSyncUtils: removeConflictingKeywords", Task.async(function* (db) {
|
||||
let entryForURL = yield PlacesUtils.keywords.fetch({
|
||||
url: bookmarkURL.href,
|
||||
});
|
||||
if (entryForURL && entryForURL.keyword !== newKeyword) {
|
||||
yield PlacesUtils.keywords.remove({
|
||||
keyword: entryForURL.keyword,
|
||||
source: SOURCE_SYNC,
|
||||
});
|
||||
// This will cause us to reupload this record for this sync, but without it,
|
||||
// we will risk data corruption.
|
||||
yield BookmarkSyncUtils.addSyncChangesForBookmarksWithURL(
|
||||
db, entryForURL.url, 1);
|
||||
}
|
||||
if (!newKeyword) {
|
||||
return;
|
||||
}
|
||||
let entryForNewKeyword = yield PlacesUtils.keywords.fetch({
|
||||
keyword: newKeyword
|
||||
});
|
||||
if (entryForNewKeyword) {
|
||||
yield PlacesUtils.keywords.remove({
|
||||
keyword: entryForNewKeyword.keyword,
|
||||
source: SOURCE_SYNC,
|
||||
});
|
||||
yield BookmarkSyncUtils.addSyncChangesForBookmarksWithURL(
|
||||
db, entryForNewKeyword.url, 1);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Sets annotations, keywords, and tags on a new bookmark. Returns a Sync
|
||||
// bookmark object.
|
||||
@ -960,7 +966,7 @@ var insertBookmarkMetadata = Task.async(function* (bookmarkItem, insertInfo) {
|
||||
}
|
||||
|
||||
if (insertInfo.keyword) {
|
||||
yield removeConflictingKeywords(bookmarkItem.url.href, insertInfo.keyword);
|
||||
yield removeConflictingKeywords(bookmarkItem.url, insertInfo.keyword);
|
||||
yield PlacesUtils.keywords.insert({
|
||||
keyword: insertInfo.keyword,
|
||||
url: bookmarkItem.url.href,
|
||||
@ -1174,7 +1180,7 @@ var updateBookmarkMetadata = Task.async(function* (oldBookmarkItem,
|
||||
|
||||
if (updateInfo.hasOwnProperty("keyword")) {
|
||||
// Unconditionally remove the old keyword.
|
||||
yield removeConflictingKeywords(oldBookmarkItem.url.href, updateInfo.keyword);
|
||||
yield removeConflictingKeywords(oldBookmarkItem.url, updateInfo.keyword);
|
||||
if (updateInfo.keyword) {
|
||||
yield PlacesUtils.keywords.insert({
|
||||
keyword: updateInfo.keyword,
|
||||
|
@ -715,6 +715,88 @@ add_task(function* test_update_keyword() {
|
||||
yield PlacesSyncUtils.bookmarks.reset();
|
||||
});
|
||||
|
||||
add_task(function* test_conflicting_keywords() {
|
||||
yield ignoreChangedRoots();
|
||||
|
||||
do_print("Insert bookmark with new keyword");
|
||||
let tbBmk = yield PlacesSyncUtils.bookmarks.insert({
|
||||
kind: "bookmark",
|
||||
syncId: makeGuid(),
|
||||
parentSyncId: "unfiled",
|
||||
url: "http://getthunderbird.com",
|
||||
keyword: "tbird",
|
||||
});
|
||||
{
|
||||
let entryByKeyword = yield PlacesUtils.keywords.fetch("tbird");
|
||||
equal(entryByKeyword.url.href, "http://getthunderbird.com/",
|
||||
"Should return new keyword entry by URL");
|
||||
let entryByURL = yield PlacesUtils.keywords.fetch({
|
||||
url: "http://getthunderbird.com",
|
||||
});
|
||||
equal(entryByURL.keyword, "tbird", "Should return new entry by keyword");
|
||||
let changes = yield PlacesSyncUtils.bookmarks.pullChanges();
|
||||
deepEqual(changes, {},
|
||||
"Should not bump change counter for new keyword entry");
|
||||
}
|
||||
|
||||
do_print("Insert bookmark with same URL and different keyword");
|
||||
let dupeTbBmk = yield PlacesSyncUtils.bookmarks.insert({
|
||||
kind: "bookmark",
|
||||
syncId: makeGuid(),
|
||||
parentSyncId: "toolbar",
|
||||
url: "http://getthunderbird.com",
|
||||
keyword: "tb",
|
||||
});
|
||||
{
|
||||
let oldKeywordByURL = yield PlacesUtils.keywords.fetch("tbird");
|
||||
ok(!oldKeywordByURL,
|
||||
"Should remove old entry when inserting bookmark with different keyword");
|
||||
let entryByKeyword = yield PlacesUtils.keywords.fetch("tb");
|
||||
equal(entryByKeyword.url.href, "http://getthunderbird.com/",
|
||||
"Should return different keyword entry by URL");
|
||||
let entryByURL = yield PlacesUtils.keywords.fetch({
|
||||
url: "http://getthunderbird.com",
|
||||
});
|
||||
equal(entryByURL.keyword, "tb", "Should return different entry by keyword");
|
||||
let changes = yield PlacesSyncUtils.bookmarks.pullChanges();
|
||||
deepEqual(Object.keys(changes).sort(), [
|
||||
tbBmk.syncId,
|
||||
dupeTbBmk.syncId,
|
||||
].sort(), "Should bump change counter for bookmarks with different keyword");
|
||||
yield setChangesSynced(changes);
|
||||
}
|
||||
|
||||
do_print("Update bookmark with different keyword");
|
||||
yield PlacesSyncUtils.bookmarks.update({
|
||||
kind: "bookmark",
|
||||
syncId: tbBmk.syncId,
|
||||
url: "http://getthunderbird.com",
|
||||
keyword: "thunderbird",
|
||||
});
|
||||
{
|
||||
let oldKeywordByURL = yield PlacesUtils.keywords.fetch("tb");
|
||||
ok(!oldKeywordByURL,
|
||||
"Should remove old entry when updating bookmark keyword");
|
||||
let entryByKeyword = yield PlacesUtils.keywords.fetch("thunderbird");
|
||||
equal(entryByKeyword.url.href, "http://getthunderbird.com/",
|
||||
"Should return updated keyword entry by URL");
|
||||
let entryByURL = yield PlacesUtils.keywords.fetch({
|
||||
url: "http://getthunderbird.com",
|
||||
});
|
||||
equal(entryByURL.keyword, "thunderbird",
|
||||
"Should return entry by updated keyword");
|
||||
let changes = yield PlacesSyncUtils.bookmarks.pullChanges();
|
||||
deepEqual(Object.keys(changes).sort(), [
|
||||
tbBmk.syncId,
|
||||
dupeTbBmk.syncId,
|
||||
].sort(), "Should bump change counter for bookmarks with updated keyword");
|
||||
yield setChangesSynced(changes);
|
||||
}
|
||||
|
||||
yield PlacesUtils.bookmarks.eraseEverything();
|
||||
yield PlacesSyncUtils.bookmarks.reset();
|
||||
});
|
||||
|
||||
add_task(function* test_update_annos() {
|
||||
let guids = yield populateTree(PlacesUtils.bookmarks.menuGuid, {
|
||||
kind: "folder",
|
||||
|
@ -75,116 +75,133 @@ var testCases = [
|
||||
domains: ["http://example.com"],
|
||||
expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
|
||||
expectedActivated: false,
|
||||
expectedHasRunningPlugin: false
|
||||
expectedHasRunningPlugin: false,
|
||||
pluginListed: true
|
||||
},
|
||||
{
|
||||
name: "Nested unknown domains",
|
||||
domains: ["http://example.com", "http://example.org"],
|
||||
expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
|
||||
expectedActivated: false,
|
||||
expectedHasRunningPlugin: false
|
||||
expectedHasRunningPlugin: false,
|
||||
pluginListed: true
|
||||
},
|
||||
{
|
||||
name: "Allowed domain",
|
||||
domains: ["http://flashallow.example.com"],
|
||||
expectedActivated: true,
|
||||
expectedHasRunningPlugin: true
|
||||
expectedHasRunningPlugin: true,
|
||||
pluginListed: true
|
||||
},
|
||||
{
|
||||
name: "Allowed nested domain",
|
||||
domains: ["http://example.com", "http://flashallow.example.com"],
|
||||
expectedActivated: true,
|
||||
expectedHasRunningPlugin: true
|
||||
expectedHasRunningPlugin: true,
|
||||
pluginListed: true
|
||||
},
|
||||
{
|
||||
name: "Subdocument of allowed domain",
|
||||
domains: ["http://flashallow.example.com", "http://example.com"],
|
||||
expectedActivated: true,
|
||||
expectedHasRunningPlugin: true
|
||||
expectedHasRunningPlugin: true,
|
||||
pluginListed: true
|
||||
},
|
||||
{
|
||||
name: "Exception to allowed domain",
|
||||
domains: ["http://exception.flashallow.example.com"],
|
||||
expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
|
||||
expectedActivated: false,
|
||||
expectedHasRunningPlugin: false
|
||||
expectedHasRunningPlugin: false,
|
||||
pluginListed: true
|
||||
},
|
||||
{
|
||||
name: "Blocked domain",
|
||||
domains: ["http://flashblock.example.com"],
|
||||
expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_SUPPRESSED,
|
||||
expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
|
||||
expectedActivated: false,
|
||||
expectedHasRunningPlugin: false
|
||||
expectedHasRunningPlugin: false,
|
||||
pluginListed: false
|
||||
},
|
||||
{
|
||||
name: "Nested blocked domain",
|
||||
domains: ["http://example.com", "http://flashblock.example.com"],
|
||||
expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_SUPPRESSED,
|
||||
expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
|
||||
expectedActivated: false,
|
||||
expectedHasRunningPlugin: false
|
||||
expectedHasRunningPlugin: false,
|
||||
pluginListed: false
|
||||
},
|
||||
{
|
||||
name: "Subdocument of blocked subdocument",
|
||||
domains: ["http://example.com", "http://flashblock.example.com", "http://example.com"],
|
||||
expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_SUPPRESSED,
|
||||
expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
|
||||
expectedActivated: false,
|
||||
expectedHasRunningPlugin: false
|
||||
expectedHasRunningPlugin: false,
|
||||
pluginListed: false
|
||||
},
|
||||
{
|
||||
name: "Blocked subdocument nested among in allowed documents",
|
||||
domains: ["http://flashallow.example.com", "http://flashblock.example.com", "http://flashallow.example.com"],
|
||||
expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_SUPPRESSED,
|
||||
expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
|
||||
expectedActivated: false,
|
||||
expectedHasRunningPlugin: false
|
||||
expectedHasRunningPlugin: false,
|
||||
pluginListed: false
|
||||
},
|
||||
{
|
||||
name: "Exception to blocked domain",
|
||||
domains: ["http://exception.flashblock.example.com"],
|
||||
expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
|
||||
expectedActivated: false,
|
||||
expectedHasRunningPlugin: false
|
||||
expectedHasRunningPlugin: false,
|
||||
pluginListed: true
|
||||
},
|
||||
{
|
||||
name: "Sub-document blocked domain in top-level context",
|
||||
domains: ["http://subdocument.example.com"],
|
||||
expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
|
||||
expectedActivated: false,
|
||||
expectedHasRunningPlugin: false
|
||||
expectedHasRunningPlugin: false,
|
||||
pluginListed: true
|
||||
},
|
||||
{
|
||||
name: "Sub-document blocked domain",
|
||||
domains: ["http://example.com", "http://subdocument.example.com"],
|
||||
expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_SUPPRESSED,
|
||||
expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
|
||||
expectedActivated: false,
|
||||
expectedHasRunningPlugin: false
|
||||
expectedHasRunningPlugin: false,
|
||||
pluginListed: false
|
||||
},
|
||||
{
|
||||
name: "Sub-document blocked subdocument of an allowed domain",
|
||||
domains: ["http://flashallow.example.com", "http://subdocument.example.com"],
|
||||
expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_SUPPRESSED,
|
||||
expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
|
||||
expectedActivated: false,
|
||||
expectedHasRunningPlugin: false
|
||||
expectedHasRunningPlugin: false,
|
||||
pluginListed: false
|
||||
},
|
||||
{
|
||||
name: "Subdocument of Sub-document blocked domain",
|
||||
domains: ["http://example.com", "http://subdocument.example.com", "http://example.com"],
|
||||
expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_SUPPRESSED,
|
||||
expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_USER_DISABLED,
|
||||
expectedActivated: false,
|
||||
expectedHasRunningPlugin: false
|
||||
expectedHasRunningPlugin: false,
|
||||
pluginListed: false
|
||||
},
|
||||
{
|
||||
name: "Sub-document exception in top-level context",
|
||||
domains: ["http://exception.subdocument.example.com"],
|
||||
expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
|
||||
expectedActivated: false,
|
||||
expectedHasRunningPlugin: false
|
||||
expectedHasRunningPlugin: false,
|
||||
pluginListed: true
|
||||
},
|
||||
{
|
||||
name: "Sub-document blocked domain exception",
|
||||
domains: ["http://example.com", "http://exception.subdocument.example.com"],
|
||||
expectedPluginFallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
|
||||
expectedActivated: false,
|
||||
expectedHasRunningPlugin: false
|
||||
expectedHasRunningPlugin: false,
|
||||
pluginListed: true
|
||||
}
|
||||
];
|
||||
|
||||
@ -212,13 +229,16 @@ function buildDocumentStructure(browser, iframeDomains) {
|
||||
});
|
||||
}
|
||||
|
||||
function getPlugin(browser, depth) {
|
||||
function getPluginInfo(browser, depth) {
|
||||
return ContentTask.spawn(browser,
|
||||
{iframeId: IFRAME_ID, depth: depth},
|
||||
function* ({iframeId, depth}) {
|
||||
let doc = content.document;
|
||||
let win = content.window;
|
||||
for (let i = 0; i < depth; ++i) {
|
||||
doc = doc.getElementById(iframeId).contentDocument;
|
||||
let frame = doc.getElementById(iframeId);
|
||||
doc = frame.contentDocument;
|
||||
win = frame.contentWindow;
|
||||
}
|
||||
|
||||
let pluginObj = doc.getElementById("testObject");
|
||||
@ -228,7 +248,8 @@ function getPlugin(browser, depth) {
|
||||
return {
|
||||
pluginFallbackType: pluginObj.pluginFallbackType,
|
||||
activated: pluginObj.activated,
|
||||
hasRunningPlugin: pluginObj.hasRunningPlugin
|
||||
hasRunningPlugin: pluginObj.hasRunningPlugin,
|
||||
listed: ("Shockwave Flash" in win.navigator.plugins)
|
||||
};
|
||||
});
|
||||
}
|
||||
@ -249,20 +270,24 @@ add_task(function* checkFlashBlockLists() {
|
||||
|
||||
yield buildDocumentStructure(tab.linkedBrowser, iframeDomains);
|
||||
|
||||
let plugin = yield getPlugin(tab.linkedBrowser, iframeDomains.length);
|
||||
let pluginInfo = yield getPluginInfo(tab.linkedBrowser, iframeDomains.length);
|
||||
|
||||
if ("expectedPluginFallbackType" in testCase) {
|
||||
is(plugin.pluginFallbackType, testCase.expectedPluginFallbackType,
|
||||
is(pluginInfo.pluginFallbackType, testCase.expectedPluginFallbackType,
|
||||
"Plugin should have the correct fallback type");
|
||||
}
|
||||
if ("expectedActivated" in testCase) {
|
||||
is(plugin.activated, testCase.expectedActivated,
|
||||
is(pluginInfo.activated, testCase.expectedActivated,
|
||||
"Plugin should have the correct activation");
|
||||
}
|
||||
if ("expectedHasRunningPlugin" in testCase) {
|
||||
is(plugin.hasRunningPlugin, testCase.expectedHasRunningPlugin,
|
||||
is(pluginInfo.hasRunningPlugin, testCase.expectedHasRunningPlugin,
|
||||
"Plugin should have the correct 'plugin running' state");
|
||||
}
|
||||
if ("pluginListed" in testCase) {
|
||||
is(pluginInfo.listed, testCase.pluginListed,
|
||||
"Plugin's existance in navigator.plugins should match expected")
|
||||
}
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
@ -285,11 +310,12 @@ add_task(function* checkFlashBlockDisabled() {
|
||||
|
||||
yield buildDocumentStructure(tab.linkedBrowser, iframeDomains);
|
||||
|
||||
let plugin = yield getPlugin(tab.linkedBrowser, iframeDomains.length);
|
||||
let pluginInfo = yield getPluginInfo(tab.linkedBrowser, iframeDomains.length);
|
||||
|
||||
// With flashblock disabled, all plugins should be activated.
|
||||
ok(plugin.activated, "Plugin should be activated");
|
||||
ok(plugin.hasRunningPlugin, "Plugin should be running");
|
||||
ok(pluginInfo.activated, "Plugin should be activated");
|
||||
ok(pluginInfo.hasRunningPlugin, "Plugin should be running");
|
||||
ok(pluginInfo.listed, "Flash should be listed in navigator.plugins");
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
const video = document.getElementById("video");
|
||||
const ccBtn = getAnonElementWithinVideoByAttribute(video, "anonid", "closedCaptionButton");
|
||||
const ttList = getAnonElementWithinVideoByAttribute(video, "anonid", "textTrackList");
|
||||
const testCases = [];
|
||||
|
||||
testCases.push(() => new Promise(resolve => {
|
||||
@ -70,12 +71,35 @@
|
||||
|
||||
SimpleTest.executeSoon(() => {
|
||||
is(ccBtn.getAttribute("enabled"), "true", "CC button should be enabled");
|
||||
caption.mode = "disabled";
|
||||
|
||||
resolve();
|
||||
});
|
||||
}));
|
||||
|
||||
testCases.push(() => new Promise(resolve => {
|
||||
synthesizeMouseAtCenter(ccBtn, {});
|
||||
|
||||
SimpleTest.executeSoon(() => {
|
||||
is(ttList.hasAttribute("hidden"), false, "Texttrack menu should show up");
|
||||
is(ttList.lastChild.getAttribute("on"), "true", "The last added item should be highlighted");
|
||||
|
||||
resolve();
|
||||
});
|
||||
}));
|
||||
|
||||
testCases.push(() => new Promise(resolve => {
|
||||
const tt = ttList.children[1];
|
||||
|
||||
isnot(tt.getAttribute("on"), "true", "Item should be off before click");
|
||||
synthesizeMouseAtCenter(tt, {});
|
||||
|
||||
SimpleTest.executeSoon(() => {
|
||||
is(tt.getAttribute("on"), "true", "Selected item should be enabled");
|
||||
is(ttList.getAttribute("hidden"), "true", "Should hide texttrack menu once clicked on an item");
|
||||
|
||||
resolve();
|
||||
});
|
||||
}));
|
||||
|
||||
function executeTestCases(tasks) {
|
||||
return tasks.reduce((promise, task) => promise.then(task), Promise.resolve());
|
||||
|
@ -1438,6 +1438,12 @@
|
||||
}
|
||||
|
||||
if (tt.index && tt.index < this.textTracksCount) {
|
||||
// Don't create items for initialized tracks. However, we
|
||||
// still need to care about mode since TextTrackManager would
|
||||
// turn on the first available track automatically.
|
||||
if (tt.mode === "showing") {
|
||||
this.changeTextTrack(tt.index);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1530,13 +1536,9 @@
|
||||
},
|
||||
|
||||
initTextTracks() {
|
||||
if (!this.isClosedCaptionAvailable) {
|
||||
this.closedCaptionButton.setAttribute("hidden", "true");
|
||||
return;
|
||||
}
|
||||
|
||||
// add 'off' button anyway as new text track might be
|
||||
// dynamically added after initialization.
|
||||
const offLabel = this.textTrackList.getAttribute("offlabel");
|
||||
|
||||
this.addNewTextTrack({
|
||||
label: offLabel,
|
||||
kind: "subtitles"
|
||||
|
@ -10,6 +10,9 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "WEBEXT_PERMISSION_PROMPTS",
|
||||
"extensions.webextPermissionPrompts", false);
|
||||
|
||||
const MSG_PROMISE_REQUEST = "WebAPIPromiseRequest";
|
||||
const MSG_PROMISE_RESULT = "WebAPIPromiseResult";
|
||||
const MSG_INSTALL_EVENT = "WebAPIInstallEvent";
|
||||
@ -239,6 +242,10 @@ class WebAPI extends APIObject {
|
||||
});
|
||||
}
|
||||
|
||||
get permissionPromptsEnabled() {
|
||||
return WEBEXT_PERMISSION_PROMPTS;
|
||||
}
|
||||
|
||||
eventListenerWasAdded(type) {
|
||||
if (this.listenerCount == 0) {
|
||||
this.broker.setAddonListener(data => {
|
||||
|
@ -104,3 +104,19 @@ add_task(testWithAPI(function*(browser) {
|
||||
compareObjects(w2, a2);
|
||||
compareObjects(w3, a3);
|
||||
}));
|
||||
|
||||
add_task(testWithAPI(function*(browser) {
|
||||
function* check(value, message) {
|
||||
let enabled = yield ContentTask.spawn(browser, null, function*() {
|
||||
return content.navigator.mozAddonManager.permissionPromptsEnabled;
|
||||
});
|
||||
is(enabled, value, message);
|
||||
}
|
||||
|
||||
const PERM = "extensions.webextPermissionPrompts";
|
||||
yield SpecialPowers.pushPrefEnv({clear: [[PERM]]});
|
||||
yield check(false, `mozAddonManager.permissionPromptsEnabled is false when ${PERM} is unset`);
|
||||
|
||||
yield SpecialPowers.pushPrefEnv({set: [[PERM, true]]});
|
||||
yield check(true, `mozAddonManager.permissionPromptsEnabled is true when ${PERM} is set`);
|
||||
}));
|
||||
|
@ -59,8 +59,6 @@ public:
|
||||
* The deque stores pointers to items.
|
||||
*/
|
||||
|
||||
class nsDequeIterator;
|
||||
|
||||
class nsDeque
|
||||
{
|
||||
typedef mozilla::fallible_t fallible_t;
|
||||
@ -156,6 +154,21 @@ public:
|
||||
*/
|
||||
void ForEach(nsDequeFunctor& aFunctor) const;
|
||||
|
||||
class ConstIterator
|
||||
{
|
||||
public:
|
||||
ConstIterator(const nsDeque& aDeque, size_t aIndex) : mDeque(aDeque), mIndex(aIndex) { }
|
||||
ConstIterator& operator++() { ++mIndex; return *this; }
|
||||
bool operator==(const ConstIterator& aOther) const { return mIndex == aOther.mIndex; }
|
||||
bool operator!=(const ConstIterator& aOther) const { return mIndex != aOther.mIndex; }
|
||||
void* operator*() const { return mDeque.ObjectAt(mIndex); }
|
||||
private:
|
||||
const nsDeque& mDeque;
|
||||
size_t mIndex;
|
||||
};
|
||||
ConstIterator begin() const { return ConstIterator(*this, 0); }
|
||||
ConstIterator end() const { return ConstIterator(*this, mSize); }
|
||||
|
||||
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
|
||||
|
||||
|
@ -13,15 +13,15 @@
|
||||
Now define the token deallocator class...
|
||||
**************************************************************/
|
||||
namespace TestNsDeque {
|
||||
|
||||
class _Dealloc: public nsDequeFunctor
|
||||
|
||||
class _Dealloc: public nsDequeFunctor
|
||||
{
|
||||
virtual void* operator()(void* aObject) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
static bool VerifyContents(const nsDeque& aDeque, const int* aContents, size_t aLength)
|
||||
static bool VerifyContents(const nsDeque& aDeque, const int* aContents, size_t aLength)
|
||||
{
|
||||
for (size_t i=0; i<aLength; ++i) {
|
||||
if (*(int*)aDeque.ObjectAt(i) != aContents[i]) {
|
||||
@ -33,9 +33,9 @@ namespace TestNsDeque {
|
||||
|
||||
class Deallocator: public nsDequeFunctor
|
||||
{
|
||||
virtual void* operator()(void* aObject)
|
||||
virtual void* operator()(void* aObject)
|
||||
{
|
||||
if (aObject)
|
||||
if (aObject)
|
||||
{
|
||||
// Set value to -1, to use in test function.
|
||||
*((int*)aObject) = -1;
|
||||
@ -47,9 +47,9 @@ namespace TestNsDeque {
|
||||
|
||||
class ForEachAdder: public nsDequeFunctor
|
||||
{
|
||||
virtual void* operator()(void* aObject)
|
||||
virtual void* operator()(void* aObject)
|
||||
{
|
||||
if (aObject)
|
||||
if (aObject)
|
||||
{
|
||||
sum += *(int*)aObject;
|
||||
}
|
||||
@ -59,10 +59,10 @@ namespace TestNsDeque {
|
||||
|
||||
private:
|
||||
int sum = 0;
|
||||
|
||||
|
||||
public:
|
||||
int GetSum() { return sum; }
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ TEST(NsDeque, OriginalTest)
|
||||
size_t i=0;
|
||||
int temp;
|
||||
nsDeque theDeque(new _Dealloc); //construct a simple one...
|
||||
|
||||
|
||||
// ints = [0...199]
|
||||
for (i=0;i<size;i++) { //initialize'em
|
||||
ints[i]=static_cast<int>(i);
|
||||
@ -312,7 +312,7 @@ TEST(NsDeque,TestEraseShouldCallDeallocator)
|
||||
|
||||
// Now check it again.
|
||||
CheckIfQueueEmpty(d);
|
||||
|
||||
|
||||
for (size_t i=0; i < NumTestValues; i++)
|
||||
{
|
||||
EXPECT_EQ(-1, *(testArray[i])) << "Erase should call deallocator: " << *(testArray[i]);
|
||||
@ -340,3 +340,27 @@ TEST(NsDeque, TestForEach)
|
||||
|
||||
d.Erase();
|
||||
}
|
||||
|
||||
TEST(NsDeque, TestRangeFor)
|
||||
{
|
||||
nsDeque d(new Deallocator());
|
||||
const size_t NumTestValues = 8;
|
||||
int sum = 0;
|
||||
|
||||
int* testArray[NumTestValues];
|
||||
for (size_t i=0; i < NumTestValues; i++)
|
||||
{
|
||||
testArray[i] = new int();
|
||||
*(testArray[i]) = i;
|
||||
sum += i;
|
||||
d.Push((void*)testArray[i]);
|
||||
}
|
||||
|
||||
int added = 0;
|
||||
for (void* ob : d) {
|
||||
added += *(int*)ob;
|
||||
}
|
||||
EXPECT_EQ(sum, added) << "Range-for should iterate over values";
|
||||
|
||||
d.Erase();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user