1 /* 2 * Copyright (C) 2009 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 /** @interface */ 32 function InspectorFrontendHostAPI() 33 { 34 } 35 36 /** @typedef {{type:string, id:(number|undefined), 37 label:(string|undefined), enabled:(boolean|undefined), checked:(boolean|undefined), 38 subItems:(!Array.<!InspectorFrontendHostAPI.ContextMenuDescriptor>|undefined)}} */ 39 InspectorFrontendHostAPI.ContextMenuDescriptor; 40 41 InspectorFrontendHostAPI.Events = { 42 AppendedToURL: "appendedToURL", 43 CanceledSaveURL: "canceledSaveURL", 44 ContextMenuCleared: "contextMenuCleared", 45 ContextMenuItemSelected: "contextMenuItemSelected", 46 DeviceCountUpdated: "deviceCountUpdated", 47 DevicesUpdated: "devicesUpdated", 48 DispatchMessage: "dispatchMessage", 49 EnterInspectElementMode: "enterInspectElementMode", 50 FileSystemsLoaded: "fileSystemsLoaded", 51 FileSystemRemoved: "fileSystemRemoved", 52 FileSystemAdded: "fileSystemAdded", 53 IndexingTotalWorkCalculated: "indexingTotalWorkCalculated", 54 IndexingWorked: "indexingWorked", 55 IndexingDone: "indexingDone", 56 KeyEventUnhandled: "keyEventUnhandled", 57 RevealSourceLine: "revealSourceLine", 58 SavedURL: "savedURL", 59 SearchCompleted: "searchCompleted", 60 SetToolbarColors: "setToolbarColors", 61 SetUseSoftMenu: "setUseSoftMenu", 62 ShowConsole: "showConsole" 63 } 64 65 InspectorFrontendHostAPI.EventDescriptors = [ 66 [InspectorFrontendHostAPI.Events.AppendedToURL, ["url"]], 67 [InspectorFrontendHostAPI.Events.CanceledSaveURL, ["url"]], 68 [InspectorFrontendHostAPI.Events.ContextMenuCleared, []], 69 [InspectorFrontendHostAPI.Events.ContextMenuItemSelected, ["id"]], 70 [InspectorFrontendHostAPI.Events.DeviceCountUpdated, ["count"]], 71 [InspectorFrontendHostAPI.Events.DevicesUpdated, ["devices"]], 72 [InspectorFrontendHostAPI.Events.DispatchMessage, ["messageObject"]], 73 [InspectorFrontendHostAPI.Events.EnterInspectElementMode, [], true], 74 [InspectorFrontendHostAPI.Events.FileSystemsLoaded, ["fileSystems"]], 75 [InspectorFrontendHostAPI.Events.FileSystemRemoved, ["fileSystemPath"]], 76 [InspectorFrontendHostAPI.Events.FileSystemAdded, ["errorMessage", "fileSystem"]], 77 [InspectorFrontendHostAPI.Events.IndexingTotalWorkCalculated, ["requestId", "fileSystemPath", "totalWork"]], 78 [InspectorFrontendHostAPI.Events.IndexingWorked, ["requestId", "fileSystemPath", "worked"]], 79 [InspectorFrontendHostAPI.Events.IndexingDone, ["requestId", "fileSystemPath"]], 80 [InspectorFrontendHostAPI.Events.KeyEventUnhandled, ["event"], true], 81 [InspectorFrontendHostAPI.Events.RevealSourceLine, ["url", "lineNumber", "columnNumber"], true], 82 [InspectorFrontendHostAPI.Events.SavedURL, ["url"]], 83 [InspectorFrontendHostAPI.Events.SearchCompleted, ["requestId", "fileSystemPath", "files"]], 84 [InspectorFrontendHostAPI.Events.SetToolbarColors, ["backgroundColor", "color"]], 85 [InspectorFrontendHostAPI.Events.SetUseSoftMenu, ["useSoftMenu"]], 86 [InspectorFrontendHostAPI.Events.ShowConsole, [], true] 87 ]; 88 89 InspectorFrontendHostAPI.prototype = { 90 addFileSystem: function() { }, 91 92 /** 93 * @param {string} url 94 * @param {string} content 95 */ 96 append: function(url, content) { }, 97 98 /** 99 * @param {number} requestId 100 * @param {string} fileSystemPath 101 */ 102 indexPath: function(requestId, fileSystemPath) { }, 103 104 /** 105 * @return {string} 106 */ 107 getSelectionBackgroundColor: function() { }, 108 109 /** 110 * @return {string} 111 */ 112 getSelectionForegroundColor: function() { }, 113 114 /** 115 * Requests inspected page to be placed atop of the inspector frontend with specified bounds. 116 * @param {{x: number, y: number, width: number, height: number}} bounds 117 */ 118 setInspectedPageBounds: function(bounds) { }, 119 120 /** 121 * Requests inspected page to be placed atop of the inspector frontend 122 * with passed insets from the frontend sides, respecting minimum size passed. 123 * @param {{top: number, left: number, right: number, bottom: number}} insets 124 * @param {{width: number, height: number}} minSize 125 */ 126 setContentsResizingStrategy: function(insets, minSize) { }, 127 128 /** 129 * @param {string} shortcuts 130 */ 131 setWhitelistedShortcuts: function(shortcuts) { }, 132 133 inspectElementCompleted: function() { }, 134 135 /** 136 * @param {number} x 137 * @param {number} y 138 */ 139 moveWindowBy: function(x, y) { }, 140 141 /** 142 * @param {string} url 143 */ 144 openInNewTab: function(url) { }, 145 146 /** 147 * @param {string} fileSystemPath 148 */ 149 removeFileSystem: function(fileSystemPath) { }, 150 151 requestFileSystems: function() { }, 152 153 /** 154 * @param {string} url 155 * @param {string} content 156 * @param {boolean} forceSaveAs 157 */ 158 save: function(url, content, forceSaveAs) { }, 159 160 /** 161 * @param {number} requestId 162 * @param {string} fileSystemPath 163 * @param {string} query 164 */ 165 searchInPath: function(requestId, fileSystemPath, query) { }, 166 167 /** 168 * @param {number} requestId 169 */ 170 stopIndexing: function(requestId) { }, 171 172 bringToFront: function() { }, 173 174 /** 175 * @param {string} browserId 176 * @param {string} url 177 */ 178 openUrlOnRemoteDeviceAndInspect: function(browserId, url) { }, 179 180 closeWindow: function() { }, 181 182 copyText: function(text) { }, 183 184 /** 185 * @param {string} url 186 */ 187 inspectedURLChanged: function(url) { }, 188 189 /** 190 * @param {string} fileSystemId 191 * @param {string} registeredName 192 * @return {?DOMFileSystem} 193 */ 194 isolatedFileSystem: function(fileSystemId, registeredName) { }, 195 196 /** 197 * @param {!FileSystem} fileSystem 198 */ 199 upgradeDraggedFileSystemPermissions: function(fileSystem) { }, 200 201 /** 202 * @return {string} 203 */ 204 platform: function() { }, 205 206 /** 207 * @return {string} 208 */ 209 port: function() { }, 210 211 /** 212 * @param {number} actionCode 213 */ 214 recordActionTaken: function(actionCode) { }, 215 216 /** 217 * @param {number} panelCode 218 */ 219 recordPanelShown: function(panelCode) { }, 220 221 /** 222 * @param {string} message 223 */ 224 sendMessageToBackend: function(message) { }, 225 226 /** 227 * @param {string} message 228 */ 229 sendMessageToEmbedder: function(message) { }, 230 231 /** 232 * @param {boolean} enabled 233 */ 234 setDeviceCountUpdatesEnabled: function(enabled) { }, 235 236 /** 237 * @param {boolean} enabled 238 */ 239 setDevicesUpdatesEnabled: function(enabled) { }, 240 241 /** 242 * @param {string} origin 243 * @param {string} script 244 */ 245 setInjectedScriptForOrigin: function(origin, script) { }, 246 247 /** 248 * @param {boolean} isDocked 249 * @param {!function()} callback 250 */ 251 setIsDocked: function(isDocked, callback) { }, 252 253 /** 254 * @param {number} zoom 255 */ 256 setZoomFactor: function(zoom) { }, 257 258 /** 259 * @return {number} 260 */ 261 zoomFactor: function() { }, 262 263 zoomIn: function() { }, 264 265 zoomOut: function() { }, 266 267 resetZoom: function() { }, 268 269 /** 270 * @param {number} x 271 * @param {number} y 272 * @param {!Array.<!InspectorFrontendHostAPI.ContextMenuDescriptor>} items 273 */ 274 showContextMenuAtPoint: function(x, y, items) { }, 275 276 /** 277 * @return {boolean} 278 */ 279 isUnderTest: function() { }, 280 281 /** 282 * @return {boolean} 283 */ 284 isHostedMode: function() { } 285 } 286 287 /** 288 * @constructor 289 * @implements {InspectorFrontendHostAPI} 290 */ 291 WebInspector.InspectorFrontendHostStub = function() 292 { 293 } 294 295 WebInspector.InspectorFrontendHostStub.prototype = { 296 /** 297 * @return {string} 298 */ 299 getSelectionBackgroundColor: function() 300 { 301 return "#6e86ff"; 302 }, 303 304 /** 305 * @return {string} 306 */ 307 getSelectionForegroundColor: function() 308 { 309 return "#ffffff"; 310 }, 311 312 /** 313 * @return {string} 314 */ 315 platform: function() 316 { 317 var match = navigator.userAgent.match(/Windows NT/); 318 if (match) 319 return "windows"; 320 match = navigator.userAgent.match(/Mac OS X/); 321 if (match) 322 return "mac"; 323 return "linux"; 324 }, 325 326 /** 327 * @return {string} 328 */ 329 port: function() 330 { 331 return "unknown"; 332 }, 333 334 bringToFront: function() 335 { 336 this._windowVisible = true; 337 }, 338 339 closeWindow: function() 340 { 341 this._windowVisible = false; 342 }, 343 344 /** 345 * @param {boolean} isDocked 346 * @param {!function()} callback 347 */ 348 setIsDocked: function(isDocked, callback) 349 { 350 }, 351 352 /** 353 * Requests inspected page to be placed atop of the inspector frontend with specified bounds. 354 * @param {{x: number, y: number, width: number, height: number}} bounds 355 */ 356 setInspectedPageBounds: function(bounds) 357 { 358 }, 359 360 /** 361 * Requests inspected page to be placed atop of the inspector frontend 362 * with passed insets from the frontend sides, respecting minimum size passed. 363 * @param {{top: number, left: number, right: number, bottom: number}} insets 364 * @param {{width: number, height: number}} minSize 365 */ 366 setContentsResizingStrategy: function(insets, minSize) 367 { 368 }, 369 370 inspectElementCompleted: function() 371 { 372 }, 373 374 /** 375 * @param {number} x 376 * @param {number} y 377 */ 378 moveWindowBy: function(x, y) 379 { 380 }, 381 382 /** 383 * @param {string} origin 384 * @param {string} script 385 */ 386 setInjectedScriptForOrigin: function(origin, script) 387 { 388 }, 389 390 /** 391 * @param {string} url 392 */ 393 inspectedURLChanged: function(url) 394 { 395 document.title = WebInspector.UIString("Developer Tools - %s", url); 396 }, 397 398 /** 399 * @param {string} text 400 */ 401 copyText: function(text) 402 { 403 WebInspector.console.error("Clipboard is not enabled in hosted mode. Please inspect using chrome://inspect"); 404 }, 405 406 /** 407 * @param {string} url 408 */ 409 openInNewTab: function(url) 410 { 411 window.open(url, "_blank"); 412 }, 413 414 /** 415 * @param {string} url 416 * @param {string} content 417 * @param {boolean} forceSaveAs 418 */ 419 save: function(url, content, forceSaveAs) 420 { 421 WebInspector.console.error("Saving files is not enabled in hosted mode. Please inspect using chrome://inspect"); 422 this.events.dispatchEventToListeners(InspectorFrontendHostAPI.Events.CanceledSaveURL, url); 423 }, 424 425 /** 426 * @param {string} url 427 * @param {string} content 428 */ 429 append: function(url, content) 430 { 431 WebInspector.console.error("Saving files is not enabled in hosted mode. Please inspect using chrome://inspect"); 432 }, 433 434 /** 435 * @param {string} message 436 */ 437 sendMessageToBackend: function(message) 438 { 439 }, 440 441 /** 442 * @param {string} message 443 */ 444 sendMessageToEmbedder: function(message) 445 { 446 }, 447 448 /** 449 * @param {number} actionCode 450 */ 451 recordActionTaken: function(actionCode) 452 { 453 }, 454 455 /** 456 * @param {number} panelCode 457 */ 458 recordPanelShown: function(panelCode) 459 { 460 }, 461 462 requestFileSystems: function() 463 { 464 }, 465 466 addFileSystem: function() 467 { 468 }, 469 470 /** 471 * @param {string} fileSystemPath 472 */ 473 removeFileSystem: function(fileSystemPath) 474 { 475 }, 476 477 /** 478 * @param {string} fileSystemId 479 * @param {string} registeredName 480 * @return {?DOMFileSystem} 481 */ 482 isolatedFileSystem: function(fileSystemId, registeredName) 483 { 484 return null; 485 }, 486 487 /** 488 * @param {!FileSystem} fileSystem 489 */ 490 upgradeDraggedFileSystemPermissions: function(fileSystem) 491 { 492 }, 493 494 /** 495 * @param {number} requestId 496 * @param {string} fileSystemPath 497 */ 498 indexPath: function(requestId, fileSystemPath) 499 { 500 }, 501 502 /** 503 * @param {number} requestId 504 */ 505 stopIndexing: function(requestId) 506 { 507 }, 508 509 /** 510 * @param {number} requestId 511 * @param {string} fileSystemPath 512 * @param {string} query 513 */ 514 searchInPath: function(requestId, fileSystemPath, query) 515 { 516 }, 517 518 /** 519 * @param {number} zoom 520 */ 521 setZoomFactor: function(zoom) 522 { 523 }, 524 525 /** 526 * @return {number} 527 */ 528 zoomFactor: function() 529 { 530 return 1; 531 }, 532 533 zoomIn: function() 534 { 535 }, 536 537 zoomOut: function() 538 { 539 }, 540 541 resetZoom: function() 542 { 543 }, 544 545 setWhitelistedShortcuts: function(shortcuts) 546 { 547 }, 548 549 /** 550 * @return {boolean} 551 */ 552 isUnderTest: function() 553 { 554 return false; 555 }, 556 557 /** 558 * @param {string} browserId 559 * @param {string} url 560 */ 561 openUrlOnRemoteDeviceAndInspect: function(browserId, url) 562 { 563 }, 564 565 /** 566 * @param {boolean} enabled 567 */ 568 setDeviceCountUpdatesEnabled: function(enabled) 569 { 570 }, 571 572 /** 573 * @param {boolean} enabled 574 */ 575 setDevicesUpdatesEnabled: function(enabled) 576 { 577 }, 578 579 /** 580 * @param {number} x 581 * @param {number} y 582 * @param {!Array.<!InspectorFrontendHostAPI.ContextMenuDescriptor>} items 583 */ 584 showContextMenuAtPoint: function(x, y, items) 585 { 586 throw "Soft context menu should be used"; 587 }, 588 589 /** 590 * @return {boolean} 591 */ 592 isHostedMode: function() 593 { 594 return true; 595 } 596 }; 597 598 /** 599 * @type {!InspectorFrontendHostAPI} 600 */ 601 var InspectorFrontendHost = window.InspectorFrontendHost || null; 602 603 (function() { 604 if (!InspectorFrontendHost) { 605 // Instantiate stub for web-hosted mode if necessary. 606 InspectorFrontendHost = new WebInspector.InspectorFrontendHostStub(); 607 } else { 608 // Otherwise add stubs for missing methods that are declared in the interface. 609 var proto = WebInspector.InspectorFrontendHostStub.prototype; 610 for (var name in proto) { 611 var value = proto[name]; 612 if (typeof value !== "function" || InspectorFrontendHost[name]) 613 continue; 614 615 InspectorFrontendHost[name] = stub.bind(null, name); 616 } 617 } 618 619 /** 620 * @param {string} name 621 */ 622 function stub(name) 623 { 624 console.error("Incompatible embedder: method InspectorFrontendHost." + name + " is missing. Using stub instead."); 625 var args = Array.prototype.slice.call(arguments, 1); 626 return proto[name].apply(InspectorFrontendHost, args); 627 } 628 629 // Attach the events object. 630 InspectorFrontendHost.events = new WebInspector.Object(); 631 })(); 632 633 /** 634 * @constructor 635 */ 636 function InspectorFrontendAPIImpl() 637 { 638 this._isLoaded = false; 639 this._pendingCommands = []; 640 this._debugFrontend = !!Runtime.queryParam("debugFrontend"); 641 642 var descriptors = InspectorFrontendHostAPI.EventDescriptors; 643 for (var i = 0; i < descriptors.length; ++i) 644 this[descriptors[i][0]] = this._dispatch.bind(this, descriptors[i][0], descriptors[i][1], descriptors[i][2]); 645 } 646 647 InspectorFrontendAPIImpl.prototype = { 648 loadCompleted: function() 649 { 650 this._isLoaded = true; 651 for (var i = 0; i < this._pendingCommands.length; ++i) 652 this._pendingCommands[i](); 653 this._pendingCommands = []; 654 if (window.opener) 655 window.opener.postMessage(["loadCompleted"], "*"); 656 }, 657 658 /** 659 * @param {string} name 660 * @param {!Array.<string>} signature 661 * @param {boolean} runOnceLoaded 662 */ 663 _dispatch: function(name, signature, runOnceLoaded) 664 { 665 var params = Array.prototype.slice.call(arguments, 3); 666 667 if (this._debugFrontend) 668 setImmediate(innerDispatch.bind(this)); 669 else 670 innerDispatch.call(this); 671 672 /** 673 * @this {!InspectorFrontendAPIImpl} 674 */ 675 function innerDispatch() 676 { 677 if (runOnceLoaded) 678 this._runOnceLoaded(dispatchAfterLoad); 679 else 680 dispatchAfterLoad(); 681 682 function dispatchAfterLoad() 683 { 684 // Single argument methods get dispatched with the param. 685 if (signature.length < 2) { 686 InspectorFrontendHost.events.dispatchEventToListeners(name, params[0]); 687 return; 688 } 689 var data = {}; 690 for (var i = 0; i < signature.length; ++i) 691 data[signature[i]] = params[i]; 692 InspectorFrontendHost.events.dispatchEventToListeners(name, data); 693 } 694 } 695 }, 696 697 /** 698 * @param {function()} command 699 */ 700 _runOnceLoaded: function(command) 701 { 702 if (this._isLoaded) { 703 command(); 704 return; 705 } 706 this._pendingCommands.push(command); 707 }, 708 709 /** 710 * @param {number} id 711 * @param {?string} error 712 */ 713 embedderMessageAck: function(id, error) 714 { 715 InspectorFrontendHost["embedderMessageAck"](id, error); 716 } 717 } 718 719 var InspectorFrontendAPI = new InspectorFrontendAPIImpl(); 720