1 /* 2 * ConsoleError.java 3 * 4 * Copyright (C) 2021 by RStudio, PBC 5 * 6 * Unless you have received this program directly from RStudio pursuant 7 * to the terms of a commercial license agreement with RStudio, then 8 * this program is licensed to you under the terms of version 3 of the 9 * GNU Affero General Public License. This program is distributed WITHOUT 10 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, 11 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the 12 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. 13 * 14 */ 15 16 package org.rstudio.studio.client.common.debugging.ui; 17 18 import com.google.gwt.dom.client.Element; 19 import com.google.gwt.dom.client.Node; 20 import com.google.gwt.dom.client.NodeList; 21 import com.google.gwt.dom.client.Style; 22 import org.rstudio.core.client.VirtualConsole; 23 import org.rstudio.studio.client.RStudioGinjector; 24 import org.rstudio.studio.client.common.debugging.model.UnhandledError; 25 26 import com.google.gwt.core.client.GWT; 27 import com.google.gwt.uibinder.client.UiBinder; 28 import com.google.gwt.uibinder.client.UiField; 29 import com.google.gwt.user.client.DOM; 30 import com.google.gwt.user.client.Event; 31 import com.google.gwt.user.client.EventListener; 32 import com.google.gwt.user.client.ui.Anchor; 33 import com.google.gwt.user.client.ui.Composite; 34 import com.google.gwt.user.client.ui.HTML; 35 import com.google.gwt.user.client.ui.HTMLPanel; 36 import com.google.gwt.user.client.ui.Image; 37 import com.google.gwt.user.client.ui.Widget; 38 39 public class ConsoleError extends Composite 40 { 41 private static ConsoleErrorUiBinder uiBinder = GWT.create(ConsoleErrorUiBinder.class); 42 43 interface ConsoleErrorUiBinder extends UiBinder<Widget, ConsoleError> 44 { 45 } 46 47 public interface Observer 48 { onErrorBoxResize()49 void onErrorBoxResize(); runCommandWithDebug(String command)50 void runCommandWithDebug(String command); 51 } 52 53 // Because we are adding interactive elements to the VirtualScroller the GWT bindings are lost. 54 // We need to programmatically find the elements and manipulate them through regular JS functions. ConsoleError(UnhandledError err, String errorClass, Observer observer, String command)55 public ConsoleError(UnhandledError err, 56 String errorClass, 57 Observer observer, 58 String command) 59 { 60 observer_ = observer; 61 command_ = command; 62 63 initWidget(uiBinder.createAndBindUi(this)); 64 65 VirtualConsole vc = RStudioGinjector.INSTANCE.getVirtualConsoleFactory().create(errorMessage.getElement()); 66 vc.submit(err.getErrorMessage().trim()); 67 errorMessage.addStyleName(errorClass); 68 69 EventListener onConsoleErrorClick = event -> 70 { 71 if (DOM.eventGetType(event) == Event.ONCLICK) 72 { 73 Element target = Element.as(event.getEventTarget()); 74 if (target == null) 75 return; 76 77 if (target.hasClassName("show_traceback_text") || target.hasClassName("show_traceback_image")) 78 { 79 setTracebackVisible(!showingTraceback_); 80 observer_.onErrorBoxResize(); 81 } 82 else if (target.hasClassName("rerun_text") || target.hasClassName("rerun_image")) 83 { 84 observer_.onErrorBoxResize(); 85 observer_.runCommandWithDebug(command_); 86 } 87 } 88 }; 89 90 rerunText.setVisible(command_ != null); 91 rerunImage.setVisible(command_ != null); 92 93 DOM.sinkEvents(this.getElement(), Event.ONCLICK); 94 DOM.setEventListener(this.getElement(), onConsoleErrorClick); 95 96 for (int i = err.getErrorFrames().length() - 1; i >= 0; i--) 97 { 98 ConsoleErrorFrame frame = new ConsoleErrorFrame(i + 1, 99 err.getErrorFrames().get(i)); 100 framePanel.add(frame); 101 } 102 } 103 setTracebackVisible(boolean visible)104 public void setTracebackVisible(boolean visible) 105 { 106 showingTraceback_ = visible; 107 108 NodeList<Node> children = this.getElement().getChildNodes(); 109 for (int i = 0; i < children.getLength(); i++) 110 { 111 Node n = children.getItem(i); 112 if (n.getNodeType() != Node.ELEMENT_NODE) 113 continue; 114 115 Element child = Element.as(children.getItem(i)); 116 if (child.hasClassName("show_traceback_text")) { 117 if (showingTraceback_) 118 child.setInnerText("Hide Traceback"); 119 else 120 child.setInnerText("Show Traceback"); 121 } 122 else if (child.hasClassName("stack_trace")) 123 { 124 if (showingTraceback_) 125 child.getStyle().setDisplay(Style.Display.BLOCK); 126 else 127 child.getStyle().setDisplay(Style.Display.NONE); 128 } 129 } 130 } 131 132 @UiField Anchor showTracebackText; 133 @UiField Image showTracebackImage; 134 @UiField Anchor rerunText; 135 @UiField Image rerunImage; 136 @UiField HTMLPanel framePanel; 137 @UiField HTML errorMessage; 138 139 private Observer observer_; 140 private boolean showingTraceback_ = false; 141 private String command_; 142 } 143