Skip to content

Commit

Permalink
Fixed search through Drawer panel.
Browse files Browse the repository at this point in the history
  • Loading branch information
3y3 authored and Marco Minetti committed May 19, 2015
1 parent d747efe commit 497b200
Show file tree
Hide file tree
Showing 11 changed files with 543 additions and 50 deletions.
1 change: 1 addition & 0 deletions front-end-node/inspector.json
Expand Up @@ -4,6 +4,7 @@
{ "name": "node/settings", "type": "autostart" },
{ "name": "node/sources", "type": "autostart" },
{ "name": "node/console", "type": "autostart" },
{ "name": "node/search", "type": "autostart" },
{ "name": "node/main", "type": "autostart" },
{ "name": "platform", "type": "autostart" },
{ "name": "main", "type": "autostart" },
Expand Down
41 changes: 40 additions & 1 deletion front-end-node/main/MainOverrides.js
Expand Up @@ -4,6 +4,7 @@ WebInspector.MainOverrides = function() {
this._unregisterShortcuts();
this._allowToSaveModifiedFiles();
this._reloadOnDetach();
this._avoidSourceMapFetchWhenInline();
};

WebInspector.MainOverrides.prototype = {
Expand Down Expand Up @@ -42,7 +43,45 @@ WebInspector.MainOverrides.prototype = {
// Front-end intercepts Cmd+R, Ctrl+R and F5 keys and reloads the debugged
// page instead of the front-end page. We want to disable this behaviour.
'F5', 'Ctrl+R', 'Meta+R'
]
],

_avoidSourceMapFetchWhenInline: function() {
WebInspector.CompilerScriptMapping.prototype.orig_loadSourceMapForScript =
WebInspector.CompilerScriptMapping.prototype._loadSourceMapForScript;

WebInspector.CompilerScriptMapping.prototype._loadSourceMapForScript = function(script, callback) {
var scriptURL = WebInspector.ParsedURL.completeURL(
script.target().resourceTreeModel.inspectedPageURL(),
script.sourceURL
);
if (!scriptURL) {
callback(null);
return;
}

console.assert(script.sourceMapURL);
var scriptSourceMapURL = (script.sourceMapURL);

var sourceMapURL = WebInspector.ParsedURL.completeURL(scriptURL, scriptSourceMapURL);
if (!sourceMapURL) {
callback(null);
return;
}

var INLINE_SOURCE_MAP_REGEX = /^(data:application\/json;base64,)(.*)$/;
var matched = INLINE_SOURCE_MAP_REGEX.exec(sourceMapURL);
if (!matched) return;

// Extracting SourceMap object from inline sourceMapURL
script.sourceMapURL = script.sourceURL + '.map';
var payload = JSON.parse(window.atob(matched[2]));

this._sourceMapForSourceMapURL[script.sourceMapURL] =
new WebInspector.SourceMap(script.sourceMapURL, payload);

this.orig_loadSourceMapForScript(script, callback);
};
}
};

new WebInspector.MainOverrides();
121 changes: 121 additions & 0 deletions front-end-node/search/SearchOverrides.js
@@ -0,0 +1,121 @@
/*jshint browser:true, nonew:false*/
/*global WebInspector:true, InspectorFrontendHost:true, InspectorFrontendHostAPI:true*/

(function() {
var createSearchRegex = function(query, caseSensitive, isRegex)
{
var regexFlags = caseSensitive ? "g" : "gi";
var regexObject;

if (isRegex) {
try {
regexObject = new RegExp('^.*?'+query+'.*?$|^.*?'+query+'.*?\n|\n.*?'+query+'.*?\n|\n.*?'+query+'.*?$', regexFlags);
} catch (e) {
// Silent catch.
}
}

if (!regexObject)
regexObject = createPlainTextSearchRegex(query, regexFlags);

return regexObject;
}

var createPlainTextSearchRegex = function(query, flags)
{
// This should be kept the same as the one in ContentSearchUtils.cpp.
var regexSpecialCharacters = "^[]{}()\\.^$*+?|-,";
var regex = "";
for (var i = 0; i < query.length; ++i) {
var c = query.charAt(i);
if (regexSpecialCharacters.indexOf(c) != -1)
regex += "\\";
regex += c;
}
return new RegExp('^.*?'+regex+'.*?$|^.*?'+regex+'.*?\n|\n.*?'+regex+'.*?\n|\n.*?'+regex+'.*?$', flags || "");
}



WebInspector.ContentProvider.performSearchInContent = function(content, query, caseSensitive, isRegex)
{
var regex = createSearchRegex(query, caseSensitive, isRegex);

var result = [];
var lastMatch;
var isMinified = false;

var firstNewLine = content.indexOf('\n');
if (content.length > 1024) {
if (firstNewLine > 1024 || firstNewLine === -1) {
isMinified = true;
}
}

while(lastMatch=regex.exec(content)) {
var lineContent = lastMatch[0];
var firstChar = lineContent.charCodeAt(0);
var lastChar = lineContent.charCodeAt(lineContent.length-1);
var lineMatchesBefore = content.substr(0,regex.lastIndex).match(/\n/g);
if (lineMatchesBefore){
var i = lineMatchesBefore.length;
if (lastChar !== 10){
++i;
} else {
lineContent = lineContent.substr(0,lineContent.length-1);
}
if (firstChar === 10){
lineContent = lineContent.substr(1);
}
if (isMinified === true && lineContent.length > 1024) {
lineContent = ' ... (line too long)';
}
result.push(new WebInspector.ContentProvider.SearchMatch(i, lineContent));
}
}
return result;
}
})()

WebInspector.FileBasedSearchResultsPane.FileTreeElement.prototype._appendSearchMatches = function(fromIndex, toIndex)
{
var searchResult = this._searchResult;
var uiSourceCode = searchResult.uiSourceCode;
var searchMatches = searchResult.searchMatches;

var queries = this._searchConfig.queries();
var regexes = [];
for (var i = 0; i < queries.length; ++i)
regexes.push(createSearchRegex(queries[i], !this._searchConfig.ignoreCase(), this._searchConfig.isRegex()));

for (var i = fromIndex; i < toIndex; ++i) {
var lineNumber = searchMatches[i].lineNumber;
var lineContent = searchMatches[i].lineContent;
var matchRanges = [];
for (var j = 0; j < regexes.length; ++j)
matchRanges = matchRanges.concat(this._regexMatchRanges(lineContent, regexes[j]));

var anchor;
if (!matchRanges[0]){
matchRanges[0] = new WebInspector.SourceRange(0,0);
anchor = this._createAnchor(uiSourceCode, lineNumber, matchRanges[0].offset);
} else {
anchor = this._createAnchor(uiSourceCode, lineNumber, matchRanges[0].offset);
}

var numberString = numberToStringWithSpacesPadding(lineNumber + 1, 4);
var lineNumberSpan = createElement("span");
lineNumberSpan.classList.add("search-match-line-number");
lineNumberSpan.textContent = numberString;
anchor.appendChild(lineNumberSpan);

var contentSpan = this._createContentSpan(lineContent, matchRanges);
anchor.appendChild(contentSpan);

var searchMatchElement = new TreeElement("");
searchMatchElement.selectable = false;
this.appendChild(searchMatchElement);
searchMatchElement.listItemElement.className = "search-match source-code";
searchMatchElement.listItemElement.appendChild(anchor);
}
}
9 changes: 9 additions & 0 deletions front-end-node/search/module.json
@@ -0,0 +1,9 @@
{
"dependencies": [
"sources",
"console"
],
"scripts": [
"SearchOverrides.js"
]
}
21 changes: 20 additions & 1 deletion lib/DebuggerAgent.js
Expand Up @@ -5,6 +5,7 @@ var convert = require('./convert.js'),
format = require('util').format,
path = require('path'),
async = require('async'),
search = require('./search.js'),
ScriptFileStorage = require('./ScriptFileStorage').ScriptFileStorage;

/**
Expand Down Expand Up @@ -205,7 +206,7 @@ DebuggerAgent.prototype = {
},

getScriptSource: function(params, done) {
this._debuggerClient.getScriptSourceById(
this._scriptManager.getScriptSourceById(
Number(params.scriptId),
function(err, source) {
if (err) return done(err);
Expand Down Expand Up @@ -493,6 +494,24 @@ DebuggerAgent.prototype = {
done(new Error('Not implemented.'));
else
done();
},

_searchScriptSourceCache: {},

searchInContent: function(params, done) {

function initSearch(content) {
if (content) {
done(null, {result: search.performSearchInContent(content, params.query, params.caseSensitive, params.isRegex)});
} else {
done(null, {result: []});
}
}

this._scriptManager.getScriptSourceByIdFromCache(Number(params.scriptId), function (err, data) {
if (err) return done(null, {result: []});
initSearch(data.scriptSource);
});
}
};

Expand Down
25 changes: 0 additions & 25 deletions lib/DebuggerClient.js
Expand Up @@ -191,31 +191,6 @@ DebuggerClient.prototype.evaluateGlobal = function(expression, done) {
);
};

/**
* @param {number} id
* @param {function(Object, string?)} callback
*/
DebuggerClient.prototype.getScriptSourceById = function(id, callback) {
this.request(
'scripts',
{
includeSource: true,
types: 4,
ids: [id]
},
function handleScriptSourceResponse(err, result) {
if (err) return callback(err);

// Some modules gets unloaded (?) after they are parsed,
// e.g. node_modules/express/node_modules/methods/index.js
// V8 request 'scripts' returns an empty result in such case
var source = result.length > 0 ? result[0].source : undefined;

callback(null, source);
}
);
};

/**
* @param {string} message
* @constructor
Expand Down
72 changes: 66 additions & 6 deletions lib/PageAgent.js
Expand Up @@ -6,8 +6,12 @@ var fs = require('fs'),
EventEmitter = require('events').EventEmitter,
async = require('async'),
convert = require('./convert.js'),
search = require('./search.js'),
ScriptFileStorage = require('./ScriptFileStorage.js').ScriptFileStorage;

// see Blink inspector > ContentSearchUtils.cpp > findMagicComment()
var SOURCE_MAP_URL_REGEX = /\/\/[@#][ \t]sourceMappingURL=[ \t]*([^\s'"]*)[ \t]*$/m,
INLINE_SOURCE_MAP_REGEX = /^(data:application\/json;base64,)(.*)$/;

/**
* @param {{preload}} config
Expand Down Expand Up @@ -165,27 +169,83 @@ extend(PageAgent.prototype, {
'// This is expected when you are debugging node\'s interactive REPL console.';

return process.nextTick(
this._convertScriptSourceToGetResourceResponse.bind(this, content, done));
this._convertScriptSourceToGetResourceResponse.bind(this, null, content, done));
}

async.waterfall(
[
this._scriptStorage.load.bind(this._scriptStorage, scriptName),
this._convertScriptSourceToGetResourceResponse.bind(this)
this._convertScriptSourceToGetResourceResponse.bind(this, params.url)
],
done
);
},

_convertScriptSourceToGetResourceResponse: function(source, done) {
return done(null, {
content: source
});
_parseSourceMapUrlFromResourceContent: function(source) {
var match = SOURCE_MAP_URL_REGEX.exec(source);
return match ? match[1] : undefined;
},

_getInlineSourceMap: function(sourceMapUrl) {
if (!sourceMapUrl) return sourceMapUrl;

var matched = INLINE_SOURCE_MAP_REGEX.exec(sourceMapUrl);

if (!matched) return null;

var sourceMapBody = matched[2];

return JSON.parse(new Buffer(sourceMapBody, 'base64').toString('ascii'));
},

_convertScriptSourceToGetResourceResponse: function(url, source, done) {
var sourceMapUrl, sourceMap, sourceFromMapContent;

sourceMapUrl = this._parseSourceMapUrlFromResourceContent(source);
if (sourceMapUrl) {
sourceMap = this._getInlineSourceMap(sourceMapUrl);
if (sourceMap) {
var cleanedUrl = url.replace(/^file:\/\//, '');
sourceMap.sources.forEach(function(file,index) {
//TODO: support/resolve relative paths
sourceFromMapContent = sourceMap.sourcesContent[index];
if (file === cleanedUrl && sourceMap.sourcesContent[index]) {
return done(null, {
content: sourceFromMapContent
});
}
});
}
}

if (!sourceFromMapContent) {
return done(null, {
content: source
});
}
},

reload: function(params, done) {
// This is called when user press Cmd+R (F5?), do we want to perform an action on this?
done();
},

_searchResourceContentCache: {},

searchInResource: function(params, done){

function initSearch(content){
if (content) {
done(null,{ result: search.performSearchInContent(content, params.query, params.caseSensitive, params.isRegex) });
} else {
done(null,{ result: [] });
}
}

this.getResourceContent({ url: params.url }, function(err,data){
if (err) return done(null,{ result: [] });
initSearch(data.content);
});
}
});

Expand Down

0 comments on commit 497b200

Please sign in to comment.