<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Shim that simulates a <webview> tag via Mutation Observers.
//
// The actual tag is implemented via the browser plugin. The internals of this
// are hidden via Shadow DOM.

var watchForTag = require('tagWatcher').watchForTag;
var eventBindings = require('event_bindings');

/** @type {Array.<string>} */
var WEB_VIEW_ATTRIBUTES = ['name', 'src', 'partition', 'autosize', 'minheight',
    'minwidth', 'maxheight', 'maxwidth'];


// All exposed api methods for <webview>, these are forwarded to the browser
// plugin.
var WEB_VIEW_API_METHODS = [
  'getProcessId',
  'reload',
  'stop',
  'terminate'
];

var WEB_VIEW_EVENTS = {
  'close': [],
  'consolemessage': ['level', 'message', 'line', 'sourceId'],
  'contentload' : [],
  'exit' : ['processId', 'reason'],
  'loadabort' : ['url', 'isTopLevel', 'reason'],
  'loadcommit' : ['url', 'isTopLevel'],
  'loadredirect' : ['oldUrl', 'newUrl', 'isTopLevel'],
  'loadstart' : ['url', 'isTopLevel'],
  'loadstop' : [],
  'responsive' : ['processId'],
  'sizechanged': ['oldHeight', 'oldWidth', 'newHeight', 'newWidth'],
  'unresponsive' : ['processId']
};

var createEvent = function(name) {
  var eventOpts = {supportsListeners: true, supportsFilters: true};
  return new eventBindings.Event(name, undefined, eventOpts);
};

var loadCommitEvent = createEvent('webview.onLoadCommit');

// The <webview> tags we wish to watch for (watchForTag) does not belong to the
// current scope's "document" reference. We need to wait until the document
// begins loading, since only then will the "document" reference
// point to the page's document (it will be reset between now and then).
// We can't listen for the "readystatechange" event on the document (because
// the object that it's dispatched on doesn't exist yet), but we can instead
// do it at the window level in the capturing phase.
window.addEventListener('readystatechange', function(e) {
  if (document.readyState != 'loading') {
    return;
  }

  document.addEventListener('DOMContentLoaded', function(e) {
    watchForTag('WEBVIEW', function(addedNode) { new WebView(addedNode); });
  });
}, true /* useCapture */);


/** @type {number} */
WebView.prototype.entryCount_;

/** @type {number} */
WebView.prototype.currentEntryIndex_;

/**
 * @constructor
 */
function WebView(webviewNode) {
  this.webviewNode_ = webviewNode;
  this.browserPluginNode_ = this.createBrowserPluginNode_();
  var shadowRoot = this.webviewNode_.webkitCreateShadowRoot();
  shadowRoot.appendChild(this.browserPluginNode_);

  this.setupFocusPropagation_();
  this.setupWebviewNodeMethods_();
  this.setupWebviewNodeProperties_();
  this.setupWebviewNodeAttributes_();
  this.setupWebviewNodeEvents_();

  // Experimental API
  this.maybeSetupExperimentalAPI_();
}

/**
 * @private
 */
WebView.prototype.createBrowserPluginNode_ = function() {
  var browserPluginNode = document.createElement('object');
  browserPluginNode.type = 'application/browser-plugin';
  browserPluginNode.setAttribute('api', 'webview');
  // The <object> node fills in the <webview> container.
  browserPluginNode.style.width = '100%';
  browserPluginNode.style.height = '100%';
  $Array.forEach(WEB_VIEW_ATTRIBUTES, function(attributeName) {
    // Only copy attributes that have been assigned values, rather than copying
    // a series of undefined attributes to BrowserPlugin.
    if (this.webviewNode_.hasAttribute(attributeName)) {
      browserPluginNode.setAttribute(
        attributeName, this.webviewNode_.getAttribute(attributeName));
    } else if (this.webviewNode_[attributeName]){
      // Reading property using has/getAttribute does not work on
      // document.DOMContentLoaded event (but works on
      // window.DOMContentLoaded event).
      // So copy from property if copying from attribute fails.
      browserPluginNode.setAttribute(
        attributeName, this.webviewNode_[attributeName]);
    }
  }, this);

  return browserPluginNode;
};

/**
 * @private
 */
WebView.prototype.setupFocusPropagation_ = function() {
  if (!this.webviewNode_.hasAttribute('tabIndex')) {
    // <webview> needs a tabIndex in order to respond to keyboard focus.
    // TODO(fsamuel): This introduces unexpected tab ordering. We need to find
    // a way to take keyboard focus without messing with tab ordering.
    // See http://crbug.com/231664.
    this.webviewNode_.setAttribute('tabIndex', 0);
  }
  var self = this;
  this.webviewNode_.addEventListener('focus', function(e) {
    // Focus the BrowserPlugin when the <webview> takes focus.
    self.browserPluginNode_.focus();
  });
  this.webviewNode_.addEventListener('blur', function(e) {
    // Blur the BrowserPlugin when the <webview> loses focus.
    self.browserPluginNode_.blur();
  });
};

/**
 * @private
 */
WebView.prototype.setupWebviewNodeMethods_ = function() {
  // this.browserPluginNode_[apiMethod] are not necessarily defined immediately
  // after the shadow object is appended to the shadow root.
  var webviewNode = this.webviewNode_;
  var browserPluginNode = this.browserPluginNode_;
  var self = this;

  webviewNode['canGoBack'] = function() {
    return self.entryCount_ > 1 && self.currentEntryIndex_ > 0;
  };

  webviewNode['canGoForward'] = function() {
    return self.currentEntryIndex_ >=0 &&
        self.currentEntryIndex_ < (self.entryCount_ - 1);
  };

  webviewNode['back'] = function() {
    webviewNode.go(-1);
  };

  webviewNode['forward'] = function() {
    webviewNode.go(1);
  };

  webviewNode['go'] = function(relativeIndex) {
    var instanceId = browserPluginNode.getGuestInstanceId();
    if (!instanceId)
      return;
    chrome.webview.go(instanceId, relativeIndex);
  };

  $Array.forEach(WEB_VIEW_API_METHODS, function(apiMethod) {
    webviewNode[apiMethod] = function(var_args) {
      return browserPluginNode[apiMethod].apply(browserPluginNode, arguments);
    };
  }, this);
  this.setupExecuteCodeAPI_();
};

/**
 * @private
 */
WebView.prototype.setupWebviewNodeProperties_ = function() {
  var ERROR_MSG_CONTENTWINDOW_NOT_AVAILABLE = '<webview>: ' +
    'contentWindow is not available at this time. It will become available ' +
        'when the page has finished loading.';

  var browserPluginNode = this.browserPluginNode_;
  // Expose getters and setters for the attributes.
  $Array.forEach(WEB_VIEW_ATTRIBUTES, function(attributeName) {
    Object.defineProperty(this.webviewNode_, attributeName, {
      get: function() {
        return browserPluginNode[attributeName];
      },
      set: function(value) {
        browserPluginNode[attributeName] = value;<commit_msg><webview>: Restore change to web_view.js that was accidentally undone.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Shim that simulates a <webview> tag via Mutation Observers.
//
// The actual tag is implemented via the browser plugin. The internals of this
// are hidden via Shadow DOM.

var watchForTag = require('tagWatcher').watchForTag;
var eventBindings = require('event_bindings');

/** @type {Array.<string>} */
var WEB_VIEW_ATTRIBUTES = ['name', 'src', 'partition', 'autosize', 'minheight',
    'minwidth', 'maxheight', 'maxwidth'];


// All exposed api methods for <webview>, these are forwarded to the browser
// plugin.
var WEB_VIEW_API_METHODS = [
  'getProcessId',
  'reload',
  'stop',
  'terminate'
];

var WEB_VIEW_EVENTS = {
  'close': [],
  'consolemessage': ['level', 'message', 'line', 'sourceId'],
  'contentload' : [],
  'exit' : ['processId', 'reason'],
  'loadabort' : ['url', 'isTopLevel', 'reason'],
  'loadcommit' : ['url', 'isTopLevel'],
  'loadredirect' : ['oldUrl', 'newUrl', 'isTopLevel'],
  'loadstart' : ['url', 'isTopLevel'],
  'loadstop' : [],
  'responsive' : ['processId'],
  'sizechanged': ['oldHeight', 'oldWidth', 'newHeight', 'newWidth'],
  'unresponsive' : ['processId']
};

var createEvent = function(name) {
  var eventOpts = {supportsListeners: true, supportsFilters: true};
  return new eventBindings.Event(name, undefined, eventOpts);
};

var loadCommitEvent = createEvent('webview.onLoadCommit');

// The <webview> tags we wish to watch for (watchForTag) does not belong to the
// current scope's "document" reference. We need to wait until the document
// begins loading, since only then will the "document" reference
// point to the page's document (it will be reset between now and then).
// We can't listen for the "readystatechange" event on the document (because
// the object that it's dispatched on doesn't exist yet), but we can instead
// do it at the window level in the capturing phase.
window.addEventListener('readystatechange', function(e) {
  if (document.readyState != 'loading') {
    return;
  }

  document.addEventListener('DOMContentLoaded', function(e) {
    watchForTag('WEBVIEW', function(addedNode) { new WebView(addedNode); });
  });
}, true /* useCapture */);


/** @type {number} */
WebView.prototype.entryCount_;

/** @type {number} */
WebView.prototype.currentEntryIndex_;

/**
 * @constructor
 */
function WebView(webviewNode) {
  this.webviewNode_ = webviewNode;
  this.browserPluginNode_ = this.createBrowserPluginNode_();
  var shadowRoot = this.webviewNode_.webkitCreateShadowRoot();
  shadowRoot.appendChild(this.browserPluginNode_);

  this.setupFocusPropagation_();
  this.setupWebviewNodeMethods_();
  this.setupWebviewNodeProperties_();
  this.setupWebviewNodeAttributes_();
  this.setupWebviewNodeEvents_();

  // Experimental API
  this.maybeSetupExperimentalAPI_();
}

/**
 * @private
 */
WebView.prototype.createBrowserPluginNode_ = function() {
  var browserPluginNode = document.createElement('object');
  browserPluginNode.type = 'application/browser-plugin';
  browserPluginNode.setAttribute('api', 'webview');
  // The <object> node fills in the <webview> container.
  browserPluginNode.style.width = '100%';
  browserPluginNode.style.height = '100%';
  $Array.forEach(WEB_VIEW_ATTRIBUTES, function(attributeName) {
    // Only copy attributes that have been assigned values, rather than copying
    // a series of undefined attributes to BrowserPlugin.
    if (this.webviewNode_.hasAttribute(attributeName)) {
      browserPluginNode.setAttribute(
        attributeName, this.webviewNode_.getAttribute(attributeName));
    } else if (this.webviewNode_[attributeName]){
      // Reading property using has/getAttribute does not work on
      // document.DOMContentLoaded event (but works on
      // window.DOMContentLoaded event).
      // So copy from property if copying from attribute fails.
      browserPluginNode.setAttribute(
        attributeName, this.webviewNode_[attributeName]);
    }
  }, this);

  return browserPluginNode;
};

/**
 * @private
 */
WebView.prototype.setupFocusPropagation_ = function() {
  if (!this.webviewNode_.hasAttribute('tabIndex')) {
    // <webview> needs a tabIndex in order to respond to keyboard focus.
    // TODO(fsamuel): This introduces unexpected tab ordering. We need to find
    // a way to take keyboard focus without messing with tab ordering.
    // See http://crbug.com/231664.
    this.webviewNode_.setAttribute('tabIndex', 0);
  }
  var self = this;
  this.webviewNode_.addEventListener('focus', function(e) {
    // Focus the BrowserPlugin when the <webview> takes focus.
    self.browserPluginNode_.focus();
  });
  this.webviewNode_.addEventListener('blur', function(e) {
    // Blur the BrowserPlugin when the <webview> loses focus.
    self.browserPluginNode_.blur();
  });
};

/**
 * @private
 */
WebView.prototype.setupWebviewNodeMethods_ = function() {
  // this.browserPluginNode_[apiMethod] are not necessarily defined immediately
  // after the shadow object is appended to the shadow root.
  var webviewNode = this.webviewNode_;
  var browserPluginNode = this.browserPluginNode_;
  var self = this;

  webviewNode['canGoBack'] = function() {
    return self.entryCount_ > 1 && self.currentEntryIndex_ > 0;
  };

  webviewNode['canGoForward'] = function() {
    return self.currentEntryIndex_ >=0 &&
        self.currentEntryIndex_ < (self.entryCount_ - 1);
  };

  webviewNode['back'] = function() {
    webviewNode.go(-1);
  };

  webviewNode['forward'] = function() {
    webviewNode.go(1);
  };

  webviewNode['go'] = function(relativeIndex) {
    var instanceId = browserPluginNode.getGuestInstanceId();
    if (!instanceId)
      return;
    chrome.webview.go(instanceId, relativeIndex);
  };

  $Array.forEach(WEB_VIEW_API_METHODS, function(apiMethod) {
    webviewNode[apiMethod] = function(var_args) {
      return $Function.apply(browserPluginNode[apiMethod],
          browserPluginNode, arguments);
    };
  }, this);
  this.setupExecuteCodeAPI_();
};

/**
 * @private
 */
WebView.prototype.setupWebviewNodeProperties_ = function() {
  var ERROR_MSG_CONTENTWINDOW_NOT_AVAILABLE = '<webview>: ' +
    'contentWindow is not available at this time. It will become available ' +
        'when the page has finished loading.';

  var browserPluginNode = this.browserPluginNode_;
  // Expose getters and setters for the attributes.
  $Array.forEach(WEB_VIEW_ATTRIBUTES, function(attributeName) {
    Object.defineProperty(this.webviewNode_, attributeName, {
      get: function() {
        return browserPluginNode[attributeName];
      },
      set: function(value) {
        browserPluginNode[attributeName] = value;
{'fraction_non_alphanumeric': 0.12877337652775733, 'fraction_numerical': 0.0033868355175968195, 'mean_word_length': 4.2042145593869735, 'pattern_counts': {'":': 0, '<': 26, '<?xml version=': 0, '>': 30, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5928988', 'n_tokens_mistral': 4056, 'n_tokens_neox': 3974, 'n_words': 1058}