Advertisement
h0x0d

Windows 10 11082 EXTENSIONPAGEANDBACKGROUND.JS

Dec 17th, 2015
2,873
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. (function (nativeMsBrowser) {
  2.     var nativeJSON = window.JSON;
  3.  
  4.     // These values should never be touched.
  5.     const componentId_extensionTrident = 1;
  6.     const componentId_backgroundHost = 2;
  7.     const componentId_proxy = 3;
  8.     const componentId_manager = 4;
  9.     const componentId_ambassador = 5;
  10.     const componentId_frame = 6;
  11.     const componentId_frameTab = 7;
  12.     const componentId_browserTab = 8;
  13.     const componentId_tabTrident = 9;
  14.     const componentId_contentScript = 10;
  15.     const componentId_webRequest = 11;
  16.  
  17.     const Bhx_Broadcast = -2;
  18.     const Bhx_TopMost = -3;
  19.     const Bhx_Parent = -4;
  20.  
  21.     // These values are pulled from bhxmessages.h. You may notice they are in alphabetical order (hint, hint).
  22.     const BHX_MSGTYPE_COURIER_START = 4092;
  23.  
  24.     const messageId_ReplyId = BHX_MSGTYPE_COURIER_START;
  25.  
  26.     const eventId_browserActionOnClicked = 9;
  27.     const eventId_contextMenusOnClicked = 11;
  28.     const eventId_pageActionOnClicked = 10;
  29.     const eventId_pageEntitiesOnClicked = 3;
  30.     const eventId_pageEntitiesOnDetected = 2;
  31.     const eventId_storageOnChanged = 8;
  32.     const eventId_tabsOnActivated = 1;
  33.     const eventId_tabsOnCreated = 0;
  34.     const eventId_tabsOnUpdated = 5;
  35.     const eventId_windowsOnCreated = 4;
  36.     const eventId_windowsOnRemoved = 6;
  37.     const eventId_windowsOnFocusChanged = 7;
  38.  
  39.     const functionId_browserActionFrameDisable = BHX_MSGTYPE_COURIER_START + 19;
  40.     const functionId_browserActionFrameEnable = BHX_MSGTYPE_COURIER_START + 18;
  41.     const functionId_browserActionFrameTabDisable = BHX_MSGTYPE_COURIER_START + 12;
  42.     const functionId_browserActionFrameTabEnable = BHX_MSGTYPE_COURIER_START + 11;
  43.     const functionId_browserActionSetMultipleIcons = BHX_MSGTYPE_COURIER_START + 16;
  44.     const functionId_contextMenusCreate = BHX_MSGTYPE_COURIER_START + 14;
  45.     const functionId_detectLanguage = BHX_MSGTYPE_COURIER_START + 9;
  46.     const functionId_getAllFrames = BHX_MSGTYPE_COURIER_START + 8;
  47.     const functionId_getFrame = BHX_MSGTYPE_COURIER_START + 7;
  48.     const functionId_notifyEventListener = BHX_MSGTYPE_COURIER_START + 10;
  49.     const functionId_pageActionGetPopup = BHX_MSGTYPE_COURIER_START + 7;
  50.     const functionId_pageActionGetTitle = BHX_MSGTYPE_COURIER_START + 5;
  51.     const functionId_pageActionHide = BHX_MSGTYPE_COURIER_START + 3;
  52.     const functionId_pageActionSetMultipleIcons = BHX_MSGTYPE_COURIER_START + 9;
  53.     const functionId_pageActionSetPopup = BHX_MSGTYPE_COURIER_START + 6;
  54.     const functionId_pageActionSetTitle = BHX_MSGTYPE_COURIER_START + 4;
  55.     const functionId_pageActionShow = BHX_MSGTYPE_COURIER_START + 2;
  56.     const functionId_storageLocalGet = BHX_MSGTYPE_COURIER_START + 17;
  57.     const functionId_storageLocalRemove = BHX_MSGTYPE_COURIER_START + 24;
  58.     const functionId_storageLocalSet = BHX_MSGTYPE_COURIER_START + 18;
  59.     const functionId_tabsCreate = BHX_MSGTYPE_COURIER_START + 4;
  60.     const functionId_tabsExecuteScriptResource = BHX_MSGTYPE_COURIER_START + 2;
  61.     const functionId_tabsExecuteScriptSnippet = BHX_MSGTYPE_COURIER_START + 3;
  62.     const functionId_tabsGet = BHX_MSGTYPE_COURIER_START + 8;
  63.     const functionId_tabsInjectCssResource = BHX_MSGTYPE_COURIER_START + 4;
  64.     const functionId_tabsInjectCssSnippet = BHX_MSGTYPE_COURIER_START + 5;
  65.     const functionId_tabsQuery = BHX_MSGTYPE_COURIER_START + 7;
  66.     const functionId_tabsQueryMultiple = BHX_MSGTYPE_COURIER_START + 13;
  67.     const functionId_tabsRemove = BHX_MSGTYPE_COURIER_START + 10;
  68.     const functionId_tabsRemoveMultiple = BHX_MSGTYPE_COURIER_START + 20;
  69.     const functionId_tabsSendMessage = BHX_MSGTYPE_COURIER_START + 1;
  70.     const functionId_windowsGet = BHX_MSGTYPE_COURIER_START + 8;
  71.     const functionId_windowsGetAll = BHX_MSGTYPE_COURIER_START + 19;
  72.     const functionId_windowsUpdate = BHX_MSGTYPE_COURIER_START + 17;
  73.  
  74.     // Synchronous function are from syncmethodhandler.h. They should also be kept in alphabetical order.
  75.     const functionId_contextMenus_getNewMenuId = 1;
  76.     const functionId_getIsInPrivate = 2;
  77.     const functionId_getMessage = 3;
  78.     const functionId_getUILanguage = 4;
  79.     const functionId_permissionsCheckInternal = 5;
  80.  
  81.     const permissionId_contextMenus = 6;
  82.     const permissionId_storage = 14;
  83.     const permissionId_tabs = 10;
  84.     const permissionId_webNavigation = 12;
  85.     const permissionId_webRequest = 11;
  86.     const permissionId_webRequestBlocking = 13;
  87.  
  88.     // API used constants
  89.     const i18n_maxAllowedSubstitutions = 9;
  90.  
  91.     var storedCallbacks = [];
  92.     var nextCallback = 0;
  93.  
  94.     var storedHandlers = [];
  95.  
  96.     Object.defineProperty(window, "msBrowser", {
  97.         get: delayInitMsBrowser,
  98.         configurable: true,
  99.         enumerable: true
  100.     });
  101.  
  102.     function delayInitMsBrowser() {
  103.         var wrapMsBrowser = Object.defineProperties({}, {
  104.             "browserAction": {
  105.                 get: delayInitBrowserAction,
  106.                 configurable: true,
  107.                 enumerable: true
  108.             },
  109.             "contextMenus": {
  110.                 get: delayInitContextMenus,
  111.                 configurable: true,
  112.                 enumerable: true
  113.             },
  114.             "extension": {
  115.                 get: delayInitExtension,
  116.                 configurable: true,
  117.                 enumerable: true
  118.             },
  119.             "i18n": {
  120.                 get: delayInitI18n,
  121.                 configurable: true,
  122.                 enumerable: true
  123.             },
  124.             "pageAction": {
  125.                 get: delayInitPageAction,
  126.                 configurable: true,
  127.                 enumerable: true
  128.             },
  129.             "pageEntities": {
  130.                 get: delayInitPageEntities,
  131.                 configurable: true,
  132.                 enumerable: true
  133.             },
  134.             "runtime": {
  135.                 get: delayInitRuntime,
  136.                 configurable: true,
  137.                 enumerable: true
  138.             },
  139.             "storage": {
  140.                 get: delayInitStorage,
  141.                 configurable: true,
  142.                 enumerable: true
  143.             },
  144.             "tabs": {
  145.                 get: delayInitTabs,
  146.                 configurable: true,
  147.                 enumerable: true
  148.             },
  149.             "windows": {
  150.                 get: delayInitWindows,
  151.                 configurable: true,
  152.                 enumerable: true
  153.             },
  154.             "webNavigation": {
  155.                 get: delayInitWebNavigation,
  156.                 configurable: true,
  157.                 enumerable: true
  158.             },
  159.             "webRequest": {
  160.                 get: delayInitWebRequest,
  161.                 configurable: true,
  162.                 enumerable: true
  163.             }
  164.         });
  165.  
  166.         // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  167.         Object.defineProperty(window, "msBrowser", {
  168.             value: wrapMsBrowser,
  169.             configurable: true,
  170.             enumerable: true
  171.         });
  172.         return wrapMsBrowser;
  173.     }
  174.  
  175.     function delayInitBrowserAction()
  176.     {
  177.         var wrapBrowserAction = Object.defineProperties({}, {
  178.             "setIcon": {
  179.                 value: browserAction_setIcon_indirect.bind(nativeMsBrowser),
  180.                 configurable: true,
  181.                 enumerable: true
  182.             },
  183.             "enable": {
  184.                 value: browserAction_enable_indirect.bind(nativeMsBrowser),
  185.                 configurable: true,
  186.                 enumerable: true
  187.             },
  188.             "disable": {
  189.                 value: browserAction_disable_indirect.bind(nativeMsBrowser),
  190.                 configurable: true,
  191.                 enumerable: true
  192.             },
  193.             "onClicked": {
  194.                 get: delayInitOnClicked,
  195.                 configurable: true,
  196.                 enumerable: true
  197.             }
  198.         });
  199.        
  200.         function browserAction_setIcon_indirect() {
  201.             var expectedArguments = [
  202.                 { type: "object", name: "details" },
  203.                 { type: "function", name: "callback", optional: true } ];
  204.             TestParameterTypes("browserAction.setIcon", arguments, expectedArguments);
  205.  
  206.             var expectedProperties = [
  207.                 { type: "object", name: "imageData", optional: true },
  208.                 { type: "string or object", name: "path", optional: true, validator: function (value) {
  209.                     if (typeof(value) === "object") {
  210.                         var invalidProperties = "";
  211.                         var invalidValues = "";
  212.                         var propertyVerb = "is not a valid property";
  213.                         var valueVerb = "is not a string";
  214.  
  215.                         for (var prop in value) {
  216.                             if (prop !== "19" && prop !== "20" && prop !== "25" && prop !== "30" && prop !== "38" && prop !== "40") {
  217.                                 if (invalidProperties.length > 0) {
  218.                                     invalidProperties += ", ";
  219.                                     propertyVerb = "are not valid properties";
  220.                                 }
  221.                                 invalidProperties += "'" + prop + "'";
  222.                             } else if (typeof(value[prop]) !== "string") {
  223.                                 if (invalidValues.length > 0) {
  224.                                     invalidValues += ", ";
  225.                                     valueVerb = "are not strings";
  226.                                 }
  227.                                 invalidValues += "'" + prop + "'";
  228.                             }
  229.                         }
  230.  
  231.                         if (invalidProperties.length > 0) {
  232.                             invalidProperties += " " + propertyVerb + " in the dictionary";
  233.  
  234.                             if (invalidValues.length > 0) {
  235.                                 invalidProperties += " and ";
  236.                             }
  237.                         }
  238.  
  239.                         if (invalidValues.length > 0) {
  240.                             invalidValues += " " + valueVerb;
  241.                         }
  242.  
  243.                         if (invalidProperties.length > 0 || invalidValues.length > 0) {
  244.                             return invalidProperties + invalidValues;
  245.                         }
  246.                     }
  247.                 }},
  248.                 { type: "integer", name: "tabId", optional: true, validator: function (index) { if (index < 0) return "Value must not be less than 0"; } }];
  249.             TestObjectProperties(0, arguments[0], expectedProperties);
  250.  
  251.             if (arguments[0].imageData && arguments[0].path) {
  252.                 throw "Error: Only one of imageData or path may be specified.";
  253.             } else if (!(arguments[0].imageData) && !(arguments[0].path)) {
  254.                 throw "Error: One of imageData or path must be specified.";
  255.             }
  256.  
  257.             var parameters;
  258.             if (arguments[0].path && GetTypeName(arguments[0].path) === "object") {
  259.                 parameters = arguments[0].path;
  260.             } else {
  261.                 parameters = { "20" : arguments[0].path };
  262.             }
  263.  
  264.             if ("tabId" in arguments[0]) {
  265.                 parameters["tabId"] = arguments[0].tabId;
  266.             }
  267.  
  268.             var destination = {};
  269.             destination.component = componentId_frame;
  270.             destination.frameId = Bhx_Broadcast;
  271.  
  272.             return ExecuteGenericFunction(functionId_browserActionSetMultipleIcons, destination, parameters, arguments.length == 2 ? arguments[1] : null);
  273.         }
  274.  
  275.         function browserAction_enable_indirect() {
  276.             var expectedArguments = [
  277.                 { type: "integer", name: "tabId", optional: true } ];
  278.             TestParameterTypes("browserAction.enable", arguments, expectedArguments);
  279.  
  280.             var parameters;
  281.             var functionId;
  282.             var destination = {};
  283.             if (arguments && arguments.length == 1 && GetTypeName(arguments[0]) === "integer") {
  284.                 if (arguments[0] < 0) {
  285.                     ThrowArgumentError(0, "Value must not be less than 0");
  286.                 }
  287.  
  288.                 parameters = { "tabId" : arguments[0] };
  289.                 functionId = functionId_browserActionFrameTabEnable;
  290.                 destination.component = componentId_frameTab;
  291.                 destination.tabId = arguments[0];
  292.                 destination.frameId = Bhx_Parent;
  293.             } else {
  294.                 functionId = functionId_browserActionFrameEnable;
  295.                 destination.component = componentId_frame;
  296.                 destination.frameId = Bhx_Broadcast;
  297.             }
  298.  
  299.             return ExecuteGenericFunction(functionId, destination, parameters, null);
  300.         }
  301.  
  302.         function browserAction_disable_indirect() {
  303.             var expectedArguments = [
  304.                 { type: "integer", name: "tabId", optional: true } ];
  305.             TestParameterTypes("browserAction.disable", arguments, expectedArguments);
  306.  
  307.             var parameters;
  308.             var functionId;
  309.             var destination = {};
  310.             if (arguments && arguments.length == 1 && GetTypeName(arguments[0]) === "integer") {
  311.                 if (arguments[0] < 0) {
  312.                     ThrowArgumentError(0, "Value must not be less than 0");
  313.                 }
  314.  
  315.                 parameters = { "tabId" : arguments[0] };
  316.                 functionId = functionId_browserActionFrameTabDisable;
  317.                 destination.component = componentId_frameTab;
  318.                 destination.tabId = arguments[0];
  319.                 destination.frameId = Bhx_Parent;
  320.             } else {
  321.                 functionId = functionId_browserActionFrameDisable;
  322.                 destination.component = componentId_frame;
  323.                 destination.frameId = Bhx_Broadcast;
  324.             }
  325.  
  326.             return ExecuteGenericFunction(functionId, destination, parameters, null);
  327.         }
  328.  
  329.         function delayInitOnClicked() {
  330.             var onClickedObject = StoreNoFilterHandler(eventId_browserActionOnClicked, function (sender, tabData, listeners) {
  331.                 for (var i = 0; i < listeners.length; i++) {
  332.                     if (typeof(listeners[i]) == "function") {
  333.                         listeners[i](tabData);
  334.                     }
  335.                 }
  336.             });
  337.  
  338.             var wrapOnClicked = Object.defineProperties({}, {
  339.                 "addListener": {
  340.                     value: onClickedObject.addListener.bind(onClickedObject),
  341.                     configurable: true,
  342.                     enumerable: true
  343.                 },
  344.                 "removeListener": {
  345.                     value: onClickedObject.removeListener.bind(onClickedObject),
  346.                     configurable: true,
  347.                     enumerable: true
  348.                 },
  349.                 "hasListener": {
  350.                     value: onClickedObject.hasListener.bind(onClickedObject),
  351.                     configurable: true,
  352.                     enumerable: true
  353.                 }
  354.             });
  355.  
  356.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  357.             Object.defineProperty(window.msBrowser.browserAction, "onClicked", {
  358.                 value: wrapOnClicked,
  359.                 configurable: true,
  360.                 enumerable: true
  361.             });
  362.  
  363.             return wrapOnClicked;
  364.         }
  365.  
  366.         // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  367.         Object.defineProperty(window.msBrowser, "browserAction", {
  368.             value: wrapBrowserAction,
  369.             configurable: true,
  370.             enumerable: true
  371.         });
  372.  
  373.         return wrapBrowserAction;
  374.     }
  375.    
  376.     function delayInitContextMenus() {
  377.         var wrapContextMenus;
  378.         var hasPermission = ExecuteGenericSynchronousFunction(functionId_permissionsCheckInternal, permissionId_contextMenus);
  379.         if (typeof(hasPermission) == "boolean" && hasPermission) {
  380.             wrapContextMenus = Object.defineProperties({}, {
  381.                 "create": {
  382.                     value: contextMenus_create_indirect.bind(nativeMsBrowser),
  383.                     configurable: true,
  384.                     enumerable: true
  385.                 },
  386.                 "onClicked": {
  387.                     get: delayInitOnClicked,
  388.                     configurable: true,
  389.                     enumerable: true
  390.                 },
  391.                 "ACTION_MENU_TOP_LEVEL_LIMIT": {
  392.                     value: 6,
  393.                     configurable: true,
  394.                     enumerable: true
  395.                 }
  396.             });
  397.         }
  398.  
  399.         function contextMenus_create_indirect() {
  400.             var createProperties = arguments[0];
  401.             var callback = arguments[1];
  402.             var expectedArguments = [
  403.                 { type: "object", name: "createProperties" },
  404.                 { type: "function", name: "callback", optional: true } ];
  405.             TestParameterTypes("contextMenus.create", arguments, expectedArguments);
  406.  
  407.             var expectedProperties = [
  408.                 { type: "string", name: "type", optional: true, validator: function (value) {
  409.                     if (value !== "normal" && value !== "checkbox" && value !== "radio" && value !== "separator") {
  410.                         return "Value must be one of: [normal, checkbox, radio, separator]";
  411.                     }
  412.                 }},
  413.                 { type: "string", name: "id", optional: true },
  414.                 { type: "string", name: "title", optional: true },
  415.                 { type: "boolean", name: "checked", optional: true },
  416.                 { type: "array", name: "contexts", optional: true, validator: function (value) {
  417.                     if (value.length == 0) {
  418.                         return "contexts cannot be an empty array"
  419.                     }
  420.  
  421.                     var valueError = false;
  422.                     for (var i = 0; i < value.length; i++) {
  423.                         if (typeof(value[i]) !== "string") {
  424.                             return "All items in the array must be strings";
  425.                         } else if (value[i] !== "all" && value[i] !== "page" && value[i] !== "frame" && value[i] !== "selection" &&
  426.                                    value[i] !== "link" && value[i] !== "editable" && value[i] !== "image" && value[i] !== "video" &&
  427.                                    value[i] !== "audio" && value[i] !== "launcher" && value[i] !== "browser_action" && value[i] !== "page_action") {
  428.                             valueError = true;
  429.                         }
  430.                     }
  431.  
  432.                     if (valueError) {
  433.                         return "Each item's value must be one of: [all, page, frame, selection, link, editable, image, video, audio, launcher, browser_action, page_action]";
  434.                     }
  435.                 }},
  436.                 { type: "function", name: "onclick", optional: true },
  437.                 { type: "integer or string", name: "parentId", optional: true },
  438.                 { type: "array", name: "documentUrlPatterns", optional: true, validator: function (value) {
  439.                     for (var i = 0; i < value.length; i++) {
  440.                         if (typeof (value[i]) !== "string") {
  441.                             return "All items in the array must be strings";
  442.                         }
  443.                         else {
  444.                             try
  445.                             {
  446.                                 validateUrlMatchPattern(value[i]);
  447.                             }
  448.                             catch (e) {
  449.                                 return "Invalid url pattern '" + value[i] + "'";
  450.                             }
  451.                         }
  452.                     }
  453.                 }},
  454.                 { type: "array", name: "targetUrlPatterns", optional: true, validator: function (value) {
  455.                     for (var i = 0; i < value.length; i++) {
  456.                         if (typeof(value[i]) !== "string") {
  457.                             return "All items in the array must be strings";
  458.                         }
  459.                         else {
  460.                             try
  461.                             {
  462.                                 validateUrlMatchPattern(value[i]);
  463.                             }
  464.                             catch (e) {
  465.                                 return "Invalid url pattern '" + value[i] + "'";
  466.                             }
  467.                         }
  468.                     }
  469.                 }},
  470.                 { type: "boolean", name: "enabled", optional: true } ];
  471.             TestObjectProperties(0, arguments[0], expectedProperties);
  472.  
  473.             if (arguments[0].type !== "separator" && !(arguments[0].title)) {
  474.                 throw "Error: A title must be set unless the type is separator.";
  475.             }
  476.  
  477.             var menuItemId;
  478.             if (createProperties.id)
  479.             {
  480.                 menuItemId = createProperties.id;
  481.             }
  482.             else
  483.             {
  484.                 var syncFunctionId = functionId_contextMenus_getNewMenuId;
  485.                 menuItemId = ExecuteGenericSynchronousFunction(syncFunctionId, null);
  486.                 if (menuItemId == 0) {
  487.                     throw "Error: Extensions using event pages must pass an id parameter to msBrowser.contextMenus.create.";
  488.                 }
  489.             }
  490.             createProperties.id = nativeJSON.stringify(menuItemId);
  491.  
  492.             if (createProperties.parentId)
  493.             {
  494.                 createProperties.parentId = nativeJSON.stringify(createProperties.parentId);
  495.             }
  496.  
  497.             var onclick = createProperties.onclick;
  498.             if (onclick) {
  499.                 var onClickIndirect = function (info, tab) {
  500.                     if (info && info.menuItemId === menuItemId) {
  501.                         onclick(info, tab);
  502.                     }
  503.                 };
  504.                 window.msBrowser.contextMenus.onClicked.addListener(onClickIndirect);
  505.                 delete createProperties.onclick;
  506.             }
  507.            
  508.             var destination = {};
  509.             destination.component = componentId_manager;
  510.             var parameters = createProperties;
  511.             var functionId = functionId_contextMenusCreate;
  512.             ExecuteGenericFunction(functionId, destination, parameters, arguments.length == 2 ? arguments[1] : null);
  513.  
  514.             return menuItemId;
  515.         }
  516.  
  517.         function delayInitOnClicked() {
  518.             var onClickedObject = StoreNoFilterHandler(eventId_contextMenusOnClicked, function (sender, allOnClickData, listeners) {
  519.                 var tab = nativeJSON.parse(allOnClickData.tabData);
  520.                 var tabOnClickData = nativeJSON.parse(allOnClickData.tabOnClickData);
  521.                 var menuItemOnClickData = nativeJSON.parse(allOnClickData.menuItemOnClickData);
  522.                
  523.                 var info = {};
  524.                 info.menuItemId = nativeJSON.parse(menuItemOnClickData.menuItemId);
  525.                 if (menuItemOnClickData.parentMenuItemId) {
  526.                     info.parentMenuItemId = nativeJSON.parse(menuItemOnClickData.parentMenuItemId);
  527.                 }
  528.  
  529.                 if (tabOnClickData.mediaType) {
  530.                     info.mediaType = tabOnClickData.mediaType;
  531.                 }
  532.  
  533.                 if (tabOnClickData.linkUrl) {
  534.                     info.linkUrl = tabOnClickData.linkUrl;
  535.                 }
  536.  
  537.                 if (tabOnClickData.srcUrl) {
  538.                     info.srcUrl = tabOnClickData.srcUrl;
  539.                 }
  540.  
  541.                 if (tabOnClickData.pageUrl) {
  542.                     info.pageUrl = tabOnClickData.pageUrl;
  543.                 }
  544.  
  545.                 if (tabOnClickData.frameUrl) {
  546.                     info.frameUrl = tabOnClickData.frameUrl;
  547.                 }
  548.  
  549.                 if (tabOnClickData.selectionText) {
  550.                     info.selectionText = tabOnClickData.selectionText;
  551.                 }
  552.  
  553.                 if (tabOnClickData.editable) {
  554.                     info.editable = tabOnClickData.editable;
  555.                 }
  556.  
  557.                 if (typeof (menuItemOnClickData.wasChecked) !== "undefined") {
  558.                     info.wasChecked = nativeJSON.parse(menuItemOnClickData.wasChecked);
  559.                 }
  560.  
  561.                 if (typeof (menuItemOnClickData.checked) !== "undefined") {
  562.                     info.checked = nativeJSON.parse(menuItemOnClickData.checked);
  563.                 }
  564.  
  565.                 for (var i = 0; i < listeners.length; i++) {
  566.                     if (typeof(listeners[i]) == "function") {
  567.                         listeners[i](info, tab);
  568.                     }
  569.                 }
  570.             });
  571.            
  572.             var wrapOnClicked = Object.defineProperties({}, {
  573.                 "addListener": {
  574.                     value: onClickedObject.addListener.bind(onClickedObject),
  575.                     configurable: true,
  576.                     enumerable: true
  577.                 },
  578.                 "removeListener": {
  579.                     value: onClickedObject.removeListener.bind(onClickedObject),
  580.                     configurable: true,
  581.                     enumerable: true
  582.                 },
  583.                 "hasListener": {
  584.                     value: onClickedObject.hasListener.bind(onClickedObject),
  585.                     configurable: true,
  586.                     enumerable: true
  587.                 }
  588.             });
  589.  
  590.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  591.             Object.defineProperty(window.msBrowser.contextMenus, "onClicked", {
  592.                 value: wrapOnClicked,
  593.                 configurable: true,
  594.                 enumerable: true
  595.             });
  596.  
  597.             return wrapOnClicked;
  598.         }
  599.  
  600.         // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  601.         Object.defineProperty(window.msBrowser, "contextMenus", {
  602.             value: wrapContextMenus,
  603.             configurable: true,
  604.             enumerable: true
  605.         });
  606.  
  607.         return wrapContextMenus;
  608.     }
  609.  
  610.     function delayInitExtension() {
  611.         var wrapExtension = Object.defineProperties({}, {
  612.             "getURL": {
  613.                 value: extension_getURL_indirect.bind(nativeMsBrowser.extension),
  614.                 configurable: true,
  615.                 enumerable: true
  616.             },
  617.             "inPrivateContext": {
  618.                 get: extension_inPrivateContext_indirect,
  619.                 configurable: true,
  620.                 enumerable: true
  621.             }
  622.         });
  623.  
  624.         function extension_getURL_indirect() {
  625.             var expectedArguments = [
  626.                 { type: "string", name: "path" } ];
  627.             TestParameterTypes("extension.getURL", arguments, expectedArguments);
  628.  
  629.             return getURL_indirect.apply(this, arguments);
  630.         }
  631.  
  632.         function extension_inPrivateContext_indirect() {
  633.             var isInPrivate = false;
  634.             var undef;
  635.             var syncValue = ExecuteGenericSynchronousFunction(functionId_getIsInPrivate, undef);
  636.             if (typeof(syncValue) == "boolean") {
  637.                 isInPrivate = syncValue;
  638.             }
  639.  
  640.             return isInPrivate;
  641.         }
  642.  
  643.         // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  644.         Object.defineProperty(window.msBrowser, "extension", {
  645.             value: wrapExtension,
  646.             configurable: true,
  647.             enumerable: true
  648.         });
  649.  
  650.         return wrapExtension;
  651.     }
  652.  
  653.     function delayInitI18n() {
  654.         var wrapI18n = Object.defineProperties({}, {
  655.             "getUILanguage": {
  656.                 get: i18n_getUILanguage_indirect,
  657.                 configurable: true,
  658.                 enumerable: true
  659.             },
  660.             "getMessage": {
  661.                 value: i18n_getMessage_indirect,
  662.                 configurable: true,
  663.                 enumerable: true
  664.             }
  665.         });
  666.  
  667.         function i18n_getUILanguage_indirect() {
  668.             var UILanguage = "";
  669.             var undef;
  670.             var syncValue = ExecuteGenericSynchronousFunction(functionId_getUILanguage, undef);
  671.             if (typeof (syncValue) == "string") {
  672.                 UILanguage = syncValue;
  673.             }
  674.  
  675.             return UILanguage;
  676.         }
  677.  
  678.         function i18n_getMessage_indirect() {
  679.             var expectedArguments = [
  680.             { type: "string", name: "messageName" },
  681.             { type: "string or array", name: "substitutions", optional: true }];
  682.             TestParameterTypes("i18n.getMessage", arguments, expectedArguments);
  683.  
  684.             var message = "";
  685.             var messageName = null;
  686.             var substCount = 0; // 0 if there's no substring
  687.             if (arguments.length == 2) {
  688.                 if (GetTypeName(arguments[1]) === "array") {
  689.                     substCount = arguments[1].length;
  690.                     if (substCount > i18n_maxAllowedSubstitutions) {
  691.                         throw "Error: Substitution strings exceed maximum allowance";
  692.                     }
  693.                     for (var i = 0; i < substCount; i++) {
  694.                         if (typeof (arguments[0][i]) !== "string") {
  695.                             throw "Error: Invalid value for argument 1. All items in the array must be strings.";
  696.                         }
  697.                     }
  698.                 }
  699.                 else if (GetTypeName(arguments[1]) === "string") {
  700.                     substCount = 1;
  701.                 }
  702.             }
  703.             var parameters = {};
  704.             parameters.messageName = arguments[0];
  705.             parameters.substCount = substCount;
  706.             parameters.substitutions = (substCount > 0) ? arguments[1] : null;
  707.  
  708.             var messageValue = ExecuteGenericSynchronousFunction(functionId_getMessage, parameters);
  709.             if (typeof (messageValue) == "string") {
  710.                 message = messageValue;
  711.             }
  712.  
  713.             return message;
  714.         };
  715.  
  716.         // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  717.         Object.defineProperty(window.msBrowser, "i18n", {
  718.             value: wrapI18n,
  719.             configurable: true,
  720.             enumerable: true
  721.         });
  722.  
  723.         return wrapI18n;
  724.     }
  725.  
  726.     function delayInitPageAction()
  727.     {
  728.         var wrapPageAction = Object.defineProperties({}, {
  729.             "getPopup": {
  730.                 value: pageAction_getPopup_indirect.bind(nativeMsBrowser.pageAction),
  731.                 configurable: true,
  732.                 enumerable: true
  733.             },
  734.             "getTitle": {
  735.                 value: pageAction_getTitle_indirect.bind(nativeMsBrowser.pageAction),
  736.                 configurable: true,
  737.                 enumerable: true
  738.             },
  739.             "hide": {
  740.                 value: pageAction_hide_indirect.bind(nativeMsBrowser.pageAction),
  741.                 configurable: true,
  742.                 enumerable: true
  743.             },
  744.             "setIcon": {
  745.                 value: pageAction_setIcon_indirect.bind(nativeMsBrowser.pageAction),
  746.                 configurable: true,
  747.                 enumerable: true
  748.             },
  749.             "setPopup": {
  750.                 value: pageAction_setPopup_indirect.bind(nativeMsBrowser.pageAction),
  751.                 configurable: true,
  752.                 enumerable: true
  753.             },
  754.             "setTitle": {
  755.                 value: pageAction_setTitle_indirect.bind(nativeMsBrowser.pageAction),
  756.                 configurable: true,
  757.                 enumerable: true
  758.             },
  759.             "show": {
  760.                 value: pageAction_show_indirect.bind(nativeMsBrowser.pageAction),
  761.                 configurable: true,
  762.                 enumerable: true
  763.             },
  764.             "onClicked": {
  765.                 get: delayInitOnClicked,
  766.                 configurable: true,
  767.                 enumerable: true
  768.             }
  769.         });
  770.  
  771.         function pageAction_getPopup_indirect() {
  772.             var expectedArguments = [
  773.                 { type: "object", name: "details" },
  774.                 { type: "function", name: "callback" } ];
  775.             TestParameterTypes("pageAction.getPopup", arguments, expectedArguments);
  776.  
  777.             var expectedProperties = [
  778.                 { type: "integer", name: "tabId", validator: function (index) { if (index < 0) return "Value must not be less than 0"; } } ];
  779.             TestObjectProperties(0, arguments[0], expectedProperties);
  780.  
  781.             var destination = {};
  782.             destination.component = componentId_frameTab;
  783.             destination.tabId = arguments[0].tabId;
  784.             destination.frameId = Bhx_Parent;
  785.  
  786.             return ExecuteGenericFunction(functionId_pageActionGetPopup, destination, null, arguments[1]);
  787.         }
  788.  
  789.         function pageAction_getTitle_indirect() {
  790.             var expectedArguments = [
  791.                 { type: "object", name: "details" },
  792.                 { type: "function", name: "callback" } ];
  793.             TestParameterTypes("pageAction.getTitle", arguments, expectedArguments);
  794.  
  795.             var expectedProperties = [
  796.                 { type: "integer", name: "tabId", validator: function (index) { if (index < 0) return "Value must not be less than 0"; } } ];
  797.             TestObjectProperties(0, arguments[0], expectedProperties);
  798.  
  799.             var destination = {};
  800.             destination.component = componentId_frameTab;
  801.             destination.tabId = arguments[0].tabId;
  802.             destination.frameId = Bhx_Parent;
  803.  
  804.             return ExecuteGenericFunction(functionId_pageActionGetTitle, destination, null, arguments[1]);
  805.         }
  806.  
  807.         function pageAction_hide_indirect() {
  808.             var expectedArguments = [
  809.                 { type: "integer", name: "tabId" } ];
  810.             TestParameterTypes("pageAction.hide", arguments, expectedArguments);
  811.  
  812.             if (arguments[0] < 0) {
  813.                 throw "Error: Invalid value for argument 1. Value must not be less than 0."
  814.             }
  815.  
  816.             var destination = {};
  817.             destination.component = componentId_frameTab;
  818.             destination.tabId = arguments[0];
  819.             destination.frameId = Bhx_Parent;
  820.  
  821.             return ExecuteGenericFunction(functionId_pageActionHide, destination, null, null);
  822.         }
  823.  
  824.         function pageAction_setIcon_indirect() {
  825.             var expectedArguments = [
  826.                 { type: "object", name: "details" },
  827.                 { type: "function", name: "callback", optional: true } ];
  828.             TestParameterTypes("pageAction.setIcon", arguments, expectedArguments);
  829.  
  830.             var expectedProperties = [
  831.                 { type: "integer", name: "tabId", validator: function (index) { if (index < 0) return "Value must not be less than 0"; } },
  832.                 { type: "object", name: "imageData", optional: true },
  833.                 { type: "string or object", name: "path", optional: true, validator: function (value) {
  834.                     if (typeof(value) === "object") {
  835.                         var invalidProperties = "";
  836.                         var invalidValues = "";
  837.                         var propertyVerb = "is not a valid property";
  838.                         var valueVerb = "is not a string";
  839.  
  840.                         for (var prop in value) {
  841.                             if (prop !== "19" && prop !== "20" && prop !== "25" && prop !== "30" && prop !== "38" && prop !== "40") {
  842.                                 if (invalidProperties.length > 0) {
  843.                                     invalidProperties += ", ";
  844.                                     propertyVerb = "are not valid properties";
  845.                                 }
  846.                                 invalidProperties += "'" + prop + "'";
  847.                             } else if (typeof(value[prop]) !== "string") {
  848.                                 if (invalidValues.length > 0) {
  849.                                     invalidValues += ", ";
  850.                                     valueVerb = "are not strings";
  851.                                 }
  852.                                 invalidValues += "'" + prop + "'";
  853.                             }
  854.                         }
  855.  
  856.                         if (invalidProperties.length > 0) {
  857.                             invalidProperties += " " + propertyVerb + " in the dictionary";
  858.  
  859.                             if (invalidValues.length > 0) {
  860.                                 invalidProperties += " and ";
  861.                             }
  862.                         }
  863.  
  864.                         if (invalidValues.length > 0) {
  865.                             invalidValues += " " + valueVerb;
  866.                         }
  867.  
  868.                         if (invalidProperties.length > 0 || invalidValues.length > 0) {
  869.                             return invalidProperties + invalidValues;
  870.                         }
  871.                     }
  872.                 }},
  873.                 { type: "integer", name: "iconIndex", optional: true } ];  // Note: Does nothing in Microsoft Edge; required for compatibility
  874.             TestObjectProperties(0, arguments[0], expectedProperties);
  875.  
  876.             if (arguments[0].imageData && arguments[0].path) {
  877.                 throw "Error: Only one of imageData or path may be specified.";
  878.             } else if (!(arguments[0].imageData) && !(arguments[0].path)) {
  879.                 throw "Error: One of imageData or path must be specified.";
  880.             }
  881.  
  882.             var parameters;
  883.             if (arguments[0].path && GetTypeName(arguments[0].path) === "object") {
  884.                 parameters = arguments[0].path;
  885.             } else {
  886.                 parameters = { "20" : arguments[0].path };
  887.             }
  888.  
  889.             var destination = {};
  890.             destination.component = componentId_frameTab;
  891.             destination.tabId = arguments[0].tabId;
  892.             destination.frameId = Bhx_Parent;
  893.  
  894.             return ExecuteGenericFunction(functionId_pageActionSetMultipleIcons, destination, parameters, arguments.length == 2 ? arguments[1] : null);
  895.         }
  896.  
  897.         function pageAction_setPopup_indirect() {
  898.             var expectedArguments = [
  899.                 { type: "object", name: "details" } ];
  900.             TestParameterTypes("pageAction.setPopup", arguments, expectedArguments);
  901.  
  902.             var expectedProperties = [
  903.                 { type: "integer", name: "tabId", validator: function (index) { if (index < 0) return "Value must not be less than 0"; } },
  904.                 { type: "string", name: "popup" } ];
  905.             TestObjectProperties(0, arguments[0], expectedProperties);
  906.  
  907.             var destination = {};
  908.             destination.component = componentId_frameTab;
  909.             destination.tabId = arguments[0].tabId;
  910.             destination.frameId = Bhx_Parent;
  911.  
  912.             return ExecuteGenericFunction(functionId_pageActionSetPopup, destination, arguments[0].popup, null);
  913.         }
  914.  
  915.         function pageAction_setTitle_indirect() {
  916.             var expectedArguments = [
  917.                 { type: "object", name: "details" } ];
  918.             TestParameterTypes("pageAction.setTitle", arguments, expectedArguments);
  919.  
  920.             var expectedProperties = [
  921.                 { type: "integer", name: "tabId", validator: function (index) { if (index < 0) return "Value must not be less than 0"; } },
  922.                 { type: "string", name: "title" } ];
  923.             TestObjectProperties(0, arguments[0], expectedProperties);
  924.  
  925.             var destination = {};
  926.             destination.component = componentId_frameTab;
  927.             destination.tabId = arguments[0].tabId;
  928.             destination.frameId = Bhx_Parent;
  929.  
  930.             return ExecuteGenericFunction(functionId_pageActionSetTitle, destination, arguments[0].title, null);
  931.         }
  932.  
  933.         function pageAction_show_indirect() {
  934.             var expectedArguments = [
  935.                 { type: "integer", name: "tabId" } ];
  936.             TestParameterTypes("pageAction.show", arguments, expectedArguments);
  937.  
  938.             if (arguments[0] < 0) {
  939.                 throw "Error: Invalid value for argument 1. Value must not be less than 0."
  940.             }
  941.  
  942.             var destination = {};
  943.             destination.component = componentId_frameTab;
  944.             destination.tabId = arguments[0];
  945.             destination.frameId = Bhx_Parent;
  946.  
  947.             return ExecuteGenericFunction(functionId_pageActionShow, destination, null, null);
  948.         }
  949.  
  950.         function delayInitOnClicked() {
  951.             var onClickedObject = StoreNoFilterHandler(eventId_pageActionOnClicked, function (sender, tabData, listeners) {
  952.                 for (var i = 0; i < listeners.length; i++) {
  953.                     if (typeof(listeners[i]) == "function") {
  954.                         listeners[i](tabData);
  955.                     }
  956.                 }
  957.             });
  958.  
  959.             var wrapOnClicked = Object.defineProperties({}, {
  960.                 "addListener": {
  961.                     value: onClickedObject.addListener.bind(onClickedObject),
  962.                     configurable: true,
  963.                     enumerable: true
  964.                 },
  965.                 "removeListener": {
  966.                     value: onClickedObject.removeListener.bind(onClickedObject),
  967.                     configurable: true,
  968.                     enumerable: true
  969.                 },
  970.                 "hasListener": {
  971.                     value: onClickedObject.hasListener.bind(onClickedObject),
  972.                     configurable: true,
  973.                     enumerable: true
  974.                 }
  975.             });
  976.  
  977.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  978.             Object.defineProperty(window.msBrowser.pageAction, "onClicked", {
  979.                 value: wrapOnClicked,
  980.                 configurable: true,
  981.                 enumerable: true
  982.             });
  983.  
  984.             return wrapOnClicked;
  985.         }
  986.  
  987.         // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  988.         Object.defineProperty(window.msBrowser, "pageAction", {
  989.             value: wrapPageAction,
  990.             configurable: true,
  991.             enumerable: true
  992.         });
  993.  
  994.         return wrapPageAction;
  995.     }
  996.  
  997.     function delayInitPageEntities() {
  998.         var wrapPageEntities = Object.defineProperties({}, {
  999.             "onClicked": {
  1000.                 get: delayInitOnClicked,
  1001.                 configurable: true,
  1002.                 enumerable: true
  1003.             },
  1004.             "onDetected": {
  1005.                 get: delayInitOnDetected,
  1006.                 configurable: true,
  1007.                 enumerable: true
  1008.             }
  1009.         });
  1010.  
  1011.         function delayInitOnClicked() {
  1012.             var onClickedObject = StoreNoFilterHandler(eventId_pageEntitiesOnClicked, function (sender, data, listeners) {
  1013.                 for (var i = 0; i < listeners.length; i++) {
  1014.                     if (typeof(listeners[i]) == "function") {
  1015.                         listeners[i](sender.tabId, data.entity, data.hitRect);
  1016.                     }
  1017.                 }
  1018.             });
  1019.  
  1020.             var wrapOnClicked = Object.defineProperties({}, {
  1021.                 "addListener": {
  1022.                     value: onClickedObject.addListener.bind(onClickedObject),
  1023.                     configurable: true,
  1024.                     enumerable: true
  1025.                 },
  1026.                 "removeListener": {
  1027.                     value: onClickedObject.removeListener.bind(onClickedObject),
  1028.                     configurable: true,
  1029.                     enumerable: true
  1030.                 },
  1031.                 "hasListener": {
  1032.                     value: onClickedObject.hasListener.bind(onClickedObject),
  1033.                     configurable: true,
  1034.                     enumerable: true
  1035.                 }
  1036.             });
  1037.  
  1038.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  1039.             Object.defineProperty(window.msBrowser.pageEntities, "onClicked", {
  1040.                 value: wrapOnClicked,
  1041.                 configurable: true,
  1042.                 enumerable: true
  1043.             });
  1044.  
  1045.             return wrapOnClicked;
  1046.         };
  1047.  
  1048.         function delayInitOnDetected() {
  1049.             var onDetectedObject = StoreNoFilterHandler(eventId_pageEntitiesOnDetected, function (sender, data, listeners) {
  1050.                 for (var i = 0; i < listeners.length; i++) {
  1051.                     if (typeof(listeners[i]) == "function") {
  1052.                         listeners[i](sender.tabId, data);
  1053.                     }
  1054.                 }
  1055.             });
  1056.  
  1057.             var wrapOnDetected = Object.defineProperties({}, {
  1058.                 "addListener": {
  1059.                     value: onDetectedObject.addListener.bind(onDetectedObject),
  1060.                     configurable: true,
  1061.                     enumerable: true
  1062.                 },
  1063.                 "removeListener": {
  1064.                     value: onDetectedObject.removeListener.bind(onDetectedObject),
  1065.                     configurable: true,
  1066.                     enumerable: true
  1067.                 },
  1068.                 "hasListener": {
  1069.                     value: onDetectedObject.hasListener.bind(onDetectedObject),
  1070.                     configurable: true,
  1071.                     enumerable: true
  1072.                 }
  1073.             });
  1074.  
  1075.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  1076.             Object.defineProperty(window.msBrowser.pageEntities, "onDetected", {
  1077.                 value: wrapOnDetected,
  1078.                 configurable: true,
  1079.                 enumerable: true
  1080.             });
  1081.  
  1082.             return wrapOnDetected;
  1083.         }
  1084.  
  1085.         // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  1086.         Object.defineProperty(window.msBrowser, "pageEntities", {
  1087.             value: wrapPageEntities,
  1088.             configurable: true,
  1089.             enumerable: true
  1090.         });
  1091.  
  1092.         return wrapPageEntities;
  1093.     }
  1094.  
  1095.     function delayInitRuntime() {
  1096.         var wrapRuntime = Object.defineProperties({}, {
  1097.             "getURL": {
  1098.                 value: runtime_getURL_indirect.bind(nativeMsBrowser),
  1099.                 configurable: true,
  1100.                 enumerable: true
  1101.             },
  1102.             "lastError": {
  1103.                 get: runtime_lastError,
  1104.                 configurable: true,
  1105.                 enumerable: true
  1106.             },
  1107.             "onMessageExternal": {
  1108.                 get: delayInitOnMessageExternal,
  1109.                 configurable: true,
  1110.                 enumerable: true
  1111.             },
  1112.             "sendMessage": {
  1113.                 value: runtime_sendMessage_indirect.bind(nativeMsBrowser.runtime),
  1114.                 configurable: true,
  1115.                 enumerable: true
  1116.             },
  1117.             "onMessage": {
  1118.                 get: delayInitOnMessage,
  1119.                 configurable: true,
  1120.                 enumerable: true
  1121.             }
  1122.         });
  1123.  
  1124.         function runtime_getURL_indirect() {
  1125.             var expectedArguments = [
  1126.                 { type: "string", name: "path" } ];
  1127.             TestParameterTypes("runtime.getURL", arguments, expectedArguments);
  1128.  
  1129.             return getURL_indirect.apply(this, arguments);
  1130.         }
  1131.  
  1132.         function runtime_lastError() {
  1133.             return nativeMsBrowser.runtime.lastError;
  1134.         }
  1135.  
  1136.         function runtime_sendMessage_createCallback(responseCallback) {
  1137.             return function () {
  1138.                 var jsonResponse = arguments[0];
  1139.                 var response; // This is initialized to undefined.
  1140.                 if (typeof(jsonResponse) !== "undefined" && jsonResponse !== "undefined") {
  1141.                     // JSON.stringify(undefined) === undefined is packed for courier messaging as 'undefined' but JSON.parse('undefined') throws javascript error.
  1142.                     // A response of "undefined" (type string) is packed for courier messaging as '"undefined"', hence will not conflict with a response of undefined (type undefined).
  1143.                     response = nativeJSON.parse(jsonResponse);
  1144.                 }
  1145.                 responseCallback(response);
  1146.             }
  1147.         }
  1148.  
  1149.         function runtime_sendMessage_indirect() {
  1150.             var expectedArguments = [
  1151.                 { type: "string", name: "extensionId", optional: true },
  1152.                 { type: "any", name: "message" },
  1153.                 { type: "object", name: "options", optional: true },
  1154.                 { type: "function", name: "responseCallback", optional: true } ];
  1155.             TestParameterTypes("runtime.sendMessage", arguments, expectedArguments);
  1156.  
  1157.             var index = arguments.length - 1;
  1158.  
  1159.             var responseCallback = null;
  1160.             if (typeof arguments[index] === "function") {
  1161.                 responseCallback = runtime_sendMessage_createCallback(arguments[index]);
  1162.                 --index;
  1163.             }
  1164.  
  1165.             var options = null;
  1166.             if (index >= 2) {
  1167.                 // More than two arguments remain so options param must be present.
  1168.                 options = arguments[index];
  1169.                 --index;
  1170.             } else if (index === 1) {
  1171.                 // The logic: assume that the first param is the optional extension id if it is present and is a string, hence no options argument in this case.
  1172.                 if (!TestArgumentType(typeof(arguments[0]), "string or null or undefined")) {
  1173.                     options = arguments[index];
  1174.                     --index;
  1175.                 }
  1176.             }
  1177.  
  1178.             if (options) {
  1179.                 var expectedProperties = [
  1180.                     { type: "boolean", name: "includeTlsChannelId", optional: true } ]; // Note: Does nothing in Microsoft Edge; required for compatibility
  1181.                 TestObjectProperties(index, options, expectedProperties);
  1182.             }
  1183.  
  1184.             var message = arguments[index]; // Must be present.
  1185.             --index;
  1186.  
  1187.             var extensionId = null;
  1188.             if (index >= 0) {
  1189.                 extensionId = arguments[index];
  1190.                 --index;
  1191.             }
  1192.  
  1193.             if (index !== -1) {
  1194.                 // This should have been caught by TestParameterTypes, but don't leave the developer hanging.
  1195.                 ThrowTypeError("runtime.sendMessage", arguments, expectedArguments);
  1196.             } else {
  1197.                 // Always stringify on sendMessage side and always parse on the callback side.
  1198.                 // Otherwise, callback cannot distinuish between a message sending an object and another message sending its string equivalent.
  1199.                 return nativeMsBrowser.runtime.sendMessage(extensionId, nativeJSON.stringify(message), options, responseCallback);
  1200.             }
  1201.         }
  1202.  
  1203.         function runtime_messagingListener_createCallback(callback) {
  1204.             return function () {
  1205.                 var result = {};
  1206.                 function sendResponse(response) {
  1207.                     result.response = nativeJSON.stringify(response);
  1208.                 }
  1209.                 var msg = nativeJSON.parse(arguments[0]);
  1210.                 result.async = callback(msg, arguments[1], sendResponse.bind(window)) ? true : false;
  1211.                 return result;
  1212.                 // Need to handle sending the response async - http://osgvsowi/674353
  1213.             }
  1214.         }
  1215.  
  1216.         function delayInitOnMessage() {
  1217.             var wrapOnMessage = Object.defineProperties({}, {
  1218.                 "addListener": {
  1219.                     value: runtime_onMessage_addListener_indirect.bind(nativeMsBrowser.runtime.onMessage),
  1220.                     configurable: true,
  1221.                     enumerable: true
  1222.                 }
  1223.             });
  1224.  
  1225.             function runtime_onMessage_addListener_indirect(callback) {
  1226.                 TestListenerArguments(arguments);
  1227.  
  1228.                 nativeMsBrowser.runtime.onMessage.addListener(runtime_messagingListener_createCallback(callback));
  1229.             }
  1230.  
  1231.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  1232.             Object.defineProperty(window.msBrowser.runtime, "onMessage", {
  1233.                 value: wrapOnMessage,
  1234.                 configurable: true,
  1235.                 enumerable: true
  1236.             });
  1237.             return wrapOnMessage;
  1238.         }
  1239.  
  1240.         function delayInitOnMessageExternal() {
  1241.             var wrapOnMessageExternal = Object.defineProperties({}, {
  1242.                 "addListener": {
  1243.                     value: runtime_onMessageExternal_addListener_indirect.bind(nativeMsBrowser.runtime.onMessageExternal),
  1244.                     configurable: true,
  1245.                     enumerable: true
  1246.                 }
  1247.             });
  1248.  
  1249.             function runtime_onMessageExternal_addListener_indirect(callback) {
  1250.                 TestListenerArguments(arguments);
  1251.  
  1252.                 nativeMsBrowser.runtime.onMessageExternal.addListener(runtime_messagingListener_createCallback(callback));
  1253.             }
  1254.  
  1255.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  1256.             Object.defineProperty(window.msBrowser.runtime, "onMessageExternal", {
  1257.                 value: wrapOnMessageExternal,
  1258.                 configurable: true,
  1259.                 enumerable: true
  1260.             });
  1261.             return wrapOnMessageExternal;
  1262.         }
  1263.  
  1264.         // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  1265.         Object.defineProperty(window.msBrowser, "runtime", {
  1266.             value: wrapRuntime,
  1267.             configurable: true,
  1268.             enumerable: true
  1269.         });
  1270.         return wrapRuntime;
  1271.     }
  1272.  
  1273.     function delayInitStorage() {
  1274.         var wrapStorage;
  1275.         var hasPermission = ExecuteGenericSynchronousFunction(functionId_permissionsCheckInternal, permissionId_storage);
  1276.         if (typeof(hasPermission) == "boolean" && hasPermission) {
  1277.             wrapStorage = Object.defineProperties({}, {
  1278.                 "local": {
  1279.                     get: delayInitLocal,
  1280.                     configurable: true,
  1281.                     enumerable: true
  1282.                 },
  1283.                 "onChanged": {
  1284.                     get: delayInitOnChanged,
  1285.                     configurable: true,
  1286.                     enumerable: true
  1287.                 }
  1288.             });
  1289.         }
  1290.  
  1291.         function parseValueFromStorage(value) {
  1292.             // TODO: handle regex http://osgvsowi/2521658
  1293.             if ((typeof (value) === "string") && (value.substr(0, 5) === "Date:")) {
  1294.                 return new Date(JSON.parse(value.substr(5)));
  1295.             } else if (typeof (value) === "string") {
  1296.                 // Everything else uses JSON
  1297.                 return JSON.parse(value);
  1298.             }
  1299.             // implicitly return undefined if value wasn't a string
  1300.         }
  1301.  
  1302.         function delayInitLocal() {
  1303.             var wrapLocal = Object.defineProperties({}, {
  1304.                 "clear": {
  1305.                     value: storage_local_clear_indirect.bind(nativeMsBrowser),
  1306.                     configurable: true,
  1307.                     enumerable: true
  1308.                 },
  1309.                 "get": {
  1310.                     value: storage_local_get_indirect.bind(nativeMsBrowser),
  1311.                     configurable: true,
  1312.                     enumerable: true
  1313.                 },
  1314.                 "getBytesInUse": {
  1315.                     value: storage_local_getBytesInUse_indirect.bind(nativeMsBrowser),
  1316.                     configurable: true,
  1317.                     enumerable: true
  1318.                 },
  1319.                 "remove": {
  1320.                     value: storage_local_remove_indirect.bind(nativeMsBrowser),
  1321.                     configurable: true,
  1322.                     enumerable: true
  1323.                 },
  1324.                 "set": {
  1325.                     value: storage_local_set_indirect.bind(nativeMsBrowser),
  1326.                     configurable: true,
  1327.                     enumerable: true
  1328.                 }
  1329.             });
  1330.  
  1331.             function TransformValuesForManager(items) {
  1332.                 var keys = Object.keys(items);
  1333.                 var values = {};
  1334.                 for (var i = 0; i < keys.length; i++) {
  1335.                     var value = items[keys[i]];
  1336.                     // TODO: handle regex http://osgvsowi/2521658
  1337.                     if (value instanceof Date) {
  1338.                         values[keys[i]] = "Date:" + JSON.stringify(value);
  1339.                     } else if (typeof value === "function") {
  1340.                         // Handle functions
  1341.                         values[keys[i]] = "{}";
  1342.                     } else {
  1343.                         // All others use JSON
  1344.                         values[keys[i]] = JSON.stringify(value);
  1345.                     }
  1346.                 }
  1347.  
  1348.                 return values;
  1349.             }
  1350.  
  1351.             function storage_local_clear_indirect() {
  1352.                 // TODO (brentcou): http://osgvsowi/319342, implement storage.local.clear
  1353.             }
  1354.  
  1355.             function storage_local_get_indirect() {
  1356.                 var expectedArguments = [
  1357.                     { type: "string or array or object", name: "keys", optional: true },
  1358.                     { type: "function", name: "callback" }];
  1359.                 TestParameterTypes("storage.local.get", arguments, expectedArguments);
  1360.  
  1361.                 if (GetTypeName(arguments[0]) === "array") {
  1362.                     for (var i = 0; i < arguments[0].length; i++) {
  1363.                         if (typeof (arguments[0][i]) !== "string") {
  1364.                             ThrowArgumentError(0, "All items in the array must be strings");
  1365.                         }
  1366.                     }
  1367.                 }
  1368.  
  1369.                 var keys = null;
  1370.                 var callbackIndex = 0;
  1371.                 if (arguments.length == 2) {
  1372.                     keys = arguments[0];
  1373.                     callbackIndex = 1;
  1374.                 }
  1375.  
  1376.                 if (GetTypeName(keys) === "string") {
  1377.                     // convert the single string case to an array with one element to simplify back end processing
  1378.                     keys = [keys];
  1379.                 }
  1380.  
  1381.                 if (GetTypeName(keys) === "object") {
  1382.                     // if it is an object, then the values need transforming the same way they do on set.
  1383.                     keys = TransformValuesForManager(keys);
  1384.                 }
  1385.  
  1386.                 var callback = arguments[callbackIndex];
  1387.                 var getCallbackWrapper = function (serializedItems) {
  1388.                     var items = {};
  1389.                     for (var key in serializedItems) {
  1390.                         items[key] = parseValueFromStorage(serializedItems[key]);
  1391.                     }
  1392.  
  1393.                     callback(items);
  1394.                 }
  1395.  
  1396.                 return ExecuteGenericFunction(
  1397.                     functionId_storageLocalGet,
  1398.                     { component: componentId_manager },
  1399.                     keys,
  1400.                     getCallbackWrapper);
  1401.             };
  1402.  
  1403.             function storage_local_getBytesInUse_indirect() {
  1404.                 // TODO (brentcou): http://osgvsowi/319318, implement storage.local.getBytesInUse
  1405.             }
  1406.  
  1407.             function storage_local_remove_indirect() {
  1408.                 var expectedArguments = [
  1409.                     { type: "string or array", name: "keys" },
  1410.                     { type: "function", name: "callback", optional: true }];
  1411.                 TestParameterTypes("storage.local.remove", arguments, expectedArguments);
  1412.  
  1413.                 if (GetTypeName(arguments[0]) === "array") {
  1414.                     for (var i = 0; i < arguments[0].length; i++) {
  1415.                         if (typeof (arguments[0][i]) !== "string") {
  1416.                             ThrowArgumentError(0, "All items in the array must be strings");
  1417.                         }
  1418.                     }
  1419.                 }
  1420.  
  1421.                 var keys = arguments[0];
  1422.                 if (GetTypeName(keys) === "string") {
  1423.                     // convert the single string case to an array with one element to simplify back end processing
  1424.                     keys = [keys];
  1425.                 }
  1426.  
  1427.                 var responseCallback = arguments.length > 1 ? arguments[1] : null;
  1428.                 return ExecuteGenericFunction(
  1429.                     functionId_storageLocalRemove,
  1430.                     { component: componentId_manager },
  1431.                     keys,
  1432.                     responseCallback);
  1433.             }
  1434.  
  1435.             function storage_local_set_indirect() {
  1436.                 var expectedArguments = [
  1437.                     { type: "object", name: "items" },
  1438.                     { type: "function", name: "callback", optional: true } ];
  1439.                 TestParameterTypes("storage.local.set", arguments, expectedArguments);
  1440.  
  1441.                 var values = TransformValuesForManager(arguments[0]);
  1442.                 var responseCallback = arguments.length > 1 ? arguments[1] : null;
  1443.                 return ExecuteGenericFunction(
  1444.                     functionId_storageLocalSet,
  1445.                     { component: componentId_manager },
  1446.                     values,
  1447.                     responseCallback);
  1448.             };
  1449.  
  1450.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  1451.             Object.defineProperty(window.msBrowser.storage, "local", {
  1452.                 value: wrapLocal,
  1453.                 configurable: true,
  1454.                 enumerable: true
  1455.             });
  1456.             return wrapLocal;
  1457.         }
  1458.  
  1459.         function delayInitOnChanged() {
  1460.             var onChangedObject = StoreNoFilterHandler(eventId_storageOnChanged, function (sender, storageChangeData, listeners) {
  1461.                 var changes = {};
  1462.                 for (var key in storageChangeData.changes) {
  1463.                     var changeData = storageChangeData.changes[key];
  1464.                     var parsedChangeData = {};
  1465.                     parsedChangeData.oldValue = parseValueFromStorage(changeData.oldValue);
  1466.                     parsedChangeData.newValue = parseValueFromStorage(changeData.newValue);
  1467.                     changes[key] = parsedChangeData;
  1468.                 }
  1469.  
  1470.                 for (var i = 0; i < listeners.length; i++) {
  1471.                     if (typeof (listeners[i]) == "function") {
  1472.                         listeners[i](changes, "local");
  1473.                     }
  1474.                 }
  1475.             });
  1476.  
  1477.             var wrapOnChanged = Object.defineProperties({}, {
  1478.                 "addListener": {
  1479.                     value: onChangedObject.addListener.bind(onChangedObject),
  1480.                     configurable: true,
  1481.                     enumerable: true
  1482.                 },
  1483.                 "removeListener": {
  1484.                     value: onChangedObject.removeListener.bind(onChangedObject),
  1485.                     configurable: true,
  1486.                     enumerable: true
  1487.                 },
  1488.                 "hasListener": {
  1489.                     value: onChangedObject.hasListener.bind(onChangedObject),
  1490.                     configurable: true,
  1491.                     enumerable: true
  1492.                 }
  1493.             });
  1494.  
  1495.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  1496.             Object.defineProperty(window.msBrowser.storage, "onChanged", {
  1497.                 value: wrapOnChanged,
  1498.                 configurable: true,
  1499.                 enumerable: true
  1500.             });
  1501.             return wrapOnChanged;
  1502.         }
  1503.  
  1504.         // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  1505.         Object.defineProperty(window.msBrowser, "storage", {
  1506.             value: wrapStorage,
  1507.             configurable: true,
  1508.             enumerable: true
  1509.         });
  1510.         return wrapStorage;
  1511.     }
  1512.  
  1513.     function delayInitTabs() {
  1514.         var wrapTabs = Object.defineProperties({}, {
  1515.             "create": {
  1516.                 value: tabs_create_indirect.bind(nativeMsBrowser),
  1517.                 configurable: true,
  1518.                 enumerable: true
  1519.             },
  1520.             "executeScript": {
  1521.                 value: tabs_executeScript_indirect.bind(nativeMsBrowser),
  1522.                 configurable: true,
  1523.                 enumerable: true
  1524.             },
  1525.             "get": {
  1526.                 value: tabs_get_indirect.bind(nativeMsBrowser),
  1527.                 configurable: true,
  1528.                 enumerable: true
  1529.             },
  1530.             "getCurrent": {
  1531.                 value: tabs_getCurrent_indirect.bind(nativeMsBrowser),
  1532.                 configurable: true,
  1533.                 enumerable: true
  1534.             },
  1535.             "insertCSS": {
  1536.                 value: tabs_insertCSS_indirect.bind(nativeMsBrowser),
  1537.                 configurable: true,
  1538.                 enumerable: true
  1539.             },
  1540.             "query": {
  1541.                 value: tabs_query_indirect.bind(nativeMsBrowser),
  1542.                 configurable: true,
  1543.                 enumerable: true
  1544.             },
  1545.             "remove": {
  1546.                 value: tabs_remove_indirect.bind(nativeMsBrowser),
  1547.                 configurable: true,
  1548.                 enumerable: true
  1549.             },
  1550.             "sendMessage": {
  1551.                 value: tabs_sendMessage_indirect.bind(nativeMsBrowser),
  1552.                 configurable: true,
  1553.                 enumerable: true
  1554.             },
  1555.             "detectLanguage": {
  1556.                 value: tabs_detectLanguage_indirect.bind(nativeMsBrowser),
  1557.                 configurable: true,
  1558.                 enumerable: true
  1559.             },
  1560.             "onActivated": {
  1561.                 get: delayInitOnActivated,
  1562.                 configurable: true,
  1563.                 enumerable: true
  1564.             },
  1565.             "onCreated": {
  1566.                 get: delayInitOnCreated,
  1567.                 configurable: true,
  1568.                 enumerable: true
  1569.             },
  1570.             "onUpdated": {
  1571.                 get: delayInitOnUpdated,
  1572.                 configurable: true,
  1573.                 enumerable: true
  1574.             }
  1575.         });
  1576.  
  1577.         function tabs_create_indirect() {
  1578.             var expectedArguments = [
  1579.                 { type: "object", name: "createProperties" },
  1580.                 { type: "function", name: "callback", optional: true } ];
  1581.             TestParameterTypes("tabs.create", arguments, expectedArguments);
  1582.  
  1583.             var expectedProperties = [
  1584.                 { type: "integer", name: "windowId", optional: true, validator: function (index) { if (index < -2) return "Value must not be less than -2"; } },
  1585.                 { type: "integer", name: "index", optional: true, validator: function (index) { if (index < 0) return "Value must not be less than 0"; } },
  1586.                 { type: "string", name: "url", optional: true },
  1587.                 { type: "boolean", name: "active", optional: true },
  1588.                 { type: "boolean", name: "selected", optional: true }, // Note: Does nothing in Microsoft Edge; required for compatibility
  1589.                 { type: "boolean", name: "pinned", optional: true }, // Note: Does nothing in Microsoft Edge; required for compatibility
  1590.                 { type: "integer", name: "openerTabId", optional: true } ]; // Note: Does nothing in Microsoft Edge; required for compatibility
  1591.             TestObjectProperties(0, arguments[0], expectedProperties);
  1592.  
  1593.             var destination = {};
  1594.             destination.component = componentId_frame;
  1595.             if (arguments[0].windowId && arguments[0].windowId > -2) {
  1596.                 destination.frameId = arguments[0].windowId;
  1597.             }
  1598.  
  1599.             var parameters = {};
  1600.             parameters.url = arguments[0].url || null;
  1601.             parameters.active = (GetTypeName(arguments[0].active) === "boolean" ? arguments[0].active : true);
  1602.             parameters.index = (GetTypeName(arguments[0].index) === "integer" ? arguments[0].index : -1);
  1603.  
  1604.             return ExecuteGenericFunction(functionId_tabsCreate, destination, parameters, arguments.length == 2 ? arguments[1] : null);
  1605.         }
  1606.  
  1607.         function tabs_detectLanguage_indirect(tabId, callback) {
  1608.             var tabId = arguments[0];
  1609.             var callback = arguments[1];
  1610.  
  1611.             var expectedArguments = [
  1612.                 { type: "integer", name: "tabId" },
  1613.                 { type: "function", name: "callback" }];
  1614.             TestParameterTypes("tabs.get", arguments, expectedArguments);
  1615.  
  1616.             if (arguments[0] < 0) {
  1617.                 throw "Error: Invalid value for argument 1. Value must not be less than 0.";
  1618.             }
  1619.  
  1620.             // Setup the message to native code and register the JS callback function
  1621.             var destination = {};
  1622.             destination.component = componentId_tabTrident;
  1623.             destination.tabId = tabId;
  1624.             destination.frameId = Bhx_Parent;
  1625.  
  1626.             var parameters;  // detectLanguages only needs the tabId
  1627.             var functionId = functionId_detectLanguage;
  1628.             return ExecuteGenericFunction(functionId, destination, parameters, callback);
  1629.         }
  1630.  
  1631.         function tabs_executeScript_indirect() {
  1632.             var expectedArguments = [
  1633.                 { type: "integer", name: "tabId", optional: true },
  1634.                 { type: "object", name: "details" },
  1635.                 { type: "function", name: "callback", optional: true } ];
  1636.             TestParameterTypes("tabs.executeScript", arguments, expectedArguments);
  1637.  
  1638.             // This is required because there are optional arguments followed by mandatory ones.
  1639.             var detailsIndex = 1;
  1640.             if (arguments.length === 1 || GetTypeName(arguments[0]) === "object") {
  1641.                 // If there is only one argument, or the first argument is an object, we should assume it is the details.
  1642.                 detailsIndex = 0;
  1643.             }
  1644.  
  1645.             var details = arguments[detailsIndex];
  1646.             var tabId = detailsIndex > 0 ? arguments[detailsIndex - 1] : null;
  1647.             var callback = detailsIndex + 1 < arguments.length ? arguments[detailsIndex + 1] : null;
  1648.  
  1649.             if (GetTypeName(tabId) === "integer" && tabId < 0) {
  1650.                 ThrowArgumentError(detailsIndex - 1, "Value must not be less than 0")
  1651.             }
  1652.  
  1653.             var expectedProperties = [
  1654.                 { type: "string", name: "code", optional: true },
  1655.                 { type: "string", name: "file", optional: true },
  1656.                 { type: "boolean", name: "allFrames", optional: true },
  1657.                 { type: "boolean", name: "matchAboutBlank", optional: true }, // Note: Does nothing in Microsoft Edge; required for compatibility
  1658.                 { type: "string", name: "runAt", optional: true, validator: function (value) {
  1659.                     if (value !== "document_start" && value !== "document_end" && value !== "document_idle") {
  1660.                         return "Value must be one of: [document_start, document_end, document_idle]";
  1661.                     }
  1662.                 }}];
  1663.             TestObjectProperties(detailsIndex, details, expectedProperties);
  1664.  
  1665.             var destination = {};
  1666.             destination.component = componentId_contentScript;
  1667.             destination.tabId = tabId;
  1668.             destination.frameId = GetTypeName(tabId) === "integer" ? Bhx_Parent : null;
  1669.             destination.extensionId = nativeMsBrowser.getExtensionId();
  1670.  
  1671.             var parameters = {};
  1672.             parameters.allFrames = details.allFrames || false;
  1673.  
  1674.             // Note: we ignore the run-at, since we always inject immediately
  1675.             if (details["file"] && details["code"]) {
  1676.                 throw "Error: Code and file should not be specified at the same time in the second argument.";
  1677.             } else if ("file" in details) {
  1678.                 parameters.file = details.file;
  1679.                 ExecuteGenericFunction(functionId_tabsExecuteScriptResource, destination, parameters, callback);
  1680.             } else if ("code" in details) {
  1681.                 parameters.code = details.code;
  1682.                 ExecuteGenericFunction(functionId_tabsExecuteScriptSnippet, destination, parameters, callback);
  1683.             } else {
  1684.                 throw "Error: No source code or file specified.";
  1685.             }
  1686.         }
  1687.  
  1688.         function tabs_get_indirect() {
  1689.             var expectedArguments = [
  1690.                 { type: "integer", name: "tabId" },
  1691.                 { type: "function", name: "callback" } ];
  1692.             TestParameterTypes("tabs.get", arguments, expectedArguments);
  1693.  
  1694.             if (arguments[0] < 0) {
  1695.                 throw "Error: Invalid value for argument 1. Value must not be less than 0.";
  1696.             }
  1697.  
  1698.             var destination = {};
  1699.             destination.component = componentId_frameTab;
  1700.             destination.tabId = arguments[0];
  1701.             destination.frameId = Bhx_Parent;
  1702.  
  1703.             var parameter;
  1704.             return ExecuteGenericFunction(functionId_tabsGet, destination, parameter, arguments[1]);
  1705.         }
  1706.  
  1707.         function tabs_getCurrent_indirect() {
  1708.             var expectedArguments = [
  1709.                 { type: "function", name: "callback" } ];
  1710.             TestParameterTypes("tabs.getCurrent", arguments, expectedArguments);
  1711.  
  1712.             var destination = {};
  1713.             destination.component = componentId_frameTab;
  1714.             destination.tabId = Bhx_Parent;
  1715.             destination.frameId = Bhx_Parent;
  1716.  
  1717.             var parameter;
  1718.             return ExecuteGenericFunction(functionId_tabsGet, destination, parameter, arguments[0]);
  1719.         }
  1720.  
  1721.         function tabs_insertCSS_indirect() {
  1722.             var expectedArguments = [
  1723.                 { type: "integer", name: "tabId", optional: true },
  1724.                 { type: "object", name: "details" },
  1725.                 { type: "function", name: "callback", optional: true } ];
  1726.             TestParameterTypes("tabs.insertCSS", arguments, expectedArguments);
  1727.  
  1728.             // This is required because there are optional arguments followed by mandatory ones.
  1729.             var detailsIndex = 1;
  1730.             if (arguments.length === 1 || GetTypeName(arguments[0]) === "object") {
  1731.                 // If there is only one argument, or the first argument is an object, we should assume it is the details.
  1732.                 detailsIndex = 0;
  1733.             }
  1734.  
  1735.             var details = arguments[detailsIndex];
  1736.             var tabId = detailsIndex > 0 ? arguments[detailsIndex - 1] : null;
  1737.             var callback = detailsIndex + 1 < arguments.length ? arguments[detailsIndex + 1] : null;
  1738.  
  1739.             if (GetTypeName(tabId) === "integer" && tabId < 0) {
  1740.                 ThrowArgumentError(detailsIndex - 1, "Value must not be less than 0")
  1741.             }
  1742.  
  1743.             var expectedProperties = [
  1744.                 { type: "string", name: "code", optional: true },
  1745.                 { type: "string", name: "file", optional: true },
  1746.                 { type: "boolean", name: "allFrames", optional: true },
  1747.                 { type: "boolean", name: "matchAboutBlank", optional: true }, // Note: Does nothing in Microsoft Edge; required for compatibility
  1748.                 { type: "string", name: "runAt", optional: true, validator: function (value) {
  1749.                     if (value !== "document_start" && value !== "document_end" && value !== "document_idle") {
  1750.                         return "Value must be one of: [document_start, document_end, document_idle]";
  1751.                     }
  1752.                 }}];
  1753.             TestObjectProperties(detailsIndex, details, expectedProperties);
  1754.  
  1755.             var destination = {};
  1756.             destination.component = componentId_contentScript;
  1757.             destination.tabId = tabId;
  1758.             destination.frameId = GetTypeName(tabId) === "integer" ? Bhx_Parent : null;
  1759.             destination.extensionId = nativeMsBrowser.getExtensionId();
  1760.  
  1761.             var parameters = {};
  1762.             parameters.allFrames = details.allFrames || false;
  1763.  
  1764.             // Note: we ignore the run-at, since we always inject immediately
  1765.             if (details["file"] && details["code"]) {
  1766.                 throw "Error: Code and file should not be specified at the same time in the second argument.";
  1767.             } else if ("file" in details) {
  1768.                 parameters.file = details.file;
  1769.                 ExecuteGenericFunction(functionId_tabsInjectCssResource, destination, parameters, callback);
  1770.             } else if ("code" in details) {
  1771.                 parameters.code = details.code;
  1772.                 ExecuteGenericFunction(functionId_tabsInjectCssSnippet, destination, parameters, callback);
  1773.             } else {
  1774.                 throw "Error: No source code or file specified.";
  1775.             }
  1776.         }
  1777.  
  1778.         function tabs_query_indirect() {
  1779.             var expectedArguments = [
  1780.                 { type: "object", name: "queryInfo" },
  1781.                 { type: "function", name: "callback" } ];
  1782.             TestParameterTypes("tabs.query", arguments, expectedArguments);
  1783.  
  1784.             var expectedProperties = [
  1785.                 { type: "boolean", name: "active", optional: true },
  1786.                 { type: "boolean", name: "pinned", optional: true }, // Note: Does nothing in Microsoft Edge; required for compatibility
  1787.                 { type: "boolean", name: "highlighted", optional: true }, // Note: Does nothing in Microsoft Edge; required for compatibility
  1788.                 { type: "boolean", name: "currentWindow", optional: true },
  1789.                 { type: "boolean", name: "lastFocusedWindow", optional: true },
  1790.                 { type: "string", name: "status", optional: true, validator: function (value) {
  1791.                     if (value !== "loading" && value !== "complete") {
  1792.                         return "Value must be one of: [loading, complete]";
  1793.                     }
  1794.                 }},
  1795.                 { type: "string", name: "title", optional: true },
  1796.                 { type: "string or array", name: "url", optional: true, validator: function (value) {
  1797.                     if (typeof(value) !== "string") {
  1798.                         for (var i = 0; i < value.length; i++) {
  1799.                             if (typeof(value[i]) !== "string") {
  1800.                                 return "All items in the array must be strings";
  1801.                             }
  1802.                         }
  1803.                     }
  1804.                 }},
  1805.                 { type: "integer", name: "windowId", optional: true, validator: function (index) { if (index < -2) return "Value must not be less than -2"; } },
  1806.                 { type: "string", name: "windowType", optional: true, validator: function (value) {
  1807.                     if (value !== "normal" && value !== "popup" && value !== "panel" && value !== "app") {
  1808.                         return "Value must be one of: [normal, popup, panel, app]";
  1809.                     }
  1810.                 }},
  1811.                 { type: "integer", name: "index", optional: true, validator: function (index) { if (index < 0) return "Value must not be less than 0"; } } ];
  1812.             TestObjectProperties(0, arguments[0], expectedProperties);
  1813.  
  1814.             var destination = {};
  1815.             var windowIdWantsCurrentWindow = false;
  1816.             if (GetTypeName(arguments[0].windowId) === "integer") {
  1817.                 if (arguments[0].windowId == -2) {
  1818.                     windowIdWantsCurrentWindow = true;
  1819.                 } else if (arguments[0].windowId >= 0) {
  1820.                     destination.frameId = arguments[0].windowId;
  1821.                 }
  1822.             }
  1823.  
  1824.             var parameters = {};
  1825.  
  1826.             if (GetTypeName(arguments[0].lastFocusedWindow) === "boolean") {
  1827.                 parameters.lastFocusedWindow = arguments[0].lastFocusedWindow;
  1828.             }
  1829.  
  1830.             // Check for any contradictions.
  1831.             var contradictionFound = false;
  1832.             if (GetTypeName(arguments[0].currentWindow) === "boolean" || windowIdWantsCurrentWindow) {
  1833.                 if (GetTypeName(arguments[0].currentWindow) === "boolean" && !(arguments[0].currentWindow) && windowIdWantsCurrentWindow) {
  1834.                     contradictionFound = true;
  1835.                 } else {
  1836.                     var currentWindowId = nativeMsBrowser.currentWindowId();
  1837.                     if (windowIdWantsCurrentWindow || arguments[0].currentWindow) {
  1838.                         if (currentWindowId == Bhx_TopMost) {
  1839.                             // This means we're looking for the last focused window.
  1840.                             if (parameters.hasOwnProperty("lastFocusedWindow") && !parameters.lastFocusedWindow) {
  1841.                                 contradictionFound = true;
  1842.                             } else {
  1843.                                 parameters.lastFocusedWindow = true;
  1844.                             }
  1845.                         } else {
  1846.                             // We want a specific window.
  1847.                             if (currentWindowId != destination.frameId) {
  1848.                                 contradictionFound = true;
  1849.                             } else {
  1850.                                 destination.frameId = currentWindowId;
  1851.                             }
  1852.                         }
  1853.                     } else {
  1854.                         if (currentWindowId == Bhx_TopMost) {
  1855.                             // This means we're avoiding the last focused window.
  1856.                             if (parameters.hasOwnProperty("lastFocusedWindow") && parameters.lastFocusedWindow) {
  1857.                                 contradictionFound = true;
  1858.                             } else {
  1859.                                 parameters.lastFocusedWindow = false;
  1860.                             }
  1861.                         } else {
  1862.                             // We're avoiding a specific window.
  1863.                             if (!(destination.hasOwnProperty("frameId"))) {
  1864.                                 parameters.windowToAvoid = currentWindowId;
  1865.                             } else if (currentWindowId == destination.frameId) {
  1866.                                 contradictionFound = true;
  1867.                             }
  1868.                         }
  1869.                     }
  1870.                 }
  1871.             }
  1872.  
  1873.             if (contradictionFound) {
  1874.                 // If we've found a contradiction return an empty array.
  1875.                 arguments[1]([]);
  1876.                 return;
  1877.             }
  1878.  
  1879.             if (GetTypeName(arguments[0].windowType) === "string") {
  1880.                 if (arguments[0].windowType == "panel" || arguments[0].windowType == "app") {
  1881.                     // These window types don't exist for Microsoft Edge. They will find no tabs.
  1882.                     arguments[1]([]);
  1883.                     return;
  1884.                 } else {
  1885.                     parameters.inPopupWindow = (arguments[0].windowType == "popup");
  1886.                 }
  1887.             }
  1888.  
  1889.             if (GetTypeName(arguments[0].active) === "boolean") {
  1890.                 parameters.active = arguments[0].active;
  1891.             }
  1892.  
  1893.             if (GetTypeName(arguments[0].status) === "string") {
  1894.                 parameters.isLoading = (arguments[0].status == "loading");
  1895.             }
  1896.  
  1897.             if (GetTypeName(arguments[0].index) === "integer") {
  1898.                 parameters.index = arguments[0].index;
  1899.             }
  1900.  
  1901.             callbackCheckParameters = {};
  1902.  
  1903.             if (GetTypeName(arguments[0].url) === "string") {
  1904.                 callbackCheckParameters.url = [ arguments[0].url ];
  1905.             } else if (GetTypeName(arguments[0].url) === "array") {
  1906.                 callbackCheckParameters.url = arguments[0].url;
  1907.             }
  1908.  
  1909.             if (GetTypeName(arguments[0].title) === "string") {
  1910.                 callbackCheckParameters.title = arguments[0].title;
  1911.             }
  1912.  
  1913.             var functionId;
  1914.             if (destination.hasOwnProperty("frameId")) {
  1915.                 destination.component = componentId_frame;
  1916.                 functionId = functionId_tabsQuery;
  1917.             } else if (parameters.lastFocusedWindow) {
  1918.                 // It must be both existent and true.
  1919.                 destination.component = componentId_frame;
  1920.                 destination.frameId = Bhx_TopMost;
  1921.                 functionId = functionId_tabsQuery;
  1922.                 delete parameters.lastFocusedWindow;
  1923.             } else {
  1924.                 destination.component = componentId_manager;
  1925.                 functionId = functionId_tabsQueryMultiple;
  1926.             }
  1927.  
  1928.             var callback = arguments[1];
  1929.             var callbackWrapper = function (tabArray) {
  1930.                 if (tabArray) {
  1931.                     if (callbackCheckParameters.url) {
  1932.                         for (var i = 0; i < callbackCheckParameters.url.length; i++) {
  1933.                             for (var j = tabArray.length - 1; j >= 0; j--) {
  1934.                                 if (!nativeMsBrowser.checkMatchesUriExpression(callbackCheckParameters.url[i], tabArray[j].url)) {
  1935.                                     tabArray.splice(j, 1);
  1936.                                 }
  1937.                             }
  1938.                         }
  1939.                     }
  1940.  
  1941.                     if (callbackCheckParameters.title) {
  1942.                         for (var j = tabArray.length - 1; j >= 0; j--) {
  1943.                             if (!nativeMsBrowser.checkMatchesGlobExpression(callbackCheckParameters.title, tabArray[j].title)) {
  1944.                                 tabArray.splice(j, 1);
  1945.                             }
  1946.                         }
  1947.                     }
  1948.  
  1949.                     var hasPermission = ExecuteGenericSynchronousFunction(functionId_permissionsCheckInternal, permissionId_tabs);
  1950.                     if (typeof(hasPermission) != "boolean" || !hasPermission) {
  1951.                         for (var j = tabArray.length - 1; j >= 0; j--) {
  1952.                             delete tabArray[j].title;
  1953.                             delete tabArray[j].url;
  1954.                         }
  1955.                     }
  1956.                 }
  1957.  
  1958.                 callback(tabArray);
  1959.             };
  1960.  
  1961.             return ExecuteGenericFunction(functionId, destination, parameters, callbackWrapper);
  1962.         }
  1963.  
  1964.         function tabs_remove_indirect() {
  1965.             var expectedArguments = [
  1966.                 { type: "integer or array", name: "tabIds" },
  1967.                 { type: "function", name: "callback", optional: true } ];
  1968.             TestParameterTypes("tabs.remove", arguments, expectedArguments);
  1969.  
  1970.             if (GetTypeName(arguments[0]) === "integer") {
  1971.                 if (arguments[0] < 0) {
  1972.                     throw "Error: Invalid value for argument 1. Value must not be less than 0.";
  1973.                 }
  1974.             } else {
  1975.                 for (var i = 0; i < arguments[0].length; i++) {
  1976.                     if (GetTypeName(arguments[0][i]) !== "integer" || arguments[0][i] < 0) {
  1977.                         throw "Error: Invalid value for argument 1. Each tab id must be a positive integer.";
  1978.                     }
  1979.                 }
  1980.             }
  1981.  
  1982.             var destination = {};
  1983.             var parameters;
  1984.             var functionId;
  1985.             if (GetTypeName(arguments[0]) === "integer") {
  1986.                 destination.component = componentId_frameTab;
  1987.                 destination.tabId = arguments[0];
  1988.                 functionId = functionId_tabsRemove;
  1989.             } else {
  1990.                 destination.component = componentId_manager;
  1991.                 parameters = arguments[0]; // The manager will need to handle this. http://osgvsowi/3629377
  1992.                 functionId = functionId_tabsRemoveMultiple;
  1993.             }
  1994.             return ExecuteGenericFunction(functionId, destination, parameters, arguments.length == 2 ? arguments[1] : null);
  1995.         }
  1996.  
  1997.         function tabs_sendMessage_createCallback(responseCallback) {
  1998.             return function () {
  1999.                 var jsonResponse = arguments[0];
  2000.                 var response; // This is initialized to undefined.
  2001.                 if (jsonResponse && jsonResponse !== 'undefined') {
  2002.                     // JSON.stringify(undefined) === undefined is packed for courier messaging as 'undefined' but JSON.parse('undefined') throws javascript error.
  2003.                     // A response of "undefined" (type string) is packed for courier messaging as '"undefined"', hence will not conflict with a response of undefined (type undefined).
  2004.                     response = nativeJSON.parse(jsonResponse);
  2005.                 }
  2006.                 responseCallback(response);
  2007.             }
  2008.         }
  2009.  
  2010.         function tabs_sendMessage_indirect(tabId, message, options, callback) {
  2011.             var expectedArguments = [
  2012.                 { type: "integer", name: "tabId" },
  2013.                 { type: "any", name: "message" },
  2014.                 { type: "object", name: "options", optional: true },
  2015.                 { type: "function", name: "responseCallback", optional: true } ];
  2016.             TestParameterTypes("tabs.executeScript", arguments, expectedArguments);
  2017.  
  2018.             if (typeof options === "function")
  2019.             {
  2020.                 callback = options;
  2021.                 options = null;
  2022.             }
  2023.  
  2024.             var responseCallback;
  2025.             if (typeof callback === "function")
  2026.             {
  2027.                 responseCallback = tabs_sendMessage_createCallback(callback);
  2028.             }
  2029.  
  2030.             if (options) {
  2031.                 var expectedProperties = [
  2032.                     { type: "integer", name: "frameId", optional: true } ];
  2033.                 TestObjectProperties(2, options, expectedProperties);
  2034.             }
  2035.  
  2036.             var destination = {};
  2037.             destination.component = componentId_contentScript;
  2038.             destination.tabId = tabId;
  2039.             destination.frameId = Bhx_Parent;
  2040.             destination.extensionId = nativeMsBrowser.getExtensionId();
  2041.  
  2042.             messageData = {};
  2043.             messageData.msg = nativeJSON.stringify(message);
  2044.             messageData.sender = { id: nativeMsBrowser.getExtensionId() };
  2045.             parameters = {};
  2046.             parameters.message = nativeJSON.stringify(messageData);
  2047.             if (options && GetTypeName(options.frameId) === "integer") {
  2048.                 parameters.frameId = options.frameId;
  2049.             }
  2050.  
  2051.             ExecuteGenericFunction(functionId_tabsSendMessage, destination, parameters, responseCallback);
  2052.         }
  2053.  
  2054.         function delayInitOnActivated() {
  2055.             var onActivatedObject = StoreNoFilterHandler(eventId_tabsOnActivated, function (sender, tabId, listeners) {
  2056.                 for (var i = 0; i < listeners.length; i++) {
  2057.                     if (typeof(listeners[i]) == "function") {
  2058.                         listeners[i](tabId);
  2059.                     }
  2060.                 }
  2061.             });
  2062.  
  2063.             var wrapOnActivated = Object.defineProperties({}, {
  2064.                 "addListener": {
  2065.                     value: onActivatedObject.addListener.bind(onActivatedObject),
  2066.                     configurable: true,
  2067.                     enumerable: true
  2068.                 },
  2069.                 "removeListener": {
  2070.                     value: onActivatedObject.removeListener.bind(onActivatedObject),
  2071.                     configurable: true,
  2072.                     enumerable: true
  2073.                 },
  2074.                 "hasListener": {
  2075.                     value: onActivatedObject.hasListener.bind(onActivatedObject),
  2076.                     configurable: true,
  2077.                     enumerable: true
  2078.                 }
  2079.             });
  2080.  
  2081.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  2082.             Object.defineProperty(window.msBrowser.tabs, "onActivated", {
  2083.                 value: wrapOnActivated,
  2084.                 configurable: true,
  2085.                 enumerable: true
  2086.             });
  2087.  
  2088.             return wrapOnActivated;
  2089.         }
  2090.  
  2091.         function delayInitOnCreated() {
  2092.             var onCreatedObject = StoreNoFilterHandler(eventId_tabsOnCreated, function (sender, tabData, listeners) {
  2093.                 var hasPermission = ExecuteGenericSynchronousFunction(functionId_permissionsCheckInternal, permissionId_tabs);
  2094.                 if (typeof(hasPermission) != "boolean" || !hasPermission) {
  2095.                     delete tabData.title;
  2096.                     delete tabData.url;
  2097.                 }
  2098.  
  2099.                 for (var i = 0; i < listeners.length; i++) {
  2100.                     if (typeof(listeners[i]) == "function") {
  2101.                         listeners[i](tabData);
  2102.                     }
  2103.                 }
  2104.             });
  2105.  
  2106.             var wrapOnCreated = Object.defineProperties({}, {
  2107.                 "addListener": {
  2108.                     value: onCreatedObject.addListener.bind(onCreatedObject),
  2109.                     configurable: true,
  2110.                     enumerable: true
  2111.                 },
  2112.                 "removeListener": {
  2113.                     value: onCreatedObject.removeListener.bind(onCreatedObject),
  2114.                     configurable: true,
  2115.                     enumerable: true
  2116.                 },
  2117.                 "hasListener": {
  2118.                     value: onCreatedObject.hasListener.bind(onCreatedObject),
  2119.                     configurable: true,
  2120.                     enumerable: true
  2121.                 }
  2122.             });
  2123.  
  2124.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  2125.             Object.defineProperty(window.msBrowser.tabs, "onCreated", {
  2126.                 value: wrapOnCreated,
  2127.                 configurable: true,
  2128.                 enumerable: true
  2129.             });
  2130.  
  2131.             return wrapOnCreated;
  2132.         }
  2133.  
  2134.         function delayInitOnUpdated() {
  2135.             var onUpdatedObject = StoreNoFilterHandler(eventId_tabsOnUpdated, function (sender, updateData, listeners) {
  2136.                 if (updateData.tabData && updateData.changeType) {
  2137.                     var changeInfo = {};
  2138.                     if (updateData.changeType === "url") {
  2139.                         changeInfo.url = updateData.tabData.url;
  2140.                     } else if (updateData.changeType === "status") {
  2141.                         changeInfo.status = updateData.tabData.status;
  2142.                     }
  2143.  
  2144.                     var hasPermission = ExecuteGenericSynchronousFunction(functionId_permissionsCheckInternal, permissionId_tabs);
  2145.                     if (typeof(hasPermission) != "boolean" || !hasPermission) {
  2146.                         delete updateData.tabData.title;
  2147.                         delete updateData.tabData.url;
  2148.                     }
  2149.  
  2150.                     for (var i = 0; i < listeners.length; i++) {
  2151.                         if (typeof(listeners[i]) == "function") {
  2152.                             listeners[i](updateData.tabData.id, changeInfo, updateData.tabData);
  2153.                         }
  2154.                     }
  2155.                 }
  2156.             });
  2157.  
  2158.             var wrapOnUpdated = Object.defineProperties({}, {
  2159.                 "addListener": {
  2160.                     value: onUpdatedObject.addListener.bind(onUpdatedObject),
  2161.                     configurable: true,
  2162.                     enumerable: true
  2163.                 },
  2164.                 "removeListener": {
  2165.                     value: onUpdatedObject.removeListener.bind(onUpdatedObject),
  2166.                     configurable: true,
  2167.                     enumerable: true
  2168.                 },
  2169.                 "hasListener": {
  2170.                     value: onUpdatedObject.hasListener.bind(onUpdatedObject),
  2171.                     configurable: true,
  2172.                     enumerable: true
  2173.                 }
  2174.             });
  2175.  
  2176.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  2177.             Object.defineProperty(window.msBrowser.tabs, "onUpdated", {
  2178.                 value: wrapOnUpdated,
  2179.                 configurable: true,
  2180.                 enumerable: true
  2181.             });
  2182.  
  2183.             return wrapOnUpdated;
  2184.         }
  2185.  
  2186.         // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  2187.         Object.defineProperty(window.msBrowser, "tabs", {
  2188.             value: wrapTabs,
  2189.             configurable: true,
  2190.             enumerable: true
  2191.         });
  2192.         return wrapTabs;
  2193.     }
  2194.  
  2195.     function delayInitWindows()
  2196.     {
  2197.         var wrapWindows = Object.defineProperties({}, {
  2198.             "get": {
  2199.                 value: windows_get_indirect.bind(nativeMsBrowser),
  2200.                 configurable: true,
  2201.                 enumerable: true
  2202.             },
  2203.             "getCurrent": {
  2204.                 value: windows_getCurrent_indirect.bind(nativeMsBrowser),
  2205.                 configurable: true,
  2206.                 enumerable: true
  2207.             },
  2208.             "getLastFocused": {
  2209.                 value: windows_getLastFocused_indirect.bind(nativeMsBrowser),
  2210.                 configurable: true,
  2211.                 enumerable: true
  2212.             },
  2213.             "getAll": {
  2214.                 value: windows_getAll_indirect.bind(nativeMsBrowser),
  2215.                 configurable: true,
  2216.                 enumerable: true
  2217.             },
  2218.             "create": {
  2219.                 value: windows_create_indirect.bind(nativeMsBrowser),
  2220.                 configurable: true,
  2221.                 enumerable: true
  2222.             },
  2223.             "update": {
  2224.                 value: windows_update_indirect.bind(nativeMsBrowser),
  2225.                 configurable: true,
  2226.                 enumerable: true
  2227.             },
  2228.             "onCreated": {
  2229.                 get: delayInitOnCreated,
  2230.                 configurable: true,
  2231.                 enumerable: true
  2232.             },
  2233.             "onRemoved": {
  2234.                 get: delayInitOnRemoved,
  2235.                 configurable: true,
  2236.                 enumerable: true
  2237.             },
  2238.             "onFocusChanged": {
  2239.                 get: delayInitOnFocusChanged,
  2240.                 configurable: true,
  2241.                 enumerable: true
  2242.             },
  2243.             "WINDOW_ID_NONE": {
  2244.                 value: -1,
  2245.                 configurable: true,
  2246.                 enumerable: true
  2247.             },
  2248.             "WINDOW_ID_CURRENT": {
  2249.                 value: -2,
  2250.                 configurable: true,
  2251.                 enumerable: true
  2252.             }
  2253.         });
  2254.  
  2255.         function windows_get_indirect() {
  2256.             var expectedArguments = [
  2257.                 { type: "integer", name: "windowId" },
  2258.                 { type: "object", name: "getInfo", optional: true },
  2259.                 { type: "function", name: "callback" } ];
  2260.             TestParameterTypes("windows.get", arguments, expectedArguments);
  2261.  
  2262.             var callback = arguments[1];
  2263.             var parameters = null;
  2264.             if (GetTypeName(arguments[1]) === "object") {
  2265.                 var expectedProperties = [
  2266.                     { type: "boolean", name: "populate", optional: true }];
  2267.                 TestObjectProperties(1, arguments[1], expectedProperties);
  2268.                 callback = arguments[2];
  2269.                 parameters = arguments[1];
  2270.             }
  2271.            
  2272.             var destination = {};
  2273.             destination.component = componentId_frame;
  2274.             destination.frameId = arguments[0];
  2275.  
  2276.             return ExecuteGenericFunction(functionId_windowsGet, destination, parameters, callback);
  2277.         }
  2278.  
  2279.         function windows_getCurrent_indirect() {
  2280.             var expectedArguments = [
  2281.                 { type: "object", name: "getInfo", optional: true },
  2282.                 { type: "function", name: "callback" } ];
  2283.             TestParameterTypes("windows.getCurrent", arguments, expectedArguments);
  2284.  
  2285.             var callback = arguments[0];
  2286.             var parameters = null;
  2287.             if (GetTypeName(arguments[0]) === "object") {
  2288.                 var expectedProperties = [
  2289.                     { type: "boolean", name: "populate", optional: true }];
  2290.                 TestObjectProperties(0, arguments[0], expectedProperties);
  2291.                 callback = arguments[1];
  2292.                 parameters = arguments[0];
  2293.             }
  2294.            
  2295.             var destination = {};
  2296.             destination.component = componentId_frame;
  2297.             destination.frameId = nativeMsBrowser.currentWindowId();
  2298.            
  2299.             return ExecuteGenericFunction(functionId_windowsGet, destination, parameters, callback);
  2300.         }
  2301.  
  2302.         function windows_getLastFocused_indirect() {
  2303.             var expectedArguments = [
  2304.                 { type: "object", name: "getInfo", optional: true },
  2305.                 { type: "function", name: "callback" } ];
  2306.             TestParameterTypes("windows.getCurrent", arguments, expectedArguments);
  2307.  
  2308.             var callback = arguments[0];
  2309.             var parameters = null;
  2310.             if (GetTypeName(arguments[0]) === "object") {
  2311.                 var expectedProperties = [
  2312.                     { type: "boolean", name: "populate", optional: true }];
  2313.                 TestObjectProperties(0, arguments[0], expectedProperties);
  2314.                 callback = arguments[1];
  2315.                 parameters = arguments[0];
  2316.             }
  2317.            
  2318.             var destination = {};
  2319.             destination.component = componentId_frame;
  2320.             destination.frameId = Bhx_TopMost;
  2321.            
  2322.             return ExecuteGenericFunction(functionId_windowsGet, destination, parameters, callback);
  2323.         }
  2324.  
  2325.         function windows_getAll_indirect() {
  2326.             var expectedArguments = [
  2327.                 { type: "object", name: "getInfo", optional: true },
  2328.                 { type: "function", name: "callback" }];
  2329.             TestParameterTypes("windows.getAll", arguments, expectedArguments);
  2330.  
  2331.             var callback = arguments[0];
  2332.             var parameters = null;
  2333.             if (GetTypeName(arguments[0]) === "object") {
  2334.                 var expectedProperties = [
  2335.                     { type: "boolean", name: "populate", optional: true }];
  2336.                 TestObjectProperties(0, arguments[0], expectedProperties);
  2337.                 callback = arguments[1];
  2338.                 parameters = arguments[0];
  2339.             }
  2340.  
  2341.             var destination = {};
  2342.             destination.component = componentId_manager;
  2343.             return ExecuteGenericFunction(functionId_windowsGetAll, destination, parameters, callback);
  2344.         }
  2345.  
  2346.         function windows_create_indirect() {
  2347.             var expectedArguments = [
  2348.                 { type: "object", name: "createData", optional: true },
  2349.                 { type: "function", name: "callback", optional: true }];
  2350.             TestParameterTypes("windows.create", arguments, expectedArguments);
  2351.  
  2352.             if (arguments[0] != nullptr && GetTypeName(arguments[0]) === "object") {
  2353.                 var expectedProperties = [
  2354.                     { type: "array", name: "url", optional: true },
  2355.                     { type: "integer", name: "tabId", optional: true, validator: function (value) { if (value < 0) return "Value must be non-negative integer."; } },
  2356.                     { type: "integer", name: "left", optional: true },
  2357.                     { type: "integer", name: "top", optional: true },
  2358.                     { type: "integer", name: "width", optional: true, validator: function (value) { if (value <= 0) return "Value must be positive integer."; } },
  2359.                     { type: "integer", name: "height", optional: true, validator: function (value) { if (value <= 0) return "Value must be positive integer."; } },
  2360.                     { type: "boolean", name: "focused", optional: true },
  2361.                     { type: "boolean", name: "inPrivate", optional: true },
  2362.                     { type: "string", name: "type", optional: true, validator: function (value) {
  2363.                         if (value != "normal" && value != "popup") {
  2364.                             return "Value must be one of: [normal, popup]";
  2365.                         }
  2366.                     }},
  2367.                     { type: "string", name: "state", optional: true, validator: function (value) {
  2368.                         if (value != "normal" && value != "minimized" && value != "maximized" && value != "fullscreen") {
  2369.                             return "Value must be one of: [normal, minimized, maximized, fullscreen]";
  2370.                         }
  2371.                     }} ];
  2372.                 TestObjectProperties(0, arguments[0], expectedProperties);
  2373.             }
  2374.  
  2375.             // TODO: Implement windows.Create http://osgvsowi/3418411
  2376.         }
  2377.        
  2378.         function windows_update_indirect() {
  2379.             var expectedArguments = [
  2380.                 { type: "integer", name: "windowId" },
  2381.                 { type: "object", name: "updateData", optional: true },
  2382.                 { type: "function", name: "callback", optional: true }];
  2383.             TestParameterTypes("windows.update", arguments, expectedArguments);
  2384.  
  2385.             var expectedProperties = [
  2386.                 { type: "integer", name: "left", optional: true },
  2387.                 { type: "integer", name: "top", optional: true },
  2388.                 { type: "integer", name: "width", optional: true, validator: function (value) { if (value <= 0) return "Value must be positive integer."; } },
  2389.                 { type: "integer", name: "height", optional: true, validator: function (value) { if (value <= 0) return "Value must be positive integer."; } },
  2390.                 { type: "boolean", name: "focused", optional: true },
  2391.                 { type: "boolean", name: "drawAttention", optional: true },
  2392.                 { type: "string", name: "state", optional: true, validator: function (value) {
  2393.                     if (value != "normal" && value != "minimized" && value != "maximized" && value != "fullscreen") {
  2394.                         return "Value must be one of: [normal, minimized, maximized, fullscreen]";
  2395.                     }
  2396.                 }} ];
  2397.             TestObjectProperties(1, arguments[1], expectedProperties);
  2398.  
  2399.             var destination = {};
  2400.             destination.component = componentId_frame;
  2401.             destination.frameId = arguments[0];
  2402.  
  2403.             return ExecuteGenericFunction(functionId_windowsUpdate, destination, arguments[1], arguments.length == 3 ? arguments[2] : null);
  2404.         }
  2405.  
  2406.         function delayInitOnCreated() {
  2407.             var onCreatedObject = StoreNoFilterHandler(eventId_windowsOnCreated, function (sender, windowData, listeners) {
  2408.                 for (var i = 0; i < listeners.length; i++) {
  2409.                     if (typeof(listeners[i]) == "function") {
  2410.                         listeners[i](windowData);
  2411.                     }
  2412.                 }
  2413.             });
  2414.  
  2415.             var wrapOnCreated = Object.defineProperties({}, {
  2416.                 "addListener": {
  2417.                     value: onCreatedObject.addListener.bind(onCreatedObject),
  2418.                     configurable: true,
  2419.                     enumerable: true
  2420.                 },
  2421.                 "removeListener": {
  2422.                     value: onCreatedObject.removeListener.bind(onCreatedObject),
  2423.                     configurable: true,
  2424.                     enumerable: true
  2425.                 },
  2426.                 "hasListener": {
  2427.                     value: onCreatedObject.hasListener.bind(onCreatedObject),
  2428.                     configurable: true,
  2429.                     enumerable: true
  2430.                 }
  2431.             });
  2432.  
  2433.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  2434.             Object.defineProperty(window.msBrowser.windows, "onCreated", {
  2435.                 value: wrapOnCreated,
  2436.                 configurable: true,
  2437.                 enumerable: true
  2438.             });
  2439.  
  2440.             return wrapOnCreated;
  2441.         }
  2442.  
  2443.         function delayInitOnRemoved() {
  2444.             var onRemovedObject = StoreNoFilterHandler(eventId_windowsOnRemoved, function (sender, windowData, listeners) {
  2445.                 for (var i = 0; i < listeners.length; i++) {
  2446.                     if (typeof(listeners[i]) == "function") {
  2447.                         listeners[i](windowData);
  2448.                     }
  2449.                 }
  2450.             });
  2451.  
  2452.             var wrapOnRemoved = Object.defineProperties({}, {
  2453.                 "addListener": {
  2454.                     value: onRemovedObject.addListener.bind(onRemovedObject),
  2455.                     configurable: true,
  2456.                     enumerable: true
  2457.                 },
  2458.                 "removeListener": {
  2459.                     value: onRemovedObject.removeListener.bind(onRemovedObject),
  2460.                     configurable: true,
  2461.                     enumerable: true
  2462.                 },
  2463.                 "hasListener": {
  2464.                     value: onRemovedObject.hasListener.bind(onRemovedObject),
  2465.                     configurable: true,
  2466.                     enumerable: true
  2467.                 }
  2468.             });
  2469.  
  2470.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  2471.             Object.defineProperty(window.msBrowser.windows, "onRemoved", {
  2472.                 value: wrapOnRemoved,
  2473.                 configurable: true,
  2474.                 enumerable: true
  2475.             });
  2476.  
  2477.             return wrapOnRemoved;
  2478.         }
  2479.  
  2480.         function delayInitOnFocusChanged() {
  2481.             var onFocusChangedObject = StoreNoFilterHandler(eventId_windowsOnFocusChanged, function (sender, windowData, listeners) {
  2482.                 for (var i = 0; i < listeners.length; i++) {
  2483.                     if (typeof(listeners[i]) == "function") {
  2484.                         listeners[i](windowData);
  2485.                     }
  2486.                 }
  2487.             });
  2488.  
  2489.             var wrapOnFocusChanged = Object.defineProperties({}, {
  2490.                 "addListener": {
  2491.                     value: onFocusChangedObject.addListener.bind(onFocusChangedObject),
  2492.                     configurable: true,
  2493.                     enumerable: true
  2494.                 },
  2495.                 "removeListener": {
  2496.                     value: onFocusChangedObject.removeListener.bind(onFocusChangedObject),
  2497.                     configurable: true,
  2498.                     enumerable: true
  2499.                 },
  2500.                 "hasListener": {
  2501.                     value: onFocusChangedObject.hasListener.bind(onFocusChangedObject),
  2502.                     configurable: true,
  2503.                     enumerable: true
  2504.                 }
  2505.             });
  2506.  
  2507.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  2508.             Object.defineProperty(window.msBrowser.windows, "onFocusChanged", {
  2509.                 value: wrapOnFocusChanged,
  2510.                 configurable: true,
  2511.                 enumerable: true
  2512.             });
  2513.  
  2514.             return wrapOnFocusChanged;
  2515.         }
  2516.  
  2517.         // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  2518.         Object.defineProperty(window.msBrowser, "windows", {
  2519.             value: wrapWindows,
  2520.             configurable: true,
  2521.             enumerable: true
  2522.         });
  2523.  
  2524.         return wrapWindows;
  2525.     }
  2526.  
  2527.     function delayInitWebNavigation() {
  2528.         var wrapWebNavigation;
  2529.         var hasPermission = ExecuteGenericSynchronousFunction(functionId_permissionsCheckInternal, permissionId_webNavigation);
  2530.         if (typeof(hasPermission) == "boolean" && hasPermission) {
  2531.             wrapWebNavigation = Object.defineProperties({}, {
  2532.                 "getFrame": {
  2533.                     value: webNavigation_getFrame_indirect.bind(nativeMsBrowser.webNavigation),
  2534.                     configurable: true,
  2535.                     enumerable: true
  2536.                 },
  2537.                 "getAllFrames": {
  2538.                     value: webNavigation_getAllFrames_indirect.bind(nativeMsBrowser.webNavigation),
  2539.                     configurable: true,
  2540.                     enumerable: true
  2541.                 },
  2542.                 "onBeforeNavigate": {
  2543.                     get: delayInitOnBeforeNavigate,
  2544.                     configurable: true,
  2545.                     enumerable: true
  2546.                 }
  2547.             });
  2548.         }
  2549.  
  2550.         function webNavigation_getFrame_indirect(details, callback) {
  2551.  
  2552.             // Test number of arguments and their types
  2553.             const expectedArguments = [
  2554.                 { type: "object", name: "details" },
  2555.                 { type: "function", name: "callback" }
  2556.             ];
  2557.             TestParameterTypes("webNavigation.getFrame", arguments, expectedArguments);
  2558.  
  2559.             // Verify the types and presence of the 'details' objects properties.
  2560.             const expectedProperties = [
  2561.                 { type: "integer", name: "tabId" },
  2562.                 { type: "integer", name: "processId" },
  2563.                 { type: "integer", name: "frameId" }
  2564.             ];
  2565.             TestObjectProperties(0, details, expectedProperties);
  2566.  
  2567.             // Setup the message to native code and register the JavaScript callback function.
  2568.             const destination = {
  2569.                 component: componentId_tabTrident,
  2570.                 tabId: details.tabId,
  2571.                 frameId: Bhx_Parent
  2572.             };
  2573.  
  2574.             // The only data needed as parameters are the 'frameId' and 'processId'.
  2575.             // This 'frameId' refers to frames such as iFrames in the page.
  2576.             const parameters = {
  2577.                 frameId: details.frameId,
  2578.                 processId: details.processId
  2579.             };
  2580.  
  2581.             // Documentation states that the result should be null when the frame doesn't exist or there is another error.
  2582.             const wrapCallback = function (details) {
  2583.                 callback(details || null);
  2584.             }
  2585.  
  2586.             return ExecuteGenericFunction(functionId_getFrame, destination, parameters, wrapCallback);
  2587.         }
  2588.  
  2589.         function webNavigation_getAllFrames_indirect(details, callback) {
  2590.  
  2591.             // Test number of arguments and their types
  2592.             const expectedArguments = [
  2593.                 { type: "object", name: "details" },
  2594.                 { type: "function", name: "callback" }
  2595.             ];
  2596.             TestParameterTypes("webNavigation.getAllFrames", arguments, expectedArguments);
  2597.  
  2598.             // Verify the types and presence of the 'details' objects properties.
  2599.             const expectedProperties = [
  2600.                 { type: "integer", name: "tabId" }
  2601.             ];
  2602.             TestObjectProperties(0, details, expectedProperties);
  2603.  
  2604.             // Setup the message to native code and register the JavaScript callback function.
  2605.             const destination = {
  2606.                 component: componentId_tabTrident,
  2607.                 tabId: details.tabId,
  2608.                 frameId: Bhx_Parent
  2609.             };
  2610.  
  2611.             var parameters; // "getAllFrames" only needs the tabId which has already been represented in the "destination" address.
  2612.  
  2613.             // Documentation states that the result should be null when the frame doesn't exist or there is another error.
  2614.             const wrapCallback = function (details) {
  2615.                 callback(details || null);
  2616.             }
  2617.  
  2618.             return ExecuteGenericFunction(functionId_getAllFrames, destination, parameters, wrapCallback);
  2619.         }
  2620.  
  2621.         function delayInitOnBeforeNavigate() {
  2622.             var wrapOnBeforeNavigate = Object.defineProperties({}, {
  2623.                 "addListener": {
  2624.                     value: nativeMsBrowser.webNavigation.onBeforeNavigate.addListener.bind(nativeMsBrowser.webNavigation.onBeforeNavigate),
  2625.                     configurable: true,
  2626.                     enumerable: true
  2627.                 }
  2628.             });
  2629.  
  2630.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  2631.             Object.defineProperty(window.msBrowser.webNavigation, "onBeforeNavigate", {
  2632.                 value: wrapOnBeforeNavigate,
  2633.                 configurable: true,
  2634.                 enumerable: true
  2635.             });
  2636.  
  2637.             return wrapOnBeforeNavigate;
  2638.         }
  2639.  
  2640.         // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  2641.         Object.defineProperty(window.msBrowser, "webNavigation", {
  2642.             value: wrapWebNavigation,
  2643.             configurable: true,
  2644.             enumerable: true
  2645.         });
  2646.  
  2647.         return wrapWebNavigation;
  2648.     }
  2649.  
  2650.     // BEGINE WebRequestCode
  2651.     /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2652.     function delayInitWebRequest() {
  2653.         var wrapWebRequest;
  2654.         var hasPermission = ExecuteGenericSynchronousFunction(functionId_permissionsCheckInternal, permissionId_webRequest);
  2655.         if (typeof(hasPermission) == "boolean" && hasPermission) {
  2656.             wrapWebRequest = Object.defineProperties({}, {
  2657.                 "onBeforeRequest": {
  2658.                     get: delayInitOnBeforeRequest.bind(nativeMsBrowser.webRequest),
  2659.                     configurable: true,
  2660.                     enumerable: true
  2661.                 },
  2662.                 "onBeforeSendHeaders": {
  2663.                     get: delayInitOnBeforeSendHeaders.bind(nativeMsBrowser.webRequest),
  2664.                     configurable: true,
  2665.                     enumerable: true
  2666.                 },
  2667.                 "onSendHeaders": {
  2668.                     get: delayInitOnSendHeaders.bind(nativeMsBrowser.webRequest),
  2669.                     configurable: true,
  2670.                     enumerable: true
  2671.                 },
  2672.                 "onHeadersReceived": {
  2673.                     get: delayInitOnHeadersReceived.bind(nativeMsBrowser.webRequest),
  2674.                     configurable: true,
  2675.                     enumerable: true
  2676.                 },
  2677.                 "onAuthRequired": {
  2678.                     get: delayInitOnAuthRequired.bind(nativeMsBrowser.webRequest),
  2679.                     configurable: true,
  2680.                     enumerable: true
  2681.                 },
  2682.                 "onResponseStarted": {
  2683.                     get: delayInitOnResponseStarted.bind(nativeMsBrowser.webRequest),
  2684.                     configurable: true,
  2685.                     enumerable: true
  2686.                 },
  2687.                 "onBeforeRedirect": {
  2688.                     get: delayInitOnBeforeRedirect.bind(nativeMsBrowser.webRequest),
  2689.                     configurable: true,
  2690.                     enumerable: true
  2691.                 },
  2692.                 "onCompleted": {
  2693.                     get: delayInitOnCompleted.bind(nativeMsBrowser.webRequest),
  2694.                     configurable: true,
  2695.                     enumerable: true
  2696.                 },
  2697.                 "onErrorOccurred": {
  2698.                     get: delayInitonErrorOccurred.bind(nativeMsBrowser.webRequest),
  2699.                     configurable: true,
  2700.                     enumerable: true
  2701.                 },
  2702.                 "handlerBehaviorChanged": {
  2703.                     value: webRequest_HandlerBehaviorChanged.bind(nativeMsBrowser.webRequest),
  2704.                     configurable: true,
  2705.                     enumerable: true
  2706.                 },
  2707.                 "MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES": {
  2708.                     get:webRequest_MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES.bind(nativeMsBrowser.webRequest),
  2709.                     set:webRequest_MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES.bind(nativeMsBrowser.webRequest),
  2710.                     configurable: true,
  2711.                     enumerable: true
  2712.                 }
  2713.             });
  2714.         }
  2715.  
  2716.         // Expected extraSpecInfo for respective WebRequest event
  2717.         var expectedWebRequstTypesForEvent = {
  2718.             onBeforeRequest: ["blocking", "requestBody"],
  2719.             onBeforeSendHeaders: ["blocking", "requestHeaders"],
  2720.             onSendHeaders: ["requestHeaders"],
  2721.             onHeadersReceived: ["blocking", "responseHeaders"],
  2722.             onAuthRequired: ["blocking", "responseHeaders", "asyncBlocking"],
  2723.             onResponseStarted: ["responseHeaders"],
  2724.             onBeforeRedirect: ["responseHeaders"],
  2725.             onCompleted: ["responseHeaders"],
  2726.             onErrorOccurred: [],
  2727.         }
  2728.  
  2729.         // Expected eventDetails for respective WebRequest event
  2730.         var expectedWebRuestEventDetails = {
  2731.             onBeforeRequest: ["requestId", "url", "method", "frameId", "parentFrameId", "requestBody", "tabId", "type", "timeStamp"],
  2732.             onBeforeSendHeaders: ["requestId", "url", "method", "frameId", "parentFrameId", "tabId", "type", "timeStamp", "requestHeaders"],
  2733.             onSendHeaders: ["requestId", "url", "method", "frameId", "parentFrameId", "tabId", "type", "timeStamp", "requestHeaders"],
  2734.             onHeadersReceived: ["requestId", "url", "method", "frameId", "parentFrameId", "tabId", "type", "timeStamp", "statusLine", "requestHeaders", "statusCode"],
  2735.             onAuthRequired: ["requestId", "url", "method", "frameId", "parentFrameId", "tabId", "type", "timeStamp", "scheme", "realm", "challenger", "isProxy", "requestHeaders", "statusLine", "statusCode"],
  2736.             onBeforeRedirect: ["requestId", "url", "method", "frameId", "parentFrameId", "tabId", "type", "timeStamp", "ip", "fromCache", "requestHeaders", "statusLine", "statusCode"],
  2737.             onResponseStarted: ["requestId", "url", "method", "frameId", "parentFrameId", "tabId", "type", "timeStamp", "ip", "fromCache", "requestHeaders", "statusLine", "statusCode", "redirectUrl"],
  2738.             onCompleted: ["requestId", "url", "method", "frameId", "parentFrameId", "tabId", "type", "timeStamp", "ip", "fromCache", "requestHeaders", "statusLine", "statusCode"],
  2739.             onErrorOccurred: ["requestId", "url", "method", "frameId", "parentFrameId", "tabId", "type", "timeStamp", "ip", "fromCache", "error"]
  2740.         }
  2741.  
  2742.         // Expected blocking response for respective WebRequest event
  2743.         var expectedWebRequestBlockingResponse = {
  2744.             onBeforeRequest: ["cancel", "redirectUrl"],
  2745.             onBeforeSendHeaders: ["cancel", "requestHeaders"],
  2746.             onSendHeaders: ["cancel"],
  2747.             onHeadersReceived: ["redirectUrl", "responseHeaders"],
  2748.             onAuthRequired: ["authCredentials"],
  2749.             onBeforeRedirect: [],
  2750.             onResponseStarted: [],
  2751.             onCompleted: [],
  2752.             onErrorOccurred: []
  2753.         }
  2754.  
  2755.         // Expected callback blocking response by its events name
  2756.         var expectedWebRequestEventsForBlockingResponse = {
  2757.             cancel: ["onBeforeRequest", "onBeforeSendHeaders", "onSendHeaders"],
  2758.             redirectUrl: ["onBeforeRequest", "onHeadersReceived"],
  2759.             requestHeaders: ["onBeforeSendHeaders"],
  2760.             responseHeaders: ["onHeadersReceived"],
  2761.             authCredentials: ["onAuthRequired"]
  2762.         }
  2763.  
  2764.         // The below order should be match with WebRequestEventId, BhxMessage.h.
  2765.         var webRequestEvents = ["onBeforeRequest", "onBeforeSendHeaders", "onSendHeaders", "onHeadersReceived", "onAuthRequired",
  2766.             "onBeforeRedirect", "onResponseStarted", "onCompleted", "onErrorOccurred"];
  2767.  
  2768.         // BEGIN Events INIT
  2769.         //////////////////////////////////////////////////////////////////////////////////////////////
  2770.         function delayInitOnBeforeRequest() {
  2771.             var wrapOnBeforeRequest = Object.defineProperties({}, {
  2772.                 "addListener": {
  2773.                     value: webRequest_onBeforeRequest_addListener_indirect.bind(nativeMsBrowser.webRequest.onBeforeRequest),
  2774.                     configurable: true,
  2775.                     enumerable: true
  2776.                 },
  2777.                 "removeListener": {
  2778.                     value: webRequest_onBeforeRequest_removeListener_indirect.bind(nativeMsBrowser.webRequest.onBeforeRequest),
  2779.                     configurable: true,
  2780.                     enumerable: true
  2781.                 }
  2782.             });
  2783.  
  2784.             function webRequest_onBeforeRequest_addListener_indirect() {
  2785.                 TestFilteredEventListenerParameters("webRequest.onBeforeRequest.addListener", arguments, expectedWebRequstTypesForEvent["onBeforeRequest"]);
  2786.                 webRequest_addListener_indirect("onBeforeRequest", arguments);
  2787.             }
  2788.  
  2789.             function webRequest_onBeforeRequest_removeListener_indirect() {
  2790.                 TestFilteredEventRemoveListenerParameters("webRequest.onBeforeRequest.removeListener", arguments);
  2791.                 webRequest_removeListener_indirect("onBeforeRequest", arguments);
  2792.             }
  2793.  
  2794.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  2795.             Object.defineProperty(window.webRequest, "onBeforeRequest", {
  2796.                 value: wrapOnBeforeRequest,
  2797.                 configurable: true,
  2798.                 enumerable: true
  2799.             });
  2800.  
  2801.             return wrapOnBeforeRequest;
  2802.         }
  2803.  
  2804.         function delayInitOnBeforeSendHeaders() {
  2805.             var wrapOnBeforeSendHeaders = Object.defineProperties({}, {
  2806.                 "addListener": {
  2807.                     value: webRequest_onBeforeSendHeaders_addListener_indirect.bind(nativeMsBrowser.webRequest.onBeforeSendHeaders),
  2808.                     configurable: true,
  2809.                     enumerable: true
  2810.                 },
  2811.                 "removeListener": {
  2812.                     value: webRequest_onBeforeSendHeaders_removeListener_indirect.bind(nativeMsBrowser.webRequest.onBeforeSendHeaders),
  2813.                     configurable: true,
  2814.                     enumerable: true
  2815.                 }
  2816.             });
  2817.  
  2818.             function webRequest_onBeforeSendHeaders_addListener_indirect() {
  2819.                 TestFilteredEventListenerParameters("webRequest.onBeforeSendHeaders.addListener", arguments, expectedWebRequstTypesForEvent["onBeforeSendHeaders"]);
  2820.                 webRequest_addListener_indirect("onBeforeSendHeaders", arguments);
  2821.             }
  2822.  
  2823.             function webRequest_onBeforeSendHeaders_removeListener_indirect() {
  2824.                 TestFilteredEventRemoveListenerParameters("webRequest.onBeforeSendHeaders.removeListener", arguments);
  2825.                 webRequest_removeListener_indirect("onBeforeSendHeaders", arguments);
  2826.             }
  2827.  
  2828.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  2829.             Object.defineProperty(window.webRequest, "onBeforeSendHeaders", {
  2830.                 value: wrapOnBeforeSendHeaders,
  2831.                 configurable: true,
  2832.                 enumerable: true
  2833.             });
  2834.  
  2835.             return wrapOnBeforeSendHeaders;
  2836.         }
  2837.         function delayInitOnSendHeaders() {
  2838.             var wrapOnSendHeaders = Object.defineProperties({}, {
  2839.                 "addListener": {
  2840.                     value: webRequest_onSendHeaders_addListener_indirect.bind(nativeMsBrowser.webRequest.onSendHeaders),
  2841.                     configurable: true,
  2842.                     enumerable: true
  2843.                 },
  2844.                 "removeListener": {
  2845.                     value: webRequest_onSendHeaders_removeListener_indirect.bind(nativeMsBrowser.webRequest.onSendHeaders),
  2846.                     configurable: true,
  2847.                     enumerable: true
  2848.                 }
  2849.             });
  2850.  
  2851.             function webRequest_onSendHeaders_addListener_indirect() {
  2852.                 TestFilteredEventListenerParameters("webRequest.onSendHeaders.addListener", arguments, expectedWebRequstTypesForEvent["onSendHeaders"]);
  2853.                 webRequest_addListener_indirect("onSendHeaders", arguments);
  2854.             }
  2855.  
  2856.             function webRequest_onSendHeaders_removeListener_indirect() {
  2857.                 TestFilteredEventRemoveListenerParameters("webRequest.onSendHeaders.removeListener", arguments);
  2858.                 webRequest_removeListener_indirect("onSendHeaders", arguments);
  2859.             }
  2860.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  2861.             Object.defineProperty(window.webRequest, "onSendHeaders", {
  2862.                 value: wrapOnSendHeaders,
  2863.                 configurable: true,
  2864.                 enumerable: true
  2865.             });
  2866.  
  2867.             return wrapOnSendHeaders;
  2868.         }
  2869.  
  2870.         function delayInitOnHeadersReceived() {
  2871.             var wrapOnHeadersReceived = Object.defineProperties({}, {
  2872.                 "addListener": {
  2873.                     value: webRequest_onHeadersReceived_addListener_indirect.bind(nativeMsBrowser.webRequest.onHeadersReceived),
  2874.                     configurable: true,
  2875.                     enumerable: true
  2876.                 },
  2877.                 "removeListener": {
  2878.                     value: webRequest_onHeadersReceived_removeListener_indirect.bind(nativeMsBrowser.webRequest.onHeadersReceived),
  2879.                     configurable: true,
  2880.                     enumerable: true
  2881.                 }
  2882.             });
  2883.  
  2884.             function webRequest_onHeadersReceived_addListener_indirect() {
  2885.                 TestFilteredEventListenerParameters("webRequest.onHeadersReceived.addListener", arguments, expectedWebRequstTypesForEvent["onHeadersReceived"]);
  2886.                 webRequest_addListener_indirect("onHeadersReceived", arguments);
  2887.             }
  2888.  
  2889.             function webRequest_onHeadersReceived_removeListener_indirect() {
  2890.                 TestFilteredEventRemoveListenerParameters("webRequest.onHeadersReceived.removeListener", arguments);
  2891.                 webRequest_removeListener_indirect("onHeadersReceived", arguments);
  2892.             }
  2893.  
  2894.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  2895.             Object.defineProperty(window.webRequest, "onHeadersReceived", {
  2896.                 value: wrapOnHeadersReceived,
  2897.                 configurable: true,
  2898.                 enumerable: true
  2899.             });
  2900.  
  2901.             return wrapOnHeadersReceived;
  2902.         }
  2903.  
  2904.         function delayInitOnAuthRequired() {
  2905.             var wrapOnAuthRequired = Object.defineProperties({}, {
  2906.                 "addListener": {
  2907.                     value: webRequest_onAuthRequired_addListener_indirect.bind(nativeMsBrowser.webRequest.onAuthRequired),
  2908.                     configurable: true,
  2909.                     enumerable: true
  2910.                 },
  2911.                 "removeListener": {
  2912.                     value: webRequest_onAuthRequired_removeListener_indirect.bind(nativeMsBrowser.webRequest.onAuthRequired),
  2913.                     configurable: true,
  2914.                     enumerable: true
  2915.                 }
  2916.             });
  2917.  
  2918.             function webRequest_onAuthRequired_addListener_indirect() {
  2919.                 TestFilteredEventListenerParameters("webRequest.onAuthRequired.addListener", arguments, expectedWebRequstTypesForEvent["onAuthRequired"]);
  2920.                 webRequest_addListener_indirect("onAuthRequired", arguments);
  2921.             }
  2922.  
  2923.             function webRequest_onAuthRequired_removeListener_indirect() {
  2924.                 TestFilteredEventRemoveListenerParameters("webRequest.onAuthRequired.removeListener", arguments);
  2925.                 webRequest_removeListener_indirect("onAuthRequired", arguments);
  2926.             }
  2927.  
  2928.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  2929.             Object.defineProperty(window.webRequest, "onAuthRequired", {
  2930.                 value: wrapOnAuthRequired,
  2931.                 configurable: true,
  2932.                 enumerable: true
  2933.             });
  2934.  
  2935.             return wrapOnAuthRequired;
  2936.         }
  2937.  
  2938.         function delayInitOnResponseStarted() {
  2939.             var wrapOnResponseStarted = Object.defineProperties({}, {
  2940.                 "addListener": {
  2941.                     value: webRequest_onResponseStarted_addListener_indirect.bind(nativeMsBrowser.webRequest.onResponseStarted),
  2942.                     configurable: true,
  2943.                     enumerable: true
  2944.                 },
  2945.                 "removeListener": {
  2946.                     value: webRequest_onResponseStarted_removeListener_indirect.bind(nativeMsBrowser.webRequest.onResponseStarted),
  2947.                     configurable: true,
  2948.                     enumerable: true
  2949.                 }
  2950.             });
  2951.  
  2952.             function webRequest_onResponseStarted_addListener_indirect() {
  2953.                 TestFilteredEventListenerParameters("webRequest.onResponseStarted.addListener", arguments, expectedWebRequstTypesForEvent["onResponseStarted"]);
  2954.                 webRequest_addListener_indirect("onResponseStarted", arguments);
  2955.             }
  2956.  
  2957.             function webRequest_onResponseStarted_removeListener_indirect() {
  2958.                 TestFilteredEventRemoveListenerParameters("webRequest.onResponseStarted.removeListener", arguments);
  2959.                 webRequest_removeListener_indirect("onResponseStarted", arguments);
  2960.             }
  2961.  
  2962.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  2963.             Object.defineProperty(window.webRequest, "onResponseStarted", {
  2964.                 value: wrapOnResponseStarted,
  2965.                 configurable: true,
  2966.                 enumerable: true
  2967.             });
  2968.  
  2969.             return wrapOnResponseStarted;
  2970.         }
  2971.  
  2972.         function delayInitOnBeforeRedirect() {
  2973.             var wrapOnBeforeRedirect = Object.defineProperties({}, {
  2974.                 "addListener": {
  2975.                     value: webRequest_onBeforeRedirect_addListener_indirect.bind(nativeMsBrowser.webRequest.onBeforeRedirect),
  2976.                     configurable: true,
  2977.                     enumerable: true
  2978.                 },
  2979.                 "removeListener": {
  2980.                     value: webRequest_onBeforeRedirect_removeListener_indirect.bind(nativeMsBrowser.webRequest.onBeforeRedirect),
  2981.                     configurable: true,
  2982.                     enumerable: true
  2983.                 }
  2984.             });
  2985.  
  2986.             function webRequest_onBeforeRedirect_addListener_indirect() {
  2987.                 TestFilteredEventListenerParameters("webRequest.onBeforeRedirect.addListener", arguments, expectedWebRequstTypesForEvent["onBeforeRedirect"]);
  2988.                 webRequest_addListener_indirect("onBeforeRedirect", arguments);
  2989.             }
  2990.  
  2991.             function webRequest_onBeforeRedirect_removeListener_indirect() {
  2992.                 TestFilteredEventRemoveListenerParameters("webRequest.onBeforeRedirect.removeListener", arguments);
  2993.                 webRequest_removeListener_indirect("onBeforeRequest", arguments);
  2994.             }
  2995.  
  2996.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  2997.             Object.defineProperty(window.webRequest, "onBeforeRedirect", {
  2998.                 value: wrapOnBeforeRedirect,
  2999.                 configurable: true,
  3000.                 enumerable: true
  3001.             });
  3002.  
  3003.             return wrapOnBeforeRedirect;
  3004.         }
  3005.  
  3006.         function delayInitOnCompleted() {
  3007.             var wrapOnCompleted = Object.defineProperties({}, {
  3008.                 "addListener": {
  3009.                     value: webRequest_onCompleted_addListener_indirect.bind(nativeMsBrowser.webRequest.onCompleted),
  3010.                     configurable: true,
  3011.                     enumerable: true
  3012.                 },
  3013.                 "removeListener": {
  3014.                     value: webRequest_onCompleted_removeListener_indirect.bind(nativeMsBrowser.webRequest.onCompleted),
  3015.                     configurable: true,
  3016.                     enumerable: true
  3017.                 }
  3018.             });
  3019.  
  3020.             function webRequest_onCompleted_addListener_indirect() {
  3021.                 TestFilteredEventListenerParameters("webRequest.onCompleted.addListener", arguments, expectedWebRequstTypesForEvent["onCompleted"]);
  3022.                 webRequest_addListener_indirect("onCompleted", arguments);
  3023.             }
  3024.  
  3025.             function webRequest_onCompleted_removeListener_indirect() {
  3026.                 TestFilteredEventRemoveListenerParameters("webRequest.onCompleted.removeListener", arguments);
  3027.                 webRequest_removeListener_indirect("onCompleted", arguments);
  3028.             }
  3029.  
  3030.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  3031.             Object.defineProperty(window.webRequest, "onCompleted", {
  3032.                 value: wrapOnCompleted,
  3033.                 configurable: true,
  3034.                 enumerable: true
  3035.             });
  3036.  
  3037.             return wrapOnCompleted;
  3038.         }
  3039.  
  3040.         function delayInitonErrorOccurred() {
  3041.             var wrapOnErrorOccurred = Object.defineProperties({}, {
  3042.                 "addListener": {
  3043.                     value: webRequest_onErrorOccurred_addListener_indirect.bind(nativeMsBrowser.webRequest.onErrorOccurred),
  3044.                     configurable: true,
  3045.                     enumerable: true
  3046.                 },
  3047.                 "removeListener": {
  3048.                     value: webRequest_onErrorOccurred_removeListener_indirect.bind(nativeMsBrowser.webRequest.onErrorOccurred),
  3049.                     configurable: true,
  3050.                     enumerable: true
  3051.                 }
  3052.             });
  3053.  
  3054.             function webRequest_onErrorOccurred_addListener_indirect() {
  3055.                 TestFilteredEventListenerParameters("webRequest.onErrorOccurred.addListener", arguments, expectedWebRequstTypesForEvent["onErrorOccurred"]);
  3056.                 webRequest_addListener_indirect("onErrorOccurred", arguments);
  3057.             }
  3058.  
  3059.             function webRequest_onErrorOccurred_removeListener_indirect() {
  3060.                 TestFilteredEventRemoveListenerParameters("webRequest.onErrorOccurred.removeListener", arguments);
  3061.                 webRequest_removeListener_indirect("onErrorOccurred", arguments);
  3062.             }
  3063.  
  3064.             // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  3065.             Object.defineProperty(window.webRequest, "onErrorOccurred", {
  3066.                 value: wrapOnErrorOccurred,
  3067.                 configurable: true,
  3068.                 enumerable: true
  3069.             });
  3070.  
  3071.             return wrapOnErrorOccurred;
  3072.         }
  3073.  
  3074.         function webRequest_HandlerBehaviorChanged() {
  3075.             var expectedArguments = [{ type: "function", name: "callback", optional: true }];
  3076.             TestParameterTypes("webRequest.HandlerBehaviorChanged", arguments, expectedArguments);
  3077.             // It does nothing more than making effective of the removeListener calling,
  3078.             if (arguments[0]) {
  3079.                 setImmediate(arguments[0]); // call callback function immediately to comply with spec.
  3080.             }
  3081.         }
  3082.  
  3083.         var MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES = 20;
  3084.         function webRequest_MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES(value) {
  3085.             // Our current implementation of HandlerBehaviorChanged doesn't require calling
  3086.             // handlerBehaviorChanged to make listener changes effective and therefore our implementation doesn't
  3087.             // really need handlerBehaviorChanged and MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES.
  3088.             // We implement this just to allow developers to migrate extension to our system without changing their code.
  3089.             if (value){
  3090.                 MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES = value;
  3091.             } else {
  3092.                 return MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES;
  3093.             }
  3094.         }
  3095.  
  3096.         function webRequest_addListener_indirect(eventName, providedArguments) {
  3097.             // Append extraInfoSpec argument to the filters
  3098.             var eventIndex = webRequestEvents.indexOf(eventName);
  3099.             if (eventIndex == -1) {
  3100.                 throw "unexpected eventName: " + eventName + ", or internal error";
  3101.             }
  3102.  
  3103.             var nativeFilteredEvent = nativeMsBrowser.webRequest.getFilteredEvent(eventIndex);
  3104.             if (!nativeFilteredEvent) {
  3105.                 throw "unexpected eventName: " + eventName + ", or internal error";
  3106.             }
  3107.  
  3108.             function clone(filter) {
  3109.                 // filter always exist for the webRequest requestFilters
  3110.                 var copy = {};
  3111.                 for (var property in filter) {
  3112.                     if (filter.hasOwnProperty(property)) {
  3113.                         copy[property] = filter[property];
  3114.                     }
  3115.                 }
  3116.                 return copy;
  3117.             }
  3118.  
  3119.             var copyFilters = clone(providedArguments[1]);
  3120.             if (copyFilters && providedArguments[2]) {
  3121.                 copyFilters.extraInfoSpec = providedArguments[2];
  3122.             }
  3123.  
  3124.             // call native AddListener  
  3125.             //  - wrapper listener, which is callback for dispatch
  3126.             //  - real listern, which is used for removeListner key
  3127.             //  - copyFilters if exist
  3128.             var realListener = providedArguments[0];
  3129.             if (copyFilters) {
  3130.                 nativeFilteredEvent.addListener(webRequest_messagingListener_createCallback(eventName, realListener, copyFilters), realListener, JSON.stringify(copyFilters));
  3131.             } else {
  3132.                 nativeFilteredEvent.addListener(webRequest_messagingListener_createCallback(eventName, realListener, copyFilters), realListener);
  3133.             }
  3134.         }
  3135.  
  3136.         function webRequest_removeListener_indirect(eventName, providedArguments) {
  3137.             // Append extraInfoSpec argument to the filters
  3138.             var eventIndex = webRequestEvents.indexOf(eventName);
  3139.             if (eventIndex == -1) {
  3140.                 throw "unexpected eventName: " + eventName + ", or internal error";
  3141.             }
  3142.  
  3143.             var nativeFilteredEvent = nativeMsBrowser.webRequest.getFilteredEvent(eventIndex);
  3144.             if (!nativeFilteredEvent) {
  3145.                 throw "unexpected eventName: " + eventName + ", or internal error";
  3146.             }
  3147.  
  3148.             var realListener = providedArguments[0];
  3149.             nativeFilteredEvent.removeListener(realListener);
  3150.         }
  3151.  
  3152.         function webRequest_messagingListener_createCallback(eventName, realListener, filters) {
  3153.             // The function is callback from the native webRequest event.
  3154.             function isExtraInfoSpecExist(info, extraInfoSpec) {
  3155.                 var exist = false;
  3156.                 if (extraInfoSpec) {
  3157.                     for (var i = 0; i < extraInfoSpec.length; i++) {
  3158.                         if (extraInfoSpec[i] === info) {
  3159.                             exist = true;
  3160.                             break;
  3161.                         }
  3162.                     }
  3163.                 }
  3164.                 return exist;
  3165.             }
  3166.  
  3167.             filters = filters || {};
  3168.  
  3169.             // Save blocking/asyncBlocking info
  3170.             var blocking = isExtraInfoSpecExist('blocking', filters.extraInfoSpec);
  3171.             var asyncBlocking = isExtraInfoSpecExist('asyncBlocking', filters.extraInfoSpec);
  3172.  
  3173.             // Pass callback function from native dispatch.
  3174.             return function (jsonEventDetails, context, tabId, frameId) {
  3175.                 // Callback from the native dispatch
  3176.                 //
  3177.  
  3178.                 // Filtering based on the input tablId and windowId.
  3179.                 // If these values exist, callback function will be forwarded to caller only when it is match.
  3180.                 var skipCallback = false;
  3181.                 if (filters.tabId) {
  3182.                     skipCallback = (filters.tabId !== tabId);
  3183.                 }
  3184.  
  3185.                 if (filters.windowId) {
  3186.                     skipCallback = (filters.windowId !== frameId);
  3187.                 }
  3188.  
  3189.                 if (!skipCallback) {
  3190.                     var eventDetails = {};
  3191.                     if (jsonEventDetails) {
  3192.                         eventDetails = nativeJSON.parse(jsonEventDetails);
  3193.                     }
  3194.  
  3195.                     // Filtering eventDetails by its expected properties
  3196.                     var trimmedEventDetails = {};
  3197.                     var allowedEventDetails = expectedWebRuestEventDetails[eventName];
  3198.                     var eventProperties = Object.keys(eventDetails);
  3199.                     for (var i = 0; i < eventProperties.length; i++) {
  3200.                         if (allowedEventDetails.indexOf(eventProperties[i]) !== -1) {
  3201.                             // Property is expected for given event
  3202.                             trimmedEventDetails[eventProperties[i]] = eventDetails[eventProperties[i]];
  3203.                         }
  3204.                     }
  3205.  
  3206.                     // Callback to app listener handler
  3207.                     var retVal;
  3208.                     if (asyncBlocking) {
  3209.                         try {
  3210.                             realListener(trimmedEventDetails, function asyncCallback(authDetails) {
  3211.                                 // Callback from app for onAuthRequired
  3212.                                 var authDetails = authDetails || {};
  3213.  
  3214.                                 // Send message directly to the tab process (destination)
  3215.                                 var destination = {};
  3216.                                 destination.component = componentId_webRequest;
  3217.                                 destination.tabId = tabId;
  3218.                                 destination.frameId = frameId;
  3219.  
  3220.                                 // Polyfill pass 'async' keyword and extension will collect all async returns and
  3221.                                 // pass counts to the tab process so it can handle all blocking and async blockings together for
  3222.                                 // single dispatch reply.
  3223.                                 context = context | (1 << 63); // toggle MSB for async response identification.
  3224.                                 ExecuteGenericFunction(messageId_ReplyId, destination, authDetails, context);
  3225.                             });
  3226.                             retVal = "async";
  3227.                         } catch (e) {
  3228.                             // swallow exception from the app code and return as 'noReturn' so tab process doesn't have to wait async callback.
  3229.                             retVal = "noReturn";
  3230.                         }
  3231.                     } else {
  3232.                         try {
  3233.                             if (blocking) {
  3234.                                 retVal = realListener(trimmedEventDetails);
  3235.                             } else {
  3236.                                 realListener(trimmedEventDetails);
  3237.                             }
  3238.                         } catch (e) {
  3239.                             // swallow exception from the app code
  3240.                             retVal = null;
  3241.                         }
  3242.  
  3243.                         // Validate retValue if it exist
  3244.                         if (blocking && retVal) {
  3245.                             var responseKeys = Object.keys(retVal);
  3246.                             var invalidResponse = false;
  3247.  
  3248.                             var errorMsg = "";
  3249.                             for (var i = 0; i < responseKeys.length; i++) {
  3250.                                 if (!expectedWebRequestEventsForBlockingResponse[responseKeys[i]]) { // If key is blockResource properties
  3251.                                     invalidResponse = true;
  3252.                                     errorMsg = "Response error: Invalid property name for blocking response: " + "[" + responseKeys[i] + "]. " + "expected properties are" + Object.keys(expectedWebRequestEventsForBlockingResponse).toString();
  3253.                                 } else if (expectedWebRequestEventsForBlockingResponse[responseKeys[i]].indexOf(eventName) === -1) {
  3254.                                     invalidResponse = true;
  3255.                                     errorMsg = "Response error: Unexpected property name for blocking response on given event. Value must be " + "[" + expectedWebRequestBlockingResponse[eventName].toString() + "]";
  3256.                                 } else {
  3257.                                     // validate redirectUri value
  3258.                                     if (responseKeys[i] === 'redirectUrl') {
  3259.                                         try {
  3260.                                             var url = new URL(retVal['redirectUrl']);
  3261.                                         } catch (e) {
  3262.                                             invalidResponse = true;
  3263.                                             errorMsg = "Response error: Invalid value of redirectUrl: " + retVal['redirectUrl'] + " is not a valid URL.";
  3264.                                         }
  3265.                                     }
  3266.                                 }
  3267.  
  3268.                                 if (invalidResponse) {
  3269.                                     // It log an error instead of throw exception as developer do not expect exception when they return from the handler.
  3270.                                     console.error(errorMsg);
  3271.  
  3272.                                     // Return no return results due to invalid response block
  3273.                                     retVal = null;
  3274.                                     break;
  3275.                                 }
  3276.                             }
  3277.                         }
  3278.                     }
  3279.                 }
  3280.  
  3281.                 // no return value, it set the 'noReturn'
  3282.                 retVal = retVal || "noReturn";
  3283.                 return nativeJSON.stringify(retVal);
  3284.             }
  3285.         }
  3286.  
  3287.         function TestFilteredEventListenerParameters(functionName, functionArguments, exptecedProperties) {
  3288.             function TestRequestFilters(filters) {
  3289.                 var valid = true;
  3290.                 var expectedFilterProperties = [
  3291.                     {
  3292.                         type: "array", name: "urls", optional: false, canValidate: false, validator: function (urls) {
  3293.                             // Validate URLs
  3294.                             for (var i = 0; i < urls.length; i++) {
  3295.                                 validateUrlMatchPattern(urls[i]);
  3296.                             }
  3297.                         }
  3298.                     },
  3299.                     {
  3300.                         type: "array", name: "types", optional: true, canValidate: true, validator: function (providedProperties) {
  3301.                             var expectedTypeProperties = ["main_frame", "sub_frame", "stylesheet", "script", "image", "object", "xmlhttprequest", "other"]
  3302.  
  3303.                             var valid = true;
  3304.                             for (var i = 0, length = providedProperties.length; i < length; i++) {
  3305.                                 if (expectedTypeProperties.indexOf(providedProperties[i]) === -1) {
  3306.                                     ThrowArgumentError(1, "Property 'types': Value must be one of: " + "[" + expectedTypeProperties.toString() + "]");
  3307.                                 }
  3308.                             }
  3309.                         }
  3310.                     },
  3311.                     {
  3312.                         type: "integer", name: "tabId", optional: true, canValidate: true
  3313.                     },
  3314.                     {
  3315.                         type: "integer", name: "windowId", optional: true, canValidate: true
  3316.                     },
  3317.                 ]
  3318.  
  3319.                 TestObjectProperties(1, filters, expectedFilterProperties);
  3320.             }
  3321.  
  3322.             function TestExtraInfoSpec(providedExtraInfo, exptecedProperties) {
  3323.                 for (var i = 0; i < providedExtraInfo.length; i++) {
  3324.                     var found = false;
  3325.                     if (exptecedProperties.indexOf(providedExtraInfo[i]) === -1) {
  3326.                         ThrowArgumentError(2, "Value must be one of: " + "[" + exptecedProperties.toString() + "]");
  3327.                     }
  3328.  
  3329.                     if (providedExtraInfo[i] == "blocking") {
  3330.                         var hasPermission = ExecuteGenericSynchronousFunction(functionId_permissionsCheckInternal, permissionId_webRequestBlocking);
  3331.                         if (typeof(hasPermission) != "boolean" || !hasPermission) {
  3332.                             ThrowArgumentError(2, "The extension requires the blocking web request permission to add blocking listeners.");
  3333.                         }
  3334.                     }
  3335.                 }
  3336.             }
  3337.  
  3338.             var expectedArguments = [
  3339.                 { type: "function", name: "callback", optional: false },
  3340.                 { type: "object", name: "filters", optional: false },
  3341.                 { type: "array", name: "extraInfoSpec", optional: true }];
  3342.  
  3343.             TestParameterTypes(functionName, functionArguments, expectedArguments);
  3344.  
  3345.             if (functionArguments[1]) {
  3346.                 // filter parameter is not optional for filtered event listener
  3347.                 TestRequestFilters(functionArguments[1]);
  3348.             } else {
  3349.                 throw "filter argument is needed";
  3350.             }
  3351.  
  3352.             if (functionArguments[2]) {
  3353.                 TestExtraInfoSpec(functionArguments[2], exptecedProperties);
  3354.             }
  3355.         }
  3356.  
  3357.         function TestFilteredEventRemoveListenerParameters(functionName, functionArguments) {
  3358.             var expectedArguments = [
  3359.                 { type: "function", name: "callback", optional: false },
  3360.                 ];
  3361.             TestParameterTypes(functionName, functionArguments, expectedArguments);
  3362.         }
  3363.  
  3364.         // Replace delayInit with the initialized object to avoid repeated calls to delayInit.
  3365.         Object.defineProperty(window, "webRequest", {
  3366.             value: wrapWebRequest,
  3367.             configurable: true,
  3368.             enumerable: true
  3369.         });
  3370.  
  3371.         return wrapWebRequest;
  3372.     }
  3373.     // END WebRequestCode
  3374.     /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  3375.  
  3376.     function getURL_indirect(resource) {
  3377.         var extensionId = nativeMsBrowser.getExtensionId();
  3378.         var url = "ms-browser-extension://" + extensionId;
  3379.  
  3380.         if (resource.substr(0, 1) !== "/") {
  3381.             url += "/";
  3382.         }
  3383.  
  3384.         url += resource;
  3385.  
  3386.         return url;
  3387.     }
  3388.  
  3389.     // The functions below are for parameter validation.
  3390.  
  3391.     function validateUrlMatchPattern(url) {
  3392.         // check if all urls success
  3393.         if (url === "<all_urls>") {
  3394.             return;
  3395.         }
  3396.  
  3397.         // check
  3398.         var index = url.search("://");
  3399.         if (index === -1) {
  3400.             ThrowArgumentError(1, "Property 'urls': Invalid url syntax " + url);
  3401.         } else {
  3402.             function validateScheme(scheme) {
  3403.                 // input: "http://", "https://"
  3404.                 var expectedSchemes = ["http://", "https://", "file://", "ftp://", "*://"];
  3405.  
  3406.                 if (expectedSchemes.indexOf(scheme) === -1) {
  3407.                     ThrowArgumentError(1, "Property 'urls': Invalid url scheme: " + scheme);
  3408.                 }
  3409.             }
  3410.  
  3411.             function checkLocalHost(hostAndPath) {
  3412.                 var pathIndex = hostAndPath.indexOf("/");
  3413.                 if (pathIndex === 0) {
  3414.                     // Only file scheme allow local host: file:///
  3415.                     if (scheme !== "file://") {
  3416.                         ThrowArgumentError(1, "Property 'urls': Local host is not allowed for given scheme: " + scheme);
  3417.                     } else {
  3418.                         return;
  3419.                     }
  3420.                 }
  3421.             }
  3422.  
  3423.             function validateHost(hostAndPath, pathIndex) {
  3424.                 var starIndex = hostAndPath.indexOf("*");
  3425.                 if (starIndex !== -1 && starIndex < pathIndex) {
  3426.                     // star exist inside a host name
  3427.                     if (starIndex !== 0 ||
  3428.                         (hostAndPath[1] !== "." && hostAndPath[1] !== "/")) {
  3429.                         // valid host name with the star is only that "*.", "*"
  3430.                         ThrowArgumentError(1, "Property 'urls': Invalid url host name: " + hostAndPath.substr(0, hostAndPath.indexOf("/")));
  3431.                     }
  3432.                 }
  3433.             }
  3434.  
  3435.             var hostAndPathIndex = index + "://".length;
  3436.             var scheme = url.substring(0, hostAndPathIndex);
  3437.             validateScheme(scheme); // get the scheme name with "://"
  3438.  
  3439.             var hostAndPath = url.substring(hostAndPathIndex);  // hostAndPath: eg. "www.bing.com/default.html", "*/", "*abc/*"
  3440.             checkLocalHost(hostAndPath);
  3441.  
  3442.             var pathIndex = hostAndPath.indexOf("/");
  3443.             if (pathIndex === -1) {
  3444.                 // no path delimieter
  3445.                 ThrowArgumentError(1, "Property 'urls': Invalid url, no path exist: " + url);
  3446.             }
  3447.             validateHost(hostAndPath, pathIndex);
  3448.         }
  3449.     }
  3450.  
  3451.     function GetTypeName(argument) {
  3452.         var type = typeof(argument);
  3453.         if (type === "number") {
  3454.             if (parseFloat(argument) == parseInt(argument)) {
  3455.                 type = "integer";
  3456.             }
  3457.         } else if (type === "object") {
  3458.             if (!argument) {
  3459.                 type = "null";
  3460.             } else if (Array.isArray(argument)) {
  3461.                 type = "array";
  3462.             }
  3463.         }
  3464.         return type;
  3465.     }
  3466.  
  3467.     function TestArgumentType(argumentType, expectedArgumentType) {
  3468.         if (expectedArgumentType === "any") {
  3469.             return true;
  3470.         } else {
  3471.             var splitArgumentTypes = expectedArgumentType.split(" or ");
  3472.             for (var i = 0; i < splitArgumentTypes.length; i++) {
  3473.                 if (argumentType === splitArgumentTypes[i]) {
  3474.                     return true;
  3475.                 }
  3476.             }
  3477.         }
  3478.         return false;
  3479.     }
  3480.  
  3481.     function ThrowTypeError(functionName, providedArguments, expectedArguments) {
  3482.         var argumentString = "";
  3483.         for (var i = 0; i < providedArguments.length; i++) {
  3484.             if (i > 0) {
  3485.                 argumentString += ", ";
  3486.             }
  3487.  
  3488.             argumentString += GetTypeName(providedArguments[i]);
  3489.         }
  3490.  
  3491.         var expectedArgumentString = "";
  3492.         for (var i = 0; i < expectedArguments.length; i++) {
  3493.              if (i > 0) {
  3494.                  expectedArgumentString += ", ";
  3495.              }
  3496.  
  3497.              if (expectedArguments[i].optional) {
  3498.                  expectedArgumentString += "optional ";
  3499.              }
  3500.  
  3501.              expectedArgumentString += expectedArguments[i].type + " " + expectedArguments[i].name;
  3502.         }
  3503.  
  3504.         throw "Error: Invocation of form " + functionName + "(" + argumentString + ") doesn't match definition " + functionName + "(" + expectedArgumentString + ").";
  3505.     }
  3506.  
  3507.     function TestParameterTypes(functionName, providedArguments, expectedArguments) {
  3508.         var valid = false;
  3509.         var skipBits = 0;
  3510.         var optionalParameterChecked = true;
  3511.  
  3512.         while (!valid && optionalParameterChecked)
  3513.         {
  3514.             var currentArgumentIndex = 0;
  3515.             var currentExpectedIndex = 0;
  3516.             var optionalParameterIndex = 0;
  3517.             optionalParameterChecked = false;
  3518.             valid = true; // Innocent until proven guilty.
  3519.  
  3520.             for (var currentExpectedIndex = 0; currentExpectedIndex < expectedArguments.length && currentArgumentIndex < providedArguments.length && valid; currentExpectedIndex++) {
  3521.                 if (expectedArguments[currentExpectedIndex].optional) {
  3522.                     var bitToCheck = 1 << optionalParameterIndex;
  3523.                     optionalParameterIndex++;
  3524.                     if (skipBits & bitToCheck === bitToCheck) {
  3525.                         continue;
  3526.                     } else {
  3527.                         optionalParameterChecked = true;
  3528.                     }
  3529.                 }
  3530.  
  3531.                 var argType = GetTypeName(providedArguments[currentArgumentIndex]);
  3532.                 if (TestArgumentType(argType, expectedArguments[currentExpectedIndex].type)) {
  3533.                     currentArgumentIndex++;
  3534.                 } else if (!expectedArguments[currentExpectedIndex].optional) {
  3535.                     valid = false;
  3536.                     break;
  3537.                 } else if (argType === "null" || argType === "undefined") {
  3538.                     // optional arguments can have null or undefined in their place.
  3539.                     currentArgumentIndex++;
  3540.                 }
  3541.             }
  3542.  
  3543.             if (valid) {
  3544.                 for (; currentExpectedIndex < expectedArguments.length; currentExpectedIndex++) {
  3545.                     if (!expectedArguments[currentExpectedIndex].optional) {
  3546.                         valid = false;
  3547.                         break;
  3548.                     }
  3549.                 }
  3550.             }
  3551.  
  3552.             if (valid && currentArgumentIndex < providedArguments.length)
  3553.             {
  3554.                 valid = false;
  3555.             }
  3556.             skipBits++;
  3557.         }
  3558.  
  3559.         if (!valid) {
  3560.             ThrowTypeError(functionName, providedArguments, expectedArguments);
  3561.         }
  3562.         return valid;
  3563.     }
  3564.  
  3565.     function ThrowArgumentError(argumentIndex, errorString)
  3566.     {
  3567.         throw "Error: Invalid value for argument " + (argumentIndex + 1) + ". " + errorString + ".";
  3568.     }
  3569.  
  3570.     function TestObjectProperties(argumentIndex, objectToTest, propertyInformation)
  3571.     {
  3572.         var errorString = "";
  3573.         var valid = true; // Innocent until proven guilty.
  3574.         var requiredProperties = {};
  3575.  
  3576.         for (var i = 0; i < propertyInformation.length; i++) {
  3577.             if (!propertyInformation[i].optional) {
  3578.                 requiredProperties[propertyInformation[i].name] = true;
  3579.             }
  3580.         }
  3581.  
  3582.         var stringPrefix = "";
  3583.  
  3584.         for (var prop in objectToTest) {
  3585.             var found = false;
  3586.             for (var i = 0; i < propertyInformation.length; i++) {
  3587.                 if (prop === propertyInformation[i].name) {
  3588.                     if (!propertyInformation[i].optional) {
  3589.                         requiredProperties[propertyInformation[i].name] = false;
  3590.                     }
  3591.  
  3592.                     var argType = GetTypeName(objectToTest[prop]);
  3593.                     if (TestArgumentType(argType, propertyInformation[i].type)) {
  3594.                        if (propertyInformation[i].validator) {
  3595.                            var validationError = propertyInformation[i].validator(objectToTest[prop]);
  3596.                            if (validationError) {
  3597.                                errorString += stringPrefix + "Property '" + prop + "': " + validationError;
  3598.                                stringPrefix = ", ";
  3599.                                valid = false;
  3600.                            }
  3601.                        }
  3602.                     } else if (!propertyInformation[i].optional || (argType !== "null" && argType !== "undefined")) {
  3603.                        if (propertyInformation[i].type.indexOf(" or ") === -1) {
  3604.                            errorString += stringPrefix + "Property '" + prop + "': Expected '" + propertyInformation[i].type + "' but got '" + GetTypeName(objectToTest[prop]) + "'";
  3605.                        } else {
  3606.                            errorString += stringPrefix + "Property '" + prop + "': Value does not match any valid type choices";
  3607.                        }
  3608.                        stringPrefix = ", ";
  3609.                        valid = false;
  3610.                     }
  3611.                     found = true;
  3612.                     break;
  3613.                 }
  3614.             }
  3615.  
  3616.             if (!found) {
  3617.                 errorString += stringPrefix + "Property '" + prop + "': Unexpected property";
  3618.                 stringPrefix = ", ";
  3619.                 valid = false;
  3620.             }
  3621.         }
  3622.  
  3623.         for (var prop in requiredProperties) {
  3624.             if (requiredProperties[prop]) {
  3625.                 errorString += stringPrefix + "Property '" + prop + "': Property is required";
  3626.                 stringPrefix = ", ";
  3627.                 valid = false;
  3628.             }
  3629.         }
  3630.  
  3631.         if (!valid)
  3632.         {
  3633.             ThrowArgumentError(argumentIndex, errorString);
  3634.         }
  3635.         return valid;
  3636.     }
  3637.  
  3638.     function TestListenerArguments(functionArguments)
  3639.     {
  3640.         if (functionArguments.length > 1)
  3641.         {
  3642.             throw "Error: This event does not support filters.";
  3643.             return false;
  3644.         }
  3645.         return true;
  3646.     }
  3647.  
  3648.     function StoreCallback(callback)
  3649.     {
  3650.         var storedId = nextCallback;
  3651.         storedCallbacks[nextCallback++] = callback;
  3652.         return storedId;
  3653.     }
  3654.  
  3655.     function InvokeCallback(callbackId, parameterString)
  3656.     {
  3657.         if (callbackId < storedCallbacks.length && storedCallbacks[callbackId]) {
  3658.             var parameters;
  3659.             if (parameterString) {
  3660.                 parameters = nativeJSON.parse(parameterString);
  3661.             }
  3662.             storedCallbacks[callbackId](parameters);
  3663.             storedCallbacks[callbackId] = null;
  3664.         }
  3665.     }
  3666.  
  3667.     function StoreNoFilterHandler(eventId, handlerFunc)
  3668.     {
  3669.         var listenerHolder = { event: eventId, handler: handlerFunc, listeners: [],
  3670.             addListener: function (listener) {
  3671.                 if (arguments.length > 1) {
  3672.                     throw "Error: This event does not support filters.";
  3673.                 }
  3674.                 this.listeners.push(listener);
  3675.  
  3676.                 if (this.listeners.length == 1) {
  3677.                     ExecuteGenericFunction(functionId_notifyEventListener, { component: componentId_manager }, this.event);
  3678.                 }
  3679.             },
  3680.             removeListener: function (listener) {
  3681.                 for (var i = 0; i < this.listeners.length; i++) {
  3682.                     if (this.listeners[i] === listener) {
  3683.                         this.listeners.splice(i, 1);
  3684.                     }
  3685.                 }
  3686.             },
  3687.             hasListener: function (listener) {
  3688.                 for (var i = 0; i < this.listeners.length; i++) {
  3689.                     if (this.listeners[i] === listener) {
  3690.                         return true;
  3691.                     }
  3692.                 }
  3693.                 return false;
  3694.             } };
  3695.  
  3696.         storedHandlers.push(listenerHolder);
  3697.         return listenerHolder;
  3698.     }
  3699.  
  3700.     function InvokeHandler(eventId, sender, parameterString)
  3701.     {
  3702.         for (var i = 0; i < storedHandlers.length; i++) {
  3703.             if (eventId === storedHandlers[i].event) {
  3704.                 var handler = storedHandlers[i].handler;
  3705.  
  3706.                 var parameters;
  3707.                 if (parameterString) {
  3708.                     parameters = nativeJSON.parse(parameterString);
  3709.                 }
  3710.  
  3711.                 handler(sender, parameters, storedHandlers[i].listeners);
  3712.                 return true;
  3713.             }
  3714.         }
  3715.         return false;
  3716.     }
  3717.  
  3718.     function ExecuteGenericFunction(functionId, destination, parameters, callback)
  3719.     {
  3720.         if (!callback && typeof(parameters) === "function") {
  3721.             callback = parameters;
  3722.             parameters = null;
  3723.         }
  3724.  
  3725.         var callbackId;
  3726.         if (callback) {
  3727.             callbackId = StoreCallback(callback);
  3728.         }
  3729.  
  3730.         var stringifiedParameters;
  3731.         if (GetTypeName(parameters) != "undefined") {
  3732.             stringifiedParameters = nativeJSON.stringify(parameters);
  3733.         }
  3734.  
  3735.         nativeMsBrowser.genericFunction(functionId, destination, stringifiedParameters, callbackId);
  3736.     }
  3737.  
  3738.     function ExecuteGenericSynchronousFunction(functionId, parameters)
  3739.     {
  3740.         var syncReturnValue;
  3741.  
  3742.         var stringifiedParameters;
  3743.         if (GetTypeName(parameters) != "undefined") {
  3744.             stringifiedParameters = nativeJSON.stringify(parameters);
  3745.         }
  3746.  
  3747.         var syncReturnString = nativeMsBrowser.genericSynchronousFunction(functionId, stringifiedParameters);
  3748.  
  3749.         if (syncReturnString) {
  3750.             syncReturnValue = nativeJSON.parse(syncReturnString);
  3751.         }
  3752.  
  3753.         return syncReturnValue;
  3754.     }
  3755.  
  3756.     nativeMsBrowser.registerGenericFunctionCallbackHandler(InvokeCallback);
  3757.     nativeMsBrowser.registerGenericListenerHandler(InvokeHandler);
  3758.  
  3759. })(window.msBrowser);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement