Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(162)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/security/SecurityPanel.js

Issue 2296953004: Send certificates to devtools when it's open instead of using certId (Closed)
Patch Set: clear certificates on didstartprovisionalload Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * @constructor 6 * @constructor
7 * @extends {WebInspector.PanelWithSidebar} 7 * @extends {WebInspector.PanelWithSidebar}
8 * @implements {WebInspector.TargetManager.Observer} 8 * @implements {WebInspector.TargetManager.Observer}
9 */ 9 */
10 WebInspector.SecurityPanel = function() 10 WebInspector.SecurityPanel = function()
(...skipping 19 matching lines...) Expand all
30 WebInspector.targetManager.observeTargets(this, WebInspector.Target.Capabili ty.Network); 30 WebInspector.targetManager.observeTargets(this, WebInspector.Target.Capabili ty.Network);
31 } 31 }
32 32
33 /** @typedef {string} */ 33 /** @typedef {string} */
34 WebInspector.SecurityPanel.Origin; 34 WebInspector.SecurityPanel.Origin;
35 35
36 /** 36 /**
37 * @typedef {Object} 37 * @typedef {Object}
38 * @property {!SecurityAgent.SecurityState} securityState - Current security sta te of the origin. 38 * @property {!SecurityAgent.SecurityState} securityState - Current security sta te of the origin.
39 * @property {?NetworkAgent.SecurityDetails} securityDetails - Security details of the origin, if available. 39 * @property {?NetworkAgent.SecurityDetails} securityDetails - Security details of the origin, if available.
40 * @property {?Promise<!NetworkAgent.CertificateDetails>} certificateDetailsProm ise - Certificate details of the origin. Only available if securityDetails are a vailable. 40 * @property {?Promise<>} certificateDetailsPromise - Certificate details of the origin.
41 * @property {?WebInspector.SecurityOriginView} originView - Current SecurityOri ginView corresponding to origin. 41 * @property {?WebInspector.SecurityOriginView} originView - Current SecurityOri ginView corresponding to origin.
42 */ 42 */
43 WebInspector.SecurityPanel.OriginState; 43 WebInspector.SecurityPanel.OriginState;
44 44
45 WebInspector.SecurityPanel.prototype = { 45 WebInspector.SecurityPanel.prototype = {
46 46
47 /** 47 /**
48 * @param {!SecurityAgent.SecurityState} securityState 48 * @param {!SecurityAgent.SecurityState} securityState
49 */ 49 */
50 setRanInsecureContentStyle: function(securityState) 50 setRanInsecureContentStyle: function(securityState)
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
165 originState.originView.setSecurityState(securityState); 165 originState.originView.setSecurityState(securityState);
166 } 166 }
167 } else { 167 } else {
168 // TODO(lgarron): Store a (deduplicated) list of different security details we have seen. https://crbug.com/503170 168 // TODO(lgarron): Store a (deduplicated) list of different security details we have seen. https://crbug.com/503170
169 var originState = {}; 169 var originState = {};
170 originState.securityState = securityState; 170 originState.securityState = securityState;
171 171
172 var securityDetails = request.securityDetails(); 172 var securityDetails = request.securityDetails();
173 if (securityDetails) { 173 if (securityDetails) {
174 originState.securityDetails = securityDetails; 174 originState.securityDetails = securityDetails;
175 originState.certificateDetailsPromise = request.networkManager() .certificateDetailsPromise(securityDetails.certificateId);
176 } 175 }
177 176
178 this._origins.set(origin, originState); 177 this._origins.set(origin, originState);
179 178
180 this._sidebarTree.addOrigin(origin, securityState); 179 this._sidebarTree.addOrigin(origin, securityState);
181 180
182 // Don't construct the origin view yet (let it happen lazily). 181 // Don't construct the origin view yet (let it happen lazily).
183 } 182 }
184 }, 183 },
185 184
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
221 /** 220 /**
222 * @param {!WebInspector.NetworkLogView.MixedContentFilterValues} filterKey 221 * @param {!WebInspector.NetworkLogView.MixedContentFilterValues} filterKey
223 * @return {number} 222 * @return {number}
224 */ 223 */
225 filterRequestCount: function(filterKey) 224 filterRequestCount: function(filterKey)
226 { 225 {
227 return this._filterRequestCounts.get(filterKey) || 0; 226 return this._filterRequestCounts.get(filterKey) || 0;
228 }, 227 },
229 228
230 /** 229 /**
230 * @param {!SecurityAgent.CertificateId} certificateId
231 */
232 showCertificateViewer: function(certificateId)
233 {
234 var securityModel = WebInspector.SecurityModel.fromTarget(this._target);
235 securityModel.showCertificateViewer(certificateId);
236 },
237
238 /**
231 * @param {!SecurityAgent.SecurityState} stateA 239 * @param {!SecurityAgent.SecurityState} stateA
232 * @param {!SecurityAgent.SecurityState} stateB 240 * @param {!SecurityAgent.SecurityState} stateB
233 * @return {!SecurityAgent.SecurityState} 241 * @return {!SecurityAgent.SecurityState}
234 */ 242 */
235 _securityStateMin: function(stateA, stateB) 243 _securityStateMin: function(stateA, stateB)
236 { 244 {
237 return WebInspector.SecurityModel.SecurityStateComparator(stateA, stateB ) < 0 ? stateA : stateB; 245 return WebInspector.SecurityModel.SecurityStateComparator(stateA, stateB ) < 0 ? stateA : stateB;
238 }, 246 },
239 247
240 /** 248 /**
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
316 * @return {!WebInspector.SecurityPanel} 324 * @return {!WebInspector.SecurityPanel}
317 */ 325 */
318 WebInspector.SecurityPanel._instance = function() 326 WebInspector.SecurityPanel._instance = function()
319 { 327 {
320 return /** @type {!WebInspector.SecurityPanel} */ (self.runtime.sharedInstan ce(WebInspector.SecurityPanel)); 328 return /** @type {!WebInspector.SecurityPanel} */ (self.runtime.sharedInstan ce(WebInspector.SecurityPanel));
321 } 329 }
322 330
323 /** 331 /**
324 * @param {string} text 332 * @param {string} text
325 * @param {!SecurityAgent.CertificateId} certificateId 333 * @param {!SecurityAgent.CertificateId} certificateId
334 * @param {!WebInspector.SecurityPanel} panel
326 * @return {!Element} 335 * @return {!Element}
327 */ 336 */
328 WebInspector.SecurityPanel.createCertificateViewerButton = function(text, certif icateId) 337 WebInspector.SecurityPanel.createCertificateViewerButton = function (text, certi ficateId, panel)
pfeldman 2016/09/02 17:15:01 We don't do space after function.
jam 2016/09/03 00:00:24 oops, editor reforamtted. fixed
329 { 338 {
330 /** 339 /**
331 * @param {!Event} e 340 * @param {!Event} e
332 */ 341 */
333 function showCertificateViewer(e) 342 function showCertificateViewer(e)
334 { 343 {
335 e.consume(); 344 e.consume();
336 WebInspector.multitargetNetworkManager.showCertificateViewer(/** @type { number} */ (certificateId)); 345 panel.showCertificateViewer(certificateId);
337 } 346 }
338 347
339 return createTextButton(text, showCertificateViewer, "security-certificate-b utton"); 348 return createTextButton(text, showCertificateViewer, "security-certificate-b utton");
349 }
350
351 /**
352 * @param {string} text
353 * @param {number} certificateIndex
354 * @return {!Element}
355 */
356 WebInspector.SecurityPanel.createCertificateViewerButton2 = function (text, cert ificateIndex)
357 {
358 /**
359 * @param {!Event} e
360 */
361 function showCertificateViewer(e) {
362 e.consume();
363 WebInspector.multitargetNetworkManager.showCertificateViewer(certificate Index);
364 }
365
366 return createTextButton(text, showCertificateViewer, "security-certificate-b utton");
340 } 367 }
341 368
342 /** 369 /**
343 * @constructor 370 * @constructor
344 * @extends {TreeOutlineInShadow} 371 * @extends {TreeOutlineInShadow}
345 * @param {!WebInspector.SecurityPanelSidebarTreeElement} mainViewElement 372 * @param {!WebInspector.SecurityPanelSidebarTreeElement} mainViewElement
346 * @param {function(!WebInspector.SecurityPanel.Origin)} showOriginInPanel 373 * @param {function(!WebInspector.SecurityPanel.Origin)} showOriginInPanel
347 */ 374 */
348 WebInspector.SecurityPanelSidebarTree = function(mainViewElement, showOriginInPa nel) 375 WebInspector.SecurityPanelSidebarTree = function(mainViewElement, showOriginInPa nel)
349 { 376 {
(...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after
592 { 619 {
593 var explanationSection = parent.createChild("div", "security-explanation "); 620 var explanationSection = parent.createChild("div", "security-explanation ");
594 explanationSection.classList.add("security-explanation-" + explanation.s ecurityState); 621 explanationSection.classList.add("security-explanation-" + explanation.s ecurityState);
595 622
596 explanationSection.createChild("div", "security-property").classList.add ("security-property-" + explanation.securityState); 623 explanationSection.createChild("div", "security-property").classList.add ("security-property-" + explanation.securityState);
597 var text = explanationSection.createChild("div", "security-explanation-t ext"); 624 var text = explanationSection.createChild("div", "security-explanation-t ext");
598 text.createChild("div", "security-explanation-title").textContent = expl anation.summary; 625 text.createChild("div", "security-explanation-title").textContent = expl anation.summary;
599 text.createChild("div").textContent = explanation.description; 626 text.createChild("div").textContent = explanation.description;
600 627
601 if (explanation.certificateId) { 628 if (explanation.certificateId) {
602 text.appendChild(WebInspector.SecurityPanel.createCertificateViewerB utton(WebInspector.UIString("View certificate"), explanation.certificateId)); 629 text.appendChild(WebInspector.SecurityPanel.createCertificateViewerB utton(WebInspector.UIString("View certificate"), explanation.certificateId, this ._panel));
603 } 630 }
604 631
605 return text; 632 return text;
606 }, 633 },
607 634
608 /** 635 /**
609 * @param {!SecurityAgent.SecurityState} newSecurityState 636 * @param {!SecurityAgent.SecurityState} newSecurityState
610 * @param {!Array<!SecurityAgent.SecurityStateExplanation>} explanations 637 * @param {!Array<!SecurityAgent.SecurityStateExplanation>} explanations
611 * @param {?SecurityAgent.InsecureContentStatus} insecureContentStatus 638 * @param {?SecurityAgent.InsecureContentStatus} insecureContentStatus
612 * @param {boolean} schemeIsCryptographic 639 * @param {boolean} schemeIsCryptographic
(...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after
833 // Create the certificate section outside the callback, so that it appea rs in the right place. 860 // Create the certificate section outside the callback, so that it appea rs in the right place.
834 var certificateSection = this.element.createChild("div", "origin-view-se ction"); 861 var certificateSection = this.element.createChild("div", "origin-view-se ction");
835 certificateSection.createChild("div", "origin-view-section-title").textC ontent = WebInspector.UIString("Certificate"); 862 certificateSection.createChild("div", "origin-view-section-title").textC ontent = WebInspector.UIString("Certificate");
836 863
837 if (originState.securityDetails.signedCertificateTimestampList.length) { 864 if (originState.securityDetails.signedCertificateTimestampList.length) {
838 // Create the Certificate Transparency section outside the callback, so that it appears in the right place. 865 // Create the Certificate Transparency section outside the callback, so that it appears in the right place.
839 var sctSection = this.element.createChild("div", "origin-view-sectio n"); 866 var sctSection = this.element.createChild("div", "origin-view-sectio n");
840 sctSection.createChild("div", "origin-view-section-title").textConte nt = WebInspector.UIString("Certificate Transparency"); 867 sctSection.createChild("div", "origin-view-section-title").textConte nt = WebInspector.UIString("Certificate Transparency");
841 } 868 }
842 869
843 /** 870 var sanDiv = this._createSanDiv(originState.securityDetails.sanList);
844 * @this {WebInspector.SecurityOriginView} 871 var validFromString = new Date(1000 * originState.securityDetails.validF rom).toUTCString();
845 * @param {?NetworkAgent.CertificateDetails} certificateDetails 872 var validUntilString = new Date(1000 * originState.securityDetails.valid To).toUTCString();
846 */ 873
847 function displayCertificateDetails(certificateDetails) 874 table = new WebInspector.SecurityDetailsTable();
875 certificateSection.appendChild(table.element());
876 table.addRow(WebInspector.UIString("Subject"), originState.securityDetai ls.subjectName);
877 table.addRow(WebInspector.UIString("SAN"), sanDiv);
878 table.addRow(WebInspector.UIString("Valid From"), validFromString);
879 table.addRow(WebInspector.UIString("Valid Until"), validUntilString);
880 table.addRow(WebInspector.UIString("Issuer"), originState.securityDetail s.issuer);
881 table.addRow("", WebInspector.SecurityPanel.createCertificateViewerButto n2(WebInspector.UIString("Open full certificate details"), originState.securityD etails.certificateIndex));
882
883 if (!originState.securityDetails.signedCertificateTimestampList.length)
884 return;
885
886 // Show summary of SCT(s) of Certificate Transparency.
887 var sctSummaryTable = new WebInspector.SecurityDetailsTable();
888 sctSummaryTable.element().classList.add("sct-summary");
889 sctSection.appendChild(sctSummaryTable.element());
890 for (var i = 0; i < originState.securityDetails.signedCertificateTimesta mpList.length; i++)
848 { 891 {
849 var sanDiv = this._createSanDiv(certificateDetails.subject); 892 var sct = originState.securityDetails.signedCertificateTimestampList [i];
850 var validFromString = new Date(1000 * certificateDetails.validFrom). toUTCString(); 893 sctSummaryTable.addRow(WebInspector.UIString("SCT"), sct.logDescript ion + " (" + sct.origin + ", " + sct.status + ")");
851 var validUntilString = new Date(1000 * certificateDetails.validTo).t oUTCString();
852
853 var table = new WebInspector.SecurityDetailsTable();
854 certificateSection.appendChild(table.element());
855 table.addRow(WebInspector.UIString("Subject"), certificateDetails.su bject.name);
856 table.addRow(WebInspector.UIString("SAN"), sanDiv);
857 table.addRow(WebInspector.UIString("Valid From"), validFromString);
858 table.addRow(WebInspector.UIString("Valid Until"), validUntilString) ;
859 table.addRow(WebInspector.UIString("Issuer"), certificateDetails.iss uer);
860 table.addRow("", WebInspector.SecurityPanel.createCertificateViewerB utton(WebInspector.UIString("Open full certificate details"), originState.securi tyDetails.certificateId));
861
862 if (!originState.securityDetails.signedCertificateTimestampList.leng th)
863 return;
864
865 // Show summary of SCT(s) of Certificate Transparency.
866 var sctSummaryTable = new WebInspector.SecurityDetailsTable();
867 sctSummaryTable.element().classList.add("sct-summary");
868 sctSection.appendChild(sctSummaryTable.element());
869 for (var i = 0; i < originState.securityDetails.signedCertificateTim estampList.length; i++)
870 {
871 var sct = originState.securityDetails.signedCertificateTimestamp List[i];
872 sctSummaryTable.addRow(WebInspector.UIString("SCT"), sct.logDesc ription + " (" + sct.origin + ", " + sct.status + ")");
873 }
874
875 // Show detailed SCT(s) of Certificate Transparency.
876 var sctTableWrapper = sctSection.createChild("div", "sct-details");
877 sctTableWrapper.classList.add("hidden");
878 for (var i = 0; i < originState.securityDetails.signedCertificateTim estampList.length; i++)
879 {
880 var sctTable = new WebInspector.SecurityDetailsTable();
881 sctTableWrapper.appendChild(sctTable.element());
882 var sct = originState.securityDetails.signedCertificateTimestamp List[i];
883 sctTable.addRow(WebInspector.UIString("Log Name"), sct.logDescri ption);
884 sctTable.addRow(WebInspector.UIString("Log ID"), sct.logId.repla ce(/(.{2})/g,"$1 "));
885 sctTable.addRow(WebInspector.UIString("Validation Status"), sct. status);
886 sctTable.addRow(WebInspector.UIString("Source"), sct.origin);
887 sctTable.addRow(WebInspector.UIString("Issued At"), new Date(sct .timestamp).toUTCString());
888 sctTable.addRow(WebInspector.UIString("Hash Algorithm"), sct.has hAlgorithm);
889 sctTable.addRow(WebInspector.UIString("Signature Algorithm"), sc t.signatureAlgorithm);
890 sctTable.addRow(WebInspector.UIString("Signature Data"), sct.sig natureData.replace(/(.{2})/g,"$1 "));
891 }
892
893 // Add link to toggle between displaying of the summary of the SCT(s ) and the detailed SCT(s).
894 var toggleSctsDetailsLink = sctSection.createChild("div", "link");
895 toggleSctsDetailsLink.classList.add("sct-toggle");
896 toggleSctsDetailsLink.textContent = WebInspector.UIString("Show full details");
897 function toggleSctDetailsDisplay()
898 {
899 var isDetailsShown = !sctTableWrapper.classList.contains("hidden ");
900 if (isDetailsShown)
901 toggleSctsDetailsLink.textContent = WebInspector.UIString("S how full details");
902 else
903 toggleSctsDetailsLink.textContent = WebInspector.UIString("H ide full details");
904 sctSummaryTable.element().classList.toggle("hidden");
905 sctTableWrapper.classList.toggle("hidden");
906 }
907 toggleSctsDetailsLink.addEventListener("click", toggleSctDetailsDisp lay, false);
908 } 894 }
909 895
910 function displayCertificateDetailsUnavailable() 896 // Show detailed SCT(s) of Certificate Transparency.
897 var sctTableWrapper = sctSection.createChild("div", "sct-details");
898 sctTableWrapper.classList.add("hidden");
899 for (var i = 0; i < originState.securityDetails.signedCertificateTimesta mpList.length; i++)
911 { 900 {
912 certificateSection.createChild("div").textContent = WebInspector.UIS tring("Certificate details unavailable."); 901 var sctTable = new WebInspector.SecurityDetailsTable();
902 sctTableWrapper.appendChild(sctTable.element());
903 var sct = originState.securityDetails.signedCertificateTimestampList [i];
904 sctTable.addRow(WebInspector.UIString("Log Name"), sct.logDescriptio n);
905 sctTable.addRow(WebInspector.UIString("Log ID"), sct.logId.replace(/ (.{2})/g,"$1 "));
906 sctTable.addRow(WebInspector.UIString("Validation Status"), sct.stat us);
907 sctTable.addRow(WebInspector.UIString("Source"), sct.origin);
908 sctTable.addRow(WebInspector.UIString("Issued At"), new Date(sct.tim estamp).toUTCString());
909 sctTable.addRow(WebInspector.UIString("Hash Algorithm"), sct.hashAlg orithm);
910 sctTable.addRow(WebInspector.UIString("Signature Algorithm"), sct.si gnatureAlgorithm);
911 sctTable.addRow(WebInspector.UIString("Signature Data"), sct.signatu reData.replace(/(.{2})/g,"$1 "));
913 } 912 }
914 913
915 originState.certificateDetailsPromise.then(displayCertificateDetails.bin d(this), displayCertificateDetailsUnavailable); 914 // Add link to toggle between displaying of the summary of the SCT(s) an d the detailed SCT(s).
915 var toggleSctsDetailsLink = sctSection.createChild("div", "link");
916 toggleSctsDetailsLink.classList.add("sct-toggle");
917 toggleSctsDetailsLink.textContent = WebInspector.UIString("Show full det ails");
918 function toggleSctDetailsDisplay()
919 {
920 var isDetailsShown = !sctTableWrapper.classList.contains("hidden");
921 if (isDetailsShown)
922 toggleSctsDetailsLink.textContent = WebInspector.UIString("Show full details");
923 else
924 toggleSctsDetailsLink.textContent = WebInspector.UIString("Hide full details");
925 sctSummaryTable.element().classList.toggle("hidden");
926 sctTableWrapper.classList.toggle("hidden");
927 }
928 toggleSctsDetailsLink.addEventListener("click", toggleSctDetailsDisplay, false);
916 929
917 var noteSection = this.element.createChild("div", "origin-view-section") ; 930 var noteSection = this.element.createChild("div", "origin-view-section") ;
918 // TODO(lgarron): Fix the issue and then remove this section. See commen t in SecurityPanel._processRequest(). 931 // TODO(lgarron): Fix the issue and then remove this section. See commen t in SecurityPanel._processRequest().
919 noteSection.createChild("div").textContent = WebInspector.UIString("The security details above are from the first inspected response."); 932 noteSection.createChild("div").textContent = WebInspector.UIString("The security details above are from the first inspected response.");
920 } else if (originState.securityState !== SecurityAgent.SecurityState.Unknown ) { 933 } else if (originState.securityState !== SecurityAgent.SecurityState.Unknown ) {
921 var notSecureSection = this.element.createChild("div", "origin-view-sect ion"); 934 var notSecureSection = this.element.createChild("div", "origin-view-sect ion");
922 notSecureSection.createChild("div", "origin-view-section-title").textCon tent = WebInspector.UIString("Not Secure"); 935 notSecureSection.createChild("div", "origin-view-section-title").textCon tent = WebInspector.UIString("Not Secure");
923 notSecureSection.createChild("div").textContent = WebInspector.UIString( "Your connection to this origin is not secure."); 936 notSecureSection.createChild("div").textContent = WebInspector.UIString( "Your connection to this origin is not secure.");
924 } else { 937 } else {
925 var noInfoSection = this.element.createChild("div", "origin-view-section "); 938 var noInfoSection = this.element.createChild("div", "origin-view-section ");
926 noInfoSection.createChild("div", "origin-view-section-title").textConten t = WebInspector.UIString("No Security Information"); 939 noInfoSection.createChild("div", "origin-view-section-title").textConten t = WebInspector.UIString("No Security Information");
927 noInfoSection.createChild("div").textContent = WebInspector.UIString("No security details are available for this origin."); 940 noInfoSection.createChild("div").textContent = WebInspector.UIString("No security details are available for this origin.");
928 } 941 }
929 } 942 }
930 943
931 WebInspector.SecurityOriginView.prototype = { 944 WebInspector.SecurityOriginView.prototype = {
932 945
933 /** 946 /**
934 * @param {!NetworkAgent.CertificateSubject} certificateSubject 947 * @param {!Array<string>} sanList
935 * *return {!Element} 948 * *return {!Element}
936 */ 949 */
937 _createSanDiv: function(certificateSubject) 950 _createSanDiv: function(sanList)
938 { 951 {
939 var sanDiv = createElement("div"); 952 var sanDiv = createElement("div");
940 var sanList = certificateSubject.sanDnsNames.concat(certificateSubject.s anIpAddresses);
941 if (sanList.length === 0) { 953 if (sanList.length === 0) {
942 sanDiv.textContent = WebInspector.UIString("(N/A)"); 954 sanDiv.textContent = WebInspector.UIString("(N/A)");
943 sanDiv.classList.add("empty-san"); 955 sanDiv.classList.add("empty-san");
944 } else { 956 } else {
945 var truncatedNumToShow = 2; 957 var truncatedNumToShow = 2;
946 var listIsTruncated = sanList.length > truncatedNumToShow; 958 var listIsTruncated = sanList.length > truncatedNumToShow;
947 for (var i = 0; i < sanList.length; i++) { 959 for (var i = 0; i < sanList.length; i++) {
948 var span = sanDiv.createChild("span", "san-entry"); 960 var span = sanDiv.createChild("span", "san-entry");
949 span.textContent = sanList[i]; 961 span.textContent = sanList[i];
950 if (listIsTruncated && i >= truncatedNumToShow) 962 if (listIsTruncated && i >= truncatedNumToShow)
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
1016 row.createChild("div").textContent = key; 1028 row.createChild("div").textContent = key;
1017 1029
1018 var valueDiv = row.createChild("div"); 1030 var valueDiv = row.createChild("div");
1019 if (typeof value === "string") { 1031 if (typeof value === "string") {
1020 valueDiv.textContent = value; 1032 valueDiv.textContent = value;
1021 } else { 1033 } else {
1022 valueDiv.appendChild(value); 1034 valueDiv.appendChild(value);
1023 } 1035 }
1024 } 1036 }
1025 } 1037 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698