1d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 2d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 3d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// found in the LICENSE file. 4d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 5d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "base/strings/stringprintf.h" 6d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "chrome/test/base/module_system_test.h" 7d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "grit/renderer_resources.h" 8d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 9d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)namespace extensions { 10d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)namespace { 11d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 12d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)class MessagingUtilsUnittest : public ModuleSystemTest { 13d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) protected: 14d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) void RegisterTestModule(const char* code) { 15d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) RegisterModule("test", base::StringPrintf( 16d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "var assert = requireNative('assert');\n" 17d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "var AssertTrue = assert.AssertTrue;\n" 18d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "var AssertFalse = assert.AssertFalse;\n" 19d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "var messagingUtils = require('messaging_utils');\n" 20d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "%s", code)); 21d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) } 22d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 23d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) private: 24d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) virtual void SetUp() OVERRIDE { 25d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) ModuleSystemTest::SetUp(); 26d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 27d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) RegisterModule("messaging_utils", IDR_MESSAGING_UTILS_JS); 28d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) } 29d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 30d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}; 31d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 32d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)TEST_F(MessagingUtilsUnittest, TestNothing) { 33d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) ExpectNoAssertionsMade(); 34d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)} 35d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 36d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)TEST_F(MessagingUtilsUnittest, NoArguments) { 37d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); 38d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) RegisterTestModule( 39d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "var args = messagingUtils.alignSendMessageArguments();\n" 40d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "AssertTrue(args === null);"); 41d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) module_system_->Require("test"); 42d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)} 43d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 44d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)TEST_F(MessagingUtilsUnittest, ZeroArguments) { 45d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); 46d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) RegisterTestModule( 47d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "var args = messagingUtils.alignSendMessageArguments([]);" 48d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "AssertTrue(args === null);"); 49d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) module_system_->Require("test"); 50d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)} 51d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)TEST_F(MessagingUtilsUnittest, TooManyArgumentsNoOptions) { 53d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); 54d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) RegisterTestModule( 55d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "var args = messagingUtils.alignSendMessageArguments(\n" 56d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) " ['a', 'b', 'c', 'd']);\n" 57d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "AssertTrue(args === null);"); 58d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) module_system_->Require("test"); 59d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)} 60d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)TEST_F(MessagingUtilsUnittest, TooManyArgumentsWithOptions) { 624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); 634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) RegisterTestModule( 644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) "var args = messagingUtils.alignSendMessageArguments(\n" 654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) " ['a', 'b', 'c', 'd', 'e'], true);\n" 664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) "AssertTrue(args === null);"); 674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) module_system_->Require("test"); 684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)TEST_F(MessagingUtilsUnittest, FinalArgumentIsNotAFunctionNoOptions) { 71d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); 72d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) RegisterTestModule( 73d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "var args = messagingUtils.alignSendMessageArguments(\n" 74d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) " ['a', 'b', 'c']);\n" 75d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "AssertTrue(args === null);"); 76d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) module_system_->Require("test"); 77d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)} 78d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)TEST_F(MessagingUtilsUnittest, FinalArgumentIsNotAFunctionWithOptions) { 804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); 814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) RegisterTestModule( 824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) "var args = messagingUtils.alignSendMessageArguments(\n" 834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) " ['a', 'b', 'c', 'd'], true);\n" 844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) "AssertTrue(args === null);"); 854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) module_system_->Require("test"); 864e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 88d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)TEST_F(MessagingUtilsUnittest, OneStringArgument) { 89d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); 90d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) // Because the request argument is required, a single argument must get 91d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) // mapped to it rather than to the optional targetId argument. 92d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) RegisterTestModule( 93d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "var args = messagingUtils.alignSendMessageArguments(['a']);\n" 94d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "AssertTrue(args.length == 3);\n" 95d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "AssertTrue(args[0] === null);\n" 96d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "AssertTrue(args[1] == 'a');\n" 97d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "AssertTrue(args[2] === null);"); 98d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) module_system_->Require("test"); 99d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)} 100d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 101d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)TEST_F(MessagingUtilsUnittest, OneStringAndOneNullArgument) { 102d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); 103d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) // Explicitly specifying null as the request is allowed. 104d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) RegisterTestModule( 105d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "var args = messagingUtils.alignSendMessageArguments(['a', null]);\n" 106d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "AssertTrue(args.length == 3);\n" 107d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "AssertTrue(args[0] == 'a');\n" 108d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "AssertTrue(args[1] === null);\n" 109d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "AssertTrue(args[2] === null);"); 110d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) module_system_->Require("test"); 111d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)} 112d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 113d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)TEST_F(MessagingUtilsUnittest, OneNullAndOneStringArgument) { 114d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); 115d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) RegisterTestModule( 116d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "var args = messagingUtils.alignSendMessageArguments([null, 'a']);\n" 117d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "AssertTrue(args.length == 3);\n" 118d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "AssertTrue(args[0] === null);\n" 119d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "AssertTrue(args[1] == 'a');\n" 120d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "AssertTrue(args[2] === null);"); 121d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) module_system_->Require("test"); 122d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)} 123d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 124d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)TEST_F(MessagingUtilsUnittest, OneStringAndOneFunctionArgument) { 125d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); 126d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) // When the arguments are a string and a function, the function is 127d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) // unambiguously the responseCallback. Because the request argument is 128d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) // required, the remaining argument must get mapped to it rather than to the 129d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) // optional targetId argument. 130d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) RegisterTestModule( 131d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "var cb = function() {};\n" 132d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "var args = messagingUtils.alignSendMessageArguments(['a', cb]);\n" 133d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "AssertTrue(args.length == 3);\n" 134d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "AssertTrue(args[0] === null);\n" 135d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "AssertTrue(args[1] == 'a');\n" 136d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) "AssertTrue(args[2] == cb);"); 137d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) module_system_->Require("test"); 138d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)} 139d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 1404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)TEST_F(MessagingUtilsUnittest, OneStringAndOneObjectArgument) { 1414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); 1424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // This tests an ambiguous set of arguments when options are present: 1434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // chrome.runtime.sendMessage('target', {'msg': 'this is a message'}); 1444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // vs. 1454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // chrome.runtime.sendMessage('request', {'includeTlsChannelId': true}); 1464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // 1474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // The question is whether the string should map to the target and the 1484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // dictionary to the message, or whether the string should map to the message 1494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // and the dictionary to the options. Because the target and message arguments 1504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // predate the options argument, we bind the string in this case to the 1514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // targetId. 1524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) RegisterTestModule( 1534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) "var obj = {'b': true};\n" 1544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) "var args = messagingUtils.alignSendMessageArguments(['a', obj], true);\n" 1554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) "AssertTrue(args.length == 4);\n" 1564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) "AssertTrue(args[0] == 'a');\n" 1574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) "AssertTrue(args[1] == obj);\n" 1584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) "AssertTrue(args[2] === null);\n" 1594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) "AssertTrue(args[3] === null);"); 1604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) module_system_->Require("test"); 1614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 1624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 1634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)TEST_F(MessagingUtilsUnittest, TwoObjectArguments) { 1644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); 1654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // When two non-string arguments are provided and options are present, the 1664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // two arguments must match request and options, respectively, because 1674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // targetId must be a string. 1684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) RegisterTestModule( 1694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) "var obj1 = {'a': 'foo'};\n" 1704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) "var obj2 = {'b': 'bar'};\n" 1714e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) "var args = messagingUtils.alignSendMessageArguments(\n" 1724e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) " [obj1, obj2], true);\n" 1734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) "AssertTrue(args.length == 4);\n" 1744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) "AssertTrue(args[0] === null);\n" 1754e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) "AssertTrue(args[1] == obj1);\n" 1764e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) "AssertTrue(args[2] == obj2);\n" 1774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) "AssertTrue(args[3] === null);"); 1784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) module_system_->Require("test"); 1794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)} 1804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 181d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)} // namespace 182d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)} // namespace extensions 183