| Index: content_decryption_module.h | 
| diff --git a/content_decryption_module.h b/content_decryption_module.h | 
| index 958c5534536394907261818bb68c94702dfb04c2..8cd6f92e2c00d784394a30ef31882b2059f52df8 100644 | 
| --- a/content_decryption_module.h | 
| +++ b/content_decryption_module.h | 
| @@ -17,8 +17,21 @@ typedef __int64 int64_t; | 
| // Define CDM_API so that functionality implemented by the CDM module | 
| // can be exported to consumers. Note: the implementation lives in | 
| // a dynamic library even in a non-component build. | 
| +// Also define CDM_CLASS_API to export class types. We have to add | 
| +// visibility attributes to make sure virtual tables in CDM consumer | 
| +// and CDM implementation are the same. Generally, it was always a | 
| +// good idea, as there're no guarantees about that for the internal | 
| +// symbols, but it has only become a practical issue after | 
| +// introduction of LTO devirtualization. See more details on | 
| +// https://crbug.com/609564#c35 | 
| #if defined(WIN32) | 
|  | 
| +#if defined(__clang__) | 
| +#define CDM_CLASS_API [[clang::lto_visibility_public]] | 
| +#else | 
| +#define CDM_CLASS_API | 
| +#endif | 
| + | 
| #if defined(CDM_IMPLEMENTATION) | 
| #define CDM_API __declspec(dllexport) | 
| #else | 
| @@ -27,6 +40,7 @@ typedef __int64 int64_t; | 
|  | 
| #else  // defined(WIN32) | 
| #define CDM_API __attribute__((visibility("default"))) | 
| +#define CDM_CLASS_API __attribute__((visibility("default"))) | 
| #endif  // defined(WIN32) | 
|  | 
| // The version number must be rolled when the exported functions are updated! | 
| @@ -379,7 +393,7 @@ enum MessageType { | 
| // Note to implementors of this interface: | 
| // Per-origin storage and the ability for users to clear it are important. | 
| // See http://www.w3.org/TR/encrypted-media/#privacy-storedinfo. | 
| -class CDM_API FileIO { | 
| +class CDM_CLASS_API FileIO { | 
| public: | 
| // Opens the file with |file_name| for read and write. | 
| // FileIOClient::OnOpenComplete() will be called after the opening | 
| @@ -419,7 +433,7 @@ class CDM_API FileIO { | 
| // When kError is returned, the FileIO object could be in an error state. All | 
| // following calls (other than Close()) could return kError. The CDM should | 
| // still call Close() to destroy the FileIO object. | 
| -class CDM_API FileIOClient { | 
| +class CDM_CLASS_API FileIOClient { | 
| public: | 
| enum Status { | 
| kSuccess = 0, | 
| @@ -460,7 +474,7 @@ class CDM_API FileIOClient { | 
| // provided in CreateCdmInstance() to allocate any Buffer that needs to | 
| // be passed back to the caller. Implementations must call Buffer::Destroy() | 
| // when a Buffer is created that will never be returned to the caller. | 
| -class CDM_API ContentDecryptionModule_7 { | 
| +class CDM_CLASS_API ContentDecryptionModule_7 { | 
| public: | 
| static const int kVersion = 7; | 
| typedef Host_7 Host; | 
| @@ -639,7 +653,7 @@ class CDM_API ContentDecryptionModule_7 { | 
| // provided in CreateCdmInstance() to allocate any Buffer that needs to | 
| // be passed back to the caller. Implementations must call Buffer::Destroy() | 
| // when a Buffer is created that will never be returned to the caller. | 
| -class CDM_API ContentDecryptionModule_8 { | 
| +class CDM_CLASS_API ContentDecryptionModule_8 { | 
| public: | 
| static const int kVersion = 8; | 
| typedef Host_8 Host; | 
| @@ -824,7 +838,7 @@ class CDM_API ContentDecryptionModule_8 { | 
| typedef ContentDecryptionModule_8 ContentDecryptionModule; | 
|  | 
| // Represents a buffer created by Allocator implementations. | 
| -class CDM_API Buffer { | 
| +class CDM_CLASS_API Buffer { | 
| public: | 
| // Destroys the buffer in the same context as it was created. | 
| virtual void Destroy() = 0; | 
| @@ -843,7 +857,7 @@ class CDM_API Buffer { | 
| void operator=(const Buffer&); | 
| }; | 
|  | 
| -class CDM_API Host_7 { | 
| +class CDM_CLASS_API Host_7 { | 
| public: | 
| static const int kVersion = 7; | 
|  | 
| @@ -980,7 +994,7 @@ class CDM_API Host_7 { | 
| virtual ~Host_7() {} | 
| }; | 
|  | 
| -class CDM_API Host_8 { | 
| +class CDM_CLASS_API Host_8 { | 
| public: | 
| static const int kVersion = 8; | 
|  | 
| @@ -1118,7 +1132,7 @@ class CDM_API Host_8 { | 
| }; | 
|  | 
| // Represents a decrypted block that has not been decoded. | 
| -class CDM_API DecryptedBlock { | 
| +class CDM_CLASS_API DecryptedBlock { | 
| public: | 
| virtual void SetDecryptedBuffer(Buffer* buffer) = 0; | 
| virtual Buffer* DecryptedBuffer() = 0; | 
| @@ -1133,7 +1147,7 @@ class CDM_API DecryptedBlock { | 
| virtual ~DecryptedBlock() {} | 
| }; | 
|  | 
| -class CDM_API VideoFrame { | 
| +class CDM_CLASS_API VideoFrame { | 
| public: | 
| enum VideoPlane { | 
| kYPlane = 0, | 
| @@ -1176,7 +1190,7 @@ class CDM_API VideoFrame { | 
| // | 
| // |<----------------- AudioFrames ------------------>| | 
| // | audio buffer 0 | audio buffer 1 | audio buffer 2 | | 
| -class CDM_API AudioFrames { | 
| +class CDM_CLASS_API AudioFrames { | 
| public: | 
| virtual void SetFrameBuffer(Buffer* buffer) = 0; | 
| virtual Buffer* FrameBuffer() = 0; | 
|  |