WebKit Bugzilla
Attachment 373321 Details for
Bug 199381
: Pipe suport for 'cenc' init data type into CDMFairPlayStreaming
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch for landing
bug-199381-20190702075744.patch (text/plain), 18.08 KB, created by
Jer Noble
on 2019-07-02 07:57:45 PDT
(
hide
)
Description:
Patch for landing
Filename:
MIME Type:
Creator:
Jer Noble
Created:
2019-07-02 07:57:45 PDT
Size:
18.08 KB
patch
obsolete
>Subversion Revision: 246933 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 5344bad819f9d902f674c0e140911b1129c39e0b..205bbc24b6584c0a91503937a92190a4952730a6 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,48 @@ >+2019-07-01 Jer Noble <jer.noble@apple.com> >+ >+ Pipe suport for 'cenc' init data type into CDMFairPlayStreaming >+ https://bugs.webkit.org/show_bug.cgi?id=199381 >+ >+ Reviewed by Eric Carlson. >+ >+ Expose the parsing of 'cenc' init data from InitDataRegistry, so it can be used in the >+ evaluation steps inside CDMFairPlayStreaming. >+ >+ + Add a new method, extractPsshBoxesFromCenc() which returns an optional array of >+ unique_ptrs to different types of ISOPSSHBoxes. >+ + Add a peekScheme() method to ISOPSSHBox so that we can create the correct subclass >+ of ISOPSSHBox from the above method. >+ + Remove an extra definition of fairPlaySystemID() from CDMFairPlayStreaming. >+ + Add 'cenc' to the list of supported types in CDMPrivateFairPlayStreaming::vaildInitDataTypes(). >+ + Add support for 'cenc' in CDMPrivateFairPlayStreaming::supportsInitData(). >+ + Format the 'cenc' init data as an encodec-JSON structure for AVContentKeySession. >+ + Update the ISOFairPlayStreamingKeyRequestInfoBox to be a ISOFullBox. >+ + Update the box name of ISOFairPlayStreamingInitDataBox. >+ >+ * Modules/encryptedmedia/InitDataRegistry.cpp: >+ (WebCore::InitDataRegistry::extractPsshBoxesFromCenc): >+ (WebCore::InitDataRegistry::extractKeyIDsCenc): >+ (WebCore::InitDataRegistry::sanitizeCenc): >+ (WebCore::InitDataRegistry::cencName): >+ (WebCore::InitDataRegistry::keyidsName): >+ (WebCore::InitDataRegistry::webmName): >+ (WebCore::extractKeyIDsCenc): Deleted. >+ (WebCore::sanitizeCenc): Deleted. >+ * Modules/encryptedmedia/InitDataRegistry.h: >+ (WebCore::SourceBuffer::changeType): >+ * platform/graphics/avfoundation/CDMFairPlayStreaming.cpp: >+ (WebCore::validInitDataTypes): >+ (WebCore::CDMPrivateFairPlayStreaming::supportsInitData const): >+ (WebCore::CDMPrivateFairPlayStreaming::fairPlaySystemID): Deleted. >+ * platform/graphics/avfoundation/CDMFairPlayStreaming.h: >+ * platform/graphics/avfoundation/ISOFairPlayStreamingPsshBox.h: >+ (isType): >+ * platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.mm: >+ (WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::requestLicense): >+ * platform/graphics/iso/ISOProtectionSystemSpecificHeaderBox.cpp: >+ (WebCore::ISOProtectionSystemSpecificHeaderBox::peekSystemID): >+ * platform/graphics/iso/ISOProtectionSystemSpecificHeaderBox.h: >+ > 2019-06-28 Wenson Hsieh <wenson_hsieh@apple.com> > > Need a way for SPI clients to know when to avoid resizing to accommodate for the input view bounds >diff --git a/Source/WebCore/Modules/encryptedmedia/InitDataRegistry.cpp b/Source/WebCore/Modules/encryptedmedia/InitDataRegistry.cpp >index 4bf71494d9b79df1f0e20405693e561c0c70ccd8..b6fd68d672ede38778c15c6feaaf46b455e5a1cd 100644 >--- a/Source/WebCore/Modules/encryptedmedia/InitDataRegistry.cpp >+++ b/Source/WebCore/Modules/encryptedmedia/InitDataRegistry.cpp >@@ -112,7 +112,7 @@ static RefPtr<SharedBuffer> sanitizeKeyids(const SharedBuffer& buffer) > return SharedBuffer::create(jsonData.data(), jsonData.length()); > } > >-static Optional<Vector<Ref<SharedBuffer>>> extractKeyIDsCenc(const SharedBuffer& buffer) >+Optional<Vector<std::unique_ptr<ISOProtectionSystemSpecificHeaderBox>>> InitDataRegistry::extractPsshBoxesFromCenc(const SharedBuffer& buffer) > { > // 4. Common SystemID and PSSH Box Format > // https://w3c.github.io/encrypted-media/format-registry/initdata/cenc.html#common-system >@@ -120,7 +120,7 @@ static Optional<Vector<Ref<SharedBuffer>>> extractKeyIDsCenc(const SharedBuffer& > return WTF::nullopt; > > unsigned offset = 0; >- Vector<Ref<SharedBuffer>> keyIDs; >+ Vector<std::unique_ptr<ISOProtectionSystemSpecificHeaderBox>> psshBoxes; > > auto view = JSC::DataView::create(buffer.tryCreateArrayBuffer(), offset, buffer.size()); > while (auto optionalBoxType = ISOBox::peekBox(view, offset)) { >@@ -130,16 +130,44 @@ static Optional<Vector<Ref<SharedBuffer>>> extractKeyIDsCenc(const SharedBuffer& > if (boxTypeName != ISOProtectionSystemSpecificHeaderBox::boxTypeName() || boxSize > buffer.size()) > return WTF::nullopt; > >- ISOProtectionSystemSpecificHeaderBox psshBox; >- if (!psshBox.read(view, offset)) >+ auto systemID = ISOProtectionSystemSpecificHeaderBox::peekSystemID(view, offset); >+#if HAVE(FAIRPLAYSTREAMING_CENC_INITDATA) >+ if (systemID == ISOFairPlayStreamingPsshBox::fairPlaySystemID()) { >+ auto fpsPssh = std::make_unique<ISOFairPlayStreamingPsshBox>(); >+ if (!fpsPssh->read(view, offset)) >+ return WTF::nullopt; >+ psshBoxes.append(WTFMove(fpsPssh)); >+ continue; >+ } >+#else >+ UNUSED_PARAM(systemID); >+#endif >+ auto psshBox = std::make_unique<ISOProtectionSystemSpecificHeaderBox>(); >+ if (!psshBox->read(view, offset)) >+ return WTF::nullopt; >+ >+ psshBoxes.append(WTFMove(psshBox)); >+ } >+ >+ return psshBoxes; >+} >+ >+Optional<Vector<Ref<SharedBuffer>>> InitDataRegistry::extractKeyIDsCenc(const SharedBuffer& buffer) >+{ >+ Vector<Ref<SharedBuffer>> keyIDs; >+ >+ auto psshBoxes = extractPsshBoxesFromCenc(buffer); >+ if (!psshBoxes) >+ return WTF::nullopt; >+ >+ for (auto& psshBox : psshBoxes.value()) { >+ ASSERT(psshBox); >+ if (!psshBox) > return WTF::nullopt; > > #if HAVE(FAIRPLAYSTREAMING_CENC_INITDATA) >- if (psshBox.systemID() == CDMPrivateFairPlayStreaming::fairPlaySystemID()) { >- ISOFairPlayStreamingPsshBox fpsPssh; >- offset -= psshBox.size(); >- if (!fpsPssh.read(view, offset)) >- return WTF::nullopt; >+ if (is<ISOFairPlayStreamingPsshBox>(*psshBox)) { >+ ISOFairPlayStreamingPsshBox& fpsPssh = downcast<ISOFairPlayStreamingPsshBox>(*psshBox); > > FourCC scheme = fpsPssh.initDataBox().info().scheme(); > if (CDMPrivateFairPlayStreaming::validFairPlayStreamingSchemes().contains(scheme)) { >@@ -151,14 +179,14 @@ static Optional<Vector<Ref<SharedBuffer>>> extractKeyIDsCenc(const SharedBuffer& > } > #endif > >- for (auto& value : psshBox.keyIDs()) >+ for (auto& value : psshBox->keyIDs()) > keyIDs.append(SharedBuffer::create(WTFMove(value))); > } > > return keyIDs; > } > >-static RefPtr<SharedBuffer> sanitizeCenc(const SharedBuffer& buffer) >+RefPtr<SharedBuffer> InitDataRegistry::sanitizeCenc(const SharedBuffer& buffer) > { > // 4. Common SystemID and PSSH Box Format > // https://w3c.github.io/encrypted-media/format-registry/initdata/cenc.html#common-system >@@ -228,6 +256,24 @@ void InitDataRegistry::registerInitDataType(const AtomString& initDataType, Init > m_types.set(initDataType, WTFMove(callbacks)); > } > >+const AtomString& InitDataRegistry::cencName() >+{ >+ static NeverDestroyed<AtomString> sinf { MAKE_STATIC_STRING_IMPL("cenc") }; >+ return sinf; >+} >+ >+const AtomString& InitDataRegistry::keyidsName() >+{ >+ static NeverDestroyed<AtomString> sinf { MAKE_STATIC_STRING_IMPL("keyids") }; >+ return sinf; >+} >+ >+const AtomString& InitDataRegistry::webmName() >+{ >+ static NeverDestroyed<AtomString> sinf { MAKE_STATIC_STRING_IMPL("webm") }; >+ return sinf; >+} >+ > } > > #endif // ENABLE(ENCRYPTED_MEDIA) >diff --git a/Source/WebCore/Modules/encryptedmedia/InitDataRegistry.h b/Source/WebCore/Modules/encryptedmedia/InitDataRegistry.h >index 1f7930b99d20c3fd5ed47673bf6ed45b086c8416..79fe5128061485eca29f1c382616b7e486e19022 100644 >--- a/Source/WebCore/Modules/encryptedmedia/InitDataRegistry.h >+++ b/Source/WebCore/Modules/encryptedmedia/InitDataRegistry.h >@@ -38,6 +38,7 @@ > > namespace WebCore { > >+class ISOProtectionSystemSpecificHeaderBox; > class SharedBuffer; > > class InitDataRegistry { >@@ -57,6 +58,14 @@ public: > }; > void registerInitDataType(const AtomString& initDataType, InitDataTypeCallbacks&&); > >+ static const AtomString& cencName(); >+ static const AtomString& keyidsName(); >+ static const AtomString& webmName(); >+ >+ static Optional<Vector<std::unique_ptr<ISOProtectionSystemSpecificHeaderBox>>> extractPsshBoxesFromCenc(const SharedBuffer&); >+ static Optional<Vector<Ref<SharedBuffer>>> extractKeyIDsCenc(const SharedBuffer&); >+ static RefPtr<SharedBuffer> sanitizeCenc(const SharedBuffer&); >+ > private: > InitDataRegistry(); > ~InitDataRegistry(); >diff --git a/Source/WebCore/platform/graphics/avfoundation/CDMFairPlayStreaming.cpp b/Source/WebCore/platform/graphics/avfoundation/CDMFairPlayStreaming.cpp >index 21b93de8881b5de767fa04c66b120a093683719f..361a992f0c4650cf2b9628a642e97b5dba0e2a3c 100644 >--- a/Source/WebCore/platform/graphics/avfoundation/CDMFairPlayStreaming.cpp >+++ b/Source/WebCore/platform/graphics/avfoundation/CDMFairPlayStreaming.cpp >@@ -32,6 +32,7 @@ > #include "CDMKeySystemConfiguration.h" > #include "CDMRestrictions.h" > #include "CDMSessionType.h" >+#include "ISOProtectionSystemSpecificHeaderBox.h" > #include "ISOSchemeInformationBox.h" > #include "ISOSchemeTypeBox.h" > #include "ISOTrackEncryptionBox.h" >@@ -48,6 +49,10 @@ > #include "CDMInstanceFairPlayStreamingAVFObjC.h" > #endif > >+#if HAVE(FAIRPLAYSTREAMING_CENC_INITDATA) >+#include "ISOFairPlayStreamingPsshBox.h" >+#endif >+ > namespace WebCore { > > const Vector<FourCC>& CDMPrivateFairPlayStreaming::validFairPlayStreamingSchemes() >@@ -74,12 +79,6 @@ const AtomString& CDMPrivateFairPlayStreaming::skdName() > return skd; > } > >-const Vector<uint8_t>& CDMPrivateFairPlayStreaming::fairPlaySystemID() >-{ >- static NeverDestroyed<Vector<uint8_t>> systemID = Vector<uint8_t>({ 0x94, 0xCE, 0x86, 0xFB, 0x07, 0xFF, 0x4F, 0x43, 0xAD, 0xB8, 0x93, 0xD2, 0xFA, 0x96, 0x8C, 0xA2 }); >- return systemID; >-} >- > static Vector<Ref<SharedBuffer>> extractSinfData(const SharedBuffer& buffer) > { > // JSON of the format: "{ sinf: [ <base64-encoded-string> ] }" >@@ -205,6 +204,9 @@ static const HashSet<AtomString>& validInitDataTypes() > static NeverDestroyed<HashSet<AtomString>> validTypes = HashSet<AtomString>({ > CDMPrivateFairPlayStreaming::sinfName(), > CDMPrivateFairPlayStreaming::skdName(), >+#if HAVE(FAIRPLAYSTREAMING_CENC_INITDATA) >+ InitDataRegistry::cencName(), >+#endif > }); > return validTypes; > } >@@ -373,6 +375,18 @@ bool CDMPrivateFairPlayStreaming::supportsInitData(const AtomString& initDataTyp > }); > } > >+#if HAVE(FAIRPLAYSTREAMING_CENC_INITDATA) >+ if (initDataType == InitDataRegistry::cencName()) { >+ auto psshBoxes = InitDataRegistry::extractPsshBoxesFromCenc(initData); >+ if (!psshBoxes) >+ return false; >+ >+ return WTF::anyOf(psshBoxes.value(), [](auto& psshBox) { >+ return is<ISOFairPlayStreamingPsshBox>(*psshBox); >+ }); >+ } >+#endif >+ > if (initDataType == skdName()) > return true; > >diff --git a/Source/WebCore/platform/graphics/avfoundation/CDMFairPlayStreaming.h b/Source/WebCore/platform/graphics/avfoundation/CDMFairPlayStreaming.h >index 4c8a6ce0aced56902a7d1d5c08d42711c28a4d75..8918e80e0c390d7e8daca19039479a0d0f695ad8 100644 >--- a/Source/WebCore/platform/graphics/avfoundation/CDMFairPlayStreaming.h >+++ b/Source/WebCore/platform/graphics/avfoundation/CDMFairPlayStreaming.h >@@ -78,7 +78,6 @@ public: > static RefPtr<SharedBuffer> sanitizeSkd(const SharedBuffer&); > > static const Vector<FourCC>& validFairPlayStreamingSchemes(); >- static const Vector<uint8_t>& fairPlaySystemID(); > }; > > } >diff --git a/Source/WebCore/platform/graphics/avfoundation/ISOFairPlayStreamingPsshBox.h b/Source/WebCore/platform/graphics/avfoundation/ISOFairPlayStreamingPsshBox.h >index 3a4eee710d9a93a9169c7933808484ca386821e5..f6b892ba90f83e16e2834677216699e7577b7543 100644 >--- a/Source/WebCore/platform/graphics/avfoundation/ISOFairPlayStreamingPsshBox.h >+++ b/Source/WebCore/platform/graphics/avfoundation/ISOFairPlayStreamingPsshBox.h >@@ -42,7 +42,7 @@ private: > FourCC m_scheme; > }; > >-class WEBCORE_EXPORT ISOFairPlayStreamingKeyRequestInfoBox : public ISOBox { >+class WEBCORE_EXPORT ISOFairPlayStreamingKeyRequestInfoBox : public ISOFullBox { > public: > static FourCC boxTypeName() { return "fkri"; } > >@@ -108,7 +108,7 @@ private: > > class WEBCORE_EXPORT ISOFairPlayStreamingInitDataBox : public ISOBox { > public: >- static FourCC boxTypeName() { return "fps "; } >+ static FourCC boxTypeName() { return "fpsdq"; } > > const ISOFairPlayStreamingInfoBox& info() const { return m_info; } > const Vector<ISOFairPlayStreamingKeyRequestBox>& requests() const { return m_requests; } >@@ -133,3 +133,7 @@ private: > }; > > } >+ >+SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ISOFairPlayStreamingPsshBox) \ >+static bool isType(const WebCore::ISOProtectionSystemSpecificHeaderBox& psshBox) { return psshBox.systemID() == WebCore::ISOFairPlayStreamingPsshBox::fairPlaySystemID(); } >+SPECIALIZE_TYPE_TRAITS_END() >diff --git a/Source/WebCore/platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.mm b/Source/WebCore/platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.mm >index bd8a6153de3a919b702a3275da16ed37071dce56..dc7142b065de693ef65a8f953ffc9548d369868f 100644 >--- a/Source/WebCore/platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.mm >+++ b/Source/WebCore/platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.mm >@@ -31,6 +31,7 @@ > #import "CDMFairPlayStreaming.h" > #import "CDMKeySystemConfiguration.h" > #import "CDMMediaCapability.h" >+#import "InitDataRegistry.h" > #import "NotImplemented.h" > #import "SharedBuffer.h" > #import "TextDecoder.h" >@@ -39,6 +40,7 @@ > #import <pal/spi/mac/AVFoundationSPI.h> > #import <wtf/Algorithms.h> > #import <wtf/FileSystem.h> >+#import <wtf/text/Base64.h> > #import <wtf/text/StringHash.h> > > #import <pal/cocoa/AVFoundationSoftLink.h> >@@ -318,6 +320,12 @@ void CDMInstanceSessionFairPlayStreamingAVFObjC::requestLicense(LicenseType lice > initializationData = initData->createNSData(); > else if (initDataType == CDMPrivateFairPlayStreaming::skdName()) > identifier = adoptNS([[NSString alloc] initWithData:initData->createNSData().get() encoding:NSUTF8StringEncoding]); >+#if HAVE(FAIRPLAYSTREAMING_CENC_INITDATA) >+ else if (initDataType == InitDataRegistry::cencName()) { >+ String psshString = base64Encode(initData->data(), initData->size()); >+ initializationData = [NSJSONSerialization dataWithJSONObject:@{ @"pssh": (NSString*)psshString } options:NSJSONWritingPrettyPrinted error:nil]; >+ } >+#endif > else { > callback(SharedBuffer::create(), emptyString(), false, Failed); > return; >diff --git a/Source/WebCore/platform/graphics/iso/ISOProtectionSystemSpecificHeaderBox.cpp b/Source/WebCore/platform/graphics/iso/ISOProtectionSystemSpecificHeaderBox.cpp >index 6e4b64fb4200bb090c055877bc96b512d551f98a..15889c4ea5485ff2c6b5204d32e838f2ac389316 100644 >--- a/Source/WebCore/platform/graphics/iso/ISOProtectionSystemSpecificHeaderBox.cpp >+++ b/Source/WebCore/platform/graphics/iso/ISOProtectionSystemSpecificHeaderBox.cpp >@@ -34,6 +34,17 @@ using JSC::DataView; > > namespace WebCore { > >+Optional<Vector<uint8_t>> ISOProtectionSystemSpecificHeaderBox::peekSystemID(JSC::DataView& view, unsigned offset) >+{ >+ auto peekResult = ISOBox::peekBox(view, offset); >+ if (!peekResult || peekResult.value().first != boxTypeName()) >+ return WTF::nullopt; >+ >+ ISOProtectionSystemSpecificHeaderBox psshBox; >+ psshBox.parse(view, offset); >+ return psshBox.systemID(); >+} >+ > bool ISOProtectionSystemSpecificHeaderBox::parse(DataView& view, unsigned& offset) > { > if (!ISOFullBox::parse(view, offset)) >diff --git a/Source/WebCore/platform/graphics/iso/ISOProtectionSystemSpecificHeaderBox.h b/Source/WebCore/platform/graphics/iso/ISOProtectionSystemSpecificHeaderBox.h >index 79dc4938ed36021a6a045af076131e6c3158f6f8..112780abcd5f2f9eda2a72756520f9ae81770f3a 100644 >--- a/Source/WebCore/platform/graphics/iso/ISOProtectionSystemSpecificHeaderBox.h >+++ b/Source/WebCore/platform/graphics/iso/ISOProtectionSystemSpecificHeaderBox.h >@@ -36,6 +36,8 @@ public: > using KeyID = Vector<uint8_t>; > static FourCC boxTypeName() { return "pssh"; } > >+ static Optional<Vector<uint8_t>> peekSystemID(JSC::DataView&, unsigned offset); >+ > Vector<uint8_t> systemID() const { return m_systemID; } > Vector<KeyID> keyIDs() const { return m_keyIDs; } > Vector<uint8_t> data() const { return m_data; } >diff --git a/Tools/ChangeLog b/Tools/ChangeLog >index 84cd264a72874f1497791b7a9026616586787963..ca8b875879e4d7a06cf9557f6f927eb7681678a6 100644 >--- a/Tools/ChangeLog >+++ b/Tools/ChangeLog >@@ -1,3 +1,13 @@ >+2019-07-01 Jer Noble <jer.noble@apple.com> >+ >+ Pipe suport for 'cenc' init data type into CDMFairPlayStreaming >+ https://bugs.webkit.org/show_bug.cgi?id=199381 >+ <rdar://problem/52483103> >+ >+ Reviewed by Eric Carlson. >+ >+ * TestWebKitAPI/Tests/WebCore/ISOBox.cpp: >+ > 2019-06-28 Jer Noble <jer.noble@apple.com> > > Add new -[WKWebView _closeAllMediaPresentations] SPI >diff --git a/Tools/TestWebKitAPI/Tests/WebCore/ISOBox.cpp b/Tools/TestWebKitAPI/Tests/WebCore/ISOBox.cpp >index 0998764b003a3796e9ddd9148f69a3421e588aec..9617b015ab9efd39501c236aaaa42cc60ed71645 100644 >--- a/Tools/TestWebKitAPI/Tests/WebCore/ISOBox.cpp >+++ b/Tools/TestWebKitAPI/Tests/WebCore/ISOBox.cpp >@@ -74,7 +74,7 @@ TEST(ISOBox, ISOProtectionSchemeInfoBox) > ASSERT_EQ(defaultIV, trackEncryptionBox->defaultConstantIV()); > } > >-static const char* base64EncodedPsshWithAssetId = "AAAAqHBzc2gAAAAAlM6G+wf/T0OtuJPS+paMogAAAIgAAACIZnBzZAAAABBmcHNpAAAAAGNlbmMAAAA4ZnBzawAAABhma3JpAAAAAAAAAAAAAAAAAAAAAQAAABhma2FpAAAAAAAAAAAAAAAAAAAA8QAAADhmcHNrAAAAGGZrcmkAAAAAAAAAAAAAAAAAAAACAAAAGGZrYWkAAAAAAAAAAAAAAAAAAADy"; >+static const char* base64EncodedPsshWithAssetId = "AAAAsHBzc2gAAAAAlM6G+wf/T0OtuJPS+paMogAAAJAAAACQZnBzZAAAABBmcHNpAAAAAGNlbmMAAAA8ZnBzawAAABxma3JpAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAYZmthaQAAAAAAAAAAAAAAAAAAAPEAAAA8ZnBzawAAABxma3JpAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAYZmthaQAAAAAAAAAAAAAAAAAAAPI="; > > TEST(ISOBox, ISOFairPlayStreamingPsshBox) > {
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 199381
:
373268
|
373321
|
373324
|
373330