diff options
Diffstat (limited to 'gfx/gl/GLContextFeatures.cpp')
-rw-r--r-- | gfx/gl/GLContextFeatures.cpp | 642 |
1 files changed, 642 insertions, 0 deletions
diff --git a/gfx/gl/GLContextFeatures.cpp b/gfx/gl/GLContextFeatures.cpp new file mode 100644 index 0000000000..b66e6b6bbf --- /dev/null +++ b/gfx/gl/GLContextFeatures.cpp @@ -0,0 +1,642 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "GLContext.h" +#include "nsPrintfCString.h" + +namespace mozilla { +namespace gl { + +const size_t kMAX_EXTENSION_GROUP_SIZE = 5; + +enum class GLVersion : uint32_t { + NONE = 0, // Feature is not supported natively by GL + GL1_2 = 120, + GL1_3 = 130, + GL2 = 200, + GL2_1 = 210, + GL3 = 300, + GL3_1 = 310, + GL3_2 = 320, + GL3_3 = 330, + GL4 = 400, + GL4_1 = 410, + GL4_2 = 420, + GL4_3 = 430, +}; + +enum class GLESVersion : uint32_t { + NONE = 0, // Feature is not support natively by GL ES + ES2 = 200, + ES3 = 300, + ES3_1 = 310, + ES3_2 = 320, +}; + +// ARB_ES2_compatibility is natively supported in OpenGL 4.1. +static const GLVersion kGLCoreVersionForES2Compat = GLVersion::GL4_1; + +// ARB_ES3_compatibility is natively supported in OpenGL 4.3. +static const GLVersion kGLCoreVersionForES3Compat = GLVersion::GL4_3; + +struct FeatureInfo { + const char* mName; + + /* The (desktop) OpenGL version that provides this feature */ + GLVersion mOpenGLVersion; + + /* The OpenGL ES version that provides this feature */ + GLESVersion mOpenGLESVersion; + + /* If there is an ARB extension, and its function symbols are + * not decorated with an ARB suffix, then its extension ID should go + * here, and NOT in mExtensions. For example, ARB_vertex_array_object + * functions do not have an ARB suffix, because it is an extension that + * was created to match core GL functionality and will never differ. + * Some ARB extensions do have a suffix, if they were created before + * a core version of the functionality existed. + * + * If there is no such ARB extension, pass 0 (GLContext::Extension_None) + */ + GLContext::GLExtensions mARBExtensionWithoutARBSuffix; + + /* Extensions that also provide this feature */ + GLContext::GLExtensions mExtensions[kMAX_EXTENSION_GROUP_SIZE]; +}; + +static const FeatureInfo sFeatureInfoArr[] = { + {"bind_buffer_offset", + GLVersion::NONE, + GLESVersion::NONE, + GLContext::Extension_None, + { + + GLContext::EXT_transform_feedback, GLContext::NV_transform_feedback2, + GLContext::Extensions_End}}, + {"blend_minmax", + GLVersion::GL2, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::EXT_blend_minmax, GLContext::Extensions_End}}, + {"clear_buffers", + GLVersion::GL3, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::Extensions_End}}, + {"copy_buffer", + GLVersion::GL3_1, + GLESVersion::ES3, + GLContext::ARB_copy_buffer, + {GLContext::Extensions_End}}, + {"depth_texture", + GLVersion::GL2, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::ARB_depth_texture, GLContext::OES_depth_texture, + // Intentionally avoid putting ANGLE_depth_texture here, + // it does not offer quite the same functionality. + GLContext::Extensions_End}}, + {"draw_buffers", + GLVersion::GL2, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::ARB_draw_buffers, GLContext::EXT_draw_buffers, + GLContext::Extensions_End}}, + {"draw_buffers_indexed", + GLVersion::GL3, + GLESVersion::ES3_2, + GLContext::Extension_None, + {GLContext::OES_draw_buffers_indexed, GLContext::Extensions_End}}, + {"draw_instanced", + GLVersion::GL3_1, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::ARB_draw_instanced, GLContext::EXT_draw_instanced, + GLContext::NV_draw_instanced, GLContext::ANGLE_instanced_arrays, + GLContext::Extensions_End}}, + {"element_index_uint", + GLVersion::GL2, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::OES_element_index_uint, GLContext::Extensions_End}}, + {"ES2_compatibility", + kGLCoreVersionForES2Compat, + GLESVersion::ES2, // OpenGL ES version + GLContext::ARB_ES2_compatibility, // no suffix on ARB extension + {GLContext::Extensions_End}}, + {"ES3_compatibility", + kGLCoreVersionForES3Compat, + GLESVersion::ES3, // OpenGL ES version + GLContext::ARB_ES3_compatibility, // no suffix on ARB extension + {GLContext::Extensions_End}}, + {"EXT_color_buffer_float", + GLVersion::GL3, + GLESVersion::ES3_2, + GLContext::Extension_None, + {GLContext::EXT_color_buffer_float, GLContext::Extensions_End}}, + {// Removes clamping for float color outputs from frag shaders. + "frag_color_float", + GLVersion::GL3, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::ARB_color_buffer_float, GLContext::EXT_color_buffer_float, + GLContext::EXT_color_buffer_half_float, + GLContext::CHROMIUM_color_buffer_float_rgba, GLContext::Extensions_End}}, + {"frag_depth", + GLVersion::GL2, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::EXT_frag_depth, GLContext::Extensions_End}}, + {// Check for just the blit framebuffer blit part of + // ARB_framebuffer_object + "framebuffer_blit", + GLVersion::GL3, + GLESVersion::ES3, + GLContext::ARB_framebuffer_object, + {GLContext::ANGLE_framebuffer_blit, GLContext::EXT_framebuffer_blit, + GLContext::NV_framebuffer_blit, GLContext::Extensions_End}}, + {// Check for just the multisample renderbuffer part of + // ARB_framebuffer_object + "framebuffer_multisample", + GLVersion::GL3, + GLESVersion::ES3, + GLContext::ARB_framebuffer_object, + {GLContext::ANGLE_framebuffer_multisample, + GLContext::APPLE_framebuffer_multisample, + GLContext::EXT_framebuffer_multisample, + GLContext::EXT_multisampled_render_to_texture, + GLContext::Extensions_End}}, + {// ARB_framebuffer_object support + "framebuffer_object", + GLVersion::GL3, + GLESVersion::ES3, + GLContext::ARB_framebuffer_object, + {GLContext::Extensions_End}}, + {// EXT_framebuffer_object/OES_framebuffer_object support + "framebuffer_object_EXT_OES", + GLVersion::GL3, + GLESVersion::ES2, + GLContext::Extension_None, + {GLContext::EXT_framebuffer_object, GLContext::OES_framebuffer_object, + GLContext::Extensions_End}}, + {"get_integer_indexed", + GLVersion::GL3, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::EXT_draw_buffers2, GLContext::Extensions_End}}, + {"get_integer64_indexed", + GLVersion::GL3_2, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::Extensions_End}}, + {"get_query_object_i64v", + GLVersion::GL3_3, + GLESVersion::NONE, + GLContext::ARB_timer_query, + {GLContext::ANGLE_timer_query, GLContext::EXT_disjoint_timer_query, + GLContext::EXT_timer_query, GLContext::Extensions_End}}, + { + "get_query_object_iv", + GLVersion::GL2, + GLESVersion::NONE, + GLContext::Extension_None, + {GLContext::Extensions_End} + /* + * XXX_get_query_object_iv only provide GetQueryObjectiv provided by + * ARB_occlusion_query (added by OpenGL 2.0). + */ + }, + {"gpu_shader4", + GLVersion::GL3, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::EXT_gpu_shader4, GLContext::Extensions_End}}, + {"instanced_arrays", + GLVersion::GL3_3, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::ARB_instanced_arrays, GLContext::NV_instanced_arrays, + GLContext::ANGLE_instanced_arrays, GLContext::Extensions_End}}, + { + "instanced_non_arrays", + GLVersion::GL3_3, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::ARB_instanced_arrays, GLContext::Extensions_End} + /* This is an expanded version of `instanced_arrays` that allows for all + * enabled active attrib arrays to have non-zero divisors. + * ANGLE_instanced_arrays and NV_instanced_arrays forbid this, but GLES3 + * has no such restriction. + */ + }, + {"internalformat_query", + GLVersion::GL4_2, + GLESVersion::ES3, + GLContext::ARB_internalformat_query, + {GLContext::Extensions_End}}, + {"invalidate_framebuffer", + GLVersion::GL4_3, + GLESVersion::ES3, + GLContext::ARB_invalidate_subdata, + {GLContext::Extensions_End}}, + {"map_buffer_range", + GLVersion::GL3, + GLESVersion::ES3, + GLContext::ARB_map_buffer_range, + {GLContext::EXT_map_buffer_range, GLContext::Extensions_End}}, + {"multiview", + GLVersion::NONE, + GLESVersion::NONE, + GLContext::Extension_None, + {GLContext::ANGLE_multiview, GLContext::OVR_multiview2, + GLContext::Extensions_End}}, + { + "occlusion_query", + GLVersion::GL2, + GLESVersion::NONE, + GLContext::Extension_None, + {GLContext::Extensions_End} + // XXX_occlusion_query depend on ARB_occlusion_query (added in + // OpenGL 2.0) + }, + { + "occlusion_query_boolean", + kGLCoreVersionForES3Compat, + GLESVersion::ES3, + GLContext::ARB_ES3_compatibility, + {GLContext::EXT_occlusion_query_boolean, GLContext::Extensions_End} + /* + * XXX_occlusion_query_boolean provide ANY_SAMPLES_PASSED_CONSERVATIVE, + * but EXT_occlusion_query_boolean is only a OpenGL ES extension. But + * it is supported on desktop if ARB_ES3_compatibility because + * EXT_occlusion_query_boolean (added in OpenGL ES 3.0). + */ + }, + { + "occlusion_query2", + GLVersion::GL3_3, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::ARB_occlusion_query2, GLContext::ARB_ES3_compatibility, + GLContext::EXT_occlusion_query_boolean, GLContext::Extensions_End} + /* + * XXX_occlusion_query2 (add in OpenGL 3.3) provide ANY_SAMPLES_PASSED, + * which is provided by ARB_occlusion_query2, + * EXT_occlusion_query_boolean (added in OpenGL ES 3.0) and + * ARB_ES3_compatibility + */ + }, + {"packed_depth_stencil", + GLVersion::GL3, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::EXT_packed_depth_stencil, GLContext::OES_packed_depth_stencil, + GLContext::Extensions_End}}, + {"prim_restart", + GLVersion::GL3_1, + GLESVersion::NONE, + GLContext::Extension_None, + {// GLContext::NV_primitive_restart, // Has different enum values. + GLContext::Extensions_End}}, + {"prim_restart_fixed", + kGLCoreVersionForES3Compat, + GLESVersion::ES3, + GLContext::ARB_ES3_compatibility, + {GLContext::Extensions_End}}, + {"provoking_vertex", + GLVersion::GL3_2, + GLESVersion::NONE, + GLContext::ARB_provoking_vertex, + {GLContext::ANGLE_provoking_vertex, GLContext::EXT_provoking_vertex, + GLContext::Extensions_End}}, + {"query_counter", + GLVersion::GL3_3, + GLESVersion::NONE, + GLContext::ARB_timer_query, + {GLContext::ANGLE_timer_query, GLContext::EXT_disjoint_timer_query, + // EXT_timer_query does NOT support GL_TIMESTAMP retrieval with + // QueryCounter. + GLContext::Extensions_End}}, + { + "query_objects", + GLVersion::GL2, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::ANGLE_timer_query, GLContext::EXT_disjoint_timer_query, + GLContext::EXT_occlusion_query_boolean, GLContext::Extensions_End} + /* + * XXX_query_objects only provide entry points commonly supported by + * ARB_occlusion_query (added in OpenGL 2.0), + * EXT_occlusion_query_boolean (added in OpenGL ES 3.0), and + * ARB_timer_query (added in OpenGL 3.3) + */ + }, + {"query_time_elapsed", + GLVersion::GL3_3, + GLESVersion::NONE, + GLContext::ARB_timer_query, + {GLContext::ANGLE_timer_query, GLContext::EXT_disjoint_timer_query, + GLContext::EXT_timer_query, GLContext::Extensions_End}}, + {"read_buffer", + GLVersion::GL2, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::Extensions_End}}, + {"renderbuffer_color_float", + GLVersion::GL3, + GLESVersion::ES3_2, + GLContext::Extension_None, + {GLContext::ARB_texture_float, GLContext::EXT_color_buffer_float, + GLContext::CHROMIUM_color_buffer_float_rgba, GLContext::Extensions_End}}, + {"renderbuffer_color_half_float", + GLVersion::GL3, + GLESVersion::ES3_2, + GLContext::Extension_None, + {GLContext::ARB_texture_float, GLContext::EXT_color_buffer_float, + GLContext::EXT_color_buffer_half_float, GLContext::Extensions_End}}, + {"robust_buffer_access_behavior", + GLVersion::NONE, + GLESVersion::NONE, + GLContext::Extension_None, + {GLContext::ARB_robust_buffer_access_behavior, + GLContext::KHR_robust_buffer_access_behavior, GLContext::Extensions_End}}, + {"robustness", + GLVersion::NONE, + GLESVersion::NONE, + GLContext::Extension_None, + {GLContext::ARB_robustness, GLContext::EXT_robustness, + GLContext::KHR_robustness, GLContext::Extensions_End}}, + {"sRGB", + GLVersion::GL3, + GLESVersion::ES3, + GLContext::ARB_framebuffer_sRGB, + {GLContext::EXT_sRGB, GLContext::EXT_framebuffer_sRGB, + GLContext::Extensions_End}}, + {"sampler_objects", + GLVersion::GL3_3, + GLESVersion::ES3, + GLContext::ARB_sampler_objects, + {GLContext::Extensions_End}}, + {"seamless_cube_map_opt_in", + GLVersion::GL3_2, + GLESVersion::NONE, + GLContext::ARB_seamless_cube_map, + {GLContext::Extensions_End}}, + {"shader_texture_lod", + GLVersion::GL3, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::ARB_shader_texture_lod, GLContext::EXT_shader_texture_lod, + GLContext::Extensions_End}}, + {// Do we have separate DRAW and READ framebuffer bind points? + "split_framebuffer", + GLVersion::GL3, + GLESVersion::ES3, + GLContext::ARB_framebuffer_object, + {GLContext::ANGLE_framebuffer_blit, + GLContext::APPLE_framebuffer_multisample, GLContext::EXT_framebuffer_blit, + GLContext::NV_framebuffer_blit, GLContext::Extensions_End}}, + {"standard_derivatives", + GLVersion::GL2, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::OES_standard_derivatives, GLContext::Extensions_End}}, + {"sync", + GLVersion::GL3_2, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::ARB_sync, GLContext::APPLE_sync, GLContext::Extensions_End}}, + {"texture_3D", + GLVersion::GL1_2, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::OES_texture_3D, GLContext::Extensions_End}}, + {"texture_3D_compressed", + GLVersion::GL1_3, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::ARB_texture_compression, GLContext::OES_texture_3D, + GLContext::Extensions_End}}, + {"texture_3D_copy", + GLVersion::GL1_2, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::EXT_copy_texture, GLContext::OES_texture_3D, + GLContext::Extensions_End}}, + {"texture_compression_bptc", + GLVersion::GL4_2, + GLESVersion::NONE, + GLContext::Extension_None, + {GLContext::ARB_texture_compression_bptc, + GLContext::EXT_texture_compression_bptc, GLContext::Extensions_End}}, + {"texture_compression_rgtc", + GLVersion::GL3, + GLESVersion::NONE, + GLContext::Extension_None, + {GLContext::ARB_texture_compression_rgtc, + GLContext::EXT_texture_compression_rgtc, GLContext::Extensions_End}}, + {"texture_float", + GLVersion::GL3, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::ARB_texture_float, GLContext::OES_texture_float, + GLContext::Extensions_End}}, + {"texture_float_linear", + GLVersion::GL3_1, + GLESVersion::NONE, + GLContext::Extension_None, + {GLContext::ARB_texture_float, GLContext::OES_texture_float_linear, + GLContext::Extensions_End}}, + { + "texture_half_float", + GLVersion::GL3, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::ARB_half_float_pixel, GLContext::ARB_texture_float, + GLContext::NV_half_float, GLContext::Extensions_End} + /** + * We are not including OES_texture_half_float in this feature, because: + * GL_HALF_FLOAT = 0x140B + * GL_HALF_FLOAT_ARB = 0x140B == GL_HALF_FLOAT + * GL_HALF_FLOAT_NV = 0x140B == GL_HALF_FLOAT + * GL_HALF_FLOAT_OES = 0x8D61 != GL_HALF_FLOAT + * WebGL handles this specifically with an OES_texture_half_float check. + */ + }, + {"texture_half_float_linear", + GLVersion::GL3_1, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::ARB_half_float_pixel, GLContext::ARB_texture_float, + GLContext::NV_half_float, GLContext::OES_texture_half_float_linear, + GLContext::Extensions_End}}, + {"texture_non_power_of_two", + GLVersion::GL2, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::ARB_texture_non_power_of_two, GLContext::OES_texture_npot, + GLContext::Extensions_End}}, + {"texture_norm16", + GLVersion::GL3_1, + GLESVersion::ES3_1, + GLContext::EXT_texture_norm16, + {GLContext::Extensions_End}}, + {"texture_rg", + GLVersion::GL3, + GLESVersion::ES3, + GLContext::ARB_texture_rg, + {GLContext::Extensions_End}}, + {"texture_storage", + GLVersion::GL4_2, + GLESVersion::ES3, + GLContext::ARB_texture_storage, + {/* + * Not including GL_EXT_texture_storage here because it + * doesn't guarantee glTexStorage3D, which is required for + * WebGL 2. + */ + GLContext::Extensions_End}}, + {"texture_swizzle", + GLVersion::GL3_3, + GLESVersion::ES3, + GLContext::ARB_texture_swizzle, + {GLContext::Extensions_End}}, + {"transform_feedback2", + GLVersion::GL4, + GLESVersion::ES3, + GLContext::ARB_transform_feedback2, + {GLContext::NV_transform_feedback2, GLContext::Extensions_End}}, + {"uniform_buffer_object", + GLVersion::GL3_1, + GLESVersion::ES3, + GLContext::ARB_uniform_buffer_object, + {GLContext::Extensions_End}}, + {"uniform_matrix_nonsquare", + GLVersion::GL2_1, + GLESVersion::ES3, + GLContext::Extension_None, + {GLContext::Extensions_End}}, + {"vertex_array_object", + GLVersion::GL3, + GLESVersion::ES3, + GLContext::ARB_vertex_array_object, // ARB extension + {GLContext::OES_vertex_array_object, GLContext::APPLE_vertex_array_object, + GLContext::Extensions_End}}}; + +static inline const FeatureInfo& GetFeatureInfo(GLFeature feature) { + static_assert(MOZ_ARRAY_LENGTH(sFeatureInfoArr) == size_t(GLFeature::EnumMax), + "Mismatched lengths for sFeatureInfoInfos and GLFeature enums"); + + MOZ_ASSERT(feature < GLFeature::EnumMax, + "GLContext::GetFeatureInfoInfo : unknown <feature>"); + + return sFeatureInfoArr[size_t(feature)]; +} + +static inline uint32_t ProfileVersionForFeature(GLFeature feature, + ContextProfile profile) { + MOZ_ASSERT(profile != ContextProfile::Unknown, + "GLContext::ProfileVersionForFeature : unknown <profile>"); + + const FeatureInfo& featureInfo = GetFeatureInfo(feature); + + if (profile == ContextProfile::OpenGLES) + return (uint32_t)featureInfo.mOpenGLESVersion; + + return (uint32_t)featureInfo.mOpenGLVersion; +} + +static bool IsFeaturePartOfProfileVersion(GLFeature feature, + ContextProfile profile, + unsigned int version) { + unsigned int profileVersion = ProfileVersionForFeature(feature, profile); + + /** + * if `profileVersion` is zero, it means that no version of the profile + * added support for the feature. + */ + return profileVersion && version >= profileVersion; +} + +bool GLContext::IsFeatureProvidedByCoreSymbols(GLFeature feature) { + if (IsFeaturePartOfProfileVersion(feature, mProfile, mVersion)) return true; + + if (IsExtensionSupported( + GetFeatureInfo(feature).mARBExtensionWithoutARBSuffix)) + return true; + + return false; +} + +const char* GLContext::GetFeatureName(GLFeature feature) { + return GetFeatureInfo(feature).mName; +} + +void GLContext::InitFeatures() { + for (size_t featureId = 0; featureId < size_t(GLFeature::EnumMax); + featureId++) { + GLFeature feature = GLFeature(featureId); + + if (IsFeaturePartOfProfileVersion(feature, mProfile, mVersion)) { + mAvailableFeatures[featureId] = true; + continue; + } + + mAvailableFeatures[featureId] = false; + + const FeatureInfo& featureInfo = GetFeatureInfo(feature); + + if (IsExtensionSupported(featureInfo.mARBExtensionWithoutARBSuffix)) { + mAvailableFeatures[featureId] = true; + continue; + } + + for (size_t j = 0; true; j++) { + MOZ_ASSERT(j < kMAX_EXTENSION_GROUP_SIZE, + "kMAX_EXTENSION_GROUP_SIZE too small"); + + if (featureInfo.mExtensions[j] == GLContext::Extensions_End) break; + + if (IsExtensionSupported(featureInfo.mExtensions[j])) { + mAvailableFeatures[featureId] = true; + break; + } + } + } + + if (ShouldDumpExts()) { + for (size_t featureId = 0; featureId < size_t(GLFeature::EnumMax); + featureId++) { + GLFeature feature = GLFeature(featureId); + printf_stderr("[%s] Feature::%s\n", + IsSupported(feature) ? "enabled" : "disabled", + GetFeatureName(feature)); + } + } +} + +void GLContext::MarkUnsupported(GLFeature feature) { + mAvailableFeatures[size_t(feature)] = false; + + const FeatureInfo& featureInfo = GetFeatureInfo(feature); + + for (size_t i = 0; true; i++) { + MOZ_ASSERT(i < kMAX_EXTENSION_GROUP_SIZE, + "kMAX_EXTENSION_GROUP_SIZE too small"); + + if (featureInfo.mExtensions[i] == GLContext::Extensions_End) break; + + MarkExtensionUnsupported(featureInfo.mExtensions[i]); + } + + MOZ_ASSERT(!IsSupported(feature), "GLContext::MarkUnsupported has failed!"); + + NS_WARNING( + nsPrintfCString("%s marked as unsupported", GetFeatureName(feature)) + .get()); +} + +} /* namespace gl */ +} /* namespace mozilla */ |