WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
Patch
bug-90869-20120712150146.patch (text/plain), 18.67 KB, created by
Kwonjin Jeong
on 2012-07-11 23:01:47 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Kwonjin Jeong
Created:
2012-07-11 23:01:47 PDT
Size:
18.67 KB
patch
obsolete
>Subversion Revision: 122269 >diff --git a/Source/WTF/ChangeLog b/Source/WTF/ChangeLog >index 0d6ab69d69fea8283cead66355c86c6fe759eae6..6184b33092fc8f0f6639d584580be2cdab4d1b32 100644 >--- a/Source/WTF/ChangeLog >+++ b/Source/WTF/ChangeLog >@@ -1,3 +1,25 @@ >+2012-07-11 Kwonjin Jeong <gram@company100.net> >+ >+ Add ImageDecodeWorker >+ https://bugs.webkit.org/show_bug.cgi?id=90869 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ ImageDecodeWorker is a thread pool implementation based on WorkQueue. >+ ImageDecodeWorker internally manages a pool of WorkQueues (n = number >+ of cores) and assigns one from the pool when an ImageDecodeWorker >+ instance is created. So two ImageDecodeWorkers can share one WorkQueue. >+ >+ Parallel image decoders create an instance of ImageDecodeWorker for >+ each image. So the number of ImageDecodeWorker instances can be >+ arbitrarily large, but the number of WorkQueues is limited to the >+ number of cores. This effectively utilizes the underlying cores. >+ >+ * wtf/chromium/MainThreadChromium.cpp: >+ (WTF::callFunctionObject): >+ (WTF): >+ (WTF::callOnMainThread): >+ > 2012-07-09 Kwang Yul Seo <skyul@company100.net> > > Move WorkQueue to WTF >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 8098476ffd05e3cb88b779b460d55811f8b81897..8c811579e4409f93a4240caa3259c8368f23e442 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,42 @@ >+2012-07-11 Kwonjin Jeong <gram@company100.net> >+ >+ Add ImageDecodeWorker >+ https://bugs.webkit.org/show_bug.cgi?id=90869 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ ImageDecodeWorker is a thread pool implementation based on WorkQueue. >+ ImageDecodeWorker internally manages a pool of WorkQueues (n = number >+ of cores) and assigns one from the pool when an ImageDecodeWorker >+ instance is created. So two ImageDecodeWorkers can share one WorkQueue. >+ >+ Parallel image decoders create an instance of ImageDecodeWorker for >+ each image. So the number of ImageDecodeWorker instances can be >+ arbitrarily large, but the number of WorkQueues is limited to the >+ number of cores. This effectively utilizes the underlying cores. >+ >+ * CMakeLists.txt: >+ * GNUmakefile.list.am: >+ * Target.pri: >+ * WebCore.gypi: >+ * WebCore.vcproj/WebCore.vcproj: >+ * WebCore.xcodeproj/project.pbxproj: >+ * platform/graphics/ImageDecodeWorker.cpp: Added. >+ (WebCore): >+ (WebCore::ImageDecodeWorker::acquireSharedWorker): >+ (WebCore::ImageDecodeWorker::initializeWorkQueuePool): >+ (WebCore::ImageDecodeWorker::queueIdWithMinimumAcquireCount): >+ (WebCore::ImageDecodeWorker::ImageDecodeWorker): >+ (WebCore::ImageDecodeWorker::~ImageDecodeWorker): >+ (WebCore::ImageDecodeWorker::hasInvalidationBegun): >+ (WebCore::ImageDecodeWorker::cancelableFunction): >+ (WebCore::ImageDecodeWorker::postTask): >+ (WebCore::ImageDecodeWorker::invalidate): >+ (WebCore::ImageDecodeWorker::ensureTerminate): >+ * platform/graphics/ImageDecodeWorker.h: Added. >+ (WebCore): >+ (ImageDecodeWorker): >+ > 2012-07-10 Huang Dongsung <luxtella@company100.net> > > Fix a potential bug of BitmapImage::frameCount(). >diff --git a/Source/WTF/wtf/chromium/MainThreadChromium.cpp b/Source/WTF/wtf/chromium/MainThreadChromium.cpp >index 9e6592b5715679ae2bc9a6d1a0b6f3b8c9e18e81..ae5fc8196b1ca69fa2a1514ee286c13f9e724203 100644 >--- a/Source/WTF/wtf/chromium/MainThreadChromium.cpp >+++ b/Source/WTF/wtf/chromium/MainThreadChromium.cpp >@@ -33,6 +33,7 @@ > > #include "Assertions.h" > #include "ChromiumThreading.h" >+#include "Functional.h" > #include "Threading.h" > > namespace WTF { >@@ -54,6 +55,18 @@ void callOnMainThread(MainThreadFunction* function, void* context) > ChromiumThreading::callOnMainThread(function, context); > } > >+static void callFunctionObject(void* context) >+{ >+ Function<void()>* function = static_cast<Function<void()>*>(context); >+ (*function)(); >+ delete function; >+} >+ >+void callOnMainThread(const Function<void()>& function) >+{ >+ callOnMainThread(callFunctionObject, new Function<void ()>(function)); >+} >+ > void callOnMainThreadAndWait(MainThreadFunction*, void*) > { > ASSERT_NOT_REACHED(); >diff --git a/Source/WebCore/CMakeLists.txt b/Source/WebCore/CMakeLists.txt >index e0737d8fd5c83137787ddb2f010791fc927fab78..6d17219f064462061de5770956142e22d6a7053e 100644 >--- a/Source/WebCore/CMakeLists.txt >+++ b/Source/WebCore/CMakeLists.txt >@@ -1194,6 +1194,7 @@ SET(WebCore_SOURCES > platform/graphics/GraphicsTypes.cpp > platform/graphics/Image.cpp > platform/graphics/ImageBuffer.cpp >+ platform/graphics/ImageDecodeWorker.cpp > platform/graphics/ImageOrientation.cpp > platform/graphics/IntRect.cpp > platform/graphics/Path.cpp >diff --git a/Source/WebCore/GNUmakefile.list.am b/Source/WebCore/GNUmakefile.list.am >index 3781727aa6a0e4e154e9ebf7619ea9f2ce3c843c..e8dc706007363fc3595acfe98fcb6a251669c4d8 100644 >--- a/Source/WebCore/GNUmakefile.list.am >+++ b/Source/WebCore/GNUmakefile.list.am >@@ -3355,6 +3355,8 @@ webcore_sources += \ > Source/WebCore/platform/graphics/ImageBufferData.h \ > Source/WebCore/platform/graphics/Image.cpp \ > Source/WebCore/platform/graphics/Image.h \ >+ Source/WebCore/platform/graphics/ImageDecodeBuffer.cpp \ >+ Source/WebCore/platform/graphics/ImageDecodeBuffer.h \ > Source/WebCore/platform/graphics/ImageObserver.h \ > Source/WebCore/platform/graphics/ImageOrientation.cpp \ > Source/WebCore/platform/graphics/ImageOrientation.h \ >diff --git a/Source/WebCore/Target.pri b/Source/WebCore/Target.pri >index b91497553f4a0e4b934cc5af4c4a627520632f58..b8cad571624283c496904cc62f07a2e11ee707c7 100644 >--- a/Source/WebCore/Target.pri >+++ b/Source/WebCore/Target.pri >@@ -1122,6 +1122,7 @@ SOURCES += \ > platform/graphics/GraphicsTypes.cpp \ > platform/graphics/Image.cpp \ > platform/graphics/ImageBuffer.cpp \ >+ platform/graphics/ImageDecodeWorker.cpp \ > platform/graphics/ImageOrientation.cpp \ > platform/graphics/ImageSource.cpp \ > platform/graphics/IntRect.cpp \ >@@ -2298,6 +2299,7 @@ HEADERS += \ > platform/graphics/GraphicsTypes.h \ > platform/graphics/GraphicsTypes3D.h \ > platform/graphics/Image.h \ >+ platform/graphics/ImageDecodeWorker.h \ > platform/graphics/ImageOrientation.h \ > platform/graphics/ImageSource.h \ > platform/graphics/IntPoint.h \ >diff --git a/Source/WebCore/WebCore.gypi b/Source/WebCore/WebCore.gypi >index cfb82a1672d4a1a3263c53ad2f4235784782e4c8..f79c1def936b7f9626cde6739da0dd81a3b540d8 100644 >--- a/Source/WebCore/WebCore.gypi >+++ b/Source/WebCore/WebCore.gypi >@@ -382,6 +382,7 @@ > 'platform/graphics/Image.h', > 'platform/graphics/ImageBuffer.h', > 'platform/graphics/ImageBufferData.h', >+ 'platform/graphics/ImageDecodeBuffer.h', > 'platform/graphics/ImageObserver.h', > 'platform/graphics/ImageOrientation.h', > 'platform/graphics/ImageSource.h', >@@ -3495,6 +3496,7 @@ > 'platform/graphics/GraphicsTypes.cpp', > 'platform/graphics/Image.cpp', > 'platform/graphics/ImageBuffer.cpp', >+ 'platform/graphics/ImageDecodeBuffer.cpp', > 'platform/graphics/ImageOrientation.cpp', > 'platform/graphics/ImageSource.cpp', > 'platform/graphics/IntRect.cpp', >diff --git a/Source/WebCore/WebCore.vcproj/WebCore.vcproj b/Source/WebCore/WebCore.vcproj/WebCore.vcproj >index 9062c8b231f6c0a5aa3bd1a94b51f4d48fd38834..497ee8f0ba0126ddfe7eac13c94d924586c1dab0 100755 >--- a/Source/WebCore/WebCore.vcproj/WebCore.vcproj >+++ b/Source/WebCore/WebCore.vcproj/WebCore.vcproj >@@ -29298,6 +29298,14 @@ > > > </File> > <File >+ RelativePath="..\platform\graphics\ImageDecodeWorker.cpp" >+ > >+ </File> >+ <File >+ RelativePath="..\platform\graphics\ImageDecodeWorker.h" >+ > >+ </File> >+ <File > RelativePath="..\platform\graphics\ImageObserver.h" > > > </File> >diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj >index c126c95af9c995781a6a0c1e342474f24485ac50..aff6f986f4f40dd371df3709cd3576265bfecba2 100644 >--- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj >+++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj >@@ -6480,6 +6480,8 @@ > FE80DA720E9C472F000D6F75 /* JSPositionError.h in Headers */ = {isa = PBXBuildFile; fileRef = FE80DA6E0E9C472F000D6F75 /* JSPositionError.h */; }; > FFD5B97A135CC97800D5E92A /* PageVisibilityState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FFD5B977135CC97800D5E92A /* PageVisibilityState.cpp */; }; > FFD5B97B135CC97800D5E92A /* PageVisibilityState.h in Headers */ = {isa = PBXBuildFile; fileRef = FFD5B978135CC97800D5E92A /* PageVisibilityState.h */; settings = {ATTRIBUTES = (Private, ); }; }; >+ FFD6B97A135CC97800D5E92A /* ImageDecodeWorker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FFD6B96A135CC97800D5E92A /* ImageDecodeWorker.cpp */; }; >+ FFD6B97B135CC97800D5E92A /* ImageDecodeWorker.h in Headers */ = {isa = PBXBuildFile; fileRef = FFD6B96B135CC97800D5E92A /* ImageDecodeWorker.h */; }; > /* End PBXBuildFile section */ > > /* Begin PBXContainerItemProxy section */ >@@ -13815,6 +13817,8 @@ > FE80DA6E0E9C472F000D6F75 /* JSPositionError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSPositionError.h; sourceTree = "<group>"; }; > FFD5B977135CC97800D5E92A /* PageVisibilityState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PageVisibilityState.cpp; sourceTree = "<group>"; }; > FFD5B978135CC97800D5E92A /* PageVisibilityState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PageVisibilityState.h; sourceTree = "<group>"; }; >+ FFD6B96A135CC97800D5E92A /* ImageDecodeWorker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageDecodeWorker.cpp; sourceTree = "<group>"; }; >+ FFD6B97B135CC97800D5E92A /* ImageDecodeWorker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageDecodeWorker.h; sourceTree = "<group>"; }; > /* End PBXFileReference section */ > > /* Begin PBXFrameworksBuildPhase section */ >@@ -19675,6 +19679,8 @@ > 43D2597613C816F400608559 /* ImageBuffer.cpp */, > B2A10B910B3818BD00099AA4 /* ImageBuffer.h */, > 22BD9F7D1353625C009BD102 /* ImageBufferData.h */, >+ FFD6B96A135CC97800D5E92A /* ImageDecodeWorker.cpp */ >+ FFD6B96A135CC97800D5E92A /* ImageDecodeWorker.h */ > BC7F44A70B9E324E00A9D081 /* ImageObserver.h */, > A8748D7412CC3F89001FBA41 /* ImageOrientation.cpp */, > A8748D6612CC3763001FBA41 /* ImageOrientation.h */, >diff --git a/Source/WebCore/platform/graphics/ImageDecodeWorker.cpp b/Source/WebCore/platform/graphics/ImageDecodeWorker.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..9cf80fd372521630c82f4abd837f0c2cb758638b >--- /dev/null >+++ b/Source/WebCore/platform/graphics/ImageDecodeWorker.cpp >@@ -0,0 +1,138 @@ >+/* >+ * Copyright (C) 2012 Company 100 Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+#include "ImageDecodeWorker.h" >+ >+#include <limits> >+ >+#include <wtf/NumberOfCores.h> >+#include <wtf/WorkQueue.h> >+#include <wtf/text/CString.h> >+#include <wtf/text/WTFString.h> >+ >+namespace WebCore { >+ >+Vector<WorkQueue*>* ImageDecodeWorker::s_workQueuePool = 0; >+Vector<int>* ImageDecodeWorker::s_workQueueAcquireCount = 0; >+ >+PassRefPtr<ImageDecodeWorker> ImageDecodeWorker::acquireSharedWorker() >+{ >+ if (!s_workQueuePool) >+ initializeWorkQueuePool(); >+ >+ return adoptRef(new ImageDecodeWorker()); >+} >+ >+void ImageDecodeWorker::initializeWorkQueuePool() >+{ >+ const int numCores = WTF::numberOfProcessorCores(); >+ >+ s_workQueueAcquireCount = new Vector<int>(numCores, 0); >+ s_workQueuePool = new Vector<WorkQueue*>(); >+ s_workQueuePool->reserveInitialCapacity(numCores); >+ for (int i = 0; i < numCores; i++) { >+ s_workQueueAcquireCount->at(i) = 0; >+ String queueName = "ImageDecodeWorker." + String::number(i); >+ s_workQueuePool->append(new WorkQueue(queueName.ascii().data())); >+ } >+} >+ >+int ImageDecodeWorker::queueIdWithMinimumAcquireCount() >+{ >+ int minimumAcquireCount = std::numeric_limits<int>::max(); >+ int index = s_workQueuePool->size(); >+ >+ for (size_t i = 0; i < s_workQueuePool->size(); i++) >+ if (s_workQueueAcquireCount->at(i) < minimumAcquireCount) { >+ minimumAcquireCount = s_workQueueAcquireCount->at(i); >+ index = i; >+ } >+ >+ ASSERT(index < s_workQueuePool->size()); >+ return index; >+} >+ >+ImageDecodeWorker::ImageDecodeWorker() >+ : m_workerStatus(Running) >+{ >+ m_workQueueId = queueIdWithMinimumAcquireCount(); >+ m_workQueue = (*s_workQueuePool)[m_workQueueId]; >+ s_workQueueAcquireCount->at(m_workQueueId)++; >+} >+ >+ImageDecodeWorker::~ImageDecodeWorker() >+{ >+ ASSERT(m_workerStatus == InvaidationReady); >+} >+ >+bool ImageDecodeWorker::hasInvalidationBegun() >+{ >+ MutexLocker locker(m_workerStatusMutex); >+ if (m_workerStatus == InvalidationBegun) { >+ m_workerStatus = InvaidationReady; >+ m_invalidationReady.signal(); >+ } >+ return m_workerStatus != Running; >+} >+ >+void ImageDecodeWorker::cancelableFunction(PassRefPtr<ImageDecodeWorker> worker, const Function<void()>& function) >+{ >+ if (UNLIKELY(worker->hasInvalidationBegun())) >+ return; >+ >+ function(); >+} >+ >+void ImageDecodeWorker::postTask(const Function<void()>& function) >+{ >+ ASSERT(m_workerStatus == Running); >+ m_workQueue->dispatch(bind(ImageDecodeWorker::cancelableFunction, this, function)); >+} >+ >+void ImageDecodeWorker::invalidate() >+{ >+ MutexLocker locker(m_workerStatusMutex); >+ >+ if (m_workerStatus != Running) >+ return; >+ >+ m_workerStatus = InvalidationBegun; >+ >+ m_workQueue->dispatch(bind(&ImageDecodeWorker::ensureTerminate, this)); >+ while (m_workerStatus != InvaidationReady) >+ m_invalidationReady.wait(m_workerStatusMutex); >+ s_workQueueAcquireCount->at(m_workQueueId)--; >+} >+ >+void ImageDecodeWorker::ensureTerminate(PassRefPtr<ImageDecodeWorker> worker) >+{ >+ MutexLocker locker(worker->m_workerStatusMutex); >+ ASSERT(worker->m_workerStatus != Running); >+ worker->m_workerStatus = InvaidationReady; >+ worker->m_invalidationReady.signal(); >+} >+ >+} // namespace WebCore >diff --git a/Source/WebCore/platform/graphics/ImageDecodeWorker.h b/Source/WebCore/platform/graphics/ImageDecodeWorker.h >new file mode 100644 >index 0000000000000000000000000000000000000000..795f57a0408b2b801a81884e33d17da99375b289 >--- /dev/null >+++ b/Source/WebCore/platform/graphics/ImageDecodeWorker.h >@@ -0,0 +1,75 @@ >+/* >+ * Copyright (C) 2012 Company 100 Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#ifndef ImageDecodeWorker_h >+#define ImageDecodeWorker_h >+ >+#include <wtf/Functional.h> >+#include <wtf/RefCounted.h> >+#include <wtf/WorkQueue.h> >+ >+class WorkQueue; >+ >+namespace WebCore { >+ >+class ImageDecodeWorker : public ThreadSafeRefCounted<ImageDecodeWorker> { >+public: >+ static PassRefPtr<ImageDecodeWorker> acquireSharedWorker(); >+ >+ virtual ~ImageDecodeWorker(); >+ >+ void postTask(const Function<void()>&); >+ void invalidate(); >+ >+private: >+ static void initializeWorkQueuePool(); >+ static int queueIdWithMinimumAcquireCount(); >+ >+ // FIXME: These two static functions should become non-static methods. >+ // However, defining them as non-static methods causes an unintended destructor >+ // call while a Function object passed WorkQueue::dispatch() is being destructed. >+ // To avoid this problem, these functions are defined as static methods. >+ static void cancelableFunction(PassRefPtr<ImageDecodeWorker>, const Function<void()>&); >+ static void ensureTerminate(PassRefPtr<ImageDecodeWorker>); >+ >+ ImageDecodeWorker(); >+ >+ bool hasInvalidationBegun(); >+ >+ static Vector<WorkQueue*>* s_workQueuePool; >+ static Vector<int>* s_workQueueAcquireCount; >+ >+ enum WorkerStatus { Running, InvalidationBegun, InvaidationReady }; >+ Mutex m_workerStatusMutex; >+ WorkerStatus m_workerStatus; >+ ThreadCondition m_invalidationReady; >+ >+ int m_workQueueId; >+ WorkQueue* m_workQueue; >+}; >+ >+} // namespace WebCore >+ >+#endif // ImageDecodeWorker_h
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 90869
:
151656
|
151666
|
151667
|
151857
|
151866
|
151872
|
152695